commit | a6ef0a64fd597e53003c7846192790ed0e3642b5 | [log] [tgz] |
---|---|---|
author | QUICHE team <quiche-dev@google.com> | Thu Mar 07 20:34:33 2019 -0500 |
committer | Victor Vasiliev <vasilvv@google.com> | Thu Mar 07 20:36:20 2019 -0500 |
tree | 15c5176d56d64d53129c6a6e8735b8dc176fa7fb | |
parent | ded03513eb39ded284ec430a55ad4bdc86daec38 [diff] |
Project import generated by Copybara. PiperOrigin-RevId: 237361882 Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/chlo_extractor.cc b/quic/core/chlo_extractor.cc new file mode 100644 index 0000000..15a118b --- /dev/null +++ b/quic/core/chlo_extractor.cc
@@ -0,0 +1,309 @@ +// 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_message.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.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, + PacketHeaderFormat form) override; + void OnPacket() override {} + void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {} + void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) 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; + 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 OnApplicationCloseFrame(const QuicApplicationCloseFrame& 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 OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override; + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& 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, + PacketHeaderFormat /*form*/) { + if (!framer_->IsSupportedVersion(version)) { + return false; + } + framer_->set_version(version); + return true; +} + +bool ChloFramerVisitor::OnUnauthenticatedPublicHeader( + const QuicPacketHeader& header) { + connection_id_ = header.destination_connection_id; + return true; +} +bool ChloFramerVisitor::OnUnauthenticatedHeader( + const QuicPacketHeader& header) { + return true; +} +bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) { + return true; +} +void ChloFramerVisitor::OnCoalescedPacket(const QuicEncryptedPacket& packet) {} +bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) { + if (framer_->transport_version() >= QUIC_VERSION_47) { + // CHLO will be sent in CRYPTO frames in v47 and above. + return false; + } + QuicStringPiece data(frame.data_buffer, frame.data_length); + if (frame.stream_id == + QuicUtils::GetCryptoStreamId(framer_->transport_version()) && + frame.offset == 0 && QuicTextUtils::StartsWith(data, "CHLO")) { + return OnHandshakeData(data); + } + return true; +} + +bool ChloFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) { + if (framer_->transport_version() < QUIC_VERSION_47) { + // 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::OnApplicationCloseFrame( + const QuicApplicationCloseFrame& 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::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) { + return true; +} + +bool ChloFramerVisitor::OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& 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) { + 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
diff --git a/quic/core/chlo_extractor.h b/quic/core/chlo_extractor.h new file mode 100644 index 0000000..4e53a10 --- /dev/null +++ b/quic/core/chlo_extractor.h
@@ -0,0 +1,45 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_CHLO_EXTRACTOR_H_ +#define QUICHE_QUIC_CORE_CHLO_EXTRACTOR_H_ + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" + +namespace quic { + +// A utility for extracting QUIC Client Hello messages from packets, +// without needs to spin up a full QuicSession. +class ChloExtractor { + public: + class Delegate { + public: + virtual ~Delegate() {} + + // Called when a CHLO message is found in the packets. + virtual void OnChlo(QuicTransportVersion version, + QuicConnectionId connection_id, + const CryptoHandshakeMessage& chlo) = 0; + }; + + // Extracts a CHLO message from |packet| and invokes the OnChlo + // method of |delegate|. Return true if a CHLO message was found, + // and false otherwise. If non-empty, + // |create_session_tag_indicators| contains a list of QUIC tags that + // if found will result in the session being created early, to + // enable support for multi-packet CHLOs. + static bool Extract(const QuicEncryptedPacket& packet, + const ParsedQuicVersionVector& versions, + const QuicTagVector& create_session_tag_indicators, + Delegate* delegate, + uint8_t connection_id_length); + + ChloExtractor(const ChloExtractor&) = delete; + ChloExtractor operator=(const ChloExtractor&) = delete; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CHLO_EXTRACTOR_H_
diff --git a/quic/core/chlo_extractor_test.cc b/quic/core/chlo_extractor_test.cc new file mode 100644 index 0000000..1e0b6f5 --- /dev/null +++ b/quic/core/chlo_extractor_test.cc
@@ -0,0 +1,168 @@ +// 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 <memory> + +#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_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +class TestDelegate : public ChloExtractor::Delegate { + public: + TestDelegate() = default; + ~TestDelegate() override = default; + + // ChloExtractor::Delegate implementation + void OnChlo(QuicTransportVersion version, + QuicConnectionId connection_id, + const CryptoHandshakeMessage& chlo) override { + version_ = version; + connection_id_ = connection_id; + chlo_ = chlo.DebugString(); + } + + QuicConnectionId connection_id() const { return connection_id_; } + QuicTransportVersion transport_version() const { return version_; } + const QuicString& chlo() const { return chlo_; } + + private: + QuicConnectionId connection_id_; + QuicTransportVersion version_; + QuicString chlo_; +}; + +class ChloExtractorTest : public QuicTest { + public: + ChloExtractorTest() { + header_.destination_connection_id = TestConnectionId(); + header_.destination_connection_id_included = CONNECTION_ID_PRESENT; + header_.version_flag = true; + header_.version = AllSupportedVersions().front(); + header_.reset_flag = false; + header_.packet_number_length = PACKET_4BYTE_PACKET_NUMBER; + header_.packet_number = QuicPacketNumber(1); + if (QuicVersionHasLongHeaderLengths(header_.version.transport_version)) { + header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + } + + void MakePacket(ParsedQuicVersion version, + QuicStringPiece data, + bool munge_offset, + bool munge_stream_id) { + QuicFrames frames; + size_t offset = 0; + if (munge_offset) { + offset++; + } + QuicFramer framer(SupportedVersions(header_.version), QuicTime::Zero(), + Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength); + if (version.transport_version < QUIC_VERSION_47 || munge_stream_id) { + QuicStreamId stream_id = + QuicUtils::GetCryptoStreamId(version.transport_version); + if (munge_stream_id) { + stream_id++; + } + frames.push_back( + QuicFrame(QuicStreamFrame(stream_id, false, offset, data))); + } else { + frames.push_back( + QuicFrame(new QuicCryptoFrame(ENCRYPTION_NONE, offset, data))); + } + std::unique_ptr<QuicPacket> packet( + BuildUnsizedDataPacket(&framer, header_, frames)); + EXPECT_TRUE(packet != nullptr); + size_t encrypted_length = + framer.EncryptPayload(ENCRYPTION_NONE, header_.packet_number, *packet, + buffer_, QUIC_ARRAYSIZE(buffer_)); + ASSERT_NE(0u, encrypted_length); + packet_ = QuicMakeUnique<QuicEncryptedPacket>(buffer_, encrypted_length); + EXPECT_TRUE(packet_ != nullptr); + DeleteFrames(&frames); + } + + protected: + TestDelegate delegate_; + QuicPacketHeader header_; + std::unique_ptr<QuicEncryptedPacket> packet_; + char buffer_[kMaxPacketSize]; +}; + +TEST_F(ChloExtractorTest, FindsValidChlo) { + CryptoHandshakeMessage client_hello; + client_hello.set_tag(kCHLO); + + QuicString client_hello_str(client_hello.GetSerialized().AsStringPiece()); + // Construct a CHLO with each supported version + for (ParsedQuicVersion version : AllSupportedVersions()) { + SCOPED_TRACE(version); + ParsedQuicVersionVector versions(SupportedVersions(version)); + header_.version = version; + if (QuicVersionHasLongHeaderLengths(version.transport_version) && + header_.version_flag) { + header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } else { + header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; + header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; + } + MakePacket(version, client_hello_str, /*munge_offset*/ false, + /*munge_stream_id*/ false); + EXPECT_TRUE(ChloExtractor::Extract(*packet_, versions, {}, &delegate_, + kQuicDefaultConnectionIdLength)) + << ParsedQuicVersionToString(version); + EXPECT_EQ(version.transport_version, delegate_.transport_version()); + EXPECT_EQ(header_.destination_connection_id, delegate_.connection_id()); + EXPECT_EQ(client_hello.DebugString(), delegate_.chlo()) + << ParsedQuicVersionToString(version); + } +} + +TEST_F(ChloExtractorTest, DoesNotFindValidChloOnWrongStream) { + CryptoHandshakeMessage client_hello; + client_hello.set_tag(kCHLO); + + QuicString client_hello_str(client_hello.GetSerialized().AsStringPiece()); + MakePacket(AllSupportedVersions()[0], client_hello_str, + /*munge_offset*/ false, /*munge_stream_id*/ true); + EXPECT_FALSE(ChloExtractor::Extract(*packet_, AllSupportedVersions(), {}, + &delegate_, + kQuicDefaultConnectionIdLength)); +} + +TEST_F(ChloExtractorTest, DoesNotFindValidChloOnWrongOffset) { + CryptoHandshakeMessage client_hello; + client_hello.set_tag(kCHLO); + + QuicString client_hello_str(client_hello.GetSerialized().AsStringPiece()); + MakePacket(AllSupportedVersions()[0], client_hello_str, /*munge_offset*/ true, + /*munge_stream_id*/ false); + EXPECT_FALSE(ChloExtractor::Extract(*packet_, AllSupportedVersions(), {}, + &delegate_, + kQuicDefaultConnectionIdLength)); +} + +TEST_F(ChloExtractorTest, DoesNotFindInvalidChlo) { + MakePacket(AllSupportedVersions()[0], "foo", /*munge_offset*/ false, + /*munge_stream_id*/ true); + EXPECT_FALSE(ChloExtractor::Extract(*packet_, AllSupportedVersions(), {}, + &delegate_, + kQuicDefaultConnectionIdLength)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc new file mode 100644 index 0000000..c60eaa2 --- /dev/null +++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -0,0 +1,183 @@ +// Copyright 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/congestion_control/bandwidth_sampler.h" + +#include <algorithm> + +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { +BandwidthSampler::BandwidthSampler() + : total_bytes_sent_(0), + total_bytes_acked_(0), + total_bytes_sent_at_last_acked_packet_(0), + last_acked_packet_sent_time_(QuicTime::Zero()), + last_acked_packet_ack_time_(QuicTime::Zero()), + is_app_limited_(false), + connection_state_map_() {} + +BandwidthSampler::~BandwidthSampler() {} + +void BandwidthSampler::OnPacketSent( + QuicTime sent_time, + QuicPacketNumber packet_number, + QuicByteCount bytes, + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) { + last_sent_packet_ = packet_number; + + if (has_retransmittable_data != HAS_RETRANSMITTABLE_DATA) { + return; + } + + total_bytes_sent_ += bytes; + + // If there are no packets in flight, the time at which the new transmission + // opens can be treated as the A_0 point for the purpose of bandwidth + // sampling. This underestimates bandwidth to some extent, and produces some + // artificially low samples for most packets in flight, but it provides with + // samples at important points where we would not have them otherwise, most + // importantly at the beginning of the connection. + if (bytes_in_flight == 0) { + last_acked_packet_ack_time_ = sent_time; + total_bytes_sent_at_last_acked_packet_ = total_bytes_sent_; + + // In this situation ack compression is not a concern, set send rate to + // effectively infinite. + last_acked_packet_sent_time_ = sent_time; + } + + if (!connection_state_map_.IsEmpty() && + packet_number > + connection_state_map_.last_packet() + kMaxTrackedPackets) { + QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " + "number " + "of tracked packets."; + } + + bool success = + connection_state_map_.Emplace(packet_number, sent_time, bytes, *this); + QUIC_BUG_IF(!success) << "BandwidthSampler failed to insert the packet " + "into the map, most likely because it's already " + "in it."; +} + +BandwidthSample BandwidthSampler::OnPacketAcknowledged( + QuicTime ack_time, + QuicPacketNumber packet_number) { + ConnectionStateOnSentPacket* sent_packet_pointer = + connection_state_map_.GetEntry(packet_number); + if (sent_packet_pointer == nullptr) { + // See the TODO below. + return BandwidthSample(); + } + BandwidthSample sample = + OnPacketAcknowledgedInner(ack_time, packet_number, *sent_packet_pointer); + connection_state_map_.Remove(packet_number); + return sample; +} + +BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( + QuicTime ack_time, + QuicPacketNumber packet_number, + const ConnectionStateOnSentPacket& sent_packet) { + total_bytes_acked_ += sent_packet.size; + total_bytes_sent_at_last_acked_packet_ = sent_packet.total_bytes_sent; + last_acked_packet_sent_time_ = sent_packet.sent_time; + last_acked_packet_ack_time_ = ack_time; + + // Exit app-limited phase once a packet that was sent while the connection is + // not app-limited is acknowledged. + if (is_app_limited_ && packet_number > end_of_app_limited_phase_) { + is_app_limited_ = false; + } + + // There might have been no packets acknowledged at the moment when the + // current packet was sent. In that case, there is no bandwidth sample to + // make. + if (sent_packet.last_acked_packet_sent_time == QuicTime::Zero()) { + return BandwidthSample(); + } + + // Infinite rate indicates that the sampler is supposed to discard the + // current send rate sample and use only the ack rate. + QuicBandwidth send_rate = QuicBandwidth::Infinite(); + if (sent_packet.sent_time > sent_packet.last_acked_packet_sent_time) { + send_rate = QuicBandwidth::FromBytesAndTimeDelta( + sent_packet.total_bytes_sent - + sent_packet.total_bytes_sent_at_last_acked_packet, + sent_packet.sent_time - sent_packet.last_acked_packet_sent_time); + } + + // During the slope calculation, ensure that ack time of the current packet is + // always larger than the time of the previous packet, otherwise division by + // zero or integer underflow can occur. + if (ack_time <= sent_packet.last_acked_packet_ack_time) { + // TODO(wub): Compare this code count before and after fixing clock jitter + // issue. + if (sent_packet.last_acked_packet_ack_time == sent_packet.sent_time) { + // This is the 1st packet after quiescense. + QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 1, 2); + } else { + QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 2, 2); + } + QUIC_LOG(ERROR) << "Time of the previously acked packet:" + << sent_packet.last_acked_packet_ack_time.ToDebuggingValue() + << " is larger than the ack time of the current packet:" + << ack_time.ToDebuggingValue(); + return BandwidthSample(); + } + QuicBandwidth ack_rate = QuicBandwidth::FromBytesAndTimeDelta( + total_bytes_acked_ - + sent_packet.total_bytes_acked_at_the_last_acked_packet, + ack_time - sent_packet.last_acked_packet_ack_time); + + BandwidthSample sample; + sample.bandwidth = std::min(send_rate, ack_rate); + // Note: this sample does not account for delayed acknowledgement time. This + // means that the RTT measurements here can be artificially high, especially + // on low bandwidth connections. + sample.rtt = ack_time - sent_packet.sent_time; + // A sample is app-limited if the packet was sent during the app-limited + // phase. + sample.is_app_limited = sent_packet.is_app_limited; + return sample; +} + +void BandwidthSampler::OnPacketLost(QuicPacketNumber packet_number) { + // TODO(vasilvv): see the comment for the case of missing packets in + // BandwidthSampler::OnPacketAcknowledged on why this does not raise a + // QUIC_BUG when removal fails. + connection_state_map_.Remove(packet_number); +} + +void BandwidthSampler::OnAppLimited() { + is_app_limited_ = true; + end_of_app_limited_phase_ = last_sent_packet_; +} + +void BandwidthSampler::RemoveObsoletePackets(QuicPacketNumber least_unacked) { + while (!connection_state_map_.IsEmpty() && + connection_state_map_.first_packet() < least_unacked) { + connection_state_map_.Remove(connection_state_map_.first_packet()); + } +} + +QuicByteCount BandwidthSampler::total_bytes_acked() const { + return total_bytes_acked_; +} + +bool BandwidthSampler::is_app_limited() const { + return is_app_limited_; +} + +QuicPacketNumber BandwidthSampler::end_of_app_limited_phase() const { + return end_of_app_limited_phase_; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h new file mode 100644 index 0000000..69aaae7 --- /dev/null +++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -0,0 +1,294 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ + +#include "net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +namespace test { +class BandwidthSamplerPeer; +} // namespace test + +struct QUIC_EXPORT_PRIVATE BandwidthSample { + // The bandwidth at that particular sample. Zero if no valid bandwidth sample + // is available. + QuicBandwidth bandwidth; + + // The RTT measurement at this particular sample. Zero if no RTT sample is + // available. Does not correct for delayed ack time. + QuicTime::Delta rtt; + + // Indicates whether the sample might be artificially low because the sender + // did not have enough data to send in order to saturate the link. + bool is_app_limited; + + BandwidthSample() + : bandwidth(QuicBandwidth::Zero()), + rtt(QuicTime::Delta::Zero()), + is_app_limited(false) {} +}; + +// An interface common to any class that can provide bandwidth samples from the +// information per individual acknowledged packet. +class QUIC_EXPORT_PRIVATE BandwidthSamplerInterface { + public: + virtual ~BandwidthSamplerInterface() {} + + // Inputs the sent packet information into the sampler. Assumes that all + // packets are sent in order. The information about the packet will not be + // released from the sampler until it the packet is either acknowledged or + // declared lost. + virtual void OnPacketSent( + QuicTime sent_time, + QuicPacketNumber packet_number, + QuicByteCount bytes, + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) = 0; + + // Notifies the sampler that the |packet_number| is acknowledged. Returns a + // bandwidth sample. If no bandwidth sample is available, + // QuicBandwidth::Zero() is returned. + virtual BandwidthSample OnPacketAcknowledged( + QuicTime ack_time, + QuicPacketNumber packet_number) = 0; + + // Informs the sampler that a packet is considered lost and it should no + // longer keep track of it. + virtual void OnPacketLost(QuicPacketNumber packet_number) = 0; + + // Informs the sampler that the connection is currently app-limited, causing + // the sampler to enter the app-limited phase. The phase will expire by + // itself. + virtual void OnAppLimited() = 0; + + // Remove all the packets lower than the specified packet number. + virtual void RemoveObsoletePackets(QuicPacketNumber least_unacked) = 0; + + // Total number of bytes currently acknowledged by the receiver. + virtual QuicByteCount total_bytes_acked() const = 0; + + // Application-limited information exported for debugging. + virtual bool is_app_limited() const = 0; + virtual QuicPacketNumber end_of_app_limited_phase() const = 0; +}; + +// BandwidthSampler keeps track of sent and acknowledged packets and outputs a +// bandwidth sample for every packet acknowledged. The samples are taken for +// individual packets, and are not filtered; the consumer has to filter the +// bandwidth samples itself. In certain cases, the sampler will locally severely +// underestimate the bandwidth, hence a maximum filter with a size of at least +// one RTT is recommended. +// +// This class bases its samples on the slope of two curves: the number of bytes +// sent over time, and the number of bytes acknowledged as received over time. +// It produces a sample of both slopes for every packet that gets acknowledged, +// based on a slope between two points on each of the corresponding curves. Note +// that due to the packet loss, the number of bytes on each curve might get +// further and further away from each other, meaning that it is not feasible to +// compare byte values coming from different curves with each other. +// +// The obvious points for measuring slope sample are the ones corresponding to +// the packet that was just acknowledged. Let us denote them as S_1 (point at +// which the current packet was sent) and A_1 (point at which the current packet +// was acknowledged). However, taking a slope requires two points on each line, +// so estimating bandwidth requires picking a packet in the past with respect to +// which the slope is measured. +// +// For that purpose, BandwidthSampler always keeps track of the most recently +// acknowledged packet, and records it together with every outgoing packet. +// When a packet gets acknowledged (A_1), it has not only information about when +// it itself was sent (S_1), but also the information about the latest +// acknowledged packet right before it was sent (S_0 and A_0). +// +// Based on that data, send and ack rate are estimated as: +// send_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0)) +// ack_rate = (bytes(A_1) - bytes(A_0)) / (time(A_1) - time(A_0)) +// +// Here, the ack rate is intuitively the rate we want to treat as bandwidth. +// However, in certain cases (e.g. ack compression) the ack rate at a point may +// end up higher than the rate at which the data was originally sent, which is +// not indicative of the real bandwidth. Hence, we use the send rate as an upper +// bound, and the sample value is +// rate_sample = min(send_rate, ack_rate) +// +// An important edge case handled by the sampler is tracking the app-limited +// samples. There are multiple meaning of "app-limited" used interchangeably, +// hence it is important to understand and to be able to distinguish between +// them. +// +// Meaning 1: connection state. The connection is said to be app-limited when +// there is no outstanding data to send. This means that certain bandwidth +// samples in the future would not be an accurate indication of the link +// capacity, and it is important to inform consumer about that. Whenever +// connection becomes app-limited, the sampler is notified via OnAppLimited() +// method. +// +// Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth +// sampler becomes notified about the connection being app-limited, it enters +// app-limited phase. In that phase, all *sent* packets are marked as +// app-limited. Note that the connection itself does not have to be +// app-limited during the app-limited phase, and in fact it will not be +// (otherwise how would it send packets?). The boolean flag below indicates +// whether the sampler is in that phase. +// +// Meaning 3: a flag on the sent packet and on the sample. If a sent packet is +// sent during the app-limited phase, the resulting sample related to the +// packet will be marked as app-limited. +// +// With the terminology issue out of the way, let us consider the question of +// what kind of situation it addresses. +// +// Consider a scenario where we first send packets 1 to 20 at a regular +// bandwidth, and then immediately run out of data. After a few seconds, we send +// packets 21 to 60, and only receive ack for 21 between sending packets 40 and +// 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0 +// we use to compute the slope is going to be packet 20, a few seconds apart +// from the current packet, hence the resulting estimate would be extremely low +// and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21, +// meaning that the bandwidth sample would exclude the quiescence. +// +// Based on the analysis of that scenario, we implement the following rule: once +// OnAppLimited() is called, all sent packets will produce app-limited samples +// up until an ack for a packet that was sent after OnAppLimited() was called. +// Note that while the scenario above is not the only scenario when the +// connection is app-limited, the approach works in other cases too. +class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { + public: + BandwidthSampler(); + ~BandwidthSampler() override; + + void OnPacketSent(QuicTime sent_time, + QuicPacketNumber packet_number, + QuicByteCount bytes, + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) override; + BandwidthSample OnPacketAcknowledged(QuicTime ack_time, + QuicPacketNumber packet_number) override; + void OnPacketLost(QuicPacketNumber packet_number) override; + + void OnAppLimited() override; + + void RemoveObsoletePackets(QuicPacketNumber least_unacked) override; + + QuicByteCount total_bytes_acked() const override; + bool is_app_limited() const override; + QuicPacketNumber end_of_app_limited_phase() const override; + + private: + friend class test::BandwidthSamplerPeer; + + // ConnectionStateOnSentPacket represents the information about a sent packet + // and the state of the connection at the moment the packet was sent, + // specifically the information about the most recently acknowledged packet at + // that moment. + struct ConnectionStateOnSentPacket { + // Time at which the packet is sent. + QuicTime sent_time; + + // Size of the packet. + QuicByteCount size; + + // The value of |total_bytes_sent_| at the time the packet was sent. + // Includes the packet itself. + QuicByteCount total_bytes_sent; + + // The value of |total_bytes_sent_at_last_acked_packet_| at the time the + // packet was sent. + QuicByteCount total_bytes_sent_at_last_acked_packet; + + // The value of |last_acked_packet_sent_time_| at the time the packet was + // sent. + QuicTime last_acked_packet_sent_time; + + // The value of |last_acked_packet_ack_time_| at the time the packet was + // sent. + QuicTime last_acked_packet_ack_time; + + // The value of |total_bytes_acked_| at the time the packet was + // sent. + QuicByteCount total_bytes_acked_at_the_last_acked_packet; + + // The value of |is_app_limited_| at the time the packet was + // sent. + bool is_app_limited; + + // Snapshot constructor. Records the current state of the bandwidth + // sampler. + ConnectionStateOnSentPacket(QuicTime sent_time, + QuicByteCount size, + const BandwidthSampler& sampler) + : sent_time(sent_time), + size(size), + total_bytes_sent(sampler.total_bytes_sent_), + total_bytes_sent_at_last_acked_packet( + sampler.total_bytes_sent_at_last_acked_packet_), + last_acked_packet_sent_time(sampler.last_acked_packet_sent_time_), + last_acked_packet_ack_time(sampler.last_acked_packet_ack_time_), + total_bytes_acked_at_the_last_acked_packet( + sampler.total_bytes_acked_), + is_app_limited(sampler.is_app_limited_) {} + + // Default constructor. Required to put this structure into + // PacketNumberIndexedQueue. + ConnectionStateOnSentPacket() + : sent_time(QuicTime::Zero()), + size(0), + total_bytes_sent(0), + total_bytes_sent_at_last_acked_packet(0), + last_acked_packet_sent_time(QuicTime::Zero()), + last_acked_packet_ack_time(QuicTime::Zero()), + total_bytes_acked_at_the_last_acked_packet(0), + is_app_limited(false) {} + }; + + // The total number of congestion controlled bytes sent during the connection. + QuicByteCount total_bytes_sent_; + + // The total number of congestion controlled bytes which were acknowledged. + QuicByteCount total_bytes_acked_; + + // The value of |total_bytes_sent_| at the time the last acknowledged packet + // was sent. Valid only when |last_acked_packet_sent_time_| is valid. + QuicByteCount total_bytes_sent_at_last_acked_packet_; + + // The time at which the last acknowledged packet was sent. Set to + // QuicTime::Zero() if no valid timestamp is available. + QuicTime last_acked_packet_sent_time_; + + // The time at which the most recent packet was acknowledged. + QuicTime last_acked_packet_ack_time_; + + // The most recently sent packet. + QuicPacketNumber last_sent_packet_; + + // Indicates whether the bandwidth sampler is currently in an app-limited + // phase. + bool is_app_limited_; + + // The packet that will be acknowledged after this one will cause the sampler + // to exit the app-limited phase. + QuicPacketNumber end_of_app_limited_phase_; + + // Record of the connection state at the point where each packet in flight was + // sent, indexed by the packet number. + PacketNumberIndexedQueue<ConnectionStateOnSentPacket> connection_state_map_; + + // Handles the actual bandwidth calculations, whereas the outer method handles + // retrieving and removing |sent_packet|. + BandwidthSample OnPacketAcknowledgedInner( + QuicTime ack_time, + QuicPacketNumber packet_number, + const ConnectionStateOnSentPacket& sent_packet); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_
diff --git a/quic/core/congestion_control/bandwidth_sampler_test.cc b/quic/core/congestion_control/bandwidth_sampler_test.cc new file mode 100644 index 0000000..e9b74c7 --- /dev/null +++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -0,0 +1,398 @@ +// Copyright 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/congestion_control/bandwidth_sampler.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" + +namespace quic { +namespace test { + +class BandwidthSamplerPeer { + public: + static size_t GetNumberOfTrackedPackets(const BandwidthSampler& sampler) { + return sampler.connection_state_map_.number_of_present_entries(); + } + + static QuicByteCount GetPacketSize(const BandwidthSampler& sampler, + QuicPacketNumber packet_number) { + return sampler.connection_state_map_.GetEntry(packet_number)->size; + } +}; + +const QuicByteCount kRegularPacketSize = 1280; +// Enforce divisibility for some of the tests. +static_assert((kRegularPacketSize & 31) == 0, + "kRegularPacketSize has to be five times divisible by 2"); + +// A test fixture with utility methods for BandwidthSampler tests. +class BandwidthSamplerTest : public QuicTest { + protected: + BandwidthSamplerTest() : bytes_in_flight_(0) { + // Ensure that the clock does not start at zero. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } + + MockClock clock_; + BandwidthSampler sampler_; + QuicByteCount bytes_in_flight_; + + void SendPacketInner(uint64_t packet_number, + QuicByteCount bytes, + HasRetransmittableData has_retransmittable_data) { + sampler_.OnPacketSent(clock_.Now(), QuicPacketNumber(packet_number), bytes, + bytes_in_flight_, has_retransmittable_data); + if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA) { + bytes_in_flight_ += bytes; + } + } + + void SendPacket(uint64_t packet_number) { + SendPacketInner(packet_number, kRegularPacketSize, + HAS_RETRANSMITTABLE_DATA); + } + + BandwidthSample AckPacketInner(uint64_t packet_number) { + QuicByteCount size = BandwidthSamplerPeer::GetPacketSize( + sampler_, QuicPacketNumber(packet_number)); + bytes_in_flight_ -= size; + return sampler_.OnPacketAcknowledged(clock_.Now(), + QuicPacketNumber(packet_number)); + } + + // Acknowledge receipt of a packet and expect it to be not app-limited. + QuicBandwidth AckPacket(uint64_t packet_number) { + BandwidthSample sample = AckPacketInner(packet_number); + EXPECT_FALSE(sample.is_app_limited); + return sample.bandwidth; + } + + void LosePacket(uint64_t packet_number) { + QuicByteCount size = BandwidthSamplerPeer::GetPacketSize( + sampler_, QuicPacketNumber(packet_number)); + bytes_in_flight_ -= size; + sampler_.OnPacketLost(QuicPacketNumber(packet_number)); + } + + // Sends one packet and acks it. Then, send 20 packets. Finally, send + // another 20 packets while acknowledging previous 20. + void Send40PacketsAndAckFirst20(QuicTime::Delta time_between_packets) { + // Send 20 packets at a constant inter-packet time. + for (int i = 1; i <= 20; i++) { + SendPacket(i); + clock_.AdvanceTime(time_between_packets); + } + + // Ack packets 1 to 20, while sending new packets at the same rate as + // before. + for (int i = 1; i <= 20; i++) { + AckPacket(i); + SendPacket(i + 20); + clock_.AdvanceTime(time_between_packets); + } + } +}; + +// Test the sampler in a simple stop-and-wait sender setting. +TEST_F(BandwidthSamplerTest, SendAndWait) { + QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10); + QuicBandwidth expected_bandwidth = + QuicBandwidth::FromBytesPerSecond(kRegularPacketSize * 100); + + // Send packets at the constant bandwidth. + for (int i = 1; i < 20; i++) { + SendPacket(i); + clock_.AdvanceTime(time_between_packets); + QuicBandwidth current_sample = AckPacket(i); + EXPECT_EQ(expected_bandwidth, current_sample); + } + + // Send packets at the exponentially decreasing bandwidth. + for (int i = 20; i < 25; i++) { + time_between_packets = time_between_packets * 2; + expected_bandwidth = expected_bandwidth * 0.5; + + SendPacket(i); + clock_.AdvanceTime(time_between_packets); + QuicBandwidth current_sample = AckPacket(i); + EXPECT_EQ(expected_bandwidth, current_sample); + } + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + EXPECT_EQ(0u, bytes_in_flight_); +} + +// Test the sampler during regular windowed sender scenario with fixed +// CWND of 20. +TEST_F(BandwidthSamplerTest, SendPaced) { + const QuicTime::Delta time_between_packets = + QuicTime::Delta::FromMilliseconds(1); + QuicBandwidth expected_bandwidth = + QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize); + + Send40PacketsAndAckFirst20(time_between_packets); + + // Ack the packets 21 to 40, arriving at the correct bandwidth. + QuicBandwidth last_bandwidth = QuicBandwidth::Zero(); + for (int i = 21; i <= 40; i++) { + last_bandwidth = AckPacket(i); + EXPECT_EQ(expected_bandwidth, last_bandwidth); + clock_.AdvanceTime(time_between_packets); + } + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + EXPECT_EQ(0u, bytes_in_flight_); +} + +// Test the sampler in a scenario where 50% of packets is consistently lost. +TEST_F(BandwidthSamplerTest, SendWithLosses) { + const QuicTime::Delta time_between_packets = + QuicTime::Delta::FromMilliseconds(1); + QuicBandwidth expected_bandwidth = + QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize) * 0.5; + + // Send 20 packets, each 1 ms apart. + for (int i = 1; i <= 20; i++) { + SendPacket(i); + clock_.AdvanceTime(time_between_packets); + } + + // Ack packets 1 to 20, losing every even-numbered packet, while sending new + // packets at the same rate as before. + for (int i = 1; i <= 20; i++) { + if (i % 2 == 0) { + AckPacket(i); + } else { + LosePacket(i); + } + SendPacket(i + 20); + clock_.AdvanceTime(time_between_packets); + } + + // Ack the packets 21 to 40 with the same loss pattern. + QuicBandwidth last_bandwidth = QuicBandwidth::Zero(); + for (int i = 21; i <= 40; i++) { + if (i % 2 == 0) { + last_bandwidth = AckPacket(i); + EXPECT_EQ(expected_bandwidth, last_bandwidth); + } else { + LosePacket(i); + } + clock_.AdvanceTime(time_between_packets); + } + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + EXPECT_EQ(0u, bytes_in_flight_); +} + +// Test the sampler in a scenario where the 50% of packets are not +// congestion controlled (specifically, non-retransmittable data is not +// congestion controlled). Should be functionally consistent in behavior with +// the SendWithLosses test. +TEST_F(BandwidthSamplerTest, NotCongestionControlled) { + const QuicTime::Delta time_between_packets = + QuicTime::Delta::FromMilliseconds(1); + QuicBandwidth expected_bandwidth = + QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize) * 0.5; + + // Send 20 packets, each 1 ms apart. Every even packet is not congestion + // controlled. + for (int i = 1; i <= 20; i++) { + SendPacketInner( + i, kRegularPacketSize, + i % 2 == 0 ? HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA); + clock_.AdvanceTime(time_between_packets); + } + + // Ensure only congestion controlled packets are tracked. + EXPECT_EQ(10u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + + // Ack packets 2 to 21, ignoring every even-numbered packet, while sending new + // packets at the same rate as before. + for (int i = 1; i <= 20; i++) { + if (i % 2 == 0) { + AckPacket(i); + } + SendPacketInner( + i + 20, kRegularPacketSize, + i % 2 == 0 ? HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA); + clock_.AdvanceTime(time_between_packets); + } + + // Ack the packets 22 to 41 with the same congestion controlled pattern. + QuicBandwidth last_bandwidth = QuicBandwidth::Zero(); + for (int i = 21; i <= 40; i++) { + if (i % 2 == 0) { + last_bandwidth = AckPacket(i); + EXPECT_EQ(expected_bandwidth, last_bandwidth); + } + clock_.AdvanceTime(time_between_packets); + } + + // Since only congestion controlled packets are entered into the map, it has + // to be empty at this point. + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + EXPECT_EQ(0u, bytes_in_flight_); +} + +// Simulate a situation where ACKs arrive in burst and earlier than usual, thus +// producing an ACK rate which is higher than the original send rate. +TEST_F(BandwidthSamplerTest, CompressedAck) { + const QuicTime::Delta time_between_packets = + QuicTime::Delta::FromMilliseconds(1); + QuicBandwidth expected_bandwidth = + QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize); + + Send40PacketsAndAckFirst20(time_between_packets); + + // Simulate an RTT somewhat lower than the one for 1-to-21 transmission. + clock_.AdvanceTime(time_between_packets * 15); + + // Ack the packets 21 to 40 almost immediately at once. + QuicBandwidth last_bandwidth = QuicBandwidth::Zero(); + QuicTime::Delta ridiculously_small_time_delta = + QuicTime::Delta::FromMicroseconds(20); + for (int i = 21; i <= 40; i++) { + last_bandwidth = AckPacket(i); + clock_.AdvanceTime(ridiculously_small_time_delta); + } + EXPECT_EQ(expected_bandwidth, last_bandwidth); + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + EXPECT_EQ(0u, bytes_in_flight_); +} + +// Tests receiving ACK packets in the reverse order. +TEST_F(BandwidthSamplerTest, ReorderedAck) { + const QuicTime::Delta time_between_packets = + QuicTime::Delta::FromMilliseconds(1); + QuicBandwidth expected_bandwidth = + QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize); + + Send40PacketsAndAckFirst20(time_between_packets); + + // Ack the packets 21 to 40 in the reverse order, while sending packets 41 to + // 60. + QuicBandwidth last_bandwidth = QuicBandwidth::Zero(); + for (int i = 0; i < 20; i++) { + last_bandwidth = AckPacket(40 - i); + EXPECT_EQ(expected_bandwidth, last_bandwidth); + SendPacket(41 + i); + clock_.AdvanceTime(time_between_packets); + } + + // Ack the packets 41 to 60, now in the regular order. + for (int i = 41; i <= 60; i++) { + last_bandwidth = AckPacket(i); + EXPECT_EQ(expected_bandwidth, last_bandwidth); + clock_.AdvanceTime(time_between_packets); + } + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + EXPECT_EQ(0u, bytes_in_flight_); +} + +// Test the app-limited logic. +TEST_F(BandwidthSamplerTest, AppLimited) { + const QuicTime::Delta time_between_packets = + QuicTime::Delta::FromMilliseconds(1); + QuicBandwidth expected_bandwidth = + QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize); + + Send40PacketsAndAckFirst20(time_between_packets); + + // We are now app-limited. Ack 21 to 40 as usual, but do not send anything for + // now. + sampler_.OnAppLimited(); + for (int i = 21; i <= 40; i++) { + QuicBandwidth current_sample = AckPacket(i); + EXPECT_EQ(expected_bandwidth, current_sample); + clock_.AdvanceTime(time_between_packets); + } + + // Enter quiescence. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + + // Send packets 41 to 60, all of which would be marked as app-limited. + for (int i = 41; i <= 60; i++) { + SendPacket(i); + clock_.AdvanceTime(time_between_packets); + } + + // Ack packets 41 to 60, while sending packets 61 to 80. 41 to 60 should be + // app-limited and underestimate the bandwidth due to that. + for (int i = 41; i <= 60; i++) { + BandwidthSample sample = AckPacketInner(i); + EXPECT_TRUE(sample.is_app_limited); + EXPECT_LT(sample.bandwidth, 0.7f * expected_bandwidth); + + SendPacket(i + 20); + clock_.AdvanceTime(time_between_packets); + } + + // Run out of packets, and then ack packet 61 to 80, all of which should have + // correct non-app-limited samples. + for (int i = 61; i <= 80; i++) { + QuicBandwidth last_bandwidth = AckPacket(i); + EXPECT_EQ(expected_bandwidth, last_bandwidth); + clock_.AdvanceTime(time_between_packets); + } + + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + EXPECT_EQ(0u, bytes_in_flight_); +} + +// Test the samples taken at the first flight of packets sent. +TEST_F(BandwidthSamplerTest, FirstRoundTrip) { + const QuicTime::Delta time_between_packets = + QuicTime::Delta::FromMilliseconds(1); + const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(800); + const int num_packets = 10; + const QuicByteCount num_bytes = kRegularPacketSize * num_packets; + const QuicBandwidth real_bandwidth = + QuicBandwidth::FromBytesAndTimeDelta(num_bytes, rtt); + + for (int i = 1; i <= 10; i++) { + SendPacket(i); + clock_.AdvanceTime(time_between_packets); + } + + clock_.AdvanceTime(rtt - num_packets * time_between_packets); + + QuicBandwidth last_sample = QuicBandwidth::Zero(); + for (int i = 1; i <= 10; i++) { + QuicBandwidth sample = AckPacket(i); + EXPECT_GT(sample, last_sample); + last_sample = sample; + clock_.AdvanceTime(time_between_packets); + } + + // The final measured sample for the first flight of sample is expected to be + // smaller than the real bandwidth, yet it should not lose more than 10%. The + // specific value of the error depends on the difference between the RTT and + // the time it takes to exhaust the congestion window (i.e. in the limit when + // all packets are sent simultaneously, last sample would indicate the real + // bandwidth). + EXPECT_LT(last_sample, real_bandwidth); + EXPECT_GT(last_sample, 0.9f * real_bandwidth); +} + +// Test sampler's ability to remove obsolete packets. +TEST_F(BandwidthSamplerTest, RemoveObsoletePackets) { + SendPacket(1); + SendPacket(2); + SendPacket(3); + SendPacket(4); + SendPacket(5); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + + EXPECT_EQ(5u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + sampler_.RemoveObsoletePackets(QuicPacketNumber(4)); + EXPECT_EQ(2u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + sampler_.OnPacketLost(QuicPacketNumber(4)); + EXPECT_EQ(1u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); + AckPacket(5); + EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc new file mode 100644 index 0000000..6a7fe2b --- /dev/null +++ b/quic/core/congestion_control/bbr_sender.cc
@@ -0,0 +1,923 @@ +// Copyright 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/congestion_control/bbr_sender.h" + +#include <algorithm> +#include <sstream> + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace { +// Constants based on TCP defaults. +// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements. +// Does not inflate the pacing rate. +const QuicByteCount kDefaultMinimumCongestionWindow = 4 * kMaxSegmentSize; + +// The gain used for the STARTUP, equal to 2/ln(2). +const float kDefaultHighGain = 2.885f; +// The newly derived gain for STARTUP, equal to 4 * ln(2) +const float kDerivedHighGain = 2.773f; +// The newly derived CWND gain for STARTUP, 2. +const float kDerivedHighCWNDGain = 2.773f; +// The gain used in STARTUP after loss has been detected. +// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth +// in measured bandwidth. +const float kStartupAfterLossGain = 1.5f; +// The cycle of gains used during the PROBE_BW stage. +const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1}; + +// The length of the gain cycle. +const size_t kGainCycleLength = sizeof(kPacingGain) / sizeof(kPacingGain[0]); +// The size of the bandwidth filter window, in round-trips. +const QuicRoundTripCount kBandwidthWindowSize = kGainCycleLength + 2; + +// The time after which the current min_rtt value expires. +const QuicTime::Delta kMinRttExpiry = QuicTime::Delta::FromSeconds(10); +// The minimum time the connection can spend in PROBE_RTT mode. +const QuicTime::Delta kProbeRttTime = QuicTime::Delta::FromMilliseconds(200); +// If the bandwidth does not increase by the factor of |kStartupGrowthTarget| +// within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection +// will exit the STARTUP mode. +const float kStartupGrowthTarget = 1.25; +const QuicRoundTripCount kRoundTripsWithoutGrowthBeforeExitingStartup = 3; +// Coefficient of target congestion window to use when basing PROBE_RTT on BDP. +const float kModerateProbeRttMultiplier = 0.75; +// Coefficient to determine if a new RTT is sufficiently similar to min_rtt that +// we don't need to enter PROBE_RTT. +const float kSimilarMinRttThreshold = 1.125; + +} // namespace + +BbrSender::DebugState::DebugState(const BbrSender& sender) + : mode(sender.mode_), + max_bandwidth(sender.max_bandwidth_.GetBest()), + round_trip_count(sender.round_trip_count_), + gain_cycle_index(sender.cycle_current_offset_), + congestion_window(sender.congestion_window_), + is_at_full_bandwidth(sender.is_at_full_bandwidth_), + bandwidth_at_last_round(sender.bandwidth_at_last_round_), + rounds_without_bandwidth_gain(sender.rounds_without_bandwidth_gain_), + min_rtt(sender.min_rtt_), + min_rtt_timestamp(sender.min_rtt_timestamp_), + recovery_state(sender.recovery_state_), + recovery_window(sender.recovery_window_), + last_sample_is_app_limited(sender.last_sample_is_app_limited_), + end_of_app_limited_phase(sender.sampler_.end_of_app_limited_phase()) {} + +BbrSender::DebugState::DebugState(const DebugState& state) = default; + +BbrSender::BbrSender(const RttStats* rtt_stats, + const QuicUnackedPacketMap* unacked_packets, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_tcp_congestion_window, + QuicRandom* random) + : rtt_stats_(rtt_stats), + unacked_packets_(unacked_packets), + random_(random), + mode_(STARTUP), + round_trip_count_(0), + max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0), + max_ack_height_(kBandwidthWindowSize, 0, 0), + aggregation_epoch_start_time_(QuicTime::Zero()), + aggregation_epoch_bytes_(0), + min_rtt_(QuicTime::Delta::Zero()), + min_rtt_timestamp_(QuicTime::Zero()), + congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS), + initial_congestion_window_(initial_tcp_congestion_window * + kDefaultTCPMSS), + max_congestion_window_(max_tcp_congestion_window * kDefaultTCPMSS), + min_congestion_window_(kDefaultMinimumCongestionWindow), + high_gain_(kDefaultHighGain), + high_cwnd_gain_(kDefaultHighGain), + drain_gain_(1.f / kDefaultHighGain), + pacing_rate_(QuicBandwidth::Zero()), + pacing_gain_(1), + congestion_window_gain_(1), + congestion_window_gain_constant_( + static_cast<float>(FLAGS_quic_bbr_cwnd_gain)), + num_startup_rtts_(kRoundTripsWithoutGrowthBeforeExitingStartup), + exit_startup_on_loss_(false), + cycle_current_offset_(0), + last_cycle_start_(QuicTime::Zero()), + is_at_full_bandwidth_(false), + rounds_without_bandwidth_gain_(0), + bandwidth_at_last_round_(QuicBandwidth::Zero()), + exiting_quiescence_(false), + exit_probe_rtt_at_(QuicTime::Zero()), + probe_rtt_round_passed_(false), + last_sample_is_app_limited_(false), + has_non_app_limited_sample_(false), + flexible_app_limited_(false), + recovery_state_(NOT_IN_RECOVERY), + recovery_window_(max_congestion_window_), + is_app_limited_recovery_(false), + slower_startup_(false), + rate_based_startup_(false), + startup_rate_reduction_multiplier_(0), + startup_bytes_lost_(0), + enable_ack_aggregation_during_startup_(false), + expire_ack_aggregation_in_startup_(false), + drain_to_target_(false), + probe_rtt_based_on_bdp_(false), + probe_rtt_skipped_if_similar_rtt_(false), + probe_rtt_disabled_if_app_limited_(false), + app_limited_since_last_probe_rtt_(false), + min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()) { + EnterStartupMode(); +} + +BbrSender::~BbrSender() {} + +void BbrSender::SetInitialCongestionWindowInPackets( + QuicPacketCount congestion_window) { + if (mode_ == STARTUP) { + initial_congestion_window_ = congestion_window * kDefaultTCPMSS; + congestion_window_ = congestion_window * kDefaultTCPMSS; + } +} + +bool BbrSender::InSlowStart() const { + return mode_ == STARTUP; +} + +void BbrSender::OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) { + last_sent_packet_ = packet_number; + + if (bytes_in_flight == 0 && sampler_.is_app_limited()) { + exiting_quiescence_ = true; + } + + if (!aggregation_epoch_start_time_.IsInitialized()) { + aggregation_epoch_start_time_ = sent_time; + } + + sampler_.OnPacketSent(sent_time, packet_number, bytes, bytes_in_flight, + is_retransmittable); +} + +bool BbrSender::CanSend(QuicByteCount bytes_in_flight) { + return bytes_in_flight < GetCongestionWindow(); +} + +QuicBandwidth BbrSender::PacingRate(QuicByteCount bytes_in_flight) const { + if (pacing_rate_.IsZero()) { + return high_gain_ * QuicBandwidth::FromBytesAndTimeDelta( + initial_congestion_window_, GetMinRtt()); + } + return pacing_rate_; +} + +QuicBandwidth BbrSender::BandwidthEstimate() const { + return max_bandwidth_.GetBest(); +} + +QuicByteCount BbrSender::GetCongestionWindow() const { + if (mode_ == PROBE_RTT) { + return ProbeRttCongestionWindow(); + } + + if (InRecovery() && !(rate_based_startup_ && mode_ == STARTUP)) { + return std::min(congestion_window_, recovery_window_); + } + + return congestion_window_; +} + +QuicByteCount BbrSender::GetSlowStartThreshold() const { + return 0; +} + +bool BbrSender::InRecovery() const { + return recovery_state_ != NOT_IN_RECOVERY; +} + +bool BbrSender::ShouldSendProbingPacket() const { + if (pacing_gain_ <= 1) { + return false; + } + + // TODO(b/77975811): If the pipe is highly under-utilized, consider not + // sending a probing transmission, because the extra bandwidth is not needed. + // If flexible_app_limited is enabled, check if the pipe is sufficiently full. + if (flexible_app_limited_) { + return !IsPipeSufficientlyFull(); + } else { + return true; + } +} + +bool BbrSender::IsPipeSufficientlyFull() const { + // See if we need more bytes in flight to see more bandwidth. + if (mode_ == STARTUP) { + // STARTUP exits if it doesn't observe a 25% bandwidth increase, so the CWND + // must be more than 25% above the target. + return unacked_packets_->bytes_in_flight() >= + GetTargetCongestionWindow(1.5); + } + if (pacing_gain_ > 1) { + // Super-unity PROBE_BW doesn't exit until 1.25 * BDP is achieved. + return unacked_packets_->bytes_in_flight() >= + GetTargetCongestionWindow(pacing_gain_); + } + // If bytes_in_flight are above the target congestion window, it should be + // possible to observe the same or more bandwidth if it's available. + return unacked_packets_->bytes_in_flight() >= GetTargetCongestionWindow(1.1); +} + +void BbrSender::SetFromConfig(const QuicConfig& config, + Perspective perspective) { + if (config.HasClientRequestedIndependentOption(kLRTT, perspective)) { + exit_startup_on_loss_ = true; + } + if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) { + num_startup_rtts_ = 1; + } + if (config.HasClientRequestedIndependentOption(k2RTT, perspective)) { + num_startup_rtts_ = 2; + } + if (config.HasClientRequestedIndependentOption(kBBRS, perspective)) { + slower_startup_ = true; + } + if (config.HasClientRequestedIndependentOption(kBBR3, perspective)) { + drain_to_target_ = true; + } + if (config.HasClientRequestedIndependentOption(kBBS1, perspective)) { + rate_based_startup_ = true; + } + if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) && + config.HasClientRequestedIndependentOption(kBBS4, perspective)) { + rate_based_startup_ = true; + // Hits 1.25x pacing multiplier when ~2/3 CWND is lost. + startup_rate_reduction_multiplier_ = 1; + } + if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) && + config.HasClientRequestedIndependentOption(kBBS5, perspective)) { + rate_based_startup_ = true; + // Hits 1.25x pacing multiplier when ~1/3 CWND is lost. + startup_rate_reduction_multiplier_ = 2; + } + if (config.HasClientRequestedIndependentOption(kBBR4, perspective)) { + max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize); + } + if (config.HasClientRequestedIndependentOption(kBBR5, perspective)) { + max_ack_height_.SetWindowLength(4 * kBandwidthWindowSize); + } + if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) && + config.HasClientRequestedIndependentOption(kBBR6, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 1, 3); + probe_rtt_based_on_bdp_ = true; + } + if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) && + config.HasClientRequestedIndependentOption(kBBR7, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 2, 3); + probe_rtt_skipped_if_similar_rtt_ = true; + } + if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) && + config.HasClientRequestedIndependentOption(kBBR8, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 3, 3); + probe_rtt_disabled_if_app_limited_ = true; + } + if (GetQuicReloadableFlag(quic_bbr_flexible_app_limited) && + config.HasClientRequestedIndependentOption(kBBR9, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_flexible_app_limited); + flexible_app_limited_ = true; + } + if (GetQuicReloadableFlag(quic_bbr_slower_startup3) && + config.HasClientRequestedIndependentOption(kBBQ1, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 1, 4); + set_high_gain(kDerivedHighGain); + set_high_cwnd_gain(kDerivedHighGain); + set_drain_gain(1.f / kDerivedHighGain); + } + if (GetQuicReloadableFlag(quic_bbr_slower_startup3) && + config.HasClientRequestedIndependentOption(kBBQ2, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 2, 4); + set_high_cwnd_gain(kDerivedHighCWNDGain); + } + if (GetQuicReloadableFlag(quic_bbr_slower_startup3) && + config.HasClientRequestedIndependentOption(kBBQ3, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 3, 4); + enable_ack_aggregation_during_startup_ = true; + } + if (GetQuicReloadableFlag(quic_bbr_slower_startup3) && + config.HasClientRequestedIndependentOption(kBBQ4, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 4, 4); + set_drain_gain(kModerateProbeRttMultiplier); + } + if (GetQuicReloadableFlag(quic_bbr_slower_startup4) && + config.HasClientRequestedIndependentOption(kBBQ5, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_slower_startup4); + expire_ack_aggregation_in_startup_ = true; + } + if (config.HasClientRequestedIndependentOption(kMIN1, perspective)) { + min_congestion_window_ = kMaxSegmentSize; + } +} + +void BbrSender::AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + if (!bandwidth.IsZero()) { + max_bandwidth_.Update(bandwidth, round_trip_count_); + } + if (!rtt.IsZero() && (min_rtt_ > rtt || min_rtt_.IsZero())) { + min_rtt_ = rtt; + } +} + +void BbrSender::OnCongestionEvent(bool /*rtt_updated*/, + QuicByteCount prior_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets) { + const QuicByteCount total_bytes_acked_before = sampler_.total_bytes_acked(); + + bool is_round_start = false; + bool min_rtt_expired = false; + + DiscardLostPackets(lost_packets); + + // Input the new data into the BBR model of the connection. + QuicByteCount excess_acked = 0; + if (!acked_packets.empty()) { + QuicPacketNumber last_acked_packet = acked_packets.rbegin()->packet_number; + is_round_start = UpdateRoundTripCounter(last_acked_packet); + min_rtt_expired = UpdateBandwidthAndMinRtt(event_time, acked_packets); + UpdateRecoveryState(last_acked_packet, !lost_packets.empty(), + is_round_start); + + const QuicByteCount bytes_acked = + sampler_.total_bytes_acked() - total_bytes_acked_before; + + excess_acked = UpdateAckAggregationBytes(event_time, bytes_acked); + } + + // Handle logic specific to PROBE_BW mode. + if (mode_ == PROBE_BW) { + UpdateGainCyclePhase(event_time, prior_in_flight, !lost_packets.empty()); + } + + // Handle logic specific to STARTUP and DRAIN modes. + if (is_round_start && !is_at_full_bandwidth_) { + CheckIfFullBandwidthReached(); + } + MaybeExitStartupOrDrain(event_time); + + // Handle logic specific to PROBE_RTT. + MaybeEnterOrExitProbeRtt(event_time, is_round_start, min_rtt_expired); + + // Calculate number of packets acked and lost. + QuicByteCount bytes_acked = + sampler_.total_bytes_acked() - total_bytes_acked_before; + QuicByteCount bytes_lost = 0; + for (const auto& packet : lost_packets) { + bytes_lost += packet.bytes_lost; + } + + // After the model is updated, recalculate the pacing rate and congestion + // window. + CalculatePacingRate(); + CalculateCongestionWindow(bytes_acked, excess_acked); + CalculateRecoveryWindow(bytes_acked, bytes_lost); + + // Cleanup internal state. + sampler_.RemoveObsoletePackets(unacked_packets_->GetLeastUnacked()); +} + +CongestionControlType BbrSender::GetCongestionControlType() const { + return kBBR; +} + +QuicTime::Delta BbrSender::GetMinRtt() const { + return !min_rtt_.IsZero() ? min_rtt_ : rtt_stats_->initial_rtt(); +} + +QuicByteCount BbrSender::GetTargetCongestionWindow(float gain) const { + QuicByteCount bdp = GetMinRtt() * BandwidthEstimate(); + QuicByteCount congestion_window = gain * bdp; + + // BDP estimate will be zero if no bandwidth samples are available yet. + if (congestion_window == 0) { + congestion_window = gain * initial_congestion_window_; + } + + return std::max(congestion_window, min_congestion_window_); +} + +QuicByteCount BbrSender::ProbeRttCongestionWindow() const { + if (probe_rtt_based_on_bdp_) { + return GetTargetCongestionWindow(kModerateProbeRttMultiplier); + } + return min_congestion_window_; +} + +void BbrSender::EnterStartupMode() { + mode_ = STARTUP; + pacing_gain_ = high_gain_; + congestion_window_gain_ = high_cwnd_gain_; +} + +void BbrSender::EnterProbeBandwidthMode(QuicTime now) { + mode_ = PROBE_BW; + congestion_window_gain_ = congestion_window_gain_constant_; + + // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is + // excluded because in that case increased gain and decreased gain would not + // follow each other. + cycle_current_offset_ = random_->RandUint64() % (kGainCycleLength - 1); + if (cycle_current_offset_ >= 1) { + cycle_current_offset_ += 1; + } + + last_cycle_start_ = now; + pacing_gain_ = kPacingGain[cycle_current_offset_]; +} + +void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) { + for (const LostPacket& packet : lost_packets) { + sampler_.OnPacketLost(packet.packet_number); + if (startup_rate_reduction_multiplier_ != 0 && mode_ == STARTUP) { + startup_bytes_lost_ += packet.bytes_lost; + } + } +} + +bool BbrSender::UpdateRoundTripCounter(QuicPacketNumber last_acked_packet) { + if (!current_round_trip_end_.IsInitialized() || + last_acked_packet > current_round_trip_end_) { + round_trip_count_++; + current_round_trip_end_ = last_sent_packet_; + return true; + } + + return false; +} + +bool BbrSender::UpdateBandwidthAndMinRtt( + QuicTime now, + const AckedPacketVector& acked_packets) { + QuicTime::Delta sample_min_rtt = QuicTime::Delta::Infinite(); + for (const auto& packet : acked_packets) { + if (packet.bytes_acked == 0) { + // Skip acked packets with 0 in flight bytes when updating bandwidth. + continue; + } + BandwidthSample bandwidth_sample = + sampler_.OnPacketAcknowledged(now, packet.packet_number); + last_sample_is_app_limited_ = bandwidth_sample.is_app_limited; + has_non_app_limited_sample_ |= !bandwidth_sample.is_app_limited; + if (!bandwidth_sample.rtt.IsZero()) { + sample_min_rtt = std::min(sample_min_rtt, bandwidth_sample.rtt); + } + + if (!bandwidth_sample.is_app_limited || + bandwidth_sample.bandwidth > BandwidthEstimate()) { + max_bandwidth_.Update(bandwidth_sample.bandwidth, round_trip_count_); + } + } + + // If none of the RTT samples are valid, return immediately. + if (sample_min_rtt.IsInfinite()) { + return false; + } + min_rtt_since_last_probe_rtt_ = + std::min(min_rtt_since_last_probe_rtt_, sample_min_rtt); + + // Do not expire min_rtt if none was ever available. + bool min_rtt_expired = + !min_rtt_.IsZero() && (now > (min_rtt_timestamp_ + kMinRttExpiry)); + + if (min_rtt_expired || sample_min_rtt < min_rtt_ || min_rtt_.IsZero()) { + QUIC_DVLOG(2) << "Min RTT updated, old value: " << min_rtt_ + << ", new value: " << sample_min_rtt + << ", current time: " << now.ToDebuggingValue(); + + if (min_rtt_expired && ShouldExtendMinRttExpiry()) { + min_rtt_expired = false; + } else { + min_rtt_ = sample_min_rtt; + } + min_rtt_timestamp_ = now; + // Reset since_last_probe_rtt fields. + min_rtt_since_last_probe_rtt_ = QuicTime::Delta::Infinite(); + app_limited_since_last_probe_rtt_ = false; + } + DCHECK(!min_rtt_.IsZero()); + + return min_rtt_expired; +} + +bool BbrSender::ShouldExtendMinRttExpiry() const { + if (probe_rtt_disabled_if_app_limited_ && app_limited_since_last_probe_rtt_) { + // Extend the current min_rtt if we've been app limited recently. + return true; + } + const bool min_rtt_increased_since_last_probe = + min_rtt_since_last_probe_rtt_ > min_rtt_ * kSimilarMinRttThreshold; + if (probe_rtt_skipped_if_similar_rtt_ && app_limited_since_last_probe_rtt_ && + !min_rtt_increased_since_last_probe) { + // Extend the current min_rtt if we've been app limited recently and an rtt + // has been measured in that time that's less than 12.5% more than the + // current min_rtt. + return true; + } + return false; +} + +void BbrSender::UpdateGainCyclePhase(QuicTime now, + QuicByteCount prior_in_flight, + bool has_losses) { + const QuicByteCount bytes_in_flight = unacked_packets_->bytes_in_flight(); + // In most cases, the cycle is advanced after an RTT passes. + bool should_advance_gain_cycling = now - last_cycle_start_ > GetMinRtt(); + + // If the pacing gain is above 1.0, the connection is trying to probe the + // bandwidth by increasing the number of bytes in flight to at least + // pacing_gain * BDP. Make sure that it actually reaches the target, as long + // as there are no losses suggesting that the buffers are not able to hold + // that much. + if (pacing_gain_ > 1.0 && !has_losses && + prior_in_flight < GetTargetCongestionWindow(pacing_gain_)) { + should_advance_gain_cycling = false; + } + + // If pacing gain is below 1.0, the connection is trying to drain the extra + // queue which could have been incurred by probing prior to it. If the number + // of bytes in flight falls down to the estimated BDP value earlier, conclude + // that the queue has been successfully drained and exit this cycle early. + if (pacing_gain_ < 1.0 && bytes_in_flight <= GetTargetCongestionWindow(1)) { + should_advance_gain_cycling = true; + } + + if (should_advance_gain_cycling) { + cycle_current_offset_ = (cycle_current_offset_ + 1) % kGainCycleLength; + last_cycle_start_ = now; + // Stay in low gain mode until the target BDP is hit. + // Low gain mode will be exited immediately when the target BDP is achieved. + if (drain_to_target_ && pacing_gain_ < 1 && + kPacingGain[cycle_current_offset_] == 1 && + bytes_in_flight > GetTargetCongestionWindow(1)) { + return; + } + pacing_gain_ = kPacingGain[cycle_current_offset_]; + } +} + +void BbrSender::CheckIfFullBandwidthReached() { + if (last_sample_is_app_limited_) { + return; + } + + QuicBandwidth target = bandwidth_at_last_round_ * kStartupGrowthTarget; + if (BandwidthEstimate() >= target) { + bandwidth_at_last_round_ = BandwidthEstimate(); + rounds_without_bandwidth_gain_ = 0; + if (expire_ack_aggregation_in_startup_) { + // Expire old excess delivery measurements now that bandwidth increased. + max_ack_height_.Reset(0, round_trip_count_); + } + return; + } + + rounds_without_bandwidth_gain_++; + if ((rounds_without_bandwidth_gain_ >= num_startup_rtts_) || + (exit_startup_on_loss_ && InRecovery())) { + DCHECK(has_non_app_limited_sample_); + is_at_full_bandwidth_ = true; + } +} + +void BbrSender::MaybeExitStartupOrDrain(QuicTime now) { + if (mode_ == STARTUP && is_at_full_bandwidth_) { + mode_ = DRAIN; + pacing_gain_ = drain_gain_; + congestion_window_gain_ = high_cwnd_gain_; + } + if (mode_ == DRAIN && + unacked_packets_->bytes_in_flight() <= GetTargetCongestionWindow(1)) { + EnterProbeBandwidthMode(now); + } +} + +void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now, + bool is_round_start, + bool min_rtt_expired) { + if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) { + mode_ = PROBE_RTT; + pacing_gain_ = 1; + // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| + // is at the target small value. + exit_probe_rtt_at_ = QuicTime::Zero(); + } + + if (mode_ == PROBE_RTT) { + sampler_.OnAppLimited(); + + if (exit_probe_rtt_at_ == QuicTime::Zero()) { + // If the window has reached the appropriate size, schedule exiting + // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but + // we allow an extra packet since QUIC checks CWND before sending a + // packet. + if (unacked_packets_->bytes_in_flight() < + ProbeRttCongestionWindow() + kMaxPacketSize) { + exit_probe_rtt_at_ = now + kProbeRttTime; + probe_rtt_round_passed_ = false; + } + } else { + if (is_round_start) { + probe_rtt_round_passed_ = true; + } + if (now >= exit_probe_rtt_at_ && probe_rtt_round_passed_) { + min_rtt_timestamp_ = now; + if (!is_at_full_bandwidth_) { + EnterStartupMode(); + } else { + EnterProbeBandwidthMode(now); + } + } + } + } + + exiting_quiescence_ = false; +} + +void BbrSender::UpdateRecoveryState(QuicPacketNumber last_acked_packet, + bool has_losses, + bool is_round_start) { + // Exit recovery when there are no losses for a round. + if (has_losses) { + end_recovery_at_ = last_sent_packet_; + } + + switch (recovery_state_) { + case NOT_IN_RECOVERY: + // Enter conservation on the first loss. + if (has_losses) { + recovery_state_ = CONSERVATION; + // This will cause the |recovery_window_| to be set to the correct + // value in CalculateRecoveryWindow(). + recovery_window_ = 0; + // Since the conservation phase is meant to be lasting for a whole + // round, extend the current round as if it were started right now. + current_round_trip_end_ = last_sent_packet_; + if (GetQuicReloadableFlag(quic_bbr_app_limited_recovery) && + last_sample_is_app_limited_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_app_limited_recovery); + is_app_limited_recovery_ = true; + } + } + break; + + case CONSERVATION: + if (is_round_start) { + recovery_state_ = GROWTH; + } + QUIC_FALLTHROUGH_INTENDED; + + case GROWTH: + // Exit recovery if appropriate. + if (!has_losses && last_acked_packet > end_recovery_at_) { + recovery_state_ = NOT_IN_RECOVERY; + is_app_limited_recovery_ = false; + } + + break; + } + if (recovery_state_ != NOT_IN_RECOVERY && is_app_limited_recovery_) { + sampler_.OnAppLimited(); + } +} + +// TODO(ianswett): Move this logic into BandwidthSampler. +QuicByteCount BbrSender::UpdateAckAggregationBytes( + QuicTime ack_time, + QuicByteCount newly_acked_bytes) { + // Compute how many bytes are expected to be delivered, assuming max bandwidth + // is correct. + QuicByteCount expected_bytes_acked = + max_bandwidth_.GetBest() * (ack_time - aggregation_epoch_start_time_); + // Reset the current aggregation epoch as soon as the ack arrival rate is less + // than or equal to the max bandwidth. + if (aggregation_epoch_bytes_ <= expected_bytes_acked) { + // Reset to start measuring a new aggregation epoch. + aggregation_epoch_bytes_ = newly_acked_bytes; + aggregation_epoch_start_time_ = ack_time; + return 0; + } + + // Compute how many extra bytes were delivered vs max bandwidth. + // Include the bytes most recently acknowledged to account for stretch acks. + aggregation_epoch_bytes_ += newly_acked_bytes; + max_ack_height_.Update(aggregation_epoch_bytes_ - expected_bytes_acked, + round_trip_count_); + return aggregation_epoch_bytes_ - expected_bytes_acked; +} + +void BbrSender::CalculatePacingRate() { + if (BandwidthEstimate().IsZero()) { + return; + } + + QuicBandwidth target_rate = pacing_gain_ * BandwidthEstimate(); + if (is_at_full_bandwidth_) { + pacing_rate_ = target_rate; + return; + } + + // Pace at the rate of initial_window / RTT as soon as RTT measurements are + // available. + if (pacing_rate_.IsZero() && !rtt_stats_->min_rtt().IsZero()) { + pacing_rate_ = QuicBandwidth::FromBytesAndTimeDelta( + initial_congestion_window_, rtt_stats_->min_rtt()); + return; + } + // Slow the pacing rate in STARTUP once loss has ever been detected. + const bool has_ever_detected_loss = end_recovery_at_.IsInitialized(); + if (slower_startup_ && has_ever_detected_loss && + has_non_app_limited_sample_) { + pacing_rate_ = kStartupAfterLossGain * BandwidthEstimate(); + return; + } + + // Slow the pacing rate in STARTUP by the bytes_lost / CWND. + if (startup_rate_reduction_multiplier_ != 0 && has_ever_detected_loss && + has_non_app_limited_sample_) { + pacing_rate_ = + (1 - (startup_bytes_lost_ * startup_rate_reduction_multiplier_ * 1.0f / + congestion_window_)) * + target_rate; + // Ensure the pacing rate doesn't drop below the startup growth target times + // the bandwidth estimate. + pacing_rate_ = + std::max(pacing_rate_, kStartupGrowthTarget * BandwidthEstimate()); + return; + } + + // Do not decrease the pacing rate during startup. + pacing_rate_ = std::max(pacing_rate_, target_rate); +} + +void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked, + QuicByteCount excess_acked) { + if (mode_ == PROBE_RTT) { + return; + } + + QuicByteCount target_window = + GetTargetCongestionWindow(congestion_window_gain_); + if (is_at_full_bandwidth_) { + // Add the max recently measured ack aggregation to CWND. + target_window += max_ack_height_.GetBest(); + } else if (enable_ack_aggregation_during_startup_) { + // Add the most recent excess acked. Because CWND never decreases in + // STARTUP, this will automatically create a very localized max filter. + target_window += excess_acked; + } + + // Instead of immediately setting the target CWND as the new one, BBR grows + // the CWND towards |target_window| by only increasing it |bytes_acked| at a + // time. + const bool add_bytes_acked = + !GetQuicReloadableFlag(quic_bbr_no_bytes_acked_in_startup_recovery) || + !InRecovery(); + if (is_at_full_bandwidth_) { + congestion_window_ = + std::min(target_window, congestion_window_ + bytes_acked); + } else if (add_bytes_acked && + (congestion_window_ < target_window || + sampler_.total_bytes_acked() < initial_congestion_window_)) { + // If the connection is not yet out of startup phase, do not decrease the + // window. + congestion_window_ = congestion_window_ + bytes_acked; + } + + // Enforce the limits on the congestion window. + congestion_window_ = std::max(congestion_window_, min_congestion_window_); + congestion_window_ = std::min(congestion_window_, max_congestion_window_); +} + +void BbrSender::CalculateRecoveryWindow(QuicByteCount bytes_acked, + QuicByteCount bytes_lost) { + if (rate_based_startup_ && mode_ == STARTUP) { + return; + } + + if (recovery_state_ == NOT_IN_RECOVERY) { + return; + } + + // Set up the initial recovery window. + if (recovery_window_ == 0) { + recovery_window_ = unacked_packets_->bytes_in_flight() + bytes_acked; + recovery_window_ = std::max(min_congestion_window_, recovery_window_); + return; + } + + // Remove losses from the recovery window, while accounting for a potential + // integer underflow. + recovery_window_ = recovery_window_ >= bytes_lost + ? recovery_window_ - bytes_lost + : kMaxSegmentSize; + + // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH, + // release additional |bytes_acked| to achieve a slow-start-like behavior. + if (recovery_state_ == GROWTH) { + recovery_window_ += bytes_acked; + } + + // Sanity checks. Ensure that we always allow to send at least an MSS or + // |bytes_acked| in response, whichever is larger. + recovery_window_ = std::max( + recovery_window_, unacked_packets_->bytes_in_flight() + bytes_acked); + if (GetQuicReloadableFlag(quic_bbr_one_mss_conservation)) { + recovery_window_ = + std::max(recovery_window_, + unacked_packets_->bytes_in_flight() + kMaxSegmentSize); + } + recovery_window_ = std::max(min_congestion_window_, recovery_window_); +} + +QuicString BbrSender::GetDebugState() const { + std::ostringstream stream; + stream << ExportDebugState(); + return stream.str(); +} + +void BbrSender::OnApplicationLimited(QuicByteCount bytes_in_flight) { + if (bytes_in_flight >= GetCongestionWindow()) { + return; + } + if (flexible_app_limited_ && IsPipeSufficientlyFull()) { + return; + } + + app_limited_since_last_probe_rtt_ = true; + sampler_.OnAppLimited(); + QUIC_DVLOG(2) << "Becoming application limited. Last sent packet: " + << last_sent_packet_ << ", CWND: " << GetCongestionWindow(); +} + +BbrSender::DebugState BbrSender::ExportDebugState() const { + return DebugState(*this); +} + +static QuicString ModeToString(BbrSender::Mode mode) { + switch (mode) { + case BbrSender::STARTUP: + return "STARTUP"; + case BbrSender::DRAIN: + return "DRAIN"; + case BbrSender::PROBE_BW: + return "PROBE_BW"; + case BbrSender::PROBE_RTT: + return "PROBE_RTT"; + } + return "???"; +} + +std::ostream& operator<<(std::ostream& os, const BbrSender::Mode& mode) { + os << ModeToString(mode); + return os; +} + +std::ostream& operator<<(std::ostream& os, const BbrSender::DebugState& state) { + os << "Mode: " << ModeToString(state.mode) << std::endl; + os << "Maximum bandwidth: " << state.max_bandwidth << std::endl; + os << "Round trip counter: " << state.round_trip_count << std::endl; + os << "Gain cycle index: " << static_cast<int>(state.gain_cycle_index) + << std::endl; + os << "Congestion window: " << state.congestion_window << " bytes" + << std::endl; + + if (state.mode == BbrSender::STARTUP) { + os << "(startup) Bandwidth at last round: " << state.bandwidth_at_last_round + << std::endl; + os << "(startup) Rounds without gain: " + << state.rounds_without_bandwidth_gain << std::endl; + } + + os << "Minimum RTT: " << state.min_rtt << std::endl; + os << "Minimum RTT timestamp: " << state.min_rtt_timestamp.ToDebuggingValue() + << std::endl; + + os << "Last sample is app-limited: " + << (state.last_sample_is_app_limited ? "yes" : "no"); + + return os; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h new file mode 100644 index 0000000..37e2695 --- /dev/null +++ b/quic/core/congestion_control/bbr_sender.h
@@ -0,0 +1,408 @@ +// Copyright 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. + +// BBR (Bottleneck Bandwidth and RTT) congestion control algorithm. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_ + +#include <cstdint> +#include <ostream> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class RttStats; + +typedef uint64_t QuicRoundTripCount; + +// BbrSender implements BBR congestion control algorithm. BBR aims to estimate +// the current available Bottleneck Bandwidth and RTT (hence the name), and +// regulates the pacing rate and the size of the congestion window based on +// those signals. +// +// BBR relies on pacing in order to function properly. Do not use BBR when +// pacing is disabled. +// +// TODO(vasilvv): implement traffic policer (long-term sampling) mode. +class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { + public: + enum Mode { + // Startup phase of the connection. + STARTUP, + // After achieving the highest possible bandwidth during the startup, lower + // the pacing rate in order to drain the queue. + DRAIN, + // Cruising mode. + PROBE_BW, + // Temporarily slow down sending in order to empty the buffer and measure + // the real minimum RTT. + PROBE_RTT, + }; + + // Indicates how the congestion control limits the amount of bytes in flight. + enum RecoveryState { + // Do not limit. + NOT_IN_RECOVERY, + // Allow an extra outstanding byte for each byte acknowledged. + CONSERVATION, + // Allow two extra outstanding bytes for each byte acknowledged (slow + // start). + GROWTH + }; + + // Debug state can be exported in order to troubleshoot potential congestion + // control issues. + struct DebugState { + explicit DebugState(const BbrSender& sender); + DebugState(const DebugState& state); + + Mode mode; + QuicBandwidth max_bandwidth; + QuicRoundTripCount round_trip_count; + int gain_cycle_index; + QuicByteCount congestion_window; + + bool is_at_full_bandwidth; + QuicBandwidth bandwidth_at_last_round; + QuicRoundTripCount rounds_without_bandwidth_gain; + + QuicTime::Delta min_rtt; + QuicTime min_rtt_timestamp; + + RecoveryState recovery_state; + QuicByteCount recovery_window; + + bool last_sample_is_app_limited; + QuicPacketNumber end_of_app_limited_phase; + }; + + BbrSender(const RttStats* rtt_stats, + const QuicUnackedPacketMap* unacked_packets, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_tcp_congestion_window, + QuicRandom* random); + BbrSender(const BbrSender&) = delete; + BbrSender& operator=(const BbrSender&) = delete; + ~BbrSender() override; + + // Start implementation of SendAlgorithmInterface. + bool InSlowStart() const override; + bool InRecovery() const override; + bool ShouldSendProbingPacket() const override; + + void SetFromConfig(const QuicConfig& config, + Perspective perspective) override; + + void AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) override; + void SetNumEmulatedConnections(int num_connections) override {} + void SetInitialCongestionWindowInPackets( + QuicPacketCount congestion_window) override; + void OnCongestionEvent(bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets) override; + void OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) override; + void OnRetransmissionTimeout(bool packets_retransmitted) override {} + void OnConnectionMigration() override {} + bool CanSend(QuicByteCount bytes_in_flight) override; + QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const override; + QuicBandwidth BandwidthEstimate() const override; + QuicByteCount GetCongestionWindow() const override; + QuicByteCount GetSlowStartThreshold() const override; + CongestionControlType GetCongestionControlType() const override; + QuicString GetDebugState() const override; + void OnApplicationLimited(QuicByteCount bytes_in_flight) override; + // End implementation of SendAlgorithmInterface. + + // Gets the number of RTTs BBR remains in STARTUP phase. + QuicRoundTripCount num_startup_rtts() const { return num_startup_rtts_; } + bool has_non_app_limited_sample() const { + return has_non_app_limited_sample_; + } + + // Sets the pacing gain used in STARTUP. Must be greater than 1. + void set_high_gain(float high_gain) { + DCHECK_LT(1.0f, high_gain); + high_gain_ = high_gain; + if (mode_ == STARTUP) { + pacing_gain_ = high_gain; + } + } + + // Sets the CWND gain used in STARTUP. Must be greater than 1. + void set_high_cwnd_gain(float high_cwnd_gain) { + DCHECK_LT(1.0f, high_cwnd_gain); + high_cwnd_gain_ = high_cwnd_gain; + if (mode_ == STARTUP) { + congestion_window_gain_ = high_cwnd_gain; + } + } + + // Sets the gain used in DRAIN. Must be less than 1. + void set_drain_gain(float drain_gain) { + DCHECK_GT(1.0f, drain_gain); + drain_gain_ = drain_gain; + } + + DebugState ExportDebugState() const; + + private: + typedef WindowedFilter<QuicBandwidth, + MaxFilter<QuicBandwidth>, + QuicRoundTripCount, + QuicRoundTripCount> + MaxBandwidthFilter; + + typedef WindowedFilter<QuicByteCount, + MaxFilter<QuicByteCount>, + QuicRoundTripCount, + QuicRoundTripCount> + MaxAckHeightFilter; + + // Returns the current estimate of the RTT of the connection. Outside of the + // edge cases, this is minimum RTT. + QuicTime::Delta GetMinRtt() const; + // Returns whether the connection has achieved full bandwidth required to exit + // the slow start. + bool IsAtFullBandwidth() const; + // Computes the target congestion window using the specified gain. + QuicByteCount GetTargetCongestionWindow(float gain) const; + // The target congestion window during PROBE_RTT. + QuicByteCount ProbeRttCongestionWindow() const; + // Returns true if the current min_rtt should be kept and we should not enter + // PROBE_RTT immediately. + bool ShouldExtendMinRttExpiry() const; + + // Enters the STARTUP mode. + void EnterStartupMode(); + // Enters the PROBE_BW mode. + void EnterProbeBandwidthMode(QuicTime now); + + // Discards the lost packets from BandwidthSampler state. + void DiscardLostPackets(const LostPacketVector& lost_packets); + // Updates the round-trip counter if a round-trip has passed. Returns true if + // the counter has been advanced. + bool UpdateRoundTripCounter(QuicPacketNumber last_acked_packet); + // Updates the current bandwidth and min_rtt estimate based on the samples for + // the received acknowledgements. Returns true if min_rtt has expired. + bool UpdateBandwidthAndMinRtt(QuicTime now, + const AckedPacketVector& acked_packets); + // Updates the current gain used in PROBE_BW mode. + void UpdateGainCyclePhase(QuicTime now, + QuicByteCount prior_in_flight, + bool has_losses); + // Tracks for how many round-trips the bandwidth has not increased + // significantly. + void CheckIfFullBandwidthReached(); + // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if + // appropriate. + void MaybeExitStartupOrDrain(QuicTime now); + // Decides whether to enter or exit PROBE_RTT. + void MaybeEnterOrExitProbeRtt(QuicTime now, + bool is_round_start, + bool min_rtt_expired); + // Determines whether BBR needs to enter, exit or advance state of the + // recovery. + void UpdateRecoveryState(QuicPacketNumber last_acked_packet, + bool has_losses, + bool is_round_start); + + // Updates the ack aggregation max filter in bytes. + // Returns the most recent addition to the filter, or |newly_acked_bytes| if + // nothing was fed in to the filter. + QuicByteCount UpdateAckAggregationBytes(QuicTime ack_time, + QuicByteCount newly_acked_bytes); + + // Determines the appropriate pacing rate for the connection. + void CalculatePacingRate(); + // Determines the appropriate congestion window for the connection. + void CalculateCongestionWindow(QuicByteCount bytes_acked, + QuicByteCount excess_acked); + // Determines the approriate window that constrains the in-flight during + // recovery. + void CalculateRecoveryWindow(QuicByteCount bytes_acked, + QuicByteCount bytes_lost); + + // Returns true if there are enough bytes in flight to ensure more bandwidth + // will be observed if present. + bool IsPipeSufficientlyFull() const; + + const RttStats* rtt_stats_; + const QuicUnackedPacketMap* unacked_packets_; + QuicRandom* random_; + + Mode mode_; + + // Bandwidth sampler provides BBR with the bandwidth measurements at + // individual points. + BandwidthSampler sampler_; + + // The number of the round trips that have occurred during the connection. + QuicRoundTripCount round_trip_count_; + + // The packet number of the most recently sent packet. + QuicPacketNumber last_sent_packet_; + // Acknowledgement of any packet after |current_round_trip_end_| will cause + // the round trip counter to advance. + QuicPacketNumber current_round_trip_end_; + + // The filter that tracks the maximum bandwidth over the multiple recent + // round-trips. + MaxBandwidthFilter max_bandwidth_; + + // Tracks the maximum number of bytes acked faster than the sending rate. + MaxAckHeightFilter max_ack_height_; + + // The time this aggregation started and the number of bytes acked during it. + QuicTime aggregation_epoch_start_time_; + QuicByteCount aggregation_epoch_bytes_; + + // Minimum RTT estimate. Automatically expires within 10 seconds (and + // triggers PROBE_RTT mode) if no new value is sampled during that period. + QuicTime::Delta min_rtt_; + // The time at which the current value of |min_rtt_| was assigned. + QuicTime min_rtt_timestamp_; + + // The maximum allowed number of bytes in flight. + QuicByteCount congestion_window_; + + // The initial value of the |congestion_window_|. + QuicByteCount initial_congestion_window_; + + // The largest value the |congestion_window_| can achieve. + QuicByteCount max_congestion_window_; + + // The smallest value the |congestion_window_| can achieve. + QuicByteCount min_congestion_window_; + + // The pacing gain applied during the STARTUP phase. + float high_gain_; + + // The CWND gain applied during the STARTUP phase. + float high_cwnd_gain_; + + // The pacing gain applied during the DRAIN phase. + float drain_gain_; + + // The current pacing rate of the connection. + QuicBandwidth pacing_rate_; + + // The gain currently applied to the pacing rate. + float pacing_gain_; + // The gain currently applied to the congestion window. + float congestion_window_gain_; + + // The gain used for the congestion window during PROBE_BW. Latched from + // quic_bbr_cwnd_gain flag. + const float congestion_window_gain_constant_; + // The number of RTTs to stay in STARTUP mode. Defaults to 3. + QuicRoundTripCount num_startup_rtts_; + // If true, exit startup if 1RTT has passed with no bandwidth increase and + // the connection is in recovery. + bool exit_startup_on_loss_; + + // Number of round-trips in PROBE_BW mode, used for determining the current + // pacing gain cycle. + int cycle_current_offset_; + // The time at which the last pacing gain cycle was started. + QuicTime last_cycle_start_; + + // Indicates whether the connection has reached the full bandwidth mode. + bool is_at_full_bandwidth_; + // Number of rounds during which there was no significant bandwidth increase. + QuicRoundTripCount rounds_without_bandwidth_gain_; + // The bandwidth compared to which the increase is measured. + QuicBandwidth bandwidth_at_last_round_; + + // Set to true upon exiting quiescence. + bool exiting_quiescence_; + + // Time at which PROBE_RTT has to be exited. Setting it to zero indicates + // that the time is yet unknown as the number of packets in flight has not + // reached the required value. + QuicTime exit_probe_rtt_at_; + // Indicates whether a round-trip has passed since PROBE_RTT became active. + bool probe_rtt_round_passed_; + + // Indicates whether the most recent bandwidth sample was marked as + // app-limited. + bool last_sample_is_app_limited_; + // Indicates whether any non app-limited samples have been recorded. + bool has_non_app_limited_sample_; + // Indicates app-limited calls should be ignored as long as there's + // enough data inflight to see more bandwidth when necessary. + bool flexible_app_limited_; + + // Current state of recovery. + RecoveryState recovery_state_; + // Receiving acknowledgement of a packet after |end_recovery_at_| will cause + // BBR to exit the recovery mode. A value above zero indicates at least one + // loss has been detected, so it must not be set back to zero. + QuicPacketNumber end_recovery_at_; + // A window used to limit the number of bytes in flight during loss recovery. + QuicByteCount recovery_window_; + // If true, consider all samples in recovery app-limited. + bool is_app_limited_recovery_; + + // When true, pace at 1.5x and disable packet conservation in STARTUP. + bool slower_startup_; + // When true, disables packet conservation in STARTUP. + bool rate_based_startup_; + // When non-zero, decreases the rate in STARTUP by the total number of bytes + // lost in STARTUP divided by CWND. + uint8_t startup_rate_reduction_multiplier_; + // Sum of bytes lost in STARTUP. + QuicByteCount startup_bytes_lost_; + + // When true, add the most recent ack aggregation measurement during STARTUP. + bool enable_ack_aggregation_during_startup_; + // When true, expire the windowed ack aggregation values in STARTUP when + // bandwidth increases more than 25%. + bool expire_ack_aggregation_in_startup_; + + // If true, will not exit low gain mode until bytes_in_flight drops below BDP + // or it's time for high gain mode. + bool drain_to_target_; + + // If true, use a CWND of 0.75*BDP during probe_rtt instead of 4 packets. + bool probe_rtt_based_on_bdp_; + // If true, skip probe_rtt and update the timestamp of the existing min_rtt to + // now if min_rtt over the last cycle is within 12.5% of the current min_rtt. + // Even if the min_rtt is 12.5% too low, the 25% gain cycling and 2x CWND gain + // should overcome an overly small min_rtt. + bool probe_rtt_skipped_if_similar_rtt_; + // If true, disable PROBE_RTT entirely as long as the connection was recently + // app limited. + bool probe_rtt_disabled_if_app_limited_; + bool app_limited_since_last_probe_rtt_; + QuicTime::Delta min_rtt_since_last_probe_rtt_; +}; + +QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const BbrSender::Mode& mode); +QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const BbrSender::DebugState& state); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc new file mode 100644 index 0000000..205cf65 --- /dev/null +++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -0,0 +1,1314 @@ +// Copyright 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/congestion_control/bbr_sender.h" + +#include <algorithm> +#include <map> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" + +namespace quic { +namespace test { + +// Use the initial CWND of 10, as 32 is too much for the test network. +const uint32_t kInitialCongestionWindowPackets = 10; +const uint32_t kDefaultWindowTCP = + kInitialCongestionWindowPackets * kDefaultTCPMSS; + +// Test network parameters. Here, the topology of the network is: +// +// BBR sender +// | +// | <-- local link (10 Mbps, 2 ms delay) +// | +// Network switch +// * <-- the bottleneck queue in the direction +// | of the receiver +// | +// | <-- test link (4 Mbps, 30 ms delay) +// | +// | +// Receiver +// +// The reason the bandwidths chosen are relatively low is the fact that the +// connection simulator uses QuicTime for its internal clock, and as such has +// the granularity of 1us, meaning that at bandwidth higher than 20 Mbps the +// packets can start to land on the same timestamp. +const QuicBandwidth kTestLinkBandwidth = + QuicBandwidth::FromKBitsPerSecond(4000); +const QuicBandwidth kLocalLinkBandwidth = + QuicBandwidth::FromKBitsPerSecond(10000); +const QuicTime::Delta kTestPropagationDelay = + QuicTime::Delta::FromMilliseconds(30); +const QuicTime::Delta kLocalPropagationDelay = + QuicTime::Delta::FromMilliseconds(2); +const QuicTime::Delta kTestTransferTime = + kTestLinkBandwidth.TransferTime(kMaxPacketSize) + + kLocalLinkBandwidth.TransferTime(kMaxPacketSize); +const QuicTime::Delta kTestRtt = + (kTestPropagationDelay + kLocalPropagationDelay + kTestTransferTime) * 2; +const QuicByteCount kTestBdp = kTestRtt * kTestLinkBandwidth; + +class BbrSenderTest : public QuicTest { + protected: + BbrSenderTest() + : simulator_(), + bbr_sender_(&simulator_, + "BBR sender", + "Receiver", + Perspective::IS_CLIENT, + /*connection_id=*/TestConnectionId(42)), + competing_sender_(&simulator_, + "Competing sender", + "Competing receiver", + Perspective::IS_CLIENT, + /*connection_id=*/TestConnectionId(43)), + receiver_(&simulator_, + "Receiver", + "BBR sender", + Perspective::IS_SERVER, + /*connection_id=*/TestConnectionId(42)), + competing_receiver_(&simulator_, + "Competing receiver", + "Competing sender", + Perspective::IS_SERVER, + /*connection_id=*/TestConnectionId(43)), + receiver_multiplexer_("Receiver multiplexer", + {&receiver_, &competing_receiver_}) { + rtt_stats_ = bbr_sender_.connection()->sent_packet_manager().GetRttStats(); + sender_ = SetupBbrSender(&bbr_sender_); + + clock_ = simulator_.GetClock(); + simulator_.set_random_generator(&random_); + + uint64_t seed = QuicRandom::GetInstance()->RandUint64(); + random_.set_seed(seed); + QUIC_LOG(INFO) << "BbrSenderTest simulator set up. Seed: " << seed; + } + + simulator::Simulator simulator_; + simulator::QuicEndpoint bbr_sender_; + simulator::QuicEndpoint competing_sender_; + simulator::QuicEndpoint receiver_; + simulator::QuicEndpoint competing_receiver_; + simulator::QuicEndpointMultiplexer receiver_multiplexer_; + std::unique_ptr<simulator::Switch> switch_; + std::unique_ptr<simulator::SymmetricLink> bbr_sender_link_; + std::unique_ptr<simulator::SymmetricLink> competing_sender_link_; + std::unique_ptr<simulator::SymmetricLink> receiver_link_; + + SimpleRandom random_; + + // Owned by different components of the connection. + const QuicClock* clock_; + const RttStats* rtt_stats_; + BbrSender* sender_; + + // Enables BBR on |endpoint| and returns the associated BBR congestion + // controller. + BbrSender* SetupBbrSender(simulator::QuicEndpoint* endpoint) { + const RttStats* rtt_stats = + endpoint->connection()->sent_packet_manager().GetRttStats(); + // Ownership of the sender will be overtaken by the endpoint. + BbrSender* sender = new BbrSender( + rtt_stats, + QuicSentPacketManagerPeer::GetUnackedPacketMap( + QuicConnectionPeer::GetSentPacketManager(endpoint->connection())), + kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets, + &random_); + QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender); + endpoint->RecordTrace(); + return sender; + } + + // Creates a default setup, which is a network with a bottleneck between the + // receiver and the switch. The switch has the buffers four times larger than + // the bottleneck BDP, which should guarantee a lack of losses. + void CreateDefaultSetup() { + switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8, + 2 * kTestBdp); + bbr_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &bbr_sender_, switch_->port(1), kLocalLinkBandwidth, + kLocalPropagationDelay); + receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &receiver_, switch_->port(2), kTestLinkBandwidth, + kTestPropagationDelay); + } + + // Same as the default setup, except the buffer now is half of the BDP. + void CreateSmallBufferSetup() { + switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8, + 0.5 * kTestBdp); + bbr_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &bbr_sender_, switch_->port(1), kLocalLinkBandwidth, + kTestPropagationDelay); + receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &receiver_, switch_->port(2), kTestLinkBandwidth, + kTestPropagationDelay); + } + + // Creates the variation of the default setup in which there is another sender + // that competes for the same bottleneck link. + void CreateCompetitionSetup() { + switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8, + 2 * kTestBdp); + + // Add a small offset to the competing link in order to avoid + // synchronization effects. + const QuicTime::Delta small_offset = QuicTime::Delta::FromMicroseconds(3); + bbr_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &bbr_sender_, switch_->port(1), kLocalLinkBandwidth, + kLocalPropagationDelay); + competing_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &competing_sender_, switch_->port(3), kLocalLinkBandwidth, + kLocalPropagationDelay + small_offset); + receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &receiver_multiplexer_, switch_->port(2), kTestLinkBandwidth, + kTestPropagationDelay); + } + + // Creates a BBR vs BBR competition setup. + void CreateBbrVsBbrSetup() { + SetupBbrSender(&competing_sender_); + CreateCompetitionSetup(); + } + + void EnableAggregation(QuicByteCount aggregation_bytes, + QuicTime::Delta aggregation_timeout) { + // Enable aggregation on the path from the receiver to the sender. + switch_->port_queue(1)->EnableAggregation(aggregation_bytes, + aggregation_timeout); + } + + void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta deadline) { + bbr_sender_.AddBytesToTransfer(transfer_size); + // TODO(vasilvv): consider rewriting this to run until the receiver actually + // receives the intended amount of bytes. + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return bbr_sender_.bytes_to_transfer() == 0; }, deadline); + EXPECT_TRUE(simulator_result) + << "Simple transfer failed. Bytes remaining: " + << bbr_sender_.bytes_to_transfer(); + QUIC_LOG(INFO) << "Simple transfer state: " << sender_->ExportDebugState(); + } + + // Drive the simulator by sending enough data to enter PROBE_BW. + void DriveOutOfStartup() { + ASSERT_FALSE(sender_->ExportDebugState().is_at_full_bandwidth); + DoSimpleTransfer(1024 * 1024, QuicTime::Delta::FromSeconds(15)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + ExpectApproxEq(kTestLinkBandwidth, + sender_->ExportDebugState().max_bandwidth, 0.02f); + } + + // Send |bytes|-sized bursts of data |number_of_bursts| times, waiting for + // |wait_time| between each burst. + void SendBursts(size_t number_of_bursts, + QuicByteCount bytes, + QuicTime::Delta wait_time) { + ASSERT_EQ(0u, bbr_sender_.bytes_to_transfer()); + for (size_t i = 0; i < number_of_bursts; i++) { + bbr_sender_.AddBytesToTransfer(bytes); + + // Transfer data and wait for three seconds between each transfer. + simulator_.RunFor(wait_time); + + // Ensure the connection did not time out. + ASSERT_TRUE(bbr_sender_.connection()->connected()); + ASSERT_TRUE(receiver_.connection()->connected()); + } + + simulator_.RunFor(wait_time + kTestRtt); + ASSERT_EQ(0u, bbr_sender_.bytes_to_transfer()); + } + + void SetConnectionOption(QuicTag option) { + QuicConfig config; + QuicTagVector options; + options.push_back(option); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + } +}; + +TEST_F(BbrSenderTest, SetInitialCongestionWindow) { + EXPECT_NE(3u * kDefaultTCPMSS, sender_->GetCongestionWindow()); + sender_->SetInitialCongestionWindowInPackets(3); + EXPECT_EQ(3u * kDefaultTCPMSS, sender_->GetCongestionWindow()); +} + +// Test a simple long data transfer in the default setup. +TEST_F(BbrSenderTest, SimpleTransfer) { + // Disable Ack Decimation on the receiver, because it can increase srtt. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + CreateDefaultSetup(); + + // At startup make sure we are at the default. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + // At startup make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + // And that window is un-affected. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + + // Verify that Sender is in slow start. + EXPECT_TRUE(sender_->InSlowStart()); + + // Verify that pacing rate is based on the initial RTT. + QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta( + 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt()); + ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(), + sender_->PacingRate(0).ToBitsPerSecond(), 0.01f); + + ASSERT_GE(kTestBdp, kDefaultWindowTCP + kDefaultTCPMSS); + + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); + + // The margin here is quite high, since there exists a possibility that the + // connection just exited high gain cycle. + ExpectApproxEq(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f); +} + +// Test a simple transfer in a situation when the buffer is less than BDP. +TEST_F(BbrSenderTest, SimpleTransferSmallBuffer) { + CreateSmallBufferSetup(); + + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth, + 0.01f); + EXPECT_GE(bbr_sender_.connection()->GetStats().packets_lost, 0u); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +TEST_F(BbrSenderTest, SimpleTransferEarlyPacketLoss) { + SetQuicReloadableFlag(quic_bbr_no_bytes_acked_in_startup_recovery, true); + // Enable rate based startup so the recovery window doesn't hide the true + // congestion_window_ in GetCongestionWindow(). + SetConnectionOption(kBBS1); + // Disable Ack Decimation on the receiver, because it can increase srtt. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + CreateDefaultSetup(); + + // At startup make sure we are at the default. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + // Verify that Sender is in slow start. + EXPECT_TRUE(sender_->InSlowStart()); + // At startup make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + // And that window is un-affected. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + + // Transfer 12MB. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + // Drop the first packet. + receiver_.DropNextIncomingPacket(); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + if (sender_->InRecovery()) { + // Two packets are acked before the first is declared lost. + EXPECT_LE(sender_->GetCongestionWindow(), + (kDefaultWindowTCP + 2 * kDefaultTCPMSS)); + } + return bbr_sender_.bytes_to_transfer() == 0 || !sender_->InSlowStart(); + }, + QuicTime::Delta::FromSeconds(30)); + EXPECT_TRUE(simulator_result) << "Simple transfer failed. Bytes remaining: " + << bbr_sender_.bytes_to_transfer(); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(1u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Test a simple long data transfer with 2 rtts of aggregation. +TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) { + CreateDefaultSetup(); + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * kTestRtt); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + // It's possible to read a bandwidth as much as 50% too high with aggregation. + EXPECT_LE(kTestLinkBandwidth * 0.99f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Tighten this bound once we understand why BBR is + // overestimating bandwidth with aggregation. b/36022633 + EXPECT_GE(kTestLinkBandwidth * 1.5f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures + // bandwidth higher than the link rate. + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt()); + ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.2f); +} + +// Test a simple long data transfer with 2 rtts of aggregation. +TEST_F(BbrSenderTest, SimpleTransferAckDecimation) { + // Decrease the CWND gain so extra CWND is required with stretch acks. + FLAGS_quic_bbr_cwnd_gain = 1.0; + sender_ = new BbrSender( + rtt_stats_, + QuicSentPacketManagerPeer::GetUnackedPacketMap( + QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())), + kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets, + &random_); + QuicConnectionPeer::SetSendAlgorithm(bbr_sender_.connection(), sender_); + // Enable Ack Decimation on the receiver. + QuicConnectionPeer::SetAckMode(receiver_.connection(), + AckMode::ACK_DECIMATION); + CreateDefaultSetup(); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + // It's possible to read a bandwidth as much as 50% too high with aggregation. + EXPECT_LE(kTestLinkBandwidth * 0.99f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Tighten this bound once we understand why BBR is + // overestimating bandwidth with aggregation. b/36022633 + EXPECT_GE(kTestLinkBandwidth * 1.5f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures + // bandwidth higher than the link rate. + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(kTestRtt * 2, rtt_stats_->smoothed_rtt()); + ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.1f); +} + +// Test a simple long data transfer with 2 rtts of aggregation. +TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes20RTTWindow) { + // Disable Ack Decimation on the receiver, because it can increase srtt. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + CreateDefaultSetup(); + SetConnectionOption(kBBR4); + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * kTestRtt); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + // It's possible to read a bandwidth as much as 50% too high with aggregation. + EXPECT_LE(kTestLinkBandwidth * 0.99f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Tighten this bound once we understand why BBR is + // overestimating bandwidth with aggregation. b/36022633 + EXPECT_GE(kTestLinkBandwidth * 1.5f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures + // bandwidth higher than the link rate. + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt()); + ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.12f); +} + +// Test a simple long data transfer with 2 rtts of aggregation. +TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes40RTTWindow) { + // Disable Ack Decimation on the receiver, because it can increase srtt. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + CreateDefaultSetup(); + SetConnectionOption(kBBR5); + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * kTestRtt); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + // It's possible to read a bandwidth as much as 50% too high with aggregation. + EXPECT_LE(kTestLinkBandwidth * 0.99f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Tighten this bound once we understand why BBR is + // overestimating bandwidth with aggregation. b/36022633 + EXPECT_GE(kTestLinkBandwidth * 1.5f, + sender_->ExportDebugState().max_bandwidth); + // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures + // bandwidth higher than the link rate. + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt()); + ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.12f); +} + +// Test the number of losses incurred by the startup phase in a situation when +// the buffer is less than BDP. +TEST_F(BbrSenderTest, PacketLossOnSmallBufferStartup) { + CreateSmallBufferSetup(); + + DriveOutOfStartup(); + float loss_rate = + static_cast<float>(bbr_sender_.connection()->GetStats().packets_lost) / + bbr_sender_.connection()->GetStats().packets_sent; + EXPECT_LE(loss_rate, 0.31); +} + +// Ensures the code transitions loss recovery states correctly (NOT_IN_RECOVERY +// -> CONSERVATION -> GROWTH -> NOT_IN_RECOVERY). +TEST_F(BbrSenderTest, RecoveryStates) { + // Set seed to the position where the gain cycling causes the sender go + // into conservation upon entering PROBE_BW. + // + // TODO(vasilvv): there should be a better way to test this. + random_.set_seed(UINT64_C(14719894707049085006)); + + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10); + bool simulator_result; + CreateSmallBufferSetup(); + + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + ASSERT_EQ(BbrSender::NOT_IN_RECOVERY, + sender_->ExportDebugState().recovery_state); + + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().recovery_state != + BbrSender::NOT_IN_RECOVERY; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::CONSERVATION, + sender_->ExportDebugState().recovery_state); + + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().recovery_state != + BbrSender::CONSERVATION; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::GROWTH, sender_->ExportDebugState().recovery_state); + + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().recovery_state != BbrSender::GROWTH; + }, + timeout); + + ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + ASSERT_EQ(BbrSender::NOT_IN_RECOVERY, + sender_->ExportDebugState().recovery_state); + ASSERT_TRUE(simulator_result); +} + +// Verify the behavior of the algorithm in the case when the connection sends +// small bursts of data after sending continuously for a while. +TEST_F(BbrSenderTest, ApplicationLimitedBursts) { + CreateDefaultSetup(); + + DriveOutOfStartup(); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); + + SendBursts(20, 512, QuicTime::Delta::FromSeconds(3)); + EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited); + ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth, + 0.01f); +} + +// Verify the behavior of the algorithm in the case when the connection sends +// small bursts of data and then starts sending continuously. +TEST_F(BbrSenderTest, ApplicationLimitedBurstsWithoutPrior) { + CreateDefaultSetup(); + + SendBursts(40, 512, QuicTime::Delta::FromSeconds(3)); + EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited); + + DriveOutOfStartup(); + ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth, + 0.01f); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Verify that the DRAIN phase works correctly. +TEST_F(BbrSenderTest, Drain) { + // Disable Ack Decimation on the receiver, because it can increase srtt. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + CreateDefaultSetup(); + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10); + // Get the queue at the bottleneck, which is the outgoing queue at the port to + // which the receiver is connected. + const simulator::Queue* queue = switch_->port_queue(2); + bool simulator_result; + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Run the startup, and verify that it fills up the queue. + ASSERT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode); + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode != BbrSender::STARTUP; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + ExpectApproxEq(sender_->BandwidthEstimate() * (1 / 2.885f), + sender_->PacingRate(0), 0.01f); + // BBR uses CWND gain of 2.88 during STARTUP, hence it will fill the buffer + // with approximately 1.88 BDPs. Here, we use 1.5 to give some margin for + // error. + EXPECT_GE(queue->bytes_queued(), 1.5 * kTestBdp); + + // Observe increased RTT due to bufferbloat. + const QuicTime::Delta queueing_delay = + kTestLinkBandwidth.TransferTime(queue->bytes_queued()); + ExpectApproxEq(kTestRtt + queueing_delay, rtt_stats_->latest_rtt(), 0.1f); + + // Transition to the drain phase and verify that it makes the queue + // have at most a BDP worth of packets. + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_->ExportDebugState().mode != BbrSender::DRAIN; }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_LE(queue->bytes_queued(), kTestBdp); + + // Wait for a few round trips and ensure we're in appropriate phase of gain + // cycling before taking an RTT measurement. + const QuicRoundTripCount start_round_trip = + sender_->ExportDebugState().round_trip_count; + simulator_result = simulator_.RunUntilOrTimeout( + [this, start_round_trip]() { + QuicRoundTripCount rounds_passed = + sender_->ExportDebugState().round_trip_count - start_round_trip; + return rounds_passed >= 4 && + sender_->ExportDebugState().gain_cycle_index == 7; + }, + timeout); + ASSERT_TRUE(simulator_result); + + // Observe the bufferbloat go away. + ExpectApproxEq(kTestRtt, rtt_stats_->smoothed_rtt(), 0.1f); +} + +// Verify that the DRAIN phase works correctly. +TEST_F(BbrSenderTest, ShallowDrain) { + SetQuicReloadableFlag(quic_bbr_slower_startup3, true); + // Disable Ack Decimation on the receiver, because it can increase srtt. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + + CreateDefaultSetup(); + // BBQ4 increases the pacing gain in DRAIN to 0.75 + SetConnectionOption(kBBQ4); + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10); + // Get the queue at the bottleneck, which is the outgoing queue at the port to + // which the receiver is connected. + const simulator::Queue* queue = switch_->port_queue(2); + bool simulator_result; + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Run the startup, and verify that it fills up the queue. + ASSERT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode); + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode != BbrSender::STARTUP; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(0.75 * sender_->BandwidthEstimate(), sender_->PacingRate(0)); + // BBR uses CWND gain of 2.88 during STARTUP, hence it will fill the buffer + // with approximately 1.88 BDPs. Here, we use 1.5 to give some margin for + // error. + EXPECT_GE(queue->bytes_queued(), 1.5 * kTestBdp); + + // Observe increased RTT due to bufferbloat. + const QuicTime::Delta queueing_delay = + kTestLinkBandwidth.TransferTime(queue->bytes_queued()); + ExpectApproxEq(kTestRtt + queueing_delay, rtt_stats_->latest_rtt(), 0.1f); + + // Transition to the drain phase and verify that it makes the queue + // have at most a BDP worth of packets. + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_->ExportDebugState().mode != BbrSender::DRAIN; }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_LE(queue->bytes_queued(), kTestBdp); + + // Wait for a few round trips and ensure we're in appropriate phase of gain + // cycling before taking an RTT measurement. + const QuicRoundTripCount start_round_trip = + sender_->ExportDebugState().round_trip_count; + simulator_result = simulator_.RunUntilOrTimeout( + [this, start_round_trip]() { + QuicRoundTripCount rounds_passed = + sender_->ExportDebugState().round_trip_count - start_round_trip; + return rounds_passed >= 4 && + sender_->ExportDebugState().gain_cycle_index == 7; + }, + timeout); + ASSERT_TRUE(simulator_result); + + // Observe the bufferbloat go away. + ExpectApproxEq(kTestRtt, rtt_stats_->smoothed_rtt(), 0.1f); +} + +// Verify that the connection enters and exits PROBE_RTT correctly. +TEST_F(BbrSenderTest, ProbeRtt) { + CreateDefaultSetup(); + DriveOutOfStartup(); + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Wait until the connection enters PROBE_RTT. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode); + + // Exit PROBE_RTT. + const QuicTime probe_rtt_start = clock_->Now(); + const QuicTime::Delta time_to_exit_probe_rtt = + kTestRtt + QuicTime::Delta::FromMilliseconds(200); + simulator_.RunFor(1.5 * time_to_exit_probe_rtt); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start); +} + +// Verify that the first sample after PROBE_RTT is not used as the bandwidth, +// because the round counter doesn't advance during PROBE_RTT. +TEST_F(BbrSenderTest, AppLimitedRecoveryNoBandwidthDecrease) { + SetQuicReloadableFlag(quic_bbr_app_limited_recovery, true); + CreateDefaultSetup(); + DriveOutOfStartup(); + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Wait until the connection enters PROBE_RTT. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode); + + const QuicBandwidth beginning_bw = sender_->BandwidthEstimate(); + + // Run for most of PROBE_RTT. + const QuicTime probe_rtt_start = clock_->Now(); + const QuicTime::Delta time_to_exit_probe_rtt = + kTestRtt + QuicTime::Delta::FromMilliseconds(200); + simulator_.RunFor(0.60 * time_to_exit_probe_rtt); + EXPECT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode); + // Lose a packet before exiting PROBE_RTT, which puts us in packet + // conservation and then continue there for a while and ensure the bandwidth + // estimate doesn't decrease. + for (int i = 0; i < 20; ++i) { + receiver_.DropNextIncomingPacket(); + simulator_.RunFor(0.9 * kTestRtt); + // Ensure the bandwidth didn't decrease and the samples are app limited. + EXPECT_LE(beginning_bw, sender_->BandwidthEstimate()); + EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited); + } + EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start); +} + +// Verify that the connection enters and exits PROBE_RTT correctly. +TEST_F(BbrSenderTest, ProbeRttBDPBasedCWNDTarget) { + CreateDefaultSetup(); + SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true); + SetConnectionOption(kBBR6); + DriveOutOfStartup(); + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Wait until the connection enters PROBE_RTT. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode); + + // Exit PROBE_RTT. + const QuicTime probe_rtt_start = clock_->Now(); + const QuicTime::Delta time_to_exit_probe_rtt = + kTestRtt + QuicTime::Delta::FromMilliseconds(200); + simulator_.RunFor(1.5 * time_to_exit_probe_rtt); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start); +} + +// Verify that the connection enters does not enter PROBE_RTT. +TEST_F(BbrSenderTest, ProbeRttSkippedAfterAppLimitedAndStableRtt) { + CreateDefaultSetup(); + SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true); + SetConnectionOption(kBBR7); + DriveOutOfStartup(); + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Wait until the connection enters PROBE_RTT. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT; + }, + timeout); + ASSERT_FALSE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); +} + +// Verify that the connection enters does not enter PROBE_RTT. +TEST_F(BbrSenderTest, ProbeRttSkippedAfterAppLimited) { + CreateDefaultSetup(); + SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true); + SetConnectionOption(kBBR8); + DriveOutOfStartup(); + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Wait until the connection enters PROBE_RTT. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT; + }, + timeout); + ASSERT_FALSE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); +} + +// Ensure that a connection that is app-limited and is at sufficiently low +// bandwidth will not exit high gain phase, and similarly ensure that the +// connection will exit low gain early if the number of bytes in flight is low. +TEST_F(BbrSenderTest, InFlightAwareGainCycling) { + // Disable Ack Decimation on the receiver, because it can increase srtt. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + CreateDefaultSetup(); + DriveOutOfStartup(); + + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5); + bool simulator_result; + + // Start a few cycles prior to the high gain one. + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_->ExportDebugState().gain_cycle_index == 6; }, + timeout); + + // Send at 10% of available rate. Run for 3 seconds, checking in the middle + // and at the end. The pacing gain should be high throughout. + QuicBandwidth target_bandwidth = 0.1f * kTestLinkBandwidth; + QuicTime::Delta burst_interval = QuicTime::Delta::FromMilliseconds(300); + for (int i = 0; i < 2; i++) { + SendBursts(5, target_bandwidth * burst_interval, burst_interval); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_EQ(0, sender_->ExportDebugState().gain_cycle_index); + ExpectApproxEq(kTestLinkBandwidth, + sender_->ExportDebugState().max_bandwidth, 0.01f); + } + + // Now that in-flight is almost zero and the pacing gain is still above 1, + // send approximately 1.25 BDPs worth of data. This should cause the + // PROBE_BW mode to enter low gain cycle, and exit it earlier than one min_rtt + // due to running out of data to send. + bbr_sender_.AddBytesToTransfer(1.3 * kTestBdp); + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_->ExportDebugState().gain_cycle_index == 1; }, + timeout); + ASSERT_TRUE(simulator_result); + simulator_.RunFor(0.75 * sender_->ExportDebugState().min_rtt); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_EQ(2, sender_->ExportDebugState().gain_cycle_index); +} + +// Ensure that the pacing rate does not drop at startup. +TEST_F(BbrSenderTest, NoBandwidthDropOnStartup) { + CreateDefaultSetup(); + + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5); + bool simulator_result; + + QuicBandwidth initial_rate = QuicBandwidth::FromBytesAndTimeDelta( + kInitialCongestionWindowPackets * kDefaultTCPMSS, + rtt_stats_->initial_rtt()); + EXPECT_GE(sender_->PacingRate(0), initial_rate); + + // Send a packet. + bbr_sender_.AddBytesToTransfer(1000); + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return receiver_.bytes_received() == 1000; }, timeout); + ASSERT_TRUE(simulator_result); + EXPECT_GE(sender_->PacingRate(0), initial_rate); + + // Wait for a while. + simulator_.RunFor(QuicTime::Delta::FromSeconds(2)); + EXPECT_GE(sender_->PacingRate(0), initial_rate); + + // Send another packet. + bbr_sender_.AddBytesToTransfer(1000); + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return receiver_.bytes_received() == 2000; }, timeout); + ASSERT_TRUE(simulator_result); + EXPECT_GE(sender_->PacingRate(0), initial_rate); +} + +// Test exiting STARTUP earlier due to the 1RTT connection option. +TEST_F(BbrSenderTest, SimpleTransfer1RTTStartup) { + CreateDefaultSetup(); + + SetConnectionOption(k1RTT); + EXPECT_EQ(1u, sender_->num_startup_rtts()); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().max_bandwidth) { + max_bw = sender_->ExportDebugState().max_bandwidth; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(1u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ(1u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Test exiting STARTUP earlier due to the 2RTT connection option. +TEST_F(BbrSenderTest, SimpleTransfer2RTTStartup) { + CreateDefaultSetup(); + + SetConnectionOption(k2RTT); + EXPECT_EQ(2u, sender_->num_startup_rtts()); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().max_bandwidth) { + max_bw = sender_->ExportDebugState().max_bandwidth; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(2u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ(2u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Test exiting STARTUP earlier upon loss due to the LRTT connection option. +TEST_F(BbrSenderTest, SimpleTransferLRTTStartup) { + CreateDefaultSetup(); + + SetConnectionOption(kLRTT); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().max_bandwidth) { + max_bw = sender_->ExportDebugState().max_bandwidth; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(3u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Test exiting STARTUP earlier upon loss due to the LRTT connection option. +TEST_F(BbrSenderTest, SimpleTransferLRTTStartupSmallBuffer) { + CreateSmallBufferSetup(); + + SetConnectionOption(kLRTT); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().max_bandwidth) { + max_bw = sender_->ExportDebugState().max_bandwidth; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_GE(2u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ(1u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Test slower pacing after loss in STARTUP due to the BBRS connection option. +TEST_F(BbrSenderTest, SimpleTransferSlowerStartup) { + CreateSmallBufferSetup(); + + SetConnectionOption(kBBRS); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().max_bandwidth) { + max_bw = sender_->ExportDebugState().max_bandwidth; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + // Expect the pacing rate in STARTUP to decrease once packet loss + // is observed, but the CWND does not. + if (bbr_sender_.connection()->GetStats().packets_lost > 0 && + !sender_->ExportDebugState().is_at_full_bandwidth && + sender_->has_non_app_limited_sample()) { + EXPECT_EQ(1.5f * max_bw, sender_->PacingRate(0)); + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_GE(3u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Ensures no change in congestion window in STARTUP after loss. +TEST_F(BbrSenderTest, SimpleTransferNoConservationInStartup) { + CreateSmallBufferSetup(); + + SetConnectionOption(kBBS1); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + bool used_conservation_cwnd = false; + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &used_conservation_cwnd]() { + if (!sender_->ExportDebugState().is_at_full_bandwidth && + sender_->GetCongestionWindow() < + sender_->ExportDebugState().congestion_window) { + used_conservation_cwnd = true; + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_FALSE(used_conservation_cwnd); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Ensures no change in congestion window in STARTUP after loss, but that the +// rate decreases. +TEST_F(BbrSenderTest, SimpleTransferStartupRateReduction) { + SetQuicReloadableFlag(quic_bbr_startup_rate_reduction, true); + CreateSmallBufferSetup(); + + SetConnectionOption(kBBS4); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + bool used_conservation_cwnd = false; + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &used_conservation_cwnd]() { + if (!sender_->ExportDebugState().is_at_full_bandwidth && + sender_->GetCongestionWindow() < + sender_->ExportDebugState().congestion_window) { + used_conservation_cwnd = true; + } + // Exit once a loss is hit. + return bbr_sender_.connection()->GetStats().packets_lost > 0 || + sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_TRUE(sender_->InRecovery()); + EXPECT_FALSE(used_conservation_cwnd); + EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode); + EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost); + + // Lose each outstanding packet and the pacing rate decreases. + const QuicBandwidth original_pacing_rate = sender_->PacingRate(0); + QuicBandwidth pacing_rate = original_pacing_rate; + const QuicByteCount original_cwnd = sender_->GetCongestionWindow(); + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(), kMaxPacketSize)); + QuicPacketNumber largest_sent = + bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket(); + for (QuicPacketNumber packet_number = + bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked(); + packet_number <= largest_sent; ++packet_number) { + lost_packets[0].packet_number = packet_number; + sender_->OnCongestionEvent(false, 0, clock_->Now(), {}, lost_packets); + EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow()); + EXPECT_GT(original_pacing_rate, sender_->PacingRate(0)); + EXPECT_GE(pacing_rate, sender_->PacingRate(0)); + EXPECT_LE(1.25 * sender_->BandwidthEstimate(), sender_->PacingRate(0)); + pacing_rate = sender_->PacingRate(0); + } +} + +// Ensures no change in congestion window in STARTUP after loss, but that the +// rate decreases twice as fast as BBS4. +TEST_F(BbrSenderTest, SimpleTransferDoubleStartupRateReduction) { + SetQuicReloadableFlag(quic_bbr_startup_rate_reduction, true); + CreateSmallBufferSetup(); + + SetConnectionOption(kBBS5); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + bool used_conservation_cwnd = false; + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &used_conservation_cwnd]() { + if (!sender_->ExportDebugState().is_at_full_bandwidth && + sender_->GetCongestionWindow() < + sender_->ExportDebugState().congestion_window) { + used_conservation_cwnd = true; + } + // Exit once a loss is hit. + return bbr_sender_.connection()->GetStats().packets_lost > 0 || + sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_TRUE(sender_->InRecovery()); + EXPECT_FALSE(used_conservation_cwnd); + EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode); + EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost); + + // Lose each outstanding packet and the pacing rate decreases. + const QuicBandwidth original_pacing_rate = sender_->PacingRate(0); + QuicBandwidth pacing_rate = original_pacing_rate; + const QuicByteCount original_cwnd = sender_->GetCongestionWindow(); + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(), kMaxPacketSize)); + QuicPacketNumber largest_sent = + bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket(); + for (QuicPacketNumber packet_number = + bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked(); + packet_number <= largest_sent; ++packet_number) { + lost_packets[0].packet_number = packet_number; + sender_->OnCongestionEvent(false, 0, clock_->Now(), {}, lost_packets); + EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow()); + EXPECT_GT(original_pacing_rate, sender_->PacingRate(0)); + EXPECT_GE(pacing_rate, sender_->PacingRate(0)); + EXPECT_LE(1.25 * sender_->BandwidthEstimate(), sender_->PacingRate(0)); + pacing_rate = sender_->PacingRate(0); + } +} + +TEST_F(BbrSenderTest, DerivedPacingGainStartup) { + SetQuicReloadableFlag(quic_bbr_slower_startup3, true); + CreateDefaultSetup(); + + SetConnectionOption(kBBQ1); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + // Verify that Sender is in slow start. + EXPECT_TRUE(sender_->InSlowStart()); + // Verify that pacing rate is based on the initial RTT. + QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta( + 2.773 * kDefaultWindowTCP, rtt_stats_->initial_rtt()); + ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(), + sender_->PacingRate(0).ToBitsPerSecond(), 0.01f); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth, + 0.01f); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +TEST_F(BbrSenderTest, DerivedCWNDGainStartup) { + SetQuicReloadableFlag(quic_bbr_slower_startup3, true); + CreateDefaultSetup(); + + SetConnectionOption(kBBQ2); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + // Verify that Sender is in slow start. + EXPECT_TRUE(sender_->InSlowStart()); + // Verify that pacing rate is based on the initial RTT. + QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta( + 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt()); + ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(), + sender_->PacingRate(0).ToBitsPerSecond(), 0.01f); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth, + 0.01f); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); + // Expect an SRTT less than 2.7 * Min RTT on exit from STARTUP. + EXPECT_GT(kTestRtt * 2.7, rtt_stats_->smoothed_rtt()); +} + +TEST_F(BbrSenderTest, AckAggregationInStartup) { + SetQuicReloadableFlag(quic_bbr_slower_startup3, true); + // Disable Ack Decimation on the receiver to avoid loss and make results + // consistent. + QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); + CreateDefaultSetup(); + + SetConnectionOption(kBBQ3); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + // Verify that Sender is in slow start. + EXPECT_TRUE(sender_->InSlowStart()); + // Verify that pacing rate is based on the initial RTT. + QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta( + 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt()); + ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(), + sender_->PacingRate(0).ToBitsPerSecond(), 0.01f); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth, + 0.01f); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Test that two BBR flows started slightly apart from each other terminate. +TEST_F(BbrSenderTest, SimpleCompetition) { + const QuicByteCount transfer_size = 10 * 1024 * 1024; + const QuicTime::Delta transfer_time = + kTestLinkBandwidth.TransferTime(transfer_size); + CreateBbrVsBbrSetup(); + + // Transfer 10% of data in first transfer. + bbr_sender_.AddBytesToTransfer(transfer_size); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return receiver_.bytes_received() >= 0.1 * transfer_size; }, + transfer_time); + ASSERT_TRUE(simulator_result); + + // Start the second transfer and wait until both finish. + competing_sender_.AddBytesToTransfer(transfer_size); + simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return receiver_.bytes_received() == transfer_size && + competing_receiver_.bytes_received() == transfer_size; + }, + 3 * transfer_time); + ASSERT_TRUE(simulator_result); +} + +// Test that BBR can resume bandwidth from cached network parameters. +TEST_F(BbrSenderTest, ResumeConnectionState) { + CreateDefaultSetup(); + + bbr_sender_.connection()->AdjustNetworkParameters(kTestLinkBandwidth, + kTestRtt); + EXPECT_EQ(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth); + EXPECT_EQ(kTestLinkBandwidth, sender_->BandwidthEstimate()); + ExpectApproxEq(kTestRtt, sender_->ExportDebugState().min_rtt, 0.01f); + + DriveOutOfStartup(); +} + +// Test with a min CWND of 1 instead of 4 packets. +TEST_F(BbrSenderTest, ProbeRTTMinCWND1) { + CreateDefaultSetup(); + SetConnectionOption(kMIN1); + DriveOutOfStartup(); + + // We have no intention of ever finishing this transfer. + bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024); + + // Wait until the connection enters PROBE_RTT. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { + return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT; + }, + timeout); + ASSERT_TRUE(simulator_result); + ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode); + // The PROBE_RTT CWND should be 1 if the min CWND is 1. + EXPECT_EQ(kDefaultTCPMSS, sender_->GetCongestionWindow()); + + // Exit PROBE_RTT. + const QuicTime probe_rtt_start = clock_->Now(); + const QuicTime::Delta time_to_exit_probe_rtt = + kTestRtt + QuicTime::Delta::FromMilliseconds(200); + simulator_.RunFor(1.5 * time_to_exit_probe_rtt); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/cubic_bytes.cc b/quic/core/congestion_control/cubic_bytes.cc new file mode 100644 index 0000000..9300a79 --- /dev/null +++ b/quic/core/congestion_control/cubic_bytes.cc
@@ -0,0 +1,191 @@ +// Copyright (c) 2015 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/congestion_control/cubic_bytes.h" + +#include <algorithm> +#include <cmath> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +namespace { + +// Constants based on TCP defaults. +// The following constants are in 2^10 fractions of a second instead of ms to +// allow a 10 shift right to divide. +const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3) + // where 0.100 is 100 ms which is the scaling + // round trip time. +const int kCubeCongestionWindowScale = 410; +// The cube factor for packets in bytes. +const uint64_t kCubeFactor = + (UINT64_C(1) << kCubeScale) / kCubeCongestionWindowScale / kDefaultTCPMSS; + +const float kDefaultCubicBackoffFactor = 0.7f; // Default Cubic backoff factor. +// Additional backoff factor when loss occurs in the concave part of the Cubic +// curve. This additional backoff factor is expected to give up bandwidth to +// new concurrent flows and speed up convergence. +const float kBetaLastMax = 0.85f; + +} // namespace + +CubicBytes::CubicBytes(const QuicClock* clock) + : clock_(clock), + num_connections_(kDefaultNumConnections), + epoch_(QuicTime::Zero()) { + ResetCubicState(); +} + +void CubicBytes::SetNumConnections(int num_connections) { + num_connections_ = num_connections; +} + +float CubicBytes::Alpha() const { + // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that + // beta here is a cwnd multiplier, and is equal to 1-beta from the paper. + // We derive the equivalent alpha for an N-connection emulation as: + const float beta = Beta(); + return 3 * num_connections_ * num_connections_ * (1 - beta) / (1 + beta); +} + +float CubicBytes::Beta() const { + // kNConnectionBeta is the backoff factor after loss for our N-connection + // emulation, which emulates the effective backoff of an ensemble of N + // TCP-Reno connections on a single loss event. The effective multiplier is + // computed as: + return (num_connections_ - 1 + kDefaultCubicBackoffFactor) / num_connections_; +} + +float CubicBytes::BetaLastMax() const { + // BetaLastMax is the additional backoff factor after loss for our + // N-connection emulation, which emulates the additional backoff of + // an ensemble of N TCP-Reno connections on a single loss event. The + // effective multiplier is computed as: + return (num_connections_ - 1 + kBetaLastMax) / num_connections_; +} + +void CubicBytes::ResetCubicState() { + epoch_ = QuicTime::Zero(); // Reset time. + last_max_congestion_window_ = 0; + acked_bytes_count_ = 0; + estimated_tcp_congestion_window_ = 0; + origin_point_congestion_window_ = 0; + time_to_origin_point_ = 0; + last_target_congestion_window_ = 0; +} + +void CubicBytes::OnApplicationLimited() { + // When sender is not using the available congestion window, the window does + // not grow. But to be RTT-independent, Cubic assumes that the sender has been + // using the entire window during the time since the beginning of the current + // "epoch" (the end of the last loss recovery period). Since + // application-limited periods break this assumption, we reset the epoch when + // in such a period. This reset effectively freezes congestion window growth + // through application-limited periods and allows Cubic growth to continue + // when the entire window is being used. + epoch_ = QuicTime::Zero(); +} + +QuicByteCount CubicBytes::CongestionWindowAfterPacketLoss( + QuicByteCount current_congestion_window) { + // Since bytes-mode Reno mode slightly under-estimates the cwnd, we + // may never reach precisely the last cwnd over the course of an + // RTT. Do not interpret a slight under-estimation as competing traffic. + if (current_congestion_window + kDefaultTCPMSS < + last_max_congestion_window_) { + // We never reached the old max, so assume we are competing with + // another flow. Use our extra back off factor to allow the other + // flow to go up. + last_max_congestion_window_ = + static_cast<int>(BetaLastMax() * current_congestion_window); + } else { + last_max_congestion_window_ = current_congestion_window; + } + epoch_ = QuicTime::Zero(); // Reset time. + return static_cast<int>(current_congestion_window * Beta()); +} + +QuicByteCount CubicBytes::CongestionWindowAfterAck( + QuicByteCount acked_bytes, + QuicByteCount current_congestion_window, + QuicTime::Delta delay_min, + QuicTime event_time) { + acked_bytes_count_ += acked_bytes; + + if (!epoch_.IsInitialized()) { + // First ACK after a loss event. + QUIC_DVLOG(1) << "Start of epoch"; + epoch_ = event_time; // Start of epoch. + acked_bytes_count_ = acked_bytes; // Reset count. + // Reset estimated_tcp_congestion_window_ to be in sync with cubic. + estimated_tcp_congestion_window_ = current_congestion_window; + if (last_max_congestion_window_ <= current_congestion_window) { + time_to_origin_point_ = 0; + origin_point_congestion_window_ = current_congestion_window; + } else { + time_to_origin_point_ = static_cast<uint32_t>( + cbrt(kCubeFactor * + (last_max_congestion_window_ - current_congestion_window))); + origin_point_congestion_window_ = last_max_congestion_window_; + } + } + // Change the time unit from microseconds to 2^10 fractions per second. Take + // the round trip time in account. This is done to allow us to use shift as a + // divide operator. + int64_t elapsed_time = + ((event_time + delay_min - epoch_).ToMicroseconds() << 10) / + kNumMicrosPerSecond; + + // Right-shifts of negative, signed numbers have implementation-dependent + // behavior, so force the offset to be positive, as is done in the kernel. + uint64_t offset = std::abs(time_to_origin_point_ - elapsed_time); + + QuicByteCount delta_congestion_window = (kCubeCongestionWindowScale * offset * + offset * offset * kDefaultTCPMSS) >> + kCubeScale; + + const bool add_delta = elapsed_time > time_to_origin_point_; + DCHECK(add_delta || + (origin_point_congestion_window_ > delta_congestion_window)); + QuicByteCount target_congestion_window = + add_delta ? origin_point_congestion_window_ + delta_congestion_window + : origin_point_congestion_window_ - delta_congestion_window; + // Limit the CWND increase to half the acked bytes. + target_congestion_window = + std::min(target_congestion_window, + current_congestion_window + acked_bytes_count_ / 2); + + DCHECK_LT(0u, estimated_tcp_congestion_window_); + // Increase the window by approximately Alpha * 1 MSS of bytes every + // time we ack an estimated tcp window of bytes. For small + // congestion windows (less than 25), the formula below will + // increase slightly slower than linearly per estimated tcp window + // of bytes. + estimated_tcp_congestion_window_ += acked_bytes_count_ * + (Alpha() * kDefaultTCPMSS) / + estimated_tcp_congestion_window_; + acked_bytes_count_ = 0; + + // We have a new cubic congestion window. + last_target_congestion_window_ = target_congestion_window; + + // Compute target congestion_window based on cubic target and estimated TCP + // congestion_window, use highest (fastest). + if (target_congestion_window < estimated_tcp_congestion_window_) { + target_congestion_window = estimated_tcp_congestion_window_; + } + + QUIC_DVLOG(1) << "Final target congestion_window: " + << target_congestion_window; + return target_congestion_window; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/cubic_bytes.h b/quic/core/congestion_control/cubic_bytes.h new file mode 100644 index 0000000..18f7c82 --- /dev/null +++ b/quic/core/congestion_control/cubic_bytes.h
@@ -0,0 +1,103 @@ +// Copyright (c) 2015 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. + +// Cubic algorithm, helper class to TCP cubic. +// For details see http://netsrv.csc.ncsu.edu/export/cubic_a_new_tcp_2008.pdf. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_CUBIC_BYTES_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_CUBIC_BYTES_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +namespace test { +class CubicBytesTest; +} // namespace test + +class QUIC_EXPORT_PRIVATE CubicBytes { + public: + explicit CubicBytes(const QuicClock* clock); + CubicBytes(const CubicBytes&) = delete; + CubicBytes& operator=(const CubicBytes&) = delete; + + void SetNumConnections(int num_connections); + + // Call after a timeout to reset the cubic state. + void ResetCubicState(); + + // Compute a new congestion window to use after a loss event. + // Returns the new congestion window in packets. The new congestion window is + // a multiplicative decrease of our current window. + QuicByteCount CongestionWindowAfterPacketLoss(QuicPacketCount current); + + // Compute a new congestion window to use after a received ACK. + // Returns the new congestion window in bytes. The new congestion window + // follows a cubic function that depends on the time passed since last packet + // loss. + QuicByteCount CongestionWindowAfterAck(QuicByteCount acked_bytes, + QuicByteCount current, + QuicTime::Delta delay_min, + QuicTime event_time); + + // Call on ack arrival when sender is unable to use the available congestion + // window. Resets Cubic state during quiescence. + void OnApplicationLimited(); + + private: + friend class test::CubicBytesTest; + + static const QuicTime::Delta MaxCubicTimeInterval() { + return QuicTime::Delta::FromMilliseconds(30); + } + + // Compute the TCP Cubic alpha, beta, and beta-last-max based on the + // current number of connections. + float Alpha() const; + float Beta() const; + float BetaLastMax() const; + + QuicByteCount last_max_congestion_window() const { + return last_max_congestion_window_; + } + + const QuicClock* clock_; + + // Number of connections to simulate. + int num_connections_; + + // Time when this cycle started, after last loss event. + QuicTime epoch_; + + // Max congestion window used just before last loss event. + // Note: to improve fairness to other streams an additional back off is + // applied to this value if the new value is below our latest value. + QuicByteCount last_max_congestion_window_; + + // Number of acked bytes since the cycle started (epoch). + QuicByteCount acked_bytes_count_; + + // TCP Reno equivalent congestion window in packets. + QuicByteCount estimated_tcp_congestion_window_; + + // Origin point of cubic function. + QuicByteCount origin_point_congestion_window_; + + // Time to origin point of cubic function in 2^10 fractions of a second. + uint32_t time_to_origin_point_; + + // Last congestion window in packets computed by cubic function. + QuicByteCount last_target_congestion_window_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_CUBIC_BYTES_H_
diff --git a/quic/core/congestion_control/cubic_bytes_test.cc b/quic/core/congestion_control/cubic_bytes_test.cc new file mode 100644 index 0000000..4f3e9b1 --- /dev/null +++ b/quic/core/congestion_control/cubic_bytes_test.cc
@@ -0,0 +1,388 @@ +// Copyright (c) 2015 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/congestion_control/cubic_bytes.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" + +namespace quic { +namespace test { +namespace { + +const float kBeta = 0.7f; // Default Cubic backoff factor. +const float kBetaLastMax = 0.85f; // Default Cubic backoff factor. +const uint32_t kNumConnections = 2; +const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections; +const float kNConnectionBetaLastMax = + (kNumConnections - 1 + kBetaLastMax) / kNumConnections; +const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections * + (1 - kNConnectionBeta) / (1 + kNConnectionBeta); + +} // namespace + +class CubicBytesTest : public QuicTest { + protected: + CubicBytesTest() + : one_ms_(QuicTime::Delta::FromMilliseconds(1)), + hundred_ms_(QuicTime::Delta::FromMilliseconds(100)), + cubic_(&clock_) {} + + QuicByteCount RenoCwndInBytes(QuicByteCount current_cwnd) { + QuicByteCount reno_estimated_cwnd = + current_cwnd + + kDefaultTCPMSS * (kNConnectionAlpha * kDefaultTCPMSS) / current_cwnd; + return reno_estimated_cwnd; + } + + QuicByteCount ConservativeCwndInBytes(QuicByteCount current_cwnd) { + QuicByteCount conservative_cwnd = current_cwnd + kDefaultTCPMSS / 2; + return conservative_cwnd; + } + + QuicByteCount CubicConvexCwndInBytes(QuicByteCount initial_cwnd, + QuicTime::Delta rtt, + QuicTime::Delta elapsed_time) { + const int64_t offset = + ((elapsed_time + rtt).ToMicroseconds() << 10) / 1000000; + const QuicByteCount delta_congestion_window = + ((410 * offset * offset * offset) * kDefaultTCPMSS >> 40); + const QuicByteCount cubic_cwnd = initial_cwnd + delta_congestion_window; + return cubic_cwnd; + } + + QuicByteCount LastMaxCongestionWindow() { + return cubic_.last_max_congestion_window(); + } + + QuicTime::Delta MaxCubicTimeInterval() { + return cubic_.MaxCubicTimeInterval(); + } + + const QuicTime::Delta one_ms_; + const QuicTime::Delta hundred_ms_; + MockClock clock_; + CubicBytes cubic_; +}; + +// TODO(jokulik): The original "AboveOrigin" test, below, is very +// loose. It's nearly impossible to make the test tighter without +// deploying the fix for convex mode. Once cubic convex is deployed, +// replace "AboveOrigin" with this test. +TEST_F(CubicBytesTest, AboveOriginWithTighterBounds) { + // Convex growth. + const QuicTime::Delta rtt_min = hundred_ms_; + int64_t rtt_min_ms = rtt_min.ToMilliseconds(); + float rtt_min_s = rtt_min_ms / 1000.0; + QuicByteCount current_cwnd = 10 * kDefaultTCPMSS; + const QuicByteCount initial_cwnd = current_cwnd; + + clock_.AdvanceTime(one_ms_); + const QuicTime initial_time = clock_.ApproximateNow(); + const QuicByteCount expected_first_cwnd = RenoCwndInBytes(current_cwnd); + current_cwnd = cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, + rtt_min, initial_time); + ASSERT_EQ(expected_first_cwnd, current_cwnd); + + // Normal TCP phase. + // The maximum number of expected Reno RTTs is calculated by + // finding the point where the cubic curve and the reno curve meet. + const int max_reno_rtts = + std::sqrt(kNConnectionAlpha / (.4 * rtt_min_s * rtt_min_s * rtt_min_s)) - + 2; + for (int i = 0; i < max_reno_rtts; ++i) { + // Alternatively, we expect it to increase by one, every time we + // receive current_cwnd/Alpha acks back. (This is another way of + // saying we expect cwnd to increase by approximately Alpha once + // we receive current_cwnd number ofacks back). + const uint64_t num_acks_this_epoch = + current_cwnd / kDefaultTCPMSS / kNConnectionAlpha; + const QuicByteCount initial_cwnd_this_epoch = current_cwnd; + for (QuicPacketCount n = 0; n < num_acks_this_epoch; ++n) { + // Call once per ACK. + const QuicByteCount expected_next_cwnd = RenoCwndInBytes(current_cwnd); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + ASSERT_EQ(expected_next_cwnd, current_cwnd); + } + // Our byte-wise Reno implementation is an estimate. We expect + // the cwnd to increase by approximately one MSS every + // cwnd/kDefaultTCPMSS/Alpha acks, but it may be off by as much as + // half a packet for smaller values of current_cwnd. + const QuicByteCount cwnd_change_this_epoch = + current_cwnd - initial_cwnd_this_epoch; + ASSERT_NEAR(kDefaultTCPMSS, cwnd_change_this_epoch, kDefaultTCPMSS / 2); + clock_.AdvanceTime(hundred_ms_); + } + + for (int i = 0; i < 54; ++i) { + const uint64_t max_acks_this_epoch = current_cwnd / kDefaultTCPMSS; + const QuicTime::Delta interval = QuicTime::Delta::FromMicroseconds( + hundred_ms_.ToMicroseconds() / max_acks_this_epoch); + for (QuicPacketCount n = 0; n < max_acks_this_epoch; ++n) { + clock_.AdvanceTime(interval); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + const QuicByteCount expected_cwnd = CubicConvexCwndInBytes( + initial_cwnd, rtt_min, (clock_.ApproximateNow() - initial_time)); + // If we allow per-ack updates, every update is a small cubic update. + ASSERT_EQ(expected_cwnd, current_cwnd); + } + } + const QuicByteCount expected_cwnd = CubicConvexCwndInBytes( + initial_cwnd, rtt_min, (clock_.ApproximateNow() - initial_time)); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + ASSERT_EQ(expected_cwnd, current_cwnd); +} + +// TODO(ianswett): This test was disabled when all fixes were enabled, but it +// may be worth fixing. +TEST_F(CubicBytesTest, DISABLED_AboveOrigin) { + // Convex growth. + const QuicTime::Delta rtt_min = hundred_ms_; + QuicByteCount current_cwnd = 10 * kDefaultTCPMSS; + // Without the signed-integer, cubic-convex fix, we start out in the + // wrong mode. + QuicPacketCount expected_cwnd = RenoCwndInBytes(current_cwnd); + // Initialize the state. + clock_.AdvanceTime(one_ms_); + ASSERT_EQ(expected_cwnd, + cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, + rtt_min, clock_.ApproximateNow())); + current_cwnd = expected_cwnd; + const QuicPacketCount initial_cwnd = expected_cwnd; + // Normal TCP phase. + for (int i = 0; i < 48; ++i) { + for (QuicPacketCount n = 1; + n < current_cwnd / kDefaultTCPMSS / kNConnectionAlpha; ++n) { + // Call once per ACK. + ASSERT_NEAR( + current_cwnd, + cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min, + clock_.ApproximateNow()), + kDefaultTCPMSS); + } + clock_.AdvanceTime(hundred_ms_); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + // When we fix convex mode and the uint64 arithmetic, we + // increase the expected_cwnd only after after the first 100ms, + // rather than after the initial 1ms. + expected_cwnd += kDefaultTCPMSS; + ASSERT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS); + } + // Cubic phase. + for (int i = 0; i < 52; ++i) { + for (QuicPacketCount n = 1; n < current_cwnd / kDefaultTCPMSS; ++n) { + // Call once per ACK. + ASSERT_NEAR( + current_cwnd, + cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min, + clock_.ApproximateNow()), + kDefaultTCPMSS); + } + clock_.AdvanceTime(hundred_ms_); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + } + // Total time elapsed so far; add min_rtt (0.1s) here as well. + float elapsed_time_s = 10.0f + 0.1f; + // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4. + expected_cwnd = + initial_cwnd / kDefaultTCPMSS + + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024; + EXPECT_EQ(expected_cwnd, current_cwnd / kDefaultTCPMSS); +} + +// Constructs an artificial scenario to ensure that cubic-convex +// increases are truly fine-grained: +// +// - After starting the epoch, this test advances the elapsed time +// sufficiently far that cubic will do small increases at less than +// MaxCubicTimeInterval() intervals. +// +// - Sets an artificially large initial cwnd to prevent Reno from the +// convex increases on every ack. +TEST_F(CubicBytesTest, AboveOriginFineGrainedCubing) { + // Start the test with an artificially large cwnd to prevent Reno + // from over-taking cubic. + QuicByteCount current_cwnd = 1000 * kDefaultTCPMSS; + const QuicByteCount initial_cwnd = current_cwnd; + const QuicTime::Delta rtt_min = hundred_ms_; + clock_.AdvanceTime(one_ms_); + QuicTime initial_time = clock_.ApproximateNow(); + + // Start the epoch and then artificially advance the time. + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(600)); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + + // We expect the algorithm to perform only non-zero, fine-grained cubic + // increases on every ack in this case. + for (int i = 0; i < 100; ++i) { + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + const QuicByteCount expected_cwnd = CubicConvexCwndInBytes( + initial_cwnd, rtt_min, (clock_.ApproximateNow() - initial_time)); + const QuicByteCount next_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + // Make sure we are performing cubic increases. + ASSERT_EQ(expected_cwnd, next_cwnd); + // Make sure that these are non-zero, less-than-packet sized + // increases. + ASSERT_GT(next_cwnd, current_cwnd); + const QuicByteCount cwnd_delta = next_cwnd - current_cwnd; + ASSERT_GT(kDefaultTCPMSS * .1, cwnd_delta); + + current_cwnd = next_cwnd; + } +} + +// Constructs an artificial scenario to show what happens when we +// allow per-ack updates, rather than limititing update freqency. In +// this scenario, the first two acks of the epoch produce the same +// cwnd. When we limit per-ack updates, this would cause the +// cessation of cubic updates for 30ms. When we allow per-ack +// updates, the window continues to grow on every ack. +TEST_F(CubicBytesTest, PerAckUpdates) { + // Start the test with a large cwnd and RTT, to force the first + // increase to be a cubic increase. + QuicPacketCount initial_cwnd_packets = 150; + QuicByteCount current_cwnd = initial_cwnd_packets * kDefaultTCPMSS; + const QuicTime::Delta rtt_min = 350 * one_ms_; + + // Initialize the epoch + clock_.AdvanceTime(one_ms_); + // Keep track of the growth of the reno-equivalent cwnd. + QuicByteCount reno_cwnd = RenoCwndInBytes(current_cwnd); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + const QuicByteCount initial_cwnd = current_cwnd; + + // Simulate the return of cwnd packets in less than + // MaxCubicInterval() time. + const QuicPacketCount max_acks = initial_cwnd_packets / kNConnectionAlpha; + const QuicTime::Delta interval = QuicTime::Delta::FromMicroseconds( + MaxCubicTimeInterval().ToMicroseconds() / (max_acks + 1)); + + // In this scenario, the first increase is dictated by the cubic + // equation, but it is less than one byte, so the cwnd doesn't + // change. Normally, without per-ack increases, any cwnd plateau + // will cause the cwnd to be pinned for MaxCubicTimeInterval(). If + // we enable per-ack updates, the cwnd will continue to grow, + // regardless of the temporary plateau. + clock_.AdvanceTime(interval); + reno_cwnd = RenoCwndInBytes(reno_cwnd); + ASSERT_EQ(current_cwnd, + cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, + rtt_min, clock_.ApproximateNow())); + for (QuicPacketCount i = 1; i < max_acks; ++i) { + clock_.AdvanceTime(interval); + const QuicByteCount next_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + reno_cwnd = RenoCwndInBytes(reno_cwnd); + // The window shoud increase on every ack. + ASSERT_LT(current_cwnd, next_cwnd); + ASSERT_EQ(reno_cwnd, next_cwnd); + current_cwnd = next_cwnd; + } + + // After all the acks are returned from the epoch, we expect the + // cwnd to have increased by nearly one packet. (Not exactly one + // packet, because our byte-wise Reno algorithm is always a slight + // under-estimation). Without per-ack updates, the current_cwnd + // would otherwise be unchanged. + const QuicByteCount minimum_expected_increase = kDefaultTCPMSS * .9; + EXPECT_LT(minimum_expected_increase + initial_cwnd, current_cwnd); +} + +TEST_F(CubicBytesTest, LossEvents) { + const QuicTime::Delta rtt_min = hundred_ms_; + QuicByteCount current_cwnd = 422 * kDefaultTCPMSS; + // Without the signed-integer, cubic-convex fix, we mistakenly + // increment cwnd after only one_ms_ and a single ack. + QuicPacketCount expected_cwnd = RenoCwndInBytes(current_cwnd); + // Initialize the state. + clock_.AdvanceTime(one_ms_); + EXPECT_EQ(expected_cwnd, + cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, + rtt_min, clock_.ApproximateNow())); + + // On the first loss, the last max congestion window is set to the + // congestion window before the loss. + QuicByteCount pre_loss_cwnd = current_cwnd; + ASSERT_EQ(0u, LastMaxCongestionWindow()); + expected_cwnd = static_cast<QuicByteCount>(current_cwnd * kNConnectionBeta); + EXPECT_EQ(expected_cwnd, + cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); + ASSERT_EQ(pre_loss_cwnd, LastMaxCongestionWindow()); + current_cwnd = expected_cwnd; + + // On the second loss, the current congestion window has not yet + // reached the last max congestion window. The last max congestion + // window will be reduced by an additional backoff factor to allow + // for competition. + pre_loss_cwnd = current_cwnd; + expected_cwnd = static_cast<QuicByteCount>(current_cwnd * kNConnectionBeta); + ASSERT_EQ(expected_cwnd, + cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); + current_cwnd = expected_cwnd; + EXPECT_GT(pre_loss_cwnd, LastMaxCongestionWindow()); + QuicByteCount expected_last_max = + static_cast<QuicByteCount>(pre_loss_cwnd * kNConnectionBetaLastMax); + EXPECT_EQ(expected_last_max, LastMaxCongestionWindow()); + EXPECT_LT(expected_cwnd, LastMaxCongestionWindow()); + // Simulate an increase, and check that we are below the origin. + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + EXPECT_GT(LastMaxCongestionWindow(), current_cwnd); + + // On the final loss, simulate the condition where the congestion + // window had a chance to grow nearly to the last congestion window. + current_cwnd = LastMaxCongestionWindow() - 1; + pre_loss_cwnd = current_cwnd; + expected_cwnd = static_cast<QuicByteCount>(current_cwnd * kNConnectionBeta); + EXPECT_EQ(expected_cwnd, + cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); + expected_last_max = pre_loss_cwnd; + ASSERT_EQ(expected_last_max, LastMaxCongestionWindow()); +} + +TEST_F(CubicBytesTest, BelowOrigin) { + // Concave growth. + const QuicTime::Delta rtt_min = hundred_ms_; + QuicByteCount current_cwnd = 422 * kDefaultTCPMSS; + // Without the signed-integer, cubic-convex fix, we mistakenly + // increment cwnd after only one_ms_ and a single ack. + QuicPacketCount expected_cwnd = RenoCwndInBytes(current_cwnd); + // Initialize the state. + clock_.AdvanceTime(one_ms_); + EXPECT_EQ(expected_cwnd, + cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, + rtt_min, clock_.ApproximateNow())); + expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta); + EXPECT_EQ(expected_cwnd, + cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); + current_cwnd = expected_cwnd; + // First update after loss to initialize the epoch. + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + // Cubic phase. + for (int i = 0; i < 40; ++i) { + clock_.AdvanceTime(hundred_ms_); + current_cwnd = cubic_.CongestionWindowAfterAck( + kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow()); + } + expected_cwnd = 553632; + EXPECT_EQ(expected_cwnd, current_cwnd); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/general_loss_algorithm.cc b/quic/core/congestion_control/general_loss_algorithm.cc new file mode 100644 index 0000000..8cd5e71 --- /dev/null +++ b/quic/core/congestion_control/general_loss_algorithm.cc
@@ -0,0 +1,236 @@ +// Copyright 2015 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/congestion_control/general_loss_algorithm.h" + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +namespace { + +// The minimum delay before a packet will be considered lost, +// regardless of SRTT. Half of the minimum TLP, since the loss algorithm only +// triggers when a nack has been receieved for the packet. +static const size_t kMinLossDelayMs = 5; + +// Default fraction of an RTT the algorithm waits before determining a packet is +// lost due to early retransmission by time based loss detection. +static const int kDefaultLossDelayShift = 2; +// Default fraction of an RTT when doing adaptive loss detection. +static const int kDefaultAdaptiveLossDelayShift = 4; + +} // namespace + +GeneralLossAlgorithm::GeneralLossAlgorithm() : GeneralLossAlgorithm(kNack) {} + +GeneralLossAlgorithm::GeneralLossAlgorithm(LossDetectionType loss_type) + : loss_detection_timeout_(QuicTime::Zero()), + least_in_flight_(1), + packet_number_space_(NUM_PACKET_NUMBER_SPACES) { + SetLossDetectionType(loss_type); +} + +void GeneralLossAlgorithm::SetLossDetectionType(LossDetectionType loss_type) { + loss_detection_timeout_ = QuicTime::Zero(); + largest_sent_on_spurious_retransmit_.Clear(); + loss_type_ = loss_type; + reordering_shift_ = loss_type == kAdaptiveTime + ? kDefaultAdaptiveLossDelayShift + : kDefaultLossDelayShift; + if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection) && + loss_type == kTime) { + QUIC_RELOADABLE_FLAG_COUNT(quic_eighth_rtt_loss_detection); + reordering_shift_ = 3; + } + largest_previously_acked_.Clear(); +} + +LossDetectionType GeneralLossAlgorithm::GetLossDetectionType() const { + return loss_type_; +} + +// Uses nack counts to decide when packets are lost. +void GeneralLossAlgorithm::DetectLosses( + const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber largest_newly_acked, + const AckedPacketVector& packets_acked, + LostPacketVector* packets_lost) { + loss_detection_timeout_ = QuicTime::Zero(); + if (!packets_acked.empty() && + packets_acked.front().packet_number == least_in_flight_) { + if (least_in_flight_ + packets_acked.size() - 1 == largest_newly_acked) { + // Optimization for the case when no packet is missing. + least_in_flight_ = largest_newly_acked + 1; + largest_previously_acked_ = largest_newly_acked; + return; + } + // There is hole in acked_packets, increment least_in_flight_ if possible. + for (const auto& acked : packets_acked) { + if (acked.packet_number != least_in_flight_) { + break; + } + ++least_in_flight_; + } + } + QuicTime::Delta max_rtt = + std::max(rtt_stats.previous_srtt(), rtt_stats.latest_rtt()); + QuicTime::Delta loss_delay = + std::max(QuicTime::Delta::FromMilliseconds(kMinLossDelayMs), + max_rtt + (max_rtt >> reordering_shift_)); + QuicPacketNumber packet_number = unacked_packets.GetLeastUnacked(); + auto it = unacked_packets.begin(); + if (least_in_flight_.IsInitialized() && least_in_flight_ >= packet_number) { + if (least_in_flight_ > unacked_packets.largest_sent_packet() + 1) { + QUIC_BUG << "least_in_flight: " << least_in_flight_ + << " is greater than largest_sent_packet + 1: " + << unacked_packets.largest_sent_packet() + 1; + } else { + it += (least_in_flight_ - packet_number); + packet_number = least_in_flight_; + } + } + // Clear least_in_flight_. + least_in_flight_.Clear(); + DCHECK(!unacked_packets.use_uber_loss_algorithm() || + packet_number_space_ == + unacked_packets.GetPacketNumberSpace(largest_newly_acked)); + for (; it != unacked_packets.end() && packet_number <= largest_newly_acked; + ++it, ++packet_number) { + if (unacked_packets.use_uber_loss_algorithm() && + unacked_packets.GetPacketNumberSpace(it->encryption_level) != + packet_number_space_) { + // Skip packets of different packet number space. + continue; + } + if (!it->in_flight) { + continue; + } + + if (loss_type_ == kNack) { + // FACK based loss detection. + if (largest_newly_acked - packet_number >= + kNumberOfNacksBeforeRetransmission) { + packets_lost->push_back(LostPacket(packet_number, it->bytes_sent)); + continue; + } + } else if (loss_type_ == kLazyFack) { + // Require two in order acks to invoke FACK, which avoids spuriously + // retransmitting packets when one packet is reordered by a large amount. + if (largest_previously_acked_.IsInitialized() && + largest_newly_acked > largest_previously_acked_ && + largest_previously_acked_ > packet_number && + largest_previously_acked_ - packet_number >= + (kNumberOfNacksBeforeRetransmission - 1)) { + packets_lost->push_back(LostPacket(packet_number, it->bytes_sent)); + continue; + } + } + + // Only early retransmit(RFC5827) when the last packet gets acked and + // there are retransmittable packets in flight. + // This also implements a timer-protected variant of FACK. + QuicPacketNumber largest_sent_retransmittable_packet; + if (unacked_packets.use_uber_loss_algorithm()) { + // Use largest_sent_retransmittable_packet of corresponding packet number + // space for timer based loss detection. + largest_sent_retransmittable_packet = + unacked_packets.GetLargestSentRetransmittableOfPacketNumberSpace( + packet_number_space_); + } else { + largest_sent_retransmittable_packet = + unacked_packets.largest_sent_retransmittable_packet(); + } + if (largest_sent_retransmittable_packet <= largest_newly_acked || + loss_type_ == kTime || loss_type_ == kAdaptiveTime) { + QuicTime when_lost = it->sent_time + loss_delay; + if (time < when_lost) { + loss_detection_timeout_ = when_lost; + if (!least_in_flight_.IsInitialized()) { + // At this point, packet_number is in flight and not detected as lost. + least_in_flight_ = packet_number; + } + break; + } + packets_lost->push_back(LostPacket(packet_number, it->bytes_sent)); + continue; + } + + // NACK-based loss detection allows for a max reordering window of 1 RTT. + if (it->sent_time + rtt_stats.smoothed_rtt() < + unacked_packets.GetTransmissionInfo(largest_newly_acked).sent_time) { + packets_lost->push_back(LostPacket(packet_number, it->bytes_sent)); + continue; + } + if (!least_in_flight_.IsInitialized()) { + // At this point, packet_number is in flight and not detected as lost. + least_in_flight_ = packet_number; + } + } + if (!least_in_flight_.IsInitialized()) { + // There is no in flight packet. + least_in_flight_ = largest_newly_acked + 1; + } + largest_previously_acked_ = largest_newly_acked; +} + +QuicTime GeneralLossAlgorithm::GetLossTimeout() const { + return loss_detection_timeout_; +} + +void GeneralLossAlgorithm::SpuriousRetransmitDetected( + const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber spurious_retransmission) { + if (loss_type_ != kAdaptiveTime || reordering_shift_ == 0) { + return; + } + // Calculate the extra time needed so this wouldn't have been declared lost. + // Extra time needed is based on how long it's been since the spurious + // retransmission was sent, because the SRTT and latest RTT may have changed. + QuicTime::Delta extra_time_needed = + time - + unacked_packets.GetTransmissionInfo(spurious_retransmission).sent_time; + // Increase the reordering fraction until enough time would be allowed. + QuicTime::Delta max_rtt = + std::max(rtt_stats.previous_srtt(), rtt_stats.latest_rtt()); + if (GetQuicReloadableFlag(quic_fix_adaptive_time_loss)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_adaptive_time_loss); + while ((max_rtt >> reordering_shift_) <= extra_time_needed && + reordering_shift_ > 0) { + --reordering_shift_; + } + return; + } + + if (largest_sent_on_spurious_retransmit_.IsInitialized() && + spurious_retransmission <= largest_sent_on_spurious_retransmit_) { + return; + } + largest_sent_on_spurious_retransmit_ = unacked_packets.largest_sent_packet(); + QuicTime::Delta proposed_extra_time(QuicTime::Delta::Zero()); + do { + proposed_extra_time = max_rtt >> reordering_shift_; + --reordering_shift_; + } while (proposed_extra_time < extra_time_needed && reordering_shift_ > 0); +} + +void GeneralLossAlgorithm::SetPacketNumberSpace( + PacketNumberSpace packet_number_space) { + if (packet_number_space_ < NUM_PACKET_NUMBER_SPACES) { + QUIC_BUG << "Cannot switch packet_number_space"; + return; + } + + packet_number_space_ = packet_number_space; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/general_loss_algorithm.h b/quic/core/congestion_control/general_loss_algorithm.h new file mode 100644 index 0000000..a2bcadd --- /dev/null +++ b/quic/core/congestion_control/general_loss_algorithm.h
@@ -0,0 +1,85 @@ +// Copyright 2015 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_CONGESTION_CONTROL_GENERAL_LOSS_ALGORITHM_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_GENERAL_LOSS_ALGORITHM_H_ + +#include <algorithm> +#include <map> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Class which can be configured to implement's TCP's approach of detecting loss +// when 3 nacks have been received for a packet or with a time threshold. +// Also implements TCP's early retransmit(RFC5827). +class QUIC_EXPORT_PRIVATE GeneralLossAlgorithm : public LossDetectionInterface { + public: + // TCP retransmits after 3 nacks. + static const QuicPacketCount kNumberOfNacksBeforeRetransmission = 3; + + GeneralLossAlgorithm(); + explicit GeneralLossAlgorithm(LossDetectionType loss_type); + GeneralLossAlgorithm(const GeneralLossAlgorithm&) = delete; + GeneralLossAlgorithm& operator=(const GeneralLossAlgorithm&) = delete; + ~GeneralLossAlgorithm() override {} + + LossDetectionType GetLossDetectionType() const override; + + // Switches the loss detection type to |loss_type| and resets the loss + // algorithm. + void SetLossDetectionType(LossDetectionType loss_type); + + // Uses |largest_acked| and time to decide when packets are lost. + void DetectLosses(const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber largest_newly_acked, + const AckedPacketVector& packets_acked, + LostPacketVector* packets_lost) override; + + // Returns a non-zero value when the early retransmit timer is active. + QuicTime GetLossTimeout() const override; + + // Increases the loss detection threshold for time loss detection. + void SpuriousRetransmitDetected( + const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber spurious_retransmission) override; + + void SetPacketNumberSpace(PacketNumberSpace packet_number_space); + + int reordering_shift() const { return reordering_shift_; } + + private: + QuicTime loss_detection_timeout_; + // Largest sent packet when a spurious retransmit is detected. + // Prevents increasing the reordering threshold multiple times per epoch. + // TODO(ianswett): Deprecate when quic_fix_adaptive_time_loss flag is + // deprecated. + QuicPacketNumber largest_sent_on_spurious_retransmit_; + LossDetectionType loss_type_; + // Fraction of a max(SRTT, latest_rtt) to permit reordering before declaring + // loss. Fraction calculated by shifting max(SRTT, latest_rtt) to the right + // by reordering_shift. + int reordering_shift_; + // The largest newly acked from the previous call to DetectLosses. + QuicPacketNumber largest_previously_acked_; + // The least in flight packet. Loss detection should start from this. Please + // note, least_in_flight_ could be largest packet ever sent + 1. + QuicPacketNumber least_in_flight_; + // This is only used when quic_use_uber_loss_algorithm is true. + PacketNumberSpace packet_number_space_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_GENERAL_LOSS_ALGORITHM_H_
diff --git a/quic/core/congestion_control/general_loss_algorithm_test.cc b/quic/core/congestion_control/general_loss_algorithm_test.cc new file mode 100644 index 0000000..91a25da --- /dev/null +++ b/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -0,0 +1,577 @@ +// Copyright 2015 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/congestion_control/general_loss_algorithm.h" + +#include <algorithm> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" + +namespace quic { +namespace test { +namespace { + +// Default packet length. +const uint32_t kDefaultLength = 1000; + +class GeneralLossAlgorithmTest : public QuicTest { + protected: + GeneralLossAlgorithmTest() : unacked_packets_(Perspective::IS_CLIENT) { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), clock_.Now()); + EXPECT_LT(0, rtt_stats_.smoothed_rtt().ToMicroseconds()); + if (unacked_packets_.use_uber_loss_algorithm()) { + loss_algorithm_.SetPacketNumberSpace(HANDSHAKE_DATA); + } + } + + ~GeneralLossAlgorithmTest() override {} + + void SendDataPacket(uint64_t packet_number) { + QuicStreamFrame frame; + frame.stream_id = QuicUtils::GetHeadersStreamId( + CurrentSupportedVersions()[0].transport_version); + SerializedPacket packet(QuicPacketNumber(packet_number), + PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength, + false, false); + packet.retransmittable_frames.push_back(QuicFrame(frame)); + unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(), + NOT_RETRANSMISSION, clock_.Now(), true); + } + + void SendAckPacket(uint64_t packet_number) { + SerializedPacket packet(QuicPacketNumber(packet_number), + PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength, + true, false); + unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(), + NOT_RETRANSMISSION, clock_.Now(), false); + } + + void VerifyLosses(uint64_t largest_newly_acked, + const AckedPacketVector& packets_acked, + const std::vector<uint64_t>& losses_expected) { + if (unacked_packets_.use_uber_loss_algorithm()) { + unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_NONE, QuicPacketNumber(largest_newly_acked)); + } else if (!unacked_packets_.largest_acked().IsInitialized() || + QuicPacketNumber(largest_newly_acked) > + unacked_packets_.largest_acked()) { + unacked_packets_.IncreaseLargestAcked( + QuicPacketNumber(largest_newly_acked)); + } + LostPacketVector lost_packets; + loss_algorithm_.DetectLosses(unacked_packets_, clock_.Now(), rtt_stats_, + QuicPacketNumber(largest_newly_acked), + packets_acked, &lost_packets); + ASSERT_EQ(losses_expected.size(), lost_packets.size()); + for (size_t i = 0; i < losses_expected.size(); ++i) { + EXPECT_EQ(lost_packets[i].packet_number, + QuicPacketNumber(losses_expected[i])); + } + } + + QuicUnackedPacketMap unacked_packets_; + GeneralLossAlgorithm loss_algorithm_; + RttStats rtt_stats_; + MockClock clock_; +}; + +TEST_F(GeneralLossAlgorithmTest, NackRetransmit1Packet) { + const size_t kNumSentPackets = 5; + // Transmit 5 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // No loss on one ack. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // No loss on two acks. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(3, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // Loss on three acks. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(4, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +// A stretch ack is an ack that covers more than 1 packet of previously +// unacknowledged data. +TEST_F(GeneralLossAlgorithmTest, NackRetransmit1PacketWith1StretchAck) { + const size_t kNumSentPackets = 10; + // Transmit 10 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // Nack the first packet 3 times in a single StretchAck. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero())); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(4, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +// Ack a packet 3 packets ahead, causing a retransmit. +TEST_F(GeneralLossAlgorithmTest, NackRetransmit1PacketSingleAck) { + const size_t kNumSentPackets = 10; + // Transmit 10 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // Nack the first packet 3 times in an AckFrame with three missing packets. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(4, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, EarlyRetransmit1Packet) { + const size_t kNumSentPackets = 2; + // Transmit 2 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // Early retransmit when the final packet gets acked and the first is nacked. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); + + clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt()); + VerifyLosses(2, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, EarlyRetransmitAllPackets) { + const size_t kNumSentPackets = 5; + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + // Advance the time 1/4 RTT between 3 and 4. + if (i == 3) { + clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt()); + } + } + AckedPacketVector packets_acked; + // Early retransmit when the final packet gets acked and 1.25 RTTs have + // elapsed since the packets were sent. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(kNumSentPackets)); + packets_acked.push_back(AckedPacket(QuicPacketNumber(kNumSentPackets), + kMaxPacketSize, QuicTime::Zero())); + // This simulates a single ack following multiple missing packets with FACK. + VerifyLosses(kNumSentPackets, packets_acked, {1, 2}); + packets_acked.clear(); + // The time has already advanced 1/4 an RTT, so ensure the timeout is set + // 1.25 RTTs after the earliest pending packet(3), not the last(4). + EXPECT_EQ(clock_.Now() + rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); + + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + VerifyLosses(kNumSentPackets, packets_acked, {3}); + EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); + clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt()); + VerifyLosses(kNumSentPackets, packets_acked, {4}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, DontEarlyRetransmitNeuteredPacket) { + const size_t kNumSentPackets = 2; + // Transmit 2 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // Neuter packet 1. + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1)); + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + + // Early retransmit when the final packet gets acked and the first is nacked. + if (unacked_packets_.use_uber_loss_algorithm()) { + unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_NONE, QuicPacketNumber(2)); + } else { + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2)); + } + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, EarlyRetransmitWithLargerUnackablePackets) { + // Transmit 2 data packets and one ack. + SendDataPacket(1); + SendDataPacket(2); + SendAckPacket(3); + AckedPacketVector packets_acked; + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + + // Early retransmit when the final packet gets acked and the first is nacked. + if (unacked_packets_.use_uber_loss_algorithm()) { + unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_NONE, QuicPacketNumber(2)); + } else { + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2)); + } + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); + + // The packet should be lost once the loss timeout is reached. + clock_.AdvanceTime(0.25 * rtt_stats_.latest_rtt()); + VerifyLosses(2, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, AlwaysLosePacketSent1RTTEarlier) { + // Transmit 1 packet and then wait an rtt plus 1ms. + SendDataPacket(1); + clock_.AdvanceTime(rtt_stats_.smoothed_rtt() + + QuicTime::Delta::FromMilliseconds(1)); + + // Transmit 2 packets. + SendDataPacket(2); + SendDataPacket(3); + AckedPacketVector packets_acked; + // Wait another RTT and ack 2. + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + if (unacked_packets_.use_uber_loss_algorithm()) { + unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_NONE, QuicPacketNumber(2)); + } else { + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2)); + } + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, {1}); +} + +// NoFack loss detection tests. +TEST_F(GeneralLossAlgorithmTest, LazyFackNackRetransmit1Packet) { + loss_algorithm_.SetLossDetectionType(kLazyFack); + const size_t kNumSentPackets = 5; + // Transmit 5 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // No loss on one ack. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // No loss on two acks. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(3, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // Loss on three acks. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(4, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +// A stretch ack is an ack that covers more than 1 packet of previously +// unacknowledged data. +TEST_F(GeneralLossAlgorithmTest, + LazyFackNoNackRetransmit1PacketWith1StretchAck) { + loss_algorithm_.SetLossDetectionType(kLazyFack); + const size_t kNumSentPackets = 10; + // Transmit 10 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // Nack the first packet 3 times in a single StretchAck. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero())); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(4, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // The timer isn't set because we expect more acks. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // Process another ack and then packet 1 will be lost. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(5), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(5, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +// Ack a packet 3 packets ahead does not cause a retransmit. +TEST_F(GeneralLossAlgorithmTest, LazyFackNackRetransmit1PacketSingleAck) { + loss_algorithm_.SetLossDetectionType(kLazyFack); + const size_t kNumSentPackets = 10; + // Transmit 10 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + // Nack the first packet 3 times in an AckFrame with three missing packets. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(4, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // The timer isn't set because we expect more acks. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // Process another ack and then packet 1 and 2 will be lost. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(5), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(5, packets_acked, {1, 2}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +// Time-based loss detection tests. +TEST_F(GeneralLossAlgorithmTest, NoLossFor500Nacks) { + loss_algorithm_.SetLossDetectionType(kTime); + const size_t kNumSentPackets = 5; + // Transmit 5 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + for (size_t i = 1; i < 500; ++i) { + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + } + if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) { + EXPECT_EQ(1.125 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } else { + EXPECT_EQ(1.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } +} + +TEST_F(GeneralLossAlgorithmTest, NoLossUntilTimeout) { + loss_algorithm_.SetLossDetectionType(kTime); + const size_t kNumSentPackets = 10; + // Transmit 10 packets at 1/10th an RTT interval. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + clock_.AdvanceTime(0.1 * rtt_stats_.smoothed_rtt()); + } + AckedPacketVector packets_acked; + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // The packet should not be lost until 1.25 RTTs pass. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) { + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(0.125 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } else { + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(0.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt()); + VerifyLosses(2, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, NoLossWithoutNack) { + loss_algorithm_.SetLossDetectionType(kTime); + const size_t kNumSentPackets = 10; + // Transmit 10 packets at 1/10th an RTT interval. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + clock_.AdvanceTime(0.1 * rtt_stats_.smoothed_rtt()); + } + AckedPacketVector packets_acked; + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // The packet should not be lost without a nack. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(1), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(1, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // The timer should still not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt()); + VerifyLosses(1, packets_acked, std::vector<uint64_t>{}); + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + VerifyLosses(1, packets_acked, std::vector<uint64_t>{}); + + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, MultipleLossesAtOnce) { + loss_algorithm_.SetLossDetectionType(kTime); + const size_t kNumSentPackets = 10; + // Transmit 10 packets at once and then go forward an RTT. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // The packet should not be lost until 1.25 RTTs pass. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(10)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(10), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(10, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) { + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(0.125 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } else { + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(0.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } + clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt()); + VerifyLosses(10, packets_acked, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(GeneralLossAlgorithmTest, NoSpuriousLossesFromLargeReordering) { + loss_algorithm_.SetLossDetectionType(kTime); + const size_t kNumSentPackets = 10; + // Transmit 10 packets at once and then go forward an RTT. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + AckedPacketVector packets_acked; + clock_.AdvanceTime(rtt_stats_.smoothed_rtt()); + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // The packet should not be lost until 1.25 RTTs pass. + + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(10)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(10), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(10, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) { + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(0.125 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } else { + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(0.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + } + clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt()); + // Now ack packets 1 to 9 and ensure the timer is no longer set and no packets + // are lost. + for (uint64_t i = 1; i <= 9; ++i) { + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(i)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(i), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(i, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + } +} + +TEST_F(GeneralLossAlgorithmTest, IncreaseThresholdUponSpuriousLoss) { + loss_algorithm_.SetLossDetectionType(kAdaptiveTime); + EXPECT_EQ(4, loss_algorithm_.reordering_shift()); + const size_t kNumSentPackets = 10; + // Transmit 2 packets at 1/10th an RTT interval. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + clock_.AdvanceTime(0.1 * rtt_stats_.smoothed_rtt()); + } + EXPECT_EQ(QuicTime::Zero() + rtt_stats_.smoothed_rtt(), clock_.Now()); + AckedPacketVector packets_acked; + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // Packet 1 should not be lost until 1/16 RTTs pass. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + packets_acked.push_back( + AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero())); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + packets_acked.clear(); + // Expect the timer to be set to 1/16 RTT's in the future. + EXPECT_EQ(rtt_stats_.smoothed_rtt() * (1.0f / 16), + loss_algorithm_.GetLossTimeout() - clock_.Now()); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}); + clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 16)); + VerifyLosses(2, packets_acked, {1}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // Retransmit packet 1 as 11 and 2 as 12. + SendDataPacket(11); + SendDataPacket(12); + + // Advance the time 1/4 RTT and indicate the loss was spurious. + // The new threshold should be 1/2 RTT. + clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 4)); + if (GetQuicReloadableFlag(quic_fix_adaptive_time_loss)) { + // The flag fixes an issue where adaptive time loss would increase the + // reordering threshold by an extra factor of two. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + } + loss_algorithm_.SpuriousRetransmitDetected(unacked_packets_, clock_.Now(), + rtt_stats_, QuicPacketNumber(11)); + EXPECT_EQ(1, loss_algorithm_.reordering_shift()); + + // Detect another spurious retransmit and ensure the threshold doesn't + // increase again. + loss_algorithm_.SpuriousRetransmitDetected(unacked_packets_, clock_.Now(), + rtt_stats_, QuicPacketNumber(12)); + EXPECT_EQ(1, loss_algorithm_.reordering_shift()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/hybrid_slow_start.cc b/quic/core/congestion_control/hybrid_slow_start.cc new file mode 100644 index 0000000..28963b1 --- /dev/null +++ b/quic/core/congestion_control/hybrid_slow_start.cc
@@ -0,0 +1,104 @@ +// Copyright (c) 2012 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/congestion_control/hybrid_slow_start.h" + +#include <algorithm> + +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +// Note(pwestin): the magic clamping numbers come from the original code in +// tcp_cubic.c. +const int64_t kHybridStartLowWindow = 16; +// Number of delay samples for detecting the increase of delay. +const uint32_t kHybridStartMinSamples = 8; +// Exit slow start if the min rtt has increased by more than 1/8th. +const int kHybridStartDelayFactorExp = 3; // 2^3 = 8 +// The original paper specifies 2 and 8ms, but those have changed over time. +const int64_t kHybridStartDelayMinThresholdUs = 4000; +const int64_t kHybridStartDelayMaxThresholdUs = 16000; + +HybridSlowStart::HybridSlowStart() + : started_(false), + hystart_found_(NOT_FOUND), + rtt_sample_count_(0), + current_min_rtt_(QuicTime::Delta::Zero()) {} + +void HybridSlowStart::OnPacketAcked(QuicPacketNumber acked_packet_number) { + // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end + // the round when the final packet of the burst is received and start it on + // the next incoming ack. + if (IsEndOfRound(acked_packet_number)) { + started_ = false; + } +} + +void HybridSlowStart::OnPacketSent(QuicPacketNumber packet_number) { + last_sent_packet_number_ = packet_number; +} + +void HybridSlowStart::Restart() { + started_ = false; + hystart_found_ = NOT_FOUND; +} + +void HybridSlowStart::StartReceiveRound(QuicPacketNumber last_sent) { + QUIC_DVLOG(1) << "Reset hybrid slow start @" << last_sent; + end_packet_number_ = last_sent; + current_min_rtt_ = QuicTime::Delta::Zero(); + rtt_sample_count_ = 0; + started_ = true; +} + +bool HybridSlowStart::IsEndOfRound(QuicPacketNumber ack) const { + return !end_packet_number_.IsInitialized() || end_packet_number_ <= ack; +} + +bool HybridSlowStart::ShouldExitSlowStart(QuicTime::Delta latest_rtt, + QuicTime::Delta min_rtt, + QuicPacketCount congestion_window) { + if (!started_) { + // Time to start the hybrid slow start. + StartReceiveRound(last_sent_packet_number_); + } + if (hystart_found_ != NOT_FOUND) { + return true; + } + // Second detection parameter - delay increase detection. + // Compare the minimum delay (current_min_rtt_) of the current + // burst of packets relative to the minimum delay during the session. + // Note: we only look at the first few(8) packets in each burst, since we + // only want to compare the lowest RTT of the burst relative to previous + // bursts. + rtt_sample_count_++; + if (rtt_sample_count_ <= kHybridStartMinSamples) { + if (current_min_rtt_.IsZero() || current_min_rtt_ > latest_rtt) { + current_min_rtt_ = latest_rtt; + } + } + // We only need to check this once per round. + if (rtt_sample_count_ == kHybridStartMinSamples) { + // Divide min_rtt by 8 to get a rtt increase threshold for exiting. + int64_t min_rtt_increase_threshold_us = + min_rtt.ToMicroseconds() >> kHybridStartDelayFactorExp; + // Ensure the rtt threshold is never less than 2ms or more than 16ms. + min_rtt_increase_threshold_us = std::min(min_rtt_increase_threshold_us, + kHybridStartDelayMaxThresholdUs); + QuicTime::Delta min_rtt_increase_threshold = + QuicTime::Delta::FromMicroseconds(std::max( + min_rtt_increase_threshold_us, kHybridStartDelayMinThresholdUs)); + + if (current_min_rtt_ > min_rtt + min_rtt_increase_threshold) { + hystart_found_ = DELAY; + } + } + // Exit from slow start if the cwnd is greater than 16 and + // increasing delay is found. + return congestion_window >= kHybridStartLowWindow && + hystart_found_ != NOT_FOUND; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/hybrid_slow_start.h b/quic/core/congestion_control/hybrid_slow_start.h new file mode 100644 index 0000000..61e40ae --- /dev/null +++ b/quic/core/congestion_control/hybrid_slow_start.h
@@ -0,0 +1,84 @@ +// Copyright (c) 2012 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. + +// This class is a helper class to TcpCubicSender. +// Slow start is the initial startup phase of TCP, it lasts until first packet +// loss. This class implements hybrid slow start of the TCP cubic send side +// congestion algorithm. The key feaure of hybrid slow start is that it tries to +// avoid running into the wall too hard during the slow start phase, which +// the traditional TCP implementation does. +// This does not implement ack train detection because it interacts poorly with +// pacing. +// http://netsrv.csc.ncsu.edu/export/hybridstart_pfldnet08.pdf +// http://research.csc.ncsu.edu/netsrv/sites/default/files/hystart_techreport_2008.pdf + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_HYBRID_SLOW_START_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_HYBRID_SLOW_START_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE HybridSlowStart { + public: + HybridSlowStart(); + HybridSlowStart(const HybridSlowStart&) = delete; + HybridSlowStart& operator=(const HybridSlowStart&) = delete; + + void OnPacketAcked(QuicPacketNumber acked_packet_number); + + void OnPacketSent(QuicPacketNumber packet_number); + + // ShouldExitSlowStart should be called on every new ack frame, since a new + // RTT measurement can be made then. + // rtt: the RTT for this ack packet. + // min_rtt: is the lowest delay (RTT) we have seen during the session. + // congestion_window: the congestion window in packets. + bool ShouldExitSlowStart(QuicTime::Delta rtt, + QuicTime::Delta min_rtt, + QuicPacketCount congestion_window); + + // Start a new slow start phase. + void Restart(); + + // TODO(ianswett): The following methods should be private, but that requires + // a follow up CL to update the unit test. + // Returns true if this ack the last packet number of our current slow start + // round. + // Call Reset if this returns true. + bool IsEndOfRound(QuicPacketNumber ack) const; + + // Call for the start of each receive round (burst) in the slow start phase. + void StartReceiveRound(QuicPacketNumber last_sent); + + // Whether slow start has started. + bool started() const { return started_; } + + private: + // Whether a condition for exiting slow start has been found. + enum HystartState { + NOT_FOUND, + DELAY, // Too much increase in the round's min_rtt was observed. + }; + + // Whether the hybrid slow start has been started. + bool started_; + HystartState hystart_found_; + // Last packet number sent which was CWND limited. + QuicPacketNumber last_sent_packet_number_; + + // Variables for tracking acks received during a slow start round. + QuicPacketNumber end_packet_number_; // End of the receive round. + uint32_t rtt_sample_count_; // Number of rtt samples in the current round. + QuicTime::Delta current_min_rtt_; // The minimum rtt of current round. +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_HYBRID_SLOW_START_H_
diff --git a/quic/core/congestion_control/hybrid_slow_start_test.cc b/quic/core/congestion_control/hybrid_slow_start_test.cc new file mode 100644 index 0000000..aa2f849 --- /dev/null +++ b/quic/core/congestion_control/hybrid_slow_start_test.cc
@@ -0,0 +1,76 @@ +// Copyright (c) 2012 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/congestion_control/hybrid_slow_start.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class HybridSlowStartTest : public QuicTest { + protected: + HybridSlowStartTest() + : one_ms_(QuicTime::Delta::FromMilliseconds(1)), + rtt_(QuicTime::Delta::FromMilliseconds(60)) {} + void SetUp() override { slow_start_ = QuicMakeUnique<HybridSlowStart>(); } + const QuicTime::Delta one_ms_; + const QuicTime::Delta rtt_; + std::unique_ptr<HybridSlowStart> slow_start_; +}; + +TEST_F(HybridSlowStartTest, Simple) { + QuicPacketNumber packet_number(1); + QuicPacketNumber end_packet_number(3); + slow_start_->StartReceiveRound(end_packet_number); + + EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++)); + + // Test duplicates. + EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number)); + + EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++)); + EXPECT_TRUE(slow_start_->IsEndOfRound(packet_number++)); + + // Test without a new registered end_packet_number; + EXPECT_TRUE(slow_start_->IsEndOfRound(packet_number++)); + + end_packet_number = QuicPacketNumber(20); + slow_start_->StartReceiveRound(end_packet_number); + while (packet_number < end_packet_number) { + EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++)); + } + EXPECT_TRUE(slow_start_->IsEndOfRound(packet_number++)); +} + +TEST_F(HybridSlowStartTest, Delay) { + // We expect to detect the increase at +1/8 of the RTT; hence at a typical + // RTT of 60ms the detection will happen at 67.5 ms. + const int kHybridStartMinSamples = 8; // Number of acks required to trigger. + + QuicPacketNumber end_packet_number(1); + slow_start_->StartReceiveRound(end_packet_number++); + + // Will not trigger since our lowest RTT in our burst is the same as the long + // term RTT provided. + for (int n = 0; n < kHybridStartMinSamples; ++n) { + EXPECT_FALSE(slow_start_->ShouldExitSlowStart( + rtt_ + QuicTime::Delta::FromMilliseconds(n), rtt_, 100)); + } + slow_start_->StartReceiveRound(end_packet_number++); + for (int n = 1; n < kHybridStartMinSamples; ++n) { + EXPECT_FALSE(slow_start_->ShouldExitSlowStart( + rtt_ + QuicTime::Delta::FromMilliseconds(n + 10), rtt_, 100)); + } + // Expect to trigger since all packets in this burst was above the long term + // RTT provided. + EXPECT_TRUE(slow_start_->ShouldExitSlowStart( + rtt_ + QuicTime::Delta::FromMilliseconds(10), rtt_, 100)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/loss_detection_interface.h b/quic/core/congestion_control/loss_detection_interface.h new file mode 100644 index 0000000..7439d3f --- /dev/null +++ b/quic/core/congestion_control/loss_detection_interface.h
@@ -0,0 +1,49 @@ +// Copyright 2014 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. + +// The pure virtual class for send side loss detection algorithm. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_ + +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QuicUnackedPacketMap; +class RttStats; + +class QUIC_EXPORT_PRIVATE LossDetectionInterface { + public: + virtual ~LossDetectionInterface() {} + + virtual LossDetectionType GetLossDetectionType() const = 0; + + // Called when a new ack arrives or the loss alarm fires. + virtual void DetectLosses(const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber largest_newly_acked, + const AckedPacketVector& packets_acked, + LostPacketVector* packets_lost) = 0; + + // Get the time the LossDetectionAlgorithm wants to re-evaluate losses. + // Returns QuicTime::Zero if no alarm needs to be set. + virtual QuicTime GetLossTimeout() const = 0; + + // Called when a |spurious_retransmission| is detected. The original + // transmission must have been caused by DetectLosses. + virtual void SpuriousRetransmitDetected( + const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber spurious_retransmission) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
diff --git a/quic/core/congestion_control/pacing_sender.cc b/quic/core/congestion_control/pacing_sender.cc new file mode 100644 index 0000000..348f410 --- /dev/null +++ b/quic/core/congestion_control/pacing_sender.cc
@@ -0,0 +1,151 @@ +// Copyright (c) 2013 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/congestion_control/pacing_sender.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { +namespace { + +// Configured maximum size of the burst coming out of quiescence. The burst +// is never larger than the current CWND in packets. +static const uint32_t kInitialUnpacedBurst = 10; + +} // namespace + +PacingSender::PacingSender() + : sender_(nullptr), + max_pacing_rate_(QuicBandwidth::Zero()), + burst_tokens_(kInitialUnpacedBurst), + ideal_next_packet_send_time_(QuicTime::Zero()), + initial_burst_size_(kInitialUnpacedBurst), + lumpy_tokens_(0), + alarm_granularity_(QuicTime::Delta::FromMilliseconds(1)), + pacing_limited_(false) { + if (GetQuicReloadableFlag(quic_donot_reset_ideal_next_packet_send_time)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_donot_reset_ideal_next_packet_send_time); + } +} + +PacingSender::~PacingSender() {} + +void PacingSender::set_sender(SendAlgorithmInterface* sender) { + DCHECK(sender != nullptr); + sender_ = sender; +} + +void PacingSender::OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets) { + DCHECK(sender_ != nullptr); + if (!lost_packets.empty()) { + // Clear any burst tokens when entering recovery. + burst_tokens_ = 0; + } + sender_->OnCongestionEvent(rtt_updated, bytes_in_flight, event_time, + acked_packets, lost_packets); +} + +void PacingSender::OnPacketSent( + QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData has_retransmittable_data) { + DCHECK(sender_ != nullptr); + sender_->OnPacketSent(sent_time, bytes_in_flight, packet_number, bytes, + has_retransmittable_data); + if (has_retransmittable_data != HAS_RETRANSMITTABLE_DATA) { + return; + } + // If in recovery, the connection is not coming out of quiescence. + if (bytes_in_flight == 0 && !sender_->InRecovery()) { + // Add more burst tokens anytime the connection is leaving quiescence, but + // limit it to the equivalent of a single bulk write, not exceeding the + // current CWND in packets. + burst_tokens_ = std::min( + initial_burst_size_, + static_cast<uint32_t>(sender_->GetCongestionWindow() / kDefaultTCPMSS)); + } + if (burst_tokens_ > 0) { + --burst_tokens_; + if (!GetQuicReloadableFlag(quic_donot_reset_ideal_next_packet_send_time)) { + ideal_next_packet_send_time_ = QuicTime::Zero(); + } + pacing_limited_ = false; + return; + } + // The next packet should be sent as soon as the current packet has been + // transferred. PacingRate is based on bytes in flight including this packet. + QuicTime::Delta delay = + PacingRate(bytes_in_flight + bytes).TransferTime(bytes); + if (!pacing_limited_ || lumpy_tokens_ == 0) { + // Reset lumpy_tokens_ if either application or cwnd throttles sending or + // token runs out. + lumpy_tokens_ = std::max( + 1u, std::min(static_cast<uint32_t>( + GetQuicFlag(FLAGS_quic_lumpy_pacing_size)), + static_cast<uint32_t>( + (sender_->GetCongestionWindow() * + GetQuicFlag(FLAGS_quic_lumpy_pacing_cwnd_fraction)) / + kDefaultTCPMSS))); + } + --lumpy_tokens_; + if (pacing_limited_) { + // Make up for lost time since pacing throttles the sending. + ideal_next_packet_send_time_ = ideal_next_packet_send_time_ + delay; + } else { + ideal_next_packet_send_time_ = + std::max(ideal_next_packet_send_time_ + delay, sent_time + delay); + } + // Stop making up for lost time if underlying sender prevents sending. + pacing_limited_ = sender_->CanSend(bytes_in_flight + bytes); +} + +void PacingSender::OnApplicationLimited() { + // The send is application limited, stop making up for lost time. + pacing_limited_ = false; +} + +QuicTime::Delta PacingSender::TimeUntilSend( + QuicTime now, + QuicByteCount bytes_in_flight) const { + DCHECK(sender_ != nullptr); + + if (!sender_->CanSend(bytes_in_flight)) { + // The underlying sender prevents sending. + return QuicTime::Delta::Infinite(); + } + + if (burst_tokens_ > 0 || bytes_in_flight == 0 || lumpy_tokens_ > 0) { + // Don't pace if we have burst tokens available or leaving quiescence. + return QuicTime::Delta::Zero(); + } + + // If the next send time is within the alarm granularity, send immediately. + if (ideal_next_packet_send_time_ > now + alarm_granularity_) { + QUIC_DVLOG(1) << "Delaying packet: " + << (ideal_next_packet_send_time_ - now).ToMicroseconds(); + return ideal_next_packet_send_time_ - now; + } + + QUIC_DVLOG(1) << "Sending packet now"; + return QuicTime::Delta::Zero(); +} + +QuicBandwidth PacingSender::PacingRate(QuicByteCount bytes_in_flight) const { + DCHECK(sender_ != nullptr); + if (!max_pacing_rate_.IsZero()) { + return QuicBandwidth::FromBitsPerSecond( + std::min(max_pacing_rate_.ToBitsPerSecond(), + sender_->PacingRate(bytes_in_flight).ToBitsPerSecond())); + } + return sender_->PacingRate(bytes_in_flight); +} + +} // namespace quic
diff --git a/quic/core/congestion_control/pacing_sender.h b/quic/core/congestion_control/pacing_sender.h new file mode 100644 index 0000000..983bbf6 --- /dev/null +++ b/quic/core/congestion_control/pacing_sender.h
@@ -0,0 +1,108 @@ +// Copyright (c) 2013 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. + +// A send algorithm that adds pacing on top of an another send algorithm. +// It uses the underlying sender's pacing rate to schedule packets. +// It also takes into consideration the expected granularity of the underlying +// alarm to ensure that alarms are not set too aggressively, and err towards +// sending packets too early instead of too late. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_PACING_SENDER_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_PACING_SENDER_H_ + +#include <cstdint> +#include <map> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +namespace test { +class QuicSentPacketManagerPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE PacingSender { + public: + PacingSender(); + PacingSender(const PacingSender&) = delete; + PacingSender& operator=(const PacingSender&) = delete; + ~PacingSender(); + + // Sets the underlying sender. Does not take ownership of |sender|. |sender| + // must not be null. This must be called before any of the + // SendAlgorithmInterface wrapper methods are called. + void set_sender(SendAlgorithmInterface* sender); + + void set_max_pacing_rate(QuicBandwidth max_pacing_rate) { + max_pacing_rate_ = max_pacing_rate; + } + + void set_alarm_granularity(QuicTime::Delta alarm_granularity) { + alarm_granularity_ = alarm_granularity; + } + + QuicBandwidth max_pacing_rate() const { return max_pacing_rate_; } + + void OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets); + + void OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData has_retransmittable_data); + + // Called when application throttles the sending, so that pacing sender stops + // making up for lost time. + void OnApplicationLimited(); + + QuicTime::Delta TimeUntilSend(QuicTime now, + QuicByteCount bytes_in_flight) const; + + QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const; + + QuicTime ideal_next_packet_send_time() const { + return ideal_next_packet_send_time_; + } + + private: + friend class test::QuicSentPacketManagerPeer; + + // Underlying sender. Not owned. + SendAlgorithmInterface* sender_; + // If not QuicBandidth::Zero, the maximum rate the PacingSender will use. + QuicBandwidth max_pacing_rate_; + + // Number of unpaced packets to be sent before packets are delayed. + uint32_t burst_tokens_; + QuicTime ideal_next_packet_send_time_; // When can the next packet be sent. + uint32_t initial_burst_size_; + + // Number of unpaced packets to be sent before packets are delayed. This token + // is consumed after burst_tokens_ ran out. + uint32_t lumpy_tokens_; + + // If the next send time is within alarm_granularity_, send immediately. + // TODO(fayang): Remove alarm_granularity_ when deprecating + // quic_offload_pacing_to_usps2 flag. + QuicTime::Delta alarm_granularity_; + + // Indicates whether pacing throttles the sending. If true, make up for lost + // time. + bool pacing_limited_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_PACING_SENDER_H_
diff --git a/quic/core/congestion_control/pacing_sender_test.cc b/quic/core/congestion_control/pacing_sender_test.cc new file mode 100644 index 0000000..0bb9258 --- /dev/null +++ b/quic/core/congestion_control/pacing_sender_test.cc
@@ -0,0 +1,432 @@ +// Copyright (c) 2013 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/congestion_control/pacing_sender.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::AtMost; +using testing::IsEmpty; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { + +const QuicByteCount kBytesInFlight = 1024; +const int kInitialBurstPackets = 10; + +class PacingSenderTest : public QuicTest { + protected: + PacingSenderTest() + : zero_time_(QuicTime::Delta::Zero()), + infinite_time_(QuicTime::Delta::Infinite()), + packet_number_(1), + mock_sender_(new StrictMock<MockSendAlgorithm>()), + pacing_sender_(new PacingSender) { + pacing_sender_->set_sender(mock_sender_.get()); + // Pick arbitrary time. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(9)); + } + + ~PacingSenderTest() override {} + + void InitPacingRate(QuicPacketCount burst_size, QuicBandwidth bandwidth) { + mock_sender_ = QuicMakeUnique<StrictMock<MockSendAlgorithm>>(); + pacing_sender_ = QuicMakeUnique<PacingSender>(); + pacing_sender_->set_sender(mock_sender_.get()); + EXPECT_CALL(*mock_sender_, PacingRate(_)).WillRepeatedly(Return(bandwidth)); + if (burst_size == 0) { + EXPECT_CALL(*mock_sender_, OnCongestionEvent(_, _, _, _, _)); + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize)); + AckedPacketVector empty; + pacing_sender_->OnCongestionEvent(true, 1234, clock_.Now(), empty, + lost_packets); + } else if (burst_size != kInitialBurstPackets) { + QUIC_LOG(FATAL) << "Unsupported burst_size " << burst_size + << " specificied, only 0 and " << kInitialBurstPackets + << " are supported."; + } + } + + void CheckPacketIsSentImmediately(HasRetransmittableData retransmittable_data, + QuicByteCount bytes_in_flight, + bool in_recovery, + bool cwnd_limited, + QuicPacketCount cwnd) { + // In order for the packet to be sendable, the underlying sender must + // permit it to be sent immediately. + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, CanSend(bytes_in_flight)) + .WillOnce(Return(true)); + // Verify that the packet can be sent immediately. + EXPECT_EQ(zero_time_, + pacing_sender_->TimeUntilSend(clock_.Now(), bytes_in_flight)); + } + + // Actually send the packet. + if (bytes_in_flight == 0) { + EXPECT_CALL(*mock_sender_, InRecovery()).WillOnce(Return(in_recovery)); + } + EXPECT_CALL(*mock_sender_, + OnPacketSent(clock_.Now(), bytes_in_flight, packet_number_, + kMaxPacketSize, retransmittable_data)); + EXPECT_CALL(*mock_sender_, GetCongestionWindow()) + .Times(AtMost(1)) + .WillRepeatedly(Return(cwnd * kDefaultTCPMSS)); + EXPECT_CALL(*mock_sender_, CanSend(bytes_in_flight + kMaxPacketSize)) + .Times(AtMost(1)) + .WillRepeatedly(Return(!cwnd_limited)); + pacing_sender_->OnPacketSent(clock_.Now(), bytes_in_flight, + packet_number_++, kMaxPacketSize, + retransmittable_data); + } + + void CheckPacketIsSentImmediately() { + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, + false, false, 10); + } + + void CheckPacketIsDelayed(QuicTime::Delta delay) { + // In order for the packet to be sendable, the underlying sender must + // permit it to be sent immediately. + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, CanSend(kBytesInFlight)) + .WillOnce(Return(true)); + // Verify that the packet is delayed. + EXPECT_EQ(delay.ToMicroseconds(), + pacing_sender_->TimeUntilSend(clock_.Now(), kBytesInFlight) + .ToMicroseconds()); + } + } + + void UpdateRtt() { + EXPECT_CALL(*mock_sender_, + OnCongestionEvent(true, kBytesInFlight, _, _, _)); + AckedPacketVector empty_acked; + LostPacketVector empty_lost; + pacing_sender_->OnCongestionEvent(true, kBytesInFlight, clock_.Now(), + empty_acked, empty_lost); + } + + void OnApplicationLimited() { pacing_sender_->OnApplicationLimited(); } + + const QuicTime::Delta zero_time_; + const QuicTime::Delta infinite_time_; + MockClock clock_; + QuicPacketNumber packet_number_; + std::unique_ptr<StrictMock<MockSendAlgorithm>> mock_sender_; + std::unique_ptr<PacingSender> pacing_sender_; +}; + +TEST_F(PacingSenderTest, NoSend) { + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, CanSend(kBytesInFlight)).WillOnce(Return(false)); + EXPECT_EQ(infinite_time_, + pacing_sender_->TimeUntilSend(clock_.Now(), kBytesInFlight)); + } +} + +TEST_F(PacingSenderTest, SendNow) { + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, CanSend(kBytesInFlight)).WillOnce(Return(true)); + EXPECT_EQ(zero_time_, + pacing_sender_->TimeUntilSend(clock_.Now(), kBytesInFlight)); + } +} + +TEST_F(PacingSenderTest, VariousSending) { + // Configure pacing rate of 1 packet per 1 ms, no initial burst. + InitPacingRate(0, QuicBandwidth::FromBytesAndTimeDelta( + kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + + // Now update the RTT and verify that packets are actually paced. + UpdateRtt(); + + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + + // The first packet was a "make up", then we sent two packets "into the + // future", so the delay should be 2. + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + // Wake up on time. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + // Wake up late. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(4)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + // Wake up really late. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + // Wake up really late again, but application pause partway through. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + OnApplicationLimited(); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + // Wake up early, but after enough time has passed to permit a send. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + CheckPacketIsSentImmediately(); +} + +TEST_F(PacingSenderTest, InitialBurst) { + // Configure pacing rate of 1 packet per 1 ms. + InitPacingRate(10, QuicBandwidth::FromBytesAndTimeDelta( + kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + + // Update the RTT and verify that the first 10 packets aren't paced. + UpdateRtt(); + + // Send 10 packets, and verify that they are not paced. + for (int i = 0; i < kInitialBurstPackets; ++i) { + CheckPacketIsSentImmediately(); + } + + // The first packet was a "make up", then we sent two packets "into the + // future", so the delay should be 2ms. + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + CheckPacketIsSentImmediately(); + + // Next time TimeUntilSend is called with no bytes in flight, pacing should + // allow a packet to be sent, and when it's sent, the tokens are refilled. + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, false, 10); + for (int i = 0; i < kInitialBurstPackets - 1; ++i) { + CheckPacketIsSentImmediately(); + } + + // The first packet was a "make up", then we sent two packets "into the + // future", so the delay should be 2ms. + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); +} + +TEST_F(PacingSenderTest, InitialBurstNoRttMeasurement) { + // Configure pacing rate of 1 packet per 1 ms. + InitPacingRate(10, QuicBandwidth::FromBytesAndTimeDelta( + kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + + // Send 10 packets, and verify that they are not paced. + for (int i = 0; i < kInitialBurstPackets; ++i) { + CheckPacketIsSentImmediately(); + } + + // The first packet was a "make up", then we sent two packets "into the + // future", so the delay should be 2ms. + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + CheckPacketIsSentImmediately(); + + // Next time TimeUntilSend is called with no bytes in flight, the tokens + // should be refilled and there should be no delay. + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, false, 10); + // Send 10 packets, and verify that they are not paced. + for (int i = 0; i < kInitialBurstPackets - 1; ++i) { + CheckPacketIsSentImmediately(); + } + + // The first packet was a "make up", then we sent two packets "into the + // future", so the delay should be 2ms. + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); +} + +TEST_F(PacingSenderTest, FastSending) { + // Ensure the pacing sender paces, even when the inter-packet spacing is less + // than the pacing granularity. + InitPacingRate(10, + QuicBandwidth::FromBytesAndTimeDelta( + 2 * kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + // Update the RTT and verify that the first 10 packets aren't paced. + UpdateRtt(); + + // Send 10 packets, and verify that they are not paced. + for (int i = 0; i < kInitialBurstPackets; ++i) { + CheckPacketIsSentImmediately(); + } + + // The first packet was a "make up", then we sent two packets "into the + // future", since it's 2 packets/ms, so the delay should be 1.5ms. + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(1500)); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + CheckPacketIsSentImmediately(); + + // Next time TimeUntilSend is called with no bytes in flight, the tokens + // should be refilled and there should be no delay. + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, false, 10); + for (int i = 0; i < kInitialBurstPackets - 1; ++i) { + CheckPacketIsSentImmediately(); + } + + // The first packet was a "make up", then we sent two packets "into the + // future", so the delay should be 1.5ms. + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(1500)); +} + +TEST_F(PacingSenderTest, NoBurstEnteringRecovery) { + // Configure pacing rate of 1 packet per 1 ms with no burst tokens. + InitPacingRate(0, QuicBandwidth::FromBytesAndTimeDelta( + kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + // Sending a packet will set burst tokens. + CheckPacketIsSentImmediately(); + + // Losing a packet will set clear burst tokens. + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize)); + AckedPacketVector empty_acked; + EXPECT_CALL(*mock_sender_, + OnCongestionEvent(true, kMaxPacketSize, _, IsEmpty(), _)); + pacing_sender_->OnCongestionEvent(true, kMaxPacketSize, clock_.Now(), + empty_acked, lost_packets); + // One packet is sent immediately, because of 1ms pacing granularity. + CheckPacketIsSentImmediately(); + // Ensure packets are immediately paced. + EXPECT_CALL(*mock_sender_, CanSend(kDefaultTCPMSS)).WillOnce(Return(true)); + // Verify the next packet is paced and delayed 2ms due to granularity. + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2), + pacing_sender_->TimeUntilSend(clock_.Now(), kDefaultTCPMSS)); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); +} + +TEST_F(PacingSenderTest, NoBurstInRecovery) { + // Configure pacing rate of 1 packet per 1 ms with no burst tokens. + InitPacingRate(0, QuicBandwidth::FromBytesAndTimeDelta( + kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + + UpdateRtt(); + + // Ensure only one packet is sent immediately and the rest are paced. + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, true, false, 10); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); +} + +TEST_F(PacingSenderTest, CwndLimited) { + // Configure pacing rate of 1 packet per 1 ms, no initial burst. + InitPacingRate(0, QuicBandwidth::FromBytesAndTimeDelta( + kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + + UpdateRtt(); + + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + // Packet 3 will be delayed 2ms. + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + // Wake up on time. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); + // After sending packet 3, cwnd is limited. + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, false, + true, 10); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + // Verify pacing sender stops making up for lost time after sending packet 3. + // Packet 6 will be delayed 2ms. + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); +} + +TEST_F(PacingSenderTest, LumpyPacingWithInitialBurstToken) { + // Set lumpy size to be 3, and cwnd faction to 0.5 + SetQuicFlag(&FLAGS_quic_lumpy_pacing_size, 3); + SetQuicFlag(&FLAGS_quic_lumpy_pacing_cwnd_fraction, 0.5f); + // Configure pacing rate of 1 packet per 1 ms. + InitPacingRate(10, QuicBandwidth::FromBytesAndTimeDelta( + kMaxPacketSize, QuicTime::Delta::FromMilliseconds(1))); + UpdateRtt(); + + // Send 10 packets, and verify that they are not paced. + for (int i = 0; i < kInitialBurstPackets; ++i) { + CheckPacketIsSentImmediately(); + } + + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + // Packet 14 will be delayed 3ms. + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(3)); + + // Wake up on time. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + // Packet 17 will be delayed 3ms. + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(3)); + + // Application throttles sending. + OnApplicationLimited(); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + // Packet 20 will be delayed 3ms. + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(3)); + + // Wake up on time. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3)); + CheckPacketIsSentImmediately(); + // After sending packet 21, cwnd is limited. + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, false, + true, 10); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + // Suppose cwnd size is 5, so that lumpy size becomes 2. + CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, false, + false, 5); + CheckPacketIsSentImmediately(); + // Packet 24 will be delayed 2ms. + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/prr_sender.cc b/quic/core/congestion_control/prr_sender.cc new file mode 100644 index 0000000..c09b312 --- /dev/null +++ b/quic/core/congestion_control/prr_sender.cc
@@ -0,0 +1,62 @@ +// Copyright (c) 2014 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/congestion_control/prr_sender.h" + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" + +namespace quic { + +PrrSender::PrrSender() + : bytes_sent_since_loss_(0), + bytes_delivered_since_loss_(0), + ack_count_since_loss_(0), + bytes_in_flight_before_loss_(0) {} + +void PrrSender::OnPacketSent(QuicByteCount sent_bytes) { + bytes_sent_since_loss_ += sent_bytes; +} + +void PrrSender::OnPacketLost(QuicByteCount prior_in_flight) { + bytes_sent_since_loss_ = 0; + bytes_in_flight_before_loss_ = prior_in_flight; + bytes_delivered_since_loss_ = 0; + ack_count_since_loss_ = 0; +} + +void PrrSender::OnPacketAcked(QuicByteCount acked_bytes) { + bytes_delivered_since_loss_ += acked_bytes; + ++ack_count_since_loss_; +} + +bool PrrSender::CanSend(QuicByteCount congestion_window, + QuicByteCount bytes_in_flight, + QuicByteCount slowstart_threshold) const { + // Return QuicTime::Zero in order to ensure limited transmit always works. + if (bytes_sent_since_loss_ == 0 || bytes_in_flight < kMaxSegmentSize) { + return true; + } + if (congestion_window > bytes_in_flight) { + // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead + // of sending the entire available window. This prevents burst retransmits + // when more packets are lost than the CWND reduction. + // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS + if (bytes_delivered_since_loss_ + ack_count_since_loss_ * kMaxSegmentSize <= + bytes_sent_since_loss_) { + return false; + } + return true; + } + // Implement Proportional Rate Reduction (RFC6937). + // Checks a simplified version of the PRR formula that doesn't use division: + // AvailableSendWindow = + // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent + if (bytes_delivered_since_loss_ * slowstart_threshold > + bytes_sent_since_loss_ * bytes_in_flight_before_loss_) { + return true; + } + return false; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/prr_sender.h b/quic/core/congestion_control/prr_sender.h new file mode 100644 index 0000000..2190912 --- /dev/null +++ b/quic/core/congestion_control/prr_sender.h
@@ -0,0 +1,43 @@ +// Copyright (c) 2014 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. + +// Implements Proportional Rate Reduction (PRR) per RFC 6937. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_PRR_SENDER_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_PRR_SENDER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE PrrSender { + public: + PrrSender(); + // OnPacketLost should be called on the first loss that triggers a recovery + // period and all other methods in this class should only be called when in + // recovery. + void OnPacketLost(QuicByteCount prior_in_flight); + void OnPacketSent(QuicByteCount sent_bytes); + void OnPacketAcked(QuicByteCount acked_bytes); + bool CanSend(QuicByteCount congestion_window, + QuicByteCount bytes_in_flight, + QuicByteCount slowstart_threshold) const; + + private: + // Bytes sent and acked since the last loss event. + // |bytes_sent_since_loss_| is the same as "prr_out_" in RFC 6937, + // and |bytes_delivered_since_loss_| is the same as "prr_delivered_". + QuicByteCount bytes_sent_since_loss_; + QuicByteCount bytes_delivered_since_loss_; + size_t ack_count_since_loss_; + + // The congestion window before the last loss event. + QuicByteCount bytes_in_flight_before_loss_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_PRR_SENDER_H_
diff --git a/quic/core/congestion_control/prr_sender_test.cc b/quic/core/congestion_control/prr_sender_test.cc new file mode 100644 index 0000000..4591065 --- /dev/null +++ b/quic/core/congestion_control/prr_sender_test.cc
@@ -0,0 +1,123 @@ +// Copyright (c) 2014 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/congestion_control/prr_sender.h" + +#include <algorithm> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +namespace { +// Constant based on TCP defaults. +const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS; +} // namespace + +class PrrSenderTest : public QuicTest {}; + +TEST_F(PrrSenderTest, SingleLossResultsInSendOnEveryOtherAck) { + PrrSender prr; + QuicPacketCount num_packets_in_flight = 50; + QuicByteCount bytes_in_flight = num_packets_in_flight * kMaxSegmentSize; + const QuicPacketCount ssthresh_after_loss = num_packets_in_flight / 2; + const QuicByteCount congestion_window = ssthresh_after_loss * kMaxSegmentSize; + + prr.OnPacketLost(bytes_in_flight); + // Ack a packet. PRR allows one packet to leave immediately. + prr.OnPacketAcked(kMaxSegmentSize); + bytes_in_flight -= kMaxSegmentSize; + EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + // Send retransmission. + prr.OnPacketSent(kMaxSegmentSize); + // PRR shouldn't allow sending any more packets. + EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + + // One packet is lost, and one ack was consumed above. PRR now paces + // transmissions through the remaining 48 acks. PRR will alternatively + // disallow and allow a packet to be sent in response to an ack. + for (uint64_t i = 0; i < ssthresh_after_loss - 1; ++i) { + // Ack a packet. PRR shouldn't allow sending a packet in response. + prr.OnPacketAcked(kMaxSegmentSize); + bytes_in_flight -= kMaxSegmentSize; + EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + // Ack another packet. PRR should now allow sending a packet in response. + prr.OnPacketAcked(kMaxSegmentSize); + bytes_in_flight -= kMaxSegmentSize; + EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + // Send a packet in response. + prr.OnPacketSent(kMaxSegmentSize); + bytes_in_flight += kMaxSegmentSize; + } + + // Since bytes_in_flight is now equal to congestion_window, PRR now maintains + // packet conservation, allowing one packet to be sent in response to an ack. + EXPECT_EQ(congestion_window, bytes_in_flight); + for (int i = 0; i < 10; ++i) { + // Ack a packet. + prr.OnPacketAcked(kMaxSegmentSize); + bytes_in_flight -= kMaxSegmentSize; + EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + // Send a packet in response, since PRR allows it. + prr.OnPacketSent(kMaxSegmentSize); + bytes_in_flight += kMaxSegmentSize; + + // Since bytes_in_flight is equal to the congestion_window, + // PRR disallows sending. + EXPECT_EQ(congestion_window, bytes_in_flight); + EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + } +} + +TEST_F(PrrSenderTest, BurstLossResultsInSlowStart) { + PrrSender prr; + QuicByteCount bytes_in_flight = 20 * kMaxSegmentSize; + const QuicPacketCount num_packets_lost = 13; + const QuicPacketCount ssthresh_after_loss = 10; + const QuicByteCount congestion_window = ssthresh_after_loss * kMaxSegmentSize; + + // Lose 13 packets. + bytes_in_flight -= num_packets_lost * kMaxSegmentSize; + prr.OnPacketLost(bytes_in_flight); + + // PRR-SSRB will allow the following 3 acks to send up to 2 packets. + for (int i = 0; i < 3; ++i) { + prr.OnPacketAcked(kMaxSegmentSize); + bytes_in_flight -= kMaxSegmentSize; + // PRR-SSRB should allow two packets to be sent. + for (int j = 0; j < 2; ++j) { + EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + // Send a packet in response. + prr.OnPacketSent(kMaxSegmentSize); + bytes_in_flight += kMaxSegmentSize; + } + // PRR should allow no more than 2 packets in response to an ack. + EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + } + + // Out of SSRB mode, PRR allows one send in response to each ack. + for (int i = 0; i < 10; ++i) { + prr.OnPacketAcked(kMaxSegmentSize); + bytes_in_flight -= kMaxSegmentSize; + EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight, + ssthresh_after_loss * kMaxSegmentSize)); + // Send a packet in response. + prr.OnPacketSent(kMaxSegmentSize); + bytes_in_flight += kMaxSegmentSize; + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/rtt_stats.cc b/quic/core/congestion_control/rtt_stats.cc new file mode 100644 index 0000000..e1a6372 --- /dev/null +++ b/quic/core/congestion_control/rtt_stats.cc
@@ -0,0 +1,103 @@ +// Copyright 2014 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/congestion_control/rtt_stats.h" + +#include <cstdlib> // std::abs + +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +namespace { + +// Default initial rtt used before any samples are received. +const int kInitialRttMs = 100; +const float kAlpha = 0.125f; +const float kOneMinusAlpha = (1 - kAlpha); +const float kBeta = 0.25f; +const float kOneMinusBeta = (1 - kBeta); + +} // namespace + +RttStats::RttStats() + : latest_rtt_(QuicTime::Delta::Zero()), + min_rtt_(QuicTime::Delta::Zero()), + smoothed_rtt_(QuicTime::Delta::Zero()), + previous_srtt_(QuicTime::Delta::Zero()), + mean_deviation_(QuicTime::Delta::Zero()), + initial_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)), + max_ack_delay_(QuicTime::Delta::Zero()), + ignore_max_ack_delay_(false) {} + +void RttStats::ExpireSmoothedMetrics() { + mean_deviation_ = std::max( + mean_deviation_, QuicTime::Delta::FromMicroseconds(std::abs( + (smoothed_rtt_ - latest_rtt_).ToMicroseconds()))); + smoothed_rtt_ = std::max(smoothed_rtt_, latest_rtt_); +} + +// Updates the RTT based on a new sample. +void RttStats::UpdateRtt(QuicTime::Delta send_delta, + QuicTime::Delta ack_delay, + QuicTime now) { + if (send_delta.IsInfinite() || send_delta <= QuicTime::Delta::Zero()) { + QUIC_LOG_FIRST_N(WARNING, 3) + << "Ignoring measured send_delta, because it's is " + << "either infinite, zero, or negative. send_delta = " + << send_delta.ToMicroseconds(); + return; + } + + // Update min_rtt_ first. min_rtt_ does not use an rtt_sample corrected for + // ack_delay but the raw observed send_delta, since poor clock granularity at + // the client may cause a high ack_delay to result in underestimation of the + // min_rtt_. + if (min_rtt_.IsZero() || min_rtt_ > send_delta) { + min_rtt_ = send_delta; + } + + QuicTime::Delta rtt_sample(send_delta); + previous_srtt_ = smoothed_rtt_; + + if (ignore_max_ack_delay_) { + ack_delay = QuicTime::Delta::Zero(); + } + // Correct for ack_delay if information received from the peer results in a + // an RTT sample at least as large as min_rtt. Otherwise, only use the + // send_delta. + if (rtt_sample > ack_delay) { + if (rtt_sample - min_rtt_ >= ack_delay) { + max_ack_delay_ = std::max(max_ack_delay_, ack_delay); + rtt_sample = rtt_sample - ack_delay; + } + } + latest_rtt_ = rtt_sample; + // First time call. + if (smoothed_rtt_.IsZero()) { + smoothed_rtt_ = rtt_sample; + mean_deviation_ = + QuicTime::Delta::FromMicroseconds(rtt_sample.ToMicroseconds() / 2); + } else { + mean_deviation_ = QuicTime::Delta::FromMicroseconds(static_cast<int64_t>( + kOneMinusBeta * mean_deviation_.ToMicroseconds() + + kBeta * std::abs((smoothed_rtt_ - rtt_sample).ToMicroseconds()))); + smoothed_rtt_ = kOneMinusAlpha * smoothed_rtt_ + kAlpha * rtt_sample; + QUIC_DVLOG(1) << " smoothed_rtt(us):" << smoothed_rtt_.ToMicroseconds() + << " mean_deviation(us):" << mean_deviation_.ToMicroseconds(); + } +} + +void RttStats::OnConnectionMigration() { + latest_rtt_ = QuicTime::Delta::Zero(); + min_rtt_ = QuicTime::Delta::Zero(); + smoothed_rtt_ = QuicTime::Delta::Zero(); + mean_deviation_ = QuicTime::Delta::Zero(); + initial_rtt_ = QuicTime::Delta::FromMilliseconds(kInitialRttMs); + max_ack_delay_ = QuicTime::Delta::Zero(); +} + +} // namespace quic
diff --git a/quic/core/congestion_control/rtt_stats.h b/quic/core/congestion_control/rtt_stats.h new file mode 100644 index 0000000..5641f13 --- /dev/null +++ b/quic/core/congestion_control/rtt_stats.h
@@ -0,0 +1,110 @@ +// Copyright 2014 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. + +// A convenience class to store rtt samples and calculate smoothed rtt. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_RTT_STATS_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_RTT_STATS_H_ + +#include <algorithm> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +namespace test { +class RttStatsPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE RttStats { + public: + RttStats(); + RttStats(const RttStats&) = delete; + RttStats& operator=(const RttStats&) = delete; + + // Updates the RTT from an incoming ack which is received |send_delta| after + // the packet is sent and the peer reports the ack being delayed |ack_delay|. + void UpdateRtt(QuicTime::Delta send_delta, + QuicTime::Delta ack_delay, + QuicTime now); + + // Causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt + // is larger. The mean deviation is increased to the most recent deviation if + // it's larger. + void ExpireSmoothedMetrics(); + + // Called when connection migrates and rtt measurement needs to be reset. + void OnConnectionMigration(); + + // Returns the EWMA smoothed RTT for the connection. + // May return Zero if no valid updates have occurred. + QuicTime::Delta smoothed_rtt() const { return smoothed_rtt_; } + + // Returns the EWMA smoothed RTT prior to the most recent RTT sample. + QuicTime::Delta previous_srtt() const { return previous_srtt_; } + + QuicTime::Delta initial_rtt() const { return initial_rtt_; } + + QuicTime::Delta SmoothedOrInitialRtt() const { + return smoothed_rtt_.IsZero() ? initial_rtt_ : smoothed_rtt_; + } + + // Sets an initial RTT to be used for SmoothedRtt before any RTT updates. + void set_initial_rtt(QuicTime::Delta initial_rtt) { + if (initial_rtt.ToMicroseconds() <= 0) { + QUIC_BUG << "Attempt to set initial rtt to <= 0."; + return; + } + initial_rtt_ = initial_rtt; + } + + // The most recent rtt measurement. + // May return Zero if no valid updates have occurred. + QuicTime::Delta latest_rtt() const { return latest_rtt_; } + + // Returns the min_rtt for the entire connection. + // May return Zero if no valid updates have occurred. + QuicTime::Delta min_rtt() const { return min_rtt_; } + + QuicTime::Delta mean_deviation() const { return mean_deviation_; } + + QuicTime::Delta max_ack_delay() const { return max_ack_delay_; } + + bool ignore_max_ack_delay() const { return ignore_max_ack_delay_; } + + void set_ignore_max_ack_delay(bool ignore_max_ack_delay) { + ignore_max_ack_delay_ = ignore_max_ack_delay; + } + + void set_initial_max_ack_delay(QuicTime::Delta initial_max_ack_delay) { + max_ack_delay_ = std::max(max_ack_delay_, initial_max_ack_delay); + } + + private: + friend class test::RttStatsPeer; + + QuicTime::Delta latest_rtt_; + QuicTime::Delta min_rtt_; + QuicTime::Delta smoothed_rtt_; + QuicTime::Delta previous_srtt_; + // Mean RTT deviation during this session. + // Approximation of standard deviation, the error is roughly 1.25 times + // larger than the standard deviation, for a normally distributed signal. + QuicTime::Delta mean_deviation_; + QuicTime::Delta initial_rtt_; + // The maximum ack delay observed over the connection after excluding ack + // delays that were too large to be included in an RTT measurement. + QuicTime::Delta max_ack_delay_; + // Whether to ignore the peer's max ack delay. + bool ignore_max_ack_delay_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_RTT_STATS_H_
diff --git a/quic/core/congestion_control/rtt_stats_test.cc b/quic/core/congestion_control/rtt_stats_test.cc new file mode 100644 index 0000000..83cb155 --- /dev/null +++ b/quic/core/congestion_control/rtt_stats_test.cc
@@ -0,0 +1,230 @@ +// Copyright 2014 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/congestion_control/rtt_stats.h" + +#include <cmath> + +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mock_log.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h" + +using testing::_; +using testing::Message; + +namespace quic { +namespace test { + +class RttStatsTest : public QuicTest { + protected: + RttStats rtt_stats_; +}; + +TEST_F(RttStatsTest, DefaultsBeforeUpdate) { + EXPECT_LT(QuicTime::Delta::Zero(), rtt_stats_.initial_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.smoothed_rtt()); +} + +TEST_F(RttStatsTest, SmoothedRtt) { + // Verify that ack_delay is ignored in the first measurement. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), + QuicTime::Delta::FromMilliseconds(100), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + // Verify that a plausible ack delay increases the max ack delay. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(400), + QuicTime::Delta::FromMilliseconds(100), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); + // Verify that Smoothed RTT includes max ack delay if it's reasonable. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(350), + QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); + // Verify that large erroneous ack_delay does not change Smoothed RTT. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200), + QuicTime::Delta::FromMilliseconds(300), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(287500), + rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); +} + +TEST_F(RttStatsTest, SmoothedRttIgnoreAckDelay) { + rtt_stats_.set_ignore_max_ack_delay(true); + // Verify that ack_delay is ignored in the first measurement. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), + QuicTime::Delta::FromMilliseconds(100), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + // Verify that a plausible ack delay increases the max ack delay. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), + QuicTime::Delta::FromMilliseconds(100), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); + // Verify that Smoothed RTT includes max ack delay if it's reasonable. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), + QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt()); + // Verify that large erroneous ack_delay does not change Smoothed RTT. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200), + QuicTime::Delta::FromMilliseconds(300), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(287500), + rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); +} + +// Ensure that the potential rounding artifacts in EWMA calculation do not cause +// the SRTT to drift too far from the exact value. +TEST_F(RttStatsTest, SmoothedRttStability) { + for (size_t time = 3; time < 20000; time++) { + RttStats stats; + for (size_t i = 0; i < 100; i++) { + stats.UpdateRtt(QuicTime::Delta::FromMicroseconds(time), + QuicTime::Delta::FromMilliseconds(0), QuicTime::Zero()); + int64_t time_delta_us = stats.smoothed_rtt().ToMicroseconds() - time; + ASSERT_LE(std::abs(time_delta_us), 1); + } + } +} + +TEST_F(RttStatsTest, PreviousSmoothedRtt) { + // Verify that ack_delay is corrected for in Smoothed RTT. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200), + QuicTime::Delta::FromMilliseconds(0), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.previous_srtt()); + // Ensure the previous SRTT is 200ms after a 100ms sample. + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(187500).ToMicroseconds(), + rtt_stats_.smoothed_rtt().ToMicroseconds()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.previous_srtt()); +} + +TEST_F(RttStatsTest, MinRtt) { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt()); + rtt_stats_.UpdateRtt( + QuicTime::Delta::FromMilliseconds(10), QuicTime::Delta::Zero(), + QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(10)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + rtt_stats_.UpdateRtt( + QuicTime::Delta::FromMilliseconds(50), QuicTime::Delta::Zero(), + QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(20)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + rtt_stats_.UpdateRtt( + QuicTime::Delta::FromMilliseconds(50), QuicTime::Delta::Zero(), + QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(30)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + rtt_stats_.UpdateRtt( + QuicTime::Delta::FromMilliseconds(50), QuicTime::Delta::Zero(), + QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(40)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + // Verify that ack_delay does not go into recording of min_rtt_. + rtt_stats_.UpdateRtt( + QuicTime::Delta::FromMilliseconds(7), + QuicTime::Delta::FromMilliseconds(2), + QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(50)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(7), rtt_stats_.min_rtt()); +} + +TEST_F(RttStatsTest, ExpireSmoothedMetrics) { + QuicTime::Delta initial_rtt = QuicTime::Delta::FromMilliseconds(10); + rtt_stats_.UpdateRtt(initial_rtt, QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt()); + EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt()); + + EXPECT_EQ(0.5 * initial_rtt, rtt_stats_.mean_deviation()); + + // Update once with a 20ms RTT. + QuicTime::Delta doubled_rtt = 2 * initial_rtt; + rtt_stats_.UpdateRtt(doubled_rtt, QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(1.125 * initial_rtt, rtt_stats_.smoothed_rtt()); + + // Expire the smoothed metrics, increasing smoothed rtt and mean deviation. + rtt_stats_.ExpireSmoothedMetrics(); + EXPECT_EQ(doubled_rtt, rtt_stats_.smoothed_rtt()); + EXPECT_EQ(0.875 * initial_rtt, rtt_stats_.mean_deviation()); + + // Now go back down to 5ms and expire the smoothed metrics, and ensure the + // mean deviation increases to 15ms. + QuicTime::Delta half_rtt = 0.5 * initial_rtt; + rtt_stats_.UpdateRtt(half_rtt, QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_GT(doubled_rtt, rtt_stats_.smoothed_rtt()); + EXPECT_LT(initial_rtt, rtt_stats_.mean_deviation()); +} + +TEST_F(RttStatsTest, UpdateRttWithBadSendDeltas) { + // Make sure we ignore bad RTTs. + CREATE_QUIC_MOCK_LOG(log); + + QuicTime::Delta initial_rtt = QuicTime::Delta::FromMilliseconds(10); + rtt_stats_.UpdateRtt(initial_rtt, QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt()); + EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt()); + + std::vector<QuicTime::Delta> bad_send_deltas; + bad_send_deltas.push_back(QuicTime::Delta::Zero()); + bad_send_deltas.push_back(QuicTime::Delta::Infinite()); + bad_send_deltas.push_back(QuicTime::Delta::FromMicroseconds(-1000)); + log.StartCapturingLogs(); + + for (QuicTime::Delta bad_send_delta : bad_send_deltas) { + SCOPED_TRACE(Message() << "bad_send_delta = " + << bad_send_delta.ToMicroseconds()); + if (QUIC_LOG_WARNING_IS_ON()) { + EXPECT_QUIC_LOG_CALL_CONTAINS(log, WARNING, "Ignoring"); + } + rtt_stats_.UpdateRtt(bad_send_delta, QuicTime::Delta::Zero(), + QuicTime::Zero()); + EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt()); + EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt()); + } +} + +TEST_F(RttStatsTest, ResetAfterConnectionMigrations) { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200), + QuicTime::Delta::FromMilliseconds(0), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(0), rtt_stats_.max_ack_delay()); + + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300), + QuicTime::Delta::FromMilliseconds(100), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay()); + + // Reset rtt stats on connection migrations. + rtt_stats_.OnConnectionMigration(); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.latest_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/send_algorithm_interface.cc b/quic/core/congestion_control/send_algorithm_interface.cc new file mode 100644 index 0000000..07efa43 --- /dev/null +++ b/quic/core/congestion_control/send_algorithm_interface.cc
@@ -0,0 +1,56 @@ +// Copyright (c) 2012 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/congestion_control/send_algorithm_interface.h" + +#include "net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h" + +namespace quic { + +class RttStats; + +// Factory for send side congestion control algorithm. +SendAlgorithmInterface* SendAlgorithmInterface::Create( + const QuicClock* clock, + const RttStats* rtt_stats, + const QuicUnackedPacketMap* unacked_packets, + CongestionControlType congestion_control_type, + QuicRandom* random, + QuicConnectionStats* stats, + QuicPacketCount initial_congestion_window) { + QuicPacketCount max_congestion_window = kDefaultMaxCongestionWindowPackets; + switch (congestion_control_type) { + case kGoogCC: // GoogCC is not supported by quic/core, fall back to BBR. + case kBBR: + return new BbrSender(rtt_stats, unacked_packets, + initial_congestion_window, max_congestion_window, + random); + case kPCC: + if (GetQuicReloadableFlag(quic_enable_pcc3)) { + return CreatePccSender(clock, rtt_stats, unacked_packets, random, stats, + initial_congestion_window, + max_congestion_window); + } + // Fall back to CUBIC if PCC is disabled. + QUIC_FALLTHROUGH_INTENDED; + case kCubicBytes: + return new TcpCubicSenderBytes( + clock, rtt_stats, false /* don't use Reno */, + initial_congestion_window, max_congestion_window, stats); + case kRenoBytes: + return new TcpCubicSenderBytes(clock, rtt_stats, true /* use Reno */, + initial_congestion_window, + max_congestion_window, stats); + } + return nullptr; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/send_algorithm_interface.h b/quic/core/congestion_control/send_algorithm_interface.h new file mode 100644 index 0000000..5df9a93 --- /dev/null +++ b/quic/core/congestion_control/send_algorithm_interface.h
@@ -0,0 +1,146 @@ +// Copyright (c) 2012 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. + +// The pure virtual class for send side congestion control algorithm. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_ + +#include <algorithm> +#include <map> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class CachedNetworkParameters; +class RttStats; + +const QuicPacketCount kDefaultMaxCongestionWindowPackets = 2000; + +class QUIC_EXPORT_PRIVATE SendAlgorithmInterface { + public: + static SendAlgorithmInterface* Create( + const QuicClock* clock, + const RttStats* rtt_stats, + const QuicUnackedPacketMap* unacked_packets, + CongestionControlType type, + QuicRandom* random, + QuicConnectionStats* stats, + QuicPacketCount initial_congestion_window); + + virtual ~SendAlgorithmInterface() {} + + virtual void SetFromConfig(const QuicConfig& config, + Perspective perspective) = 0; + + // Sets the number of connections to emulate when doing congestion control, + // particularly for congestion avoidance. Can be set any time. + virtual void SetNumEmulatedConnections(int num_connections) = 0; + + // Sets the initial congestion window in number of packets. May be ignored + // if called after the initial congestion window is no longer relevant. + virtual void SetInitialCongestionWindowInPackets(QuicPacketCount packets) = 0; + + // Indicates an update to the congestion state, caused either by an incoming + // ack or loss event timeout. |rtt_updated| indicates whether a new + // latest_rtt sample has been taken, |prior_in_flight| the bytes in flight + // prior to the congestion event. |acked_packets| and |lost_packets| are any + // packets considered acked or lost as a result of the congestion event. + virtual void OnCongestionEvent(bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets) = 0; + + // Inform that we sent |bytes| to the wire, and if the packet is + // retransmittable. |bytes_in_flight| is the number of bytes in flight before + // the packet was sent. + // Note: this function must be called for every packet sent to the wire. + virtual void OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) = 0; + + // Called when the retransmission timeout fires. Neither OnPacketAbandoned + // nor OnPacketLost will be called for these packets. + virtual void OnRetransmissionTimeout(bool packets_retransmitted) = 0; + + // Called when connection migrates and cwnd needs to be reset. + virtual void OnConnectionMigration() = 0; + + // Make decision on whether the sender can send right now. Note that even + // when this method returns true, the sending can be delayed due to pacing. + virtual bool CanSend(QuicByteCount bytes_in_flight) = 0; + + // The pacing rate of the send algorithm. May be zero if the rate is unknown. + virtual QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const = 0; + + // What's the current estimated bandwidth in bytes per second. + // Returns 0 when it does not have an estimate. + virtual QuicBandwidth BandwidthEstimate() const = 0; + + // Returns the size of the current congestion window in bytes. Note, this is + // not the *available* window. Some send algorithms may not use a congestion + // window and will return 0. + virtual QuicByteCount GetCongestionWindow() const = 0; + + // Whether the send algorithm is currently in slow start. When true, the + // BandwidthEstimate is expected to be too low. + virtual bool InSlowStart() const = 0; + + // Whether the send algorithm is currently in recovery. + virtual bool InRecovery() const = 0; + + // True when the congestion control is probing for more bandwidth and needs + // enough data to not be app-limited to do so. + // TODO(ianswett): In the future, this API may want to indicate the size of + // the probing packet. + virtual bool ShouldSendProbingPacket() const = 0; + + // Returns the size of the slow start congestion window in bytes, + // aka ssthresh. Only defined for Cubic and Reno, other algorithms return 0. + virtual QuicByteCount GetSlowStartThreshold() const = 0; + + virtual CongestionControlType GetCongestionControlType() const = 0; + + // Notifies the congestion control algorithm of an external network + // measurement or prediction. Either |bandwidth| or |rtt| may be zero if no + // sample is available. + virtual void AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) = 0; + + // Retrieves debugging information about the current state of the + // send algorithm. + virtual QuicString GetDebugState() const = 0; + + // Called when the connection has no outstanding data to send. Specifically, + // this means that none of the data streams are write-blocked, there are no + // packets in the connection queue, and there are no pending retransmissins, + // i.e. the sender cannot send anything for reasons other than being blocked + // by congestion controller. This includes cases when the connection is + // blocked by the flow controller. + // + // The fact that this method is called does not necessarily imply that the + // connection would not be blocked by the congestion control if it actually + // tried to send data. If the congestion control algorithm needs to exclude + // such cases, it should use the internal state it uses for congestion control + // for that. + virtual void OnApplicationLimited(QuicByteCount bytes_in_flight) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
diff --git a/quic/core/congestion_control/send_algorithm_test.cc b/quic/core/congestion_control/send_algorithm_test.cc new file mode 100644 index 0000000..d7bfd85 --- /dev/null +++ b/quic/core/congestion_control/send_algorithm_test.cc
@@ -0,0 +1,369 @@ +// Copyright 2013 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 <algorithm> +#include <map> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" + +namespace quic { +namespace test { +namespace { + +// Use the initial CWND of 10, as 32 is too much for the test network. +const uint32_t kInitialCongestionWindowPackets = 10; + +// Test network parameters. Here, the topology of the network is: +// +// QUIC Sender +// | +// | <-- local link +// | +// Network switch +// * <-- the bottleneck queue in the direction +// | of the receiver +// | +// | <-- test link +// | +// | +// Receiver +// +// When setting the bandwidth of the local link and test link, choose +// a bandwidth lower than 20Mbps, as the clock-granularity of the +// simulator can only handle a granularity of 1us. + +// Default settings between the switch and the sender. +const QuicBandwidth kLocalLinkBandwidth = + QuicBandwidth::FromKBitsPerSecond(10000); +const QuicTime::Delta kLocalPropagationDelay = + QuicTime::Delta::FromMilliseconds(2); + +// Wired network settings. A typical desktop network setup, a +// high-bandwidth, 30ms test link to the receiver. +const QuicBandwidth kTestLinkWiredBandwidth = + QuicBandwidth::FromKBitsPerSecond(4000); +const QuicTime::Delta kTestLinkWiredPropagationDelay = + QuicTime::Delta::FromMilliseconds(50); +const QuicTime::Delta kTestWiredTransferTime = + kTestLinkWiredBandwidth.TransferTime(kMaxPacketSize) + + kLocalLinkBandwidth.TransferTime(kMaxPacketSize); +const QuicTime::Delta kTestWiredRtt = + (kTestLinkWiredPropagationDelay + kLocalPropagationDelay + + kTestWiredTransferTime) * + 2; +const QuicByteCount kTestWiredBdp = kTestWiredRtt * kTestLinkWiredBandwidth; + +// Small BDP, Bandwidth-policed network settings. In this scenario, +// the receiver has a low-bandwidth, short propagation-delay link, +// resulting in a small BDP. We model the policer by setting the +// queue size to only one packet. +const QuicBandwidth kTestLinkLowBdpBandwidth = + QuicBandwidth::FromKBitsPerSecond(200); +const QuicTime::Delta kTestLinkLowBdpPropagationDelay = + QuicTime::Delta::FromMilliseconds(50); +const QuicByteCount kTestPolicerQueue = kMaxPacketSize; + +// Satellite network settings. In a satellite network, the bottleneck +// buffer is typically sized for non-satellite links , but the +// propagation delay of the test link to the receiver is as much as a +// quarter second. +const QuicTime::Delta kTestSatellitePropagationDelay = + QuicTime::Delta::FromMilliseconds(250); + +// Cellular scenarios. In a cellular network, the bottleneck queue at +// the edge of the network can be as great as 3MB. +const QuicBandwidth kTestLink2GBandwidth = + QuicBandwidth::FromKBitsPerSecond(100); +const QuicBandwidth kTestLink3GBandwidth = + QuicBandwidth::FromKBitsPerSecond(1500); +const QuicByteCount kCellularQueue = 3 * 1024 * 1024; +const QuicTime::Delta kTestCellularPropagationDelay = + QuicTime::Delta::FromMilliseconds(40); + +// Small RTT scenario, below the per-ack-update threshold of 30ms. +const QuicTime::Delta kTestLinkSmallRTTDelay = + QuicTime::Delta::FromMilliseconds(10); + +const char* CongestionControlTypeToString(CongestionControlType cc_type) { + switch (cc_type) { + case kCubicBytes: + return "CUBIC_BYTES"; + case kRenoBytes: + return "RENO_BYTES"; + case kBBR: + return "BBR"; + case kPCC: + return "PCC"; + default: + QUIC_DLOG(FATAL) << "Unexpected CongestionControlType"; + return nullptr; + } +} + +struct TestParams { + explicit TestParams(CongestionControlType congestion_control_type) + : congestion_control_type(congestion_control_type) {} + + friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { + os << "{ congestion_control_type: " + << CongestionControlTypeToString(p.congestion_control_type); + os << " }"; + return os; + } + + const CongestionControlType congestion_control_type; +}; + +QuicString TestParamToString(const testing::TestParamInfo<TestParams>& params) { + return QuicStrCat( + CongestionControlTypeToString(params.param.congestion_control_type), "_"); +} + +// Constructs various test permutations. +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + for (const CongestionControlType congestion_control_type : + {kBBR, kCubicBytes, kRenoBytes, kPCC}) { + params.push_back(TestParams(congestion_control_type)); + } + return params; +} + +} // namespace + +class SendAlgorithmTest : public QuicTestWithParam<TestParams> { + protected: + SendAlgorithmTest() + : simulator_(), + quic_sender_(&simulator_, + "QUIC sender", + "Receiver", + Perspective::IS_CLIENT, + TestConnectionId()), + receiver_(&simulator_, + "Receiver", + "QUIC sender", + Perspective::IS_SERVER, + TestConnectionId()) { + rtt_stats_ = quic_sender_.connection()->sent_packet_manager().GetRttStats(); + sender_ = SendAlgorithmInterface::Create( + simulator_.GetClock(), rtt_stats_, + QuicSentPacketManagerPeer::GetUnackedPacketMap( + QuicConnectionPeer::GetSentPacketManager( + quic_sender_.connection())), + GetParam().congestion_control_type, &random_, &stats_, + kInitialCongestionWindowPackets); + quic_sender_.RecordTrace(); + + QuicConnectionPeer::SetSendAlgorithm(quic_sender_.connection(), sender_); + clock_ = simulator_.GetClock(); + simulator_.set_random_generator(&random_); + + uint64_t seed = QuicRandom::GetInstance()->RandUint64(); + random_.set_seed(seed); + QUIC_LOG(INFO) << "SendAlgorithmTest simulator set up. Seed: " << seed; + } + + // Creates a simulated network, with default settings between the + // sender and the switch and the given settings from the switch to + // the receiver. + void CreateSetup(const QuicBandwidth& test_bandwidth, + const QuicTime::Delta& test_link_delay, + QuicByteCount bottleneck_queue_length) { + switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8, + bottleneck_queue_length); + quic_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &quic_sender_, switch_->port(1), kLocalLinkBandwidth, + kLocalPropagationDelay); + receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>( + &receiver_, switch_->port(2), test_bandwidth, test_link_delay); + } + + void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta deadline) { + quic_sender_.AddBytesToTransfer(transfer_size); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return quic_sender_.bytes_to_transfer() == 0; }, deadline); + EXPECT_TRUE(simulator_result) + << "Simple transfer failed. Bytes remaining: " + << quic_sender_.bytes_to_transfer(); + } + + void SendBursts(size_t number_of_bursts, + QuicByteCount bytes, + QuicTime::Delta rtt, + QuicTime::Delta wait_time) { + ASSERT_EQ(0u, quic_sender_.bytes_to_transfer()); + for (size_t i = 0; i < number_of_bursts; i++) { + quic_sender_.AddBytesToTransfer(bytes); + + // Transfer data and wait for three seconds between each transfer. + simulator_.RunFor(wait_time); + + // Ensure the connection did not time out. + ASSERT_TRUE(quic_sender_.connection()->connected()); + ASSERT_TRUE(receiver_.connection()->connected()); + } + + simulator_.RunFor(wait_time + rtt); + EXPECT_EQ(0u, quic_sender_.bytes_to_transfer()); + } + + // Estimates the elapsed time for a given transfer size, given the + // bottleneck bandwidth and link propagation delay. + QuicTime::Delta EstimatedElapsedTime( + QuicByteCount transfer_size_bytes, + QuicBandwidth test_link_bandwidth, + const QuicTime::Delta& test_link_delay) const { + return test_link_bandwidth.TransferTime(transfer_size_bytes) + + 2 * test_link_delay; + } + + QuicTime QuicSenderStartTime() { + return quic_sender_.connection()->GetStats().connection_creation_time; + } + + void PrintTransferStats() { + const QuicConnectionStats& stats = quic_sender_.connection()->GetStats(); + QUIC_LOG(INFO) << "Summary for scenario " << GetParam(); + QUIC_LOG(INFO) << "Sender stats is " << stats; + const double rtx_rate = + static_cast<double>(stats.bytes_retransmitted) / stats.bytes_sent; + QUIC_LOG(INFO) << "Retransmit rate (num_rtx/num_total_sent): " << rtx_rate; + QUIC_LOG(INFO) << "Connection elapsed time: " + << (clock_->Now() - QuicSenderStartTime()).ToMilliseconds() + << " (ms)"; + } + + simulator::Simulator simulator_; + simulator::QuicEndpoint quic_sender_; + simulator::QuicEndpoint receiver_; + std::unique_ptr<simulator::Switch> switch_; + std::unique_ptr<simulator::SymmetricLink> quic_sender_link_; + std::unique_ptr<simulator::SymmetricLink> receiver_link_; + QuicConnectionStats stats_; + + SimpleRandom random_; + + // Owned by different components of the connection. + const QuicClock* clock_; + const RttStats* rtt_stats_; + SendAlgorithmInterface* sender_; +}; + +INSTANTIATE_TEST_SUITE_P(SendAlgorithmTests, SendAlgorithmTest, + ::testing::ValuesIn(GetTestParams()), + TestParamToString); + +// Test a simple long data transfer in the default setup. +TEST_P(SendAlgorithmTest, SimpleWiredNetworkTransfer) { + CreateSetup(kTestLinkWiredBandwidth, kTestLinkWiredPropagationDelay, + kTestWiredBdp); + const QuicByteCount kTransferSizeBytes = 12 * 1024 * 1024; + const QuicTime::Delta maximum_elapsed_time = + EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth, + kTestLinkWiredPropagationDelay) * + 1.2; + DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time); + PrintTransferStats(); +} + +TEST_P(SendAlgorithmTest, LowBdpPolicedNetworkTransfer) { + CreateSetup(kTestLinkLowBdpBandwidth, kTestLinkLowBdpPropagationDelay, + kTestPolicerQueue); + const QuicByteCount kTransferSizeBytes = 5 * 1024 * 1024; + const QuicTime::Delta maximum_elapsed_time = + EstimatedElapsedTime(kTransferSizeBytes, kTestLinkLowBdpBandwidth, + kTestLinkLowBdpPropagationDelay) * + 1.2; + DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time); + PrintTransferStats(); +} + +TEST_P(SendAlgorithmTest, AppLimitedBurstsOverWiredNetwork) { + CreateSetup(kTestLinkWiredBandwidth, kTestLinkWiredPropagationDelay, + kTestWiredBdp); + const QuicByteCount kBurstSizeBytes = 512; + const int kNumBursts = 20; + const QuicTime::Delta kWaitTime = QuicTime::Delta::FromSeconds(3); + SendBursts(kNumBursts, kBurstSizeBytes, kTestWiredRtt, kWaitTime); + PrintTransferStats(); + + const QuicTime::Delta estimated_burst_time = + EstimatedElapsedTime(kBurstSizeBytes, kTestLinkWiredBandwidth, + kTestLinkWiredPropagationDelay) + + kWaitTime; + const QuicTime::Delta max_elapsed_time = + kNumBursts * estimated_burst_time + kWaitTime; + const QuicTime::Delta actual_elapsed_time = + clock_->Now() - QuicSenderStartTime(); + EXPECT_GE(max_elapsed_time, actual_elapsed_time); +} + +TEST_P(SendAlgorithmTest, SatelliteNetworkTransfer) { + CreateSetup(kTestLinkWiredBandwidth, kTestSatellitePropagationDelay, + kTestWiredBdp); + const QuicByteCount kTransferSizeBytes = 12 * 1024 * 1024; + const QuicTime::Delta maximum_elapsed_time = + EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth, + kTestSatellitePropagationDelay) * + 1.25; + DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time); + PrintTransferStats(); +} + +TEST_P(SendAlgorithmTest, 2GNetworkTransfer) { + CreateSetup(kTestLink2GBandwidth, kTestCellularPropagationDelay, + kCellularQueue); + const QuicByteCount kTransferSizeBytes = 1024 * 1024; + const QuicTime::Delta maximum_elapsed_time = + EstimatedElapsedTime(kTransferSizeBytes, kTestLink2GBandwidth, + kTestCellularPropagationDelay) * + 1.2; + DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time); + PrintTransferStats(); +} + +TEST_P(SendAlgorithmTest, 3GNetworkTransfer) { + CreateSetup(kTestLink3GBandwidth, kTestCellularPropagationDelay, + kCellularQueue); + const QuicByteCount kTransferSizeBytes = 5 * 1024 * 1024; + const QuicTime::Delta maximum_elapsed_time = + EstimatedElapsedTime(kTransferSizeBytes, kTestLink3GBandwidth, + kTestCellularPropagationDelay) * + 1.2; + DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time); + PrintTransferStats(); +} + +TEST_P(SendAlgorithmTest, LowRTTTransfer) { + CreateSetup(kTestLinkWiredBandwidth, kTestLinkSmallRTTDelay, kCellularQueue); + + const QuicByteCount kTransferSizeBytes = 12 * 1024 * 1024; + const QuicTime::Delta maximum_elapsed_time = + EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth, + kTestLinkSmallRTTDelay) * + 1.2; + DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time); + PrintTransferStats(); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/tcp_cubic_sender_bytes.cc b/quic/core/congestion_control/tcp_cubic_sender_bytes.cc new file mode 100644 index 0000000..5b843eb --- /dev/null +++ b/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
@@ -0,0 +1,436 @@ +// Copyright (c) 2015 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/congestion_control/tcp_cubic_sender_bytes.h" + +#include <algorithm> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace { +// Constants based on TCP defaults. +const QuicByteCount kMaxBurstBytes = 3 * kDefaultTCPMSS; +const float kRenoBeta = 0.7f; // Reno backoff factor. +// The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a +// fast retransmission. +const QuicByteCount kDefaultMinimumCongestionWindow = 2 * kDefaultTCPMSS; +} // namespace + +TcpCubicSenderBytes::TcpCubicSenderBytes( + const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_congestion_window, + QuicConnectionStats* stats) + : rtt_stats_(rtt_stats), + stats_(stats), + reno_(reno), + num_connections_(kDefaultNumConnections), + min4_mode_(false), + last_cutback_exited_slowstart_(false), + slow_start_large_reduction_(false), + no_prr_(false), + cubic_(clock), + num_acked_packets_(0), + congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS), + min_congestion_window_(kDefaultMinimumCongestionWindow), + max_congestion_window_(max_congestion_window * kDefaultTCPMSS), + slowstart_threshold_(max_congestion_window * kDefaultTCPMSS), + initial_tcp_congestion_window_(initial_tcp_congestion_window * + kDefaultTCPMSS), + initial_max_tcp_congestion_window_(max_congestion_window * + kDefaultTCPMSS), + min_slow_start_exit_window_(min_congestion_window_) {} + +TcpCubicSenderBytes::~TcpCubicSenderBytes() {} + +void TcpCubicSenderBytes::SetFromConfig(const QuicConfig& config, + Perspective perspective) { + if (perspective == Perspective::IS_SERVER) { + if (!GetQuicReloadableFlag(quic_unified_iw_options)) { + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW03)) { + // Initial window experiment. + SetInitialCongestionWindowInPackets(3); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { + // Initial window experiment. + SetInitialCongestionWindowInPackets(10); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW20)) { + // Initial window experiment. + SetInitialCongestionWindowInPackets(20); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW50)) { + // Initial window experiment. + SetInitialCongestionWindowInPackets(50); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN1)) { + // Min CWND experiment. + SetMinCongestionWindowInPackets(1); + } + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN4)) { + // Min CWND of 4 experiment. + min4_mode_ = true; + SetMinCongestionWindowInPackets(1); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kSSLR)) { + // Slow Start Fast Exit experiment. + slow_start_large_reduction_ = true; + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kNPRR)) { + // Use unity pacing instead of PRR. + no_prr_ = true; + } + } +} + +void TcpCubicSenderBytes::AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + if (bandwidth.IsZero() || rtt.IsZero()) { + return; + } + + SetCongestionWindowFromBandwidthAndRtt(bandwidth, rtt); +} + +float TcpCubicSenderBytes::RenoBeta() const { + // kNConnectionBeta is the backoff factor after loss for our N-connection + // emulation, which emulates the effective backoff of an ensemble of N + // TCP-Reno connections on a single loss event. The effective multiplier is + // computed as: + return (num_connections_ - 1 + kRenoBeta) / num_connections_; +} + +void TcpCubicSenderBytes::OnCongestionEvent( + bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets) { + if (rtt_updated && InSlowStart() && + hybrid_slow_start_.ShouldExitSlowStart( + rtt_stats_->latest_rtt(), rtt_stats_->min_rtt(), + GetCongestionWindow() / kDefaultTCPMSS)) { + ExitSlowstart(); + } + for (const LostPacket& lost_packet : lost_packets) { + OnPacketLost(lost_packet.packet_number, lost_packet.bytes_lost, + prior_in_flight); + } + for (const AckedPacket acked_packet : acked_packets) { + OnPacketAcked(acked_packet.packet_number, acked_packet.bytes_acked, + prior_in_flight, event_time); + } +} + +void TcpCubicSenderBytes::OnPacketAcked(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount prior_in_flight, + QuicTime event_time) { + if (largest_acked_packet_number_.IsInitialized()) { + largest_acked_packet_number_ = + std::max(acked_packet_number, largest_acked_packet_number_); + } else { + largest_acked_packet_number_ = acked_packet_number; + } + if (InRecovery()) { + if (!no_prr_) { + // PRR is used when in recovery. + prr_.OnPacketAcked(acked_bytes); + } + return; + } + MaybeIncreaseCwnd(acked_packet_number, acked_bytes, prior_in_flight, + event_time); + if (InSlowStart()) { + hybrid_slow_start_.OnPacketAcked(acked_packet_number); + } +} + +void TcpCubicSenderBytes::OnPacketSent( + QuicTime /*sent_time*/, + QuicByteCount /*bytes_in_flight*/, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) { + if (InSlowStart()) { + ++(stats_->slowstart_packets_sent); + } + + if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { + return; + } + if (InRecovery()) { + // PRR is used when in recovery. + prr_.OnPacketSent(bytes); + } + DCHECK(!largest_sent_packet_number_.IsInitialized() || + largest_sent_packet_number_ < packet_number); + largest_sent_packet_number_ = packet_number; + hybrid_slow_start_.OnPacketSent(packet_number); +} + +bool TcpCubicSenderBytes::CanSend(QuicByteCount bytes_in_flight) { + if (!no_prr_ && InRecovery()) { + // PRR is used when in recovery. + return prr_.CanSend(GetCongestionWindow(), bytes_in_flight, + GetSlowStartThreshold()); + } + if (GetCongestionWindow() > bytes_in_flight) { + return true; + } + if (min4_mode_ && bytes_in_flight < 4 * kDefaultTCPMSS) { + return true; + } + return false; +} + +QuicBandwidth TcpCubicSenderBytes::PacingRate( + QuicByteCount /* bytes_in_flight */) const { + // We pace at twice the rate of the underlying sender's bandwidth estimate + // during slow start and 1.25x during congestion avoidance to ensure pacing + // doesn't prevent us from filling the window. + QuicTime::Delta srtt = rtt_stats_->SmoothedOrInitialRtt(); + const QuicBandwidth bandwidth = + QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); + return bandwidth * (InSlowStart() ? 2 : (no_prr_ && InRecovery() ? 1 : 1.25)); +} + +QuicBandwidth TcpCubicSenderBytes::BandwidthEstimate() const { + QuicTime::Delta srtt = rtt_stats_->smoothed_rtt(); + if (srtt.IsZero()) { + // If we haven't measured an rtt, the bandwidth estimate is unknown. + return QuicBandwidth::Zero(); + } + return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); +} + +bool TcpCubicSenderBytes::InSlowStart() const { + return GetCongestionWindow() < GetSlowStartThreshold(); +} + +bool TcpCubicSenderBytes::IsCwndLimited(QuicByteCount bytes_in_flight) const { + const QuicByteCount congestion_window = GetCongestionWindow(); + if (bytes_in_flight >= congestion_window) { + return true; + } + const QuicByteCount available_bytes = congestion_window - bytes_in_flight; + const bool slow_start_limited = + InSlowStart() && bytes_in_flight > congestion_window / 2; + return slow_start_limited || available_bytes <= kMaxBurstBytes; +} + +bool TcpCubicSenderBytes::InRecovery() const { + return largest_acked_packet_number_.IsInitialized() && + largest_sent_at_last_cutback_.IsInitialized() && + largest_acked_packet_number_ <= largest_sent_at_last_cutback_; +} + +bool TcpCubicSenderBytes::ShouldSendProbingPacket() const { + return false; +} + +void TcpCubicSenderBytes::OnRetransmissionTimeout(bool packets_retransmitted) { + largest_sent_at_last_cutback_.Clear(); + if (!packets_retransmitted) { + return; + } + hybrid_slow_start_.Restart(); + HandleRetransmissionTimeout(); +} + +QuicString TcpCubicSenderBytes::GetDebugState() const { + return ""; +} + +void TcpCubicSenderBytes::OnApplicationLimited(QuicByteCount bytes_in_flight) {} + +void TcpCubicSenderBytes::SetCongestionWindowFromBandwidthAndRtt( + QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + QuicByteCount new_congestion_window = bandwidth.ToBytesPerPeriod(rtt); + // Limit new CWND if needed. + congestion_window_ = + std::max(min_congestion_window_, + std::min(new_congestion_window, + kMaxResumptionCongestionWindow * kDefaultTCPMSS)); +} + +void TcpCubicSenderBytes::SetInitialCongestionWindowInPackets( + QuicPacketCount congestion_window) { + congestion_window_ = congestion_window * kDefaultTCPMSS; +} + +void TcpCubicSenderBytes::SetMinCongestionWindowInPackets( + QuicPacketCount congestion_window) { + min_congestion_window_ = congestion_window * kDefaultTCPMSS; +} + +void TcpCubicSenderBytes::SetNumEmulatedConnections(int num_connections) { + num_connections_ = std::max(1, num_connections); + cubic_.SetNumConnections(num_connections_); +} + +void TcpCubicSenderBytes::ExitSlowstart() { + slowstart_threshold_ = congestion_window_; +} + +void TcpCubicSenderBytes::OnPacketLost(QuicPacketNumber packet_number, + QuicByteCount lost_bytes, + QuicByteCount prior_in_flight) { + // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets + // already sent should be treated as a single loss event, since it's expected. + if (largest_sent_at_last_cutback_.IsInitialized() && + packet_number <= largest_sent_at_last_cutback_) { + if (last_cutback_exited_slowstart_) { + ++stats_->slowstart_packets_lost; + stats_->slowstart_bytes_lost += lost_bytes; + if (slow_start_large_reduction_) { + // Reduce congestion window by lost_bytes for every loss. + congestion_window_ = std::max(congestion_window_ - lost_bytes, + min_slow_start_exit_window_); + slowstart_threshold_ = congestion_window_; + } + } + QUIC_DVLOG(1) << "Ignoring loss for largest_missing:" << packet_number + << " because it was sent prior to the last CWND cutback."; + return; + } + ++stats_->tcp_loss_events; + last_cutback_exited_slowstart_ = InSlowStart(); + if (InSlowStart()) { + ++stats_->slowstart_packets_lost; + } + + if (!no_prr_) { + prr_.OnPacketLost(prior_in_flight); + } + + // TODO(b/77268641): Separate out all of slow start into a separate class. + if (slow_start_large_reduction_ && InSlowStart()) { + DCHECK_LT(kDefaultTCPMSS, congestion_window_); + if (congestion_window_ >= 2 * initial_tcp_congestion_window_) { + min_slow_start_exit_window_ = congestion_window_ / 2; + } + congestion_window_ = congestion_window_ - kDefaultTCPMSS; + } else if (reno_) { + congestion_window_ = congestion_window_ * RenoBeta(); + } else { + congestion_window_ = + cubic_.CongestionWindowAfterPacketLoss(congestion_window_); + } + if (congestion_window_ < min_congestion_window_) { + congestion_window_ = min_congestion_window_; + } + slowstart_threshold_ = congestion_window_; + largest_sent_at_last_cutback_ = largest_sent_packet_number_; + // Reset packet count from congestion avoidance mode. We start counting again + // when we're out of recovery. + num_acked_packets_ = 0; + QUIC_DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; +} + +QuicByteCount TcpCubicSenderBytes::GetCongestionWindow() const { + return congestion_window_; +} + +QuicByteCount TcpCubicSenderBytes::GetSlowStartThreshold() const { + return slowstart_threshold_; +} + +// Called when we receive an ack. Normal TCP tracks how many packets one ack +// represents, but quic has a separate ack for each packet. +void TcpCubicSenderBytes::MaybeIncreaseCwnd( + QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount prior_in_flight, + QuicTime event_time) { + QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery."; + // Do not increase the congestion window unless the sender is close to using + // the current window. + if (!IsCwndLimited(prior_in_flight)) { + cubic_.OnApplicationLimited(); + return; + } + if (congestion_window_ >= max_congestion_window_) { + return; + } + if (InSlowStart()) { + // TCP slow start, exponential growth, increase by one for each ACK. + congestion_window_ += kDefaultTCPMSS; + QUIC_DVLOG(1) << "Slow start; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; + return; + } + // Congestion avoidance. + if (reno_) { + // Classic Reno congestion avoidance. + ++num_acked_packets_; + // Divide by num_connections to smoothly increase the CWND at a faster rate + // than conventional Reno. + if (num_acked_packets_ * num_connections_ >= + congestion_window_ / kDefaultTCPMSS) { + congestion_window_ += kDefaultTCPMSS; + num_acked_packets_ = 0; + } + + QUIC_DVLOG(1) << "Reno; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_ + << " congestion window count: " << num_acked_packets_; + } else { + congestion_window_ = std::min( + max_congestion_window_, + cubic_.CongestionWindowAfterAck(acked_bytes, congestion_window_, + rtt_stats_->min_rtt(), event_time)); + QUIC_DVLOG(1) << "Cubic; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; + } +} + +void TcpCubicSenderBytes::HandleRetransmissionTimeout() { + cubic_.ResetCubicState(); + slowstart_threshold_ = congestion_window_ / 2; + congestion_window_ = min_congestion_window_; +} + +void TcpCubicSenderBytes::OnConnectionMigration() { + hybrid_slow_start_.Restart(); + prr_ = PrrSender(); + largest_sent_packet_number_.Clear(); + largest_acked_packet_number_.Clear(); + largest_sent_at_last_cutback_.Clear(); + last_cutback_exited_slowstart_ = false; + cubic_.ResetCubicState(); + num_acked_packets_ = 0; + congestion_window_ = initial_tcp_congestion_window_; + max_congestion_window_ = initial_max_tcp_congestion_window_; + slowstart_threshold_ = initial_max_tcp_congestion_window_; +} + +CongestionControlType TcpCubicSenderBytes::GetCongestionControlType() const { + return reno_ ? kRenoBytes : kCubicBytes; +} + +} // namespace quic
diff --git a/quic/core/congestion_control/tcp_cubic_sender_bytes.h b/quic/core/congestion_control/tcp_cubic_sender_bytes.h new file mode 100644 index 0000000..0ecb6f4 --- /dev/null +++ b/quic/core/congestion_control/tcp_cubic_sender_bytes.h
@@ -0,0 +1,173 @@ +// Copyright (c) 2015 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. + +// TCP cubic send side congestion algorithm, emulates the behavior of TCP cubic. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class RttStats; + +// Maximum window to allow when doing bandwidth resumption. +const QuicPacketCount kMaxResumptionCongestionWindow = 200; + +namespace test { +class TcpCubicSenderBytesPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE TcpCubicSenderBytes : public SendAlgorithmInterface { + public: + TcpCubicSenderBytes(const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_congestion_window, + QuicConnectionStats* stats); + TcpCubicSenderBytes(const TcpCubicSenderBytes&) = delete; + TcpCubicSenderBytes& operator=(const TcpCubicSenderBytes&) = delete; + ~TcpCubicSenderBytes() override; + + // Start implementation of SendAlgorithmInterface. + void SetFromConfig(const QuicConfig& config, + Perspective perspective) override; + void AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) override; + void SetNumEmulatedConnections(int num_connections) override; + void SetInitialCongestionWindowInPackets( + QuicPacketCount congestion_window) override; + void OnConnectionMigration() override; + void OnCongestionEvent(bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets) override; + void OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) override; + void OnRetransmissionTimeout(bool packets_retransmitted) override; + bool CanSend(QuicByteCount bytes_in_flight) override; + QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const override; + QuicBandwidth BandwidthEstimate() const override; + QuicByteCount GetCongestionWindow() const override; + QuicByteCount GetSlowStartThreshold() const override; + CongestionControlType GetCongestionControlType() const override; + bool InSlowStart() const override; + bool InRecovery() const override; + bool ShouldSendProbingPacket() const override; + QuicString GetDebugState() const override; + void OnApplicationLimited(QuicByteCount bytes_in_flight) override; + // End implementation of SendAlgorithmInterface. + + QuicByteCount min_congestion_window() const { return min_congestion_window_; } + + protected: + // Compute the TCP Reno beta based on the current number of connections. + float RenoBeta() const; + + bool IsCwndLimited(QuicByteCount bytes_in_flight) const; + + // TODO(ianswett): Remove these and migrate to OnCongestionEvent. + void OnPacketAcked(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount prior_in_flight, + QuicTime event_time); + void SetCongestionWindowFromBandwidthAndRtt(QuicBandwidth bandwidth, + QuicTime::Delta rtt); + void SetMinCongestionWindowInPackets(QuicPacketCount congestion_window); + void ExitSlowstart(); + void OnPacketLost(QuicPacketNumber largest_loss, + QuicByteCount lost_bytes, + QuicByteCount prior_in_flight); + void MaybeIncreaseCwnd(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount prior_in_flight, + QuicTime event_time); + void HandleRetransmissionTimeout(); + + private: + friend class test::TcpCubicSenderBytesPeer; + + HybridSlowStart hybrid_slow_start_; + PrrSender prr_; + const RttStats* rtt_stats_; + QuicConnectionStats* stats_; + + // If true, Reno congestion control is used instead of Cubic. + const bool reno_; + + // Number of connections to simulate. + uint32_t num_connections_; + + // Track the largest packet that has been sent. + QuicPacketNumber largest_sent_packet_number_; + + // Track the largest packet that has been acked. + QuicPacketNumber largest_acked_packet_number_; + + // Track the largest packet number outstanding when a CWND cutback occurs. + QuicPacketNumber largest_sent_at_last_cutback_; + + // Whether to use 4 packets as the actual min, but pace lower. + bool min4_mode_; + + // Whether the last loss event caused us to exit slowstart. + // Used for stats collection of slowstart_packets_lost + bool last_cutback_exited_slowstart_; + + // When true, exit slow start with large cutback of congestion window. + bool slow_start_large_reduction_; + + // When true, use unity pacing instead of PRR. + bool no_prr_; + + CubicBytes cubic_; + + // ACK counter for the Reno implementation. + uint64_t num_acked_packets_; + + // Congestion window in bytes. + QuicByteCount congestion_window_; + + // Minimum congestion window in bytes. + QuicByteCount min_congestion_window_; + + // Maximum congestion window in bytes. + QuicByteCount max_congestion_window_; + + // Slow start congestion window in bytes, aka ssthresh. + QuicByteCount slowstart_threshold_; + + // Initial TCP congestion window in bytes. This variable can only be set when + // this algorithm is created. + const QuicByteCount initial_tcp_congestion_window_; + + // Initial maximum TCP congestion window in bytes. This variable can only be + // set when this algorithm is created. + const QuicByteCount initial_max_tcp_congestion_window_; + + // The minimum window when exiting slow start with large reduction. + QuicByteCount min_slow_start_exit_window_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_
diff --git a/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc b/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc new file mode 100644 index 0000000..5eb6226 --- /dev/null +++ b/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc
@@ -0,0 +1,834 @@ +// Copyright (c) 2015 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/congestion_control/tcp_cubic_sender_bytes.h" + +#include <algorithm> +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" + +namespace quic { +namespace test { + +// TODO(ianswett): A number of theses tests were written with the assumption of +// an initial CWND of 10. They have carefully calculated values which should be +// updated to be based on kInitialCongestionWindow. +const uint32_t kInitialCongestionWindowPackets = 10; +const uint32_t kMaxCongestionWindowPackets = 200; +const uint32_t kDefaultWindowTCP = + kInitialCongestionWindowPackets * kDefaultTCPMSS; +const float kRenoBeta = 0.7f; // Reno backoff factor. + +class TcpCubicSenderBytesPeer : public TcpCubicSenderBytes { + public: + TcpCubicSenderBytesPeer(const QuicClock* clock, bool reno) + : TcpCubicSenderBytes(clock, + &rtt_stats_, + reno, + kInitialCongestionWindowPackets, + kMaxCongestionWindowPackets, + &stats_) {} + + const HybridSlowStart& hybrid_slow_start() const { + return hybrid_slow_start_; + } + + float GetRenoBeta() const { return RenoBeta(); } + + RttStats rtt_stats_; + QuicConnectionStats stats_; +}; + +class TcpCubicSenderBytesTest : public QuicTest { + protected: + TcpCubicSenderBytesTest() + : one_ms_(QuicTime::Delta::FromMilliseconds(1)), + sender_(new TcpCubicSenderBytesPeer(&clock_, true)), + packet_number_(1), + acked_packet_number_(0), + bytes_in_flight_(0) {} + + int SendAvailableSendWindow() { + return SendAvailableSendWindow(kDefaultTCPMSS); + } + + int SendAvailableSendWindow(QuicPacketLength packet_length) { + // Send as long as TimeUntilSend returns Zero. + int packets_sent = 0; + bool can_send = sender_->CanSend(bytes_in_flight_); + while (can_send) { + sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, + QuicPacketNumber(packet_number_++), kDefaultTCPMSS, + HAS_RETRANSMITTABLE_DATA); + ++packets_sent; + bytes_in_flight_ += kDefaultTCPMSS; + can_send = sender_->CanSend(bytes_in_flight_); + } + return packets_sent; + } + + // Normal is that TCP acks every other segment. + void AckNPackets(int n) { + sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60), + QuicTime::Delta::Zero(), clock_.Now()); + AckedPacketVector acked_packets; + LostPacketVector lost_packets; + for (int i = 0; i < n; ++i) { + ++acked_packet_number_; + acked_packets.push_back( + AckedPacket(QuicPacketNumber(acked_packet_number_), kDefaultTCPMSS, + QuicTime::Zero())); + } + sender_->OnCongestionEvent(true, bytes_in_flight_, clock_.Now(), + acked_packets, lost_packets); + bytes_in_flight_ -= n * kDefaultTCPMSS; + clock_.AdvanceTime(one_ms_); + } + + void LoseNPackets(int n) { LoseNPackets(n, kDefaultTCPMSS); } + + void LoseNPackets(int n, QuicPacketLength packet_length) { + AckedPacketVector acked_packets; + LostPacketVector lost_packets; + for (int i = 0; i < n; ++i) { + ++acked_packet_number_; + lost_packets.push_back( + LostPacket(QuicPacketNumber(acked_packet_number_), packet_length)); + } + sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(), + acked_packets, lost_packets); + bytes_in_flight_ -= n * packet_length; + } + + // Does not increment acked_packet_number_. + void LosePacket(uint64_t packet_number) { + AckedPacketVector acked_packets; + LostPacketVector lost_packets; + lost_packets.push_back( + LostPacket(QuicPacketNumber(packet_number), kDefaultTCPMSS)); + sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(), + acked_packets, lost_packets); + bytes_in_flight_ -= kDefaultTCPMSS; + } + + const QuicTime::Delta one_ms_; + MockClock clock_; + std::unique_ptr<TcpCubicSenderBytesPeer> sender_; + uint64_t packet_number_; + uint64_t acked_packet_number_; + QuicByteCount bytes_in_flight_; +}; + +TEST_F(TcpCubicSenderBytesTest, SimpleSender) { + // At startup make sure we are at the default. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + // At startup make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + // Make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + // And that window is un-affected. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + + // Fill the send window with data, then verify that we can't send. + SendAvailableSendWindow(); + EXPECT_FALSE(sender_->CanSend(sender_->GetCongestionWindow())); +} + +TEST_F(TcpCubicSenderBytesTest, ApplicationLimitedSlowStart) { + // Send exactly 10 packets and ensure the CWND ends at 14 packets. + const int kNumberOfAcks = 5; + // At startup make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + // Make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + + SendAvailableSendWindow(); + for (int i = 0; i < kNumberOfAcks; ++i) { + AckNPackets(2); + } + QuicByteCount bytes_to_send = sender_->GetCongestionWindow(); + // It's expected 2 acks will arrive when the bytes_in_flight are greater than + // half the CWND. + EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * 2, bytes_to_send); +} + +TEST_F(TcpCubicSenderBytesTest, ExponentialSlowStart) { + const int kNumberOfAcks = 20; + // At startup make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + EXPECT_EQ(QuicBandwidth::Zero(), sender_->BandwidthEstimate()); + // Make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + const QuicByteCount cwnd = sender_->GetCongestionWindow(); + EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAcks, cwnd); + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta( + cwnd, sender_->rtt_stats_.smoothed_rtt()), + sender_->BandwidthEstimate()); +} + +TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLoss) { + sender_->SetNumEmulatedConnections(1); + const int kNumberOfAcks = 10; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose a packet to exit slow start. + LoseNPackets(1); + size_t packets_in_recovery_window = expected_send_window / kDefaultTCPMSS; + + // We should now have fallen out of slow start with a reduced window. + expected_send_window *= kRenoBeta; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Recovery phase. We need to ack every packet in the recovery window before + // we exit recovery. + size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS; + QUIC_DLOG(INFO) << "number_packets: " << number_of_packets_in_window; + AckNPackets(packets_in_recovery_window); + SendAvailableSendWindow(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // We need to ack an entire window before we increase CWND by 1. + AckNPackets(number_of_packets_in_window - 2); + SendAvailableSendWindow(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Next ack should increase cwnd by 1. + AckNPackets(1); + expected_send_window += kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Now RTO and ensure slow start gets reset. + EXPECT_TRUE(sender_->hybrid_slow_start().started()); + sender_->OnRetransmissionTimeout(true); + EXPECT_FALSE(sender_->hybrid_slow_start().started()); +} + +TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLossWithLargeReduction) { + QuicConfig config; + QuicTagVector options; + options.push_back(kSSLR); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + + sender_->SetNumEmulatedConnections(1); + const int kNumberOfAcks = (kDefaultWindowTCP / (2 * kDefaultTCPMSS)) - 1; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose a packet to exit slow start. We should now have fallen out of + // slow start with a window reduced by 1. + LoseNPackets(1); + expected_send_window -= kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose 5 packets in recovery and verify that congestion window is reduced + // further. + LoseNPackets(5); + expected_send_window -= 5 * kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + // Lose another 10 packets and ensure it reduces below half the peak CWND, + // because we never acked the full IW. + LoseNPackets(10); + expected_send_window -= 10 * kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + size_t packets_in_recovery_window = expected_send_window / kDefaultTCPMSS; + + // Recovery phase. We need to ack every packet in the recovery window before + // we exit recovery. + size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS; + QUIC_DLOG(INFO) << "number_packets: " << number_of_packets_in_window; + AckNPackets(packets_in_recovery_window); + SendAvailableSendWindow(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // We need to ack an entire window before we increase CWND by 1. + AckNPackets(number_of_packets_in_window - 1); + SendAvailableSendWindow(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Next ack should increase cwnd by 1. + AckNPackets(1); + expected_send_window += kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Now RTO and ensure slow start gets reset. + EXPECT_TRUE(sender_->hybrid_slow_start().started()); + sender_->OnRetransmissionTimeout(true); + EXPECT_FALSE(sender_->hybrid_slow_start().started()); +} + +TEST_F(TcpCubicSenderBytesTest, SlowStartHalfPacketLossWithLargeReduction) { + QuicConfig config; + QuicTagVector options; + options.push_back(kSSLR); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + + sender_->SetNumEmulatedConnections(1); + const int kNumberOfAcks = 10; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window in half sized packets. + SendAvailableSendWindow(kDefaultTCPMSS / 2); + AckNPackets(2); + } + SendAvailableSendWindow(kDefaultTCPMSS / 2); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose a packet to exit slow start. We should now have fallen out of + // slow start with a window reduced by 1. + LoseNPackets(1); + expected_send_window -= kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose 10 packets in recovery and verify that congestion window is reduced + // by 5 packets. + LoseNPackets(10, kDefaultTCPMSS / 2); + expected_send_window -= 5 * kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLossWithMaxHalfReduction) { + QuicConfig config; + QuicTagVector options; + options.push_back(kSSLR); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + + sender_->SetNumEmulatedConnections(1); + const int kNumberOfAcks = kInitialCongestionWindowPackets / 2; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose a packet to exit slow start. We should now have fallen out of + // slow start with a window reduced by 1. + LoseNPackets(1); + expected_send_window -= kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose half the outstanding packets in recovery and verify the congestion + // window is only reduced by a max of half. + LoseNPackets(kNumberOfAcks * 2); + expected_send_window -= (kNumberOfAcks * 2 - 1) * kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + LoseNPackets(5); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, NoPRRWhenLessThanOnePacketInFlight) { + SendAvailableSendWindow(); + LoseNPackets(kInitialCongestionWindowPackets - 1); + AckNPackets(1); + // PRR will allow 2 packets for every ack during recovery. + EXPECT_EQ(2, SendAvailableSendWindow()); + // Simulate abandoning all packets by supplying a bytes_in_flight of 0. + // PRR should now allow a packet to be sent, even though prr's state variables + // believe it has sent enough packets. + EXPECT_TRUE(sender_->CanSend(0)); +} + +TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLossPRR) { + sender_->SetNumEmulatedConnections(1); + // Test based on the first example in RFC6937. + // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example. + const int kNumberOfAcks = 5; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + LoseNPackets(1); + + // We should now have fallen out of slow start with a reduced window. + size_t send_window_before_loss = expected_send_window; + expected_send_window *= kRenoBeta; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Testing TCP proportional rate reduction. + // We should send packets paced over the received acks for the remaining + // outstanding packets. The number of packets before we exit recovery is the + // original CWND minus the packet that has been lost and the one which + // triggered the loss. + size_t remaining_packets_in_recovery = + send_window_before_loss / kDefaultTCPMSS - 2; + + for (size_t i = 0; i < remaining_packets_in_recovery; ++i) { + AckNPackets(1); + SendAvailableSendWindow(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + + // We need to ack another window before we increase CWND by 1. + size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS; + for (size_t i = 0; i < number_of_packets_in_window; ++i) { + AckNPackets(1); + EXPECT_EQ(1, SendAvailableSendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + + AckNPackets(1); + expected_send_window += kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, SlowStartBurstPacketLossPRR) { + sender_->SetNumEmulatedConnections(1); + // Test based on the second example in RFC6937, though we also implement + // forward acknowledgements, so the first two incoming acks will trigger + // PRR immediately. + // Ack 20 packets in 10 acks to raise the CWND to 30. + const int kNumberOfAcks = 10; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Lose one more than the congestion window reduction, so that after loss, + // bytes_in_flight is lesser than the congestion window. + size_t send_window_after_loss = kRenoBeta * expected_send_window; + size_t num_packets_to_lose = + (expected_send_window - send_window_after_loss) / kDefaultTCPMSS + 1; + LoseNPackets(num_packets_to_lose); + // Immediately after the loss, ensure at least one packet can be sent. + // Losses without subsequent acks can occur with timer based loss detection. + EXPECT_TRUE(sender_->CanSend(bytes_in_flight_)); + AckNPackets(1); + + // We should now have fallen out of slow start with a reduced window. + expected_send_window *= kRenoBeta; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Only 2 packets should be allowed to be sent, per PRR-SSRB. + EXPECT_EQ(2, SendAvailableSendWindow()); + + // Ack the next packet, which triggers another loss. + LoseNPackets(1); + AckNPackets(1); + + // Send 2 packets to simulate PRR-SSRB. + EXPECT_EQ(2, SendAvailableSendWindow()); + + // Ack the next packet, which triggers another loss. + LoseNPackets(1); + AckNPackets(1); + + // Send 2 packets to simulate PRR-SSRB. + EXPECT_EQ(2, SendAvailableSendWindow()); + + // Exit recovery and return to sending at the new rate. + for (int i = 0; i < kNumberOfAcks; ++i) { + AckNPackets(1); + EXPECT_EQ(1, SendAvailableSendWindow()); + } +} + +TEST_F(TcpCubicSenderBytesTest, RTOCongestionWindow) { + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + // Expect the window to decrease to the minimum once the RTO fires and slow + // start threshold to be set to 1/2 of the CWND. + sender_->OnRetransmissionTimeout(true); + EXPECT_EQ(2 * kDefaultTCPMSS, sender_->GetCongestionWindow()); + EXPECT_EQ(5u * kDefaultTCPMSS, sender_->GetSlowStartThreshold()); +} + +TEST_F(TcpCubicSenderBytesTest, RTOCongestionWindowNoRetransmission) { + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + + // Expect the window to remain unchanged if the RTO fires but no packets are + // retransmitted. + sender_->OnRetransmissionTimeout(false); + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, TcpCubicResetEpochOnQuiescence) { + const int kMaxCongestionWindow = 50; + const QuicByteCount kMaxCongestionWindowBytes = + kMaxCongestionWindow * kDefaultTCPMSS; + int num_sent = SendAvailableSendWindow(); + + // Make sure we fall out of slow start. + QuicByteCount saved_cwnd = sender_->GetCongestionWindow(); + LoseNPackets(1); + EXPECT_GT(saved_cwnd, sender_->GetCongestionWindow()); + + // Ack the rest of the outstanding packets to get out of recovery. + for (int i = 1; i < num_sent; ++i) { + AckNPackets(1); + } + EXPECT_EQ(0u, bytes_in_flight_); + + // Send a new window of data and ack all; cubic growth should occur. + saved_cwnd = sender_->GetCongestionWindow(); + num_sent = SendAvailableSendWindow(); + for (int i = 0; i < num_sent; ++i) { + AckNPackets(1); + } + EXPECT_LT(saved_cwnd, sender_->GetCongestionWindow()); + EXPECT_GT(kMaxCongestionWindowBytes, sender_->GetCongestionWindow()); + EXPECT_EQ(0u, bytes_in_flight_); + + // Quiescent time of 100 seconds + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100000)); + + // Send new window of data and ack one packet. Cubic epoch should have + // been reset; ensure cwnd increase is not dramatic. + saved_cwnd = sender_->GetCongestionWindow(); + SendAvailableSendWindow(); + AckNPackets(1); + EXPECT_NEAR(saved_cwnd, sender_->GetCongestionWindow(), kDefaultTCPMSS); + EXPECT_GT(kMaxCongestionWindowBytes, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, MultipleLossesInOneWindow) { + SendAvailableSendWindow(); + const QuicByteCount initial_window = sender_->GetCongestionWindow(); + LosePacket(acked_packet_number_ + 1); + const QuicByteCount post_loss_window = sender_->GetCongestionWindow(); + EXPECT_GT(initial_window, post_loss_window); + LosePacket(acked_packet_number_ + 3); + EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow()); + LosePacket(packet_number_ - 1); + EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow()); + + // Lose a later packet and ensure the window decreases. + LosePacket(packet_number_); + EXPECT_GT(post_loss_window, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, ConfigureMaxInitialWindow) { + SetQuicReloadableFlag(quic_unified_iw_options, false); + QuicConfig config; + + // Verify that kCOPT: kIW10 forces the congestion window to the default of 10. + QuicTagVector options; + options.push_back(kIW10); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + EXPECT_EQ(10u * kDefaultTCPMSS, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, SetInitialCongestionWindow) { + EXPECT_NE(3u * kDefaultTCPMSS, sender_->GetCongestionWindow()); + sender_->SetInitialCongestionWindowInPackets(3); + EXPECT_EQ(3u * kDefaultTCPMSS, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, 2ConnectionCongestionAvoidanceAtEndOfRecovery) { + sender_->SetNumEmulatedConnections(2); + // Ack 10 packets in 5 acks to raise the CWND to 20. + const int kNumberOfAcks = 5; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + LoseNPackets(1); + + // We should now have fallen out of slow start with a reduced window. + expected_send_window = expected_send_window * sender_->GetRenoBeta(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // No congestion window growth should occur in recovery phase, i.e., until the + // currently outstanding 20 packets are acked. + for (int i = 0; i < 10; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + EXPECT_TRUE(sender_->InRecovery()); + AckNPackets(2); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + EXPECT_FALSE(sender_->InRecovery()); + + // Out of recovery now. Congestion window should not grow for half an RTT. + size_t packets_in_send_window = expected_send_window / kDefaultTCPMSS; + SendAvailableSendWindow(); + AckNPackets(packets_in_send_window / 2 - 2); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Next ack should increase congestion window by 1MSS. + SendAvailableSendWindow(); + AckNPackets(2); + expected_send_window += kDefaultTCPMSS; + packets_in_send_window += 1; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Congestion window should remain steady again for half an RTT. + SendAvailableSendWindow(); + AckNPackets(packets_in_send_window / 2 - 1); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Next ack should cause congestion window to grow by 1MSS. + SendAvailableSendWindow(); + AckNPackets(2); + expected_send_window += kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, 1ConnectionCongestionAvoidanceAtEndOfRecovery) { + sender_->SetNumEmulatedConnections(1); + // Ack 10 packets in 5 acks to raise the CWND to 20. + const int kNumberOfAcks = 5; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + LoseNPackets(1); + + // We should now have fallen out of slow start with a reduced window. + expected_send_window *= kRenoBeta; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // No congestion window growth should occur in recovery phase, i.e., until the + // currently outstanding 20 packets are acked. + for (int i = 0; i < 10; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + EXPECT_TRUE(sender_->InRecovery()); + AckNPackets(2); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + EXPECT_FALSE(sender_->InRecovery()); + + // Out of recovery now. Congestion window should not grow during RTT. + for (uint64_t i = 0; i < expected_send_window / kDefaultTCPMSS - 2; i += 2) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + + // Next ack should cause congestion window to grow by 1MSS. + SendAvailableSendWindow(); + AckNPackets(2); + expected_send_window += kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, BandwidthResumption) { + // Test that when provided with CachedNetworkParameters and opted in to the + // bandwidth resumption experiment, that the TcpCubicSenderPackets sets + // initial CWND appropriately. + + // Set some common values. + const QuicPacketCount kNumberOfPackets = 123; + const QuicBandwidth kBandwidthEstimate = + QuicBandwidth::FromBytesPerSecond(kNumberOfPackets * kDefaultTCPMSS); + const QuicTime::Delta kRttEstimate = QuicTime::Delta::FromSeconds(1); + sender_->AdjustNetworkParameters(kBandwidthEstimate, kRttEstimate); + EXPECT_EQ(kNumberOfPackets * kDefaultTCPMSS, sender_->GetCongestionWindow()); + + // Resume with an illegal value of 0 and verify the server ignores it. + sender_->AdjustNetworkParameters(QuicBandwidth::Zero(), kRttEstimate); + EXPECT_EQ(kNumberOfPackets * kDefaultTCPMSS, sender_->GetCongestionWindow()); + + // Resumed CWND is limited to be in a sensible range. + const QuicBandwidth kUnreasonableBandwidth = + QuicBandwidth::FromBytesPerSecond((kMaxCongestionWindowPackets + 1) * + kDefaultTCPMSS); + sender_->AdjustNetworkParameters(kUnreasonableBandwidth, + QuicTime::Delta::FromSeconds(1)); + EXPECT_EQ(kMaxCongestionWindowPackets * kDefaultTCPMSS, + sender_->GetCongestionWindow()); +} + +TEST_F(TcpCubicSenderBytesTest, PaceBelowCWND) { + QuicConfig config; + + // Verify that kCOPT: kMIN4 forces the min CWND to 1 packet, but allows up + // to 4 to be sent. + QuicTagVector options; + options.push_back(kMIN4); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + sender_->OnRetransmissionTimeout(true); + EXPECT_EQ(kDefaultTCPMSS, sender_->GetCongestionWindow()); + EXPECT_TRUE(sender_->CanSend(kDefaultTCPMSS)); + EXPECT_TRUE(sender_->CanSend(2 * kDefaultTCPMSS)); + EXPECT_TRUE(sender_->CanSend(3 * kDefaultTCPMSS)); + EXPECT_FALSE(sender_->CanSend(4 * kDefaultTCPMSS)); +} + +TEST_F(TcpCubicSenderBytesTest, NoPRR) { + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100); + sender_->rtt_stats_.UpdateRtt(rtt, QuicTime::Delta::Zero(), QuicTime::Zero()); + + sender_->SetNumEmulatedConnections(1); + // Verify that kCOPT: kNPRR allows all packets to be sent, even if only one + // ack has been received. + QuicTagVector options; + options.push_back(kNPRR); + QuicConfig config; + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + SendAvailableSendWindow(); + LoseNPackets(9); + AckNPackets(1); + + // We should now have fallen out of slow start with a reduced window. + EXPECT_EQ(kRenoBeta * kDefaultWindowTCP, sender_->GetCongestionWindow()); + const QuicPacketCount window_in_packets = + kRenoBeta * kDefaultWindowTCP / kDefaultTCPMSS; + const QuicBandwidth expected_pacing_rate = + QuicBandwidth::FromBytesAndTimeDelta(kRenoBeta * kDefaultWindowTCP, + sender_->rtt_stats_.smoothed_rtt()); + EXPECT_EQ(expected_pacing_rate, sender_->PacingRate(0)); + EXPECT_EQ(window_in_packets, + static_cast<uint64_t>(SendAvailableSendWindow())); + EXPECT_EQ(expected_pacing_rate, + sender_->PacingRate(kRenoBeta * kDefaultWindowTCP)); +} + +TEST_F(TcpCubicSenderBytesTest, ResetAfterConnectionMigration) { + // Starts from slow start. + sender_->SetNumEmulatedConnections(1); + const int kNumberOfAcks = 10; + for (int i = 0; i < kNumberOfAcks; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Loses a packet to exit slow start. + LoseNPackets(1); + + // We should now have fallen out of slow start with a reduced window. Slow + // start threshold is also updated. + expected_send_window *= kRenoBeta; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + EXPECT_EQ(expected_send_window, sender_->GetSlowStartThreshold()); + + // Resets cwnd and slow start threshold on connection migrations. + sender_->OnConnectionMigration(); + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + EXPECT_EQ(kMaxCongestionWindowPackets * kDefaultTCPMSS, + sender_->GetSlowStartThreshold()); + EXPECT_FALSE(sender_->hybrid_slow_start().started()); +} + +TEST_F(TcpCubicSenderBytesTest, DefaultMaxCwnd) { + RttStats rtt_stats; + QuicConnectionStats stats; + std::unique_ptr<SendAlgorithmInterface> sender(SendAlgorithmInterface::Create( + &clock_, &rtt_stats, /*unacked_packets=*/nullptr, kCubicBytes, + QuicRandom::GetInstance(), &stats, kInitialCongestionWindow)); + + AckedPacketVector acked_packets; + LostPacketVector missing_packets; + for (uint64_t i = 1; i < kDefaultMaxCongestionWindowPackets; ++i) { + acked_packets.clear(); + acked_packets.push_back( + AckedPacket(QuicPacketNumber(i), 1350, QuicTime::Zero())); + sender->OnCongestionEvent(true, sender->GetCongestionWindow(), clock_.Now(), + acked_packets, missing_packets); + } + EXPECT_EQ(kDefaultMaxCongestionWindowPackets, + sender->GetCongestionWindow() / kDefaultTCPMSS); +} + +TEST_F(TcpCubicSenderBytesTest, LimitCwndIncreaseInCongestionAvoidance) { + // Enable Cubic. + sender_ = QuicMakeUnique<TcpCubicSenderBytesPeer>(&clock_, false); + + int num_sent = SendAvailableSendWindow(); + + // Make sure we fall out of slow start. + QuicByteCount saved_cwnd = sender_->GetCongestionWindow(); + LoseNPackets(1); + EXPECT_GT(saved_cwnd, sender_->GetCongestionWindow()); + + // Ack the rest of the outstanding packets to get out of recovery. + for (int i = 1; i < num_sent; ++i) { + AckNPackets(1); + } + EXPECT_EQ(0u, bytes_in_flight_); + // Send a new window of data and ack all; cubic growth should occur. + saved_cwnd = sender_->GetCongestionWindow(); + num_sent = SendAvailableSendWindow(); + + // Ack packets until the CWND increases. + while (sender_->GetCongestionWindow() == saved_cwnd) { + AckNPackets(1); + SendAvailableSendWindow(); + } + // Bytes in flight may be larger than the CWND if the CWND isn't an exact + // multiple of the packet sizes being sent. + EXPECT_GE(bytes_in_flight_, sender_->GetCongestionWindow()); + saved_cwnd = sender_->GetCongestionWindow(); + + // Advance time 2 seconds waiting for an ack. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2000)); + + // Ack two packets. The CWND should increase by only one packet. + AckNPackets(2); + EXPECT_EQ(saved_cwnd + kDefaultTCPMSS, sender_->GetCongestionWindow()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.cc b/quic/core/congestion_control/uber_loss_algorithm.cc new file mode 100644 index 0000000..8db151b --- /dev/null +++ b/quic/core/congestion_control/uber_loss_algorithm.cc
@@ -0,0 +1,86 @@ +// Copyright 2019 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/congestion_control/uber_loss_algorithm.h" + +#include <algorithm> + +namespace quic { + +UberLossAlgorithm::UberLossAlgorithm() : UberLossAlgorithm(kNack) {} + +UberLossAlgorithm::UberLossAlgorithm(LossDetectionType loss_type) + : loss_type_(loss_type) { + SetLossDetectionType(loss_type); + for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) { + general_loss_algorithms_[i].SetPacketNumberSpace( + static_cast<PacketNumberSpace>(i)); + } +} + +LossDetectionType UberLossAlgorithm::GetLossDetectionType() const { + return loss_type_; +} + +void UberLossAlgorithm::SetLossDetectionType(LossDetectionType loss_type) { + loss_type_ = loss_type; + for (auto& loss_algorithm : general_loss_algorithms_) { + loss_algorithm.SetLossDetectionType(loss_type); + } +} + +void UberLossAlgorithm::DetectLosses( + const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber /*largest_newly_acked*/, + const AckedPacketVector& packets_acked, + LostPacketVector* packets_lost) { + DCHECK(unacked_packets.use_uber_loss_algorithm()); + for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) { + const QuicPacketNumber largest_acked = + unacked_packets.GetLargestAckedOfPacketNumberSpace( + static_cast<PacketNumberSpace>(i)); + if (!largest_acked.IsInitialized() || + unacked_packets.GetLeastUnacked() > largest_acked) { + // Skip detecting losses if no packet has been received for this packet + // number space or the least_unacked is greater than largest_acked. + continue; + } + + general_loss_algorithms_[i].DetectLosses(unacked_packets, time, rtt_stats, + largest_acked, packets_acked, + packets_lost); + } +} + +QuicTime UberLossAlgorithm::GetLossTimeout() const { + QuicTime loss_timeout = QuicTime::Zero(); + // Returns the earliest non-zero loss timeout. + for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) { + const QuicTime timeout = general_loss_algorithms_[i].GetLossTimeout(); + if (!loss_timeout.IsInitialized()) { + loss_timeout = timeout; + continue; + } + if (timeout.IsInitialized()) { + loss_timeout = std::min(loss_timeout, timeout); + } + } + return loss_timeout; +} + +void UberLossAlgorithm::SpuriousRetransmitDetected( + const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber spurious_retransmission) { + DCHECK(unacked_packets.use_uber_loss_algorithm()); + general_loss_algorithms_[unacked_packets.GetPacketNumberSpace( + spurious_retransmission)] + .SpuriousRetransmitDetected(unacked_packets, time, rtt_stats, + spurious_retransmission); +} + +} // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.h b/quic/core/congestion_control/uber_loss_algorithm.h new file mode 100644 index 0000000..dddbcb3 --- /dev/null +++ b/quic/core/congestion_control/uber_loss_algorithm.h
@@ -0,0 +1,53 @@ +// Copyright 2019 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_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_ + +#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h" + +namespace quic { + +// This class comprises multiple loss algorithms, each per packet number space. +class QUIC_EXPORT_PRIVATE UberLossAlgorithm : public LossDetectionInterface { + public: + UberLossAlgorithm(); + explicit UberLossAlgorithm(LossDetectionType loss_type); + UberLossAlgorithm(const UberLossAlgorithm&) = delete; + UberLossAlgorithm& operator=(const UberLossAlgorithm&) = delete; + ~UberLossAlgorithm() override {} + + LossDetectionType GetLossDetectionType() const override; + + // Switches the loss detection type to |loss_type| and resets loss algorithm + // for all packet number spaces. + void SetLossDetectionType(LossDetectionType loss_type); + + // Detects lost packets. + void DetectLosses(const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber largest_newly_acked, + const AckedPacketVector& packets_acked, + LostPacketVector* packets_lost) override; + + // Returns the earliest time the early retransmit timer should be active. + QuicTime GetLossTimeout() const override; + + // Increases the loss detection threshold for time loss detection. + void SpuriousRetransmitDetected( + const QuicUnackedPacketMap& unacked_packets, + QuicTime time, + const RttStats& rtt_stats, + QuicPacketNumber spurious_retransmission) override; + + private: + LossDetectionType loss_type_; + // One loss algorithm per packet number space. + GeneralLossAlgorithm general_loss_algorithms_[NUM_PACKET_NUMBER_SPACES]; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_
diff --git a/quic/core/congestion_control/uber_loss_algorithm_test.cc b/quic/core/congestion_control/uber_loss_algorithm_test.cc new file mode 100644 index 0000000..9aa2041 --- /dev/null +++ b/quic/core/congestion_control/uber_loss_algorithm_test.cc
@@ -0,0 +1,159 @@ +// Copyright 2019 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/congestion_control/uber_loss_algorithm.h" + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h" + +namespace quic { +namespace test { +namespace { + +// Default packet length. +const uint32_t kDefaultLength = 1000; + +class UberLossAlgorithmTest : public QuicTest { + protected: + UberLossAlgorithmTest() { + SetQuicReloadableFlag(quic_use_uber_loss_algorithm, true); + unacked_packets_ = + QuicMakeUnique<QuicUnackedPacketMap>(Perspective::IS_CLIENT); + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), clock_.Now()); + EXPECT_LT(0, rtt_stats_.smoothed_rtt().ToMicroseconds()); + } + + void SendPacket(uint64_t packet_number, EncryptionLevel encryption_level) { + QuicStreamFrame frame; + frame.stream_id = + encryption_level == ENCRYPTION_NONE + ? QuicUtils::GetCryptoStreamId( + CurrentSupportedVersions()[0].transport_version) + : QuicUtils::GetHeadersStreamId( + CurrentSupportedVersions()[0].transport_version); + SerializedPacket packet(QuicPacketNumber(packet_number), + PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength, + false, false); + packet.encryption_level = encryption_level; + packet.retransmittable_frames.push_back(QuicFrame(frame)); + unacked_packets_->AddSentPacket(&packet, QuicPacketNumber(), + NOT_RETRANSMISSION, clock_.Now(), true); + } + + void AckPackets(const std::vector<uint64_t>& packets_acked) { + packets_acked_.clear(); + for (uint64_t acked : packets_acked) { + unacked_packets_->RemoveFromInFlight(QuicPacketNumber(acked)); + packets_acked_.push_back(AckedPacket(QuicPacketNumber(acked), + kMaxPacketSize, QuicTime::Zero())); + } + } + + void VerifyLosses(uint64_t largest_newly_acked, + const AckedPacketVector& packets_acked, + const std::vector<uint64_t>& losses_expected) { + LostPacketVector lost_packets; + loss_algorithm_.DetectLosses(*unacked_packets_, clock_.Now(), rtt_stats_, + QuicPacketNumber(largest_newly_acked), + packets_acked, &lost_packets); + ASSERT_EQ(losses_expected.size(), lost_packets.size()); + for (size_t i = 0; i < losses_expected.size(); ++i) { + EXPECT_EQ(lost_packets[i].packet_number, + QuicPacketNumber(losses_expected[i])); + } + } + + MockClock clock_; + std::unique_ptr<QuicUnackedPacketMap> unacked_packets_; + RttStats rtt_stats_; + UberLossAlgorithm loss_algorithm_; + AckedPacketVector packets_acked_; +}; + +TEST_F(UberLossAlgorithmTest, ScenarioA) { + // This test mimics a scenario: client sends 1-CHLO, 2-0RTT, 3-0RTT, + // timeout and retransmits 4-CHLO. Server acks packet 1 (ack gets lost). + // Server receives and buffers packets 2 and 3. Server receives packet 4 and + // processes handshake asynchronously, so server acks 4 and cannot process + // packets 2 and 3. + SendPacket(1, ENCRYPTION_NONE); + SendPacket(2, ENCRYPTION_ZERO_RTT); + SendPacket(3, ENCRYPTION_ZERO_RTT); + unacked_packets_->RemoveFromInFlight(QuicPacketNumber(1)); + SendPacket(4, ENCRYPTION_NONE); + + AckPackets({1, 4}); + unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_NONE, QuicPacketNumber(4)); + // Verify no packet is detected lost. + VerifyLosses(4, packets_acked_, std::vector<uint64_t>{}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(UberLossAlgorithmTest, ScenarioB) { + // This test mimics a scenario: client sends 3-0RTT, 4-0RTT, receives SHLO, + // sends 5-1RTT, 6-1RTT. + SendPacket(3, ENCRYPTION_ZERO_RTT); + SendPacket(4, ENCRYPTION_ZERO_RTT); + SendPacket(5, ENCRYPTION_FORWARD_SECURE); + SendPacket(6, ENCRYPTION_FORWARD_SECURE); + + AckPackets({4}); + unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_ZERO_RTT, QuicPacketNumber(4)); + // No packet loss by acking 4. + VerifyLosses(4, packets_acked_, std::vector<uint64_t>{}); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + + // Acking 6 causes 3 to be detected loss. + AckPackets({6}); + unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(6)); + VerifyLosses(6, packets_acked_, std::vector<uint64_t>{3}); + EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); + packets_acked_.clear(); + + clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt()); + // Verify 5 will be early retransmitted. + VerifyLosses(6, packets_acked_, {5}); +} + +TEST_F(UberLossAlgorithmTest, ScenarioC) { + // This test mimics a scenario: server sends 1-SHLO, 2-1RTT, 3-1RTT, 4-1RTT + // and retransmit 4-SHLO. Client receives and buffers packet 4. Client + // receives packet 5 and processes 4. + QuicUnackedPacketMapPeer::SetPerspective(unacked_packets_.get(), + Perspective::IS_SERVER); + SendPacket(1, ENCRYPTION_ZERO_RTT); + SendPacket(2, ENCRYPTION_FORWARD_SECURE); + SendPacket(3, ENCRYPTION_FORWARD_SECURE); + SendPacket(4, ENCRYPTION_FORWARD_SECURE); + unacked_packets_->RemoveFromInFlight(QuicPacketNumber(1)); + SendPacket(5, ENCRYPTION_ZERO_RTT); + + AckPackets({4, 5}); + unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(4)); + unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace( + ENCRYPTION_ZERO_RTT, QuicPacketNumber(5)); + // No packet loss by acking 5. + VerifyLosses(5, packets_acked_, std::vector<uint64_t>{}); + EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(), + loss_algorithm_.GetLossTimeout()); + packets_acked_.clear(); + + clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt()); + // Verify 2 and 3 will be early retransmitted. + VerifyLosses(5, packets_acked_, std::vector<uint64_t>{2, 3}); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/congestion_control/windowed_filter.h b/quic/core/congestion_control/windowed_filter.h new file mode 100644 index 0000000..8729895 --- /dev/null +++ b/quic/core/congestion_control/windowed_filter.h
@@ -0,0 +1,160 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_WINDOWED_FILTER_H_ +#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_WINDOWED_FILTER_H_ + +// Implements Kathleen Nichols' algorithm for tracking the minimum (or maximum) +// estimate of a stream of samples over some fixed time interval. (E.g., +// the minimum RTT over the past five minutes.) The algorithm keeps track of +// the best, second best, and third best min (or max) estimates, maintaining an +// invariant that the measurement time of the n'th best >= n-1'th best. + +// The algorithm works as follows. On a reset, all three estimates are set to +// the same sample. The second best estimate is then recorded in the second +// quarter of the window, and a third best estimate is recorded in the second +// half of the window, bounding the worst case error when the true min is +// monotonically increasing (or true max is monotonically decreasing) over the +// window. +// +// A new best sample replaces all three estimates, since the new best is lower +// (or higher) than everything else in the window and it is the most recent. +// The window thus effectively gets reset on every new min. The same property +// holds true for second best and third best estimates. Specifically, when a +// sample arrives that is better than the second best but not better than the +// best, it replaces the second and third best estimates but not the best +// estimate. Similarly, a sample that is better than the third best estimate +// but not the other estimates replaces only the third best estimate. +// +// Finally, when the best expires, it is replaced by the second best, which in +// turn is replaced by the third best. The newest sample replaces the third +// best. + +#include "net/third_party/quiche/src/quic/core/quic_time.h" + +namespace quic { + +// Compares two values and returns true if the first is less than or equal +// to the second. +template <class T> +struct MinFilter { + bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; } +}; + +// Compares two values and returns true if the first is greater than or equal +// to the second. +template <class T> +struct MaxFilter { + bool operator()(const T& lhs, const T& rhs) const { return lhs >= rhs; } +}; + +// Use the following to construct a windowed filter object of type T. +// For example, a min filter using QuicTime as the time type: +// WindowedFilter<T, MinFilter<T>, QuicTime, QuicTime::Delta> ObjectName; +// A max filter using 64-bit integers as the time type: +// WindowedFilter<T, MaxFilter<T>, uint64_t, int64_t> ObjectName; +// Specifically, this template takes four arguments: +// 1. T -- type of the measurement that is being filtered. +// 2. Compare -- MinFilter<T> or MaxFilter<T>, depending on the type of filter +// desired. +// 3. TimeT -- the type used to represent timestamps. +// 4. TimeDeltaT -- the type used to represent continuous time intervals between +// two timestamps. Has to be the type of (a - b) if both |a| and |b| are +// of type TimeT. +template <class T, class Compare, typename TimeT, typename TimeDeltaT> +class WindowedFilter { + public: + // |window_length| is the period after which a best estimate expires. + // |zero_value| is used as the uninitialized value for objects of T. + // Importantly, |zero_value| should be an invalid value for a true sample. + WindowedFilter(TimeDeltaT window_length, T zero_value, TimeT zero_time) + : window_length_(window_length), + zero_value_(zero_value), + estimates_{Sample(zero_value_, zero_time), + Sample(zero_value_, zero_time), + Sample(zero_value_, zero_time)} {} + + // Changes the window length. Does not update any current samples. + void SetWindowLength(TimeDeltaT window_length) { + window_length_ = window_length; + } + + // Updates best estimates with |sample|, and expires and updates best + // estimates as necessary. + void Update(T new_sample, TimeT new_time) { + // Reset all estimates if they have not yet been initialized, if new sample + // is a new best, or if the newest recorded estimate is too old. + if (estimates_[0].sample == zero_value_ || + Compare()(new_sample, estimates_[0].sample) || + new_time - estimates_[2].time > window_length_) { + Reset(new_sample, new_time); + return; + } + + if (Compare()(new_sample, estimates_[1].sample)) { + estimates_[1] = Sample(new_sample, new_time); + estimates_[2] = estimates_[1]; + } else if (Compare()(new_sample, estimates_[2].sample)) { + estimates_[2] = Sample(new_sample, new_time); + } + + // Expire and update estimates as necessary. + if (new_time - estimates_[0].time > window_length_) { + // The best estimate hasn't been updated for an entire window, so promote + // second and third best estimates. + estimates_[0] = estimates_[1]; + estimates_[1] = estimates_[2]; + estimates_[2] = Sample(new_sample, new_time); + // Need to iterate one more time. Check if the new best estimate is + // outside the window as well, since it may also have been recorded a + // long time ago. Don't need to iterate once more since we cover that + // case at the beginning of the method. + if (new_time - estimates_[0].time > window_length_) { + estimates_[0] = estimates_[1]; + estimates_[1] = estimates_[2]; + } + return; + } + if (estimates_[1].sample == estimates_[0].sample && + new_time - estimates_[1].time > window_length_ >> 2) { + // A quarter of the window has passed without a better sample, so the + // second-best estimate is taken from the second quarter of the window. + estimates_[2] = estimates_[1] = Sample(new_sample, new_time); + return; + } + + if (estimates_[2].sample == estimates_[1].sample && + new_time - estimates_[2].time > window_length_ >> 1) { + // We've passed a half of the window without a better estimate, so take + // a third-best estimate from the second half of the window. + estimates_[2] = Sample(new_sample, new_time); + } + } + + // Resets all estimates to new sample. + void Reset(T new_sample, TimeT new_time) { + estimates_[0] = estimates_[1] = estimates_[2] = + Sample(new_sample, new_time); + } + + T GetBest() const { return estimates_[0].sample; } + T GetSecondBest() const { return estimates_[1].sample; } + T GetThirdBest() const { return estimates_[2].sample; } + + private: + struct Sample { + T sample; + TimeT time; + Sample(T init_sample, TimeT init_time) + : sample(init_sample), time(init_time) {} + }; + + TimeDeltaT window_length_; // Time length of window. + T zero_value_; // Uninitialized value of T. + Sample estimates_[3]; // Best estimate is element 0. +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_WINDOWED_FILTER_H_
diff --git a/quic/core/congestion_control/windowed_filter_test.cc b/quic/core/congestion_control/windowed_filter_test.cc new file mode 100644 index 0000000..d9f3655 --- /dev/null +++ b/quic/core/congestion_control/windowed_filter_test.cc
@@ -0,0 +1,387 @@ +// 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/congestion_control/windowed_filter.h" + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class WindowedFilterTest : public QuicTest { + public: + // Set the window to 99ms, so 25ms is more than a quarter rtt. + WindowedFilterTest() + : windowed_min_rtt_(QuicTime::Delta::FromMilliseconds(99), + QuicTime::Delta::Zero(), + QuicTime::Zero()), + windowed_max_bw_(QuicTime::Delta::FromMilliseconds(99), + QuicBandwidth::Zero(), + QuicTime::Zero()) {} + + // Sets up windowed_min_rtt_ to have the following values: + // Best = 20ms, recorded at 25ms + // Second best = 40ms, recorded at 75ms + // Third best = 50ms, recorded at 100ms + void InitializeMinFilter() { + QuicTime now = QuicTime::Zero(); + QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10); + for (int i = 0; i < 5; ++i) { + windowed_min_rtt_.Update(rtt_sample, now); + VLOG(1) << "i: " << i << " sample: " << rtt_sample.ToMilliseconds() + << " mins: " + << " " << windowed_min_rtt_.GetBest().ToMilliseconds() << " " + << windowed_min_rtt_.GetSecondBest().ToMilliseconds() << " " + << windowed_min_rtt_.GetThirdBest().ToMilliseconds(); + now = now + QuicTime::Delta::FromMilliseconds(25); + rtt_sample = rtt_sample + QuicTime::Delta::FromMilliseconds(10); + } + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), + windowed_min_rtt_.GetBest()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40), + windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), + windowed_min_rtt_.GetThirdBest()); + } + + // Sets up windowed_max_bw_ to have the following values: + // Best = 900 bps, recorded at 25ms + // Second best = 700 bps, recorded at 75ms + // Third best = 600 bps, recorded at 100ms + void InitializeMaxFilter() { + QuicTime now = QuicTime::Zero(); + QuicBandwidth bw_sample = QuicBandwidth::FromBitsPerSecond(1000); + for (int i = 0; i < 5; ++i) { + windowed_max_bw_.Update(bw_sample, now); + VLOG(1) << "i: " << i << " sample: " << bw_sample.ToBitsPerSecond() + << " maxs: " + << " " << windowed_max_bw_.GetBest().ToBitsPerSecond() << " " + << windowed_max_bw_.GetSecondBest().ToBitsPerSecond() << " " + << windowed_max_bw_.GetThirdBest().ToBitsPerSecond(); + now = now + QuicTime::Delta::FromMilliseconds(25); + bw_sample = bw_sample - QuicBandwidth::FromBitsPerSecond(100); + } + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900), + windowed_max_bw_.GetBest()); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(700), + windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(600), + windowed_max_bw_.GetThirdBest()); + } + + protected: + WindowedFilter<QuicTime::Delta, + MinFilter<QuicTime::Delta>, + QuicTime, + QuicTime::Delta> + windowed_min_rtt_; + WindowedFilter<QuicBandwidth, + MaxFilter<QuicBandwidth>, + QuicTime, + QuicTime::Delta> + windowed_max_bw_; +}; + +namespace { +// Test helper function: updates the filter with a lot of small values in order +// to ensure that it is not susceptible to noise. +void UpdateWithIrrelevantSamples( + WindowedFilter<uint64_t, MaxFilter<uint64_t>, uint64_t, uint64_t>* filter, + uint64_t max_value, + uint64_t time) { + for (uint64_t i = 0; i < 1000; i++) { + filter->Update(i % max_value, time); + } +} +} // namespace + +TEST_F(WindowedFilterTest, UninitializedEstimates) { + EXPECT_EQ(QuicTime::Delta::Zero(), windowed_min_rtt_.GetBest()); + EXPECT_EQ(QuicTime::Delta::Zero(), windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(QuicTime::Delta::Zero(), windowed_min_rtt_.GetThirdBest()); + EXPECT_EQ(QuicBandwidth::Zero(), windowed_max_bw_.GetBest()); + EXPECT_EQ(QuicBandwidth::Zero(), windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(QuicBandwidth::Zero(), windowed_max_bw_.GetThirdBest()); +} + +TEST_F(WindowedFilterTest, MonotonicallyIncreasingMin) { + QuicTime now = QuicTime::Zero(); + QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10); + windowed_min_rtt_.Update(rtt_sample, now); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), windowed_min_rtt_.GetBest()); + + // Gradually increase the rtt samples and ensure the windowed min rtt starts + // rising. + for (int i = 0; i < 6; ++i) { + now = now + QuicTime::Delta::FromMilliseconds(25); + rtt_sample = rtt_sample + QuicTime::Delta::FromMilliseconds(10); + windowed_min_rtt_.Update(rtt_sample, now); + VLOG(1) << "i: " << i << " sample: " << rtt_sample.ToMilliseconds() + << " mins: " + << " " << windowed_min_rtt_.GetBest().ToMilliseconds() << " " + << windowed_min_rtt_.GetSecondBest().ToMilliseconds() << " " + << windowed_min_rtt_.GetThirdBest().ToMilliseconds(); + if (i < 3) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), + windowed_min_rtt_.GetBest()); + } else if (i == 3) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), + windowed_min_rtt_.GetBest()); + } else if (i < 6) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40), + windowed_min_rtt_.GetBest()); + } + } +} + +TEST_F(WindowedFilterTest, MonotonicallyDecreasingMax) { + QuicTime now = QuicTime::Zero(); + QuicBandwidth bw_sample = QuicBandwidth::FromBitsPerSecond(1000); + windowed_max_bw_.Update(bw_sample, now); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(1000), windowed_max_bw_.GetBest()); + + // Gradually decrease the bw samples and ensure the windowed max bw starts + // decreasing. + for (int i = 0; i < 6; ++i) { + now = now + QuicTime::Delta::FromMilliseconds(25); + bw_sample = bw_sample - QuicBandwidth::FromBitsPerSecond(100); + windowed_max_bw_.Update(bw_sample, now); + VLOG(1) << "i: " << i << " sample: " << bw_sample.ToBitsPerSecond() + << " maxs: " + << " " << windowed_max_bw_.GetBest().ToBitsPerSecond() << " " + << windowed_max_bw_.GetSecondBest().ToBitsPerSecond() << " " + << windowed_max_bw_.GetThirdBest().ToBitsPerSecond(); + if (i < 3) { + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(1000), + windowed_max_bw_.GetBest()); + } else if (i == 3) { + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900), + windowed_max_bw_.GetBest()); + } else if (i < 6) { + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(700), + windowed_max_bw_.GetBest()); + } + } +} + +TEST_F(WindowedFilterTest, SampleChangesThirdBestMin) { + InitializeMinFilter(); + // RTT sample lower than the third-choice min-rtt sets that, but nothing else. + QuicTime::Delta rtt_sample = + windowed_min_rtt_.GetThirdBest() - QuicTime::Delta::FromMilliseconds(5); + // This assert is necessary to avoid triggering -Wstrict-overflow + // See crbug/616957 + ASSERT_GT(windowed_min_rtt_.GetThirdBest(), + QuicTime::Delta::FromMilliseconds(5)); + // Latest sample was recorded at 100ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101); + windowed_min_rtt_.Update(rtt_sample, now); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40), + windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), windowed_min_rtt_.GetBest()); +} + +TEST_F(WindowedFilterTest, SampleChangesThirdBestMax) { + InitializeMaxFilter(); + // BW sample higher than the third-choice max sets that, but nothing else. + QuicBandwidth bw_sample = + windowed_max_bw_.GetThirdBest() + QuicBandwidth::FromBitsPerSecond(50); + // Latest sample was recorded at 100ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101); + windowed_max_bw_.Update(bw_sample, now); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(700), + windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900), windowed_max_bw_.GetBest()); +} + +TEST_F(WindowedFilterTest, SampleChangesSecondBestMin) { + InitializeMinFilter(); + // RTT sample lower than the second-choice min sets that and also + // the third-choice min. + QuicTime::Delta rtt_sample = + windowed_min_rtt_.GetSecondBest() - QuicTime::Delta::FromMilliseconds(5); + // This assert is necessary to avoid triggering -Wstrict-overflow + // See crbug/616957 + ASSERT_GT(windowed_min_rtt_.GetSecondBest(), + QuicTime::Delta::FromMilliseconds(5)); + // Latest sample was recorded at 100ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101); + windowed_min_rtt_.Update(rtt_sample, now); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), windowed_min_rtt_.GetBest()); +} + +TEST_F(WindowedFilterTest, SampleChangesSecondBestMax) { + InitializeMaxFilter(); + // BW sample higher than the second-choice max sets that and also + // the third-choice max. + QuicBandwidth bw_sample = + windowed_max_bw_.GetSecondBest() + QuicBandwidth::FromBitsPerSecond(50); + // Latest sample was recorded at 100ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101); + windowed_max_bw_.Update(bw_sample, now); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900), windowed_max_bw_.GetBest()); +} + +TEST_F(WindowedFilterTest, SampleChangesAllMins) { + InitializeMinFilter(); + // RTT sample lower than the first-choice min-rtt sets that and also + // the second and third-choice mins. + QuicTime::Delta rtt_sample = + windowed_min_rtt_.GetBest() - QuicTime::Delta::FromMilliseconds(5); + // This assert is necessary to avoid triggering -Wstrict-overflow + // See crbug/616957 + ASSERT_GT(windowed_min_rtt_.GetBest(), QuicTime::Delta::FromMilliseconds(5)); + // Latest sample was recorded at 100ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101); + windowed_min_rtt_.Update(rtt_sample, now); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetBest()); +} + +TEST_F(WindowedFilterTest, SampleChangesAllMaxs) { + InitializeMaxFilter(); + // BW sample higher than the first-choice max sets that and also + // the second and third-choice maxs. + QuicBandwidth bw_sample = + windowed_max_bw_.GetBest() + QuicBandwidth::FromBitsPerSecond(50); + // Latest sample was recorded at 100ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101); + windowed_max_bw_.Update(bw_sample, now); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetBest()); +} + +TEST_F(WindowedFilterTest, ExpireBestMin) { + InitializeMinFilter(); + QuicTime::Delta old_third_best = windowed_min_rtt_.GetThirdBest(); + QuicTime::Delta old_second_best = windowed_min_rtt_.GetSecondBest(); + QuicTime::Delta rtt_sample = + old_third_best + QuicTime::Delta::FromMilliseconds(5); + // Best min sample was recorded at 25ms, so expiry time is 124ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(125); + windowed_min_rtt_.Update(rtt_sample, now); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); + EXPECT_EQ(old_third_best, windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(old_second_best, windowed_min_rtt_.GetBest()); +} + +TEST_F(WindowedFilterTest, ExpireBestMax) { + InitializeMaxFilter(); + QuicBandwidth old_third_best = windowed_max_bw_.GetThirdBest(); + QuicBandwidth old_second_best = windowed_max_bw_.GetSecondBest(); + QuicBandwidth bw_sample = + old_third_best - QuicBandwidth::FromBitsPerSecond(50); + // Best max sample was recorded at 25ms, so expiry time is 124ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(125); + windowed_max_bw_.Update(bw_sample, now); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); + EXPECT_EQ(old_third_best, windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(old_second_best, windowed_max_bw_.GetBest()); +} + +TEST_F(WindowedFilterTest, ExpireSecondBestMin) { + InitializeMinFilter(); + QuicTime::Delta old_third_best = windowed_min_rtt_.GetThirdBest(); + QuicTime::Delta rtt_sample = + old_third_best + QuicTime::Delta::FromMilliseconds(5); + // Second best min sample was recorded at 75ms, so expiry time is 174ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(175); + windowed_min_rtt_.Update(rtt_sample, now); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(old_third_best, windowed_min_rtt_.GetBest()); +} + +TEST_F(WindowedFilterTest, ExpireSecondBestMax) { + InitializeMaxFilter(); + QuicBandwidth old_third_best = windowed_max_bw_.GetThirdBest(); + QuicBandwidth bw_sample = + old_third_best - QuicBandwidth::FromBitsPerSecond(50); + // Second best max sample was recorded at 75ms, so expiry time is 174ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(175); + windowed_max_bw_.Update(bw_sample, now); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(old_third_best, windowed_max_bw_.GetBest()); +} + +TEST_F(WindowedFilterTest, ExpireAllMins) { + InitializeMinFilter(); + QuicTime::Delta rtt_sample = + windowed_min_rtt_.GetThirdBest() + QuicTime::Delta::FromMilliseconds(5); + // This assert is necessary to avoid triggering -Wstrict-overflow + // See crbug/616957 + ASSERT_LT(windowed_min_rtt_.GetThirdBest(), + QuicTime::Delta::Infinite() - QuicTime::Delta::FromMilliseconds(5)); + // Third best min sample was recorded at 100ms, so expiry time is 199ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(200); + windowed_min_rtt_.Update(rtt_sample, now); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest()); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest()); + EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetBest()); +} + +TEST_F(WindowedFilterTest, ExpireAllMaxs) { + InitializeMaxFilter(); + QuicBandwidth bw_sample = + windowed_max_bw_.GetThirdBest() - QuicBandwidth::FromBitsPerSecond(50); + // Third best max sample was recorded at 100ms, so expiry time is 199ms. + QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(200); + windowed_max_bw_.Update(bw_sample, now); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest()); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest()); + EXPECT_EQ(bw_sample, windowed_max_bw_.GetBest()); +} + +// Test the windowed filter where the time used is an exact counter instead of a +// timestamp. This is useful if, for example, the time is measured in round +// trips. +TEST_F(WindowedFilterTest, ExpireCounterBasedMax) { + // Create a window which starts at t = 0 and expires after two cycles. + WindowedFilter<uint64_t, MaxFilter<uint64_t>, uint64_t, uint64_t> max_filter( + 2, 0, 0); + + const uint64_t kBest = 50000; + // Insert 50000 at t = 1. + max_filter.Update(50000, 1); + EXPECT_EQ(kBest, max_filter.GetBest()); + UpdateWithIrrelevantSamples(&max_filter, 20, 1); + EXPECT_EQ(kBest, max_filter.GetBest()); + + // Insert 40000 at t = 2. Nothing is expected to expire. + max_filter.Update(40000, 2); + EXPECT_EQ(kBest, max_filter.GetBest()); + UpdateWithIrrelevantSamples(&max_filter, 20, 2); + EXPECT_EQ(kBest, max_filter.GetBest()); + + // Insert 30000 at t = 3. Nothing is expected to expire yet. + max_filter.Update(30000, 3); + EXPECT_EQ(kBest, max_filter.GetBest()); + UpdateWithIrrelevantSamples(&max_filter, 20, 3); + EXPECT_EQ(kBest, max_filter.GetBest()); + VLOG(0) << max_filter.GetSecondBest(); + VLOG(0) << max_filter.GetThirdBest(); + + // Insert 20000 at t = 4. 50000 at t = 1 expires, so 40000 becomes the new + // maximum. + const uint64_t kNewBest = 40000; + max_filter.Update(20000, 4); + EXPECT_EQ(kNewBest, max_filter.GetBest()); + UpdateWithIrrelevantSamples(&max_filter, 20, 4); + EXPECT_EQ(kNewBest, max_filter.GetBest()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/aead_base_decrypter.cc b/quic/core/crypto/aead_base_decrypter.cc new file mode 100644 index 0000000..53bd0f0 --- /dev/null +++ b/quic/core/crypto/aead_base_decrypter.cc
@@ -0,0 +1,202 @@ +// Copyright (c) 2013 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/crypto/aead_base_decrypter.h" + +#include <cstdint> + +#include "third_party/boringssl/src/include/openssl/crypto.h" +#include "third_party/boringssl/src/include/openssl/err.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace { + +// Clear OpenSSL error stack. +void ClearOpenSslErrors() { + while (ERR_get_error()) { + } +} + +// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error +// stack. +void DLogOpenSslErrors() { +#ifdef NDEBUG + ClearOpenSslErrors(); +#else + while (uint32_t error = ERR_get_error()) { + char buf[120]; + ERR_error_string_n(error, buf, QUIC_ARRAYSIZE(buf)); + QUIC_DLOG(ERROR) << "OpenSSL error: " << buf; + } +#endif +} + +const EVP_AEAD* InitAndCall(const EVP_AEAD* (*aead_getter)()) { + // Ensure BoringSSL is initialized before calling |aead_getter|. In Chromium, + // the static initializer is disabled. + CRYPTO_library_init(); + return aead_getter(); +} + +} // namespace + +AeadBaseDecrypter::AeadBaseDecrypter(const EVP_AEAD* (*aead_getter)(), + size_t key_size, + size_t auth_tag_size, + size_t nonce_size, + bool use_ietf_nonce_construction) + : aead_alg_(InitAndCall(aead_getter)), + key_size_(key_size), + auth_tag_size_(auth_tag_size), + nonce_size_(nonce_size), + use_ietf_nonce_construction_(use_ietf_nonce_construction), + have_preliminary_key_(false) { + DCHECK_GT(256u, key_size); + DCHECK_GT(256u, auth_tag_size); + DCHECK_GT(256u, nonce_size); + DCHECK_LE(key_size_, sizeof(key_)); + DCHECK_LE(nonce_size_, sizeof(iv_)); +} + +AeadBaseDecrypter::~AeadBaseDecrypter() {} + +bool AeadBaseDecrypter::SetKey(QuicStringPiece key) { + DCHECK_EQ(key.size(), key_size_); + if (key.size() != key_size_) { + return false; + } + memcpy(key_, key.data(), key.size()); + + EVP_AEAD_CTX_cleanup(ctx_.get()); + if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, auth_tag_size_, + nullptr)) { + DLogOpenSslErrors(); + return false; + } + + return true; +} + +bool AeadBaseDecrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) { + if (use_ietf_nonce_construction_) { + QUIC_BUG << "Attempted to set nonce prefix on IETF QUIC crypter"; + return false; + } + DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber)); + if (nonce_prefix.size() != nonce_size_ - sizeof(QuicPacketNumber)) { + return false; + } + memcpy(iv_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool AeadBaseDecrypter::SetIV(QuicStringPiece iv) { + if (!use_ietf_nonce_construction_) { + QUIC_BUG << "Attempted to set IV on Google QUIC crypter"; + return false; + } + DCHECK_EQ(iv.size(), nonce_size_); + if (iv.size() != nonce_size_) { + return false; + } + memcpy(iv_, iv.data(), iv.size()); + return true; +} + +bool AeadBaseDecrypter::SetPreliminaryKey(QuicStringPiece key) { + DCHECK(!have_preliminary_key_); + SetKey(key); + have_preliminary_key_ = true; + + return true; +} + +bool AeadBaseDecrypter::SetDiversificationNonce( + const DiversificationNonce& nonce) { + if (!have_preliminary_key_) { + return true; + } + + QuicString key, nonce_prefix; + size_t prefix_size = nonce_size_ - sizeof(QuicPacketNumber); + DiversifyPreliminaryKey( + QuicStringPiece(reinterpret_cast<const char*>(key_), key_size_), + QuicStringPiece(reinterpret_cast<const char*>(iv_), prefix_size), nonce, + key_size_, prefix_size, &key, &nonce_prefix); + + if (!SetKey(key) || !SetNoncePrefix(nonce_prefix)) { + DCHECK(false); + return false; + } + + have_preliminary_key_ = false; + return true; +} + +bool AeadBaseDecrypter::DecryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) { + if (ciphertext.length() < auth_tag_size_) { + return false; + } + + if (have_preliminary_key_) { + QUIC_BUG << "Unable to decrypt while key diversification is pending"; + return false; + } + + uint8_t nonce[kMaxNonceSize]; + memcpy(nonce, iv_, nonce_size_); + size_t prefix_len = nonce_size_ - sizeof(packet_number); + if (use_ietf_nonce_construction_) { + for (size_t i = 0; i < sizeof(packet_number); ++i) { + nonce[prefix_len + i] ^= + (packet_number >> ((sizeof(packet_number) - i - 1) * 8)) & 0xff; + } + } else { + memcpy(nonce + prefix_len, &packet_number, sizeof(packet_number)); + } + if (!EVP_AEAD_CTX_open( + ctx_.get(), reinterpret_cast<uint8_t*>(output), output_length, + max_output_length, reinterpret_cast<const uint8_t*>(nonce), + nonce_size_, reinterpret_cast<const uint8_t*>(ciphertext.data()), + ciphertext.size(), + reinterpret_cast<const uint8_t*>(associated_data.data()), + associated_data.size())) { + // Because QuicFramer does trial decryption, decryption errors are expected + // when encryption level changes. So we don't log decryption errors. + ClearOpenSslErrors(); + return false; + } + return true; +} + +size_t AeadBaseDecrypter::GetKeySize() const { + return key_size_; +} + +size_t AeadBaseDecrypter::GetIVSize() const { + return nonce_size_; +} + +QuicStringPiece AeadBaseDecrypter::GetKey() const { + return QuicStringPiece(reinterpret_cast<const char*>(key_), key_size_); +} + +QuicStringPiece AeadBaseDecrypter::GetNoncePrefix() const { + return QuicStringPiece(reinterpret_cast<const char*>(iv_), + nonce_size_ - sizeof(QuicPacketNumber)); +} + +} // namespace quic
diff --git a/quic/core/crypto/aead_base_decrypter.h b/quic/core/crypto/aead_base_decrypter.h new file mode 100644 index 0000000..d374722 --- /dev/null +++ b/quic/core/crypto/aead_base_decrypter.h
@@ -0,0 +1,74 @@ +// Copyright (c) 2013 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_CRYPTO_AEAD_BASE_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_ + +#include <cstddef> + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// AeadBaseDecrypter is the base class of AEAD QuicDecrypter subclasses. +class QUIC_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter { + public: + // This takes the function pointer rather than the EVP_AEAD itself so + // subclasses do not need to call CRYPTO_library_init. + AeadBaseDecrypter(const EVP_AEAD* (*aead_getter)(), + size_t key_size, + size_t auth_tag_size, + size_t nonce_size, + bool use_ietf_nonce_construction); + AeadBaseDecrypter(const AeadBaseDecrypter&) = delete; + AeadBaseDecrypter& operator=(const AeadBaseDecrypter&) = delete; + ~AeadBaseDecrypter() override; + + // QuicDecrypter implementation + bool SetKey(QuicStringPiece key) override; + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override; + bool SetIV(QuicStringPiece iv) override; + bool SetPreliminaryKey(QuicStringPiece key) override; + bool SetDiversificationNonce(const DiversificationNonce& nonce) override; + bool DecryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) override; + size_t GetKeySize() const override; + size_t GetIVSize() const override; + QuicStringPiece GetKey() const override; + QuicStringPiece GetNoncePrefix() const override; + + protected: + // Make these constants available to the subclasses so that the subclasses + // can assert at compile time their key_size_ and nonce_size_ do not + // exceed the maximum. + static const size_t kMaxKeySize = 32; + static const size_t kMaxNonceSize = 12; + + private: + const EVP_AEAD* const aead_alg_; + const size_t key_size_; + const size_t auth_tag_size_; + const size_t nonce_size_; + const bool use_ietf_nonce_construction_; + bool have_preliminary_key_; + + // The key. + unsigned char key_[kMaxKeySize]; + // The IV used to construct the nonce. + unsigned char iv_[kMaxNonceSize]; + + bssl::ScopedEVP_AEAD_CTX ctx_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_
diff --git a/quic/core/crypto/aead_base_encrypter.cc b/quic/core/crypto/aead_base_encrypter.cc new file mode 100644 index 0000000..405292e --- /dev/null +++ b/quic/core/crypto/aead_base_encrypter.cc
@@ -0,0 +1,187 @@ +// Copyright (c) 2013 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/crypto/aead_base_encrypter.h" + +#include "third_party/boringssl/src/include/openssl/crypto.h" +#include "third_party/boringssl/src/include/openssl/err.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +namespace { + +// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error +// stack. +void DLogOpenSslErrors() { +#ifdef NDEBUG + while (ERR_get_error()) { + } +#else + while (unsigned long error = ERR_get_error()) { + char buf[120]; + ERR_error_string_n(error, buf, QUIC_ARRAYSIZE(buf)); + QUIC_DLOG(ERROR) << "OpenSSL error: " << buf; + } +#endif +} + +const EVP_AEAD* InitAndCall(const EVP_AEAD* (*aead_getter)()) { + // Ensure BoringSSL is initialized before calling |aead_getter|. In Chromium, + // the static initializer is disabled. + CRYPTO_library_init(); + return aead_getter(); +} + +} // namespace + +AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* (*aead_getter)(), + size_t key_size, + size_t auth_tag_size, + size_t nonce_size, + bool use_ietf_nonce_construction) + : aead_alg_(InitAndCall(aead_getter)), + key_size_(key_size), + auth_tag_size_(auth_tag_size), + nonce_size_(nonce_size), + use_ietf_nonce_construction_(use_ietf_nonce_construction) { + DCHECK_LE(key_size_, sizeof(key_)); + DCHECK_LE(nonce_size_, sizeof(iv_)); + DCHECK_GE(kMaxNonceSize, nonce_size_); +} + +AeadBaseEncrypter::~AeadBaseEncrypter() {} + +bool AeadBaseEncrypter::SetKey(QuicStringPiece key) { + DCHECK_EQ(key.size(), key_size_); + if (key.size() != key_size_) { + return false; + } + memcpy(key_, key.data(), key.size()); + + EVP_AEAD_CTX_cleanup(ctx_.get()); + + if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, auth_tag_size_, + nullptr)) { + DLogOpenSslErrors(); + return false; + } + + return true; +} + +bool AeadBaseEncrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) { + if (use_ietf_nonce_construction_) { + QUIC_BUG << "Attempted to set nonce prefix on IETF QUIC crypter"; + return false; + } + DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber)); + if (nonce_prefix.size() != nonce_size_ - sizeof(QuicPacketNumber)) { + return false; + } + memcpy(iv_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool AeadBaseEncrypter::SetIV(QuicStringPiece iv) { + if (!use_ietf_nonce_construction_) { + QUIC_BUG << "Attempted to set IV on Google QUIC crypter"; + return false; + } + DCHECK_EQ(iv.size(), nonce_size_); + if (iv.size() != nonce_size_) { + return false; + } + memcpy(iv_, iv.data(), iv.size()); + return true; +} + +bool AeadBaseEncrypter::Encrypt(QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + unsigned char* output) { + DCHECK_EQ(nonce.size(), nonce_size_); + + size_t ciphertext_len; + if (!EVP_AEAD_CTX_seal( + ctx_.get(), output, &ciphertext_len, + plaintext.size() + auth_tag_size_, + reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(), + reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size(), + reinterpret_cast<const uint8_t*>(associated_data.data()), + associated_data.size())) { + DLogOpenSslErrors(); + return false; + } + + return true; +} + +bool AeadBaseEncrypter::EncryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + char* output, + size_t* output_length, + size_t max_output_length) { + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + if (max_output_length < ciphertext_size) { + return false; + } + // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the + // same packet number twice. + QUIC_ALIGNED(4) char nonce_buffer[kMaxNonceSize]; + memcpy(nonce_buffer, iv_, nonce_size_); + size_t prefix_len = nonce_size_ - sizeof(packet_number); + if (use_ietf_nonce_construction_) { + for (size_t i = 0; i < sizeof(packet_number); ++i) { + nonce_buffer[prefix_len + i] ^= + (packet_number >> ((sizeof(packet_number) - i - 1) * 8)) & 0xff; + } + } else { + memcpy(nonce_buffer + prefix_len, &packet_number, sizeof(packet_number)); + } + + if (!Encrypt(QuicStringPiece(nonce_buffer, nonce_size_), associated_data, + plaintext, reinterpret_cast<unsigned char*>(output))) { + return false; + } + *output_length = ciphertext_size; + return true; +} + +size_t AeadBaseEncrypter::GetKeySize() const { + return key_size_; +} + +size_t AeadBaseEncrypter::GetNoncePrefixSize() const { + return nonce_size_ - sizeof(QuicPacketNumber); +} + +size_t AeadBaseEncrypter::GetIVSize() const { + return nonce_size_; +} + +size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - auth_tag_size_; +} + +size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + auth_tag_size_; +} + +QuicStringPiece AeadBaseEncrypter::GetKey() const { + return QuicStringPiece(reinterpret_cast<const char*>(key_), key_size_); +} + +QuicStringPiece AeadBaseEncrypter::GetNoncePrefix() const { + return QuicStringPiece(reinterpret_cast<const char*>(iv_), + GetNoncePrefixSize()); +} + +} // namespace quic
diff --git a/quic/core/crypto/aead_base_encrypter.h b/quic/core/crypto/aead_base_encrypter.h new file mode 100644 index 0000000..316d24e --- /dev/null +++ b/quic/core/crypto/aead_base_encrypter.h
@@ -0,0 +1,81 @@ +// Copyright (c) 2013 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_CRYPTO_AEAD_BASE_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_ + +#include <cstddef> + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// AeadBaseEncrypter is the base class of AEAD QuicEncrypter subclasses. +class QUIC_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter { + public: + // This takes the function pointer rather than the EVP_AEAD itself so + // subclasses do not need to call CRYPTO_library_init. + AeadBaseEncrypter(const EVP_AEAD* (*aead_getter)(), + size_t key_size, + size_t auth_tag_size, + size_t nonce_size, + bool use_ietf_nonce_construction); + AeadBaseEncrypter(const AeadBaseEncrypter&) = delete; + AeadBaseEncrypter& operator=(const AeadBaseEncrypter&) = delete; + ~AeadBaseEncrypter() override; + + // QuicEncrypter implementation + bool SetKey(QuicStringPiece key) override; + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override; + bool SetIV(QuicStringPiece iv) override; + bool EncryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + char* output, + size_t* output_length, + size_t max_output_length) override; + size_t GetKeySize() const override; + size_t GetNoncePrefixSize() const override; + size_t GetIVSize() const override; + size_t GetMaxPlaintextSize(size_t ciphertext_size) const override; + size_t GetCiphertextSize(size_t plaintext_size) const override; + QuicStringPiece GetKey() const override; + QuicStringPiece GetNoncePrefix() const override; + + // Necessary so unit tests can explicitly specify a nonce, instead of an IV + // (or nonce prefix) and packet number. + bool Encrypt(QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + unsigned char* output); + + protected: + // Make these constants available to the subclasses so that the subclasses + // can assert at compile time their key_size_ and nonce_size_ do not + // exceed the maximum. + static const size_t kMaxKeySize = 32; + enum : size_t { kMaxNonceSize = 12 }; + + private: + const EVP_AEAD* const aead_alg_; + const size_t key_size_; + const size_t auth_tag_size_; + const size_t nonce_size_; + const bool use_ietf_nonce_construction_; + + // The key. + unsigned char key_[kMaxKeySize]; + // The IV used to construct the nonce. + unsigned char iv_[kMaxNonceSize]; + + bssl::ScopedEVP_AEAD_CTX ctx_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_
diff --git a/quic/core/crypto/aes_128_gcm_12_decrypter.cc b/quic/core/crypto/aes_128_gcm_12_decrypter.cc new file mode 100644 index 0000000..f76246b --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_12_decrypter.cc
@@ -0,0 +1,35 @@ +// Copyright (c) 2017 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/crypto/aes_128_gcm_12_decrypter.h" + +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "third_party/boringssl/src/include/openssl/tls1.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 16; +const size_t kNonceSize = 12; + +} // namespace + +Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() + : AeadBaseDecrypter(EVP_aead_aes_128_gcm, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ false) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {} + +uint32_t Aes128Gcm12Decrypter::cipher_id() const { + return TLS1_CK_AES_128_GCM_SHA256; +} + +} // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_12_decrypter.h b/quic/core/crypto/aes_128_gcm_12_decrypter.h new file mode 100644 index 0000000..e8c7318 --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_12_decrypter.h
@@ -0,0 +1,39 @@ +// Copyright (c) 2017 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_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the +// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by +// calling QuicDecrypter::Create(kAESG). +// +// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix +// of the nonce is four bytes. +class QUIC_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AeadBaseDecrypter { + public: + enum { + // Authentication tags are truncated to 96 bits. + kAuthTagSize = 12, + }; + + Aes128Gcm12Decrypter(); + Aes128Gcm12Decrypter(const Aes128Gcm12Decrypter&) = delete; + Aes128Gcm12Decrypter& operator=(const Aes128Gcm12Decrypter&) = delete; + ~Aes128Gcm12Decrypter() override; + + uint32_t cipher_id() const override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
diff --git a/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc b/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc new file mode 100644 index 0000000..6513e45 --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -0,0 +1,286 @@ +// Copyright (c) 2013 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/crypto/aes_128_gcm_12_decrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The AES GCM test vectors come from the file gcmDecrypt128.rsp +// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on +// 2013-02-01. The test vectors in that file look like this: +// +// [Keylen = 128] +// [IVlen = 96] +// [PTlen = 0] +// [AADlen = 0] +// [Taglen = 128] +// +// Count = 0 +// Key = cf063a34d4a9a76c2c86787d3f96db71 +// IV = 113b9785971864c83b01c787 +// CT = +// AAD = +// Tag = 72ac8493e3a5228b5d130a69d2510e42 +// PT = +// +// Count = 1 +// Key = a49a5e26a2f8cb63d05546c2a62f5343 +// IV = 907763b19b9b4ab6bd4f0281 +// CT = +// AAD = +// Tag = a2be08210d8c470a8df6e8fbd79ec5cf +// FAIL +// +// ... +// +// The gcmDecrypt128.rsp file is huge (2.6 MB), so I selected just a +// few test vectors for this unit test. + +// Describes a group of test vectors that all have a given key length, IV +// length, plaintext length, AAD length, and tag length. +struct TestGroupInfo { + size_t key_len; + size_t iv_len; + size_t pt_len; + size_t aad_len; + size_t tag_len; +}; + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + // Input: + const char* key; + const char* iv; + const char* ct; + const char* aad; + const char* tag; + + // Expected output: + const char* pt; // An empty string "" means decryption succeeded and + // the plaintext is zero-length. nullptr means decryption + // failed. +}; + +const TestGroupInfo test_group_info[] = { + {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128}, + {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128}, +}; + +const TestVector test_group_0[] = { + {"cf063a34d4a9a76c2c86787d3f96db71", "113b9785971864c83b01c787", "", "", + "72ac8493e3a5228b5d130a69d2510e42", ""}, + { + "a49a5e26a2f8cb63d05546c2a62f5343", "907763b19b9b4ab6bd4f0281", "", "", + "a2be08210d8c470a8df6e8fbd79ec5cf", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_1[] = { + { + "d1f6af919cde85661208bdce0c27cb22", "898c6929b435017bf031c3c5", "", + "7c5faa40e636bbc91107e68010c92b9f", "ae45f11777540a2caeb128be8092468a", + nullptr // FAIL + }, + {"2370e320d4344208e0ff5683f243b213", "04dbb82f044d30831c441228", "", + "d43a8e5089eea0d026c03a85178b27da", "2a049c049d25aa95969b451d93c31c6e", + ""}, + {nullptr}}; + +const TestVector test_group_2[] = { + {"e98b72a9881a84ca6b76e0f43e68647a", "8b23299fde174053f3d652ba", + "5a3c1cf1985dbb8bed818036fdd5ab42", "", "23c7ab0f952b7091cd324835043b5eb5", + "28286a321293253c3e0aa2704a278032"}, + {"33240636cd3236165f1a553b773e728e", "17c4d61493ecdc8f31700b12", + "47bb7e23f7bdfe05a8091ac90e4f8b2e", "", "b723c70e931d9785f40fd4ab1d612dc9", + "95695a5b12f2870b9cc5fdc8f218a97d"}, + { + "5164df856f1e9cac04a79b808dc5be39", "e76925d5355e0584ce871b2b", + "0216c899c88d6e32c958c7e553daa5bc", "", + "a145319896329c96df291f64efbe0e3a", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_3[] = { + {"af57f42c60c0fc5a09adb81ab86ca1c3", "a2dc01871f37025dc0fc9a79", + "b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947" + "338b22f9bad09093276a331e9c79c7f4", + "41dc38988945fcb44faf2ef72d0061289ef8efd8", + "4f71e72bde0018f555c5adcce062e005", + "3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa7279" + "5b2f69b041596e8817d0a3c16f8fadeb"}, + {"ebc753e5422b377d3cb64b58ffa41b61", "2e1821efaced9acf1f241c9b", + "069567190554e9ab2b50a4e1fbf9c147340a5025fdbd201929834eaf6532325899ccb9" + "f401823e04b05817243d2142a3589878", + "b9673412fd4f88ba0e920f46dd6438ff791d8eef", + "534d9234d2351cf30e565de47baece0b", + "39077edb35e9c5a4b1e4c2a6b9bb1fce77f00f5023af40333d6d699014c2bcf4209c18" + "353a18017f5b36bfc00b1f6dcb7ed485"}, + { + "52bdbbf9cf477f187ec010589cb39d58", "d3be36d3393134951d324b31", + "700188da144fa692cf46e4a8499510a53d90903c967f7f13e8a1bd8151a74adc4fe63e" + "32b992760b3a5f99e9a47838867000a9", + "93c4fc6a4135f54d640b0c976bf755a06a292c33", + "8ca4e38aa3dfa6b1d0297021ccf3ea5f", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_4[] = { + {"da2bb7d581493d692380c77105590201", "44aa3e7856ca279d2eb020c6", + "9290d430c9e89c37f0446dbd620c9a6b34b1274aeb6f911f75867efcf95b6feda69f1a" + "f4ee16c761b3c9aeac3da03aa9889c88", + "4cd171b23bddb3a53cdf959d5c1710b481eb3785a90eb20a2345ee00d0bb7868c367ab" + "12e6f4dd1dee72af4eee1d197777d1d6499cc541f34edbf45cda6ef90b3c024f9272d7" + "2ec1909fb8fba7db88a4d6f7d3d925980f9f9f72", + "9e3ac938d3eb0cadd6f5c9e35d22ba38", + "9bbf4c1a2742f6ac80cb4e8a052e4a8f4f07c43602361355b717381edf9fabd4cb7e3a" + "d65dbd1378b196ac270588dd0621f642"}, + {"d74e4958717a9d5c0e235b76a926cae8", "0b7471141e0c70b1995fd7b1", + "e701c57d2330bf066f9ff8cf3ca4343cafe4894651cd199bdaaa681ba486b4a65c5a22" + "b0f1420be29ea547d42c713bc6af66aa", + "4a42b7aae8c245c6f1598a395316e4b8484dbd6e64648d5e302021b1d3fa0a38f46e22" + "bd9c8080b863dc0016482538a8562a4bd0ba84edbe2697c76fd039527ac179ec5506cf" + "34a6039312774cedebf4961f3978b14a26509f96", + "e192c23cb036f0b31592989119eed55d", + "840d9fb95e32559fb3602e48590280a172ca36d9b49ab69510f5bd552bfab7a306f85f" + "f0a34bc305b88b804c60b90add594a17"}, + { + "1986310c725ac94ecfe6422e75fc3ee7", "93ec4214fa8e6dc4e3afc775", + "b178ec72f85a311ac4168f42a4b2c23113fbea4b85f4b9dabb74e143eb1b8b0a361e02" + "43edfd365b90d5b325950df0ada058f9", + "e80b88e62c49c958b5e0b8b54f532d9ff6aa84c8a40132e93e55b59fc24e8decf28463" + "139f155d1e8ce4ee76aaeefcd245baa0fc519f83a5fb9ad9aa40c4b21126013f576c42" + "72c2cb136c8fd091cc4539877a5d1e72d607f960", + "8b347853f11d75e81e8a95010be81f17", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_5[] = { + {"387218b246c1a8257748b56980e50c94", "dd7e014198672be39f95b69d", + "cdba9e73eaf3d38eceb2b04a8d", "", "ecf90f4a47c9c626d6fb2c765d201556", + "48f5b426baca03064554cc2b30"}, + {"294de463721e359863887c820524b3d4", "3338b35c9d57a5d28190e8c9", + "2f46634e74b8e4c89812ac83b9", "", "dabd506764e68b82a7e720aa18da0abe", + "46a2e55c8e264df211bd112685"}, + {"28ead7fd2179e0d12aa6d5d88c58c2dc", "5055347f18b4d5add0ae5c41", + "142d8210c3fb84774cdbd0447a", "", "5fd321d9cdb01952dc85f034736c2a7d", + "3b95b981086ee73cc4d0cc1422"}, + { + "7d7b6c988137b8d470c57bf674a09c87", "9edf2aa970d016ac962e1fd8", + "a85b66c3cb5eab91d5bdc8bc0e", "", "dc054efc01f3afd21d9c2484819f569a", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector* const test_group_array[] = { + test_group_0, test_group_1, test_group_2, + test_group_3, test_group_4, test_group_5, +}; + +} // namespace + +namespace quic { +namespace test { + +// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the plaintext. +QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece ciphertext) { + uint64_t packet_number; + QuicStringPiece nonce_prefix(nonce.data(), + nonce.size() - sizeof(packet_number)); + decrypter->SetNoncePrefix(nonce_prefix); + memcpy(&packet_number, nonce.data() + nonce_prefix.size(), + sizeof(packet_number)); + std::unique_ptr<char[]> output(new char[ciphertext.length()]); + size_t output_length = 0; + const bool success = decrypter->DecryptPacket( + packet_number, associated_data, ciphertext, output.get(), &output_length, + ciphertext.length()); + if (!success) { + return nullptr; + } + return new QuicData(output.release(), output_length, true); +} + +class Aes128Gcm12DecrypterTest : public QuicTest {}; + +TEST_F(Aes128Gcm12DecrypterTest, Decrypt) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vectors = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vectors[j].key != nullptr; j++) { + // If not present then decryption is expected to fail. + bool has_pt = test_vectors[j].pt; + + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[j].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[j].iv); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[j].ct); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[j].aad); + QuicString tag = QuicTextUtils::HexDecode(test_vectors[j].tag); + QuicString pt; + if (has_pt) { + pt = QuicTextUtils::HexDecode(test_vectors[j].pt); + } + + // The test vector's lengths should look sane. Note that the lengths + // in |test_info| are in bits. + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); + if (has_pt) { + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + } + + // The test vectors have 16 byte authenticators but this code only uses + // the first 12. + ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize), + tag.length()); + tag.resize(Aes128Gcm12Decrypter::kAuthTagSize); + QuicString ciphertext = ct + tag; + + Aes128Gcm12Decrypter decrypter; + ASSERT_TRUE(decrypter.SetKey(key)); + + std::unique_ptr<QuicData> decrypted( + DecryptWithNonce(&decrypter, iv, + // This deliberately tests that the decrypter can + // handle an AAD that is set to nullptr, as opposed + // to a zero-length, non-nullptr pointer. + aad.length() ? aad : QuicStringPiece(), ciphertext)); + if (!decrypted.get()) { + EXPECT_FALSE(has_pt); + continue; + } + EXPECT_TRUE(has_pt); + + ASSERT_EQ(pt.length(), decrypted->length()); + test::CompareCharArraysWithHexError("plaintext", decrypted->data(), + pt.length(), pt.data(), pt.length()); + } + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter.cc b/quic/core/crypto/aes_128_gcm_12_encrypter.cc new file mode 100644 index 0000000..d488463 --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_12_encrypter.cc
@@ -0,0 +1,30 @@ +// Copyright (c) 2013 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/crypto/aes_128_gcm_12_encrypter.h" + +#include "third_party/boringssl/src/include/openssl/evp.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 16; +const size_t kNonceSize = 12; + +} // namespace + +Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() + : AeadBaseEncrypter(EVP_aead_aes_128_gcm, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ false) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {} + +} // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter.h b/quic/core/crypto/aes_128_gcm_12_encrypter.h new file mode 100644 index 0000000..99df129 --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_12_encrypter.h
@@ -0,0 +1,35 @@ +// Copyright (c) 2013 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_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// An Aes128Gcm12Encrypter is a QuicEncrypter that implements the +// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by +// calling QuicEncrypter::Create(kAESG). +// +// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix +// of the nonce is four bytes. +class QUIC_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AeadBaseEncrypter { + public: + enum { + // Authentication tags are truncated to 96 bits. + kAuthTagSize = 12, + }; + + Aes128Gcm12Encrypter(); + Aes128Gcm12Encrypter(const Aes128Gcm12Encrypter&) = delete; + Aes128Gcm12Encrypter& operator=(const Aes128Gcm12Encrypter&) = delete; + ~Aes128Gcm12Encrypter() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc new file mode 100644 index 0000000..67660cf --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -0,0 +1,241 @@ +// Copyright (c) 2017 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/crypto/aes_128_gcm_12_encrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The AES GCM test vectors come from the file gcmEncryptExtIV128.rsp +// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on +// 2013-02-01. The test vectors in that file look like this: +// +// [Keylen = 128] +// [IVlen = 96] +// [PTlen = 0] +// [AADlen = 0] +// [Taglen = 128] +// +// Count = 0 +// Key = 11754cd72aec309bf52f7687212e8957 +// IV = 3c819d9a9bed087615030b65 +// PT = +// AAD = +// CT = +// Tag = 250327c674aaf477aef2675748cf6971 +// +// Count = 1 +// Key = ca47248ac0b6f8372a97ac43508308ed +// IV = ffd2b598feabc9019262d2be +// PT = +// AAD = +// CT = +// Tag = 60d20404af527d248d893ae495707d1a +// +// ... +// +// The gcmEncryptExtIV128.rsp file is huge (2.8 MB), so I selected just a +// few test vectors for this unit test. + +// Describes a group of test vectors that all have a given key length, IV +// length, plaintext length, AAD length, and tag length. +struct TestGroupInfo { + size_t key_len; + size_t iv_len; + size_t pt_len; + size_t aad_len; + size_t tag_len; +}; + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + const char* key; + const char* iv; + const char* pt; + const char* aad; + const char* ct; + const char* tag; +}; + +const TestGroupInfo test_group_info[] = { + {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128}, + {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128}, +}; + +const TestVector test_group_0[] = { + {"11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", "", "", + "250327c674aaf477aef2675748cf6971"}, + {"ca47248ac0b6f8372a97ac43508308ed", "ffd2b598feabc9019262d2be", "", "", "", + "60d20404af527d248d893ae495707d1a"}, + {nullptr}}; + +const TestVector test_group_1[] = { + {"77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", "", + "7a43ec1d9c0a5a78a0b16533a6213cab", "", + "209fcc8d3675ed938e9c7166709dd946"}, + {"7680c5d3ca6154758e510f4d25b98820", "f8f105f9c3df4965780321f8", "", + "c94c410194c765e3dcc7964379758ed3", "", + "94dca8edfcf90bb74b153c8d48a17930"}, + {nullptr}}; + +const TestVector test_group_2[] = { + {"7fddb57453c241d03efbed3ac44e371c", "ee283a3fc75575e33efd4887", + "d5de42b461646c255c87bd2962d3b9a2", "", "2ccda4a5415cb91e135c2a0f78c9b2fd", + "b36d1df9b9d5e596f83e8b7f52971cb3"}, + {"ab72c77b97cb5fe9a382d9fe81ffdbed", "54cc7dc2c37ec006bcc6d1da", + "007c5e5b3e59df24a7c355584fc1518d", "", "0e1bde206a07a9c2c1b65300f8c64997", + "2b4401346697138c7a4891ee59867d0c"}, + {nullptr}}; + +const TestVector test_group_3[] = { + {"fe47fcce5fc32665d2ae399e4eec72ba", "5adb9609dbaeb58cbd6e7275", + "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1" + "b840382c4bccaf3bafb4ca8429bea063", + "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a", + "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf539304373636525" + "3ddbc5db8778371495da76d269e5db3e", + "291ef1982e4defedaa2249f898556b47"}, + {"ec0c2ba17aa95cd6afffe949da9cc3a8", "296bce5b50b7d66096d627ef", + "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987" + "b764b9611f6c0f8641843d5d58f3a242", + "f8d00f05d22bf68599bcdeb131292ad6e2df5d14", + "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a0716299" + "5506fde6309ffc19e716eddf1a828c5a", + "890147971946b627c40016da1ecf3e77"}, + {nullptr}}; + +const TestVector test_group_4[] = { + {"2c1f21cf0f6fb3661943155c3e3d8492", "23cb5ff362e22426984d1907", + "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d6" + "8b5615ba7c1220ff6510e259f06655d8", + "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e" + "3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f" + "4488f33cfb5e979e42b6e1cfc0a60238982a7aec", + "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222" + "b6ad57af43e1895df9dca2a5344a62cc", + "57a3ee28136e94c74838997ae9823f3a"}, + {"d9f7d2411091f947b4d6f1e2d1f0fb2e", "e1934f5db57cc983e6b180e7", + "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490" + "c2c6f6166f4a59431e182663fcaea05a", + "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d" + "0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a201" + "15d2e51398344b16bee1ed7c499b353d6c597af8", + "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d57" + "3c7891c2a91fbc48db29967ec9542b23", + "21b51ca862cb637cdd03b99a0f93b134"}, + {nullptr}}; + +const TestVector test_group_5[] = { + {"fe9bb47deb3a61e423c2231841cfd1fb", "4d328eb776f500a2f7fb47aa", + "f1cc3818e421876bb6b8bbd6c9", "", "b88c5c1977b35b517b0aeae967", + "43fd4727fe5cdb4b5b42818dea7ef8c9"}, + {"6703df3701a7f54911ca72e24dca046a", "12823ab601c350ea4bc2488c", + "793cd125b0b84a043e3ac67717", "", "b2051c80014f42f08735a7b0cd", + "38e6bcd29962e5f2c13626b85a877101"}, + {nullptr}}; + +const TestVector* const test_group_array[] = { + test_group_0, test_group_1, test_group_2, + test_group_3, test_group_4, test_group_5, +}; + +} // namespace + +namespace quic { +namespace test { + +// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the ciphertext. +QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece plaintext) { + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!encrypter->Encrypt(nonce, associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return nullptr; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +class Aes128Gcm12EncrypterTest : public QuicTest {}; + +TEST_F(Aes128Gcm12EncrypterTest, Encrypt) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vectors = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vectors[j].key != nullptr; j++) { + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[j].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[j].iv); + QuicString pt = QuicTextUtils::HexDecode(test_vectors[j].pt); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[j].aad); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[j].ct); + QuicString tag = QuicTextUtils::HexDecode(test_vectors[j].tag); + + // The test vector's lengths should look sane. Note that the lengths + // in |test_info| are in bits. + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); + + Aes128Gcm12Encrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(key)); + std::unique_ptr<QuicData> encrypted( + EncryptWithNonce(&encrypter, iv, + // This deliberately tests that the encrypter can + // handle an AAD that is set to nullptr, as opposed + // to a zero-length, non-nullptr pointer. + aad.length() ? aad : QuicStringPiece(), pt)); + ASSERT_TRUE(encrypted.get()); + + // The test vectors have 16 byte authenticators but this code only uses + // the first 12. + ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize), + tag.length()); + tag.resize(Aes128Gcm12Encrypter::kAuthTagSize); + + ASSERT_EQ(ct.length() + tag.length(), encrypted->length()); + test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), + ct.length(), ct.data(), ct.length()); + test::CompareCharArraysWithHexError( + "authentication tag", encrypted->data() + ct.length(), tag.length(), + tag.data(), tag.length()); + } + } +} + +TEST_F(Aes128Gcm12EncrypterTest, GetMaxPlaintextSize) { + Aes128Gcm12Encrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22)); +} + +TEST_F(Aes128Gcm12EncrypterTest, GetCiphertextSize) { + Aes128Gcm12Encrypter encrypter; + EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(112u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(22u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_decrypter.cc b/quic/core/crypto/aes_128_gcm_decrypter.cc new file mode 100644 index 0000000..43616ad --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_decrypter.cc
@@ -0,0 +1,37 @@ +// Copyright (c) 2017 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/crypto/aes_128_gcm_decrypter.h" + +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "third_party/boringssl/src/include/openssl/tls1.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 16; +const size_t kNonceSize = 12; + +} // namespace + +Aes128GcmDecrypter::Aes128GcmDecrypter() + : AeadBaseDecrypter(EVP_aead_aes_128_gcm, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ true) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +Aes128GcmDecrypter::~Aes128GcmDecrypter() {} + +uint32_t Aes128GcmDecrypter::cipher_id() const { + return TLS1_CK_AES_128_GCM_SHA256; +} + +} // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_decrypter.h b/quic/core/crypto/aes_128_gcm_decrypter.h new file mode 100644 index 0000000..ecee5bb --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_decrypter.h
@@ -0,0 +1,37 @@ +// Copyright (c) 2017 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_CRYPTO_AES_128_GCM_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// An Aes128GcmDecrypter is a QuicDecrypter that implements the +// AEAD_AES_128_GCM algorithm specified in RFC 5116 for use in IETF QUIC. +// +// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV +// that is XOR'd with the packet number to compute the nonce. +class QUIC_EXPORT_PRIVATE Aes128GcmDecrypter : public AeadBaseDecrypter { + public: + enum { + kAuthTagSize = 16, + }; + + Aes128GcmDecrypter(); + Aes128GcmDecrypter(const Aes128GcmDecrypter&) = delete; + Aes128GcmDecrypter& operator=(const Aes128GcmDecrypter&) = delete; + ~Aes128GcmDecrypter() override; + + uint32_t cipher_id() const override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_
diff --git a/quic/core/crypto/aes_128_gcm_decrypter_test.cc b/quic/core/crypto/aes_128_gcm_decrypter_test.cc new file mode 100644 index 0000000..300e8a2 --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_decrypter_test.cc
@@ -0,0 +1,275 @@ +// Copyright (c) 2017 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/crypto/aes_128_gcm_decrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The AES GCM test vectors come from the file gcmDecrypt128.rsp +// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on +// 2013-02-01. The test vectors in that file look like this: +// +// [Keylen = 128] +// [IVlen = 96] +// [PTlen = 0] +// [AADlen = 0] +// [Taglen = 128] +// +// Count = 0 +// Key = cf063a34d4a9a76c2c86787d3f96db71 +// IV = 113b9785971864c83b01c787 +// CT = +// AAD = +// Tag = 72ac8493e3a5228b5d130a69d2510e42 +// PT = +// +// Count = 1 +// Key = a49a5e26a2f8cb63d05546c2a62f5343 +// IV = 907763b19b9b4ab6bd4f0281 +// CT = +// AAD = +// Tag = a2be08210d8c470a8df6e8fbd79ec5cf +// FAIL +// +// ... +// +// The gcmDecrypt128.rsp file is huge (2.6 MB), so I selected just a +// few test vectors for this unit test. + +// Describes a group of test vectors that all have a given key length, IV +// length, plaintext length, AAD length, and tag length. +struct TestGroupInfo { + size_t key_len; + size_t iv_len; + size_t pt_len; + size_t aad_len; + size_t tag_len; +}; + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + // Input: + const char* key; + const char* iv; + const char* ct; + const char* aad; + const char* tag; + + // Expected output: + const char* pt; // An empty string "" means decryption succeeded and + // the plaintext is zero-length. nullptr means decryption + // failed. +}; + +const TestGroupInfo test_group_info[] = { + {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128}, + {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128}, +}; + +const TestVector test_group_0[] = { + {"cf063a34d4a9a76c2c86787d3f96db71", "113b9785971864c83b01c787", "", "", + "72ac8493e3a5228b5d130a69d2510e42", ""}, + { + "a49a5e26a2f8cb63d05546c2a62f5343", "907763b19b9b4ab6bd4f0281", "", "", + "a2be08210d8c470a8df6e8fbd79ec5cf", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_1[] = { + { + "d1f6af919cde85661208bdce0c27cb22", "898c6929b435017bf031c3c5", "", + "7c5faa40e636bbc91107e68010c92b9f", "ae45f11777540a2caeb128be8092468a", + nullptr // FAIL + }, + {"2370e320d4344208e0ff5683f243b213", "04dbb82f044d30831c441228", "", + "d43a8e5089eea0d026c03a85178b27da", "2a049c049d25aa95969b451d93c31c6e", + ""}, + {nullptr}}; + +const TestVector test_group_2[] = { + {"e98b72a9881a84ca6b76e0f43e68647a", "8b23299fde174053f3d652ba", + "5a3c1cf1985dbb8bed818036fdd5ab42", "", "23c7ab0f952b7091cd324835043b5eb5", + "28286a321293253c3e0aa2704a278032"}, + {"33240636cd3236165f1a553b773e728e", "17c4d61493ecdc8f31700b12", + "47bb7e23f7bdfe05a8091ac90e4f8b2e", "", "b723c70e931d9785f40fd4ab1d612dc9", + "95695a5b12f2870b9cc5fdc8f218a97d"}, + { + "5164df856f1e9cac04a79b808dc5be39", "e76925d5355e0584ce871b2b", + "0216c899c88d6e32c958c7e553daa5bc", "", + "a145319896329c96df291f64efbe0e3a", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_3[] = { + {"af57f42c60c0fc5a09adb81ab86ca1c3", "a2dc01871f37025dc0fc9a79", + "b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947" + "338b22f9bad09093276a331e9c79c7f4", + "41dc38988945fcb44faf2ef72d0061289ef8efd8", + "4f71e72bde0018f555c5adcce062e005", + "3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa7279" + "5b2f69b041596e8817d0a3c16f8fadeb"}, + {"ebc753e5422b377d3cb64b58ffa41b61", "2e1821efaced9acf1f241c9b", + "069567190554e9ab2b50a4e1fbf9c147340a5025fdbd201929834eaf6532325899ccb9" + "f401823e04b05817243d2142a3589878", + "b9673412fd4f88ba0e920f46dd6438ff791d8eef", + "534d9234d2351cf30e565de47baece0b", + "39077edb35e9c5a4b1e4c2a6b9bb1fce77f00f5023af40333d6d699014c2bcf4209c18" + "353a18017f5b36bfc00b1f6dcb7ed485"}, + { + "52bdbbf9cf477f187ec010589cb39d58", "d3be36d3393134951d324b31", + "700188da144fa692cf46e4a8499510a53d90903c967f7f13e8a1bd8151a74adc4fe63e" + "32b992760b3a5f99e9a47838867000a9", + "93c4fc6a4135f54d640b0c976bf755a06a292c33", + "8ca4e38aa3dfa6b1d0297021ccf3ea5f", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_4[] = { + {"da2bb7d581493d692380c77105590201", "44aa3e7856ca279d2eb020c6", + "9290d430c9e89c37f0446dbd620c9a6b34b1274aeb6f911f75867efcf95b6feda69f1a" + "f4ee16c761b3c9aeac3da03aa9889c88", + "4cd171b23bddb3a53cdf959d5c1710b481eb3785a90eb20a2345ee00d0bb7868c367ab" + "12e6f4dd1dee72af4eee1d197777d1d6499cc541f34edbf45cda6ef90b3c024f9272d7" + "2ec1909fb8fba7db88a4d6f7d3d925980f9f9f72", + "9e3ac938d3eb0cadd6f5c9e35d22ba38", + "9bbf4c1a2742f6ac80cb4e8a052e4a8f4f07c43602361355b717381edf9fabd4cb7e3a" + "d65dbd1378b196ac270588dd0621f642"}, + {"d74e4958717a9d5c0e235b76a926cae8", "0b7471141e0c70b1995fd7b1", + "e701c57d2330bf066f9ff8cf3ca4343cafe4894651cd199bdaaa681ba486b4a65c5a22" + "b0f1420be29ea547d42c713bc6af66aa", + "4a42b7aae8c245c6f1598a395316e4b8484dbd6e64648d5e302021b1d3fa0a38f46e22" + "bd9c8080b863dc0016482538a8562a4bd0ba84edbe2697c76fd039527ac179ec5506cf" + "34a6039312774cedebf4961f3978b14a26509f96", + "e192c23cb036f0b31592989119eed55d", + "840d9fb95e32559fb3602e48590280a172ca36d9b49ab69510f5bd552bfab7a306f85f" + "f0a34bc305b88b804c60b90add594a17"}, + { + "1986310c725ac94ecfe6422e75fc3ee7", "93ec4214fa8e6dc4e3afc775", + "b178ec72f85a311ac4168f42a4b2c23113fbea4b85f4b9dabb74e143eb1b8b0a361e02" + "43edfd365b90d5b325950df0ada058f9", + "e80b88e62c49c958b5e0b8b54f532d9ff6aa84c8a40132e93e55b59fc24e8decf28463" + "139f155d1e8ce4ee76aaeefcd245baa0fc519f83a5fb9ad9aa40c4b21126013f576c42" + "72c2cb136c8fd091cc4539877a5d1e72d607f960", + "8b347853f11d75e81e8a95010be81f17", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_5[] = { + {"387218b246c1a8257748b56980e50c94", "dd7e014198672be39f95b69d", + "cdba9e73eaf3d38eceb2b04a8d", "", "ecf90f4a47c9c626d6fb2c765d201556", + "48f5b426baca03064554cc2b30"}, + {"294de463721e359863887c820524b3d4", "3338b35c9d57a5d28190e8c9", + "2f46634e74b8e4c89812ac83b9", "", "dabd506764e68b82a7e720aa18da0abe", + "46a2e55c8e264df211bd112685"}, + {"28ead7fd2179e0d12aa6d5d88c58c2dc", "5055347f18b4d5add0ae5c41", + "142d8210c3fb84774cdbd0447a", "", "5fd321d9cdb01952dc85f034736c2a7d", + "3b95b981086ee73cc4d0cc1422"}, + { + "7d7b6c988137b8d470c57bf674a09c87", "9edf2aa970d016ac962e1fd8", + "a85b66c3cb5eab91d5bdc8bc0e", "", "dc054efc01f3afd21d9c2484819f569a", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector* const test_group_array[] = { + test_group_0, test_group_1, test_group_2, + test_group_3, test_group_4, test_group_5, +}; + +} // namespace + +namespace quic { +namespace test { + +// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the plaintext. +QuicData* DecryptWithNonce(Aes128GcmDecrypter* decrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece ciphertext) { + decrypter->SetIV(nonce); + std::unique_ptr<char[]> output(new char[ciphertext.length()]); + size_t output_length = 0; + const bool success = + decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(), + &output_length, ciphertext.length()); + if (!success) { + return nullptr; + } + return new QuicData(output.release(), output_length, true); +} + +class Aes128GcmDecrypterTest : public QuicTest {}; + +TEST_F(Aes128GcmDecrypterTest, Decrypt) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vectors = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vectors[j].key != nullptr; j++) { + // If not present then decryption is expected to fail. + bool has_pt = test_vectors[j].pt; + + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[j].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[j].iv); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[j].ct); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[j].aad); + QuicString tag = QuicTextUtils::HexDecode(test_vectors[j].tag); + QuicString pt; + if (has_pt) { + pt = QuicTextUtils::HexDecode(test_vectors[j].pt); + } + + // The test vector's lengths should look sane. Note that the lengths + // in |test_info| are in bits. + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); + if (has_pt) { + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + } + QuicString ciphertext = ct + tag; + + Aes128GcmDecrypter decrypter; + ASSERT_TRUE(decrypter.SetKey(key)); + + std::unique_ptr<QuicData> decrypted( + DecryptWithNonce(&decrypter, iv, + // This deliberately tests that the decrypter can + // handle an AAD that is set to nullptr, as opposed + // to a zero-length, non-nullptr pointer. + aad.length() ? aad : QuicStringPiece(), ciphertext)); + if (!decrypted.get()) { + EXPECT_FALSE(has_pt); + continue; + } + EXPECT_TRUE(has_pt); + + ASSERT_EQ(pt.length(), decrypted->length()); + test::CompareCharArraysWithHexError("plaintext", decrypted->data(), + pt.length(), pt.data(), pt.length()); + } + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_encrypter.cc b/quic/core/crypto/aes_128_gcm_encrypter.cc new file mode 100644 index 0000000..ed08a41 --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_encrypter.cc
@@ -0,0 +1,30 @@ +// Copyright (c) 2017 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/crypto/aes_128_gcm_encrypter.h" + +#include "third_party/boringssl/src/include/openssl/evp.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 16; +const size_t kNonceSize = 12; + +} // namespace + +Aes128GcmEncrypter::Aes128GcmEncrypter() + : AeadBaseEncrypter(EVP_aead_aes_128_gcm, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ true) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +Aes128GcmEncrypter::~Aes128GcmEncrypter() {} + +} // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_encrypter.h b/quic/core/crypto/aes_128_gcm_encrypter.h new file mode 100644 index 0000000..a545ca8 --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_encrypter.h
@@ -0,0 +1,33 @@ +// Copyright (c) 2017 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_CRYPTO_AES_128_GCM_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// An Aes128GcmEncrypter is a QuicEncrypter that implements the +// AEAD_AES_128_GCM algorithm specified in RFC 5116 for use in IETF QUIC. +// +// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV +// that is XOR'd with the packet number to compute the nonce. +class QUIC_EXPORT_PRIVATE Aes128GcmEncrypter : public AeadBaseEncrypter { + public: + enum { + kAuthTagSize = 16, + }; + + Aes128GcmEncrypter(); + Aes128GcmEncrypter(const Aes128GcmEncrypter&) = delete; + Aes128GcmEncrypter& operator=(const Aes128GcmEncrypter&) = delete; + ~Aes128GcmEncrypter() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
diff --git a/quic/core/crypto/aes_128_gcm_encrypter_test.cc b/quic/core/crypto/aes_128_gcm_encrypter_test.cc new file mode 100644 index 0000000..959cb9d --- /dev/null +++ b/quic/core/crypto/aes_128_gcm_encrypter_test.cc
@@ -0,0 +1,258 @@ +// Copyright (c) 2017 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/crypto/aes_128_gcm_encrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The AES GCM test vectors come from the file gcmEncryptExtIV128.rsp +// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on +// 2013-02-01. The test vectors in that file look like this: +// +// [Keylen = 128] +// [IVlen = 96] +// [PTlen = 0] +// [AADlen = 0] +// [Taglen = 128] +// +// Count = 0 +// Key = 11754cd72aec309bf52f7687212e8957 +// IV = 3c819d9a9bed087615030b65 +// PT = +// AAD = +// CT = +// Tag = 250327c674aaf477aef2675748cf6971 +// +// Count = 1 +// Key = ca47248ac0b6f8372a97ac43508308ed +// IV = ffd2b598feabc9019262d2be +// PT = +// AAD = +// CT = +// Tag = 60d20404af527d248d893ae495707d1a +// +// ... +// +// The gcmEncryptExtIV128.rsp file is huge (2.8 MB), so I selected just a +// few test vectors for this unit test. + +// Describes a group of test vectors that all have a given key length, IV +// length, plaintext length, AAD length, and tag length. +struct TestGroupInfo { + size_t key_len; + size_t iv_len; + size_t pt_len; + size_t aad_len; + size_t tag_len; +}; + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + const char* key; + const char* iv; + const char* pt; + const char* aad; + const char* ct; + const char* tag; +}; + +const TestGroupInfo test_group_info[] = { + {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128}, + {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128}, +}; + +const TestVector test_group_0[] = { + {"11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", "", "", + "250327c674aaf477aef2675748cf6971"}, + {"ca47248ac0b6f8372a97ac43508308ed", "ffd2b598feabc9019262d2be", "", "", "", + "60d20404af527d248d893ae495707d1a"}, + {nullptr}}; + +const TestVector test_group_1[] = { + {"77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", "", + "7a43ec1d9c0a5a78a0b16533a6213cab", "", + "209fcc8d3675ed938e9c7166709dd946"}, + {"7680c5d3ca6154758e510f4d25b98820", "f8f105f9c3df4965780321f8", "", + "c94c410194c765e3dcc7964379758ed3", "", + "94dca8edfcf90bb74b153c8d48a17930"}, + {nullptr}}; + +const TestVector test_group_2[] = { + {"7fddb57453c241d03efbed3ac44e371c", "ee283a3fc75575e33efd4887", + "d5de42b461646c255c87bd2962d3b9a2", "", "2ccda4a5415cb91e135c2a0f78c9b2fd", + "b36d1df9b9d5e596f83e8b7f52971cb3"}, + {"ab72c77b97cb5fe9a382d9fe81ffdbed", "54cc7dc2c37ec006bcc6d1da", + "007c5e5b3e59df24a7c355584fc1518d", "", "0e1bde206a07a9c2c1b65300f8c64997", + "2b4401346697138c7a4891ee59867d0c"}, + {nullptr}}; + +const TestVector test_group_3[] = { + {"fe47fcce5fc32665d2ae399e4eec72ba", "5adb9609dbaeb58cbd6e7275", + "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1" + "b840382c4bccaf3bafb4ca8429bea063", + "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a", + "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf539304373636525" + "3ddbc5db8778371495da76d269e5db3e", + "291ef1982e4defedaa2249f898556b47"}, + {"ec0c2ba17aa95cd6afffe949da9cc3a8", "296bce5b50b7d66096d627ef", + "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987" + "b764b9611f6c0f8641843d5d58f3a242", + "f8d00f05d22bf68599bcdeb131292ad6e2df5d14", + "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a0716299" + "5506fde6309ffc19e716eddf1a828c5a", + "890147971946b627c40016da1ecf3e77"}, + {nullptr}}; + +const TestVector test_group_4[] = { + {"2c1f21cf0f6fb3661943155c3e3d8492", "23cb5ff362e22426984d1907", + "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d6" + "8b5615ba7c1220ff6510e259f06655d8", + "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e" + "3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f" + "4488f33cfb5e979e42b6e1cfc0a60238982a7aec", + "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222" + "b6ad57af43e1895df9dca2a5344a62cc", + "57a3ee28136e94c74838997ae9823f3a"}, + {"d9f7d2411091f947b4d6f1e2d1f0fb2e", "e1934f5db57cc983e6b180e7", + "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490" + "c2c6f6166f4a59431e182663fcaea05a", + "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d" + "0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a201" + "15d2e51398344b16bee1ed7c499b353d6c597af8", + "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d57" + "3c7891c2a91fbc48db29967ec9542b23", + "21b51ca862cb637cdd03b99a0f93b134"}, + {nullptr}}; + +const TestVector test_group_5[] = { + {"fe9bb47deb3a61e423c2231841cfd1fb", "4d328eb776f500a2f7fb47aa", + "f1cc3818e421876bb6b8bbd6c9", "", "b88c5c1977b35b517b0aeae967", + "43fd4727fe5cdb4b5b42818dea7ef8c9"}, + {"6703df3701a7f54911ca72e24dca046a", "12823ab601c350ea4bc2488c", + "793cd125b0b84a043e3ac67717", "", "b2051c80014f42f08735a7b0cd", + "38e6bcd29962e5f2c13626b85a877101"}, + {nullptr}}; + +const TestVector* const test_group_array[] = { + test_group_0, test_group_1, test_group_2, + test_group_3, test_group_4, test_group_5, +}; + +} // namespace + +namespace quic { +namespace test { + +// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the ciphertext. +QuicData* EncryptWithNonce(Aes128GcmEncrypter* encrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece plaintext) { + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!encrypter->Encrypt(nonce, associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return nullptr; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +class Aes128GcmEncrypterTest : public QuicTest {}; + +TEST_F(Aes128GcmEncrypterTest, Encrypt) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vectors = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vectors[j].key != nullptr; j++) { + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[j].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[j].iv); + QuicString pt = QuicTextUtils::HexDecode(test_vectors[j].pt); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[j].aad); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[j].ct); + QuicString tag = QuicTextUtils::HexDecode(test_vectors[j].tag); + + // The test vector's lengths should look sane. Note that the lengths + // in |test_info| are in bits. + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); + + Aes128GcmEncrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(key)); + std::unique_ptr<QuicData> encrypted( + EncryptWithNonce(&encrypter, iv, + // This deliberately tests that the encrypter can + // handle an AAD that is set to nullptr, as opposed + // to a zero-length, non-nullptr pointer. + aad.length() ? aad : QuicStringPiece(), pt)); + ASSERT_TRUE(encrypted.get()); + + ASSERT_EQ(ct.length() + tag.length(), encrypted->length()); + test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), + ct.length(), ct.data(), ct.length()); + test::CompareCharArraysWithHexError( + "authentication tag", encrypted->data() + ct.length(), tag.length(), + tag.data(), tag.length()); + } + } +} + +TEST_F(Aes128GcmEncrypterTest, EncryptPacket) { + QuicString key = QuicTextUtils::HexDecode("d95a145250826c25a77b6a84fd4d34fc"); + QuicString iv = QuicTextUtils::HexDecode("50c4431ebb18283448e276e2"); + uint64_t packet_num = 0x13278f44; + QuicString aad = + QuicTextUtils::HexDecode("875d49f64a70c9cbe713278f44ff000005"); + QuicString pt = QuicTextUtils::HexDecode("aa0003a250bd000000000001"); + QuicString ct = QuicTextUtils::HexDecode( + "7dd4708b989ee7d38a013e3656e9b37beefd05808fe1ab41e3b4f2c0"); + + std::vector<char> out(ct.size()); + size_t out_size; + + Aes128GcmEncrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(key)); + ASSERT_TRUE(encrypter.SetIV(iv)); + ASSERT_TRUE(encrypter.EncryptPacket(packet_num, aad, pt, out.data(), + &out_size, out.size())); + EXPECT_EQ(out_size, out.size()); + test::CompareCharArraysWithHexError("ciphertext", out.data(), out.size(), + ct.data(), ct.size()); +} + +TEST_F(Aes128GcmEncrypterTest, GetMaxPlaintextSize) { + Aes128GcmEncrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26)); +} + +TEST_F(Aes128GcmEncrypterTest, GetCiphertextSize) { + Aes128GcmEncrypter encrypter; + EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(116u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(26u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/aes_256_gcm_decrypter.cc b/quic/core/crypto/aes_256_gcm_decrypter.cc new file mode 100644 index 0000000..08087a7 --- /dev/null +++ b/quic/core/crypto/aes_256_gcm_decrypter.cc
@@ -0,0 +1,37 @@ +// Copyright (c) 2017 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/crypto/aes_256_gcm_decrypter.h" + +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "third_party/boringssl/src/include/openssl/tls1.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 32; +const size_t kNonceSize = 12; + +} // namespace + +Aes256GcmDecrypter::Aes256GcmDecrypter() + : AeadBaseDecrypter(EVP_aead_aes_256_gcm, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ true) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +Aes256GcmDecrypter::~Aes256GcmDecrypter() {} + +uint32_t Aes256GcmDecrypter::cipher_id() const { + return TLS1_CK_AES_256_GCM_SHA384; +} + +} // namespace quic
diff --git a/quic/core/crypto/aes_256_gcm_decrypter.h b/quic/core/crypto/aes_256_gcm_decrypter.h new file mode 100644 index 0000000..682a55d --- /dev/null +++ b/quic/core/crypto/aes_256_gcm_decrypter.h
@@ -0,0 +1,37 @@ +// Copyright (c) 2017 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_CRYPTO_AES_256_GCM_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// An Aes256GcmDecrypter is a QuicDecrypter that implements the +// AEAD_AES_256_GCM algorithm specified in RFC 5116 for use in IETF QUIC. +// +// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV +// that is XOR'd with the packet number to compute the nonce. +class QUIC_EXPORT_PRIVATE Aes256GcmDecrypter : public AeadBaseDecrypter { + public: + enum { + kAuthTagSize = 16, + }; + + Aes256GcmDecrypter(); + Aes256GcmDecrypter(const Aes256GcmDecrypter&) = delete; + Aes256GcmDecrypter& operator=(const Aes256GcmDecrypter&) = delete; + ~Aes256GcmDecrypter() override; + + uint32_t cipher_id() const override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_
diff --git a/quic/core/crypto/aes_256_gcm_decrypter_test.cc b/quic/core/crypto/aes_256_gcm_decrypter_test.cc new file mode 100644 index 0000000..cb5f702 --- /dev/null +++ b/quic/core/crypto/aes_256_gcm_decrypter_test.cc
@@ -0,0 +1,279 @@ +// Copyright (c) 2017 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/crypto/aes_256_gcm_decrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The AES GCM test vectors come from the file gcmDecrypt256.rsp +// downloaded from +// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#GCMVS +// on 2017-09-27. The test vectors in that file look like this: +// +// [Keylen = 256] +// [IVlen = 96] +// [PTlen = 0] +// [AADlen = 0] +// [Taglen = 128] +// +// Count = 0 +// Key = f5a2b27c74355872eb3ef6c5feafaa740e6ae990d9d48c3bd9bb8235e589f010 +// IV = 58d2240f580a31c1d24948e9 +// CT = +// AAD = +// Tag = 15e051a5e4a5f5da6cea92e2ebee5bac +// PT = +// +// Count = 1 +// Key = e5a8123f2e2e007d4e379ba114a2fb66e6613f57c72d4e4f024964053028a831 +// IV = 51e43385bf533e168427e1ad +// CT = +// AAD = +// Tag = 38fe845c66e66bdd884c2aecafd280e6 +// FAIL +// +// ... +// +// The gcmDecrypt256.rsp file is huge (3.0 MB), so a few test vectors were +// selected for this unit test. + +// Describes a group of test vectors that all have a given key length, IV +// length, plaintext length, AAD length, and tag length. +struct TestGroupInfo { + size_t key_len; + size_t iv_len; + size_t pt_len; + size_t aad_len; + size_t tag_len; +}; + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + // Input: + const char* key; + const char* iv; + const char* ct; + const char* aad; + const char* tag; + + // Expected output: + const char* pt; // An empty string "" means decryption succeeded and + // the plaintext is zero-length. nullptr means decryption + // failed. +}; + +const TestGroupInfo test_group_info[] = { + {256, 96, 0, 0, 128}, {256, 96, 0, 128, 128}, {256, 96, 128, 0, 128}, + {256, 96, 408, 160, 128}, {256, 96, 408, 720, 128}, {256, 96, 104, 0, 128}, +}; + +const TestVector test_group_0[] = { + {"f5a2b27c74355872eb3ef6c5feafaa740e6ae990d9d48c3bd9bb8235e589f010", + "58d2240f580a31c1d24948e9", "", "", "15e051a5e4a5f5da6cea92e2ebee5bac", + ""}, + { + "e5a8123f2e2e007d4e379ba114a2fb66e6613f57c72d4e4f024964053028a831", + "51e43385bf533e168427e1ad", "", "", "38fe845c66e66bdd884c2aecafd280e6", + nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_1[] = { + {"6dfdafd6703c285c01f14fd10a6012862b2af950d4733abb403b2e745b26945d", + "3749d0b3d5bacb71be06ade6", "", "c0d249871992e70302ae008193d1e89f", + "4aa4cc69f84ee6ac16d9bfb4e05de500", ""}, + { + "2c392a5eb1a9c705371beda3a901c7c61dca4d93b4291de1dd0dd15ec11ffc45", + "0723fb84a08f4ea09841f32a", "", "140be561b6171eab942c486a94d33d43", + "aa0e1c9b57975bfc91aa137231977d2c", nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_2[] = { + {"4c8ebfe1444ec1b2d503c6986659af2c94fafe945f72c1e8486a5acfedb8a0f8", + "473360e0ad24889959858995", "d2c78110ac7e8f107c0df0570bd7c90c", "", + "c26a379b6d98ef2852ead8ce83a833a7", "7789b41cb3ee548814ca0b388c10b343"}, + {"3934f363fd9f771352c4c7a060682ed03c2864223a1573b3af997e2ababd60ab", + "efe2656d878c586e41c539c4", "e0de64302ac2d04048d65a87d2ad09fe", "", + "33cbd8d2fb8a3a03e30c1eb1b53c1d99", "697aff2d6b77e5ed6232770e400c1ead"}, + { + "c997768e2d14e3d38259667a6649079de77beb4543589771e5068e6cd7cd0b14", + "835090aed9552dbdd45277e2", "9f6607d68e22ccf21928db0986be126e", "", + "f32617f67c574fd9f44ef76ff880ab9f", nullptr // FAIL + }, + {nullptr}}; + +const TestVector test_group_3[] = { + { + "e9d381a9c413bee66175d5586a189836e5c20f5583535ab4d3f3e612dc21700e", + "23e81571da1c7821c681c7ca", + "a25f3f580306cd5065d22a6b7e9660110af7204bb77d370f7f34bee547feeff7b32a59" + "6fce29c9040e68b1589aad48da881990", + "6f39c9ae7b8e8a58a95f0dd8ea6a9087cbccdfd6", + "5b6dcd70eefb0892fab1539298b92a4b", + nullptr // FAIL + }, + {"6450d4501b1e6cfbe172c4c8570363e96b496591b842661c28c2f6c908379cad", + "7e4262035e0bf3d60e91668a", + "5a99b336fd3cfd82f10fb08f7045012415f0d9a06bb92dcf59c6f0dbe62d433671aacb8a1" + "c52ce7bbf6aea372bf51e2ba79406", + "f1c522f026e4c5d43851da516a1b78768ab18171", + "fe93b01636f7bb0458041f213e98de65", + "17449e236ef5858f6d891412495ead4607bfae2a2d735182a2a0242f9d52fc5345ef912db" + "e16f3bb4576fe3bcafe336dee6085"}, + {"90f2e71ccb1148979cb742efc8f921de95457d898c84ce28edeed701650d3a26", + "aba58ad60047ba553f6e4c98", + "3fc77a5fe9203d091c7916587c9763cf2e4d0d53ca20b078b851716f1dab4873fe342b7b3" + "01402f015d00263bf3f77c58a99d6", + "2abe465df6e5be47f05b92c9a93d76ae3611fac5", + "9cb3d04637048bc0bddef803ffbb56cf", + "1d21639640e11638a2769e3fab78778f84be3f4a8ce28dfd99cb2e75171e05ea8e94e30aa" + "78b54bb402b39d613616a8ed951dc"}, + {nullptr}}; + +const TestVector test_group_4[] = { + { + "e36aca93414b13f5313e76a7244588ee116551d1f34c32859166f2eb0ac1a9b7", + "e9e701b1ccef6bddd03391d8", + "5b059ac6733b6de0e8cf5b88b7301c02c993426f71bb12abf692e9deeacfac1ff1644c" + "87d4df130028f515f0feda636309a24d", + "6a08fe6e55a08f283cec4c4b37676e770f402af6102f548ad473ec6236da764f7076ff" + "d41bbd9611b439362d899682b7b0f839fc5a68d9df54afd1e2b3c4e7d072454ee27111" + "d52193d28b9c4f925d2a8b451675af39191a2cba", + "43c7c9c93cc265fc8e192000e0417b5b", + nullptr // FAIL + }, + {"5f72046245d3f4a0877e50a86554bfd57d1c5e073d1ed3b5451f6d0fc2a8507a", + "ea6f5b391e44b751b26bce6f", + "0e6e0b2114c40769c15958d965a14dcf50b680e0185a4409d77d894ca15b1e698dd83b353" + "6b18c05d8cd0873d1edce8150ecb5", + "9b3a68c941d42744673fb60fea49075eae77322e7e70e34502c115b6495ebfc796d629080" + "7653c6b53cd84281bd0311656d0013f44619d2748177e99e8f8347c989a7b59f9d8dcf00f" + "31db0684a4a83e037e8777bae55f799b0d", + "fdaaff86ceb937502cd9012d03585800", + "b0a881b751cc1eb0c912a4cf9bd971983707dbd2411725664503455c55db25cdb19bc669c" + "2654a3a8011de6bf7eff3f9f07834"}, + {"ab639bae205547607506522bd3cdca7861369e2b42ef175ff135f6ba435d5a8e", + "5fbb63eb44bd59fee458d8f6", + "9a34c62bed0972285503a32812877187a54dedbd55d2317fed89282bf1af4ba0b6bb9f9e1" + "6dd86da3b441deb7841262bc6bd63", + "1ef2b1768b805587935ffaf754a11bd2a305076d6374f1f5098b1284444b78f55408a786d" + "a37e1b7f1401c330d3585ef56f3e4d35eaaac92e1381d636477dc4f4beaf559735e902d6b" + "e58723257d4ac1ed9bd213de387f35f3c4", + "e0299e079bff46fd12e36d1c60e41434", + "e5a3ce804a8516cdd12122c091256b789076576040dbf3c55e8be3c016025896b8a72532b" + "fd51196cc82efca47aa0fd8e2e0dc"}, + {nullptr}}; + +const TestVector test_group_5[] = { + { + "8b37c4b8cf634704920059866ad96c49e9da502c63fca4a3a7a4dcec74cb0610", + "cb59344d2b06c4ae57cd0ea4", "66ab935c93555e786b775637a3", "", + "d8733acbb564d8afaa99d7ca2e2f92a9", nullptr // FAIL + }, + {"a71dac1377a3bf5d7fb1b5e36bee70d2e01de2a84a1c1009ba7448f7f26131dc", + "c5b60dda3f333b1146e9da7c", "43af49ec1ae3738a20755034d6", "", + "6f80b6ef2d8830a55eb63680a8dff9e0", "5b87141335f2becac1a559e05f"}, + {"dc1f64681014be221b00793bbcf5a5bc675b968eb7a3a3d5aa5978ef4fa45ecc", + "056ae9a1a69e38af603924fe", "33013a48d9ea0df2911d583271", "", + "5b8f9cc22303e979cd1524187e9f70fe", "2a7e05612191c8bce2f529dca9"}, + {nullptr}}; + +const TestVector* const test_group_array[] = { + test_group_0, test_group_1, test_group_2, + test_group_3, test_group_4, test_group_5, +}; + +} // namespace + +namespace quic { +namespace test { + +// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the plaintext. +QuicData* DecryptWithNonce(Aes256GcmDecrypter* decrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece ciphertext) { + decrypter->SetIV(nonce); + std::unique_ptr<char[]> output(new char[ciphertext.length()]); + size_t output_length = 0; + const bool success = + decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(), + &output_length, ciphertext.length()); + if (!success) { + return nullptr; + } + return new QuicData(output.release(), output_length, true); +} + +class Aes256GcmDecrypterTest : public QuicTest {}; + +TEST_F(Aes256GcmDecrypterTest, Decrypt) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vectors = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vectors[j].key != nullptr; j++) { + // If not present then decryption is expected to fail. + bool has_pt = test_vectors[j].pt; + + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[j].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[j].iv); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[j].ct); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[j].aad); + QuicString tag = QuicTextUtils::HexDecode(test_vectors[j].tag); + QuicString pt; + if (has_pt) { + pt = QuicTextUtils::HexDecode(test_vectors[j].pt); + } + + // The test vector's lengths should look sane. Note that the lengths + // in |test_info| are in bits. + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); + if (has_pt) { + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + } + QuicString ciphertext = ct + tag; + + Aes256GcmDecrypter decrypter; + ASSERT_TRUE(decrypter.SetKey(key)); + + std::unique_ptr<QuicData> decrypted( + DecryptWithNonce(&decrypter, iv, + // This deliberately tests that the decrypter can + // handle an AAD that is set to nullptr, as opposed + // to a zero-length, non-nullptr pointer. + aad.length() ? aad : QuicStringPiece(), ciphertext)); + if (!decrypted.get()) { + EXPECT_FALSE(has_pt); + continue; + } + EXPECT_TRUE(has_pt); + + ASSERT_EQ(pt.length(), decrypted->length()); + test::CompareCharArraysWithHexError("plaintext", decrypted->data(), + pt.length(), pt.data(), pt.length()); + } + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/aes_256_gcm_encrypter.cc b/quic/core/crypto/aes_256_gcm_encrypter.cc new file mode 100644 index 0000000..b329f8e --- /dev/null +++ b/quic/core/crypto/aes_256_gcm_encrypter.cc
@@ -0,0 +1,30 @@ +// Copyright (c) 2017 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/crypto/aes_256_gcm_encrypter.h" + +#include "third_party/boringssl/src/include/openssl/evp.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 32; +const size_t kNonceSize = 12; + +} // namespace + +Aes256GcmEncrypter::Aes256GcmEncrypter() + : AeadBaseEncrypter(EVP_aead_aes_256_gcm, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ true) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +Aes256GcmEncrypter::~Aes256GcmEncrypter() {} + +} // namespace quic
diff --git a/quic/core/crypto/aes_256_gcm_encrypter.h b/quic/core/crypto/aes_256_gcm_encrypter.h new file mode 100644 index 0000000..6c9e81c --- /dev/null +++ b/quic/core/crypto/aes_256_gcm_encrypter.h
@@ -0,0 +1,33 @@ +// Copyright (c) 2017 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_CRYPTO_AES_256_GCM_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// An Aes256GcmEncrypter is a QuicEncrypter that implements the +// AEAD_AES_256_GCM algorithm specified in RFC 5116 for use in IETF QUIC. +// +// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV +// that is XOR'd with the packet number to compute the nonce. +class QUIC_EXPORT_PRIVATE Aes256GcmEncrypter : public AeadBaseEncrypter { + public: + enum { + kAuthTagSize = 16, + }; + + Aes256GcmEncrypter(); + Aes256GcmEncrypter(const Aes256GcmEncrypter&) = delete; + Aes256GcmEncrypter& operator=(const Aes256GcmEncrypter&) = delete; + ~Aes256GcmEncrypter() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
diff --git a/quic/core/crypto/aes_256_gcm_encrypter_test.cc b/quic/core/crypto/aes_256_gcm_encrypter_test.cc new file mode 100644 index 0000000..0472986 --- /dev/null +++ b/quic/core/crypto/aes_256_gcm_encrypter_test.cc
@@ -0,0 +1,242 @@ +// Copyright (c) 2017 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/crypto/aes_256_gcm_encrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The AES GCM test vectors come from the file gcmEncryptExtIV256.rsp +// downloaded from +// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#GCMVS +// on 2017-09-27. The test vectors in that file look like this: +// +// [Keylen = 256] +// [IVlen = 96] +// [PTlen = 0] +// [AADlen = 0] +// [Taglen = 128] +// +// Count = 0 +// Key = b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4 +// IV = 516c33929df5a3284ff463d7 +// PT = +// AAD = +// CT = +// Tag = bdc1ac884d332457a1d2664f168c76f0 +// +// Count = 1 +// Key = 5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f +// IV = 770ac1a5a3d476d5d96944a1 +// PT = +// AAD = +// CT = +// Tag = 196d691e1047093ca4b3d2ef4baba216 +// +// ... +// +// The gcmEncryptExtIV256.rsp file is huge (3.2 MB), so a few test vectors were +// selected for this unit test. + +// Describes a group of test vectors that all have a given key length, IV +// length, plaintext length, AAD length, and tag length. +struct TestGroupInfo { + size_t key_len; + size_t iv_len; + size_t pt_len; + size_t aad_len; + size_t tag_len; +}; + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + const char* key; + const char* iv; + const char* pt; + const char* aad; + const char* ct; + const char* tag; +}; + +const TestGroupInfo test_group_info[] = { + {256, 96, 0, 0, 128}, {256, 96, 0, 128, 128}, {256, 96, 128, 0, 128}, + {256, 96, 408, 160, 128}, {256, 96, 408, 720, 128}, {256, 96, 104, 0, 128}, +}; + +const TestVector test_group_0[] = { + {"b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4", + "516c33929df5a3284ff463d7", "", "", "", + "bdc1ac884d332457a1d2664f168c76f0"}, + {"5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f", + "770ac1a5a3d476d5d96944a1", "", "", "", + "196d691e1047093ca4b3d2ef4baba216"}, + {nullptr}}; + +const TestVector test_group_1[] = { + {"78dc4e0aaf52d935c3c01eea57428f00ca1fd475f5da86a49c8dd73d68c8e223", + "d79cf22d504cc793c3fb6c8a", "", "b96baa8c1c75a671bfb2d08d06be5f36", "", + "3e5d486aa2e30b22e040b85723a06e76"}, + {"4457ff33683cca6ca493878bdc00373893a9763412eef8cddb54f91318e0da88", + "699d1f29d7b8c55300bb1fd2", "", "6749daeea367d0e9809e2dc2f309e6e3", "", + "d60c74d2517fde4a74e0cd4709ed43a9"}, + {nullptr}}; + +const TestVector test_group_2[] = { + {"31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22", + "0d18e06c7c725ac9e362e1ce", "2db5168e932556f8089a0622981d017d", "", + "fa4362189661d163fcd6a56d8bf0405a", "d636ac1bbedd5cc3ee727dc2ab4a9489"}, + {"460fc864972261c2560e1eb88761ff1c992b982497bd2ac36c04071cbb8e5d99", + "8a4a16b9e210eb68bcb6f58d", "99e4e926ffe927f691893fb79a96b067", "", + "133fc15751621b5f325c7ff71ce08324", "ec4e87e0cf74a13618d0b68636ba9fa7"}, + {nullptr}}; + +const TestVector test_group_3[] = { + {"24501ad384e473963d476edcfe08205237acfd49b5b8f33857f8114e863fec7f", + "9ff18563b978ec281b3f2794", + "27f348f9cdc0c5bd5e66b1ccb63ad920ff2219d14e8d631b3872265cf117ee86757accb15" + "8bd9abb3868fdc0d0b074b5f01b2c", + "adb5ec720ccf9898500028bf34afccbcaca126ef", + "eb7cb754c824e8d96f7c6d9b76c7d26fb874ffbf1d65c6f64a698d839b0b06145dae82057" + "ad55994cf59ad7f67c0fa5e85fab8", + "bc95c532fecc594c36d1550286a7a3f0"}, + {"fb43f5ab4a1738a30c1e053d484a94254125d55dccee1ad67c368bc1a985d235", + "9fbb5f8252db0bca21f1c230", + "34b797bb82250e23c5e796db2c37e488b3b99d1b981cea5e5b0c61a0b39adb6bd6ef1f507" + "22e2e4f81115cfcf53f842e2a6c08", + "98f8ae1735c39f732e2cbee1156dabeb854ec7a2", + "871cd53d95a8b806bd4821e6c4456204d27fd704ba3d07ce25872dc604ea5c5ea13322186" + "b7489db4fa060c1fd4159692612c8", + "07b48e4a32fac47e115d7ac7445d8330"}, + {nullptr}}; + +const TestVector test_group_4[] = { + {"148579a3cbca86d5520d66c0ec71ca5f7e41ba78e56dc6eebd566fed547fe691", + "b08a5ea1927499c6ecbfd4e0", + "9d0b15fdf1bd595f91f8b3abc0f7dec927dfd4799935a1795d9ce00c9b879434420fe42c2" + "75a7cd7b39d638fb81ca52b49dc41", + "e4f963f015ffbb99ee3349bbaf7e8e8e6c2a71c230a48f9d59860a29091d2747e01a5ca57" + "2347e247d25f56ba7ae8e05cde2be3c97931292c02370208ecd097ef692687fecf2f419d3" + "200162a6480a57dad408a0dfeb492e2c5d", + "2097e372950a5e9383c675e89eea1c314f999159f5611344b298cda45e62843716f215f82" + "ee663919c64002a5c198d7878fd3f", + "adbecdb0d5c2224d804d2886ff9a5760"}, + {"e49af19182faef0ebeeba9f2d3be044e77b1212358366e4ef59e008aebcd9788", + "e7f37d79a6a487a5a703edbb", + "461cd0caf7427a3d44408d825ed719237272ecd503b9094d1f62c97d63ed83a0b50bdc804" + "ffdd7991da7a5b6dcf48d4bcd2cbc", + "19a9a1cfc647346781bef51ed9070d05f99a0e0192a223c5cd2522dbdf97d9739dd39fb17" + "8ade3339e68774b058aa03e9a20a9a205bc05f32381df4d63396ef691fefd5a71b49a2ad8" + "2d5ea428778ca47ee1398792762413cff4", + "32ca3588e3e56eb4c8301b009d8b84b8a900b2b88ca3c21944205e9dd7311757b51394ae9" + "0d8bb3807b471677614f4198af909", + "3e403d035c71d88f1be1a256c89ba6ad"}, + {nullptr}}; + +const TestVector test_group_5[] = { + {"82c4f12eeec3b2d3d157b0f992d292b237478d2cecc1d5f161389b97f999057a", + "7b40b20f5f397177990ef2d1", "982a296ee1cd7086afad976945", "", + "ec8e05a0471d6b43a59ca5335f", "113ddeafc62373cac2f5951bb9165249"}, + {"db4340af2f835a6c6d7ea0ca9d83ca81ba02c29b7410f221cb6071114e393240", + "40e438357dd80a85cac3349e", "8ddb3397bd42853193cb0f80c9", "", + "b694118c85c41abf69e229cb0f", "c07f1b8aafbd152f697eb67f2a85fe45"}, + {nullptr}}; + +const TestVector* const test_group_array[] = { + test_group_0, test_group_1, test_group_2, + test_group_3, test_group_4, test_group_5, +}; + +} // namespace + +namespace quic { +namespace test { + +// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the ciphertext. +QuicData* EncryptWithNonce(Aes256GcmEncrypter* encrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece plaintext) { + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!encrypter->Encrypt(nonce, associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return nullptr; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +class Aes256GcmEncrypterTest : public QuicTest {}; + +TEST_F(Aes256GcmEncrypterTest, Encrypt) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vectors = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vectors[j].key != nullptr; j++) { + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[j].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[j].iv); + QuicString pt = QuicTextUtils::HexDecode(test_vectors[j].pt); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[j].aad); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[j].ct); + QuicString tag = QuicTextUtils::HexDecode(test_vectors[j].tag); + + // The test vector's lengths should look sane. Note that the lengths + // in |test_info| are in bits. + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); + + Aes256GcmEncrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(key)); + std::unique_ptr<QuicData> encrypted( + EncryptWithNonce(&encrypter, iv, + // This deliberately tests that the encrypter can + // handle an AAD that is set to nullptr, as opposed + // to a zero-length, non-nullptr pointer. + aad.length() ? aad : QuicStringPiece(), pt)); + ASSERT_TRUE(encrypted.get()); + + ASSERT_EQ(ct.length() + tag.length(), encrypted->length()); + test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), + ct.length(), ct.data(), ct.length()); + test::CompareCharArraysWithHexError( + "authentication tag", encrypted->data() + ct.length(), tag.length(), + tag.data(), tag.length()); + } + } +} + +TEST_F(Aes256GcmEncrypterTest, GetMaxPlaintextSize) { + Aes256GcmEncrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26)); +} + +TEST_F(Aes256GcmEncrypterTest, GetCiphertextSize) { + Aes256GcmEncrypter encrypter; + EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(116u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(26u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/cert_compressor.cc b/quic/core/crypto/cert_compressor.cc new file mode 100644 index 0000000..204539b --- /dev/null +++ b/quic/core/crypto/cert_compressor.cc
@@ -0,0 +1,642 @@ +// Copyright (c) 2013 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/crypto/cert_compressor.h" + +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "third_party/zlib/zlib.h" + +namespace quic { + +namespace { + +// kCommonCertSubstrings contains ~1500 bytes of common certificate substrings +// in order to help zlib. This was generated via a fairly dumb algorithm from +// the Alexa Top 5000 set - we could probably do better. +static const unsigned char kCommonCertSubstrings[] = { + 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, + 0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, + 0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07, + 0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34, + 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, + 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65, + 0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2, + 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, + 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, + 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, + 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33, + 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, + 0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, + 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, + 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, + 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, + 0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, + 0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17, + 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72, + 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73, + 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, + 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01, + 0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31, + 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65, + 0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, + 0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, + 0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, + 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72, + 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72, + 0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63, + 0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, + 0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, + 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27, + 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, +}; + +// CertEntry represents a certificate in compressed form. Each entry is one of +// the three types enumerated in |Type|. +struct CertEntry { + public: + enum Type { + // Type 0 is reserved to mean "end of list" in the wire format. + + // COMPRESSED means that the certificate is included in the trailing zlib + // data. + COMPRESSED = 1, + // CACHED means that the certificate is already known to the peer and will + // be replaced by its 64-bit hash (in |hash|). + CACHED = 2, + // COMMON means that the certificate is in a common certificate set known + // to the peer with hash |set_hash| and certificate index |index|. + COMMON = 3, + }; + + Type type; + uint64_t hash; + uint64_t set_hash; + uint32_t index; +}; + +// MatchCerts returns a vector of CertEntries describing how to most +// efficiently represent |certs| to a peer who has the common sets identified +// by |client_common_set_hashes| and who has cached the certificates with the +// 64-bit, FNV-1a hashes in |client_cached_cert_hashes|. +std::vector<CertEntry> MatchCerts(const std::vector<QuicString>& certs, + QuicStringPiece client_common_set_hashes, + QuicStringPiece client_cached_cert_hashes, + const CommonCertSets* common_sets) { + std::vector<CertEntry> entries; + entries.reserve(certs.size()); + + const bool cached_valid = + client_cached_cert_hashes.size() % sizeof(uint64_t) == 0 && + !client_cached_cert_hashes.empty(); + + for (auto i = certs.begin(); i != certs.end(); ++i) { + CertEntry entry; + + if (cached_valid) { + bool cached = false; + + uint64_t hash = QuicUtils::FNV1a_64_Hash(*i); + // This assumes that the machine is little-endian. + for (size_t j = 0; j < client_cached_cert_hashes.size(); + j += sizeof(uint64_t)) { + uint64_t cached_hash; + memcpy(&cached_hash, client_cached_cert_hashes.data() + j, + sizeof(uint64_t)); + if (hash != cached_hash) { + continue; + } + + entry.type = CertEntry::CACHED; + entry.hash = hash; + entries.push_back(entry); + cached = true; + break; + } + + if (cached) { + continue; + } + } + + if (common_sets && common_sets->MatchCert(*i, client_common_set_hashes, + &entry.set_hash, &entry.index)) { + entry.type = CertEntry::COMMON; + entries.push_back(entry); + continue; + } + + entry.type = CertEntry::COMPRESSED; + entries.push_back(entry); + } + + return entries; +} + +// CertEntriesSize returns the size, in bytes, of the serialised form of +// |entries|. +size_t CertEntriesSize(const std::vector<CertEntry>& entries) { + size_t entries_size = 0; + + for (auto i = entries.begin(); i != entries.end(); ++i) { + entries_size++; + switch (i->type) { + case CertEntry::COMPRESSED: + break; + case CertEntry::CACHED: + entries_size += sizeof(uint64_t); + break; + case CertEntry::COMMON: + entries_size += sizeof(uint64_t) + sizeof(uint32_t); + break; + } + } + + entries_size++; // for end marker + + return entries_size; +} + +// SerializeCertEntries serialises |entries| to |out|, which must have enough +// space to contain them. +void SerializeCertEntries(uint8_t* out, const std::vector<CertEntry>& entries) { + for (auto i = entries.begin(); i != entries.end(); ++i) { + *out++ = static_cast<uint8_t>(i->type); + switch (i->type) { + case CertEntry::COMPRESSED: + break; + case CertEntry::CACHED: + memcpy(out, &i->hash, sizeof(i->hash)); + out += sizeof(uint64_t); + break; + case CertEntry::COMMON: + // Assumes a little-endian machine. + memcpy(out, &i->set_hash, sizeof(i->set_hash)); + out += sizeof(i->set_hash); + memcpy(out, &i->index, sizeof(uint32_t)); + out += sizeof(uint32_t); + break; + } + } + + *out++ = 0; // end marker +} + +// ZlibDictForEntries returns a string that contains the zlib pre-shared +// dictionary to use in order to decompress a zlib block following |entries|. +// |certs| is one-to-one with |entries| and contains the certificates for those +// entries that are CACHED or COMMON. +QuicString ZlibDictForEntries(const std::vector<CertEntry>& entries, + const std::vector<QuicString>& certs) { + QuicString zlib_dict; + + // The dictionary starts with the common and cached certs in reverse order. + size_t zlib_dict_size = 0; + for (size_t i = certs.size() - 1; i < certs.size(); i--) { + if (entries[i].type != CertEntry::COMPRESSED) { + zlib_dict_size += certs[i].size(); + } + } + + // At the end of the dictionary is a block of common certificate substrings. + zlib_dict_size += sizeof(kCommonCertSubstrings); + + zlib_dict.reserve(zlib_dict_size); + + for (size_t i = certs.size() - 1; i < certs.size(); i--) { + if (entries[i].type != CertEntry::COMPRESSED) { + zlib_dict += certs[i]; + } + } + + zlib_dict += QuicString(reinterpret_cast<const char*>(kCommonCertSubstrings), + sizeof(kCommonCertSubstrings)); + + DCHECK_EQ(zlib_dict.size(), zlib_dict_size); + + return zlib_dict; +} + +// HashCerts returns the FNV-1a hashes of |certs|. +std::vector<uint64_t> HashCerts(const std::vector<QuicString>& certs) { + std::vector<uint64_t> ret; + ret.reserve(certs.size()); + + for (auto i = certs.begin(); i != certs.end(); ++i) { + ret.push_back(QuicUtils::FNV1a_64_Hash(*i)); + } + + return ret; +} + +// ParseEntries parses the serialised form of a vector of CertEntries from +// |in_out| and writes them to |out_entries|. CACHED and COMMON entries are +// resolved using |cached_certs| and |common_sets| and written to |out_certs|. +// |in_out| is updated to contain the trailing data. +bool ParseEntries(QuicStringPiece* in_out, + const std::vector<QuicString>& cached_certs, + const CommonCertSets* common_sets, + std::vector<CertEntry>* out_entries, + std::vector<QuicString>* out_certs) { + QuicStringPiece in = *in_out; + std::vector<uint64_t> cached_hashes; + + out_entries->clear(); + out_certs->clear(); + + for (;;) { + if (in.empty()) { + return false; + } + CertEntry entry; + const uint8_t type_byte = in[0]; + in.remove_prefix(1); + + if (type_byte == 0) { + break; + } + + entry.type = static_cast<CertEntry::Type>(type_byte); + + switch (entry.type) { + case CertEntry::COMPRESSED: + out_certs->push_back(QuicString()); + break; + case CertEntry::CACHED: { + if (in.size() < sizeof(uint64_t)) { + return false; + } + memcpy(&entry.hash, in.data(), sizeof(uint64_t)); + in.remove_prefix(sizeof(uint64_t)); + + if (cached_hashes.size() != cached_certs.size()) { + cached_hashes = HashCerts(cached_certs); + } + bool found = false; + for (size_t i = 0; i < cached_hashes.size(); i++) { + if (cached_hashes[i] == entry.hash) { + out_certs->push_back(cached_certs[i]); + found = true; + break; + } + } + if (!found) { + return false; + } + break; + } + case CertEntry::COMMON: { + if (!common_sets) { + return false; + } + if (in.size() < sizeof(uint64_t) + sizeof(uint32_t)) { + return false; + } + memcpy(&entry.set_hash, in.data(), sizeof(uint64_t)); + in.remove_prefix(sizeof(uint64_t)); + memcpy(&entry.index, in.data(), sizeof(uint32_t)); + in.remove_prefix(sizeof(uint32_t)); + + QuicStringPiece cert = + common_sets->GetCert(entry.set_hash, entry.index); + if (cert.empty()) { + return false; + } + out_certs->push_back(QuicString(cert)); + break; + } + default: + return false; + } + out_entries->push_back(entry); + } + + *in_out = in; + return true; +} + +// ScopedZLib deals with the automatic destruction of a zlib context. +class ScopedZLib { + public: + enum Type { + INFLATE, + DEFLATE, + }; + + explicit ScopedZLib(Type type) : z_(nullptr), type_(type) {} + + void reset(z_stream* z) { + Clear(); + z_ = z; + } + + ~ScopedZLib() { Clear(); } + + private: + void Clear() { + if (!z_) { + return; + } + + if (type_ == DEFLATE) { + deflateEnd(z_); + } else { + inflateEnd(z_); + } + z_ = nullptr; + } + + z_stream* z_; + const Type type_; +}; + +} // anonymous namespace + +// static +QuicString CertCompressor::CompressChain( + const std::vector<QuicString>& certs, + QuicStringPiece client_common_set_hashes, + QuicStringPiece client_cached_cert_hashes, + const CommonCertSets* common_sets) { + const std::vector<CertEntry> entries = MatchCerts( + certs, client_common_set_hashes, client_cached_cert_hashes, common_sets); + DCHECK_EQ(entries.size(), certs.size()); + + size_t uncompressed_size = 0; + for (size_t i = 0; i < entries.size(); i++) { + if (entries[i].type == CertEntry::COMPRESSED) { + uncompressed_size += 4 /* uint32_t length */ + certs[i].size(); + } + } + + size_t compressed_size = 0; + z_stream z; + ScopedZLib scoped_z(ScopedZLib::DEFLATE); + + if (uncompressed_size > 0) { + memset(&z, 0, sizeof(z)); + int rv = deflateInit(&z, Z_DEFAULT_COMPRESSION); + DCHECK_EQ(Z_OK, rv); + if (rv != Z_OK) { + return ""; + } + scoped_z.reset(&z); + + QuicString zlib_dict = ZlibDictForEntries(entries, certs); + + rv = deflateSetDictionary( + &z, reinterpret_cast<const uint8_t*>(&zlib_dict[0]), zlib_dict.size()); + DCHECK_EQ(Z_OK, rv); + if (rv != Z_OK) { + return ""; + } + + compressed_size = deflateBound(&z, uncompressed_size); + } + + const size_t entries_size = CertEntriesSize(entries); + + QuicString result; + result.resize(entries_size + (uncompressed_size > 0 ? 4 : 0) + + compressed_size); + + uint8_t* j = reinterpret_cast<uint8_t*>(&result[0]); + SerializeCertEntries(j, entries); + j += entries_size; + + if (uncompressed_size == 0) { + return result; + } + + uint32_t uncompressed_size_32 = uncompressed_size; + memcpy(j, &uncompressed_size_32, sizeof(uint32_t)); + j += sizeof(uint32_t); + + int rv; + + z.next_out = j; + z.avail_out = compressed_size; + + for (size_t i = 0; i < certs.size(); i++) { + if (entries[i].type != CertEntry::COMPRESSED) { + continue; + } + + uint32_t length32 = certs[i].size(); + z.next_in = reinterpret_cast<uint8_t*>(&length32); + z.avail_in = sizeof(length32); + rv = deflate(&z, Z_NO_FLUSH); + DCHECK_EQ(Z_OK, rv); + DCHECK_EQ(0u, z.avail_in); + if (rv != Z_OK || z.avail_in) { + return ""; + } + + z.next_in = + const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(certs[i].data())); + z.avail_in = certs[i].size(); + rv = deflate(&z, Z_NO_FLUSH); + DCHECK_EQ(Z_OK, rv); + DCHECK_EQ(0u, z.avail_in); + if (rv != Z_OK || z.avail_in) { + return ""; + } + } + + z.avail_in = 0; + rv = deflate(&z, Z_FINISH); + DCHECK_EQ(Z_STREAM_END, rv); + if (rv != Z_STREAM_END) { + return ""; + } + + result.resize(result.size() - z.avail_out); + return result; +} + +// static +bool CertCompressor::DecompressChain( + QuicStringPiece in, + const std::vector<QuicString>& cached_certs, + const CommonCertSets* common_sets, + std::vector<QuicString>* out_certs) { + std::vector<CertEntry> entries; + if (!ParseEntries(&in, cached_certs, common_sets, &entries, out_certs)) { + return false; + } + DCHECK_EQ(entries.size(), out_certs->size()); + + std::unique_ptr<uint8_t[]> uncompressed_data; + QuicStringPiece uncompressed; + + if (!in.empty()) { + if (in.size() < sizeof(uint32_t)) { + return false; + } + + uint32_t uncompressed_size; + memcpy(&uncompressed_size, in.data(), sizeof(uncompressed_size)); + in.remove_prefix(sizeof(uint32_t)); + + if (uncompressed_size > 128 * 1024) { + return false; + } + + uncompressed_data = QuicMakeUnique<uint8_t[]>(uncompressed_size); + z_stream z; + ScopedZLib scoped_z(ScopedZLib::INFLATE); + + memset(&z, 0, sizeof(z)); + z.next_out = uncompressed_data.get(); + z.avail_out = uncompressed_size; + z.next_in = + const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(in.data())); + z.avail_in = in.size(); + + if (Z_OK != inflateInit(&z)) { + return false; + } + scoped_z.reset(&z); + + int rv = inflate(&z, Z_FINISH); + if (rv == Z_NEED_DICT) { + QuicString zlib_dict = ZlibDictForEntries(entries, *out_certs); + const uint8_t* dict = reinterpret_cast<const uint8_t*>(zlib_dict.data()); + if (Z_OK != inflateSetDictionary(&z, dict, zlib_dict.size())) { + return false; + } + rv = inflate(&z, Z_FINISH); + } + + if (Z_STREAM_END != rv || z.avail_out > 0 || z.avail_in > 0) { + return false; + } + + uncompressed = QuicStringPiece( + reinterpret_cast<char*>(uncompressed_data.get()), uncompressed_size); + } + + for (size_t i = 0; i < entries.size(); i++) { + switch (entries[i].type) { + case CertEntry::COMPRESSED: + if (uncompressed.size() < sizeof(uint32_t)) { + return false; + } + uint32_t cert_len; + memcpy(&cert_len, uncompressed.data(), sizeof(cert_len)); + uncompressed.remove_prefix(sizeof(uint32_t)); + if (uncompressed.size() < cert_len) { + return false; + } + (*out_certs)[i] = QuicString(uncompressed.substr(0, cert_len)); + uncompressed.remove_prefix(cert_len); + break; + case CertEntry::CACHED: + case CertEntry::COMMON: + break; + } + } + + if (!uncompressed.empty()) { + return false; + } + + return true; +} + +} // namespace quic
diff --git a/quic/core/crypto/cert_compressor.h b/quic/core/crypto/cert_compressor.h new file mode 100644 index 0000000..624ac71 --- /dev/null +++ b/quic/core/crypto/cert_compressor.h
@@ -0,0 +1,57 @@ +// Copyright (c) 2013 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_CRYPTO_CERT_COMPRESSOR_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_ + +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// CertCompressor provides functions for compressing and decompressing +// certificate chains using three techniquies: +// 1) The peer may provide a list of a 64-bit, FNV-1a hashes of certificates +// that they already have. In the event that one of them is to be +// compressed, it can be replaced with just the hash. +// 2) The peer may provide a number of hashes that represent sets of +// pre-shared certificates (CommonCertSets). If one of those certificates +// is to be compressed, and it's known to the given CommonCertSets, then it +// can be replaced with a set hash and certificate index. +// 3) Otherwise the certificates are compressed with zlib using a pre-shared +// dictionary that consists of the certificates handled with the above +// methods and a small chunk of common substrings. +class QUIC_EXPORT_PRIVATE CertCompressor { + public: + CertCompressor() = delete; + + // CompressChain compresses the certificates in |certs| and returns a + // compressed representation. |common_sets| contains the common certificate + // sets known locally and |client_common_set_hashes| contains the hashes of + // the common sets known to the peer. |client_cached_cert_hashes| contains + // 64-bit, FNV-1a hashes of certificates that the peer already possesses. + static QuicString CompressChain(const std::vector<QuicString>& certs, + QuicStringPiece client_common_set_hashes, + QuicStringPiece client_cached_cert_hashes, + const CommonCertSets* common_sets); + + // DecompressChain decompresses the result of |CompressChain|, given in |in|, + // into a series of certificates that are written to |out_certs|. + // |cached_certs| contains certificates that the peer may have omitted and + // |common_sets| contains the common certificate sets known locally. + static bool DecompressChain(QuicStringPiece in, + const std::vector<QuicString>& cached_certs, + const CommonCertSets* common_sets, + std::vector<QuicString>* out_certs); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_
diff --git a/quic/core/crypto/cert_compressor_test.cc b/quic/core/crypto/cert_compressor_test.cc new file mode 100644 index 0000000..87b330a --- /dev/null +++ b/quic/core/crypto/cert_compressor_test.cc
@@ -0,0 +1,130 @@ +// Copyright (c) 2013 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/crypto/cert_compressor.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" + +namespace quic { +namespace test { + +class CertCompressorTest : public QuicTest {}; + +TEST_F(CertCompressorTest, EmptyChain) { + std::vector<QuicString> chain; + const QuicString compressed = CertCompressor::CompressChain( + chain, QuicStringPiece(), QuicStringPiece(), nullptr); + EXPECT_EQ("00", QuicTextUtils::HexEncode(compressed)); + + std::vector<QuicString> chain2, cached_certs; + ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr, + &chain2)); + EXPECT_EQ(chain.size(), chain2.size()); +} + +TEST_F(CertCompressorTest, Compressed) { + std::vector<QuicString> chain; + chain.push_back("testcert"); + const QuicString compressed = CertCompressor::CompressChain( + chain, QuicStringPiece(), QuicStringPiece(), nullptr); + ASSERT_GE(compressed.size(), 2u); + EXPECT_EQ("0100", QuicTextUtils::HexEncode(compressed.substr(0, 2))); + + std::vector<QuicString> chain2, cached_certs; + ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr, + &chain2)); + EXPECT_EQ(chain.size(), chain2.size()); + EXPECT_EQ(chain[0], chain2[0]); +} + +TEST_F(CertCompressorTest, Common) { + std::vector<QuicString> chain; + chain.push_back("testcert"); + static const uint64_t set_hash = 42; + std::unique_ptr<CommonCertSets> common_sets( + crypto_test_utils::MockCommonCertSets(chain[0], set_hash, 1)); + const QuicString compressed = CertCompressor::CompressChain( + chain, + QuicStringPiece(reinterpret_cast<const char*>(&set_hash), + sizeof(set_hash)), + QuicStringPiece(), common_sets.get()); + EXPECT_EQ( + "03" /* common */ + "2a00000000000000" /* set hash 42 */ + "01000000" /* index 1 */ + "00" /* end of list */, + QuicTextUtils::HexEncode(compressed)); + + std::vector<QuicString> chain2, cached_certs; + ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, + common_sets.get(), &chain2)); + EXPECT_EQ(chain.size(), chain2.size()); + EXPECT_EQ(chain[0], chain2[0]); +} + +TEST_F(CertCompressorTest, Cached) { + std::vector<QuicString> chain; + chain.push_back("testcert"); + uint64_t hash = QuicUtils::FNV1a_64_Hash(chain[0]); + QuicStringPiece hash_bytes(reinterpret_cast<char*>(&hash), sizeof(hash)); + const QuicString compressed = CertCompressor::CompressChain( + chain, QuicStringPiece(), hash_bytes, nullptr); + + EXPECT_EQ("02" /* cached */ + QuicTextUtils::HexEncode(hash_bytes) + + "00" /* end of list */, + QuicTextUtils::HexEncode(compressed)); + + std::vector<QuicString> cached_certs, chain2; + cached_certs.push_back(chain[0]); + ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr, + &chain2)); + EXPECT_EQ(chain.size(), chain2.size()); + EXPECT_EQ(chain[0], chain2[0]); +} + +TEST_F(CertCompressorTest, BadInputs) { + std::vector<QuicString> cached_certs, chain; + + EXPECT_FALSE(CertCompressor::DecompressChain( + QuicTextUtils::HexEncode("04") /* bad entry type */, cached_certs, + nullptr, &chain)); + + EXPECT_FALSE(CertCompressor::DecompressChain( + QuicTextUtils::HexEncode("01") /* no terminator */, cached_certs, nullptr, + &chain)); + + EXPECT_FALSE(CertCompressor::DecompressChain( + QuicTextUtils::HexEncode("0200") /* hash truncated */, cached_certs, + nullptr, &chain)); + + EXPECT_FALSE(CertCompressor::DecompressChain( + QuicTextUtils::HexEncode("0300") /* hash and index truncated */, + cached_certs, nullptr, &chain)); + + /* without a CommonCertSets */ + EXPECT_FALSE(CertCompressor::DecompressChain( + QuicTextUtils::HexEncode("03" + "0000000000000000" + "00000000"), + cached_certs, nullptr, &chain)); + + std::unique_ptr<CommonCertSets> common_sets( + crypto_test_utils::MockCommonCertSets("foo", 42, 1)); + + /* incorrect hash and index */ + EXPECT_FALSE(CertCompressor::DecompressChain( + QuicTextUtils::HexEncode("03" + "a200000000000000" + "00000000"), + cached_certs, nullptr, &chain)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter.cc b/quic/core/crypto/chacha20_poly1305_decrypter.cc new file mode 100644 index 0000000..83fd15e --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_decrypter.cc
@@ -0,0 +1,35 @@ +// Copyright 2014 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/crypto/chacha20_poly1305_decrypter.h" + +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "third_party/boringssl/src/include/openssl/tls1.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 32; +const size_t kNonceSize = 12; + +} // namespace + +ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter() + : AeadBaseDecrypter(EVP_aead_chacha20_poly1305, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ false) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {} + +uint32_t ChaCha20Poly1305Decrypter::cipher_id() const { + return TLS1_CK_CHACHA20_POLY1305_SHA256; +} + +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter.h b/quic/core/crypto/chacha20_poly1305_decrypter.h new file mode 100644 index 0000000..9f65a49 --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_decrypter.h
@@ -0,0 +1,40 @@ +// Copyright 2014 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_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A ChaCha20Poly1305Decrypter is a QuicDecrypter that implements the +// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539, except that +// it truncates the Poly1305 authenticator to 12 bytes. Create an instance +// by calling QuicDecrypter::Create(kCC20). +// +// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the +// nonce is four bytes. +class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Decrypter : public AeadBaseDecrypter { + public: + enum { + kAuthTagSize = 12, + }; + + ChaCha20Poly1305Decrypter(); + ChaCha20Poly1305Decrypter(const ChaCha20Poly1305Decrypter&) = delete; + ChaCha20Poly1305Decrypter& operator=(const ChaCha20Poly1305Decrypter&) = + delete; + ~ChaCha20Poly1305Decrypter() override; + + uint32_t cipher_id() const override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter_test.cc b/quic/core/crypto/chacha20_poly1305_decrypter_test.cc new file mode 100644 index 0000000..5f5ae01 --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
@@ -0,0 +1,176 @@ +// Copyright 2014 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/crypto/chacha20_poly1305_decrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The test vectors come from RFC 7539 Section 2.8.2. + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + // Input: + const char* key; + const char* iv; + const char* fixed; + const char* aad; + const char* ct; + + // Expected output: + const char* pt; // An empty string "" means decryption succeeded and + // the plaintext is zero-length. nullptr means decryption + // failed. +}; + +const TestVector test_vectors[] = { + {"808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4041424344454647", + + "07000000", + + "50515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902ecb", // "d0600691" truncated + + "4c616469657320616e642047656e746c" + "656d656e206f662074686520636c6173" + "73206f66202739393a20496620492063" + "6f756c64206f6666657220796f75206f" + "6e6c79206f6e652074697020666f7220" + "746865206675747572652c2073756e73" + "637265656e20776f756c642062652069" + "742e"}, + // Modify the ciphertext (Poly1305 authenticator). + {"808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4041424344454647", + + "07000000", + + "50515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902ecc", // "d0600691" truncated + + nullptr}, + // Modify the associated data. + {"808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4041424344454647", + + "07000000", + + "60515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902ecb", // "d0600691" truncated + + nullptr}, + {nullptr}}; + +} // namespace + +namespace quic { +namespace test { + +// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the plaintext. +QuicData* DecryptWithNonce(ChaCha20Poly1305Decrypter* decrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece ciphertext) { + uint64_t packet_number; + QuicStringPiece nonce_prefix(nonce.data(), + nonce.size() - sizeof(packet_number)); + decrypter->SetNoncePrefix(nonce_prefix); + memcpy(&packet_number, nonce.data() + nonce_prefix.size(), + sizeof(packet_number)); + std::unique_ptr<char[]> output(new char[ciphertext.length()]); + size_t output_length = 0; + const bool success = decrypter->DecryptPacket( + packet_number, associated_data, ciphertext, output.get(), &output_length, + ciphertext.length()); + if (!success) { + return nullptr; + } + return new QuicData(output.release(), output_length, true); +} + +class ChaCha20Poly1305DecrypterTest : public QuicTest {}; + +TEST_F(ChaCha20Poly1305DecrypterTest, Decrypt) { + for (size_t i = 0; test_vectors[i].key != nullptr; i++) { + // If not present then decryption is expected to fail. + bool has_pt = test_vectors[i].pt; + + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[i].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[i].iv); + QuicString fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[i].aad); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[i].ct); + QuicString pt; + if (has_pt) { + pt = QuicTextUtils::HexDecode(test_vectors[i].pt); + } + + ChaCha20Poly1305Decrypter decrypter; + ASSERT_TRUE(decrypter.SetKey(key)); + std::unique_ptr<QuicData> decrypted(DecryptWithNonce( + &decrypter, fixed + iv, + // This deliberately tests that the decrypter can handle an AAD that + // is set to nullptr, as opposed to a zero-length, non-nullptr pointer. + QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()), + ct)); + if (!decrypted.get()) { + EXPECT_FALSE(has_pt); + continue; + } + EXPECT_TRUE(has_pt); + + EXPECT_EQ(12u, ct.size() - decrypted->length()); + ASSERT_EQ(pt.length(), decrypted->length()); + test::CompareCharArraysWithHexError("plaintext", decrypted->data(), + pt.length(), pt.data(), pt.length()); + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_encrypter.cc b/quic/core/crypto/chacha20_poly1305_encrypter.cc new file mode 100644 index 0000000..eb51d83 --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_encrypter.cc
@@ -0,0 +1,30 @@ +// Copyright 2014 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/crypto/chacha20_poly1305_encrypter.h" + +#include "third_party/boringssl/src/include/openssl/evp.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 32; +const size_t kNonceSize = 12; + +} // namespace + +ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter() + : AeadBaseEncrypter(EVP_aead_chacha20_poly1305, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ false) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {} + +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_encrypter.h b/quic/core/crypto/chacha20_poly1305_encrypter.h new file mode 100644 index 0000000..ca8aca8 --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_encrypter.h
@@ -0,0 +1,36 @@ +// Copyright 2014 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_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the +// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539, except that +// it truncates the Poly1305 authenticator to 12 bytes. Create an instance +// by calling QuicEncrypter::Create(kCC20). +// +// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the +// nonce is four bytes. +class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Encrypter : public AeadBaseEncrypter { + public: + enum { + kAuthTagSize = 12, + }; + + ChaCha20Poly1305Encrypter(); + ChaCha20Poly1305Encrypter(const ChaCha20Poly1305Encrypter&) = delete; + ChaCha20Poly1305Encrypter& operator=(const ChaCha20Poly1305Encrypter&) = + delete; + ~ChaCha20Poly1305Encrypter() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
diff --git a/quic/core/crypto/chacha20_poly1305_encrypter_test.cc b/quic/core/crypto/chacha20_poly1305_encrypter_test.cc new file mode 100644 index 0000000..d8c27bf --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
@@ -0,0 +1,157 @@ +// Copyright 2014 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/crypto/chacha20_poly1305_encrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The test vectors come from RFC 7539 Section 2.8.2. + +// Each test vector consists of five strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + const char* key; + const char* pt; + const char* iv; + const char* fixed; + const char* aad; + const char* ct; +}; + +const TestVector test_vectors[] = { + { + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4c616469657320616e642047656e746c" + "656d656e206f662074686520636c6173" + "73206f66202739393a20496620492063" + "6f756c64206f6666657220796f75206f" + "6e6c79206f6e652074697020666f7220" + "746865206675747572652c2073756e73" + "637265656e20776f756c642062652069" + "742e", + + "4041424344454647", + + "07000000", + + "50515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902ecb", // "d0600691" truncated + }, + {nullptr}}; + +} // namespace + +namespace quic { +namespace test { + +// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the ciphertext. +QuicData* EncryptWithNonce(ChaCha20Poly1305Encrypter* encrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece plaintext) { + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!encrypter->Encrypt(nonce, associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return nullptr; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +class ChaCha20Poly1305EncrypterTest : public QuicTest {}; + +TEST_F(ChaCha20Poly1305EncrypterTest, EncryptThenDecrypt) { + ChaCha20Poly1305Encrypter encrypter; + ChaCha20Poly1305Decrypter decrypter; + + QuicString key = QuicTextUtils::HexDecode(test_vectors[0].key); + ASSERT_TRUE(encrypter.SetKey(key)); + ASSERT_TRUE(decrypter.SetKey(key)); + ASSERT_TRUE(encrypter.SetNoncePrefix("abcd")); + ASSERT_TRUE(decrypter.SetNoncePrefix("abcd")); + + uint64_t packet_number = UINT64_C(0x123456789ABC); + QuicString associated_data = "associated_data"; + QuicString plaintext = "plaintext"; + char encrypted[1024]; + size_t len; + ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext, + encrypted, &len, + QUIC_ARRAYSIZE(encrypted))); + QuicStringPiece ciphertext(encrypted, len); + char decrypted[1024]; + ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data, + ciphertext, decrypted, &len, + QUIC_ARRAYSIZE(decrypted))); +} + +TEST_F(ChaCha20Poly1305EncrypterTest, Encrypt) { + for (size_t i = 0; test_vectors[i].key != nullptr; i++) { + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[i].key); + QuicString pt = QuicTextUtils::HexDecode(test_vectors[i].pt); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[i].iv); + QuicString fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[i].aad); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[i].ct); + + ChaCha20Poly1305Encrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(key)); + std::unique_ptr<QuicData> encrypted(EncryptWithNonce( + &encrypter, fixed + iv, + // This deliberately tests that the encrypter can handle an AAD that + // is set to nullptr, as opposed to a zero-length, non-nullptr pointer. + QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()), + pt)); + ASSERT_TRUE(encrypted.get()); + EXPECT_EQ(12u, ct.size() - pt.size()); + EXPECT_EQ(12u, encrypted->length() - pt.size()); + + test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), + encrypted->length(), ct.data(), + ct.length()); + } +} + +TEST_F(ChaCha20Poly1305EncrypterTest, GetMaxPlaintextSize) { + ChaCha20Poly1305Encrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22)); +} + +TEST_F(ChaCha20Poly1305EncrypterTest, GetCiphertextSize) { + ChaCha20Poly1305Encrypter encrypter; + EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(112u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(22u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc b/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc new file mode 100644 index 0000000..4c10a53 --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
@@ -0,0 +1,37 @@ +// Copyright 2017 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/crypto/chacha20_poly1305_tls_decrypter.h" + +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "third_party/boringssl/src/include/openssl/tls1.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 32; +const size_t kNonceSize = 12; + +} // namespace + +ChaCha20Poly1305TlsDecrypter::ChaCha20Poly1305TlsDecrypter() + : AeadBaseDecrypter(EVP_aead_chacha20_poly1305, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ true) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +ChaCha20Poly1305TlsDecrypter::~ChaCha20Poly1305TlsDecrypter() {} + +uint32_t ChaCha20Poly1305TlsDecrypter::cipher_id() const { + return TLS1_CK_CHACHA20_POLY1305_SHA256; +} + +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter.h b/quic/core/crypto/chacha20_poly1305_tls_decrypter.h new file mode 100644 index 0000000..37c26a9 --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
@@ -0,0 +1,39 @@ +// Copyright 2017 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_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A ChaCha20Poly1305TlsDecrypter is a QuicDecrypter that implements the +// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539 for use in IETF QUIC. +// +// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 bytes IV +// that is XOR'd with the packet number to compute the nonce. +class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsDecrypter + : public AeadBaseDecrypter { + public: + enum { + kAuthTagSize = 16, + }; + + ChaCha20Poly1305TlsDecrypter(); + ChaCha20Poly1305TlsDecrypter(const ChaCha20Poly1305TlsDecrypter&) = delete; + ChaCha20Poly1305TlsDecrypter& operator=(const ChaCha20Poly1305TlsDecrypter&) = + delete; + ~ChaCha20Poly1305TlsDecrypter() override; + + uint32_t cipher_id() const override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc b/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc new file mode 100644 index 0000000..dd74539 --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
@@ -0,0 +1,171 @@ +// Copyright 2017 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/crypto/chacha20_poly1305_tls_decrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The test vectors come from RFC 7539 Section 2.8.2. + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + // Input: + const char* key; + const char* iv; + const char* fixed; + const char* aad; + const char* ct; + + // Expected output: + const char* pt; // An empty string "" means decryption succeeded and + // the plaintext is zero-length. nullptr means decryption + // failed. +}; + +const TestVector test_vectors[] = { + {"808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4041424344454647", + + "07000000", + + "50515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902ecbd0600691", + + "4c616469657320616e642047656e746c" + "656d656e206f662074686520636c6173" + "73206f66202739393a20496620492063" + "6f756c64206f6666657220796f75206f" + "6e6c79206f6e652074697020666f7220" + "746865206675747572652c2073756e73" + "637265656e20776f756c642062652069" + "742e"}, + // Modify the ciphertext (Poly1305 authenticator). + {"808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4041424344454647", + + "07000000", + + "50515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902eccd0600691", + + nullptr}, + // Modify the associated data. + {"808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4041424344454647", + + "07000000", + + "60515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902ecbd0600691", + + nullptr}, + {nullptr}}; + +} // namespace + +namespace quic { +namespace test { + +// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the plaintext. +QuicData* DecryptWithNonce(ChaCha20Poly1305TlsDecrypter* decrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece ciphertext) { + decrypter->SetIV(nonce); + std::unique_ptr<char[]> output(new char[ciphertext.length()]); + size_t output_length = 0; + const bool success = + decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(), + &output_length, ciphertext.length()); + if (!success) { + return nullptr; + } + return new QuicData(output.release(), output_length, true); +} + +class ChaCha20Poly1305TlsDecrypterTest : public QuicTest {}; + +TEST_F(ChaCha20Poly1305TlsDecrypterTest, Decrypt) { + for (size_t i = 0; test_vectors[i].key != nullptr; i++) { + // If not present then decryption is expected to fail. + bool has_pt = test_vectors[i].pt; + + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[i].key); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[i].iv); + QuicString fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[i].aad); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[i].ct); + QuicString pt; + if (has_pt) { + pt = QuicTextUtils::HexDecode(test_vectors[i].pt); + } + + ChaCha20Poly1305TlsDecrypter decrypter; + ASSERT_TRUE(decrypter.SetKey(key)); + std::unique_ptr<QuicData> decrypted(DecryptWithNonce( + &decrypter, fixed + iv, + // This deliberately tests that the decrypter can handle an AAD that + // is set to nullptr, as opposed to a zero-length, non-nullptr pointer. + QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()), + ct)); + if (!decrypted.get()) { + EXPECT_FALSE(has_pt); + continue; + } + EXPECT_TRUE(has_pt); + + EXPECT_EQ(16u, ct.size() - decrypted->length()); + ASSERT_EQ(pt.length(), decrypted->length()); + test::CompareCharArraysWithHexError("plaintext", decrypted->data(), + pt.length(), pt.data(), pt.length()); + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc b/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc new file mode 100644 index 0000000..c023ecb --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
@@ -0,0 +1,30 @@ +// Copyright 2017 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/crypto/chacha20_poly1305_tls_encrypter.h" + +#include "third_party/boringssl/src/include/openssl/evp.h" + +namespace quic { + +namespace { + +const size_t kKeySize = 32; +const size_t kNonceSize = 12; + +} // namespace + +ChaCha20Poly1305TlsEncrypter::ChaCha20Poly1305TlsEncrypter() + : AeadBaseEncrypter(EVP_aead_chacha20_poly1305, + kKeySize, + kAuthTagSize, + kNonceSize, + /* use_ietf_nonce_construction */ true) { + static_assert(kKeySize <= kMaxKeySize, "key size too big"); + static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big"); +} + +ChaCha20Poly1305TlsEncrypter::~ChaCha20Poly1305TlsEncrypter() {} + +} // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_tls_encrypter.h b/quic/core/crypto/chacha20_poly1305_tls_encrypter.h new file mode 100644 index 0000000..f80c8db --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
@@ -0,0 +1,35 @@ +// Copyright 2017 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_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the +// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539 for use in IETF QUIC. +// +// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV +// that is XOR'd with the packet number to compute the nonce. +class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsEncrypter + : public AeadBaseEncrypter { + public: + enum { + kAuthTagSize = 16, + }; + + ChaCha20Poly1305TlsEncrypter(); + ChaCha20Poly1305TlsEncrypter(const ChaCha20Poly1305TlsEncrypter&) = delete; + ChaCha20Poly1305TlsEncrypter& operator=(const ChaCha20Poly1305TlsEncrypter&) = + delete; + ~ChaCha20Poly1305TlsEncrypter() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
diff --git a/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc b/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc new file mode 100644 index 0000000..905472b --- /dev/null +++ b/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
@@ -0,0 +1,156 @@ +// Copyright 2017 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/crypto/chacha20_poly1305_tls_encrypter.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace { + +// The test vectors come from RFC 7539 Section 2.8.2. + +// Each test vector consists of five strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a nullptr |key| +// marks the end of an array of test vectors. +struct TestVector { + const char* key; + const char* pt; + const char* iv; + const char* fixed; + const char* aad; + const char* ct; +}; + +const TestVector test_vectors[] = {{ + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f", + + "4c616469657320616e642047656e746c" + "656d656e206f662074686520636c6173" + "73206f66202739393a20496620492063" + "6f756c64206f6666657220796f75206f" + "6e6c79206f6e652074697020666f7220" + "746865206675747572652c2073756e73" + "637265656e20776f756c642062652069" + "742e", + + "4041424344454647", + + "07000000", + + "50515253c0c1c2c3c4c5c6c7", + + "d31a8d34648e60db7b86afbc53ef7ec2" + "a4aded51296e08fea9e2b5a736ee62d6" + "3dbea45e8ca9671282fafb69da92728b" + "1a71de0a9e060b2905d6a5b67ecd3b36" + "92ddbd7f2d778b8c9803aee328091b58" + "fab324e4fad675945585808b4831d7bc" + "3ff4def08e4b7a9de576d26586cec64b" + "6116" + "1ae10b594f09e26a7e902ecbd0600691", + }, + {nullptr}}; + +} // namespace + +namespace quic { +namespace test { + +// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the ciphertext. +QuicData* EncryptWithNonce(ChaCha20Poly1305TlsEncrypter* encrypter, + QuicStringPiece nonce, + QuicStringPiece associated_data, + QuicStringPiece plaintext) { + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!encrypter->Encrypt(nonce, associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return nullptr; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +class ChaCha20Poly1305TlsEncrypterTest : public QuicTest {}; + +TEST_F(ChaCha20Poly1305TlsEncrypterTest, EncryptThenDecrypt) { + ChaCha20Poly1305TlsEncrypter encrypter; + ChaCha20Poly1305TlsDecrypter decrypter; + + QuicString key = QuicTextUtils::HexDecode(test_vectors[0].key); + ASSERT_TRUE(encrypter.SetKey(key)); + ASSERT_TRUE(decrypter.SetKey(key)); + ASSERT_TRUE(encrypter.SetIV("abcdefghijkl")); + ASSERT_TRUE(decrypter.SetIV("abcdefghijkl")); + + uint64_t packet_number = UINT64_C(0x123456789ABC); + QuicString associated_data = "associated_data"; + QuicString plaintext = "plaintext"; + char encrypted[1024]; + size_t len; + ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext, + encrypted, &len, + QUIC_ARRAYSIZE(encrypted))); + QuicStringPiece ciphertext(encrypted, len); + char decrypted[1024]; + ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data, + ciphertext, decrypted, &len, + QUIC_ARRAYSIZE(decrypted))); +} + +TEST_F(ChaCha20Poly1305TlsEncrypterTest, Encrypt) { + for (size_t i = 0; test_vectors[i].key != nullptr; i++) { + // Decode the test vector. + QuicString key = QuicTextUtils::HexDecode(test_vectors[i].key); + QuicString pt = QuicTextUtils::HexDecode(test_vectors[i].pt); + QuicString iv = QuicTextUtils::HexDecode(test_vectors[i].iv); + QuicString fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed); + QuicString aad = QuicTextUtils::HexDecode(test_vectors[i].aad); + QuicString ct = QuicTextUtils::HexDecode(test_vectors[i].ct); + + ChaCha20Poly1305TlsEncrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(key)); + std::unique_ptr<QuicData> encrypted(EncryptWithNonce( + &encrypter, fixed + iv, + // This deliberately tests that the encrypter can handle an AAD that + // is set to nullptr, as opposed to a zero-length, non-nullptr pointer. + QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()), + pt)); + ASSERT_TRUE(encrypted.get()); + EXPECT_EQ(16u, ct.size() - pt.size()); + EXPECT_EQ(16u, encrypted->length() - pt.size()); + + test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), + encrypted->length(), ct.data(), + ct.length()); + } +} + +TEST_F(ChaCha20Poly1305TlsEncrypterTest, GetMaxPlaintextSize) { + ChaCha20Poly1305TlsEncrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26)); +} + +TEST_F(ChaCha20Poly1305TlsEncrypterTest, GetCiphertextSize) { + ChaCha20Poly1305TlsEncrypter encrypter; + EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(116u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(26u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/channel_id.cc b/quic/core/crypto/channel_id.cc new file mode 100644 index 0000000..0c4a3e9 --- /dev/null +++ b/quic/core/crypto/channel_id.cc
@@ -0,0 +1,89 @@ +// Copyright 2013 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/crypto/channel_id.h" + +#include <cstdint> + +#include "third_party/boringssl/src/include/openssl/bn.h" +#include "third_party/boringssl/src/include/openssl/ec.h" +#include "third_party/boringssl/src/include/openssl/ecdsa.h" +#include "third_party/boringssl/src/include/openssl/nid.h" +#include "third_party/boringssl/src/include/openssl/sha.h" + +namespace quic { + +// static +const char ChannelIDVerifier::kContextStr[] = "QUIC ChannelID"; +// static +const char ChannelIDVerifier::kClientToServerStr[] = "client -> server"; + +// static +bool ChannelIDVerifier::Verify(QuicStringPiece key, + QuicStringPiece signed_data, + QuicStringPiece signature) { + return VerifyRaw(key, signed_data, signature, true); +} + +// static +bool ChannelIDVerifier::VerifyRaw(QuicStringPiece key, + QuicStringPiece signed_data, + QuicStringPiece signature, + bool is_channel_id_signature) { + if (key.size() != 32 * 2 || signature.size() != 32 * 2) { + return false; + } + + bssl::UniquePtr<EC_GROUP> p256( + EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + if (p256.get() == nullptr) { + return false; + } + + bssl::UniquePtr<BIGNUM> x(BN_new()), y(BN_new()), r(BN_new()), s(BN_new()); + + ECDSA_SIG sig; + sig.r = r.get(); + sig.s = s.get(); + + const uint8_t* key_bytes = reinterpret_cast<const uint8_t*>(key.data()); + const uint8_t* signature_bytes = + reinterpret_cast<const uint8_t*>(signature.data()); + + if (BN_bin2bn(key_bytes + 0, 32, x.get()) == nullptr || + BN_bin2bn(key_bytes + 32, 32, y.get()) == nullptr || + BN_bin2bn(signature_bytes + 0, 32, sig.r) == nullptr || + BN_bin2bn(signature_bytes + 32, 32, sig.s) == nullptr) { + return false; + } + + bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get())); + if (point.get() == nullptr || + !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(), + y.get(), nullptr)) { + return false; + } + + bssl::UniquePtr<EC_KEY> ecdsa_key(EC_KEY_new()); + if (ecdsa_key.get() == nullptr || + !EC_KEY_set_group(ecdsa_key.get(), p256.get()) || + !EC_KEY_set_public_key(ecdsa_key.get(), point.get())) { + return false; + } + + SHA256_CTX sha256; + SHA256_Init(&sha256); + if (is_channel_id_signature) { + SHA256_Update(&sha256, kContextStr, strlen(kContextStr) + 1); + SHA256_Update(&sha256, kClientToServerStr, strlen(kClientToServerStr) + 1); + } + SHA256_Update(&sha256, signed_data.data(), signed_data.size()); + + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256_Final(digest, &sha256); + + return ECDSA_do_verify(digest, sizeof(digest), &sig, ecdsa_key.get()) == 1; +} + +} // namespace quic
diff --git a/quic/core/crypto/channel_id.h b/quic/core/crypto/channel_id.h new file mode 100644 index 0000000..a6c8de5 --- /dev/null +++ b/quic/core/crypto/channel_id.h
@@ -0,0 +1,98 @@ +// Copyright 2013 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_CRYPTO_CHANNEL_ID_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_ + +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// ChannelIDKey is an interface that supports signing with and serializing a +// ChannelID key. +class QUIC_EXPORT_PRIVATE ChannelIDKey { + public: + virtual ~ChannelIDKey() {} + + // Sign signs |signed_data| using the ChannelID private key and puts the + // signature into |out_signature|. It returns true on success. + virtual bool Sign(QuicStringPiece signed_data, + QuicString* out_signature) const = 0; + + // SerializeKey returns the serialized ChannelID public key. + virtual QuicString SerializeKey() const = 0; +}; + +// ChannelIDSourceCallback provides a generic mechanism for a ChannelIDSource +// to call back after an asynchronous GetChannelIDKey operation. +class ChannelIDSourceCallback { + public: + virtual ~ChannelIDSourceCallback() {} + + // Run is called on the original thread to mark the completion of an + // asynchonous GetChannelIDKey operation. If |*channel_id_key| is not nullptr + // then the channel ID lookup is successful. |Run| may take ownership of + // |*channel_id_key| by calling |release| on it. + virtual void Run(std::unique_ptr<ChannelIDKey>* channel_id_key) = 0; +}; + +// ChannelIDSource is an abstract interface by which a QUIC client can obtain +// a ChannelIDKey for a given hostname. +class QUIC_EXPORT_PRIVATE ChannelIDSource { + public: + virtual ~ChannelIDSource() {} + + // GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it + // returns QUIC_SUCCESS and stores the ChannelIDKey in |*channel_id_key|, + // which the caller takes ownership of. On failure, it returns QUIC_FAILURE. + // + // This function may also return QUIC_PENDING, in which case the + // ChannelIDSource will call back, on the original thread, via |callback| + // when complete. In this case, the ChannelIDSource will take ownership of + // |callback|. + virtual QuicAsyncStatus GetChannelIDKey( + const QuicString& hostname, + std::unique_ptr<ChannelIDKey>* channel_id_key, + ChannelIDSourceCallback* callback) = 0; +}; + +// ChannelIDVerifier verifies ChannelID signatures. +class QUIC_EXPORT_PRIVATE ChannelIDVerifier { + public: + ChannelIDVerifier() = delete; + + // kContextStr is prepended to the data to be signed in order to ensure that + // a ChannelID signature cannot be used in a different context. (The + // terminating NUL byte is inclued.) + static const char kContextStr[]; + // kClientToServerStr follows kContextStr to specify that the ChannelID is + // being used in the client to server direction. (The terminating NUL byte is + // included.) + static const char kClientToServerStr[]; + + // Verify returns true iff |signature| is a valid signature of |signed_data| + // by |key|. + static bool Verify(QuicStringPiece key, + QuicStringPiece signed_data, + QuicStringPiece signature); + + // FOR TESTING ONLY: VerifyRaw returns true iff |signature| is a valid + // signature of |signed_data| by |key|. |is_channel_id_signature| indicates + // whether |signature| is a ChannelID signature (with kContextStr prepended + // to the data to be signed). + static bool VerifyRaw(QuicStringPiece key, + QuicStringPiece signed_data, + QuicStringPiece signature, + bool is_channel_id_signature); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_
diff --git a/quic/core/crypto/channel_id_test.cc b/quic/core/crypto/channel_id_test.cc new file mode 100644 index 0000000..3e03580 --- /dev/null +++ b/quic/core/crypto/channel_id_test.cc
@@ -0,0 +1,321 @@ +// Copyright 2013 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/crypto/channel_id.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" + +namespace quic { +namespace test { + +namespace { + +// The following ECDSA signature verification test vectors for P-256,SHA-256 +// come from the SigVer.rsp file in +// http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip +// downloaded on 2013-06-11. +struct TestVector { + // Input: + const char* msg; + const char* qx; + const char* qy; + const char* r; + const char* s; + + // Expected output: + bool result; // true means "P", false means "F" +}; + +const TestVector test_vector[] = { + { + "e4796db5f785f207aa30d311693b3702821dff1168fd2e04c0836825aefd850d" + "9aa60326d88cde1a23c7745351392ca2288d632c264f197d05cd424a30336c19" + "fd09bb229654f0222fcb881a4b35c290a093ac159ce13409111ff0358411133c" + "24f5b8e2090d6db6558afc36f06ca1f6ef779785adba68db27a409859fc4c4a0", + "87f8f2b218f49845f6f10eec3877136269f5c1a54736dbdf69f89940cad41555", + "e15f369036f49842fac7a86c8a2b0557609776814448b8f5e84aa9f4395205e9", + "d19ff48b324915576416097d2544f7cbdf8768b1454ad20e0baac50e211f23b0", + "a3e81e59311cdfff2d4784949f7a2cb50ba6c3a91fa54710568e61aca3e847c6", + false // F (3 - S changed) + }, + { + "069a6e6b93dfee6df6ef6997cd80dd2182c36653cef10c655d524585655462d6" + "83877f95ecc6d6c81623d8fac4e900ed0019964094e7de91f1481989ae187300" + "4565789cbf5dc56c62aedc63f62f3b894c9c6f7788c8ecaadc9bd0e81ad91b2b" + "3569ea12260e93924fdddd3972af5273198f5efda0746219475017557616170e", + "5cf02a00d205bdfee2016f7421807fc38ae69e6b7ccd064ee689fc1a94a9f7d2", + "ec530ce3cc5c9d1af463f264d685afe2b4db4b5828d7e61b748930f3ce622a85", + "dc23d130c6117fb5751201455e99f36f59aba1a6a21cf2d0e7481a97451d6693", + "d6ce7708c18dbf35d4f8aa7240922dc6823f2e7058cbc1484fcad1599db5018c", + false // F (2 - R changed) + }, + { + "df04a346cf4d0e331a6db78cca2d456d31b0a000aa51441defdb97bbeb20b94d" + "8d746429a393ba88840d661615e07def615a342abedfa4ce912e562af7149598" + "96858af817317a840dcff85a057bb91a3c2bf90105500362754a6dd321cdd861" + "28cfc5f04667b57aa78c112411e42da304f1012d48cd6a7052d7de44ebcc01de", + "2ddfd145767883ffbb0ac003ab4a44346d08fa2570b3120dcce94562422244cb", + "5f70c7d11ac2b7a435ccfbbae02c3df1ea6b532cc0e9db74f93fffca7c6f9a64", + "9913111cff6f20c5bf453a99cd2c2019a4e749a49724a08774d14e4c113edda8", + "9467cd4cd21ecb56b0cab0a9a453b43386845459127a952421f5c6382866c5cc", + false // F (4 - Q changed) + }, + { + "e1130af6a38ccb412a9c8d13e15dbfc9e69a16385af3c3f1e5da954fd5e7c45f" + "d75e2b8c36699228e92840c0562fbf3772f07e17f1add56588dd45f7450e1217" + "ad239922dd9c32695dc71ff2424ca0dec1321aa47064a044b7fe3c2b97d03ce4" + "70a592304c5ef21eed9f93da56bb232d1eeb0035f9bf0dfafdcc4606272b20a3", + "e424dc61d4bb3cb7ef4344a7f8957a0c5134e16f7a67c074f82e6e12f49abf3c", + "970eed7aa2bc48651545949de1dddaf0127e5965ac85d1243d6f60e7dfaee927", + "bf96b99aa49c705c910be33142017c642ff540c76349b9dab72f981fd9347f4f", + "17c55095819089c2e03b9cd415abdf12444e323075d98f31920b9e0f57ec871c", + true // P (0 ) + }, + { + "73c5f6a67456ae48209b5f85d1e7de7758bf235300c6ae2bdceb1dcb27a7730f" + "b68c950b7fcada0ecc4661d3578230f225a875e69aaa17f1e71c6be5c831f226" + "63bac63d0c7a9635edb0043ff8c6f26470f02a7bc56556f1437f06dfa27b487a" + "6c4290d8bad38d4879b334e341ba092dde4e4ae694a9c09302e2dbf443581c08", + "e0fc6a6f50e1c57475673ee54e3a57f9a49f3328e743bf52f335e3eeaa3d2864", + "7f59d689c91e463607d9194d99faf316e25432870816dde63f5d4b373f12f22a", + "1d75830cd36f4c9aa181b2c4221e87f176b7f05b7c87824e82e396c88315c407", + "cb2acb01dac96efc53a32d4a0d85d0c2e48955214783ecf50a4f0414a319c05a", + true // P (0 ) + }, + { + "666036d9b4a2426ed6585a4e0fd931a8761451d29ab04bd7dc6d0c5b9e38e6c2" + "b263ff6cb837bd04399de3d757c6c7005f6d7a987063cf6d7e8cb38a4bf0d74a" + "282572bd01d0f41e3fd066e3021575f0fa04f27b700d5b7ddddf50965993c3f9" + "c7118ed78888da7cb221849b3260592b8e632d7c51e935a0ceae15207bedd548", + "a849bef575cac3c6920fbce675c3b787136209f855de19ffe2e8d29b31a5ad86", + "bf5fe4f7858f9b805bd8dcc05ad5e7fb889de2f822f3d8b41694e6c55c16b471", + "25acc3aa9d9e84c7abf08f73fa4195acc506491d6fc37cb9074528a7db87b9d6", + "9b21d5b5259ed3f2ef07dfec6cc90d3a37855d1ce122a85ba6a333f307d31537", + false // F (2 - R changed) + }, + { + "7e80436bce57339ce8da1b5660149a20240b146d108deef3ec5da4ae256f8f89" + "4edcbbc57b34ce37089c0daa17f0c46cd82b5a1599314fd79d2fd2f446bd5a25" + "b8e32fcf05b76d644573a6df4ad1dfea707b479d97237a346f1ec632ea5660ef" + "b57e8717a8628d7f82af50a4e84b11f21bdff6839196a880ae20b2a0918d58cd", + "3dfb6f40f2471b29b77fdccba72d37c21bba019efa40c1c8f91ec405d7dcc5df", + "f22f953f1e395a52ead7f3ae3fc47451b438117b1e04d613bc8555b7d6e6d1bb", + "548886278e5ec26bed811dbb72db1e154b6f17be70deb1b210107decb1ec2a5a", + "e93bfebd2f14f3d827ca32b464be6e69187f5edbd52def4f96599c37d58eee75", + false // F (4 - Q changed) + }, + { + "1669bfb657fdc62c3ddd63269787fc1c969f1850fb04c933dda063ef74a56ce1" + "3e3a649700820f0061efabf849a85d474326c8a541d99830eea8131eaea584f2" + "2d88c353965dabcdc4bf6b55949fd529507dfb803ab6b480cd73ca0ba00ca19c" + "438849e2cea262a1c57d8f81cd257fb58e19dec7904da97d8386e87b84948169", + "69b7667056e1e11d6caf6e45643f8b21e7a4bebda463c7fdbc13bc98efbd0214", + "d3f9b12eb46c7c6fda0da3fc85bc1fd831557f9abc902a3be3cb3e8be7d1aa2f", + "288f7a1cd391842cce21f00e6f15471c04dc182fe4b14d92dc18910879799790", + "247b3c4e89a3bcadfea73c7bfd361def43715fa382b8c3edf4ae15d6e55e9979", + false // F (1 - Message changed) + }, + { + "3fe60dd9ad6caccf5a6f583b3ae65953563446c4510b70da115ffaa0ba04c076" + "115c7043ab8733403cd69c7d14c212c655c07b43a7c71b9a4cffe22c2684788e" + "c6870dc2013f269172c822256f9e7cc674791bf2d8486c0f5684283e1649576e" + "fc982ede17c7b74b214754d70402fb4bb45ad086cf2cf76b3d63f7fce39ac970", + "bf02cbcf6d8cc26e91766d8af0b164fc5968535e84c158eb3bc4e2d79c3cc682", + "069ba6cb06b49d60812066afa16ecf7b51352f2c03bd93ec220822b1f3dfba03", + "f5acb06c59c2b4927fb852faa07faf4b1852bbb5d06840935e849c4d293d1bad", + "049dab79c89cc02f1484c437f523e080a75f134917fda752f2d5ca397addfe5d", + false // F (3 - S changed) + }, + { + "983a71b9994d95e876d84d28946a041f8f0a3f544cfcc055496580f1dfd4e312" + "a2ad418fe69dbc61db230cc0c0ed97e360abab7d6ff4b81ee970a7e97466acfd" + "9644f828ffec538abc383d0e92326d1c88c55e1f46a668a039beaa1be631a891" + "29938c00a81a3ae46d4aecbf9707f764dbaccea3ef7665e4c4307fa0b0a3075c", + "224a4d65b958f6d6afb2904863efd2a734b31798884801fcab5a590f4d6da9de", + "178d51fddada62806f097aa615d33b8f2404e6b1479f5fd4859d595734d6d2b9", + "87b93ee2fecfda54deb8dff8e426f3c72c8864991f8ec2b3205bb3b416de93d2", + "4044a24df85be0cc76f21a4430b75b8e77b932a87f51e4eccbc45c263ebf8f66", + false // F (2 - R changed) + }, + { + "4a8c071ac4fd0d52faa407b0fe5dab759f7394a5832127f2a3498f34aac28733" + "9e043b4ffa79528faf199dc917f7b066ad65505dab0e11e6948515052ce20cfd" + "b892ffb8aa9bf3f1aa5be30a5bbe85823bddf70b39fd7ebd4a93a2f75472c1d4" + "f606247a9821f1a8c45a6cb80545de2e0c6c0174e2392088c754e9c8443eb5af", + "43691c7795a57ead8c5c68536fe934538d46f12889680a9cb6d055a066228369", + "f8790110b3c3b281aa1eae037d4f1234aff587d903d93ba3af225c27ddc9ccac", + "8acd62e8c262fa50dd9840480969f4ef70f218ebf8ef9584f199031132c6b1ce", + "cfca7ed3d4347fb2a29e526b43c348ae1ce6c60d44f3191b6d8ea3a2d9c92154", + false // F (3 - S changed) + }, + { + "0a3a12c3084c865daf1d302c78215d39bfe0b8bf28272b3c0b74beb4b7409db0" + "718239de700785581514321c6440a4bbaea4c76fa47401e151e68cb6c29017f0" + "bce4631290af5ea5e2bf3ed742ae110b04ade83a5dbd7358f29a85938e23d87a" + "c8233072b79c94670ff0959f9c7f4517862ff829452096c78f5f2e9a7e4e9216", + "9157dbfcf8cf385f5bb1568ad5c6e2a8652ba6dfc63bc1753edf5268cb7eb596", + "972570f4313d47fc96f7c02d5594d77d46f91e949808825b3d31f029e8296405", + "dfaea6f297fa320b707866125c2a7d5d515b51a503bee817de9faa343cc48eeb", + "8f780ad713f9c3e5a4f7fa4c519833dfefc6a7432389b1e4af463961f09764f2", + false // F (1 - Message changed) + }, + { + "785d07a3c54f63dca11f5d1a5f496ee2c2f9288e55007e666c78b007d95cc285" + "81dce51f490b30fa73dc9e2d45d075d7e3a95fb8a9e1465ad191904124160b7c" + "60fa720ef4ef1c5d2998f40570ae2a870ef3e894c2bc617d8a1dc85c3c557749" + "28c38789b4e661349d3f84d2441a3b856a76949b9f1f80bc161648a1cad5588e", + "072b10c081a4c1713a294f248aef850e297991aca47fa96a7470abe3b8acfdda", + "9581145cca04a0fb94cedce752c8f0370861916d2a94e7c647c5373ce6a4c8f5", + "09f5483eccec80f9d104815a1be9cc1a8e5b12b6eb482a65c6907b7480cf4f19", + "a4f90e560c5e4eb8696cb276e5165b6a9d486345dedfb094a76e8442d026378d", + false // F (4 - Q changed) + }, + { + "76f987ec5448dd72219bd30bf6b66b0775c80b394851a43ff1f537f140a6e722" + "9ef8cd72ad58b1d2d20298539d6347dd5598812bc65323aceaf05228f738b5ad" + "3e8d9fe4100fd767c2f098c77cb99c2992843ba3eed91d32444f3b6db6cd212d" + "d4e5609548f4bb62812a920f6e2bf1581be1ebeebdd06ec4e971862cc42055ca", + "09308ea5bfad6e5adf408634b3d5ce9240d35442f7fe116452aaec0d25be8c24", + "f40c93e023ef494b1c3079b2d10ef67f3170740495ce2cc57f8ee4b0618b8ee5", + "5cc8aa7c35743ec0c23dde88dabd5e4fcd0192d2116f6926fef788cddb754e73", + "9c9c045ebaa1b828c32f82ace0d18daebf5e156eb7cbfdc1eff4399a8a900ae7", + false // F (1 - Message changed) + }, + { + "60cd64b2cd2be6c33859b94875120361a24085f3765cb8b2bf11e026fa9d8855" + "dbe435acf7882e84f3c7857f96e2baab4d9afe4588e4a82e17a78827bfdb5ddb" + "d1c211fbc2e6d884cddd7cb9d90d5bf4a7311b83f352508033812c776a0e00c0" + "03c7e0d628e50736c7512df0acfa9f2320bd102229f46495ae6d0857cc452a84", + "2d98ea01f754d34bbc3003df5050200abf445ec728556d7ed7d5c54c55552b6d", + "9b52672742d637a32add056dfd6d8792f2a33c2e69dafabea09b960bc61e230a", + "06108e525f845d0155bf60193222b3219c98e3d49424c2fb2a0987f825c17959", + "62b5cdd591e5b507e560167ba8f6f7cda74673eb315680cb89ccbc4eec477dce", + true // P (0 ) + }, + {nullptr}}; + +// Returns true if |ch| is a lowercase hexadecimal digit. +bool IsHexDigit(char ch) { + return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f'); +} + +// Converts a lowercase hexadecimal digit to its integer value. +int HexDigitToInt(char ch) { + if ('0' <= ch && ch <= '9') { + return ch - '0'; + } + return ch - 'a' + 10; +} + +// |in| is a string consisting of lowercase hexadecimal digits, where +// every two digits represent one byte. |out| is a buffer of size |max_len|. +// Converts |in| to bytes and stores the bytes in the |out| buffer. The +// number of bytes converted is returned in |*out_len|. Returns true on +// success, false on failure. +bool DecodeHexString(const char* in, + char* out, + size_t* out_len, + size_t max_len) { + if (!in) { + *out_len = static_cast<size_t>(-1); + return true; + } + *out_len = 0; + while (*in != '\0') { + if (!IsHexDigit(*in) || !IsHexDigit(*(in + 1))) { + return false; + } + if (*out_len >= max_len) { + return false; + } + out[*out_len] = HexDigitToInt(*in) * 16 + HexDigitToInt(*(in + 1)); + (*out_len)++; + in += 2; + } + return true; +} + +} // namespace + +class ChannelIDTest : public QuicTest {}; + +// A known answer test for ChannelIDVerifier. +TEST_F(ChannelIDTest, VerifyKnownAnswerTest) { + char msg[1024]; + size_t msg_len; + char key[64]; + size_t qx_len; + size_t qy_len; + char signature[64]; + size_t r_len; + size_t s_len; + + for (size_t i = 0; test_vector[i].msg != nullptr; i++) { + SCOPED_TRACE(i); + // Decode the test vector. + ASSERT_TRUE( + DecodeHexString(test_vector[i].msg, msg, &msg_len, sizeof(msg))); + ASSERT_TRUE(DecodeHexString(test_vector[i].qx, key, &qx_len, sizeof(key))); + ASSERT_TRUE(DecodeHexString(test_vector[i].qy, key + qx_len, &qy_len, + sizeof(key) - qx_len)); + ASSERT_TRUE(DecodeHexString(test_vector[i].r, signature, &r_len, + sizeof(signature))); + ASSERT_TRUE(DecodeHexString(test_vector[i].s, signature + r_len, &s_len, + sizeof(signature) - r_len)); + + // The test vector's lengths should look sane. + EXPECT_EQ(sizeof(key) / 2, qx_len); + EXPECT_EQ(sizeof(key) / 2, qy_len); + EXPECT_EQ(sizeof(signature) / 2, r_len); + EXPECT_EQ(sizeof(signature) / 2, s_len); + + EXPECT_EQ( + test_vector[i].result, + ChannelIDVerifier::VerifyRaw( + QuicStringPiece(key, sizeof(key)), QuicStringPiece(msg, msg_len), + QuicStringPiece(signature, sizeof(signature)), false)); + } +} + +TEST_F(ChannelIDTest, SignAndVerify) { + std::unique_ptr<ChannelIDSource> source( + crypto_test_utils::ChannelIDSourceForTesting()); + + const QuicString signed_data = "signed data"; + const QuicString hostname = "foo.example.com"; + std::unique_ptr<ChannelIDKey> channel_id_key; + QuicAsyncStatus status = + source->GetChannelIDKey(hostname, &channel_id_key, nullptr); + ASSERT_EQ(QUIC_SUCCESS, status); + + QuicString signature; + ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature)); + + QuicString key = channel_id_key->SerializeKey(); + EXPECT_TRUE(ChannelIDVerifier::Verify(key, signed_data, signature)); + + EXPECT_FALSE(ChannelIDVerifier::Verify("a" + key, signed_data, signature)); + EXPECT_FALSE(ChannelIDVerifier::Verify(key, "a" + signed_data, signature)); + + std::unique_ptr<char[]> bad_key(new char[key.size()]); + memcpy(bad_key.get(), key.data(), key.size()); + bad_key[1] ^= 0x80; + EXPECT_FALSE(ChannelIDVerifier::Verify(QuicString(bad_key.get(), key.size()), + signed_data, signature)); + + std::unique_ptr<char[]> bad_signature(new char[signature.size()]); + memcpy(bad_signature.get(), signature.data(), signature.size()); + bad_signature[1] ^= 0x80; + EXPECT_FALSE(ChannelIDVerifier::Verify( + key, signed_data, QuicString(bad_signature.get(), signature.size()))); + + EXPECT_FALSE(ChannelIDVerifier::Verify(key, "wrong signed data", signature)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/common_cert_set.cc b/quic/core/crypto/common_cert_set.cc new file mode 100644 index 0000000..f649619 --- /dev/null +++ b/quic/core/crypto/common_cert_set.cc
@@ -0,0 +1,165 @@ +// Copyright (c) 2013 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/crypto/common_cert_set.h" + +#include <cstddef> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" + +namespace quic { + +namespace common_cert_set_2 { +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_2.c" +} + +namespace common_cert_set_3 { +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_3.c" +} + +namespace { + +struct CertSet { + // num_certs contains the number of certificates in this set. + size_t num_certs; + // certs is an array of |num_certs| pointers to the DER encoded certificates. + const unsigned char* const* certs; + // lens is an array of |num_certs| integers describing the length, in bytes, + // of each certificate. + const size_t* lens; + // hash contains the 64-bit, FNV-1a hash of this set. + uint64_t hash; +}; + +const CertSet kSets[] = { + { + common_cert_set_2::kNumCerts, + common_cert_set_2::kCerts, + common_cert_set_2::kLens, + common_cert_set_2::kHash, + }, + { + common_cert_set_3::kNumCerts, + common_cert_set_3::kCerts, + common_cert_set_3::kLens, + common_cert_set_3::kHash, + }, +}; + +const uint64_t kSetHashes[] = { + common_cert_set_2::kHash, + common_cert_set_3::kHash, +}; + +// Compare returns a value less than, equal to or greater than zero if |a| is +// lexicographically less than, equal to or greater than |b|, respectively. +int Compare(QuicStringPiece a, const unsigned char* b, size_t b_len) { + size_t len = a.size(); + if (len > b_len) { + len = b_len; + } + int n = memcmp(a.data(), b, len); + if (n != 0) { + return n; + } + + if (a.size() < b_len) { + return -1; + } else if (a.size() > b_len) { + return 1; + } + return 0; +} + +// CommonCertSetsQUIC implements the CommonCertSets interface using the default +// certificate sets. +class CommonCertSetsQUIC : public CommonCertSets { + public: + // CommonCertSets interface. + QuicStringPiece GetCommonHashes() const override { + return QuicStringPiece(reinterpret_cast<const char*>(kSetHashes), + sizeof(uint64_t) * QUIC_ARRAYSIZE(kSetHashes)); + } + + QuicStringPiece GetCert(uint64_t hash, uint32_t index) const override { + for (size_t i = 0; i < QUIC_ARRAYSIZE(kSets); i++) { + if (kSets[i].hash == hash) { + if (index < kSets[i].num_certs) { + return QuicStringPiece( + reinterpret_cast<const char*>(kSets[i].certs[index]), + kSets[i].lens[index]); + } + break; + } + } + + return QuicStringPiece(); + } + + bool MatchCert(QuicStringPiece cert, + QuicStringPiece common_set_hashes, + uint64_t* out_hash, + uint32_t* out_index) const override { + if (common_set_hashes.size() % sizeof(uint64_t) != 0) { + return false; + } + + for (size_t i = 0; i < common_set_hashes.size() / sizeof(uint64_t); i++) { + uint64_t hash; + memcpy(&hash, common_set_hashes.data() + i * sizeof(uint64_t), + sizeof(uint64_t)); + + for (size_t j = 0; j < QUIC_ARRAYSIZE(kSets); j++) { + if (kSets[j].hash != hash) { + continue; + } + + if (kSets[j].num_certs == 0) { + continue; + } + + // Binary search for a matching certificate. + size_t min = 0; + size_t max = kSets[j].num_certs - 1; + while (max >= min) { + size_t mid = min + ((max - min) / 2); + int n = Compare(cert, kSets[j].certs[mid], kSets[j].lens[mid]); + if (n < 0) { + if (mid == 0) { + break; + } + max = mid - 1; + } else if (n > 0) { + min = mid + 1; + } else { + *out_hash = hash; + *out_index = mid; + return true; + } + } + } + } + + return false; + } + + CommonCertSetsQUIC() {} + CommonCertSetsQUIC(const CommonCertSetsQUIC&) = delete; + CommonCertSetsQUIC& operator=(const CommonCertSetsQUIC&) = delete; + ~CommonCertSetsQUIC() override {} +}; + +} // anonymous namespace + +CommonCertSets::~CommonCertSets() {} + +// static +const CommonCertSets* CommonCertSets::GetInstanceQUIC() { + static CommonCertSetsQUIC* certs = new CommonCertSetsQUIC(); + return certs; +} + +} // namespace quic
diff --git a/quic/core/crypto/common_cert_set.h b/quic/core/crypto/common_cert_set.h new file mode 100644 index 0000000..57c0fea --- /dev/null +++ b/quic/core/crypto/common_cert_set.h
@@ -0,0 +1,47 @@ +// Copyright (c) 2013 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_CRYPTO_COMMON_CERT_SET_H_ +#define QUICHE_QUIC_CORE_CRYPTO_COMMON_CERT_SET_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// CommonCertSets is an interface to an object that contains a number of common +// certificate sets and can match against them. +class QUIC_EXPORT_PRIVATE CommonCertSets { + public: + virtual ~CommonCertSets(); + + // GetInstanceQUIC returns the standard QUIC common certificate sets. + static const CommonCertSets* GetInstanceQUIC(); + + // GetCommonHashes returns a QuicStringPiece containing the hashes of common + // sets supported by this object. The 64-bit hashes are concatenated in the + // QuicStringPiece. + virtual QuicStringPiece GetCommonHashes() const = 0; + + // GetCert returns a specific certificate (at index |index|) in the common + // set identified by |hash|. If no such certificate is known, an empty + // QuicStringPiece is returned. + virtual QuicStringPiece GetCert(uint64_t hash, uint32_t index) const = 0; + + // MatchCert tries to find |cert| in one of the common certificate sets + // identified by |common_set_hashes|. On success it puts the hash of the + // set in |out_hash|, the index of |cert| in the set in |out_index| and + // returns true. Otherwise it returns false. + virtual bool MatchCert(QuicStringPiece cert, + QuicStringPiece common_set_hashes, + uint64_t* out_hash, + uint32_t* out_index) const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_COMMON_CERT_SET_H_
diff --git a/quic/core/crypto/common_cert_set_2.c b/quic/core/crypto/common_cert_set_2.c new file mode 100644 index 0000000..ec205fc --- /dev/null +++ b/quic/core/crypto/common_cert_set_2.c
@@ -0,0 +1,122 @@ +/* This file contains common certificates. It's designed to be #included in + * another file, in a namespace. */ + +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_2a.inc" +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_2b.inc" + +static const size_t kNumCerts = 54; +static const unsigned char* const kCerts[] = { + kDERCert0, + kDERCert1, + kDERCert2, + kDERCert3, + kDERCert4, + kDERCert5, + kDERCert6, + kDERCert7, + kDERCert8, + kDERCert9, + kDERCert10, + kDERCert11, + kDERCert12, + kDERCert13, + kDERCert14, + kDERCert15, + kDERCert16, + kDERCert17, + kDERCert18, + kDERCert19, + kDERCert20, + kDERCert21, + kDERCert22, + kDERCert23, + kDERCert24, + kDERCert25, + kDERCert26, + kDERCert27, + kDERCert28, + kDERCert29, + kDERCert30, + kDERCert31, + kDERCert32, + kDERCert33, + kDERCert34, + kDERCert35, + kDERCert36, + kDERCert37, + kDERCert38, + kDERCert39, + kDERCert40, + kDERCert41, + kDERCert42, + kDERCert43, + kDERCert44, + kDERCert45, + kDERCert46, + kDERCert47, + kDERCert48, + kDERCert49, + kDERCert50, + kDERCert51, + kDERCert52, + kDERCert53, +}; + +static const size_t kLens[] = { + 897, + 911, + 985, + 1012, + 1049, + 1062, + 1065, + 1071, + 1084, + 1096, + 1097, + 1105, + 1107, + 1117, + 1127, + 1133, + 1136, + 1138, + 1153, + 1171, + 1172, + 1176, + 1182, + 1188, + 1194, + 1203, + 1205, + 1206, + 1210, + 1222, + 1226, + 1236, + 1236, + 1236, + 1238, + 1256, + 1270, + 1280, + 1283, + 1284, + 1287, + 1315, + 1327, + 1340, + 1418, + 1447, + 1509, + 1520, + 1570, + 1581, + 1592, + 1628, + 1632, + 1770, +}; + +static const uint64_t kHash = UINT64_C(0xe81a92926081e801);
diff --git a/quic/core/crypto/common_cert_set_2a.inc b/quic/core/crypto/common_cert_set_2a.inc new file mode 100644 index 0000000..75e648c --- /dev/null +++ b/quic/core/crypto/common_cert_set_2a.inc
@@ -0,0 +1,5622 @@ +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1227750 (0x12bbe6) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority + Validity + Not Before: May 21 04:00:00 2002 GMT + Not After : Aug 21 04:00:00 2018 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df: + 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8: + 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29: + bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4: + 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3: + ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92: + 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d: + 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14: + 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd: + d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6: + d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5: + 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39: + 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05: + 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2: + fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32: + eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07: + 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b: + e4:f9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4 + + X509v3 Subject Key Identifier: + C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/crls/secureca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.geotrust.com/resources/repository + + Signature Algorithm: sha1WithRSAEncryption + 76:e1:12:6e:4e:4b:16:12:86:30:06:b2:81:08:cf:f0:08:c7: + c7:71:7e:66:ee:c2:ed:d4:3b:1f:ff:f0:f0:c8:4e:d6:43:38: + b0:b9:30:7d:18:d0:55:83:a2:6a:cb:36:11:9c:e8:48:66:a3: + 6d:7f:b8:13:d4:47:fe:8b:5a:5c:73:fc:ae:d9:1b:32:19:38: + ab:97:34:14:aa:96:d2:eb:a3:1c:14:08:49:b6:bb:e5:91:ef: + 83:36:eb:1d:56:6f:ca:da:bc:73:63:90:e4:7f:7b:3e:22:cb: + 3d:07:ed:5f:38:74:9c:e3:03:50:4e:a1:af:98:ee:61:f2:84: + 3f:12 +-----BEGIN CERTIFICATE----- +MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT +MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 +aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw +WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE +AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m +OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu +T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c +JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR +Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz +PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm +aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM +TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g +LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO +BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv +dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB +AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL +NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W +b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert0[] = { + 0x30, 0x82, 0x03, 0x7d, 0x30, 0x82, 0x02, 0xe6, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x12, 0xbb, 0xe6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45, + 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x32, 0x30, + 0x35, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, + 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, + 0x5a, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x12, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0xcc, 0x18, 0x63, 0x30, 0xfd, + 0xf4, 0x17, 0x23, 0x1a, 0x56, 0x7e, 0x5b, 0xdf, 0x3c, 0x6c, 0x38, 0xe4, + 0x71, 0xb7, 0x78, 0x91, 0xd4, 0xbc, 0xa1, 0xd8, 0x4c, 0xf8, 0xa8, 0x43, + 0xb6, 0x03, 0xe9, 0x4d, 0x21, 0x07, 0x08, 0x88, 0xda, 0x58, 0x2f, 0x66, + 0x39, 0x29, 0xbd, 0x05, 0x78, 0x8b, 0x9d, 0x38, 0xe8, 0x05, 0xb7, 0x6a, + 0x7e, 0x71, 0xa4, 0xe6, 0xc4, 0x60, 0xa6, 0xb0, 0xef, 0x80, 0xe4, 0x89, + 0x28, 0x0f, 0x9e, 0x25, 0xd6, 0xed, 0x83, 0xf3, 0xad, 0xa6, 0x91, 0xc7, + 0x98, 0xc9, 0x42, 0x18, 0x35, 0x14, 0x9d, 0xad, 0x98, 0x46, 0x92, 0x2e, + 0x4f, 0xca, 0xf1, 0x87, 0x43, 0xc1, 0x16, 0x95, 0x57, 0x2d, 0x50, 0xef, + 0x89, 0x2d, 0x80, 0x7a, 0x57, 0xad, 0xf2, 0xee, 0x5f, 0x6b, 0xd2, 0x00, + 0x8d, 0xb9, 0x14, 0xf8, 0x14, 0x15, 0x35, 0xd9, 0xc0, 0x46, 0xa3, 0x7b, + 0x72, 0xc8, 0x91, 0xbf, 0xc9, 0x55, 0x2b, 0xcd, 0xd0, 0x97, 0x3e, 0x9c, + 0x26, 0x64, 0xcc, 0xdf, 0xce, 0x83, 0x19, 0x71, 0xca, 0x4e, 0xe6, 0xd4, + 0xd5, 0x7b, 0xa9, 0x19, 0xcd, 0x55, 0xde, 0xc8, 0xec, 0xd2, 0x5e, 0x38, + 0x53, 0xe5, 0x5c, 0x4f, 0x8c, 0x2d, 0xfe, 0x50, 0x23, 0x36, 0xfc, 0x66, + 0xe6, 0xcb, 0x8e, 0xa4, 0x39, 0x19, 0x00, 0xb7, 0x95, 0x02, 0x39, 0x91, + 0x0b, 0x0e, 0xfe, 0x38, 0x2e, 0xd1, 0x1d, 0x05, 0x9a, 0xf6, 0x4d, 0x3e, + 0x6f, 0x0f, 0x07, 0x1d, 0xaf, 0x2c, 0x1e, 0x8f, 0x60, 0x39, 0xe2, 0xfa, + 0x36, 0x53, 0x13, 0x39, 0xd4, 0x5e, 0x26, 0x2b, 0xdb, 0x3d, 0xa8, 0x14, + 0xbd, 0x32, 0xeb, 0x18, 0x03, 0x28, 0x52, 0x04, 0x71, 0xe5, 0xab, 0x33, + 0x3d, 0xe1, 0x38, 0xbb, 0x07, 0x36, 0x84, 0x62, 0x9c, 0x79, 0xea, 0x16, + 0x30, 0xf4, 0x5f, 0xc0, 0x2b, 0xe8, 0x71, 0x6b, 0xe4, 0xf9, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xf0, 0x30, 0x81, 0xed, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, + 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, + 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, + 0x2d, 0xa0, 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, + 0x63, 0x75, 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4e, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, + 0x00, 0x76, 0xe1, 0x12, 0x6e, 0x4e, 0x4b, 0x16, 0x12, 0x86, 0x30, 0x06, + 0xb2, 0x81, 0x08, 0xcf, 0xf0, 0x08, 0xc7, 0xc7, 0x71, 0x7e, 0x66, 0xee, + 0xc2, 0xed, 0xd4, 0x3b, 0x1f, 0xff, 0xf0, 0xf0, 0xc8, 0x4e, 0xd6, 0x43, + 0x38, 0xb0, 0xb9, 0x30, 0x7d, 0x18, 0xd0, 0x55, 0x83, 0xa2, 0x6a, 0xcb, + 0x36, 0x11, 0x9c, 0xe8, 0x48, 0x66, 0xa3, 0x6d, 0x7f, 0xb8, 0x13, 0xd4, + 0x47, 0xfe, 0x8b, 0x5a, 0x5c, 0x73, 0xfc, 0xae, 0xd9, 0x1b, 0x32, 0x19, + 0x38, 0xab, 0x97, 0x34, 0x14, 0xaa, 0x96, 0xd2, 0xeb, 0xa3, 0x1c, 0x14, + 0x08, 0x49, 0xb6, 0xbb, 0xe5, 0x91, 0xef, 0x83, 0x36, 0xeb, 0x1d, 0x56, + 0x6f, 0xca, 0xda, 0xbc, 0x73, 0x63, 0x90, 0xe4, 0x7f, 0x7b, 0x3e, 0x22, + 0xcb, 0x3d, 0x07, 0xed, 0x5f, 0x38, 0x74, 0x9c, 0xe3, 0x03, 0x50, 0x4e, + 0xa1, 0xaf, 0x98, 0xee, 0x61, 0xf2, 0x84, 0x3f, 0x12, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 880226 (0xd6e62) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority + Validity + Not Before: Nov 27 00:00:00 2006 GMT + Not After : Aug 21 16:15:00 2018 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:be:b8:15:7b:ff:d4:7c:7d:67:ad:83:64:7b:c8: + 42:53:2d:df:f6:84:08:20:61:d6:01:59:6a:9c:44: + 11:af:ef:76:fd:95:7e:ce:61:30:bb:7a:83:5f:02: + bd:01:66:ca:ee:15:8d:6f:a1:30:9c:bd:a1:85:9e: + 94:3a:f3:56:88:00:31:cf:d8:ee:6a:96:02:d9:ed: + 03:8c:fb:75:6d:e7:ea:b8:55:16:05:16:9a:f4:e0: + 5e:b1:88:c0:64:85:5c:15:4d:88:c7:b7:ba:e0:75: + e9:ad:05:3d:9d:c7:89:48:e0:bb:28:c8:03:e1:30: + 93:64:5e:52:c0:59:70:22:35:57:88:8a:f1:95:0a: + 83:d7:bc:31:73:01:34:ed:ef:46:71:e0:6b:02:a8: + 35:72:6b:97:9b:66:e0:cb:1c:79:5f:d8:1a:04:68: + 1e:47:02:e6:9d:60:e2:36:97:01:df:ce:35:92:df: + be:67:c7:6d:77:59:3b:8f:9d:d6:90:15:94:bc:42: + 34:10:c1:39:f9:b1:27:3e:7e:d6:8a:75:c5:b2:af: + 96:d3:a2:de:9b:e4:98:be:7d:e1:e9:81:ad:b6:6f: + fc:d7:0e:da:e0:34:b0:0d:1a:77:e7:e3:08:98:ef: + 58:fa:9c:84:b7:36:af:c2:df:ac:d2:f4:10:06:70: + 71:35 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92 + X509v3 Authority Key Identifier: + keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4 + + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/crls/secureca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.geotrust.com/resources/cps + + Signature Algorithm: sha1WithRSAEncryption + af:f3:0e:d6:72:ab:c7:a9:97:ca:2a:6b:84:39:de:79:a9:f0: + 81:e5:08:67:ab:d7:2f:20:02:01:71:0c:04:22:c9:1e:88:95: + 03:c9:49:3a:af:67:08:49:b0:d5:08:f5:20:3d:80:91:a0:c5: + 87:a3:fb:c9:a3:17:91:f9:a8:2f:ae:e9:0f:df:96:72:0f:75: + 17:80:5d:78:01:4d:9f:1f:6d:7b:d8:f5:42:38:23:1a:99:93: + f4:83:be:3b:35:74:e7:37:13:35:7a:ac:b4:b6:90:82:6c:27: + a4:e0:ec:9e:35:bd:bf:e5:29:a1:47:9f:5b:32:fc:e9:99:7d: + 2b:39 +-----BEGIN CERTIFICATE----- +MIIDizCCAvSgAwIBAgIDDW5iMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT +MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 +aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMTI3MDAwMDAwWhcNMTgwODIxMTYxNTAw +WjBYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UE +AxMoR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64FXv/1Hx9Z62DZHvIQlMt3/aE +CCBh1gFZapxEEa/vdv2Vfs5hMLt6g18CvQFmyu4VjW+hMJy9oYWelDrzVogAMc/Y +7mqWAtntA4z7dW3n6rhVFgUWmvTgXrGIwGSFXBVNiMe3uuB16a0FPZ3HiUjguyjI +A+Ewk2ReUsBZcCI1V4iK8ZUKg9e8MXMBNO3vRnHgawKoNXJrl5tm4MsceV/YGgRo +HkcC5p1g4jaXAd/ONZLfvmfHbXdZO4+d1pAVlLxCNBDBOfmxJz5+1op1xbKvltOi +3pvkmL594emBrbZv/NcO2uA0sA0ad+fjCJjvWPqchLc2r8LfrNL0EAZwcTUCAwEA +AaOB6DCB5TAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFCzVUEGXFYvwjzZhW0r7 +a9mZyTOSMB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMA8GA1UdEwEB +/wQFMAMBAf8wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j +b20vY3Jscy9zZWN1cmVjYS5jcmwwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYB +BQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwDQYJ +KoZIhvcNAQEFBQADgYEAr/MO1nKrx6mXyiprhDneeanwgeUIZ6vXLyACAXEMBCLJ +HoiVA8lJOq9nCEmw1Qj1ID2AkaDFh6P7yaMXkfmoL67pD9+Wcg91F4BdeAFNnx9t +e9j1QjgjGpmT9IO+OzV05zcTNXqstLaQgmwnpODsnjW9v+UpoUefWzL86Zl9Kzk= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert1[] = { + 0x30, 0x82, 0x03, 0x8b, 0x30, 0x82, 0x02, 0xf4, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x0d, 0x6e, 0x62, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45, + 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, + 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, + 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x31, 0x36, 0x31, 0x35, 0x30, 0x30, + 0x5a, 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xbe, 0xb8, 0x15, 0x7b, 0xff, 0xd4, 0x7c, 0x7d, + 0x67, 0xad, 0x83, 0x64, 0x7b, 0xc8, 0x42, 0x53, 0x2d, 0xdf, 0xf6, 0x84, + 0x08, 0x20, 0x61, 0xd6, 0x01, 0x59, 0x6a, 0x9c, 0x44, 0x11, 0xaf, 0xef, + 0x76, 0xfd, 0x95, 0x7e, 0xce, 0x61, 0x30, 0xbb, 0x7a, 0x83, 0x5f, 0x02, + 0xbd, 0x01, 0x66, 0xca, 0xee, 0x15, 0x8d, 0x6f, 0xa1, 0x30, 0x9c, 0xbd, + 0xa1, 0x85, 0x9e, 0x94, 0x3a, 0xf3, 0x56, 0x88, 0x00, 0x31, 0xcf, 0xd8, + 0xee, 0x6a, 0x96, 0x02, 0xd9, 0xed, 0x03, 0x8c, 0xfb, 0x75, 0x6d, 0xe7, + 0xea, 0xb8, 0x55, 0x16, 0x05, 0x16, 0x9a, 0xf4, 0xe0, 0x5e, 0xb1, 0x88, + 0xc0, 0x64, 0x85, 0x5c, 0x15, 0x4d, 0x88, 0xc7, 0xb7, 0xba, 0xe0, 0x75, + 0xe9, 0xad, 0x05, 0x3d, 0x9d, 0xc7, 0x89, 0x48, 0xe0, 0xbb, 0x28, 0xc8, + 0x03, 0xe1, 0x30, 0x93, 0x64, 0x5e, 0x52, 0xc0, 0x59, 0x70, 0x22, 0x35, + 0x57, 0x88, 0x8a, 0xf1, 0x95, 0x0a, 0x83, 0xd7, 0xbc, 0x31, 0x73, 0x01, + 0x34, 0xed, 0xef, 0x46, 0x71, 0xe0, 0x6b, 0x02, 0xa8, 0x35, 0x72, 0x6b, + 0x97, 0x9b, 0x66, 0xe0, 0xcb, 0x1c, 0x79, 0x5f, 0xd8, 0x1a, 0x04, 0x68, + 0x1e, 0x47, 0x02, 0xe6, 0x9d, 0x60, 0xe2, 0x36, 0x97, 0x01, 0xdf, 0xce, + 0x35, 0x92, 0xdf, 0xbe, 0x67, 0xc7, 0x6d, 0x77, 0x59, 0x3b, 0x8f, 0x9d, + 0xd6, 0x90, 0x15, 0x94, 0xbc, 0x42, 0x34, 0x10, 0xc1, 0x39, 0xf9, 0xb1, + 0x27, 0x3e, 0x7e, 0xd6, 0x8a, 0x75, 0xc5, 0xb2, 0xaf, 0x96, 0xd3, 0xa2, + 0xde, 0x9b, 0xe4, 0x98, 0xbe, 0x7d, 0xe1, 0xe9, 0x81, 0xad, 0xb6, 0x6f, + 0xfc, 0xd7, 0x0e, 0xda, 0xe0, 0x34, 0xb0, 0x0d, 0x1a, 0x77, 0xe7, 0xe3, + 0x08, 0x98, 0xef, 0x58, 0xfa, 0x9c, 0x84, 0xb7, 0x36, 0xaf, 0xc2, 0xdf, + 0xac, 0xd2, 0xf4, 0x10, 0x06, 0x70, 0x71, 0x35, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x81, 0xe8, 0x30, 0x81, 0xe5, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2c, 0xd5, + 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb, + 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b, + 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98, + 0x90, 0x9f, 0xd4, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3a, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, + 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55, + 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x81, 0x81, 0x00, 0xaf, 0xf3, 0x0e, 0xd6, 0x72, 0xab, 0xc7, 0xa9, 0x97, + 0xca, 0x2a, 0x6b, 0x84, 0x39, 0xde, 0x79, 0xa9, 0xf0, 0x81, 0xe5, 0x08, + 0x67, 0xab, 0xd7, 0x2f, 0x20, 0x02, 0x01, 0x71, 0x0c, 0x04, 0x22, 0xc9, + 0x1e, 0x88, 0x95, 0x03, 0xc9, 0x49, 0x3a, 0xaf, 0x67, 0x08, 0x49, 0xb0, + 0xd5, 0x08, 0xf5, 0x20, 0x3d, 0x80, 0x91, 0xa0, 0xc5, 0x87, 0xa3, 0xfb, + 0xc9, 0xa3, 0x17, 0x91, 0xf9, 0xa8, 0x2f, 0xae, 0xe9, 0x0f, 0xdf, 0x96, + 0x72, 0x0f, 0x75, 0x17, 0x80, 0x5d, 0x78, 0x01, 0x4d, 0x9f, 0x1f, 0x6d, + 0x7b, 0xd8, 0xf5, 0x42, 0x38, 0x23, 0x1a, 0x99, 0x93, 0xf4, 0x83, 0xbe, + 0x3b, 0x35, 0x74, 0xe7, 0x37, 0x13, 0x35, 0x7a, 0xac, 0xb4, 0xb6, 0x90, + 0x82, 0x6c, 0x27, 0xa4, 0xe0, 0xec, 0x9e, 0x35, 0xbd, 0xbf, 0xe5, 0x29, + 0xa1, 0x47, 0x9f, 0x5b, 0x32, 0xfc, 0xe9, 0x99, 0x7d, 0x2b, 0x39, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 145105 (0x236d1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Feb 19 22:45:05 2010 GMT + Not After : Feb 18 22:45:05 2020 GMT + Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3: + fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24: + 43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6: + 41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd: + 5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8: + e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37: + e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47: + 69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a: + e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c: + 03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79: + f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc: + b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad: + f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67: + 58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b: + c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b: + 2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18: + 5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1: + 50:8b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30 + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://ocsp.geotrust.com + + Signature Algorithm: sha1WithRSAEncryption + ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98: + f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2: + 2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5: + 03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9: + 0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63: + a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad: + 39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0: + c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74: + 4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51: + 59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c: + fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3: + ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d: + 99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6: + 4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54: + 6c:7a:b7:b7 +-----BEGIN CERTIFICATE----- +MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG +EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM +IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0 +l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e +6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb +ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8 +N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5 +HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd +gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC +St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w +EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js +Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw +JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B +AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x +/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O +SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61 +04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4 +knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK +LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert2[] = { + 0x30, 0x82, 0x03, 0xd5, 0x30, 0x82, 0x02, 0xbd, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x36, 0xd1, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, + 0x32, 0x31, 0x39, 0x32, 0x32, 0x34, 0x35, 0x30, 0x35, 0x5a, 0x17, 0x0d, + 0x32, 0x30, 0x30, 0x32, 0x31, 0x38, 0x32, 0x32, 0x34, 0x35, 0x30, 0x35, + 0x5a, 0x30, 0x3c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0e, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x0b, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, + 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xc7, 0x71, 0xf8, 0x56, 0xc7, 0x1e, 0xd9, 0xcc, 0xb5, 0xad, 0xf6, 0xb4, + 0x97, 0xa3, 0xfb, 0xa1, 0xe6, 0x0b, 0x50, 0x5f, 0x50, 0xaa, 0x3a, 0xda, + 0x0f, 0xfc, 0x3d, 0x29, 0x24, 0x43, 0xc6, 0x10, 0x29, 0xc1, 0xfc, 0x55, + 0x40, 0x72, 0xee, 0xbd, 0xea, 0xdf, 0x9f, 0xb6, 0x41, 0xf4, 0x48, 0x4b, + 0xc8, 0x6e, 0xfe, 0x4f, 0x57, 0x12, 0x8b, 0x5b, 0xfa, 0x92, 0xdd, 0x5e, + 0xe8, 0xad, 0xf3, 0xf0, 0x1b, 0xb1, 0x7b, 0x4d, 0xfb, 0xcf, 0xfd, 0xd1, + 0xe5, 0xf8, 0xe3, 0xdc, 0xe7, 0xf5, 0x73, 0x7f, 0xdf, 0x01, 0x49, 0xcf, + 0x8c, 0x56, 0xc1, 0xbd, 0x37, 0xe3, 0x5b, 0xbe, 0xb5, 0x4f, 0x8b, 0x8b, + 0xf0, 0xda, 0x4f, 0xc7, 0xe3, 0xdd, 0x55, 0x47, 0x69, 0xdf, 0xf2, 0x5b, + 0x7b, 0x07, 0x4f, 0x3d, 0xe5, 0xac, 0x21, 0xc1, 0xc8, 0x1d, 0x7a, 0xe8, + 0xe7, 0xf6, 0x0f, 0xa1, 0xaa, 0xf5, 0x6f, 0xde, 0xa8, 0x65, 0x4f, 0x10, + 0x89, 0x9c, 0x03, 0xf3, 0x89, 0x7a, 0xa5, 0x5e, 0x01, 0x72, 0x33, 0xed, + 0xa9, 0xe9, 0x5a, 0x1e, 0x79, 0xf3, 0x87, 0xc8, 0xdf, 0xc8, 0xc5, 0xfc, + 0x37, 0xc8, 0x9a, 0x9a, 0xd7, 0xb8, 0x76, 0xcc, 0xb0, 0x3e, 0xe7, 0xfd, + 0xe6, 0x54, 0xea, 0xdf, 0x5f, 0x52, 0x41, 0x78, 0x59, 0x57, 0xad, 0xf1, + 0x12, 0xd6, 0x7f, 0xbc, 0xd5, 0x9f, 0x70, 0xd3, 0x05, 0x6c, 0xfa, 0xa3, + 0x7d, 0x67, 0x58, 0xdd, 0x26, 0x62, 0x1d, 0x31, 0x92, 0x0c, 0x79, 0x79, + 0x1c, 0x8e, 0xcf, 0xca, 0x7b, 0xc1, 0x66, 0xaf, 0xa8, 0x74, 0x48, 0xfb, + 0x8e, 0x82, 0xc2, 0x9e, 0x2c, 0x99, 0x5c, 0x7b, 0x2d, 0x5d, 0x9b, 0xbc, + 0x5b, 0x57, 0x9e, 0x7c, 0x3a, 0x7a, 0x13, 0xad, 0xf2, 0xa3, 0x18, 0x5b, + 0x2b, 0x59, 0x0f, 0xcd, 0x5c, 0x3a, 0xeb, 0x68, 0x33, 0xc6, 0x28, 0x1d, + 0x82, 0xd1, 0x50, 0x8b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xd9, + 0x30, 0x81, 0xd6, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6b, 0x69, 0x3d, 0x6a, 0x18, 0x42, + 0x4a, 0xdd, 0x8f, 0x02, 0x65, 0x39, 0xfd, 0x35, 0x24, 0x86, 0x78, 0x91, + 0x16, 0x30, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, + 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, + 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3a, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b, + 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, + 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, + 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, + 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xab, 0xbc, 0xbc, + 0x0a, 0x5d, 0x18, 0x94, 0xe3, 0xc1, 0xb1, 0xc3, 0xa8, 0x4c, 0x55, 0xd6, + 0xbe, 0xb4, 0x98, 0xf1, 0xee, 0x3c, 0x1c, 0xcd, 0xcf, 0xf3, 0x24, 0x24, + 0x5c, 0x96, 0x03, 0x27, 0x58, 0xfc, 0x36, 0xae, 0xa2, 0x2f, 0x8f, 0xf1, + 0xfe, 0xda, 0x2b, 0x02, 0xc3, 0x33, 0xbd, 0xc8, 0xdd, 0x48, 0x22, 0x2b, + 0x60, 0x0f, 0xa5, 0x03, 0x10, 0xfd, 0x77, 0xf8, 0xd0, 0xed, 0x96, 0x67, + 0x4f, 0xfd, 0xea, 0x47, 0x20, 0x70, 0x54, 0xdc, 0xa9, 0x0c, 0x55, 0x7e, + 0xe1, 0x96, 0x25, 0x8a, 0xd9, 0xb5, 0xda, 0x57, 0x4a, 0xbe, 0x8d, 0x8e, + 0x49, 0x43, 0x63, 0xa5, 0x6c, 0x4e, 0x27, 0x87, 0x25, 0xeb, 0x5b, 0x6d, + 0xfe, 0xa2, 0x7f, 0x38, 0x28, 0xe0, 0x36, 0xab, 0xad, 0x39, 0xa5, 0xa5, + 0x62, 0xc4, 0xb7, 0x5c, 0x58, 0x2c, 0xaa, 0x5d, 0x01, 0x60, 0xa6, 0x62, + 0x67, 0xa3, 0xc0, 0xc7, 0x62, 0x23, 0xf4, 0xe7, 0x6c, 0x46, 0xee, 0xb5, + 0xd3, 0x80, 0x6a, 0x22, 0x13, 0xd2, 0x2d, 0x3f, 0x74, 0x4f, 0xea, 0xaf, + 0x8c, 0x5f, 0xb4, 0x38, 0x9c, 0xdb, 0xae, 0xce, 0xaf, 0x84, 0x1e, 0xa6, + 0xf6, 0x34, 0x51, 0x59, 0x79, 0xd3, 0xe3, 0x75, 0xdc, 0xbc, 0xd7, 0xf3, + 0x73, 0xdf, 0x92, 0xec, 0xd2, 0x20, 0x59, 0x6f, 0x9c, 0xfb, 0x95, 0xf8, + 0x92, 0x76, 0x18, 0x0a, 0x7c, 0x0f, 0x2c, 0xa6, 0xca, 0xde, 0x8a, 0x62, + 0x7b, 0xd8, 0xf3, 0xce, 0x5f, 0x68, 0xbd, 0x8f, 0x3e, 0xc1, 0x74, 0xbb, + 0x15, 0x72, 0x3a, 0x16, 0x83, 0xa9, 0x0b, 0xe6, 0x4d, 0x99, 0x9c, 0xd8, + 0x57, 0xec, 0xa8, 0x01, 0x51, 0xc7, 0x6f, 0x57, 0x34, 0x5e, 0xab, 0x4a, + 0x2c, 0x42, 0xf6, 0x4f, 0x1c, 0x89, 0x78, 0xde, 0x26, 0x4e, 0xf5, 0x6f, + 0x93, 0x4c, 0x15, 0x6b, 0x27, 0x56, 0x4d, 0x00, 0x54, 0x6c, 0x7a, 0xb7, + 0xb7, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146051 (0x23a83) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Apr 5 15:15:56 2013 GMT + Not After : Dec 31 23:59:59 2016 GMT + Subject: C=US, O=Google Inc, CN=Google Internet Authority G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9c:2a:04:77:5c:d8:50:91:3a:06:a3:82:e0:d8: + 50:48:bc:89:3f:f1:19:70:1a:88:46:7e:e0:8f:c5: + f1:89:ce:21:ee:5a:fe:61:0d:b7:32:44:89:a0:74: + 0b:53:4f:55:a4:ce:82:62:95:ee:eb:59:5f:c6:e1: + 05:80:12:c4:5e:94:3f:bc:5b:48:38:f4:53:f7:24: + e6:fb:91:e9:15:c4:cf:f4:53:0d:f4:4a:fc:9f:54: + de:7d:be:a0:6b:6f:87:c0:d0:50:1f:28:30:03:40: + da:08:73:51:6c:7f:ff:3a:3c:a7:37:06:8e:bd:4b: + 11:04:eb:7d:24:de:e6:f9:fc:31:71:fb:94:d5:60: + f3:2e:4a:af:42:d2:cb:ea:c4:6a:1a:b2:cc:53:dd: + 15:4b:8b:1f:c8:19:61:1f:cd:9d:a8:3e:63:2b:84: + 35:69:65:84:c8:19:c5:46:22:f8:53:95:be:e3:80: + 4a:10:c6:2a:ec:ba:97:20:11:c7:39:99:10:04:a0: + f0:61:7a:95:25:8c:4e:52:75:e2:b6:ed:08:ca:14: + fc:ce:22:6a:b3:4e:cf:46:03:97:97:03:7e:c0:b1: + de:7b:af:45:33:cf:ba:3e:71:b7:de:f4:25:25:c2: + 0d:35:89:9d:9d:fb:0e:11:79:89:1e:37:c5:af:8e: + 72:69 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + 4A:DD:06:16:1B:BC:F6:68:B5:76:F5:81:B6:BB:62:1A:BA:5A:81:2F + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.11129.2.5.1 + + Signature Algorithm: sha256WithRSAEncryption + aa:fa:a9:20:cd:6a:67:83:ed:5e:d4:7e:de:1d:c4:7f:e0:25: + 06:00:c5:24:fb:a9:c8:2d:6d:7e:de:9d:82:65:2c:81:63:34: + 66:3e:e9:52:c2:08:b4:cb:2f:f7:5f:99:3a:6a:9c:50:7a:85: + 05:8c:7d:d1:2a:48:84:d3:09:6c:7c:c2:cd:35:9f:f3:82:ee: + 52:de:68:5f:e4:00:8a:17:20:96:f7:29:8d:9a:4d:cb:a8:de: + 86:c8:0d:6f:56:87:03:7d:03:3f:dc:fa:79:7d:21:19:f9:c8: + 3a:2f:51:76:8c:c7:41:92:71:8f:25:ce:37:f8:4a:4c:00:23: + ef:c4:35:10:ae:e0:23:80:73:7c:4d:34:2e:c8:6e:90:d6:10: + 1e:99:84:73:1a:70:f2:ed:55:0e:ee:17:06:ea:67:ee:32:eb: + 2c:dd:67:07:3f:f6:8b:c2:70:de:5b:00:e6:bb:1b:d3:36:1a: + 22:6c:6c:b0:35:42:6c:90:09:3d:93:e9:64:09:22:0e:85:06: + 9f:c2:73:21:d3:e6:5f:80:e4:8d:85:22:3a:73:03:b1:60:8e: + ae:68:e2:f4:3e:97:e7:60:12:09:68:36:de:3a:d6:e2:43:95: + 5b:37:81:92:81:1f:bb:8d:d7:ad:52:64:16:57:96:d9:5e:34: + 7e:c8:35:d8 +-----BEGIN CERTIFICATE----- +MIID8DCCAtigAwIBAgIDAjqDMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTMwNDA1MTUxNTU2WhcNMTYxMjMxMjM1OTU5WjBJMQswCQYDVQQG +EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy +bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP +VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv +h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE +ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ +EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC +DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7 +qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD +VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov +L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig +JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ +MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEAqvqpIM1qZ4PtXtR+ +3h3Ef+AlBgDFJPupyC1tft6dgmUsgWM0Zj7pUsIItMsv91+ZOmqcUHqFBYx90SpI +hNMJbHzCzTWf84LuUt5oX+QAihcglvcpjZpNy6jehsgNb1aHA30DP9z6eX0hGfnI +Oi9RdozHQZJxjyXON/hKTAAj78Q1EK7gI4BzfE00LshukNYQHpmEcxpw8u1VDu4X +Bupn7jLrLN1nBz/2i8Jw3lsA5rsb0zYaImxssDVCbJAJPZPpZAkiDoUGn8JzIdPm +X4DkjYUiOnMDsWCOrmji9D6X52ASCWg23jrW4kOVWzeBkoEfu43XrVJkFleW2V40 +fsg12A== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert3[] = { + 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, + 0x34, 0x30, 0x35, 0x31, 0x35, 0x31, 0x35, 0x35, 0x36, 0x5a, 0x17, 0x0d, + 0x31, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, + 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, + 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3, + 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a, + 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a, + 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f, + 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1, + 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4, + 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53, + 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f, + 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73, + 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b, + 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb, + 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4, + 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19, + 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65, + 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80, + 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99, + 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75, + 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e, + 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf, + 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2, + 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37, + 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, + 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81, + 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, + 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, + 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10, + 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, + 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0xaa, 0xfa, 0xa9, 0x20, 0xcd, 0x6a, 0x67, 0x83, 0xed, 0x5e, 0xd4, 0x7e, + 0xde, 0x1d, 0xc4, 0x7f, 0xe0, 0x25, 0x06, 0x00, 0xc5, 0x24, 0xfb, 0xa9, + 0xc8, 0x2d, 0x6d, 0x7e, 0xde, 0x9d, 0x82, 0x65, 0x2c, 0x81, 0x63, 0x34, + 0x66, 0x3e, 0xe9, 0x52, 0xc2, 0x08, 0xb4, 0xcb, 0x2f, 0xf7, 0x5f, 0x99, + 0x3a, 0x6a, 0x9c, 0x50, 0x7a, 0x85, 0x05, 0x8c, 0x7d, 0xd1, 0x2a, 0x48, + 0x84, 0xd3, 0x09, 0x6c, 0x7c, 0xc2, 0xcd, 0x35, 0x9f, 0xf3, 0x82, 0xee, + 0x52, 0xde, 0x68, 0x5f, 0xe4, 0x00, 0x8a, 0x17, 0x20, 0x96, 0xf7, 0x29, + 0x8d, 0x9a, 0x4d, 0xcb, 0xa8, 0xde, 0x86, 0xc8, 0x0d, 0x6f, 0x56, 0x87, + 0x03, 0x7d, 0x03, 0x3f, 0xdc, 0xfa, 0x79, 0x7d, 0x21, 0x19, 0xf9, 0xc8, + 0x3a, 0x2f, 0x51, 0x76, 0x8c, 0xc7, 0x41, 0x92, 0x71, 0x8f, 0x25, 0xce, + 0x37, 0xf8, 0x4a, 0x4c, 0x00, 0x23, 0xef, 0xc4, 0x35, 0x10, 0xae, 0xe0, + 0x23, 0x80, 0x73, 0x7c, 0x4d, 0x34, 0x2e, 0xc8, 0x6e, 0x90, 0xd6, 0x10, + 0x1e, 0x99, 0x84, 0x73, 0x1a, 0x70, 0xf2, 0xed, 0x55, 0x0e, 0xee, 0x17, + 0x06, 0xea, 0x67, 0xee, 0x32, 0xeb, 0x2c, 0xdd, 0x67, 0x07, 0x3f, 0xf6, + 0x8b, 0xc2, 0x70, 0xde, 0x5b, 0x00, 0xe6, 0xbb, 0x1b, 0xd3, 0x36, 0x1a, + 0x22, 0x6c, 0x6c, 0xb0, 0x35, 0x42, 0x6c, 0x90, 0x09, 0x3d, 0x93, 0xe9, + 0x64, 0x09, 0x22, 0x0e, 0x85, 0x06, 0x9f, 0xc2, 0x73, 0x21, 0xd3, 0xe6, + 0x5f, 0x80, 0xe4, 0x8d, 0x85, 0x22, 0x3a, 0x73, 0x03, 0xb1, 0x60, 0x8e, + 0xae, 0x68, 0xe2, 0xf4, 0x3e, 0x97, 0xe7, 0x60, 0x12, 0x09, 0x68, 0x36, + 0xde, 0x3a, 0xd6, 0xe2, 0x43, 0x95, 0x5b, 0x37, 0x81, 0x92, 0x81, 0x1f, + 0xbb, 0x8d, 0xd7, 0xad, 0x52, 0x64, 0x16, 0x57, 0x96, 0xd9, 0x5e, 0x34, + 0x7e, 0xc8, 0x35, 0xd8, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120033005 (0x7278eed) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root + Validity + Not Before: Apr 18 16:36:18 2012 GMT + Not After : Aug 13 16:35:17 2018 GMT + Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79: + d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a: + 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2: + 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01: + 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7: + 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6: + 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c: + a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70: + 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77: + d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae: + 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18: + 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85: + ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9: + 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5: + c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a: + ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0: + 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27: + 1a:39 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:3 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://cybertrust.omniroot.com/repository + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Authority Key Identifier: + DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root + serial:01:A5 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl + + Signature Algorithm: sha1WithRSAEncryption + 93:1d:fe:8b:ae:46:ec:cb:a9:0f:ab:e5:ef:ca:b2:68:16:68: + d8:8f:fa:13:a9:af:b3:cb:2d:e7:4b:6e:8e:69:2a:c2:2b:10: + 0a:8d:f6:ae:73:b6:b9:fb:14:fd:5f:6d:b8:50:b6:c4:8a:d6: + 40:7e:d7:c3:cb:73:dc:c9:5d:5b:af:b0:41:b5:37:eb:ea:dc: + 20:91:c4:34:6a:f4:a1:f3:96:9d:37:86:97:e1:71:a4:dd:7d: + fa:44:84:94:ae:d7:09:04:22:76:0f:64:51:35:a9:24:0f:f9: + 0b:db:32:da:c2:fe:c1:b9:2a:5c:7a:27:13:ca:b1:48:3a:71: + d0:43 +-----BEGIN CERTIFICATE----- +MIIEFTCCA36gAwIBAgIEByeO7TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MB4XDTEyMDQxODE2MzYxOFoXDTE4MDgxMzE2MzUxN1owWjELMAkG +A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz +dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO +KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn +c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP +wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg +kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc +B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAUcw +ggFDMBIGA1UdEwEB/wQIMAYBAf8CAQMwSgYDVR0gBEMwQTA/BgRVHSAAMDcwNQYI +KwYBBQUHAgEWKWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0 +b3J5MA4GA1UdDwEB/wQEAwIBBjCBiQYDVR0jBIGBMH+heaR3MHUxCzAJBgNVBAYT +AlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJl +clRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg +R2xvYmFsIFJvb3SCAgGlMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVi +bGljLXRydXN0LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwDQYJKoZIhvcN +AQEFBQADgYEAkx3+i65G7MupD6vl78qyaBZo2I/6E6mvs8st50tujmkqwisQCo32 +rnO2ufsU/V9tuFC2xIrWQH7Xw8tz3MldW6+wQbU36+rcIJHENGr0ofOWnTeGl+Fx +pN19+kSElK7XCQQidg9kUTWpJA/5C9sy2sL+wbkqXHonE8qxSDpx0EM= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert4[] = { + 0x30, 0x82, 0x04, 0x15, 0x30, 0x82, 0x03, 0x7e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0x8e, 0xed, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, + 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, + 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x32, 0x30, 0x34, 0x31, 0x38, 0x31, 0x36, 0x33, 0x36, 0x31, + 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x33, 0x31, 0x36, + 0x33, 0x35, 0x31, 0x37, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69, + 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79, + 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, + 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, + 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04, + 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79, + 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e, + 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d, + 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12, + 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88, + 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7, + 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5, + 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab, + 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5, + 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f, + 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77, + 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0, + 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd, + 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0, + 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4, + 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9, + 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c, + 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c, + 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b, + 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76, + 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27, + 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x47, 0x30, + 0x82, 0x01, 0x43, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30, + 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f, + 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x37, 0x30, 0x35, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x29, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x81, 0x89, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77, + 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, + 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, + 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82, + 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e, + 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52, + 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x93, 0x1d, 0xfe, + 0x8b, 0xae, 0x46, 0xec, 0xcb, 0xa9, 0x0f, 0xab, 0xe5, 0xef, 0xca, 0xb2, + 0x68, 0x16, 0x68, 0xd8, 0x8f, 0xfa, 0x13, 0xa9, 0xaf, 0xb3, 0xcb, 0x2d, + 0xe7, 0x4b, 0x6e, 0x8e, 0x69, 0x2a, 0xc2, 0x2b, 0x10, 0x0a, 0x8d, 0xf6, + 0xae, 0x73, 0xb6, 0xb9, 0xfb, 0x14, 0xfd, 0x5f, 0x6d, 0xb8, 0x50, 0xb6, + 0xc4, 0x8a, 0xd6, 0x40, 0x7e, 0xd7, 0xc3, 0xcb, 0x73, 0xdc, 0xc9, 0x5d, + 0x5b, 0xaf, 0xb0, 0x41, 0xb5, 0x37, 0xeb, 0xea, 0xdc, 0x20, 0x91, 0xc4, + 0x34, 0x6a, 0xf4, 0xa1, 0xf3, 0x96, 0x9d, 0x37, 0x86, 0x97, 0xe1, 0x71, + 0xa4, 0xdd, 0x7d, 0xfa, 0x44, 0x84, 0x94, 0xae, 0xd7, 0x09, 0x04, 0x22, + 0x76, 0x0f, 0x64, 0x51, 0x35, 0xa9, 0x24, 0x0f, 0xf9, 0x0b, 0xdb, 0x32, + 0xda, 0xc2, 0xfe, 0xc1, 0xb9, 0x2a, 0x5c, 0x7a, 0x27, 0x13, 0xca, 0xb1, + 0x48, 0x3a, 0x71, 0xd0, 0x43, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146041 (0x23a79) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Sep 8 20:41:10 2014 GMT + Not After : May 20 20:41:10 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9a:7d:98:68:11:40:c1:5f:72:ec:55:b3:b1:63: + f3:32:22:72:91:c6:16:05:bb:08:82:31:b4:f6:ee: + d4:18:39:11:2f:2e:da:47:fe:51:31:6e:5b:f2:a9: + 0a:eb:2f:bb:f5:61:59:65:57:02:cd:80:ff:c7:70: + 32:54:89:fd:db:ae:99:72:d4:4f:0c:26:b9:2e:63: + 30:7d:de:14:5b:6a:d7:52:78:21:f9:bf:bc:50:d5: + 54:12:59:d8:b5:36:d9:21:47:b8:3f:6a:58:1d:8c: + 72:e1:97:95:d3:e1:45:a8:f1:5a:e5:be:fe:e3:53: + 7c:a5:f0:52:e0:cf:39:94:0c:19:71:f2:c0:25:07: + 48:7d:1c:e6:f1:39:25:2f:98:79:43:e8:18:72:f4: + 65:86:98:5a:00:04:47:da:4b:58:1e:7c:86:b1:4b: + 35:a6:20:00:1c:cd:1b:3b:22:5d:d1:93:28:33:12: + 23:94:08:aa:c3:3a:f5:d1:c6:8c:7e:99:d3:18:a0: + ad:9d:18:cf:49:ad:10:03:f7:99:33:26:86:46:9a: + 2f:a0:ba:6c:6e:c8:88:02:b7:6e:fa:7a:9e:98:4a: + ee:9a:31:7d:19:14:60:0c:ec:8f:20:23:3c:da:97: + 26:b6:ea:80:6c:8a:57:9e:20:ee:6f:17:25:4a:32: + ad:35 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + AC:32:ED:5A:C9:E0:DE:30:9C:90:58:55:26:63:F6:72:A6:54:5F:E3 + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + Signature Algorithm: sha256WithRSAEncryption + 61:40:ad:21:0f:03:bb:95:dc:89:fc:a3:cb:05:71:e9:1c:59: + 97:35:c2:fa:6b:05:a4:16:c6:56:46:37:74:1b:1b:f1:3e:2c: + e8:37:19:b7:94:d2:0f:0e:c5:bf:14:07:2b:34:cd:5b:b4:8d: + c7:56:9d:19:fc:02:b4:9e:90:31:fa:a4:44:c6:75:dd:dd:1f: + 25:54:a3:30:4c:ac:db:fe:c4:88:f7:31:26:18:47:ae:4c:20: + 19:1a:c7:ae:3e:98:0a:16:3d:d2:c2:a6:5d:0d:2e:29:7d:b2: + 9d:c7:41:32:17:ca:9d:ae:39:bf:91:98:de:e7:44:e2:95:9c: + 94:5c:6c:42:1b:59:c9:7b:68:13:a8:96:09:74:ee:40:14:a4: + d5:d7:c9:7b:33:a3:0f:5a:69:9c:1a:fa:6f:12:47:1c:df:1e: + 4c:70:4e:6d:dd:fe:1c:87:b5:9d:e1:54:07:09:8a:cd:be:aa: + a8:46:78:6e:16:f2:e7:91:0e:c3:af:da:76:00:d1:d8:a2:46: + 24:03:a5:1a:85:81:56:83:63:27:ba:90:8e:f9:62:11:ba:a7: + 7c:90:a9:1a:66:b4:c5:bc:8f:29:41:ab:eb:8d:99:a6:cc:91: + 64:ba:dc:c6:a6:4c:b3:b4:23:26:51:72:56:f9:f3:74:55:9f: + 25:75:4f:2b +-----BEGIN CERTIFICATE----- +MIIEIjCCAwqgAwIBAgIDAjp5MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTQwOTA4MjA0MTEwWhcNMjIwNTIwMjA0MTEwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +U1NMIENBIC0gRzQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCafZho +EUDBX3LsVbOxY/MyInKRxhYFuwiCMbT27tQYOREvLtpH/lExblvyqQrrL7v1YVll +VwLNgP/HcDJUif3brply1E8MJrkuYzB93hRbatdSeCH5v7xQ1VQSWdi1NtkhR7g/ +algdjHLhl5XT4UWo8Vrlvv7jU3yl8FLgzzmUDBlx8sAlB0h9HObxOSUvmHlD6Bhy +9GWGmFoABEfaS1gefIaxSzWmIAAczRs7Il3RkygzEiOUCKrDOvXRxox+mdMYoK2d +GM9JrRAD95kzJoZGmi+gumxuyIgCt276ep6YSu6aMX0ZFGAM7I8gIzzalya26oBs +ileeIO5vFyVKMq01AgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjAdBgNVHQ4EFgQUrDLtWsng3jCckFhVJmP2cqZUX+MwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCigJoYk +aHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUFBwEB +BCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARFMEMw +QQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0 +LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQBhQK0hDwO7ldyJ +/KPLBXHpHFmXNcL6awWkFsZWRjd0GxvxPizoNxm3lNIPDsW/FAcrNM1btI3HVp0Z +/AK0npAx+qRExnXd3R8lVKMwTKzb/sSI9zEmGEeuTCAZGseuPpgKFj3SwqZdDS4p +fbKdx0EyF8qdrjm/kZje50TilZyUXGxCG1nJe2gTqJYJdO5AFKTV18l7M6MPWmmc +GvpvEkcc3x5McE5t3f4ch7Wd4VQHCYrNvqqoRnhuFvLnkQ7Dr9p2ANHYokYkA6Ua +hYFWg2MnupCO+WIRuqd8kKkaZrTFvI8pQavrjZmmzJFkutzGpkyztCMmUXJW+fN0 +VZ8ldU8r +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert5[] = { + 0x30, 0x82, 0x04, 0x22, 0x30, 0x82, 0x03, 0x0a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x79, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x39, 0x30, 0x38, 0x32, 0x30, 0x34, 0x31, 0x31, 0x30, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x30, 0x34, 0x31, 0x31, 0x30, + 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9a, 0x7d, 0x98, 0x68, + 0x11, 0x40, 0xc1, 0x5f, 0x72, 0xec, 0x55, 0xb3, 0xb1, 0x63, 0xf3, 0x32, + 0x22, 0x72, 0x91, 0xc6, 0x16, 0x05, 0xbb, 0x08, 0x82, 0x31, 0xb4, 0xf6, + 0xee, 0xd4, 0x18, 0x39, 0x11, 0x2f, 0x2e, 0xda, 0x47, 0xfe, 0x51, 0x31, + 0x6e, 0x5b, 0xf2, 0xa9, 0x0a, 0xeb, 0x2f, 0xbb, 0xf5, 0x61, 0x59, 0x65, + 0x57, 0x02, 0xcd, 0x80, 0xff, 0xc7, 0x70, 0x32, 0x54, 0x89, 0xfd, 0xdb, + 0xae, 0x99, 0x72, 0xd4, 0x4f, 0x0c, 0x26, 0xb9, 0x2e, 0x63, 0x30, 0x7d, + 0xde, 0x14, 0x5b, 0x6a, 0xd7, 0x52, 0x78, 0x21, 0xf9, 0xbf, 0xbc, 0x50, + 0xd5, 0x54, 0x12, 0x59, 0xd8, 0xb5, 0x36, 0xd9, 0x21, 0x47, 0xb8, 0x3f, + 0x6a, 0x58, 0x1d, 0x8c, 0x72, 0xe1, 0x97, 0x95, 0xd3, 0xe1, 0x45, 0xa8, + 0xf1, 0x5a, 0xe5, 0xbe, 0xfe, 0xe3, 0x53, 0x7c, 0xa5, 0xf0, 0x52, 0xe0, + 0xcf, 0x39, 0x94, 0x0c, 0x19, 0x71, 0xf2, 0xc0, 0x25, 0x07, 0x48, 0x7d, + 0x1c, 0xe6, 0xf1, 0x39, 0x25, 0x2f, 0x98, 0x79, 0x43, 0xe8, 0x18, 0x72, + 0xf4, 0x65, 0x86, 0x98, 0x5a, 0x00, 0x04, 0x47, 0xda, 0x4b, 0x58, 0x1e, + 0x7c, 0x86, 0xb1, 0x4b, 0x35, 0xa6, 0x20, 0x00, 0x1c, 0xcd, 0x1b, 0x3b, + 0x22, 0x5d, 0xd1, 0x93, 0x28, 0x33, 0x12, 0x23, 0x94, 0x08, 0xaa, 0xc3, + 0x3a, 0xf5, 0xd1, 0xc6, 0x8c, 0x7e, 0x99, 0xd3, 0x18, 0xa0, 0xad, 0x9d, + 0x18, 0xcf, 0x49, 0xad, 0x10, 0x03, 0xf7, 0x99, 0x33, 0x26, 0x86, 0x46, + 0x9a, 0x2f, 0xa0, 0xba, 0x6c, 0x6e, 0xc8, 0x88, 0x02, 0xb7, 0x6e, 0xfa, + 0x7a, 0x9e, 0x98, 0x4a, 0xee, 0x9a, 0x31, 0x7d, 0x19, 0x14, 0x60, 0x0c, + 0xec, 0x8f, 0x20, 0x23, 0x3c, 0xda, 0x97, 0x26, 0xb6, 0xea, 0x80, 0x6c, + 0x8a, 0x57, 0x9e, 0x20, 0xee, 0x6f, 0x17, 0x25, 0x4a, 0x32, 0xad, 0x35, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, + 0x19, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, + 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xac, 0x32, 0xed, + 0x5a, 0xc9, 0xe0, 0xde, 0x30, 0x9c, 0x90, 0x58, 0x55, 0x26, 0x63, 0xf6, + 0x72, 0xa6, 0x54, 0x5f, 0xe3, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, + 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, + 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, + 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, + 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x61, 0x40, 0xad, 0x21, 0x0f, 0x03, 0xbb, 0x95, 0xdc, 0x89, + 0xfc, 0xa3, 0xcb, 0x05, 0x71, 0xe9, 0x1c, 0x59, 0x97, 0x35, 0xc2, 0xfa, + 0x6b, 0x05, 0xa4, 0x16, 0xc6, 0x56, 0x46, 0x37, 0x74, 0x1b, 0x1b, 0xf1, + 0x3e, 0x2c, 0xe8, 0x37, 0x19, 0xb7, 0x94, 0xd2, 0x0f, 0x0e, 0xc5, 0xbf, + 0x14, 0x07, 0x2b, 0x34, 0xcd, 0x5b, 0xb4, 0x8d, 0xc7, 0x56, 0x9d, 0x19, + 0xfc, 0x02, 0xb4, 0x9e, 0x90, 0x31, 0xfa, 0xa4, 0x44, 0xc6, 0x75, 0xdd, + 0xdd, 0x1f, 0x25, 0x54, 0xa3, 0x30, 0x4c, 0xac, 0xdb, 0xfe, 0xc4, 0x88, + 0xf7, 0x31, 0x26, 0x18, 0x47, 0xae, 0x4c, 0x20, 0x19, 0x1a, 0xc7, 0xae, + 0x3e, 0x98, 0x0a, 0x16, 0x3d, 0xd2, 0xc2, 0xa6, 0x5d, 0x0d, 0x2e, 0x29, + 0x7d, 0xb2, 0x9d, 0xc7, 0x41, 0x32, 0x17, 0xca, 0x9d, 0xae, 0x39, 0xbf, + 0x91, 0x98, 0xde, 0xe7, 0x44, 0xe2, 0x95, 0x9c, 0x94, 0x5c, 0x6c, 0x42, + 0x1b, 0x59, 0xc9, 0x7b, 0x68, 0x13, 0xa8, 0x96, 0x09, 0x74, 0xee, 0x40, + 0x14, 0xa4, 0xd5, 0xd7, 0xc9, 0x7b, 0x33, 0xa3, 0x0f, 0x5a, 0x69, 0x9c, + 0x1a, 0xfa, 0x6f, 0x12, 0x47, 0x1c, 0xdf, 0x1e, 0x4c, 0x70, 0x4e, 0x6d, + 0xdd, 0xfe, 0x1c, 0x87, 0xb5, 0x9d, 0xe1, 0x54, 0x07, 0x09, 0x8a, 0xcd, + 0xbe, 0xaa, 0xa8, 0x46, 0x78, 0x6e, 0x16, 0xf2, 0xe7, 0x91, 0x0e, 0xc3, + 0xaf, 0xda, 0x76, 0x00, 0xd1, 0xd8, 0xa2, 0x46, 0x24, 0x03, 0xa5, 0x1a, + 0x85, 0x81, 0x56, 0x83, 0x63, 0x27, 0xba, 0x90, 0x8e, 0xf9, 0x62, 0x11, + 0xba, 0xa7, 0x7c, 0x90, 0xa9, 0x1a, 0x66, 0xb4, 0xc5, 0xbc, 0x8f, 0x29, + 0x41, 0xab, 0xeb, 0x8d, 0x99, 0xa6, 0xcc, 0x91, 0x64, 0xba, 0xdc, 0xc6, + 0xa6, 0x4c, 0xb3, 0xb4, 0x23, 0x26, 0x51, 0x72, 0x56, 0xf9, 0xf3, 0x74, + 0x55, 0x9f, 0x25, 0x75, 0x4f, 0x2b, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146039 (0x23a77) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Aug 29 21:39:32 2014 GMT + Not After : May 20 21:39:32 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:54:9b:d9:58:5d:1e:2c:56:c6:d5:e8:7f:f4: + 7d:16:03:ff:d0:8b:5a:e4:8e:a7:dd:54:2e:d4:04: + c0:5d:98:9c:8d:90:0f:bc:10:65:5f:da:9a:d6:44: + 7c:c0:9f:b5:e9:4a:8c:0b:06:43:04:bb:f4:96:e2: + 26:f6:61:01:91:66:31:22:c3:34:34:5f:3f:3f:91: + 2f:44:5f:dc:c7:14:b6:03:9f:86:4b:0e:a3:ff:a0: + 80:02:83:c3:d3:1f:69:52:d6:9d:64:0f:c9:83:e7: + 1b:c4:70:ac:94:e7:c3:a4:6a:2c:bd:b8:9e:69:d8: + be:0a:8f:16:63:5a:68:71:80:7b:30:de:15:04:bf: + cc:d3:bf:3e:48:05:55:7a:b3:d7:10:0c:03:fc:9b: + fd:08:a7:8c:8c:db:a7:8e:f1:1e:63:dc:b3:01:2f: + 7f:af:57:c3:3c:48:a7:83:68:21:a7:2f:e7:a7:3f: + f0:b5:0c:fc:f5:84:d1:53:bc:0e:72:4f:60:0c:42: + b8:98:ad:19:88:57:d7:04:ec:87:bf:7e:87:4e:a3: + 21:f9:53:fd:36:98:48:8d:d6:f8:bb:48:f2:29:c8: + 64:d1:cc:54:48:53:8b:af:b7:65:1e:bf:29:33:29: + d9:29:60:48:f8:ff:91:bc:57:58:e5:35:2e:bb:69: + b6:59 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + C3:9C:F3:FC:D3:46:08:34:BB:CE:46:7F:A0:7C:5B:F3:E2:08:CB:59 + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + Signature Algorithm: sha256WithRSAEncryption + a3:58:1e:c6:43:32:ac:ac:2f:93:78:b7:ea:ae:54:40:47:2d: + 7e:78:8d:50:f6:f8:66:ac:d6:4f:73:d6:44:ef:af:0b:cc:5b: + c1:f4:4f:9a:8f:49:7e:60:af:c2:27:c7:16:f1:fb:93:81:90: + a9:7c:ef:6f:7e:6e:45:94:16:84:bd:ec:49:f1:c4:0e:f4:af: + 04:59:83:87:0f:2c:3b:97:c3:5a:12:9b:7b:04:35:7b:a3:95: + 33:08:7b:93:71:22:42:b3:a9:d9:6f:4f:81:92:fc:07:b6:79: + bc:84:4a:9d:77:09:f1:c5:89:f2:f0:b4:9c:54:aa:12:7b:0d: + ba:4f:ef:93:19:ec:ef:7d:4e:61:a3:8e:76:9c:59:cf:8c:94: + b1:84:97:f7:1a:b9:07:b8:b2:c6:4f:13:79:db:bf:4f:51:1b: + 7f:69:0d:51:2a:c1:d6:15:ff:37:51:34:65:51:f4:1e:be:38: + 6a:ec:0e:ab:bf:3d:7b:39:05:7b:f4:f3:fb:1a:a1:d0:c8:7e: + 4e:64:8d:cd:8c:61:55:90:fe:3a:ca:5d:25:0f:f8:1d:a3:4a: + 74:56:4f:1a:55:40:70:75:25:a6:33:2e:ba:4b:a5:5d:53:9a: + 0d:30:e1:8d:5f:61:2c:af:cc:ef:b0:99:a1:80:ff:0b:f2:62: + 4c:70:26:98 +-----BEGIN CERTIFICATE----- +MIIEJTCCAw2gAwIBAgIDAjp3MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTQwODI5MjEzOTMyWhcNMjIwNTIwMjEzOTMyWjBHMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXUmFwaWRTU0wg +U0hBMjU2IENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv +VJvZWF0eLFbG1eh/9H0WA//Qi1rkjqfdVC7UBMBdmJyNkA+8EGVf2prWRHzAn7Xp +SowLBkMEu/SW4ib2YQGRZjEiwzQ0Xz8/kS9EX9zHFLYDn4ZLDqP/oIACg8PTH2lS +1p1kD8mD5xvEcKyU58Okaiy9uJ5p2L4KjxZjWmhxgHsw3hUEv8zTvz5IBVV6s9cQ +DAP8m/0Ip4yM26eO8R5j3LMBL3+vV8M8SKeDaCGnL+enP/C1DPz1hNFTvA5yT2AM +QriYrRmIV9cE7Ie/fodOoyH5U/02mEiN1vi7SPIpyGTRzFRIU4uvt2UevykzKdkp +YEj4/5G8V1jlNS67abZZAgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7 +qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUw5zz/NNGCDS7zkZ/oHxb8+IIy1kwEgYD +VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig +JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF +BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARF +MEMwQQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3Ry +dXN0LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQCjWB7GQzKs +rC+TeLfqrlRARy1+eI1Q9vhmrNZPc9ZE768LzFvB9E+aj0l+YK/CJ8cW8fuTgZCp +fO9vfm5FlBaEvexJ8cQO9K8EWYOHDyw7l8NaEpt7BDV7o5UzCHuTcSJCs6nZb0+B +kvwHtnm8hEqddwnxxYny8LScVKoSew26T++TGezvfU5ho452nFnPjJSxhJf3GrkH +uLLGTxN5279PURt/aQ1RKsHWFf83UTRlUfQevjhq7A6rvz17OQV79PP7GqHQyH5O +ZI3NjGFVkP46yl0lD/gdo0p0Vk8aVUBwdSWmMy66S6VdU5oNMOGNX2Esr8zvsJmh +gP8L8mJMcCaY +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert6[] = { + 0x30, 0x82, 0x04, 0x25, 0x30, 0x82, 0x03, 0x0d, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x77, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x38, 0x32, 0x39, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32, + 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20, + 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, + 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, + 0x54, 0x9b, 0xd9, 0x58, 0x5d, 0x1e, 0x2c, 0x56, 0xc6, 0xd5, 0xe8, 0x7f, + 0xf4, 0x7d, 0x16, 0x03, 0xff, 0xd0, 0x8b, 0x5a, 0xe4, 0x8e, 0xa7, 0xdd, + 0x54, 0x2e, 0xd4, 0x04, 0xc0, 0x5d, 0x98, 0x9c, 0x8d, 0x90, 0x0f, 0xbc, + 0x10, 0x65, 0x5f, 0xda, 0x9a, 0xd6, 0x44, 0x7c, 0xc0, 0x9f, 0xb5, 0xe9, + 0x4a, 0x8c, 0x0b, 0x06, 0x43, 0x04, 0xbb, 0xf4, 0x96, 0xe2, 0x26, 0xf6, + 0x61, 0x01, 0x91, 0x66, 0x31, 0x22, 0xc3, 0x34, 0x34, 0x5f, 0x3f, 0x3f, + 0x91, 0x2f, 0x44, 0x5f, 0xdc, 0xc7, 0x14, 0xb6, 0x03, 0x9f, 0x86, 0x4b, + 0x0e, 0xa3, 0xff, 0xa0, 0x80, 0x02, 0x83, 0xc3, 0xd3, 0x1f, 0x69, 0x52, + 0xd6, 0x9d, 0x64, 0x0f, 0xc9, 0x83, 0xe7, 0x1b, 0xc4, 0x70, 0xac, 0x94, + 0xe7, 0xc3, 0xa4, 0x6a, 0x2c, 0xbd, 0xb8, 0x9e, 0x69, 0xd8, 0xbe, 0x0a, + 0x8f, 0x16, 0x63, 0x5a, 0x68, 0x71, 0x80, 0x7b, 0x30, 0xde, 0x15, 0x04, + 0xbf, 0xcc, 0xd3, 0xbf, 0x3e, 0x48, 0x05, 0x55, 0x7a, 0xb3, 0xd7, 0x10, + 0x0c, 0x03, 0xfc, 0x9b, 0xfd, 0x08, 0xa7, 0x8c, 0x8c, 0xdb, 0xa7, 0x8e, + 0xf1, 0x1e, 0x63, 0xdc, 0xb3, 0x01, 0x2f, 0x7f, 0xaf, 0x57, 0xc3, 0x3c, + 0x48, 0xa7, 0x83, 0x68, 0x21, 0xa7, 0x2f, 0xe7, 0xa7, 0x3f, 0xf0, 0xb5, + 0x0c, 0xfc, 0xf5, 0x84, 0xd1, 0x53, 0xbc, 0x0e, 0x72, 0x4f, 0x60, 0x0c, + 0x42, 0xb8, 0x98, 0xad, 0x19, 0x88, 0x57, 0xd7, 0x04, 0xec, 0x87, 0xbf, + 0x7e, 0x87, 0x4e, 0xa3, 0x21, 0xf9, 0x53, 0xfd, 0x36, 0x98, 0x48, 0x8d, + 0xd6, 0xf8, 0xbb, 0x48, 0xf2, 0x29, 0xc8, 0x64, 0xd1, 0xcc, 0x54, 0x48, + 0x53, 0x8b, 0xaf, 0xb7, 0x65, 0x1e, 0xbf, 0x29, 0x33, 0x29, 0xd9, 0x29, + 0x60, 0x48, 0xf8, 0xff, 0x91, 0xbc, 0x57, 0x58, 0xe5, 0x35, 0x2e, 0xbb, + 0x69, 0xb6, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, + 0x30, 0x82, 0x01, 0x19, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xc3, 0x9c, 0xf3, 0xfc, 0xd3, 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, 0x7f, + 0xa0, 0x7c, 0x5b, 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, + 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, + 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, + 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, + 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x58, 0x1e, 0xc6, 0x43, 0x32, 0xac, + 0xac, 0x2f, 0x93, 0x78, 0xb7, 0xea, 0xae, 0x54, 0x40, 0x47, 0x2d, 0x7e, + 0x78, 0x8d, 0x50, 0xf6, 0xf8, 0x66, 0xac, 0xd6, 0x4f, 0x73, 0xd6, 0x44, + 0xef, 0xaf, 0x0b, 0xcc, 0x5b, 0xc1, 0xf4, 0x4f, 0x9a, 0x8f, 0x49, 0x7e, + 0x60, 0xaf, 0xc2, 0x27, 0xc7, 0x16, 0xf1, 0xfb, 0x93, 0x81, 0x90, 0xa9, + 0x7c, 0xef, 0x6f, 0x7e, 0x6e, 0x45, 0x94, 0x16, 0x84, 0xbd, 0xec, 0x49, + 0xf1, 0xc4, 0x0e, 0xf4, 0xaf, 0x04, 0x59, 0x83, 0x87, 0x0f, 0x2c, 0x3b, + 0x97, 0xc3, 0x5a, 0x12, 0x9b, 0x7b, 0x04, 0x35, 0x7b, 0xa3, 0x95, 0x33, + 0x08, 0x7b, 0x93, 0x71, 0x22, 0x42, 0xb3, 0xa9, 0xd9, 0x6f, 0x4f, 0x81, + 0x92, 0xfc, 0x07, 0xb6, 0x79, 0xbc, 0x84, 0x4a, 0x9d, 0x77, 0x09, 0xf1, + 0xc5, 0x89, 0xf2, 0xf0, 0xb4, 0x9c, 0x54, 0xaa, 0x12, 0x7b, 0x0d, 0xba, + 0x4f, 0xef, 0x93, 0x19, 0xec, 0xef, 0x7d, 0x4e, 0x61, 0xa3, 0x8e, 0x76, + 0x9c, 0x59, 0xcf, 0x8c, 0x94, 0xb1, 0x84, 0x97, 0xf7, 0x1a, 0xb9, 0x07, + 0xb8, 0xb2, 0xc6, 0x4f, 0x13, 0x79, 0xdb, 0xbf, 0x4f, 0x51, 0x1b, 0x7f, + 0x69, 0x0d, 0x51, 0x2a, 0xc1, 0xd6, 0x15, 0xff, 0x37, 0x51, 0x34, 0x65, + 0x51, 0xf4, 0x1e, 0xbe, 0x38, 0x6a, 0xec, 0x0e, 0xab, 0xbf, 0x3d, 0x7b, + 0x39, 0x05, 0x7b, 0xf4, 0xf3, 0xfb, 0x1a, 0xa1, 0xd0, 0xc8, 0x7e, 0x4e, + 0x64, 0x8d, 0xcd, 0x8c, 0x61, 0x55, 0x90, 0xfe, 0x3a, 0xca, 0x5d, 0x25, + 0x0f, 0xf8, 0x1d, 0xa3, 0x4a, 0x74, 0x56, 0x4f, 0x1a, 0x55, 0x40, 0x70, + 0x75, 0x25, 0xa6, 0x33, 0x2e, 0xba, 0x4b, 0xa5, 0x5d, 0x53, 0x9a, 0x0d, + 0x30, 0xe1, 0x8d, 0x5f, 0x61, 0x2c, 0xaf, 0xcc, 0xef, 0xb0, 0x99, 0xa1, + 0x80, 0xff, 0x0b, 0xf2, 0x62, 0x4c, 0x70, 0x26, 0x98, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 11:20:96:f6:c8:03:7c:9e:07:b1:38:bf:2e:72:10:8a:d7:ed + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, O=Certplus, CN=Class 2 Primary CA + Validity + Not Before: Jun 5 00:00:00 2007 GMT + Not After : Jun 20 00:00:00 2019 GMT + Subject: C=FR, O=KEYNECTIS, CN=CLASS 2 KEYNECTIS CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c6:be:fe:44:23:04:d4:ef:2f:3b:86:aa:35:58: + 81:d1:e1:9a:d6:b1:d4:27:45:28:fc:d1:1e:46:85: + ba:54:23:11:7d:e0:66:3f:d4:a3:57:66:78:f9:6b: + eb:74:7c:2a:b8:37:a5:e8:70:ae:82:b5:4e:d4:81: + fe:5b:e2:ea:e7:22:16:f8:f9:d7:ba:3a:f6:88:56: + dc:c4:f2:a0:a4:e5:75:06:60:72:2b:fb:f5:94:ee: + 2c:83:28:de:91:9a:b3:83:3a:b0:9f:08:fa:dd:d8: + 9e:8c:24:e6:df:66:5b:c8:7e:a3:62:4d:3f:3a:85: + 23:ec:e8:71:8f:0a:00:ac:89:6d:7e:d8:72:e5:dd: + c1:94:8e:5f:e4:73:e6:c1:c6:0c:87:58:4f:37:da: + d1:a9:88:26:76:b4:ee:11:8d:f6:ad:b2:a7:bc:73: + c4:cd:1c:6e:1a:e6:8d:72:56:44:a0:98:f7:92:f9: + d7:79:9b:03:e6:68:5f:a4:5c:7c:3d:50:b4:83:cc: + e5:ac:0d:e1:3e:4f:14:f2:b4:e4:7d:bf:71:a4:c3: + 97:73:38:d6:52:7c:c8:a4:b5:ea:e9:b2:54:56:d4: + eb:b8:57:3a:40:52:5a:5e:46:27:a3:7b:30:2d:08: + 3d:85:1e:9a:f0:32:a8:f2:10:a2:83:9b:e2:28:f6: + 9d:cb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 1.3.6.4.1.22234.2.5.3.3 + CPS: http://www.keynectis.com/PC + Policy: 1.3.6.4.1.22234.2.5.1.3 + CPS: http://www.keynectis.com/PC + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.certplus.com/CRL/class2.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 00:11:41:DF:3B:9D:3B:CB:B8:A2:C1:33:92:A8:81:CC:E5:7D:E7:99 + X509v3 Authority Key Identifier: + keyid:E3:73:2D:DF:CB:0E:28:0C:DE:DD:B3:A4:CA:79:B8:8E:BB:E8:30:89 + + Signature Algorithm: sha1WithRSAEncryption + 08:88:fe:1f:a2:ca:cd:e2:a0:f1:2e:7c:67:49:fb:dc:94:ac: + 7f:41:0d:78:01:ba:31:f7:9b:fb:31:18:77:2f:66:25:94:b8: + 6d:16:74:81:f1:c0:ae:67:c6:14:45:7a:01:d1:13:88:fc:e2: + 8d:22:1d:bd:1e:0c:c7:a9:7e:d0:c3:97:f6:37:5b:41:5e:67: + 94:8e:ab:69:02:17:18:f5:4d:38:c2:49:28:09:6e:5a:9b:a6: + 27:db:c0:5f:8f:44:9c:90:65:99:d8:b3:2e:c1:92:ee:1a:9d: + 0f:72:45:20:fa:2c:0c:9c:5d:cd:5b:54:41:54:4f:d3:e2:c7: + 59:84:3f:17:7b:7d:0e:c2:ef:62:c7:ba:b1:26:6c:83:4e:d3: + 19:c5:ff:56:a7:b4:45:3f:7a:9e:fa:d0:39:3e:80:46:75:5d: + 5a:79:7a:33:c5:01:bc:02:44:ce:1b:c0:31:4e:47:96:15:6e: + e7:e4:76:f0:c2:90:0d:a1:78:f4:38:00:91:2b:65:7c:79:13: + a8:3e:91:14:dc:88:05:08:d7:6f:53:f6:15:43:ee:c5:53:56: + 1a:02:b5:a6:a2:46:8d:1e:13:e4:67:c2:45:5f:40:5e:10:42: + 58:b5:cd:44:a3:94:4c:1c:54:90:4d:91:9a:26:8b:ad:a2:80: + 50:8d:14:14 +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgISESCW9sgDfJ4HsTi/LnIQitftMA0GCSqGSIb3DQEBBQUA +MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xh +c3MgMiBQcmltYXJ5IENBMB4XDTA3MDYwNTAwMDAwMFoXDTE5MDYyMDAwMDAwMFow +QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoTCUtFWU5FQ1RJUzEdMBsGA1UEAxMUQ0xB +U1MgMiBLRVlORUNUSVMgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDGvv5EIwTU7y87hqo1WIHR4ZrWsdQnRSj80R5GhbpUIxF94GY/1KNXZnj5a+t0 +fCq4N6XocK6CtU7Ugf5b4urnIhb4+de6OvaIVtzE8qCk5XUGYHIr+/WU7iyDKN6R +mrODOrCfCPrd2J6MJObfZlvIfqNiTT86hSPs6HGPCgCsiW1+2HLl3cGUjl/kc+bB +xgyHWE832tGpiCZ2tO4Rjfatsqe8c8TNHG4a5o1yVkSgmPeS+dd5mwPmaF+kXHw9 +ULSDzOWsDeE+TxTytOR9v3Gkw5dzONZSfMikterpslRW1Ou4VzpAUlpeRiejezAt +CD2FHprwMqjyEKKDm+Io9p3LAgMBAAGjggEgMIIBHDASBgNVHRMBAf8ECDAGAQH/ +AgEAMH0GA1UdIAR2MHQwOAYLKwYEAYGtWgIFAwMwKTAnBggrBgEFBQcCARYbaHR0 +cDovL3d3dy5rZXluZWN0aXMuY29tL1BDMDgGCysGBAGBrVoCBQEDMCkwJwYIKwYB +BQUHAgEWG2h0dHA6Ly93d3cua2V5bmVjdGlzLmNvbS9QQzA3BgNVHR8EMDAuMCyg +KqAohiZodHRwOi8vd3d3LmNlcnRwbHVzLmNvbS9DUkwvY2xhc3MyLmNybDAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAARQd87nTvLuKLBM5KogczlfeeZMB8GA1Ud +IwQYMBaAFONzLd/LDigM3t2zpMp5uI676DCJMA0GCSqGSIb3DQEBBQUAA4IBAQAI +iP4fosrN4qDxLnxnSfvclKx/QQ14Abox95v7MRh3L2YllLhtFnSB8cCuZ8YURXoB +0ROI/OKNIh29HgzHqX7Qw5f2N1tBXmeUjqtpAhcY9U04wkkoCW5am6Yn28Bfj0Sc +kGWZ2LMuwZLuGp0PckUg+iwMnF3NW1RBVE/T4sdZhD8Xe30Owu9ix7qxJmyDTtMZ +xf9Wp7RFP3qe+tA5PoBGdV1aeXozxQG8AkTOG8AxTkeWFW7n5HbwwpANoXj0OACR +K2V8eROoPpEU3IgFCNdvU/YVQ+7FU1YaArWmokaNHhPkZ8JFX0BeEEJYtc1Eo5RM +HFSQTZGaJoutooBQjRQU +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert7[] = { + 0x30, 0x82, 0x04, 0x2b, 0x30, 0x82, 0x03, 0x13, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x12, 0x11, 0x20, 0x96, 0xf6, 0xc8, 0x03, 0x7c, 0x9e, 0x07, + 0xb1, 0x38, 0xbf, 0x2e, 0x72, 0x10, 0x8a, 0xd7, 0xed, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x30, 0x3d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x46, 0x52, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x08, 0x43, 0x65, 0x72, 0x74, 0x70, 0x6c, 0x75, 0x73, 0x31, 0x1b, + 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x43, 0x6c, 0x61, + 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x30, 0x36, 0x30, + 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, + 0x30, 0x36, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, + 0x40, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x46, 0x52, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x09, 0x4b, 0x45, 0x59, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x53, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x14, 0x43, 0x4c, 0x41, + 0x53, 0x53, 0x20, 0x32, 0x20, 0x4b, 0x45, 0x59, 0x4e, 0x45, 0x43, 0x54, + 0x49, 0x53, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xc6, 0xbe, 0xfe, 0x44, 0x23, 0x04, 0xd4, 0xef, 0x2f, 0x3b, + 0x86, 0xaa, 0x35, 0x58, 0x81, 0xd1, 0xe1, 0x9a, 0xd6, 0xb1, 0xd4, 0x27, + 0x45, 0x28, 0xfc, 0xd1, 0x1e, 0x46, 0x85, 0xba, 0x54, 0x23, 0x11, 0x7d, + 0xe0, 0x66, 0x3f, 0xd4, 0xa3, 0x57, 0x66, 0x78, 0xf9, 0x6b, 0xeb, 0x74, + 0x7c, 0x2a, 0xb8, 0x37, 0xa5, 0xe8, 0x70, 0xae, 0x82, 0xb5, 0x4e, 0xd4, + 0x81, 0xfe, 0x5b, 0xe2, 0xea, 0xe7, 0x22, 0x16, 0xf8, 0xf9, 0xd7, 0xba, + 0x3a, 0xf6, 0x88, 0x56, 0xdc, 0xc4, 0xf2, 0xa0, 0xa4, 0xe5, 0x75, 0x06, + 0x60, 0x72, 0x2b, 0xfb, 0xf5, 0x94, 0xee, 0x2c, 0x83, 0x28, 0xde, 0x91, + 0x9a, 0xb3, 0x83, 0x3a, 0xb0, 0x9f, 0x08, 0xfa, 0xdd, 0xd8, 0x9e, 0x8c, + 0x24, 0xe6, 0xdf, 0x66, 0x5b, 0xc8, 0x7e, 0xa3, 0x62, 0x4d, 0x3f, 0x3a, + 0x85, 0x23, 0xec, 0xe8, 0x71, 0x8f, 0x0a, 0x00, 0xac, 0x89, 0x6d, 0x7e, + 0xd8, 0x72, 0xe5, 0xdd, 0xc1, 0x94, 0x8e, 0x5f, 0xe4, 0x73, 0xe6, 0xc1, + 0xc6, 0x0c, 0x87, 0x58, 0x4f, 0x37, 0xda, 0xd1, 0xa9, 0x88, 0x26, 0x76, + 0xb4, 0xee, 0x11, 0x8d, 0xf6, 0xad, 0xb2, 0xa7, 0xbc, 0x73, 0xc4, 0xcd, + 0x1c, 0x6e, 0x1a, 0xe6, 0x8d, 0x72, 0x56, 0x44, 0xa0, 0x98, 0xf7, 0x92, + 0xf9, 0xd7, 0x79, 0x9b, 0x03, 0xe6, 0x68, 0x5f, 0xa4, 0x5c, 0x7c, 0x3d, + 0x50, 0xb4, 0x83, 0xcc, 0xe5, 0xac, 0x0d, 0xe1, 0x3e, 0x4f, 0x14, 0xf2, + 0xb4, 0xe4, 0x7d, 0xbf, 0x71, 0xa4, 0xc3, 0x97, 0x73, 0x38, 0xd6, 0x52, + 0x7c, 0xc8, 0xa4, 0xb5, 0xea, 0xe9, 0xb2, 0x54, 0x56, 0xd4, 0xeb, 0xb8, + 0x57, 0x3a, 0x40, 0x52, 0x5a, 0x5e, 0x46, 0x27, 0xa3, 0x7b, 0x30, 0x2d, + 0x08, 0x3d, 0x85, 0x1e, 0x9a, 0xf0, 0x32, 0xa8, 0xf2, 0x10, 0xa2, 0x83, + 0x9b, 0xe2, 0x28, 0xf6, 0x9d, 0xcb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, + 0x02, 0x01, 0x00, 0x30, 0x7d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x76, + 0x30, 0x74, 0x30, 0x38, 0x06, 0x0b, 0x2b, 0x06, 0x04, 0x01, 0x81, 0xad, + 0x5a, 0x02, 0x05, 0x03, 0x03, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1b, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6b, 0x65, 0x79, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x43, + 0x30, 0x38, 0x06, 0x0b, 0x2b, 0x06, 0x04, 0x01, 0x81, 0xad, 0x5a, 0x02, + 0x05, 0x01, 0x03, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6b, 0x65, 0x79, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x43, 0x30, 0x37, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, 0x30, 0x2e, 0x30, 0x2c, 0xa0, + 0x2a, 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x70, 0x6c, 0x75, 0x73, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x00, 0x11, + 0x41, 0xdf, 0x3b, 0x9d, 0x3b, 0xcb, 0xb8, 0xa2, 0xc1, 0x33, 0x92, 0xa8, + 0x81, 0xcc, 0xe5, 0x7d, 0xe7, 0x99, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe3, 0x73, 0x2d, 0xdf, 0xcb, + 0x0e, 0x28, 0x0c, 0xde, 0xdd, 0xb3, 0xa4, 0xca, 0x79, 0xb8, 0x8e, 0xbb, + 0xe8, 0x30, 0x89, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x08, + 0x88, 0xfe, 0x1f, 0xa2, 0xca, 0xcd, 0xe2, 0xa0, 0xf1, 0x2e, 0x7c, 0x67, + 0x49, 0xfb, 0xdc, 0x94, 0xac, 0x7f, 0x41, 0x0d, 0x78, 0x01, 0xba, 0x31, + 0xf7, 0x9b, 0xfb, 0x31, 0x18, 0x77, 0x2f, 0x66, 0x25, 0x94, 0xb8, 0x6d, + 0x16, 0x74, 0x81, 0xf1, 0xc0, 0xae, 0x67, 0xc6, 0x14, 0x45, 0x7a, 0x01, + 0xd1, 0x13, 0x88, 0xfc, 0xe2, 0x8d, 0x22, 0x1d, 0xbd, 0x1e, 0x0c, 0xc7, + 0xa9, 0x7e, 0xd0, 0xc3, 0x97, 0xf6, 0x37, 0x5b, 0x41, 0x5e, 0x67, 0x94, + 0x8e, 0xab, 0x69, 0x02, 0x17, 0x18, 0xf5, 0x4d, 0x38, 0xc2, 0x49, 0x28, + 0x09, 0x6e, 0x5a, 0x9b, 0xa6, 0x27, 0xdb, 0xc0, 0x5f, 0x8f, 0x44, 0x9c, + 0x90, 0x65, 0x99, 0xd8, 0xb3, 0x2e, 0xc1, 0x92, 0xee, 0x1a, 0x9d, 0x0f, + 0x72, 0x45, 0x20, 0xfa, 0x2c, 0x0c, 0x9c, 0x5d, 0xcd, 0x5b, 0x54, 0x41, + 0x54, 0x4f, 0xd3, 0xe2, 0xc7, 0x59, 0x84, 0x3f, 0x17, 0x7b, 0x7d, 0x0e, + 0xc2, 0xef, 0x62, 0xc7, 0xba, 0xb1, 0x26, 0x6c, 0x83, 0x4e, 0xd3, 0x19, + 0xc5, 0xff, 0x56, 0xa7, 0xb4, 0x45, 0x3f, 0x7a, 0x9e, 0xfa, 0xd0, 0x39, + 0x3e, 0x80, 0x46, 0x75, 0x5d, 0x5a, 0x79, 0x7a, 0x33, 0xc5, 0x01, 0xbc, + 0x02, 0x44, 0xce, 0x1b, 0xc0, 0x31, 0x4e, 0x47, 0x96, 0x15, 0x6e, 0xe7, + 0xe4, 0x76, 0xf0, 0xc2, 0x90, 0x0d, 0xa1, 0x78, 0xf4, 0x38, 0x00, 0x91, + 0x2b, 0x65, 0x7c, 0x79, 0x13, 0xa8, 0x3e, 0x91, 0x14, 0xdc, 0x88, 0x05, + 0x08, 0xd7, 0x6f, 0x53, 0xf6, 0x15, 0x43, 0xee, 0xc5, 0x53, 0x56, 0x1a, + 0x02, 0xb5, 0xa6, 0xa2, 0x46, 0x8d, 0x1e, 0x13, 0xe4, 0x67, 0xc2, 0x45, + 0x5f, 0x40, 0x5e, 0x10, 0x42, 0x58, 0xb5, 0xcd, 0x44, 0xa3, 0x94, 0x4c, + 0x1c, 0x54, 0x90, 0x4d, 0x91, 0x9a, 0x26, 0x8b, 0xad, 0xa2, 0x80, 0x50, + 0x8d, 0x14, 0x14, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120024505 (0x7276db9) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root + Validity + Not Before: Nov 30 16:35:21 2010 GMT + Not After : Aug 10 15:34:26 2018 GMT + Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79: + d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a: + 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2: + 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01: + 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7: + 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6: + 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c: + a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70: + 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77: + d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae: + 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18: + 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85: + ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9: + 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5: + c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a: + ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0: + 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27: + 1a:39 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:3 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://cybertrust.omniroot.com/repository.cfm + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Authority Key Identifier: + DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root + serial:01:A5 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl + + X509v3 Subject Key Identifier: + E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + Signature Algorithm: sha1WithRSAEncryption + 16:b4:2c:c9:f1:5e:e1:a2:7b:9b:78:20:7a:4a:70:70:86:19: + 00:b7:05:2a:e8:c9:25:39:0f:c3:64:3c:75:09:d9:89:15:80: + 07:c2:8d:bc:29:a5:64:50:cf:71:75:47:23:bd:4d:d8:7f:77: + 9a:51:10:6e:4e:1f:20:3c:47:9c:43:74:7f:96:84:10:4c:13: + 43:be:f8:e0:72:2e:ff:bf:ae:3c:0a:03:60:82:4b:6f:f9:9a: + c5:1e:f6:af:90:3b:9f:61:3b:3e:de:9b:05:1a:c6:2c:3c:57: + 21:08:0f:54:fa:28:63:6c:e8:1b:9c:0f:cf:dd:30:44:13:b9: + 57:fe +-----BEGIN CERTIFICATE----- +MIIEODCCA6GgAwIBAgIEBydtuTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MB4XDTEwMTEzMDE2MzUyMVoXDTE4MDgxMDE1MzQyNlowWjELMAkG +A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz +dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO +KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn +c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP +wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg +kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc +B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAWow +ggFmMBIGA1UdEwEB/wQIMAYBAf8CAQMwTgYDVR0gBEcwRTBDBgRVHSAAMDswOQYI +KwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0 +b3J5LmNmbTAOBgNVHQ8BAf8EBAMCAQYwgYkGA1UdIwSBgTB/oXmkdzB1MQswCQYD +VQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUg +Q3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRy +dXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vd3d3 +LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwvMjAxOC9jZHAuY3JsMB0GA1Ud +DgQWBBTlnVkwgkdYzKz6CFQ2hns6tQRN8DANBgkqhkiG9w0BAQUFAAOBgQAWtCzJ +8V7honubeCB6SnBwhhkAtwUq6MklOQ/DZDx1CdmJFYAHwo28KaVkUM9xdUcjvU3Y +f3eaURBuTh8gPEecQ3R/loQQTBNDvvjgci7/v648CgNggktv+ZrFHvavkDufYTs+ +3psFGsYsPFchCA9U+ihjbOgbnA/P3TBEE7lX/g== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert8[] = { + 0x30, 0x82, 0x04, 0x38, 0x30, 0x82, 0x03, 0xa1, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0x6d, 0xb9, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, + 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, + 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x30, 0x31, 0x31, 0x33, 0x30, 0x31, 0x36, 0x33, 0x35, 0x32, + 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x30, 0x31, 0x35, + 0x33, 0x34, 0x32, 0x36, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69, + 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79, + 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, + 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, + 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04, + 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79, + 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e, + 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d, + 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12, + 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88, + 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7, + 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5, + 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab, + 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5, + 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f, + 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77, + 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0, + 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd, + 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0, + 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4, + 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9, + 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c, + 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c, + 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b, + 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76, + 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27, + 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x6a, 0x30, + 0x82, 0x01, 0x66, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30, + 0x4e, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43, + 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, + 0x81, 0x89, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, + 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, + 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, + 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, + 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, + 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, + 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, + 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, + 0x6e, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, + 0x64, 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, + 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, + 0xf0, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x16, 0xb4, 0x2c, 0xc9, + 0xf1, 0x5e, 0xe1, 0xa2, 0x7b, 0x9b, 0x78, 0x20, 0x7a, 0x4a, 0x70, 0x70, + 0x86, 0x19, 0x00, 0xb7, 0x05, 0x2a, 0xe8, 0xc9, 0x25, 0x39, 0x0f, 0xc3, + 0x64, 0x3c, 0x75, 0x09, 0xd9, 0x89, 0x15, 0x80, 0x07, 0xc2, 0x8d, 0xbc, + 0x29, 0xa5, 0x64, 0x50, 0xcf, 0x71, 0x75, 0x47, 0x23, 0xbd, 0x4d, 0xd8, + 0x7f, 0x77, 0x9a, 0x51, 0x10, 0x6e, 0x4e, 0x1f, 0x20, 0x3c, 0x47, 0x9c, + 0x43, 0x74, 0x7f, 0x96, 0x84, 0x10, 0x4c, 0x13, 0x43, 0xbe, 0xf8, 0xe0, + 0x72, 0x2e, 0xff, 0xbf, 0xae, 0x3c, 0x0a, 0x03, 0x60, 0x82, 0x4b, 0x6f, + 0xf9, 0x9a, 0xc5, 0x1e, 0xf6, 0xaf, 0x90, 0x3b, 0x9f, 0x61, 0x3b, 0x3e, + 0xde, 0x9b, 0x05, 0x1a, 0xc6, 0x2c, 0x3c, 0x57, 0x21, 0x08, 0x0f, 0x54, + 0xfa, 0x28, 0x63, 0x6c, 0xe8, 0x1b, 0x9c, 0x0f, 0xcf, 0xdd, 0x30, 0x44, + 0x13, 0xb9, 0x57, 0xfe, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146040 (0x23a78) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Aug 29 22:24:58 2014 GMT + Not After : May 20 22:24:58 2022 GMT + Subject: C=US, O=GeoTrust Inc., OU=Domain Validated SSL, CN=GeoTrust DV SSL CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:df:41:94:7a:da:f7:e4:31:43:b6:ea:01:1b:5c: + ce:63:ea:fa:6d:a3:d9:6a:ee:2d:9a:75:f9:d5:9c: + 5b:bd:34:df:d8:1c:c9:6d:d8:04:88:da:6e:b5:b7: + b5:f0:30:ae:40:d6:5d:fa:c4:53:c1:d4:22:9d:04: + 4e:11:a6:95:d5:45:7c:41:05:58:e0:4c:dd:f9:ee: + 55:bd:5f:46:dc:ad:13:08:9d:2c:e4:f7:82:e6:07: + 2b:9e:0e:8c:34:a1:ce:c4:a1:e0:81:70:86:00:06: + 3f:2d:ea:7c:9b:28:ae:1b:28:8b:39:09:d3:e7:f0: + 45:a4:b1:ba:11:67:90:55:7b:8f:de:ed:38:5c:a1: + e1:e3:83:c4:c3:72:91:4f:98:ee:1c:c2:80:aa:64: + a5:3e:83:62:1c:cc:e0:9e:f8:5a:c0:13:12:7d:a2: + a7:8b:a3:e7:9f:2a:d7:9b:ca:cb:ed:97:01:9c:28: + 84:51:04:50:41:bc:b4:fc:78:e9:1b:cf:14:ea:1f: + 0f:fc:2e:01:32:8d:b6:35:cb:0a:18:3b:ec:5a:3e: + 3c:1b:d3:99:43:1e:2f:f7:bd:f3:5b:12:b9:07:5e: + ed:3e:d1:a9:87:cc:77:72:27:d4:d9:75:a2:63:4b: + 93:36:bd:e5:5c:d7:bf:5f:79:0d:b3:32:a7:0b:b2: + 63:23 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + 0B:50:EC:77:EF:2A:9B:FF:EC:03:A1:0A:FF:AD:C6:E4:2A:18:C7:3E + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + Signature Algorithm: sha256WithRSAEncryption + 33:24:d5:90:aa:29:0c:35:b9:2f:c3:c7:42:93:c0:c6:10:4b: + 03:08:76:84:10:a2:e0:e7:53:12:27:f2:0a:da:7f:3a:dc:fd: + 5c:79:5a:8f:17:74:43:53:b1:d5:d1:5d:59:b9:a6:84:64:ca: + f1:3a:0a:59:96:10:bf:a9:81:57:8b:5c:87:dc:7f:e3:e4:bb: + 05:7a:a0:32:09:13:4e:10:81:28:1f:9c:03:62:bc:f4:01:b5: + 29:83:46:07:b9:e7:b8:5d:c8:e9:d1:dd:ad:3b:f8:34:db:c1: + d1:95:a9:91:18:ed:3c:2c:37:11:4d:cc:fe:53:3e:50:43:f9: + c3:56:41:ac:53:9b:6c:05:b2:9a:e2:e0:59:57:30:32:b6:26: + 4e:13:25:cd:fa:48:70:0f:75:55:60:11:f5:3b:d5:5e:5a:3c: + 8b:5b:0f:0f:62:42:48:61:85:8b:10:f4:c1:88:bf:7f:5f:8a: + c2:d7:cd:2b:94:5c:1f:34:4a:08:af:eb:ae:89:a8:48:75:55: + 95:1d:bb:c0:9a:01:b9:f4:03:22:3e:d4:e6:52:30:0d:67:b9: + c0:91:fd:2d:4c:30:8e:bd:8c:a5:04:91:bb:a4:ab:7f:0f:d8: + 6f:f0:66:00:c9:a3:5c:f5:b0:8f:83:e6:9c:5a:e6:b6:b9:c5: + bc:be:e4:02 +-----BEGIN CERTIFICATE----- +MIIERDCCAyygAwIBAgIDAjp4MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTQwODI5MjIyNDU4WhcNMjIwNTIwMjIyNDU4WjBmMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh +bGlkYXRlZCBTU0wxIDAeBgNVBAMTF0dlb1RydXN0IERWIFNTTCBDQSAtIEc0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA30GUetr35DFDtuoBG1zOY+r6 +baPZau4tmnX51ZxbvTTf2BzJbdgEiNputbe18DCuQNZd+sRTwdQinQROEaaV1UV8 +QQVY4Ezd+e5VvV9G3K0TCJ0s5PeC5gcrng6MNKHOxKHggXCGAAY/Lep8myiuGyiL +OQnT5/BFpLG6EWeQVXuP3u04XKHh44PEw3KRT5juHMKAqmSlPoNiHMzgnvhawBMS +faKni6PnnyrXm8rL7ZcBnCiEUQRQQby0/HjpG88U6h8P/C4BMo22NcsKGDvsWj48 +G9OZQx4v973zWxK5B17tPtGph8x3cifU2XWiY0uTNr3lXNe/X3kNszKnC7JjIwID +AQABo4IBHTCCARkwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4wHQYD +VR0OBBYEFAtQ7HfvKpv/7AOhCv+txuQqGMc+MBIGA1UdEwEB/wQIMAYBAf8CAQAw +DgYDVR0PAQH/BAQDAgEGMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9nLnN5bWNi +LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAuBggrBgEFBQcBAQQiMCAwHgYIKwYBBQUH +MAGGEmh0dHA6Ly9nLnN5bWNkLmNvbTBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYw +MzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2Vz +L2NwczANBgkqhkiG9w0BAQsFAAOCAQEAMyTVkKopDDW5L8PHQpPAxhBLAwh2hBCi +4OdTEifyCtp/Otz9XHlajxd0Q1Ox1dFdWbmmhGTK8ToKWZYQv6mBV4tch9x/4+S7 +BXqgMgkTThCBKB+cA2K89AG1KYNGB7nnuF3I6dHdrTv4NNvB0ZWpkRjtPCw3EU3M +/lM+UEP5w1ZBrFObbAWymuLgWVcwMrYmThMlzfpIcA91VWAR9TvVXlo8i1sPD2JC +SGGFixD0wYi/f1+KwtfNK5RcHzRKCK/rromoSHVVlR27wJoBufQDIj7U5lIwDWe5 +wJH9LUwwjr2MpQSRu6Srfw/Yb/BmAMmjXPWwj4PmnFrmtrnFvL7kAg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert9[] = { + 0x30, 0x82, 0x04, 0x44, 0x30, 0x82, 0x03, 0x2c, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x38, 0x32, 0x39, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38, + 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x14, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, + 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x47, 0x65, + 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, + 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdf, 0x41, 0x94, 0x7a, 0xda, 0xf7, + 0xe4, 0x31, 0x43, 0xb6, 0xea, 0x01, 0x1b, 0x5c, 0xce, 0x63, 0xea, 0xfa, + 0x6d, 0xa3, 0xd9, 0x6a, 0xee, 0x2d, 0x9a, 0x75, 0xf9, 0xd5, 0x9c, 0x5b, + 0xbd, 0x34, 0xdf, 0xd8, 0x1c, 0xc9, 0x6d, 0xd8, 0x04, 0x88, 0xda, 0x6e, + 0xb5, 0xb7, 0xb5, 0xf0, 0x30, 0xae, 0x40, 0xd6, 0x5d, 0xfa, 0xc4, 0x53, + 0xc1, 0xd4, 0x22, 0x9d, 0x04, 0x4e, 0x11, 0xa6, 0x95, 0xd5, 0x45, 0x7c, + 0x41, 0x05, 0x58, 0xe0, 0x4c, 0xdd, 0xf9, 0xee, 0x55, 0xbd, 0x5f, 0x46, + 0xdc, 0xad, 0x13, 0x08, 0x9d, 0x2c, 0xe4, 0xf7, 0x82, 0xe6, 0x07, 0x2b, + 0x9e, 0x0e, 0x8c, 0x34, 0xa1, 0xce, 0xc4, 0xa1, 0xe0, 0x81, 0x70, 0x86, + 0x00, 0x06, 0x3f, 0x2d, 0xea, 0x7c, 0x9b, 0x28, 0xae, 0x1b, 0x28, 0x8b, + 0x39, 0x09, 0xd3, 0xe7, 0xf0, 0x45, 0xa4, 0xb1, 0xba, 0x11, 0x67, 0x90, + 0x55, 0x7b, 0x8f, 0xde, 0xed, 0x38, 0x5c, 0xa1, 0xe1, 0xe3, 0x83, 0xc4, + 0xc3, 0x72, 0x91, 0x4f, 0x98, 0xee, 0x1c, 0xc2, 0x80, 0xaa, 0x64, 0xa5, + 0x3e, 0x83, 0x62, 0x1c, 0xcc, 0xe0, 0x9e, 0xf8, 0x5a, 0xc0, 0x13, 0x12, + 0x7d, 0xa2, 0xa7, 0x8b, 0xa3, 0xe7, 0x9f, 0x2a, 0xd7, 0x9b, 0xca, 0xcb, + 0xed, 0x97, 0x01, 0x9c, 0x28, 0x84, 0x51, 0x04, 0x50, 0x41, 0xbc, 0xb4, + 0xfc, 0x78, 0xe9, 0x1b, 0xcf, 0x14, 0xea, 0x1f, 0x0f, 0xfc, 0x2e, 0x01, + 0x32, 0x8d, 0xb6, 0x35, 0xcb, 0x0a, 0x18, 0x3b, 0xec, 0x5a, 0x3e, 0x3c, + 0x1b, 0xd3, 0x99, 0x43, 0x1e, 0x2f, 0xf7, 0xbd, 0xf3, 0x5b, 0x12, 0xb9, + 0x07, 0x5e, 0xed, 0x3e, 0xd1, 0xa9, 0x87, 0xcc, 0x77, 0x72, 0x27, 0xd4, + 0xd9, 0x75, 0xa2, 0x63, 0x4b, 0x93, 0x36, 0xbd, 0xe5, 0x5c, 0xd7, 0xbf, + 0x5f, 0x79, 0x0d, 0xb3, 0x32, 0xa7, 0x0b, 0xb2, 0x63, 0x23, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11, + 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0x50, 0xec, 0x77, 0xef, + 0x2a, 0x9b, 0xff, 0xec, 0x03, 0xa1, 0x0a, 0xff, 0xad, 0xc6, 0xe4, 0x2a, + 0x18, 0xc7, 0x3e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e, + 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x2e, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, + 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, + 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4c, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, + 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, + 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x33, 0x24, 0xd5, 0x90, 0xaa, 0x29, 0x0c, 0x35, 0xb9, 0x2f, 0xc3, 0xc7, + 0x42, 0x93, 0xc0, 0xc6, 0x10, 0x4b, 0x03, 0x08, 0x76, 0x84, 0x10, 0xa2, + 0xe0, 0xe7, 0x53, 0x12, 0x27, 0xf2, 0x0a, 0xda, 0x7f, 0x3a, 0xdc, 0xfd, + 0x5c, 0x79, 0x5a, 0x8f, 0x17, 0x74, 0x43, 0x53, 0xb1, 0xd5, 0xd1, 0x5d, + 0x59, 0xb9, 0xa6, 0x84, 0x64, 0xca, 0xf1, 0x3a, 0x0a, 0x59, 0x96, 0x10, + 0xbf, 0xa9, 0x81, 0x57, 0x8b, 0x5c, 0x87, 0xdc, 0x7f, 0xe3, 0xe4, 0xbb, + 0x05, 0x7a, 0xa0, 0x32, 0x09, 0x13, 0x4e, 0x10, 0x81, 0x28, 0x1f, 0x9c, + 0x03, 0x62, 0xbc, 0xf4, 0x01, 0xb5, 0x29, 0x83, 0x46, 0x07, 0xb9, 0xe7, + 0xb8, 0x5d, 0xc8, 0xe9, 0xd1, 0xdd, 0xad, 0x3b, 0xf8, 0x34, 0xdb, 0xc1, + 0xd1, 0x95, 0xa9, 0x91, 0x18, 0xed, 0x3c, 0x2c, 0x37, 0x11, 0x4d, 0xcc, + 0xfe, 0x53, 0x3e, 0x50, 0x43, 0xf9, 0xc3, 0x56, 0x41, 0xac, 0x53, 0x9b, + 0x6c, 0x05, 0xb2, 0x9a, 0xe2, 0xe0, 0x59, 0x57, 0x30, 0x32, 0xb6, 0x26, + 0x4e, 0x13, 0x25, 0xcd, 0xfa, 0x48, 0x70, 0x0f, 0x75, 0x55, 0x60, 0x11, + 0xf5, 0x3b, 0xd5, 0x5e, 0x5a, 0x3c, 0x8b, 0x5b, 0x0f, 0x0f, 0x62, 0x42, + 0x48, 0x61, 0x85, 0x8b, 0x10, 0xf4, 0xc1, 0x88, 0xbf, 0x7f, 0x5f, 0x8a, + 0xc2, 0xd7, 0xcd, 0x2b, 0x94, 0x5c, 0x1f, 0x34, 0x4a, 0x08, 0xaf, 0xeb, + 0xae, 0x89, 0xa8, 0x48, 0x75, 0x55, 0x95, 0x1d, 0xbb, 0xc0, 0x9a, 0x01, + 0xb9, 0xf4, 0x03, 0x22, 0x3e, 0xd4, 0xe6, 0x52, 0x30, 0x0d, 0x67, 0xb9, + 0xc0, 0x91, 0xfd, 0x2d, 0x4c, 0x30, 0x8e, 0xbd, 0x8c, 0xa5, 0x04, 0x91, + 0xbb, 0xa4, 0xab, 0x7f, 0x0f, 0xd8, 0x6f, 0xf0, 0x66, 0x00, 0xc9, 0xa3, + 0x5c, 0xf5, 0xb0, 0x8f, 0x83, 0xe6, 0x9c, 0x5a, 0xe6, 0xb6, 0xb9, 0xc5, + 0xbc, 0xbe, 0xe4, 0x02, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 33:65:50:08:79:ad:73:e2:30:b9:e0:1d:0d:7f:ac:91 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com + Validity + Not Before: Nov 17 00:00:00 2006 GMT + Not After : Dec 30 23:59:59 2020 GMT + Subject: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ac:a0:f0:fb:80:59:d4:9c:c7:a4:cf:9d:a1:59: + 73:09:10:45:0c:0d:2c:6e:68:f1:6c:5b:48:68:49: + 59:37:fc:0b:33:19:c2:77:7f:cc:10:2d:95:34:1c: + e6:eb:4d:09:a7:1c:d2:b8:c9:97:36:02:b7:89:d4: + 24:5f:06:c0:cc:44:94:94:8d:02:62:6f:eb:5a:dd: + 11:8d:28:9a:5c:84:90:10:7a:0d:bd:74:66:2f:6a: + 38:a0:e2:d5:54:44:eb:1d:07:9f:07:ba:6f:ee:e9: + fd:4e:0b:29:f5:3e:84:a0:01:f1:9c:ab:f8:1c:7e: + 89:a4:e8:a1:d8:71:65:0d:a3:51:7b:ee:bc:d2:22: + 60:0d:b9:5b:9d:df:ba:fc:51:5b:0b:af:98:b2:e9: + 2e:e9:04:e8:62:87:de:2b:c8:d7:4e:c1:4c:64:1e: + dd:cf:87:58:ba:4a:4f:ca:68:07:1d:1c:9d:4a:c6: + d5:2f:91:cc:7c:71:72:1c:c5:c0:67:eb:32:fd:c9: + 92:5c:94:da:85:c0:9b:bf:53:7d:2b:09:f4:8c:9d: + 91:1f:97:6a:52:cb:de:09:36:a4:77:d8:7b:87:50: + 44:d5:3e:6e:29:69:fb:39:49:26:1e:09:a5:80:7b: + 40:2d:eb:e8:27:85:c9:fe:61:fd:7e:e6:7c:97:1d: + d5:9d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.thawte.com/cps + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.thawte.com/ThawtePremiumServerCA.crl + + Signature Algorithm: sha1WithRSAEncryption + 84:a8:4c:c9:3e:2a:bc:9a:e2:cc:8f:0b:b2:25:77:c4:61:89: + 89:63:5a:d4:a3:15:40:d4:fb:5e:3f:b4:43:ea:63:17:2b:6b: + 99:74:9e:09:a8:dd:d4:56:15:2e:7a:79:31:5f:63:96:53:1b: + 34:d9:15:ea:4f:6d:70:ca:be:f6:82:a9:ed:da:85:77:cc:76: + 1c:6a:81:0a:21:d8:41:99:7f:5e:2e:82:c1:e8:aa:f7:93:81: + 05:aa:92:b4:1f:b7:9a:c0:07:17:f5:cb:c6:b4:4c:0e:d7:56: + dc:71:20:74:38:d6:74:c6:d6:8f:6b:af:8b:8d:a0:6c:29:0b: + 61:e0 +-----BEGIN CERTIFICATE----- +MIIERTCCA66gAwIBAgIQM2VQCHmtc+IwueAdDX+skTANBgkqhkiG9w0BAQUFADCB +zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ +Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE +CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh +d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl +cnZlckB0aGF3dGUuY29tMB4XDTA2MTExNzAwMDAwMFoXDTIwMTIzMDIzNTk1OVow +gakxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xKDAmBgNVBAsT +H0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xODA2BgNVBAsTLyhjKSAy +MDA2IHRoYXd0ZSwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYD +VQQDExZ0aGF3dGUgUHJpbWFyeSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEArKDw+4BZ1JzHpM+doVlzCRBFDA0sbmjxbFtIaElZN/wLMxnC +d3/MEC2VNBzm600JpxzSuMmXNgK3idQkXwbAzESUlI0CYm/rWt0RjSiaXISQEHoN +vXRmL2o4oOLVVETrHQefB7pv7un9Tgsp9T6EoAHxnKv4HH6JpOih2HFlDaNRe+68 +0iJgDblbnd+6/FFbC6+Ysuku6QToYofeK8jXTsFMZB7dz4dYukpPymgHHRydSsbV +L5HMfHFyHMXAZ+sy/cmSXJTahcCbv1N9Kwn0jJ2RH5dqUsveCTakd9h7h1BE1T5u +KWn7OUkmHgmlgHtALevoJ4XJ/mH9fuZ8lx3VnQIDAQABo4HCMIG/MA8GA1UdEwEB +/wQFMAMBAf8wOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBz +Oi8vd3d3LnRoYXd0ZS5jb20vY3BzMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +e1tFz6/Oy3r9MZIaarbzRutXSFAwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL2Ny +bC50aGF3dGUuY29tL1RoYXd0ZVByZW1pdW1TZXJ2ZXJDQS5jcmwwDQYJKoZIhvcN +AQEFBQADgYEAhKhMyT4qvJrizI8LsiV3xGGJiWNa1KMVQNT7Xj+0Q+pjFytrmXSe +Cajd1FYVLnp5MV9jllMbNNkV6k9tcMq+9oKp7dqFd8x2HGqBCiHYQZl/Xi6Cweiq +95OBBaqStB+3msAHF/XLxrRMDtdW3HEgdDjWdMbWj2uvi42gbCkLYeA= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert10[] = { + 0x30, 0x82, 0x04, 0x45, 0x30, 0x82, 0x03, 0xae, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x33, 0x65, 0x50, 0x08, 0x79, 0xad, 0x73, 0xe2, 0x30, + 0xb9, 0xe0, 0x1d, 0x0d, 0x7f, 0xac, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xce, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x5a, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0c, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x43, 0x61, 0x70, + 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, + 0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31, 0x1d, 0x30, + 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77, + 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61, + 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30, + 0x26, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, + 0x16, 0x19, 0x70, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31, + 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, + 0x31, 0x32, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, + 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, + 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, + 0x30, 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, + 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, + 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, + 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xac, 0xa0, 0xf0, 0xfb, 0x80, 0x59, 0xd4, 0x9c, 0xc7, 0xa4, 0xcf, 0x9d, + 0xa1, 0x59, 0x73, 0x09, 0x10, 0x45, 0x0c, 0x0d, 0x2c, 0x6e, 0x68, 0xf1, + 0x6c, 0x5b, 0x48, 0x68, 0x49, 0x59, 0x37, 0xfc, 0x0b, 0x33, 0x19, 0xc2, + 0x77, 0x7f, 0xcc, 0x10, 0x2d, 0x95, 0x34, 0x1c, 0xe6, 0xeb, 0x4d, 0x09, + 0xa7, 0x1c, 0xd2, 0xb8, 0xc9, 0x97, 0x36, 0x02, 0xb7, 0x89, 0xd4, 0x24, + 0x5f, 0x06, 0xc0, 0xcc, 0x44, 0x94, 0x94, 0x8d, 0x02, 0x62, 0x6f, 0xeb, + 0x5a, 0xdd, 0x11, 0x8d, 0x28, 0x9a, 0x5c, 0x84, 0x90, 0x10, 0x7a, 0x0d, + 0xbd, 0x74, 0x66, 0x2f, 0x6a, 0x38, 0xa0, 0xe2, 0xd5, 0x54, 0x44, 0xeb, + 0x1d, 0x07, 0x9f, 0x07, 0xba, 0x6f, 0xee, 0xe9, 0xfd, 0x4e, 0x0b, 0x29, + 0xf5, 0x3e, 0x84, 0xa0, 0x01, 0xf1, 0x9c, 0xab, 0xf8, 0x1c, 0x7e, 0x89, + 0xa4, 0xe8, 0xa1, 0xd8, 0x71, 0x65, 0x0d, 0xa3, 0x51, 0x7b, 0xee, 0xbc, + 0xd2, 0x22, 0x60, 0x0d, 0xb9, 0x5b, 0x9d, 0xdf, 0xba, 0xfc, 0x51, 0x5b, + 0x0b, 0xaf, 0x98, 0xb2, 0xe9, 0x2e, 0xe9, 0x04, 0xe8, 0x62, 0x87, 0xde, + 0x2b, 0xc8, 0xd7, 0x4e, 0xc1, 0x4c, 0x64, 0x1e, 0xdd, 0xcf, 0x87, 0x58, + 0xba, 0x4a, 0x4f, 0xca, 0x68, 0x07, 0x1d, 0x1c, 0x9d, 0x4a, 0xc6, 0xd5, + 0x2f, 0x91, 0xcc, 0x7c, 0x71, 0x72, 0x1c, 0xc5, 0xc0, 0x67, 0xeb, 0x32, + 0xfd, 0xc9, 0x92, 0x5c, 0x94, 0xda, 0x85, 0xc0, 0x9b, 0xbf, 0x53, 0x7d, + 0x2b, 0x09, 0xf4, 0x8c, 0x9d, 0x91, 0x1f, 0x97, 0x6a, 0x52, 0xcb, 0xde, + 0x09, 0x36, 0xa4, 0x77, 0xd8, 0x7b, 0x87, 0x50, 0x44, 0xd5, 0x3e, 0x6e, + 0x29, 0x69, 0xfb, 0x39, 0x49, 0x26, 0x1e, 0x09, 0xa5, 0x80, 0x7b, 0x40, + 0x2d, 0xeb, 0xe8, 0x27, 0x85, 0xc9, 0xfe, 0x61, 0xfd, 0x7e, 0xe6, 0x7c, + 0x97, 0x1d, 0xd5, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xc2, + 0x30, 0x81, 0xbf, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3b, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, + 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, + 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x40, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xa0, 0x33, 0xa0, + 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65, 0x6d, 0x69, + 0x75, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x84, 0xa8, 0x4c, + 0xc9, 0x3e, 0x2a, 0xbc, 0x9a, 0xe2, 0xcc, 0x8f, 0x0b, 0xb2, 0x25, 0x77, + 0xc4, 0x61, 0x89, 0x89, 0x63, 0x5a, 0xd4, 0xa3, 0x15, 0x40, 0xd4, 0xfb, + 0x5e, 0x3f, 0xb4, 0x43, 0xea, 0x63, 0x17, 0x2b, 0x6b, 0x99, 0x74, 0x9e, + 0x09, 0xa8, 0xdd, 0xd4, 0x56, 0x15, 0x2e, 0x7a, 0x79, 0x31, 0x5f, 0x63, + 0x96, 0x53, 0x1b, 0x34, 0xd9, 0x15, 0xea, 0x4f, 0x6d, 0x70, 0xca, 0xbe, + 0xf6, 0x82, 0xa9, 0xed, 0xda, 0x85, 0x77, 0xcc, 0x76, 0x1c, 0x6a, 0x81, + 0x0a, 0x21, 0xd8, 0x41, 0x99, 0x7f, 0x5e, 0x2e, 0x82, 0xc1, 0xe8, 0xaa, + 0xf7, 0x93, 0x81, 0x05, 0xaa, 0x92, 0xb4, 0x1f, 0xb7, 0x9a, 0xc0, 0x07, + 0x17, 0xf5, 0xcb, 0xc6, 0xb4, 0x4c, 0x0e, 0xd7, 0x56, 0xdc, 0x71, 0x20, + 0x74, 0x38, 0xd6, 0x74, 0xc6, 0xd6, 0x8f, 0x6b, 0xaf, 0x8b, 0x8d, 0xa0, + 0x6c, 0x29, 0x0b, 0x61, 0xe0, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:00:00:00:00:01:44:4e:f0:36:31 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA + Validity + Not Before: Feb 20 10:00:00 2014 GMT + Not After : Feb 20 10:00:00 2024 GMT + Subject: C=BE, O=GlobalSign nv-sa, CN=AlphaSSL CA - SHA256 - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:01:ec:e4:ec:73:60:fb:7e:8f:6a:b7:c6:17: + e3:92:64:32:d4:ac:00:d9:a2:0f:b9:ed:ee:6b:8a: + 86:ca:92:67:d9:74:d7:5d:47:02:3c:8f:40:d6:9e: + 6d:14:cd:c3:da:29:39:a7:0f:05:0a:68:a2:66:1a: + 1e:c4:b2:8b:76:58:e5:ab:5d:1d:8f:40:b3:39:8b: + ef:1e:83:7d:22:d0:e3:a9:00:2e:ec:53:cf:62:19: + 85:44:28:4c:c0:27:cb:7b:0e:ec:10:64:00:10:a4: + 05:cc:a0:72:be:41:6c:31:5b:48:e4:b1:ec:b9:23: + eb:55:4d:d0:7d:62:4a:a5:b4:a5:a4:59:85:c5:25: + 91:a6:fe:a6:09:9f:06:10:6d:8f:81:0c:64:40:5e: + 73:00:9a:e0:2e:65:98:54:10:00:70:98:c8:e1:ed: + 34:5f:d8:9c:c7:0d:c0:d6:23:59:45:fc:fe:55:7a: + 86:ee:94:60:22:f1:ae:d1:e6:55:46:f6:99:c5:1b: + 08:74:5f:ac:b0:64:84:8f:89:38:1c:a1:a7:90:21: + 4f:02:6e:bd:e0:61:67:d4:f8:42:87:0f:0a:f7:c9: + 04:6d:2a:a9:2f:ef:42:a5:df:dd:a3:53:db:98:1e: + 81:f9:9a:72:7b:5a:de:4f:3e:7f:a2:58:a0:e2:17: + ad:67 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + F5:CD:D5:3C:08:50:F9:6A:4F:3A:B7:97:DA:56:83:E6:69:D2:68:F7 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.alphassl.com/repository/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.globalsign.net/root.crl + + Authority Information Access: + OCSP - URI:http://ocsp.globalsign.com/rootr1 + + X509v3 Authority Key Identifier: + keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + + Signature Algorithm: sha256WithRSAEncryption + 60:40:68:16:47:e7:16:8d:db:5c:a1:56:2a:cb:f4:5c:9b:b0: + 1e:a2:4b:f5:cb:02:3f:f8:0b:a1:f2:a7:42:d4:b7:4c:eb:e3: + 66:80:f3:25:43:78:2e:1b:17:56:07:52:18:cb:d1:a8:ec:e6: + fb:73:3e:a4:62:8c:80:b4:d2:c5:12:73:a3:d3:fa:02:38:be: + 63:3d:84:b8:99:c1:f1:ba:f7:9f:c3:40:d1:58:18:53:c1:62: + dd:af:18:42:7f:34:4e:c5:43:d5:71:b0:30:00:c7:e3:90:ae: + 3f:57:86:97:ce:ea:0c:12:8e:22:70:e3:66:a7:54:7f:2e:28: + cb:d4:54:d0:b3:1e:62:67:08:f9:27:e1:cb:e3:66:b8:24:1b: + 89:6a:89:44:65:f2:d9:4c:d2:58:1c:8c:4e:c0:95:a1:d4:ef: + 67:2f:38:20:e8:2e:ff:96:51:f0:ba:d8:3d:92:70:47:65:1c: + 9e:73:72:b4:60:0c:5c:e2:d1:73:76:e0:af:4e:e2:e5:37:a5: + 45:2f:8a:23:3e:87:c7:30:e6:31:38:7c:f4:dd:52:ca:f3:53: + 04:25:57:56:66:94:e8:0b:ee:e6:03:14:4e:ee:fd:6d:94:64: + 9e:5e:ce:79:d4:b2:a6:cf:40:b1:44:a8:3e:87:19:5e:e9:f8: + 21:16:59:53 +-----BEGIN CERTIFICATE----- +MIIETTCCAzWgAwIBAgILBAAAAAABRE7wNjEwDQYJKoZIhvcNAQELBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw +MDBaFw0yNDAyMjAxMDAwMDBaMEwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMSIwIAYDVQQDExlBbHBoYVNTTCBDQSAtIFNIQTI1NiAtIEcy +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gHs5OxzYPt+j2q3xhfj +kmQy1KwA2aIPue3ua4qGypJn2XTXXUcCPI9A1p5tFM3D2ik5pw8FCmiiZhoexLKL +dljlq10dj0CzOYvvHoN9ItDjqQAu7FPPYhmFRChMwCfLew7sEGQAEKQFzKByvkFs +MVtI5LHsuSPrVU3QfWJKpbSlpFmFxSWRpv6mCZ8GEG2PgQxkQF5zAJrgLmWYVBAA +cJjI4e00X9icxw3A1iNZRfz+VXqG7pRgIvGu0eZVRvaZxRsIdF+ssGSEj4k4HKGn +kCFPAm694GFn1PhChw8K98kEbSqpL+9Cpd/do1PbmB6B+Zpye1reTz5/olig4het +ZwIDAQABo4IBIzCCAR8wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C +AQAwHQYDVR0OBBYEFPXN1TwIUPlqTzq3l9pWg+Zp0mj3MEUGA1UdIAQ+MDwwOgYE +VR0gADAyMDAGCCsGAQUFBwIBFiRodHRwczovL3d3dy5hbHBoYXNzbC5jb20vcmVw +b3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWdu +Lm5ldC9yb290LmNybDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6 +Ly9vY3NwLmdsb2JhbHNpZ24uY29tL3Jvb3RyMTAfBgNVHSMEGDAWgBRge2YaRQ2X +yolQL30EzTSo//z9SzANBgkqhkiG9w0BAQsFAAOCAQEAYEBoFkfnFo3bXKFWKsv0 +XJuwHqJL9csCP/gLofKnQtS3TOvjZoDzJUN4LhsXVgdSGMvRqOzm+3M+pGKMgLTS +xRJzo9P6Aji+Yz2EuJnB8br3n8NA0VgYU8Fi3a8YQn80TsVD1XGwMADH45CuP1eG +l87qDBKOInDjZqdUfy4oy9RU0LMeYmcI+Sfhy+NmuCQbiWqJRGXy2UzSWByMTsCV +odTvZy84IOgu/5ZR8LrYPZJwR2UcnnNytGAMXOLRc3bgr07i5TelRS+KIz6HxzDm +MTh89N1SyvNTBCVXVmaU6Avu5gMUTu79bZRknl7OedSyps9AsUSoPocZXun4IRZZ +Uw== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert11[] = { + 0x30, 0x82, 0x04, 0x4d, 0x30, 0x82, 0x03, 0x35, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0, + 0x36, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, + 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4c, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, + 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, + 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x41, + 0x6c, 0x70, 0x68, 0x61, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, + 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0x01, 0xec, + 0xe4, 0xec, 0x73, 0x60, 0xfb, 0x7e, 0x8f, 0x6a, 0xb7, 0xc6, 0x17, 0xe3, + 0x92, 0x64, 0x32, 0xd4, 0xac, 0x00, 0xd9, 0xa2, 0x0f, 0xb9, 0xed, 0xee, + 0x6b, 0x8a, 0x86, 0xca, 0x92, 0x67, 0xd9, 0x74, 0xd7, 0x5d, 0x47, 0x02, + 0x3c, 0x8f, 0x40, 0xd6, 0x9e, 0x6d, 0x14, 0xcd, 0xc3, 0xda, 0x29, 0x39, + 0xa7, 0x0f, 0x05, 0x0a, 0x68, 0xa2, 0x66, 0x1a, 0x1e, 0xc4, 0xb2, 0x8b, + 0x76, 0x58, 0xe5, 0xab, 0x5d, 0x1d, 0x8f, 0x40, 0xb3, 0x39, 0x8b, 0xef, + 0x1e, 0x83, 0x7d, 0x22, 0xd0, 0xe3, 0xa9, 0x00, 0x2e, 0xec, 0x53, 0xcf, + 0x62, 0x19, 0x85, 0x44, 0x28, 0x4c, 0xc0, 0x27, 0xcb, 0x7b, 0x0e, 0xec, + 0x10, 0x64, 0x00, 0x10, 0xa4, 0x05, 0xcc, 0xa0, 0x72, 0xbe, 0x41, 0x6c, + 0x31, 0x5b, 0x48, 0xe4, 0xb1, 0xec, 0xb9, 0x23, 0xeb, 0x55, 0x4d, 0xd0, + 0x7d, 0x62, 0x4a, 0xa5, 0xb4, 0xa5, 0xa4, 0x59, 0x85, 0xc5, 0x25, 0x91, + 0xa6, 0xfe, 0xa6, 0x09, 0x9f, 0x06, 0x10, 0x6d, 0x8f, 0x81, 0x0c, 0x64, + 0x40, 0x5e, 0x73, 0x00, 0x9a, 0xe0, 0x2e, 0x65, 0x98, 0x54, 0x10, 0x00, + 0x70, 0x98, 0xc8, 0xe1, 0xed, 0x34, 0x5f, 0xd8, 0x9c, 0xc7, 0x0d, 0xc0, + 0xd6, 0x23, 0x59, 0x45, 0xfc, 0xfe, 0x55, 0x7a, 0x86, 0xee, 0x94, 0x60, + 0x22, 0xf1, 0xae, 0xd1, 0xe6, 0x55, 0x46, 0xf6, 0x99, 0xc5, 0x1b, 0x08, + 0x74, 0x5f, 0xac, 0xb0, 0x64, 0x84, 0x8f, 0x89, 0x38, 0x1c, 0xa1, 0xa7, + 0x90, 0x21, 0x4f, 0x02, 0x6e, 0xbd, 0xe0, 0x61, 0x67, 0xd4, 0xf8, 0x42, + 0x87, 0x0f, 0x0a, 0xf7, 0xc9, 0x04, 0x6d, 0x2a, 0xa9, 0x2f, 0xef, 0x42, + 0xa5, 0xdf, 0xdd, 0xa3, 0x53, 0xdb, 0x98, 0x1e, 0x81, 0xf9, 0x9a, 0x72, + 0x7b, 0x5a, 0xde, 0x4f, 0x3e, 0x7f, 0xa2, 0x58, 0xa0, 0xe2, 0x17, 0xad, + 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x23, 0x30, 0x82, + 0x01, 0x1f, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xf5, 0xcd, 0xd5, 0x3c, 0x08, 0x50, 0xf9, 0x6a, 0x4f, 0x3a, 0xb7, + 0x97, 0xda, 0x56, 0x83, 0xe6, 0x69, 0xd2, 0x68, 0xf7, 0x30, 0x45, 0x06, + 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x04, + 0x55, 0x1d, 0x20, 0x00, 0x30, 0x32, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x24, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, + 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, + 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, + 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, + 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, + 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, + 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x60, 0x40, 0x68, + 0x16, 0x47, 0xe7, 0x16, 0x8d, 0xdb, 0x5c, 0xa1, 0x56, 0x2a, 0xcb, 0xf4, + 0x5c, 0x9b, 0xb0, 0x1e, 0xa2, 0x4b, 0xf5, 0xcb, 0x02, 0x3f, 0xf8, 0x0b, + 0xa1, 0xf2, 0xa7, 0x42, 0xd4, 0xb7, 0x4c, 0xeb, 0xe3, 0x66, 0x80, 0xf3, + 0x25, 0x43, 0x78, 0x2e, 0x1b, 0x17, 0x56, 0x07, 0x52, 0x18, 0xcb, 0xd1, + 0xa8, 0xec, 0xe6, 0xfb, 0x73, 0x3e, 0xa4, 0x62, 0x8c, 0x80, 0xb4, 0xd2, + 0xc5, 0x12, 0x73, 0xa3, 0xd3, 0xfa, 0x02, 0x38, 0xbe, 0x63, 0x3d, 0x84, + 0xb8, 0x99, 0xc1, 0xf1, 0xba, 0xf7, 0x9f, 0xc3, 0x40, 0xd1, 0x58, 0x18, + 0x53, 0xc1, 0x62, 0xdd, 0xaf, 0x18, 0x42, 0x7f, 0x34, 0x4e, 0xc5, 0x43, + 0xd5, 0x71, 0xb0, 0x30, 0x00, 0xc7, 0xe3, 0x90, 0xae, 0x3f, 0x57, 0x86, + 0x97, 0xce, 0xea, 0x0c, 0x12, 0x8e, 0x22, 0x70, 0xe3, 0x66, 0xa7, 0x54, + 0x7f, 0x2e, 0x28, 0xcb, 0xd4, 0x54, 0xd0, 0xb3, 0x1e, 0x62, 0x67, 0x08, + 0xf9, 0x27, 0xe1, 0xcb, 0xe3, 0x66, 0xb8, 0x24, 0x1b, 0x89, 0x6a, 0x89, + 0x44, 0x65, 0xf2, 0xd9, 0x4c, 0xd2, 0x58, 0x1c, 0x8c, 0x4e, 0xc0, 0x95, + 0xa1, 0xd4, 0xef, 0x67, 0x2f, 0x38, 0x20, 0xe8, 0x2e, 0xff, 0x96, 0x51, + 0xf0, 0xba, 0xd8, 0x3d, 0x92, 0x70, 0x47, 0x65, 0x1c, 0x9e, 0x73, 0x72, + 0xb4, 0x60, 0x0c, 0x5c, 0xe2, 0xd1, 0x73, 0x76, 0xe0, 0xaf, 0x4e, 0xe2, + 0xe5, 0x37, 0xa5, 0x45, 0x2f, 0x8a, 0x23, 0x3e, 0x87, 0xc7, 0x30, 0xe6, + 0x31, 0x38, 0x7c, 0xf4, 0xdd, 0x52, 0xca, 0xf3, 0x53, 0x04, 0x25, 0x57, + 0x56, 0x66, 0x94, 0xe8, 0x0b, 0xee, 0xe6, 0x03, 0x14, 0x4e, 0xee, 0xfd, + 0x6d, 0x94, 0x64, 0x9e, 0x5e, 0xce, 0x79, 0xd4, 0xb2, 0xa6, 0xcf, 0x40, + 0xb1, 0x44, 0xa8, 0x3e, 0x87, 0x19, 0x5e, 0xe9, 0xf8, 0x21, 0x16, 0x59, + 0x53, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146031 (0x23a6f) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Nov 5 21:36:50 2013 GMT + Not After : May 20 21:36:50 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e3:be:7e:0a:86:a3:cf:6b:6d:3d:2b:a1:97:ad: + 49:24:4d:d7:77:b9:34:79:08:a5:9e:a2:9e:de:47: + 12:92:3d:7e:ea:19:86:b1:e8:4f:3d:5f:f7:d0:a7: + 77:9a:5b:1f:0a:03:b5:19:53:db:a5:21:94:69:63: + 9d:6a:4c:91:0c:10:47:be:11:fa:6c:86:25:b7:ab: + 04:68:42:38:09:65:f0:14:da:19:9e:fa:6b:0b:ab: + 62:ef:8d:a7:ef:63:70:23:a8:af:81:f3:d1:6e:88: + 67:53:ec:12:a4:29:75:8a:a7:f2:57:3d:a2:83:98: + 97:f2:0a:7d:d4:e7:43:6e:30:78:62:22:59:59:b8: + 71:27:45:aa:0f:66:c6:55:3f:fa:32:17:2b:31:8f: + 46:a0:fa:69:14:7c:9d:9f:5a:e2:eb:33:4e:10:a6: + b3:ed:77:63:d8:c3:9e:f4:dd:df:79:9a:7a:d4:ee: + de:dd:9a:cc:c3:b7:a9:5d:cc:11:3a:07:bb:6f:97: + a4:01:23:47:95:1f:a3:77:fa:58:92:c6:c7:d0:bd: + cf:93:18:42:b7:7e:f7:9e:65:ea:d5:3b:ca:ed:ac: + c5:70:a1:fe:d4:10:9a:f0:12:04:44:ac:1a:5b:78: + 50:45:57:4c:6f:bd:80:cb:81:5c:2d:b3:bc:76:a1: + 1e:65 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + D2:6F:F7:96:F4:85:3F:72:3C:30:7D:23:DA:85:78:9B:A3:7C:5A:7C + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g1.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g2.symcb.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-539 + Signature Algorithm: sha256WithRSAEncryption + a0:d4:f7:2c:fb:74:0b:7f:64:f1:cd:43:6a:9f:62:53:1c:02: + 7c:98:90:a2:ee:4f:68:d4:20:1a:73:12:3e:77:b3:50:eb:72: + bc:ee:88:be:7f:17:ea:77:8f:83:61:95:4f:84:a1:cb:32:4f: + 6c:21:be:d2:69:96:7d:63:bd:dc:2b:a8:1f:d0:13:84:70:fe: + f6:35:95:89:f9:a6:77:b0:46:c8:bb:b7:13:f5:c9:60:69:d6: + 4c:fe:d2:8e:ef:d3:60:c1:80:80:e1:e7:fb:8b:6f:21:79:4a: + e0:dc:a9:1b:c1:b7:fb:c3:49:59:5c:b5:77:07:44:d4:97:fc: + 49:00:89:6f:06:4e:01:70:19:ac:2f:11:c0:e2:e6:0f:2f:86: + 4b:8d:7b:c3:b9:a7:2e:f4:f1:ac:16:3e:39:49:51:9e:17:4b: + 4f:10:3a:5b:a5:a8:92:6f:fd:fa:d6:0b:03:4d:47:56:57:19: + f3:cb:6b:f5:f3:d6:cf:b0:f5:f5:a3:11:d2:20:53:13:34:37: + 05:2c:43:5a:63:df:8d:40:d6:85:1e:51:e9:51:17:1e:03:56: + c9:f1:30:ad:e7:9b:11:a2:b9:d0:31:81:9b:68:b1:d9:e8:f3: + e6:94:7e:c7:ae:13:2f:87:ed:d0:25:b0:68:f9:de:08:5a:f3: + 29:cc:d4:92 +-----BEGIN CERTIFICATE----- +MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTMxMTA1MjEzNjUwWhcNMjIwNTIwMjEzNjUwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +U1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjvn4K +hqPPa209K6GXrUkkTdd3uTR5CKWeop7eRxKSPX7qGYax6E89X/fQp3eaWx8KA7UZ +U9ulIZRpY51qTJEMEEe+EfpshiW3qwRoQjgJZfAU2hme+msLq2LvjafvY3AjqK+B +89FuiGdT7BKkKXWKp/JXPaKDmJfyCn3U50NuMHhiIllZuHEnRaoPZsZVP/oyFysx +j0ag+mkUfJ2fWuLrM04QprPtd2PYw5703d95mnrU7t7dmszDt6ldzBE6B7tvl6QB +I0eVH6N3+liSxsfQvc+TGEK3fveeZerVO8rtrMVwof7UEJrwEgRErBpbeFBFV0xv +vYDLgVwts7x2oR5lAgMBAAGjggFKMIIBRjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjAdBgNVHQ4EFgQU0m/3lvSFP3I8MH0j2oV4m6N8WnwwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNgYDVR0fBC8wLTAroCmgJ4Yl +aHR0cDovL2cxLnN5bWNiLmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAvBggrBgEFBQcB +AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9nMi5zeW1jYi5jb20wTAYDVR0gBEUw +QzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1 +c3QuY29tL3Jlc291cmNlcy9jcHMwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5 +bWFudGVjUEtJLTEtNTM5MA0GCSqGSIb3DQEBCwUAA4IBAQCg1Pcs+3QLf2TxzUNq +n2JTHAJ8mJCi7k9o1CAacxI+d7NQ63K87oi+fxfqd4+DYZVPhKHLMk9sIb7SaZZ9 +Y73cK6gf0BOEcP72NZWJ+aZ3sEbIu7cT9clgadZM/tKO79NgwYCA4ef7i28heUrg +3Kkbwbf7w0lZXLV3B0TUl/xJAIlvBk4BcBmsLxHA4uYPL4ZLjXvDuacu9PGsFj45 +SVGeF0tPEDpbpaiSb/361gsDTUdWVxnzy2v189bPsPX1oxHSIFMTNDcFLENaY9+N +QNaFHlHpURceA1bJ8TCt55sRornQMYGbaLHZ6PPmlH7HrhMvh+3QJbBo+d4IWvMp +zNSS +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert12[] = { + 0x30, 0x82, 0x04, 0x4f, 0x30, 0x82, 0x03, 0x37, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x6f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, + 0x31, 0x30, 0x35, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30, + 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe3, 0xbe, 0x7e, 0x0a, + 0x86, 0xa3, 0xcf, 0x6b, 0x6d, 0x3d, 0x2b, 0xa1, 0x97, 0xad, 0x49, 0x24, + 0x4d, 0xd7, 0x77, 0xb9, 0x34, 0x79, 0x08, 0xa5, 0x9e, 0xa2, 0x9e, 0xde, + 0x47, 0x12, 0x92, 0x3d, 0x7e, 0xea, 0x19, 0x86, 0xb1, 0xe8, 0x4f, 0x3d, + 0x5f, 0xf7, 0xd0, 0xa7, 0x77, 0x9a, 0x5b, 0x1f, 0x0a, 0x03, 0xb5, 0x19, + 0x53, 0xdb, 0xa5, 0x21, 0x94, 0x69, 0x63, 0x9d, 0x6a, 0x4c, 0x91, 0x0c, + 0x10, 0x47, 0xbe, 0x11, 0xfa, 0x6c, 0x86, 0x25, 0xb7, 0xab, 0x04, 0x68, + 0x42, 0x38, 0x09, 0x65, 0xf0, 0x14, 0xda, 0x19, 0x9e, 0xfa, 0x6b, 0x0b, + 0xab, 0x62, 0xef, 0x8d, 0xa7, 0xef, 0x63, 0x70, 0x23, 0xa8, 0xaf, 0x81, + 0xf3, 0xd1, 0x6e, 0x88, 0x67, 0x53, 0xec, 0x12, 0xa4, 0x29, 0x75, 0x8a, + 0xa7, 0xf2, 0x57, 0x3d, 0xa2, 0x83, 0x98, 0x97, 0xf2, 0x0a, 0x7d, 0xd4, + 0xe7, 0x43, 0x6e, 0x30, 0x78, 0x62, 0x22, 0x59, 0x59, 0xb8, 0x71, 0x27, + 0x45, 0xaa, 0x0f, 0x66, 0xc6, 0x55, 0x3f, 0xfa, 0x32, 0x17, 0x2b, 0x31, + 0x8f, 0x46, 0xa0, 0xfa, 0x69, 0x14, 0x7c, 0x9d, 0x9f, 0x5a, 0xe2, 0xeb, + 0x33, 0x4e, 0x10, 0xa6, 0xb3, 0xed, 0x77, 0x63, 0xd8, 0xc3, 0x9e, 0xf4, + 0xdd, 0xdf, 0x79, 0x9a, 0x7a, 0xd4, 0xee, 0xde, 0xdd, 0x9a, 0xcc, 0xc3, + 0xb7, 0xa9, 0x5d, 0xcc, 0x11, 0x3a, 0x07, 0xbb, 0x6f, 0x97, 0xa4, 0x01, + 0x23, 0x47, 0x95, 0x1f, 0xa3, 0x77, 0xfa, 0x58, 0x92, 0xc6, 0xc7, 0xd0, + 0xbd, 0xcf, 0x93, 0x18, 0x42, 0xb7, 0x7e, 0xf7, 0x9e, 0x65, 0xea, 0xd5, + 0x3b, 0xca, 0xed, 0xac, 0xc5, 0x70, 0xa1, 0xfe, 0xd4, 0x10, 0x9a, 0xf0, + 0x12, 0x04, 0x44, 0xac, 0x1a, 0x5b, 0x78, 0x50, 0x45, 0x57, 0x4c, 0x6f, + 0xbd, 0x80, 0xcb, 0x81, 0x5c, 0x2d, 0xb3, 0xbc, 0x76, 0xa1, 0x1e, 0x65, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4a, 0x30, 0x82, 0x01, + 0x46, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, + 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd2, 0x6f, 0xf7, + 0x96, 0xf4, 0x85, 0x3f, 0x72, 0x3c, 0x30, 0x7d, 0x23, 0xda, 0x85, 0x78, + 0x9b, 0xa3, 0x7c, 0x5a, 0x7c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79, + 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, + 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, + 0x6c, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, + 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, + 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, + 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, + 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, + 0x35, 0x33, 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa0, + 0xd4, 0xf7, 0x2c, 0xfb, 0x74, 0x0b, 0x7f, 0x64, 0xf1, 0xcd, 0x43, 0x6a, + 0x9f, 0x62, 0x53, 0x1c, 0x02, 0x7c, 0x98, 0x90, 0xa2, 0xee, 0x4f, 0x68, + 0xd4, 0x20, 0x1a, 0x73, 0x12, 0x3e, 0x77, 0xb3, 0x50, 0xeb, 0x72, 0xbc, + 0xee, 0x88, 0xbe, 0x7f, 0x17, 0xea, 0x77, 0x8f, 0x83, 0x61, 0x95, 0x4f, + 0x84, 0xa1, 0xcb, 0x32, 0x4f, 0x6c, 0x21, 0xbe, 0xd2, 0x69, 0x96, 0x7d, + 0x63, 0xbd, 0xdc, 0x2b, 0xa8, 0x1f, 0xd0, 0x13, 0x84, 0x70, 0xfe, 0xf6, + 0x35, 0x95, 0x89, 0xf9, 0xa6, 0x77, 0xb0, 0x46, 0xc8, 0xbb, 0xb7, 0x13, + 0xf5, 0xc9, 0x60, 0x69, 0xd6, 0x4c, 0xfe, 0xd2, 0x8e, 0xef, 0xd3, 0x60, + 0xc1, 0x80, 0x80, 0xe1, 0xe7, 0xfb, 0x8b, 0x6f, 0x21, 0x79, 0x4a, 0xe0, + 0xdc, 0xa9, 0x1b, 0xc1, 0xb7, 0xfb, 0xc3, 0x49, 0x59, 0x5c, 0xb5, 0x77, + 0x07, 0x44, 0xd4, 0x97, 0xfc, 0x49, 0x00, 0x89, 0x6f, 0x06, 0x4e, 0x01, + 0x70, 0x19, 0xac, 0x2f, 0x11, 0xc0, 0xe2, 0xe6, 0x0f, 0x2f, 0x86, 0x4b, + 0x8d, 0x7b, 0xc3, 0xb9, 0xa7, 0x2e, 0xf4, 0xf1, 0xac, 0x16, 0x3e, 0x39, + 0x49, 0x51, 0x9e, 0x17, 0x4b, 0x4f, 0x10, 0x3a, 0x5b, 0xa5, 0xa8, 0x92, + 0x6f, 0xfd, 0xfa, 0xd6, 0x0b, 0x03, 0x4d, 0x47, 0x56, 0x57, 0x19, 0xf3, + 0xcb, 0x6b, 0xf5, 0xf3, 0xd6, 0xcf, 0xb0, 0xf5, 0xf5, 0xa3, 0x11, 0xd2, + 0x20, 0x53, 0x13, 0x34, 0x37, 0x05, 0x2c, 0x43, 0x5a, 0x63, 0xdf, 0x8d, + 0x40, 0xd6, 0x85, 0x1e, 0x51, 0xe9, 0x51, 0x17, 0x1e, 0x03, 0x56, 0xc9, + 0xf1, 0x30, 0xad, 0xe7, 0x9b, 0x11, 0xa2, 0xb9, 0xd0, 0x31, 0x81, 0x9b, + 0x68, 0xb1, 0xd9, 0xe8, 0xf3, 0xe6, 0x94, 0x7e, 0xc7, 0xae, 0x13, 0x2f, + 0x87, 0xed, 0xd0, 0x25, 0xb0, 0x68, 0xf9, 0xde, 0x08, 0x5a, 0xf3, 0x29, + 0xcc, 0xd4, 0x92, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146019 (0x23a63) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Aug 27 20:40:40 2012 GMT + Not After : May 20 20:40:40 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b9:27:f9:4f:d8:f6:b7:15:3f:8f:cd:ce:d6:8d: + 1c:6b:fd:7f:da:54:21:4e:03:d8:ca:d0:72:52:15: + b8:c9:82:5b:58:79:84:ff:24:72:6f:f2:69:7f:bc: + 96:d9:9a:7a:c3:3e:a9:cf:50:22:13:0e:86:19:db: + e8:49:ef:8b:e6:d6:47:f2:fd:73:45:08:ae:8f:ac: + 5e:b6:f8:9e:7c:f7:10:ff:92:43:66:ef:1c:d4:ee: + a1:46:88:11:89:49:79:7a:25:ce:4b:6a:f0:d7:1c: + 76:1a:29:3c:c9:e4:fd:1e:85:dc:e0:31:65:05:47: + 16:ac:0a:07:4b:2e:70:5e:6b:06:a7:6b:3a:6c:af: + 05:12:c4:b2:11:25:d6:3e:97:29:f0:83:6c:57:1c: + d8:a5:ef:cc:ec:fd:d6:12:f1:3f:db:40:b4:ae:0f: + 18:d3:c5:af:40:92:5d:07:5e:4e:fe:62:17:37:89: + e9:8b:74:26:a2:ed:b8:0a:e7:6c:15:5b:35:90:72: + dd:d8:4d:21:d4:40:23:5c:8f:ee:80:31:16:ab:68: + 55:f4:0e:3b:54:e9:04:4d:f0:cc:4e:81:5e:e9:6f: + 52:69:4e:be:a6:16:6d:42:f5:51:ff:e0:0b:56:3c: + 98:4f:73:8f:0e:6f:1a:23:f1:c9:c8:d9:df:bc:ec: + 52:d7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + 11:4A:D0:73:39:D5:5B:69:08:5C:BA:3D:BF:64:9A:A8:8B:1C:55:BC + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://ocsp.geotrust.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-254 + Signature Algorithm: sha1WithRSAEncryption + 3c:e5:3d:5a:1b:a2:37:2a:e3:46:cf:36:96:18:3c:7b:f1:84: + c5:57:86:77:40:9d:35:f0:12:f0:78:18:fb:22:a4:de:98:4b: + 78:81:e6:4d:86:e3:91:0f:42:e3:b9:dc:a0:d6:ff:a9:f8:b1: + 79:97:99:d1:c3:6c:42:a5:92:94:e0:5d:0c:33:18:25:c9:2b: + 95:53:e0:e5:a9:0c:7d:47:fe:7f:51:31:44:5e:f7:2a:1e:35: + a2:94:32:f7:c9:ee:c0:b6:c6:9a:ac:de:99:21:6a:23:a0:38: + 64:ee:a3:c4:88:73:32:3b:50:ce:bf:ad:d3:75:1e:a6:f4:e9: + f9:42:6b:60:b2:dd:45:fd:5d:57:08:ce:2d:50:e6:12:32:16: + 13:8a:f2:94:a2:9b:47:a8:86:7f:d9:98:e5:f7:e5:76:74:64: + d8:91:bc:84:16:28:d8:25:44:30:7e:82:d8:ac:b1:e4:c0:e4: + 15:6c:db:b6:24:27:02:2a:01:12:85:ba:31:88:58:47:74:e3: + b8:d2:64:a6:c3:32:59:2e:29:4b:45:f1:5b:89:49:2e:82:9a: + c6:18:15:44:d0:2e:64:01:15:68:38:f9:f6:f9:66:03:0c:55: + 1b:9d:bf:00:40:ae:f0:48:27:4c:e0:80:5e:2d:b9:2a:15:7a: + bc:66:f8:35 +-----BEGIN CERTIFICATE----- +MIIEWTCCA0GgAwIBAgIDAjpjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTIwODI3MjA0MDQwWhcNMjIwNTIwMjA0MDQwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +U1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5J/lP +2Pa3FT+Pzc7WjRxr/X/aVCFOA9jK0HJSFbjJgltYeYT/JHJv8ml/vJbZmnrDPqnP +UCITDoYZ2+hJ74vm1kfy/XNFCK6PrF62+J589xD/kkNm7xzU7qFGiBGJSXl6Jc5L +avDXHHYaKTzJ5P0ehdzgMWUFRxasCgdLLnBeawanazpsrwUSxLIRJdY+lynwg2xX +HNil78zs/dYS8T/bQLSuDxjTxa9Akl0HXk7+Yhc3iemLdCai7bgK52wVWzWQct3Y +TSHUQCNcj+6AMRaraFX0DjtU6QRN8MxOgV7pb1JpTr6mFm1C9VH/4AtWPJhPc48O +bxoj8cnI2d+87FLXAgMBAAGjggFUMIIBUDAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjAdBgNVHQ4EFgQUEUrQcznVW2kIXLo9v2SaqIscVbwwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2gK4Yp +aHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYB +BQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20w +TAYDVR0gBEUwQzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93 +d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwKgYDVR0RBCMwIaQfMB0xGzAZ +BgNVBAMTElZlcmlTaWduTVBLSS0yLTI1NDANBgkqhkiG9w0BAQUFAAOCAQEAPOU9 +WhuiNyrjRs82lhg8e/GExVeGd0CdNfAS8HgY+yKk3phLeIHmTYbjkQ9C47ncoNb/ +qfixeZeZ0cNsQqWSlOBdDDMYJckrlVPg5akMfUf+f1ExRF73Kh41opQy98nuwLbG +mqzemSFqI6A4ZO6jxIhzMjtQzr+t03UepvTp+UJrYLLdRf1dVwjOLVDmEjIWE4ry +lKKbR6iGf9mY5ffldnRk2JG8hBYo2CVEMH6C2Kyx5MDkFWzbtiQnAioBEoW6MYhY +R3TjuNJkpsMyWS4pS0XxW4lJLoKaxhgVRNAuZAEVaDj59vlmAwxVG52/AECu8Egn +TOCAXi25KhV6vGb4NQ== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert13[] = { + 0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x41, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x63, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, + 0x38, 0x32, 0x37, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30, + 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x27, 0xf9, 0x4f, + 0xd8, 0xf6, 0xb7, 0x15, 0x3f, 0x8f, 0xcd, 0xce, 0xd6, 0x8d, 0x1c, 0x6b, + 0xfd, 0x7f, 0xda, 0x54, 0x21, 0x4e, 0x03, 0xd8, 0xca, 0xd0, 0x72, 0x52, + 0x15, 0xb8, 0xc9, 0x82, 0x5b, 0x58, 0x79, 0x84, 0xff, 0x24, 0x72, 0x6f, + 0xf2, 0x69, 0x7f, 0xbc, 0x96, 0xd9, 0x9a, 0x7a, 0xc3, 0x3e, 0xa9, 0xcf, + 0x50, 0x22, 0x13, 0x0e, 0x86, 0x19, 0xdb, 0xe8, 0x49, 0xef, 0x8b, 0xe6, + 0xd6, 0x47, 0xf2, 0xfd, 0x73, 0x45, 0x08, 0xae, 0x8f, 0xac, 0x5e, 0xb6, + 0xf8, 0x9e, 0x7c, 0xf7, 0x10, 0xff, 0x92, 0x43, 0x66, 0xef, 0x1c, 0xd4, + 0xee, 0xa1, 0x46, 0x88, 0x11, 0x89, 0x49, 0x79, 0x7a, 0x25, 0xce, 0x4b, + 0x6a, 0xf0, 0xd7, 0x1c, 0x76, 0x1a, 0x29, 0x3c, 0xc9, 0xe4, 0xfd, 0x1e, + 0x85, 0xdc, 0xe0, 0x31, 0x65, 0x05, 0x47, 0x16, 0xac, 0x0a, 0x07, 0x4b, + 0x2e, 0x70, 0x5e, 0x6b, 0x06, 0xa7, 0x6b, 0x3a, 0x6c, 0xaf, 0x05, 0x12, + 0xc4, 0xb2, 0x11, 0x25, 0xd6, 0x3e, 0x97, 0x29, 0xf0, 0x83, 0x6c, 0x57, + 0x1c, 0xd8, 0xa5, 0xef, 0xcc, 0xec, 0xfd, 0xd6, 0x12, 0xf1, 0x3f, 0xdb, + 0x40, 0xb4, 0xae, 0x0f, 0x18, 0xd3, 0xc5, 0xaf, 0x40, 0x92, 0x5d, 0x07, + 0x5e, 0x4e, 0xfe, 0x62, 0x17, 0x37, 0x89, 0xe9, 0x8b, 0x74, 0x26, 0xa2, + 0xed, 0xb8, 0x0a, 0xe7, 0x6c, 0x15, 0x5b, 0x35, 0x90, 0x72, 0xdd, 0xd8, + 0x4d, 0x21, 0xd4, 0x40, 0x23, 0x5c, 0x8f, 0xee, 0x80, 0x31, 0x16, 0xab, + 0x68, 0x55, 0xf4, 0x0e, 0x3b, 0x54, 0xe9, 0x04, 0x4d, 0xf0, 0xcc, 0x4e, + 0x81, 0x5e, 0xe9, 0x6f, 0x52, 0x69, 0x4e, 0xbe, 0xa6, 0x16, 0x6d, 0x42, + 0xf5, 0x51, 0xff, 0xe0, 0x0b, 0x56, 0x3c, 0x98, 0x4f, 0x73, 0x8f, 0x0e, + 0x6f, 0x1a, 0x23, 0xf1, 0xc9, 0xc8, 0xd9, 0xdf, 0xbc, 0xec, 0x52, 0xd7, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x54, 0x30, 0x82, 0x01, + 0x50, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, + 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x11, 0x4a, 0xd0, + 0x73, 0x39, 0xd5, 0x5b, 0x69, 0x08, 0x5c, 0xba, 0x3d, 0xbf, 0x64, 0x9a, + 0xa8, 0x8b, 0x1c, 0x55, 0xbc, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, + 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, + 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, + 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, + 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, + 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, + 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x32, 0x35, + 0x34, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0xe5, 0x3d, + 0x5a, 0x1b, 0xa2, 0x37, 0x2a, 0xe3, 0x46, 0xcf, 0x36, 0x96, 0x18, 0x3c, + 0x7b, 0xf1, 0x84, 0xc5, 0x57, 0x86, 0x77, 0x40, 0x9d, 0x35, 0xf0, 0x12, + 0xf0, 0x78, 0x18, 0xfb, 0x22, 0xa4, 0xde, 0x98, 0x4b, 0x78, 0x81, 0xe6, + 0x4d, 0x86, 0xe3, 0x91, 0x0f, 0x42, 0xe3, 0xb9, 0xdc, 0xa0, 0xd6, 0xff, + 0xa9, 0xf8, 0xb1, 0x79, 0x97, 0x99, 0xd1, 0xc3, 0x6c, 0x42, 0xa5, 0x92, + 0x94, 0xe0, 0x5d, 0x0c, 0x33, 0x18, 0x25, 0xc9, 0x2b, 0x95, 0x53, 0xe0, + 0xe5, 0xa9, 0x0c, 0x7d, 0x47, 0xfe, 0x7f, 0x51, 0x31, 0x44, 0x5e, 0xf7, + 0x2a, 0x1e, 0x35, 0xa2, 0x94, 0x32, 0xf7, 0xc9, 0xee, 0xc0, 0xb6, 0xc6, + 0x9a, 0xac, 0xde, 0x99, 0x21, 0x6a, 0x23, 0xa0, 0x38, 0x64, 0xee, 0xa3, + 0xc4, 0x88, 0x73, 0x32, 0x3b, 0x50, 0xce, 0xbf, 0xad, 0xd3, 0x75, 0x1e, + 0xa6, 0xf4, 0xe9, 0xf9, 0x42, 0x6b, 0x60, 0xb2, 0xdd, 0x45, 0xfd, 0x5d, + 0x57, 0x08, 0xce, 0x2d, 0x50, 0xe6, 0x12, 0x32, 0x16, 0x13, 0x8a, 0xf2, + 0x94, 0xa2, 0x9b, 0x47, 0xa8, 0x86, 0x7f, 0xd9, 0x98, 0xe5, 0xf7, 0xe5, + 0x76, 0x74, 0x64, 0xd8, 0x91, 0xbc, 0x84, 0x16, 0x28, 0xd8, 0x25, 0x44, + 0x30, 0x7e, 0x82, 0xd8, 0xac, 0xb1, 0xe4, 0xc0, 0xe4, 0x15, 0x6c, 0xdb, + 0xb6, 0x24, 0x27, 0x02, 0x2a, 0x01, 0x12, 0x85, 0xba, 0x31, 0x88, 0x58, + 0x47, 0x74, 0xe3, 0xb8, 0xd2, 0x64, 0xa6, 0xc3, 0x32, 0x59, 0x2e, 0x29, + 0x4b, 0x45, 0xf1, 0x5b, 0x89, 0x49, 0x2e, 0x82, 0x9a, 0xc6, 0x18, 0x15, + 0x44, 0xd0, 0x2e, 0x64, 0x01, 0x15, 0x68, 0x38, 0xf9, 0xf6, 0xf9, 0x66, + 0x03, 0x0c, 0x55, 0x1b, 0x9d, 0xbf, 0x00, 0x40, 0xae, 0xf0, 0x48, 0x27, + 0x4c, 0xe0, 0x80, 0x5e, 0x2d, 0xb9, 0x2a, 0x15, 0x7a, 0xbc, 0x66, 0xf8, + 0x35, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:00:00:00:00:01:44:4e:f0:3e:20 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA + Validity + Not Before: Feb 20 10:00:00 2014 GMT + Not After : Feb 20 10:00:00 2024 GMT + Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Domain Validation CA - SHA256 - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a9:dd:cc:0e:b3:e2:32:39:dd:49:22:a8:13:69: + 93:87:88:e1:0c:ee:71:7d:bd:90:87:96:5d:59:f2: + cc:b3:d2:58:57:57:f9:46:ef:6c:26:d8:36:42:8e: + 7e:30:b3:2f:9a:3e:53:7b:1f:6e:b6:a2:4c:45:1f: + 3c:d3:15:93:1c:89:ed:3c:f4:57:de:ca:bd:ec:06: + 9a:6a:2a:a0:19:52:7f:51:d1:74:39:08:9f:ab:eb: + d7:86:13:15:97:ae:36:c3:54:66:0e:5a:f2:a0:73: + 85:31:e3:b2:64:14:6a:ff:a5:a2:8e:24:bb:bd:85: + 52:15:a2:79:ee:f0:b5:ee:3d:b8:f4:7d:80:bc:d9: + 90:35:65:b8:17:a9:ad:b3:98:9f:a0:7e:7d:6e:fb: + 3f:ad:7c:c2:1b:59:36:96:da:37:32:4b:4b:5d:35: + 02:63:8e:db:a7:cf:62:ee:cc:2e:d4:8d:c9:bd:3c: + 6a:91:72:a2:22:a7:72:2d:20:d1:fa:ca:37:da:18: + 98:e6:16:24:71:25:4b:c4:e5:7b:89:52:09:02:fd: + 59:2b:04:6e:ca:07:81:d4:b3:da:da:db:e3:cc:80: + a8:56:07:06:7c:96:08:37:9d:db:38:b6:62:34:91: + 62:07:74:01:38:d8:72:30:e2:eb:90:71:26:62:c0: + 57:f3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + EA:4E:7C:D4:80:2D:E5:15:81:86:26:8C:82:6D:C0:98:A4:CF:97:0F + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.globalsign.com/repository/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.globalsign.net/root.crl + + Authority Information Access: + OCSP - URI:http://ocsp.globalsign.com/rootr1 + + X509v3 Authority Key Identifier: + keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + + Signature Algorithm: sha256WithRSAEncryption + d7:45:9e:a0:dc:e0:e3:61:5a:0b:7d:77:84:17:2d:65:5a:82: + 9a:8d:a3:27:2a:85:f7:c9:ef:e9:86:fd:d4:47:cd:01:52:96: + c5:43:bd:37:b1:e1:b8:f2:a9:d2:8a:11:84:71:91:15:89:dc: + 02:9d:0b:cb:6c:33:85:34:28:9e:20:b2:b1:97:dc:6d:0b:10: + c1:3c:cd:5f:ea:5d:d7:98:31:c5:34:99:5c:00:61:55:c4:1b: + 02:5b:c5:e3:89:c8:b4:b8:6f:1e:38:f2:56:26:e9:41:ef:3d: + cd:ac:99:4f:59:4a:57:2d:4b:7d:ae:c7:88:fb:d6:98:3b:f5: + e5:f0:e8:89:89:b9:8b:03:cb:5a:23:1f:a4:fd:b8:ea:fb:2e: + 9d:ae:6a:73:09:bc:fc:d5:a0:b5:44:82:ab:44:91:2e:50:2e: + 57:c1:43:d8:91:04:8b:e9:11:2e:5f:b4:3f:79:df:1e:fb:3f: + 30:00:8b:53:e3:b7:2c:1d:3b:4d:8b:dc:e4:64:1d:04:58:33: + af:1b:55:e7:ab:0c:bf:30:04:74:e4:f3:0e:2f:30:39:8d:4b: + 04:8c:1e:75:66:66:49:e0:be:40:34:c7:5c:5a:51:92:ba:12: + 3c:52:d5:04:82:55:2d:67:a5:df:b7:95:7c:ee:3f:c3:08:ba: + 04:be:c0:46 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgILBAAAAAABRE7wPiAwDQYJKoZIhvcNAQELBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw +MDBaFw0yNDAyMjAxMDAwMDBaMGAxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMTYwNAYDVQQDEy1HbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0 +aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCp3cwOs+IyOd1JIqgTaZOHiOEM7nF9vZCHll1Z8syz0lhXV/lG72wm2DZC +jn4wsy+aPlN7H262okxFHzzTFZMcie089Ffeyr3sBppqKqAZUn9R0XQ5CJ+r69eG +ExWXrjbDVGYOWvKgc4Ux47JkFGr/paKOJLu9hVIVonnu8LXuPbj0fYC82ZA1ZbgX +qa2zmJ+gfn1u+z+tfMIbWTaW2jcyS0tdNQJjjtunz2LuzC7Ujcm9PGqRcqIip3It +INH6yjfaGJjmFiRxJUvE5XuJUgkC/VkrBG7KB4HUs9ra2+PMgKhWBwZ8lgg3nds4 +tmI0kWIHdAE42HIw4uuQcSZiwFfzAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMC +AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6k581IAt5RWBhiaMgm3A +mKTPlw8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v +d3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSG +Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEE +MTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290 +cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEL +BQADggEBANdFnqDc4ONhWgt9d4QXLWVagpqNoycqhffJ7+mG/dRHzQFSlsVDvTex +4bjyqdKKEYRxkRWJ3AKdC8tsM4U0KJ4gsrGX3G0LEME8zV/qXdeYMcU0mVwAYVXE +GwJbxeOJyLS4bx448lYm6UHvPc2smU9ZSlctS32ux4j71pg79eXw6ImJuYsDy1oj +H6T9uOr7Lp2uanMJvPzVoLVEgqtEkS5QLlfBQ9iRBIvpES5ftD953x77PzAAi1Pj +tywdO02L3ORkHQRYM68bVeerDL8wBHTk8w4vMDmNSwSMHnVmZkngvkA0x1xaUZK6 +EjxS1QSCVS1npd+3lXzuP8MIugS+wEY= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert14[] = { + 0x30, 0x82, 0x04, 0x63, 0x30, 0x82, 0x03, 0x4b, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0, + 0x3e, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, + 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x60, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, + 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, + 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, + 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xa9, 0xdd, 0xcc, 0x0e, 0xb3, 0xe2, 0x32, + 0x39, 0xdd, 0x49, 0x22, 0xa8, 0x13, 0x69, 0x93, 0x87, 0x88, 0xe1, 0x0c, + 0xee, 0x71, 0x7d, 0xbd, 0x90, 0x87, 0x96, 0x5d, 0x59, 0xf2, 0xcc, 0xb3, + 0xd2, 0x58, 0x57, 0x57, 0xf9, 0x46, 0xef, 0x6c, 0x26, 0xd8, 0x36, 0x42, + 0x8e, 0x7e, 0x30, 0xb3, 0x2f, 0x9a, 0x3e, 0x53, 0x7b, 0x1f, 0x6e, 0xb6, + 0xa2, 0x4c, 0x45, 0x1f, 0x3c, 0xd3, 0x15, 0x93, 0x1c, 0x89, 0xed, 0x3c, + 0xf4, 0x57, 0xde, 0xca, 0xbd, 0xec, 0x06, 0x9a, 0x6a, 0x2a, 0xa0, 0x19, + 0x52, 0x7f, 0x51, 0xd1, 0x74, 0x39, 0x08, 0x9f, 0xab, 0xeb, 0xd7, 0x86, + 0x13, 0x15, 0x97, 0xae, 0x36, 0xc3, 0x54, 0x66, 0x0e, 0x5a, 0xf2, 0xa0, + 0x73, 0x85, 0x31, 0xe3, 0xb2, 0x64, 0x14, 0x6a, 0xff, 0xa5, 0xa2, 0x8e, + 0x24, 0xbb, 0xbd, 0x85, 0x52, 0x15, 0xa2, 0x79, 0xee, 0xf0, 0xb5, 0xee, + 0x3d, 0xb8, 0xf4, 0x7d, 0x80, 0xbc, 0xd9, 0x90, 0x35, 0x65, 0xb8, 0x17, + 0xa9, 0xad, 0xb3, 0x98, 0x9f, 0xa0, 0x7e, 0x7d, 0x6e, 0xfb, 0x3f, 0xad, + 0x7c, 0xc2, 0x1b, 0x59, 0x36, 0x96, 0xda, 0x37, 0x32, 0x4b, 0x4b, 0x5d, + 0x35, 0x02, 0x63, 0x8e, 0xdb, 0xa7, 0xcf, 0x62, 0xee, 0xcc, 0x2e, 0xd4, + 0x8d, 0xc9, 0xbd, 0x3c, 0x6a, 0x91, 0x72, 0xa2, 0x22, 0xa7, 0x72, 0x2d, + 0x20, 0xd1, 0xfa, 0xca, 0x37, 0xda, 0x18, 0x98, 0xe6, 0x16, 0x24, 0x71, + 0x25, 0x4b, 0xc4, 0xe5, 0x7b, 0x89, 0x52, 0x09, 0x02, 0xfd, 0x59, 0x2b, + 0x04, 0x6e, 0xca, 0x07, 0x81, 0xd4, 0xb3, 0xda, 0xda, 0xdb, 0xe3, 0xcc, + 0x80, 0xa8, 0x56, 0x07, 0x06, 0x7c, 0x96, 0x08, 0x37, 0x9d, 0xdb, 0x38, + 0xb6, 0x62, 0x34, 0x91, 0x62, 0x07, 0x74, 0x01, 0x38, 0xd8, 0x72, 0x30, + 0xe2, 0xeb, 0x90, 0x71, 0x26, 0x62, 0xc0, 0x57, 0xf3, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25, 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xea, 0x4e, 0x7c, + 0xd4, 0x80, 0x2d, 0xe5, 0x15, 0x81, 0x86, 0x26, 0x8c, 0x82, 0x6d, 0xc0, + 0x98, 0xa4, 0xcf, 0x97, 0x0f, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, + 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, + 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, + 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, + 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, + 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, + 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, + 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, + 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xd7, 0x45, 0x9e, 0xa0, 0xdc, + 0xe0, 0xe3, 0x61, 0x5a, 0x0b, 0x7d, 0x77, 0x84, 0x17, 0x2d, 0x65, 0x5a, + 0x82, 0x9a, 0x8d, 0xa3, 0x27, 0x2a, 0x85, 0xf7, 0xc9, 0xef, 0xe9, 0x86, + 0xfd, 0xd4, 0x47, 0xcd, 0x01, 0x52, 0x96, 0xc5, 0x43, 0xbd, 0x37, 0xb1, + 0xe1, 0xb8, 0xf2, 0xa9, 0xd2, 0x8a, 0x11, 0x84, 0x71, 0x91, 0x15, 0x89, + 0xdc, 0x02, 0x9d, 0x0b, 0xcb, 0x6c, 0x33, 0x85, 0x34, 0x28, 0x9e, 0x20, + 0xb2, 0xb1, 0x97, 0xdc, 0x6d, 0x0b, 0x10, 0xc1, 0x3c, 0xcd, 0x5f, 0xea, + 0x5d, 0xd7, 0x98, 0x31, 0xc5, 0x34, 0x99, 0x5c, 0x00, 0x61, 0x55, 0xc4, + 0x1b, 0x02, 0x5b, 0xc5, 0xe3, 0x89, 0xc8, 0xb4, 0xb8, 0x6f, 0x1e, 0x38, + 0xf2, 0x56, 0x26, 0xe9, 0x41, 0xef, 0x3d, 0xcd, 0xac, 0x99, 0x4f, 0x59, + 0x4a, 0x57, 0x2d, 0x4b, 0x7d, 0xae, 0xc7, 0x88, 0xfb, 0xd6, 0x98, 0x3b, + 0xf5, 0xe5, 0xf0, 0xe8, 0x89, 0x89, 0xb9, 0x8b, 0x03, 0xcb, 0x5a, 0x23, + 0x1f, 0xa4, 0xfd, 0xb8, 0xea, 0xfb, 0x2e, 0x9d, 0xae, 0x6a, 0x73, 0x09, + 0xbc, 0xfc, 0xd5, 0xa0, 0xb5, 0x44, 0x82, 0xab, 0x44, 0x91, 0x2e, 0x50, + 0x2e, 0x57, 0xc1, 0x43, 0xd8, 0x91, 0x04, 0x8b, 0xe9, 0x11, 0x2e, 0x5f, + 0xb4, 0x3f, 0x79, 0xdf, 0x1e, 0xfb, 0x3f, 0x30, 0x00, 0x8b, 0x53, 0xe3, + 0xb7, 0x2c, 0x1d, 0x3b, 0x4d, 0x8b, 0xdc, 0xe4, 0x64, 0x1d, 0x04, 0x58, + 0x33, 0xaf, 0x1b, 0x55, 0xe7, 0xab, 0x0c, 0xbf, 0x30, 0x04, 0x74, 0xe4, + 0xf3, 0x0e, 0x2f, 0x30, 0x39, 0x8d, 0x4b, 0x04, 0x8c, 0x1e, 0x75, 0x66, + 0x66, 0x49, 0xe0, 0xbe, 0x40, 0x34, 0xc7, 0x5c, 0x5a, 0x51, 0x92, 0xba, + 0x12, 0x3c, 0x52, 0xd5, 0x04, 0x82, 0x55, 0x2d, 0x67, 0xa5, 0xdf, 0xb7, + 0x95, 0x7c, 0xee, 0x3f, 0xc3, 0x08, 0xba, 0x04, 0xbe, 0xc0, 0x46, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:00:00:00:00:01:44:4e:f0:42:47 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA + Validity + Not Before: Feb 20 10:00:00 2014 GMT + Not After : Feb 20 10:00:00 2024 GMT + Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Organization Validation CA - SHA256 - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c7:0e:6c:3f:23:93:7f:cc:70:a5:9d:20:c3:0e: + 53:3f:7e:c0:4e:c2:98:49:ca:47:d5:23:ef:03:34: + 85:74:c8:a3:02:2e:46:5c:0b:7d:c9:88:9d:4f:8b: + f0:f8:9c:6c:8c:55:35:db:bf:f2:b3:ea:fb:e3:56: + e7:4a:46:d9:13:22:ca:36:d5:9b:c1:a8:e3:96:43: + 93:f2:0c:bc:e6:f9:e6:e8:99:c8:63:48:78:7f:57: + 36:69:1a:19:1d:5a:d1:d4:7d:c2:9c:d4:7f:e1:80: + 12:ae:7a:ea:88:ea:57:d8:ca:0a:0a:3a:12:49:a2: + 62:19:7a:0d:24:f7:37:eb:b4:73:92:7b:05:23:9b: + 12:b5:ce:eb:29:df:a4:14:02:b9:01:a5:d4:a6:9c: + 43:64:88:de:f8:7e:fe:e3:f5:1e:e5:fe:dc:a3:a8: + e4:66:31:d9:4c:25:e9:18:b9:89:59:09:ae:e9:9d: + 1c:6d:37:0f:4a:1e:35:20:28:e2:af:d4:21:8b:01: + c4:45:ad:6e:2b:63:ab:92:6b:61:0a:4d:20:ed:73: + ba:7c:ce:fe:16:b5:db:9f:80:f0:d6:8b:6c:d9:08: + 79:4a:4f:78:65:da:92:bc:be:35:f9:b3:c4:f9:27: + 80:4e:ff:96:52:e6:02:20:e1:07:73:e9:5d:2b:bd: + b2:f1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + 96:DE:61:F1:BD:1C:16:29:53:1C:C0:CC:7D:3B:83:00:40:E6:1A:7C + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.globalsign.com/repository/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.globalsign.net/root.crl + + Authority Information Access: + OCSP - URI:http://ocsp.globalsign.com/rootr1 + + X509v3 Authority Key Identifier: + keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + + Signature Algorithm: sha256WithRSAEncryption + 46:2a:ee:5e:bd:ae:01:60:37:31:11:86:71:74:b6:46:49:c8: + 10:16:fe:2f:62:23:17:ab:1f:87:f8:82:ed:ca:df:0e:2c:df: + 64:75:8e:e5:18:72:a7:8c:3a:8b:c9:ac:a5:77:50:f7:ef:9e: + a4:e0:a0:8f:14:57:a3:2a:5f:ec:7e:6d:10:e6:ba:8d:b0:08: + 87:76:0e:4c:b2:d9:51:bb:11:02:f2:5c:dd:1c:bd:f3:55:96: + 0f:d4:06:c0:fc:e2:23:8a:24:70:d3:bb:f0:79:1a:a7:61:70: + 83:8a:af:06:c5:20:d8:a1:63:d0:6c:ae:4f:32:d7:ae:7c:18: + 45:75:05:29:77:df:42:40:64:64:86:be:2a:76:09:31:6f:1d: + 24:f4:99:d0:85:fe:f2:21:08:f9:c6:f6:f1:d0:59:ed:d6:56: + 3c:08:28:03:67:ba:f0:f9:f1:90:16:47:ae:67:e6:bc:80:48: + e9:42:76:34:97:55:69:24:0e:83:d6:a0:2d:b4:f5:f3:79:8a: + 49:28:74:1a:41:a1:c2:d3:24:88:35:30:60:94:17:b4:e1:04: + 22:31:3d:3b:2f:17:06:b2:b8:9d:86:2b:5a:69:ef:83:f5:4b: + c4:aa:b4:2a:f8:7c:a1:b1:85:94:8c:f4:0c:87:0c:f4:ac:40: + f8:59:49:98 +-----BEGIN CERTIFICATE----- +MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw +MDBaFw0yNDAyMjAxMDAwMDBaMGYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMTwwOgYDVQQDEzNHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBW +YWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDHDmw/I5N/zHClnSDDDlM/fsBOwphJykfVI+8DNIV0yKMCLkZc +C33JiJ1Pi/D4nGyMVTXbv/Kz6vvjVudKRtkTIso21ZvBqOOWQ5PyDLzm+ebomchj +SHh/VzZpGhkdWtHUfcKc1H/hgBKueuqI6lfYygoKOhJJomIZeg0k9zfrtHOSewUj +mxK1zusp36QUArkBpdSmnENkiN74fv7j9R7l/tyjqORmMdlMJekYuYlZCa7pnRxt +Nw9KHjUgKOKv1CGLAcRFrW4rY6uSa2EKTSDtc7p8zv4WtdufgPDWi2zZCHlKT3hl +2pK8vjX5s8T5J4BO/5ZS5gIg4Qdz6V0rvbLxAgMBAAGjggElMIIBITAOBgNVHQ8B +Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlt5h8b0cFilT +HMDMfTuDAEDmGnwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0 +dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCow +KKAmoCSGImh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYB +BQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNv +bS9yb290cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZI +hvcNAQELBQADggEBAEYq7l69rgFgNzERhnF0tkZJyBAW/i9iIxerH4f4gu3K3w4s +32R1juUYcqeMOovJrKV3UPfvnqTgoI8UV6MqX+x+bRDmuo2wCId2Dkyy2VG7EQLy +XN0cvfNVlg/UBsD84iOKJHDTu/B5GqdhcIOKrwbFINihY9Bsrk8y1658GEV1BSl3 +30JAZGSGvip2CTFvHST0mdCF/vIhCPnG9vHQWe3WVjwIKANnuvD58ZAWR65n5ryA +SOlCdjSXVWkkDoPWoC209fN5ikkodBpBocLTJIg1MGCUF7ThBCIxPTsvFwayuJ2G +K1pp74P1S8SqtCr4fKGxhZSM9AyHDPSsQPhZSZg= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert15[] = { + 0x30, 0x82, 0x04, 0x69, 0x30, 0x82, 0x03, 0x51, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0, + 0x42, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, + 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, + 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, + 0x31, 0x3c, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x33, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x4f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, + 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, + 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc7, + 0x0e, 0x6c, 0x3f, 0x23, 0x93, 0x7f, 0xcc, 0x70, 0xa5, 0x9d, 0x20, 0xc3, + 0x0e, 0x53, 0x3f, 0x7e, 0xc0, 0x4e, 0xc2, 0x98, 0x49, 0xca, 0x47, 0xd5, + 0x23, 0xef, 0x03, 0x34, 0x85, 0x74, 0xc8, 0xa3, 0x02, 0x2e, 0x46, 0x5c, + 0x0b, 0x7d, 0xc9, 0x88, 0x9d, 0x4f, 0x8b, 0xf0, 0xf8, 0x9c, 0x6c, 0x8c, + 0x55, 0x35, 0xdb, 0xbf, 0xf2, 0xb3, 0xea, 0xfb, 0xe3, 0x56, 0xe7, 0x4a, + 0x46, 0xd9, 0x13, 0x22, 0xca, 0x36, 0xd5, 0x9b, 0xc1, 0xa8, 0xe3, 0x96, + 0x43, 0x93, 0xf2, 0x0c, 0xbc, 0xe6, 0xf9, 0xe6, 0xe8, 0x99, 0xc8, 0x63, + 0x48, 0x78, 0x7f, 0x57, 0x36, 0x69, 0x1a, 0x19, 0x1d, 0x5a, 0xd1, 0xd4, + 0x7d, 0xc2, 0x9c, 0xd4, 0x7f, 0xe1, 0x80, 0x12, 0xae, 0x7a, 0xea, 0x88, + 0xea, 0x57, 0xd8, 0xca, 0x0a, 0x0a, 0x3a, 0x12, 0x49, 0xa2, 0x62, 0x19, + 0x7a, 0x0d, 0x24, 0xf7, 0x37, 0xeb, 0xb4, 0x73, 0x92, 0x7b, 0x05, 0x23, + 0x9b, 0x12, 0xb5, 0xce, 0xeb, 0x29, 0xdf, 0xa4, 0x14, 0x02, 0xb9, 0x01, + 0xa5, 0xd4, 0xa6, 0x9c, 0x43, 0x64, 0x88, 0xde, 0xf8, 0x7e, 0xfe, 0xe3, + 0xf5, 0x1e, 0xe5, 0xfe, 0xdc, 0xa3, 0xa8, 0xe4, 0x66, 0x31, 0xd9, 0x4c, + 0x25, 0xe9, 0x18, 0xb9, 0x89, 0x59, 0x09, 0xae, 0xe9, 0x9d, 0x1c, 0x6d, + 0x37, 0x0f, 0x4a, 0x1e, 0x35, 0x20, 0x28, 0xe2, 0xaf, 0xd4, 0x21, 0x8b, + 0x01, 0xc4, 0x45, 0xad, 0x6e, 0x2b, 0x63, 0xab, 0x92, 0x6b, 0x61, 0x0a, + 0x4d, 0x20, 0xed, 0x73, 0xba, 0x7c, 0xce, 0xfe, 0x16, 0xb5, 0xdb, 0x9f, + 0x80, 0xf0, 0xd6, 0x8b, 0x6c, 0xd9, 0x08, 0x79, 0x4a, 0x4f, 0x78, 0x65, + 0xda, 0x92, 0xbc, 0xbe, 0x35, 0xf9, 0xb3, 0xc4, 0xf9, 0x27, 0x80, 0x4e, + 0xff, 0x96, 0x52, 0xe6, 0x02, 0x20, 0xe1, 0x07, 0x73, 0xe9, 0x5d, 0x2b, + 0xbd, 0xb2, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25, + 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x96, 0xde, 0x61, 0xf1, 0xbd, 0x1c, 0x16, 0x29, 0x53, + 0x1c, 0xc0, 0xcc, 0x7d, 0x3b, 0x83, 0x00, 0x40, 0xe6, 0x1a, 0x7c, 0x30, + 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, + 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, + 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, + 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, + 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, + 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0x46, 0x2a, 0xee, 0x5e, 0xbd, 0xae, 0x01, 0x60, 0x37, 0x31, 0x11, + 0x86, 0x71, 0x74, 0xb6, 0x46, 0x49, 0xc8, 0x10, 0x16, 0xfe, 0x2f, 0x62, + 0x23, 0x17, 0xab, 0x1f, 0x87, 0xf8, 0x82, 0xed, 0xca, 0xdf, 0x0e, 0x2c, + 0xdf, 0x64, 0x75, 0x8e, 0xe5, 0x18, 0x72, 0xa7, 0x8c, 0x3a, 0x8b, 0xc9, + 0xac, 0xa5, 0x77, 0x50, 0xf7, 0xef, 0x9e, 0xa4, 0xe0, 0xa0, 0x8f, 0x14, + 0x57, 0xa3, 0x2a, 0x5f, 0xec, 0x7e, 0x6d, 0x10, 0xe6, 0xba, 0x8d, 0xb0, + 0x08, 0x87, 0x76, 0x0e, 0x4c, 0xb2, 0xd9, 0x51, 0xbb, 0x11, 0x02, 0xf2, + 0x5c, 0xdd, 0x1c, 0xbd, 0xf3, 0x55, 0x96, 0x0f, 0xd4, 0x06, 0xc0, 0xfc, + 0xe2, 0x23, 0x8a, 0x24, 0x70, 0xd3, 0xbb, 0xf0, 0x79, 0x1a, 0xa7, 0x61, + 0x70, 0x83, 0x8a, 0xaf, 0x06, 0xc5, 0x20, 0xd8, 0xa1, 0x63, 0xd0, 0x6c, + 0xae, 0x4f, 0x32, 0xd7, 0xae, 0x7c, 0x18, 0x45, 0x75, 0x05, 0x29, 0x77, + 0xdf, 0x42, 0x40, 0x64, 0x64, 0x86, 0xbe, 0x2a, 0x76, 0x09, 0x31, 0x6f, + 0x1d, 0x24, 0xf4, 0x99, 0xd0, 0x85, 0xfe, 0xf2, 0x21, 0x08, 0xf9, 0xc6, + 0xf6, 0xf1, 0xd0, 0x59, 0xed, 0xd6, 0x56, 0x3c, 0x08, 0x28, 0x03, 0x67, + 0xba, 0xf0, 0xf9, 0xf1, 0x90, 0x16, 0x47, 0xae, 0x67, 0xe6, 0xbc, 0x80, + 0x48, 0xe9, 0x42, 0x76, 0x34, 0x97, 0x55, 0x69, 0x24, 0x0e, 0x83, 0xd6, + 0xa0, 0x2d, 0xb4, 0xf5, 0xf3, 0x79, 0x8a, 0x49, 0x28, 0x74, 0x1a, 0x41, + 0xa1, 0xc2, 0xd3, 0x24, 0x88, 0x35, 0x30, 0x60, 0x94, 0x17, 0xb4, 0xe1, + 0x04, 0x22, 0x31, 0x3d, 0x3b, 0x2f, 0x17, 0x06, 0xb2, 0xb8, 0x9d, 0x86, + 0x2b, 0x5a, 0x69, 0xef, 0x83, 0xf5, 0x4b, 0xc4, 0xaa, 0xb4, 0x2a, 0xf8, + 0x7c, 0xa1, 0xb1, 0x85, 0x94, 0x8c, 0xf4, 0x0c, 0x87, 0x0c, 0xf4, 0xac, + 0x40, 0xf8, 0x59, 0x49, 0x98, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 4d:5f:2c:34:08:b2:4c:20:cd:6d:50:7e:24:4d:c9:ec + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Feb 8 00:00:00 2010 GMT + Not After : Feb 7 23:59:59 2020 GMT + Subject: C=US, O=Thawte, Inc., CN=Thawte SSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:99:e4:85:5b:76:49:7d:2f:05:d8:c5:ac:c8:c8: + a9:d3:dc:98:e6:d7:34:a6:2f:0c:f2:22:26:d8:a3: + c9:14:4c:8f:05:a4:45:e8:14:0c:58:90:05:1a:b7: + c5:c1:06:a5:80:af:bb:1d:49:6b:52:34:88:c3:59: + e7:ef:6b:c4:27:41:8c:2b:66:1d:d0:e0:a3:97:98: + 19:34:4b:41:d5:98:d5:c7:05:ad:a2:e4:d7:ed:0c: + ad:4f:c1:b5:b0:21:fd:3e:50:53:b2:c4:90:d0:d4: + 30:67:6c:9a:f1:0e:74:c4:c2:dc:8a:e8:97:ff:c9: + 92:ae:01:8a:56:0a:98:32:b0:00:23:ec:90:1a:60: + c3:ed:bb:3a:cb:0f:63:9f:0d:44:c9:52:e1:25:96: + bf:ed:50:95:89:7f:56:14:b1:b7:61:1d:1c:07:8c: + 3a:2c:f7:ff:80:de:39:45:d5:af:1a:d1:78:d8:c7: + 71:6a:a3:19:a7:32:50:21:e9:f2:0e:a1:c6:13:03: + 44:48:d1:66:a8:52:57:d7:11:b4:93:8b:e5:99:9f: + 5d:e7:78:51:e5:4d:f6:b7:59:b4:76:b5:09:37:4d: + 06:38:13:7a:1c:08:98:5c:c4:48:4a:cb:52:a0:a9: + f8:b1:9d:8e:7b:79:b0:20:2f:3c:96:a8:11:62:47: + bb:11 + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.thawte.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.thawte.com/ThawtePCA.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-9 + X509v3 Subject Key Identifier: + A7:A2:83:BB:34:45:40:3D:FC:D5:30:4F:12:B9:3E:A1:01:9F:F6:DB + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha1WithRSAEncryption + 80:22:80:e0:6c:c8:95:16:d7:57:26:87:f3:72:34:db:c6:72: + 56:27:3e:d3:96:f6:2e:25:91:a5:3e:33:97:a7:4b:e5:2f:fb: + 25:7d:2f:07:61:fa:6f:83:74:4c:4c:53:72:20:a4:7a:cf:51: + 51:56:81:88:b0:6d:1f:36:2c:c8:2b:b1:88:99:c1:fe:44:ab: + 48:51:7c:d8:f2:44:64:2a:d8:71:a7:fb:1a:2f:f9:19:8d:34: + b2:23:bf:c4:4c:55:1d:8e:44:e8:aa:5d:9a:dd:9f:fd:03:c7: + ba:24:43:8d:2d:47:44:db:f6:d8:98:c8:b2:f9:da:ef:ed:29: + 5c:69:12:fa:d1:23:96:0f:bf:9c:0d:f2:79:45:53:37:9a:56: + 2f:e8:57:10:70:f6:ee:89:0c:49:89:9a:c1:23:f5:c2:2a:cc: + 41:cf:22:ab:65:6e:b7:94:82:6d:2f:40:5f:58:de:eb:95:2b: + a6:72:68:52:19:91:2a:ae:75:9d:4e:92:e6:ca:de:54:ea:18: + ab:25:3c:e6:64:a6:79:1f:26:7d:61:ed:7d:d2:e5:71:55:d8: + 93:17:7c:14:38:30:3c:df:86:e3:4c:ad:49:e3:97:59:ce:1b: + 9b:2b:ce:dc:65:d4:0b:28:6b:4e:84:46:51:44:f7:33:08:2d: + 58:97:21:ae +-----BEGIN CERTIFICATE----- +MIIEbDCCA1SgAwIBAgIQTV8sNAiyTCDNbVB+JE3J7DANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTAwMjA4MDAwMDAwWhcNMjAw +MjA3MjM1OTU5WjA8MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMu +MRYwFAYDVQQDEw1UaGF3dGUgU1NMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAmeSFW3ZJfS8F2MWsyMip09yY5tc0pi8M8iIm2KPJFEyPBaRF6BQM +WJAFGrfFwQalgK+7HUlrUjSIw1nn72vEJ0GMK2Yd0OCjl5gZNEtB1ZjVxwWtouTX +7QytT8G1sCH9PlBTssSQ0NQwZ2ya8Q50xMLciuiX/8mSrgGKVgqYMrAAI+yQGmDD +7bs6yw9jnw1EyVLhJZa/7VCViX9WFLG3YR0cB4w6LPf/gN45RdWvGtF42MdxaqMZ +pzJQIenyDqHGEwNESNFmqFJX1xG0k4vlmZ9d53hR5U32t1m0drUJN00GOBN6HAiY +XMRISstSoKn4sZ2Oe3mwIC88lqgRYke7EQIDAQABo4H7MIH4MDIGCCsGAQUFBwEB +BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTASBgNVHRMB +Af8ECDAGAQH/AgEAMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudGhhd3Rl +LmNvbS9UaGF3dGVQQ0EuY3JsMA4GA1UdDwEB/wQEAwIBBjAoBgNVHREEITAfpB0w +GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItOTAdBgNVHQ4EFgQUp6KDuzRFQD38 +1TBPErk+oQGf9tswHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutXSFAwDQYJ +KoZIhvcNAQEFBQADggEBAIAigOBsyJUW11cmh/NyNNvGclYnPtOW9i4lkaU+M5en +S+Uv+yV9Lwdh+m+DdExMU3IgpHrPUVFWgYiwbR82LMgrsYiZwf5Eq0hRfNjyRGQq +2HGn+xov+RmNNLIjv8RMVR2OROiqXZrdn/0Dx7okQ40tR0Tb9tiYyLL52u/tKVxp +EvrRI5YPv5wN8nlFUzeaVi/oVxBw9u6JDEmJmsEj9cIqzEHPIqtlbreUgm0vQF9Y +3uuVK6ZyaFIZkSqudZ1OkubK3lTqGKslPOZkpnkfJn1h7X3S5XFV2JMXfBQ4MDzf +huNMrUnjl1nOG5srztxl1Asoa06ERlFE9zMILViXIa4= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert16[] = { + 0x30, 0x82, 0x04, 0x6c, 0x30, 0x82, 0x03, 0x54, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x4d, 0x5f, 0x2c, 0x34, 0x08, 0xb2, 0x4c, 0x20, 0xcd, + 0x6d, 0x50, 0x7e, 0x24, 0x4d, 0xc9, 0xec, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, + 0x32, 0x30, 0x37, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x3c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0d, 0x54, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xe4, 0x85, + 0x5b, 0x76, 0x49, 0x7d, 0x2f, 0x05, 0xd8, 0xc5, 0xac, 0xc8, 0xc8, 0xa9, + 0xd3, 0xdc, 0x98, 0xe6, 0xd7, 0x34, 0xa6, 0x2f, 0x0c, 0xf2, 0x22, 0x26, + 0xd8, 0xa3, 0xc9, 0x14, 0x4c, 0x8f, 0x05, 0xa4, 0x45, 0xe8, 0x14, 0x0c, + 0x58, 0x90, 0x05, 0x1a, 0xb7, 0xc5, 0xc1, 0x06, 0xa5, 0x80, 0xaf, 0xbb, + 0x1d, 0x49, 0x6b, 0x52, 0x34, 0x88, 0xc3, 0x59, 0xe7, 0xef, 0x6b, 0xc4, + 0x27, 0x41, 0x8c, 0x2b, 0x66, 0x1d, 0xd0, 0xe0, 0xa3, 0x97, 0x98, 0x19, + 0x34, 0x4b, 0x41, 0xd5, 0x98, 0xd5, 0xc7, 0x05, 0xad, 0xa2, 0xe4, 0xd7, + 0xed, 0x0c, 0xad, 0x4f, 0xc1, 0xb5, 0xb0, 0x21, 0xfd, 0x3e, 0x50, 0x53, + 0xb2, 0xc4, 0x90, 0xd0, 0xd4, 0x30, 0x67, 0x6c, 0x9a, 0xf1, 0x0e, 0x74, + 0xc4, 0xc2, 0xdc, 0x8a, 0xe8, 0x97, 0xff, 0xc9, 0x92, 0xae, 0x01, 0x8a, + 0x56, 0x0a, 0x98, 0x32, 0xb0, 0x00, 0x23, 0xec, 0x90, 0x1a, 0x60, 0xc3, + 0xed, 0xbb, 0x3a, 0xcb, 0x0f, 0x63, 0x9f, 0x0d, 0x44, 0xc9, 0x52, 0xe1, + 0x25, 0x96, 0xbf, 0xed, 0x50, 0x95, 0x89, 0x7f, 0x56, 0x14, 0xb1, 0xb7, + 0x61, 0x1d, 0x1c, 0x07, 0x8c, 0x3a, 0x2c, 0xf7, 0xff, 0x80, 0xde, 0x39, + 0x45, 0xd5, 0xaf, 0x1a, 0xd1, 0x78, 0xd8, 0xc7, 0x71, 0x6a, 0xa3, 0x19, + 0xa7, 0x32, 0x50, 0x21, 0xe9, 0xf2, 0x0e, 0xa1, 0xc6, 0x13, 0x03, 0x44, + 0x48, 0xd1, 0x66, 0xa8, 0x52, 0x57, 0xd7, 0x11, 0xb4, 0x93, 0x8b, 0xe5, + 0x99, 0x9f, 0x5d, 0xe7, 0x78, 0x51, 0xe5, 0x4d, 0xf6, 0xb7, 0x59, 0xb4, + 0x76, 0xb5, 0x09, 0x37, 0x4d, 0x06, 0x38, 0x13, 0x7a, 0x1c, 0x08, 0x98, + 0x5c, 0xc4, 0x48, 0x4a, 0xcb, 0x52, 0xa0, 0xa9, 0xf8, 0xb1, 0x9d, 0x8e, + 0x7b, 0x79, 0xb0, 0x20, 0x2f, 0x3c, 0x96, 0xa8, 0x11, 0x62, 0x47, 0xbb, + 0x11, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xfb, 0x30, 0x81, 0xf8, + 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, + 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, + 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, + 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x28, + 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30, + 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, + 0x2d, 0x32, 0x2d, 0x39, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0xa7, 0xa2, 0x83, 0xbb, 0x34, 0x45, 0x40, 0x3d, 0xfc, + 0xd5, 0x30, 0x4f, 0x12, 0xb9, 0x3e, 0xa1, 0x01, 0x9f, 0xf6, 0xdb, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, + 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x80, 0x22, 0x80, 0xe0, 0x6c, 0xc8, 0x95, 0x16, + 0xd7, 0x57, 0x26, 0x87, 0xf3, 0x72, 0x34, 0xdb, 0xc6, 0x72, 0x56, 0x27, + 0x3e, 0xd3, 0x96, 0xf6, 0x2e, 0x25, 0x91, 0xa5, 0x3e, 0x33, 0x97, 0xa7, + 0x4b, 0xe5, 0x2f, 0xfb, 0x25, 0x7d, 0x2f, 0x07, 0x61, 0xfa, 0x6f, 0x83, + 0x74, 0x4c, 0x4c, 0x53, 0x72, 0x20, 0xa4, 0x7a, 0xcf, 0x51, 0x51, 0x56, + 0x81, 0x88, 0xb0, 0x6d, 0x1f, 0x36, 0x2c, 0xc8, 0x2b, 0xb1, 0x88, 0x99, + 0xc1, 0xfe, 0x44, 0xab, 0x48, 0x51, 0x7c, 0xd8, 0xf2, 0x44, 0x64, 0x2a, + 0xd8, 0x71, 0xa7, 0xfb, 0x1a, 0x2f, 0xf9, 0x19, 0x8d, 0x34, 0xb2, 0x23, + 0xbf, 0xc4, 0x4c, 0x55, 0x1d, 0x8e, 0x44, 0xe8, 0xaa, 0x5d, 0x9a, 0xdd, + 0x9f, 0xfd, 0x03, 0xc7, 0xba, 0x24, 0x43, 0x8d, 0x2d, 0x47, 0x44, 0xdb, + 0xf6, 0xd8, 0x98, 0xc8, 0xb2, 0xf9, 0xda, 0xef, 0xed, 0x29, 0x5c, 0x69, + 0x12, 0xfa, 0xd1, 0x23, 0x96, 0x0f, 0xbf, 0x9c, 0x0d, 0xf2, 0x79, 0x45, + 0x53, 0x37, 0x9a, 0x56, 0x2f, 0xe8, 0x57, 0x10, 0x70, 0xf6, 0xee, 0x89, + 0x0c, 0x49, 0x89, 0x9a, 0xc1, 0x23, 0xf5, 0xc2, 0x2a, 0xcc, 0x41, 0xcf, + 0x22, 0xab, 0x65, 0x6e, 0xb7, 0x94, 0x82, 0x6d, 0x2f, 0x40, 0x5f, 0x58, + 0xde, 0xeb, 0x95, 0x2b, 0xa6, 0x72, 0x68, 0x52, 0x19, 0x91, 0x2a, 0xae, + 0x75, 0x9d, 0x4e, 0x92, 0xe6, 0xca, 0xde, 0x54, 0xea, 0x18, 0xab, 0x25, + 0x3c, 0xe6, 0x64, 0xa6, 0x79, 0x1f, 0x26, 0x7d, 0x61, 0xed, 0x7d, 0xd2, + 0xe5, 0x71, 0x55, 0xd8, 0x93, 0x17, 0x7c, 0x14, 0x38, 0x30, 0x3c, 0xdf, + 0x86, 0xe3, 0x4c, 0xad, 0x49, 0xe3, 0x97, 0x59, 0xce, 0x1b, 0x9b, 0x2b, + 0xce, 0xdc, 0x65, 0xd4, 0x0b, 0x28, 0x6b, 0x4e, 0x84, 0x46, 0x51, 0x44, + 0xf7, 0x33, 0x08, 0x2d, 0x58, 0x97, 0x21, 0xae, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 6e:8a:90:eb:cf:f0:44:8a:72:0d:08:05:d0:82:a5:44 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust EV SSL CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d9:b4:05:f2:38:67:0f:09:e7:7c:f5:63:2a:e5: + b9:5e:a8:11:ae:75:71:d9:4c:84:67:ad:89:5d:fc: + 28:3d:2a:b0:a5:d5:d4:e6:30:0a:84:d4:e4:18:cb: + 85:37:c5:46:71:eb:1c:7b:69:db:65:69:8c:30:05: + 3e:07:e1:6f:3c:c1:0b:61:e6:38:44:fc:bc:8c:2f: + 4e:75:57:f5:96:99:7c:3e:87:1f:0f:90:4b:70:c3: + 3f:39:45:3b:3a:6b:cb:bb:7b:40:54:d1:8b:4b:a1: + 72:d2:04:e9:e0:72:1a:93:11:7a:2f:f1:ab:9d:9c: + 98:58:ae:2c:ea:77:5f:2f:2e:87:af:b8:6b:e3:e2: + e2:3f:d6:3d:e0:96:44:df:11:55:63:52:2f:f4:26: + 78:c4:0f:20:4d:0a:c0:68:70:15:86:38:ee:b7:76: + 88:ab:18:8f:4f:35:1e:d4:8c:c9:db:7e:3d:44:d4: + 36:8c:c1:37:b5:59:5b:87:f9:e9:f1:d4:c5:28:bd: + 1d:dc:cc:96:72:d1:7a:a1:a7:20:b5:b8:af:f8:6e: + a5:60:7b:2b:8d:1f:ee:f4:2b:d6:69:cd:af:ca:80: + 58:29:e8:4c:00:20:8a:49:0a:6e:8e:8c:a8:d1:00: + 12:84:b6:c5:e2:95:a2:c0:3b:a4:6b:f0:82:d0:96: + 5d:25 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://g2.symcb.com + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.geotrust.com/resources/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g1.symcb.com/GeoTrustPCA.crl + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-538 + X509v3 Subject Key Identifier: + DE:CF:5C:50:B7:AE:02:1F:15:17:AA:16:E8:0D:B5:28:9D:6A:5A:F3 + X509v3 Authority Key Identifier: + keyid:2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92 + + Signature Algorithm: sha256WithRSAEncryption + b4:8e:bd:07:b9:9a:85:ec:3b:67:bd:07:60:61:e6:84:d1:d4: + ef:eb:1b:ba:0b:82:4b:95:64:b6:66:53:23:bd:b7:84:dd:e4: + 7b:8d:09:da:cf:b2:f5:f1:c3:bf:87:84:be:4e:a6:a8:c2:e7: + 12:39:28:34:e0:a4:56:44:40:0c:9f:88:a3:15:d3:e8:d3:5e: + e3:1c:04:60:fb:69:36:4f:6a:7e:0c:2a:28:c1:f3:aa:58:0e: + 6c:ce:1d:07:c3:4a:c0:9c:8d:c3:74:b1:ae:82:f0:1a:e1:f9: + 4e:29:bd:46:de:b7:1d:f9:7d:db:d9:0f:84:cb:92:45:cc:1c: + b3:18:f6:a0:cf:71:6f:0c:2e:9b:d2:2d:b3:99:93:83:44:ac: + 15:aa:9b:2e:67:ec:4f:88:69:05:56:7b:8b:b2:43:a9:3a:6c: + 1c:13:33:25:1b:fd:a8:c8:57:02:fb:1c:e0:d1:bd:3b:56:44: + 65:c3:63:f5:1b:ef:ec:30:d9:e3:6e:2e:13:e9:39:08:2a:0c: + 72:f3:9a:cc:f6:27:29:84:d3:ef:4c:c7:84:11:65:1f:c6:e3: + 81:03:db:87:cc:78:f7:b5:9d:96:3e:6a:7f:bc:11:85:7a:75: + e6:41:7d:0d:cf:f9:e5:85:69:25:8f:c7:8d:07:2d:f8:69:0f: + cb:41:53:00 +-----BEGIN CERTIFICATE----- +MIIEbjCCA1agAwIBAgIQboqQ68/wRIpyDQgF0IKlRDANBgkqhkiG9w0BAQsFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMzEw +MzEwMDAwMDBaFw0yMzEwMzAyMzU5NTlaMEcxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMSAwHgYDVQQDExdHZW9UcnVzdCBFViBTU0wgQ0EgLSBH +NDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANm0BfI4Zw8J53z1Yyrl +uV6oEa51cdlMhGetiV38KD0qsKXV1OYwCoTU5BjLhTfFRnHrHHtp22VpjDAFPgfh +bzzBC2HmOET8vIwvTnVX9ZaZfD6HHw+QS3DDPzlFOzpry7t7QFTRi0uhctIE6eBy +GpMRei/xq52cmFiuLOp3Xy8uh6+4a+Pi4j/WPeCWRN8RVWNSL/QmeMQPIE0KwGhw +FYY47rd2iKsYj081HtSMydt+PUTUNozBN7VZW4f56fHUxSi9HdzMlnLReqGnILW4 +r/hupWB7K40f7vQr1mnNr8qAWCnoTAAgikkKbo6MqNEAEoS2xeKVosA7pGvwgtCW +XSUCAwEAAaOCAUMwggE/MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQD +AgEGMC8GCCsGAQUFBwEBBCMwITAfBggrBgEFBQcwAYYTaHR0cDovL2cyLnN5bWNi +LmNvbTBHBgNVHSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93 +d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2cxLnN5bWNiLmNvbS9HZW9UcnVzdFBDQS5jcmwwKQYDVR0RBCIwIKQe +MBwxGjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTM4MB0GA1UdDgQWBBTez1xQt64C +HxUXqhboDbUonWpa8zAfBgNVHSMEGDAWgBQs1VBBlxWL8I82YVtK+2vZmckzkjAN +BgkqhkiG9w0BAQsFAAOCAQEAtI69B7mahew7Z70HYGHmhNHU7+sbuguCS5VktmZT +I723hN3ke40J2s+y9fHDv4eEvk6mqMLnEjkoNOCkVkRADJ+IoxXT6NNe4xwEYPtp +Nk9qfgwqKMHzqlgObM4dB8NKwJyNw3SxroLwGuH5Tim9Rt63Hfl929kPhMuSRcwc +sxj2oM9xbwwum9Its5mTg0SsFaqbLmfsT4hpBVZ7i7JDqTpsHBMzJRv9qMhXAvsc +4NG9O1ZEZcNj9Rvv7DDZ424uE+k5CCoMcvOazPYnKYTT70zHhBFlH8bjgQPbh8x4 +97Wdlj5qf7wRhXp15kF9Dc/55YVpJY/HjQct+GkPy0FTAA== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert17[] = { + 0x30, 0x82, 0x04, 0x6e, 0x30, 0x82, 0x03, 0x56, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x6e, 0x8a, 0x90, 0xeb, 0xcf, 0xf0, 0x44, 0x8a, 0x72, + 0x0d, 0x08, 0x05, 0xd0, 0x82, 0xa5, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x58, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, + 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, + 0x33, 0x31, 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, + 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, + 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, + 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, + 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd9, 0xb4, + 0x05, 0xf2, 0x38, 0x67, 0x0f, 0x09, 0xe7, 0x7c, 0xf5, 0x63, 0x2a, 0xe5, + 0xb9, 0x5e, 0xa8, 0x11, 0xae, 0x75, 0x71, 0xd9, 0x4c, 0x84, 0x67, 0xad, + 0x89, 0x5d, 0xfc, 0x28, 0x3d, 0x2a, 0xb0, 0xa5, 0xd5, 0xd4, 0xe6, 0x30, + 0x0a, 0x84, 0xd4, 0xe4, 0x18, 0xcb, 0x85, 0x37, 0xc5, 0x46, 0x71, 0xeb, + 0x1c, 0x7b, 0x69, 0xdb, 0x65, 0x69, 0x8c, 0x30, 0x05, 0x3e, 0x07, 0xe1, + 0x6f, 0x3c, 0xc1, 0x0b, 0x61, 0xe6, 0x38, 0x44, 0xfc, 0xbc, 0x8c, 0x2f, + 0x4e, 0x75, 0x57, 0xf5, 0x96, 0x99, 0x7c, 0x3e, 0x87, 0x1f, 0x0f, 0x90, + 0x4b, 0x70, 0xc3, 0x3f, 0x39, 0x45, 0x3b, 0x3a, 0x6b, 0xcb, 0xbb, 0x7b, + 0x40, 0x54, 0xd1, 0x8b, 0x4b, 0xa1, 0x72, 0xd2, 0x04, 0xe9, 0xe0, 0x72, + 0x1a, 0x93, 0x11, 0x7a, 0x2f, 0xf1, 0xab, 0x9d, 0x9c, 0x98, 0x58, 0xae, + 0x2c, 0xea, 0x77, 0x5f, 0x2f, 0x2e, 0x87, 0xaf, 0xb8, 0x6b, 0xe3, 0xe2, + 0xe2, 0x3f, 0xd6, 0x3d, 0xe0, 0x96, 0x44, 0xdf, 0x11, 0x55, 0x63, 0x52, + 0x2f, 0xf4, 0x26, 0x78, 0xc4, 0x0f, 0x20, 0x4d, 0x0a, 0xc0, 0x68, 0x70, + 0x15, 0x86, 0x38, 0xee, 0xb7, 0x76, 0x88, 0xab, 0x18, 0x8f, 0x4f, 0x35, + 0x1e, 0xd4, 0x8c, 0xc9, 0xdb, 0x7e, 0x3d, 0x44, 0xd4, 0x36, 0x8c, 0xc1, + 0x37, 0xb5, 0x59, 0x5b, 0x87, 0xf9, 0xe9, 0xf1, 0xd4, 0xc5, 0x28, 0xbd, + 0x1d, 0xdc, 0xcc, 0x96, 0x72, 0xd1, 0x7a, 0xa1, 0xa7, 0x20, 0xb5, 0xb8, + 0xaf, 0xf8, 0x6e, 0xa5, 0x60, 0x7b, 0x2b, 0x8d, 0x1f, 0xee, 0xf4, 0x2b, + 0xd6, 0x69, 0xcd, 0xaf, 0xca, 0x80, 0x58, 0x29, 0xe8, 0x4c, 0x00, 0x20, + 0x8a, 0x49, 0x0a, 0x6e, 0x8e, 0x8c, 0xa8, 0xd1, 0x00, 0x12, 0x84, 0xb6, + 0xc5, 0xe2, 0x95, 0xa2, 0xc0, 0x3b, 0xa4, 0x6b, 0xf0, 0x82, 0xd0, 0x96, + 0x5d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x43, 0x30, + 0x82, 0x01, 0x3f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, + 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79, + 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, + 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, + 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x38, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0xde, 0xcf, 0x5c, 0x50, 0xb7, 0xae, 0x02, + 0x1f, 0x15, 0x17, 0xaa, 0x16, 0xe8, 0x0d, 0xb5, 0x28, 0x9d, 0x6a, 0x5a, + 0xf3, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x2c, 0xd5, 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, + 0x61, 0x5b, 0x4a, 0xfb, 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xb4, 0x8e, 0xbd, 0x07, 0xb9, 0x9a, + 0x85, 0xec, 0x3b, 0x67, 0xbd, 0x07, 0x60, 0x61, 0xe6, 0x84, 0xd1, 0xd4, + 0xef, 0xeb, 0x1b, 0xba, 0x0b, 0x82, 0x4b, 0x95, 0x64, 0xb6, 0x66, 0x53, + 0x23, 0xbd, 0xb7, 0x84, 0xdd, 0xe4, 0x7b, 0x8d, 0x09, 0xda, 0xcf, 0xb2, + 0xf5, 0xf1, 0xc3, 0xbf, 0x87, 0x84, 0xbe, 0x4e, 0xa6, 0xa8, 0xc2, 0xe7, + 0x12, 0x39, 0x28, 0x34, 0xe0, 0xa4, 0x56, 0x44, 0x40, 0x0c, 0x9f, 0x88, + 0xa3, 0x15, 0xd3, 0xe8, 0xd3, 0x5e, 0xe3, 0x1c, 0x04, 0x60, 0xfb, 0x69, + 0x36, 0x4f, 0x6a, 0x7e, 0x0c, 0x2a, 0x28, 0xc1, 0xf3, 0xaa, 0x58, 0x0e, + 0x6c, 0xce, 0x1d, 0x07, 0xc3, 0x4a, 0xc0, 0x9c, 0x8d, 0xc3, 0x74, 0xb1, + 0xae, 0x82, 0xf0, 0x1a, 0xe1, 0xf9, 0x4e, 0x29, 0xbd, 0x46, 0xde, 0xb7, + 0x1d, 0xf9, 0x7d, 0xdb, 0xd9, 0x0f, 0x84, 0xcb, 0x92, 0x45, 0xcc, 0x1c, + 0xb3, 0x18, 0xf6, 0xa0, 0xcf, 0x71, 0x6f, 0x0c, 0x2e, 0x9b, 0xd2, 0x2d, + 0xb3, 0x99, 0x93, 0x83, 0x44, 0xac, 0x15, 0xaa, 0x9b, 0x2e, 0x67, 0xec, + 0x4f, 0x88, 0x69, 0x05, 0x56, 0x7b, 0x8b, 0xb2, 0x43, 0xa9, 0x3a, 0x6c, + 0x1c, 0x13, 0x33, 0x25, 0x1b, 0xfd, 0xa8, 0xc8, 0x57, 0x02, 0xfb, 0x1c, + 0xe0, 0xd1, 0xbd, 0x3b, 0x56, 0x44, 0x65, 0xc3, 0x63, 0xf5, 0x1b, 0xef, + 0xec, 0x30, 0xd9, 0xe3, 0x6e, 0x2e, 0x13, 0xe9, 0x39, 0x08, 0x2a, 0x0c, + 0x72, 0xf3, 0x9a, 0xcc, 0xf6, 0x27, 0x29, 0x84, 0xd3, 0xef, 0x4c, 0xc7, + 0x84, 0x11, 0x65, 0x1f, 0xc6, 0xe3, 0x81, 0x03, 0xdb, 0x87, 0xcc, 0x78, + 0xf7, 0xb5, 0x9d, 0x96, 0x3e, 0x6a, 0x7f, 0xbc, 0x11, 0x85, 0x7a, 0x75, + 0xe6, 0x41, 0x7d, 0x0d, 0xcf, 0xf9, 0xe5, 0x85, 0x69, 0x25, 0x8f, 0xc7, + 0x8d, 0x07, 0x2d, 0xf8, 0x69, 0x0f, 0xcb, 0x41, 0x53, 0x00, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1828629 (0x1be715) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority + Validity + Not Before: Jan 1 07:00:00 2014 GMT + Not After : May 30 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bf:71:62:08:f1:fa:59:34:f7:1b:c9:18:a3:f7: + 80:49:58:e9:22:83:13:a6:c5:20:43:01:3b:84:f1: + e6:85:49:9f:27:ea:f6:84:1b:4e:a0:b4:db:70:98: + c7:32:01:b1:05:3e:07:4e:ee:f4:fa:4f:2f:59:30: + 22:e7:ab:19:56:6b:e2:80:07:fc:f3:16:75:80:39: + 51:7b:e5:f9:35:b6:74:4e:a9:8d:82:13:e4:b6:3f: + a9:03:83:fa:a2:be:8a:15:6a:7f:de:0b:c3:b6:19: + 14:05:ca:ea:c3:a8:04:94:3b:46:7c:32:0d:f3:00: + 66:22:c8:8d:69:6d:36:8c:11:18:b7:d3:b2:1c:60: + b4:38:fa:02:8c:ce:d3:dd:46:07:de:0a:3e:eb:5d: + 7c:c8:7c:fb:b0:2b:53:a4:92:62:69:51:25:05:61: + 1a:44:81:8c:2c:a9:43:96:23:df:ac:3a:81:9a:0e: + 29:c5:1c:a9:e9:5d:1e:b6:9e:9e:30:0a:39:ce:f1: + 88:80:fb:4b:5d:cc:32:ec:85:62:43:25:34:02:56: + 27:01:91:b4:3b:70:2a:3f:6e:b1:e8:9c:88:01:7d: + 9f:d4:f9:db:53:6d:60:9d:bf:2c:e7:58:ab:b8:5f: + 46:fc:ce:c4:1b:03:3c:09:eb:49:31:5c:69:46:b3: + e0:47 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE + X509v3 Authority Key Identifier: + keyid:D2:C4:B0:D2:91:D4:4C:11:71:B3:61:CB:3D:A1:FE:DD:A8:6A:D4:E3 + + Authority Information Access: + OCSP - URI:http://ocsp.godaddy.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.godaddy.com/gdroot.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.godaddy.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 59:0b:53:bd:92:86:11:a7:24:7b:ed:5b:31:cf:1d:1f:6c:70: + c5:b8:6e:be:4e:bb:f6:be:97:50:e1:30:7f:ba:28:5c:62:94: + c2:e3:7e:33:f7:fb:42:76:85:db:95:1c:8c:22:58:75:09:0c: + 88:65:67:39:0a:16:09:c5:a0:38:97:a4:c5:23:93:3f:b4:18: + a6:01:06:44:91:e3:a7:69:27:b4:5a:25:7f:3a:b7:32:cd:dd: + 84:ff:2a:38:29:33:a4:dd:67:b2:85:fe:a1:88:20:1c:50:89: + c8:dc:2a:f6:42:03:37:4c:e6:88:df:d5:af:24:f2:b1:c3:df: + cc:b5:ec:e0:99:5e:b7:49:54:20:3c:94:18:0c:c7:1c:52:18: + 49:a4:6d:e1:b3:58:0b:c9:d8:ec:d9:ae:1c:32:8e:28:70:0d: + e2:fe:a6:17:9e:84:0f:bd:57:70:b3:5a:e9:1f:a0:86:53:bb: + ef:7c:ff:69:0b:e0:48:c3:b7:93:0b:c8:0a:54:c4:ac:5d:14: + 67:37:6c:ca:a5:2f:31:08:37:aa:6e:6f:8c:bc:9b:e2:57:5d: + 24:81:af:97:97:9c:84:ad:6c:ac:37:4c:66:f3:61:91:11:20: + e4:be:30:9f:7a:a4:29:09:b0:e1:34:5f:64:77:18:40:51:df: + 8c:30:a6:af +-----BEGIN CERTIFICATE----- +MIIEfTCCA2WgAwIBAgIDG+cVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVT +MSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIEluYy4xMTAvBgNVBAsTKEdv +IERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMTAx +MDcwMDAwWhcNMzEwNTMwMDcwMDAwWjCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHku +Y29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1 +dGhvcml0eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3Fi +CPH6WTT3G8kYo/eASVjpIoMTpsUgQwE7hPHmhUmfJ+r2hBtOoLTbcJjHMgGxBT4H +Tu70+k8vWTAi56sZVmvigAf88xZ1gDlRe+X5NbZ0TqmNghPktj+pA4P6or6KFWp/ +3gvDthkUBcrqw6gElDtGfDIN8wBmIsiNaW02jBEYt9OyHGC0OPoCjM7T3UYH3go+ +6118yHz7sCtTpJJiaVElBWEaRIGMLKlDliPfrDqBmg4pxRyp6V0etp6eMAo5zvGI +gPtLXcwy7IViQyU0AlYnAZG0O3AqP26x6JyIAX2f1PnbU21gnb8s51iruF9G/M7E +GwM8CetJMVxpRrPgRwIDAQABo4IBFzCCARMwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9BUFuIMGU2g/eMB8GA1Ud +IwQYMBaAFNLEsNKR1EwRcbNhyz2h/t2oatTjMDQGCCsGAQUFBwEBBCgwJjAkBggr +BgEFBQcwAYYYaHR0cDovL29jc3AuZ29kYWRkeS5jb20vMDIGA1UdHwQrMCkwJ6Al +oCOGIWh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Ryb290LmNybDBGBgNVHSAEPzA9 +MDsGBFUdIAAwMzAxBggrBgEFBQcCARYlaHR0cHM6Ly9jZXJ0cy5nb2RhZGR5LmNv +bS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAWQtTvZKGEacke+1bMc8d +H2xwxbhuvk679r6XUOEwf7ooXGKUwuN+M/f7QnaF25UcjCJYdQkMiGVnOQoWCcWg +OJekxSOTP7QYpgEGRJHjp2kntFolfzq3Ms3dhP8qOCkzpN1nsoX+oYggHFCJyNwq +9kIDN0zmiN/VryTyscPfzLXs4Jlet0lUIDyUGAzHHFIYSaRt4bNYC8nY7NmuHDKO +KHAN4v6mF56ED71XcLNa6R+ghlO773z/aQvgSMO3kwvIClTErF0UZzdsyqUvMQg3 +qm5vjLyb4lddJIGvl5echK1srDdMZvNhkREg5L4wn3qkKQmw4TRfZHcYQFHfjDCm +rw== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert18[] = { + 0x30, 0x82, 0x04, 0x7d, 0x30, 0x82, 0x03, 0x65, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x1b, 0xe7, 0x15, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x63, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x54, + 0x68, 0x65, 0x20, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x47, 0x6f, + 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x31, 0x30, 0x31, + 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, + 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, + 0x83, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, + 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, + 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44, + 0x61, 0x64, 0x64, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf, 0x71, 0x62, + 0x08, 0xf1, 0xfa, 0x59, 0x34, 0xf7, 0x1b, 0xc9, 0x18, 0xa3, 0xf7, 0x80, + 0x49, 0x58, 0xe9, 0x22, 0x83, 0x13, 0xa6, 0xc5, 0x20, 0x43, 0x01, 0x3b, + 0x84, 0xf1, 0xe6, 0x85, 0x49, 0x9f, 0x27, 0xea, 0xf6, 0x84, 0x1b, 0x4e, + 0xa0, 0xb4, 0xdb, 0x70, 0x98, 0xc7, 0x32, 0x01, 0xb1, 0x05, 0x3e, 0x07, + 0x4e, 0xee, 0xf4, 0xfa, 0x4f, 0x2f, 0x59, 0x30, 0x22, 0xe7, 0xab, 0x19, + 0x56, 0x6b, 0xe2, 0x80, 0x07, 0xfc, 0xf3, 0x16, 0x75, 0x80, 0x39, 0x51, + 0x7b, 0xe5, 0xf9, 0x35, 0xb6, 0x74, 0x4e, 0xa9, 0x8d, 0x82, 0x13, 0xe4, + 0xb6, 0x3f, 0xa9, 0x03, 0x83, 0xfa, 0xa2, 0xbe, 0x8a, 0x15, 0x6a, 0x7f, + 0xde, 0x0b, 0xc3, 0xb6, 0x19, 0x14, 0x05, 0xca, 0xea, 0xc3, 0xa8, 0x04, + 0x94, 0x3b, 0x46, 0x7c, 0x32, 0x0d, 0xf3, 0x00, 0x66, 0x22, 0xc8, 0x8d, + 0x69, 0x6d, 0x36, 0x8c, 0x11, 0x18, 0xb7, 0xd3, 0xb2, 0x1c, 0x60, 0xb4, + 0x38, 0xfa, 0x02, 0x8c, 0xce, 0xd3, 0xdd, 0x46, 0x07, 0xde, 0x0a, 0x3e, + 0xeb, 0x5d, 0x7c, 0xc8, 0x7c, 0xfb, 0xb0, 0x2b, 0x53, 0xa4, 0x92, 0x62, + 0x69, 0x51, 0x25, 0x05, 0x61, 0x1a, 0x44, 0x81, 0x8c, 0x2c, 0xa9, 0x43, + 0x96, 0x23, 0xdf, 0xac, 0x3a, 0x81, 0x9a, 0x0e, 0x29, 0xc5, 0x1c, 0xa9, + 0xe9, 0x5d, 0x1e, 0xb6, 0x9e, 0x9e, 0x30, 0x0a, 0x39, 0xce, 0xf1, 0x88, + 0x80, 0xfb, 0x4b, 0x5d, 0xcc, 0x32, 0xec, 0x85, 0x62, 0x43, 0x25, 0x34, + 0x02, 0x56, 0x27, 0x01, 0x91, 0xb4, 0x3b, 0x70, 0x2a, 0x3f, 0x6e, 0xb1, + 0xe8, 0x9c, 0x88, 0x01, 0x7d, 0x9f, 0xd4, 0xf9, 0xdb, 0x53, 0x6d, 0x60, + 0x9d, 0xbf, 0x2c, 0xe7, 0x58, 0xab, 0xb8, 0x5f, 0x46, 0xfc, 0xce, 0xc4, + 0x1b, 0x03, 0x3c, 0x09, 0xeb, 0x49, 0x31, 0x5c, 0x69, 0x46, 0xb3, 0xe0, + 0x47, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x17, 0x30, 0x82, + 0x01, 0x13, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3a, 0x9a, + 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef, 0xf6, 0xbd, 0x05, 0x41, 0x6e, + 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd2, 0xc4, 0xb0, 0xd2, 0x91, + 0xd4, 0x4c, 0x11, 0x71, 0xb3, 0x61, 0xcb, 0x3d, 0xa1, 0xfe, 0xdd, 0xa8, + 0x6a, 0xd4, 0xe3, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x32, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, + 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, + 0x6c, 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, + 0x30, 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, + 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0x0b, 0x53, + 0xbd, 0x92, 0x86, 0x11, 0xa7, 0x24, 0x7b, 0xed, 0x5b, 0x31, 0xcf, 0x1d, + 0x1f, 0x6c, 0x70, 0xc5, 0xb8, 0x6e, 0xbe, 0x4e, 0xbb, 0xf6, 0xbe, 0x97, + 0x50, 0xe1, 0x30, 0x7f, 0xba, 0x28, 0x5c, 0x62, 0x94, 0xc2, 0xe3, 0x7e, + 0x33, 0xf7, 0xfb, 0x42, 0x76, 0x85, 0xdb, 0x95, 0x1c, 0x8c, 0x22, 0x58, + 0x75, 0x09, 0x0c, 0x88, 0x65, 0x67, 0x39, 0x0a, 0x16, 0x09, 0xc5, 0xa0, + 0x38, 0x97, 0xa4, 0xc5, 0x23, 0x93, 0x3f, 0xb4, 0x18, 0xa6, 0x01, 0x06, + 0x44, 0x91, 0xe3, 0xa7, 0x69, 0x27, 0xb4, 0x5a, 0x25, 0x7f, 0x3a, 0xb7, + 0x32, 0xcd, 0xdd, 0x84, 0xff, 0x2a, 0x38, 0x29, 0x33, 0xa4, 0xdd, 0x67, + 0xb2, 0x85, 0xfe, 0xa1, 0x88, 0x20, 0x1c, 0x50, 0x89, 0xc8, 0xdc, 0x2a, + 0xf6, 0x42, 0x03, 0x37, 0x4c, 0xe6, 0x88, 0xdf, 0xd5, 0xaf, 0x24, 0xf2, + 0xb1, 0xc3, 0xdf, 0xcc, 0xb5, 0xec, 0xe0, 0x99, 0x5e, 0xb7, 0x49, 0x54, + 0x20, 0x3c, 0x94, 0x18, 0x0c, 0xc7, 0x1c, 0x52, 0x18, 0x49, 0xa4, 0x6d, + 0xe1, 0xb3, 0x58, 0x0b, 0xc9, 0xd8, 0xec, 0xd9, 0xae, 0x1c, 0x32, 0x8e, + 0x28, 0x70, 0x0d, 0xe2, 0xfe, 0xa6, 0x17, 0x9e, 0x84, 0x0f, 0xbd, 0x57, + 0x70, 0xb3, 0x5a, 0xe9, 0x1f, 0xa0, 0x86, 0x53, 0xbb, 0xef, 0x7c, 0xff, + 0x69, 0x0b, 0xe0, 0x48, 0xc3, 0xb7, 0x93, 0x0b, 0xc8, 0x0a, 0x54, 0xc4, + 0xac, 0x5d, 0x14, 0x67, 0x37, 0x6c, 0xca, 0xa5, 0x2f, 0x31, 0x08, 0x37, + 0xaa, 0x6e, 0x6f, 0x8c, 0xbc, 0x9b, 0xe2, 0x57, 0x5d, 0x24, 0x81, 0xaf, + 0x97, 0x97, 0x9c, 0x84, 0xad, 0x6c, 0xac, 0x37, 0x4c, 0x66, 0xf3, 0x61, + 0x91, 0x11, 0x20, 0xe4, 0xbe, 0x30, 0x9f, 0x7a, 0xa4, 0x29, 0x09, 0xb0, + 0xe1, 0x34, 0x5f, 0x64, 0x77, 0x18, 0x40, 0x51, 0xdf, 0x8c, 0x30, 0xa6, + 0xaf, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 06:9e:1d:b7:7f:cf:1d:fb:a9:7a:f5:e5:c9:a2:40:37 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA + Validity + Not Before: Mar 8 12:00:00 2013 GMT + Not After : Mar 8 12:00:00 2023 GMT + Subject: C=US, O=DigiCert Inc, CN=DigiCert Secure Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bb:57:e4:21:a9:d5:9b:60:37:7e:8e:a1:61:7f: + 81:e2:1a:c2:75:64:d9:91:50:0b:e4:36:44:24:6e: + 30:d2:9b:7a:27:fa:c2:6a:ae:6a:70:09:38:b9:20: + 0a:c8:65:10:4a:88:ac:31:f2:dc:92:f2:63:a1:5d: + 80:63:59:80:92:23:1c:e6:ef:76:4a:50:35:c9:d8: + 71:38:b9:ed:f0:e6:42:ae:d3:38:26:79:30:f9:22: + 94:c6:db:a6:3f:41:78:90:d8:de:5c:7e:69:7d:f8: + 90:15:3a:d0:a1:a0:be:fa:b2:b2:19:a1:d8:2b:d1: + ce:bf:6b:dd:49:ab:a3:92:fe:b5:ab:c8:c1:3e:ee: + 01:00:d8:a9:44:b8:42:73:88:c3:61:f5:ab:4a:83: + 28:0a:d2:d4:49:fa:6a:b1:cd:df:57:2c:94:e5:e2: + ca:83:5f:b7:ba:62:5c:2f:68:a5:f0:c0:b9:fd:2b: + d1:e9:1f:d8:1a:62:15:bd:ff:3d:a6:f7:cb:ef:e6: + db:65:2f:25:38:ec:fb:e6:20:66:58:96:34:19:d2: + 15:ce:21:d3:24:cc:d9:14:6f:d8:fe:55:c7:e7:6f: + b6:0f:1a:8c:49:be:29:f2:ba:5a:9a:81:26:37:24: + 6f:d7:48:12:6c:2e:59:f5:9c:18:bb:d9:f6:68:e2: + df:45 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl + + Full Name: + URI:http://crl4.digicert.com/DigiCertGlobalRootCA.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.digicert.com/CPS + + X509v3 Subject Key Identifier: + 90:71:DB:37:EB:73:C8:EF:DC:D5:1E:12:B6:34:BA:2B:5A:A0:A6:92 + X509v3 Authority Key Identifier: + keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55 + + Signature Algorithm: sha1WithRSAEncryption + 30:ce:d1:95:51:00:ae:06:0b:a1:0e:02:c0:17:ac:b6:7f:8f: + 20:f6:40:75:74:1c:cc:78:b1:a4:4f:ea:f4:d0:c4:9d:a2:de: + 81:07:26:1f:40:88:51:f0:1f:cf:b7:4c:40:99:d0:f4:3c:71: + 98:73:88:97:2c:19:d7:6e:84:8f:a4:1f:9c:5a:20:e3:51:5c: + b0:c5:9e:99:6a:4f:c8:69:f7:10:ff:4e:ad:19:d9:c9:58:b3: + 33:ae:0c:d9:96:29:9e:71:b2:70:63:a3:b6:99:16:42:1d:65: + f3:f7:a0:1e:7d:c5:d4:65:14:b2:62:84:d4:6c:5c:08:0c:d8: + 6c:93:2b:b4:76:59:8a:d1:7f:ff:03:d8:c2:5d:b8:2f:22:d6: + 38:f0:f6:9c:6b:7d:46:eb:99:74:f7:eb:4a:0e:a9:a6:04:eb: + 7b:ce:f0:5c:6b:98:31:5a:98:40:eb:69:c4:05:f4:20:a8:ca: + 08:3a:65:6c:38:15:f5:5c:2c:b2:55:e4:2c:6b:41:f0:be:5c: + 46:ca:4a:29:a0:48:5e:20:d2:45:ff:05:de:34:af:70:4b:81: + 39:e2:ca:07:57:7c:b6:31:dc:21:29:e2:be:97:0e:77:90:14: + 51:40:e1:bf:e3:cc:1b:19:9c:25:ca:a7:06:b2:53:df:23:b2: + cf:12:19:a3 +-----BEGIN CERTIFICATE----- +MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEgxCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxIjAgBgNVBAMTGURpZ2lDZXJ0IFNlY3Vy +ZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V+Qh +qdWbYDd+jqFhf4HiGsJ1ZNmRUAvkNkQkbjDSm3on+sJqrmpwCTi5IArIZRBKiKwx +8tyS8mOhXYBjWYCSIxzm73ZKUDXJ2HE4ue3w5kKu0zgmeTD5IpTG26Y/QXiQ2N5c +fml9+JAVOtChoL76srIZodgr0c6/a91Jq6OS/rWryME+7gEA2KlEuEJziMNh9atK +gygK0tRJ+mqxzd9XLJTl4sqDX7e6YlwvaKXwwLn9K9HpH9gaYhW9/z2m98vv5ttl +LyU47PvmIGZYljQZ0hXOIdMkzNkUb9j+Vcfnb7YPGoxJvinyulqagSY3JG/XSBJs +Lln1nBi72fZo4t9FAgMBAAGjggFaMIIBVjASBgNVHRMBAf8ECDAGAQH/AgEAMA4G +A1UdDwEB/wQEAwIBhjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6 +Ly9vY3NwLmRpZ2ljZXJ0LmNvbTB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3Js +My5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDegNaAzhjFo +dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3Js +MD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k +aWdpY2VydC5jb20vQ1BTMB0GA1UdDgQWBBSQcds363PI79zVHhK2NLorWqCmkjAf +BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTANBgkqhkiG9w0BAQUFAAOC +AQEAMM7RlVEArgYLoQ4CwBestn+PIPZAdXQczHixpE/q9NDEnaLegQcmH0CIUfAf +z7dMQJnQ9DxxmHOIlywZ126Ej6QfnFog41FcsMWemWpPyGn3EP9OrRnZyVizM64M +2ZYpnnGycGOjtpkWQh1l8/egHn3F1GUUsmKE1GxcCAzYbJMrtHZZitF//wPYwl24 +LyLWOPD2nGt9RuuZdPfrSg6ppgTre87wXGuYMVqYQOtpxAX0IKjKCDplbDgV9Vws +slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh +v+PMGxmcJcqnBrJT3yOyzxIZow== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert19[] = { + 0x30, 0x82, 0x04, 0x8f, 0x30, 0x82, 0x03, 0x77, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x06, 0x9e, 0x1d, 0xb7, 0x7f, 0xcf, 0x1d, 0xfb, 0xa9, + 0x7a, 0xf5, 0xe5, 0xc9, 0xa2, 0x40, 0x37, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x61, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x33, 0x30, 0x38, 0x31, + 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x33, + 0x30, 0x38, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x48, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, + 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbb, 0x57, 0xe4, 0x21, + 0xa9, 0xd5, 0x9b, 0x60, 0x37, 0x7e, 0x8e, 0xa1, 0x61, 0x7f, 0x81, 0xe2, + 0x1a, 0xc2, 0x75, 0x64, 0xd9, 0x91, 0x50, 0x0b, 0xe4, 0x36, 0x44, 0x24, + 0x6e, 0x30, 0xd2, 0x9b, 0x7a, 0x27, 0xfa, 0xc2, 0x6a, 0xae, 0x6a, 0x70, + 0x09, 0x38, 0xb9, 0x20, 0x0a, 0xc8, 0x65, 0x10, 0x4a, 0x88, 0xac, 0x31, + 0xf2, 0xdc, 0x92, 0xf2, 0x63, 0xa1, 0x5d, 0x80, 0x63, 0x59, 0x80, 0x92, + 0x23, 0x1c, 0xe6, 0xef, 0x76, 0x4a, 0x50, 0x35, 0xc9, 0xd8, 0x71, 0x38, + 0xb9, 0xed, 0xf0, 0xe6, 0x42, 0xae, 0xd3, 0x38, 0x26, 0x79, 0x30, 0xf9, + 0x22, 0x94, 0xc6, 0xdb, 0xa6, 0x3f, 0x41, 0x78, 0x90, 0xd8, 0xde, 0x5c, + 0x7e, 0x69, 0x7d, 0xf8, 0x90, 0x15, 0x3a, 0xd0, 0xa1, 0xa0, 0xbe, 0xfa, + 0xb2, 0xb2, 0x19, 0xa1, 0xd8, 0x2b, 0xd1, 0xce, 0xbf, 0x6b, 0xdd, 0x49, + 0xab, 0xa3, 0x92, 0xfe, 0xb5, 0xab, 0xc8, 0xc1, 0x3e, 0xee, 0x01, 0x00, + 0xd8, 0xa9, 0x44, 0xb8, 0x42, 0x73, 0x88, 0xc3, 0x61, 0xf5, 0xab, 0x4a, + 0x83, 0x28, 0x0a, 0xd2, 0xd4, 0x49, 0xfa, 0x6a, 0xb1, 0xcd, 0xdf, 0x57, + 0x2c, 0x94, 0xe5, 0xe2, 0xca, 0x83, 0x5f, 0xb7, 0xba, 0x62, 0x5c, 0x2f, + 0x68, 0xa5, 0xf0, 0xc0, 0xb9, 0xfd, 0x2b, 0xd1, 0xe9, 0x1f, 0xd8, 0x1a, + 0x62, 0x15, 0xbd, 0xff, 0x3d, 0xa6, 0xf7, 0xcb, 0xef, 0xe6, 0xdb, 0x65, + 0x2f, 0x25, 0x38, 0xec, 0xfb, 0xe6, 0x20, 0x66, 0x58, 0x96, 0x34, 0x19, + 0xd2, 0x15, 0xce, 0x21, 0xd3, 0x24, 0xcc, 0xd9, 0x14, 0x6f, 0xd8, 0xfe, + 0x55, 0xc7, 0xe7, 0x6f, 0xb6, 0x0f, 0x1a, 0x8c, 0x49, 0xbe, 0x29, 0xf2, + 0xba, 0x5a, 0x9a, 0x81, 0x26, 0x37, 0x24, 0x6f, 0xd7, 0x48, 0x12, 0x6c, + 0x2e, 0x59, 0xf5, 0x9c, 0x18, 0xbb, 0xd9, 0xf6, 0x68, 0xe2, 0xdf, 0x45, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5a, 0x30, 0x82, 0x01, + 0x56, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x86, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, + 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x7b, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, + 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, + 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, + 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, + 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x90, 0x71, 0xdb, 0x37, 0xeb, 0x73, 0xc8, 0xef, 0xdc, 0xd5, + 0x1e, 0x12, 0xb6, 0x34, 0xba, 0x2b, 0x5a, 0xa0, 0xa6, 0x92, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x03, + 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, 0x66, 0xf0, 0xa3, 0xe2, 0x1b, + 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x01, 0x00, 0x30, 0xce, 0xd1, 0x95, 0x51, 0x00, 0xae, 0x06, 0x0b, + 0xa1, 0x0e, 0x02, 0xc0, 0x17, 0xac, 0xb6, 0x7f, 0x8f, 0x20, 0xf6, 0x40, + 0x75, 0x74, 0x1c, 0xcc, 0x78, 0xb1, 0xa4, 0x4f, 0xea, 0xf4, 0xd0, 0xc4, + 0x9d, 0xa2, 0xde, 0x81, 0x07, 0x26, 0x1f, 0x40, 0x88, 0x51, 0xf0, 0x1f, + 0xcf, 0xb7, 0x4c, 0x40, 0x99, 0xd0, 0xf4, 0x3c, 0x71, 0x98, 0x73, 0x88, + 0x97, 0x2c, 0x19, 0xd7, 0x6e, 0x84, 0x8f, 0xa4, 0x1f, 0x9c, 0x5a, 0x20, + 0xe3, 0x51, 0x5c, 0xb0, 0xc5, 0x9e, 0x99, 0x6a, 0x4f, 0xc8, 0x69, 0xf7, + 0x10, 0xff, 0x4e, 0xad, 0x19, 0xd9, 0xc9, 0x58, 0xb3, 0x33, 0xae, 0x0c, + 0xd9, 0x96, 0x29, 0x9e, 0x71, 0xb2, 0x70, 0x63, 0xa3, 0xb6, 0x99, 0x16, + 0x42, 0x1d, 0x65, 0xf3, 0xf7, 0xa0, 0x1e, 0x7d, 0xc5, 0xd4, 0x65, 0x14, + 0xb2, 0x62, 0x84, 0xd4, 0x6c, 0x5c, 0x08, 0x0c, 0xd8, 0x6c, 0x93, 0x2b, + 0xb4, 0x76, 0x59, 0x8a, 0xd1, 0x7f, 0xff, 0x03, 0xd8, 0xc2, 0x5d, 0xb8, + 0x2f, 0x22, 0xd6, 0x38, 0xf0, 0xf6, 0x9c, 0x6b, 0x7d, 0x46, 0xeb, 0x99, + 0x74, 0xf7, 0xeb, 0x4a, 0x0e, 0xa9, 0xa6, 0x04, 0xeb, 0x7b, 0xce, 0xf0, + 0x5c, 0x6b, 0x98, 0x31, 0x5a, 0x98, 0x40, 0xeb, 0x69, 0xc4, 0x05, 0xf4, + 0x20, 0xa8, 0xca, 0x08, 0x3a, 0x65, 0x6c, 0x38, 0x15, 0xf5, 0x5c, 0x2c, + 0xb2, 0x55, 0xe4, 0x2c, 0x6b, 0x41, 0xf0, 0xbe, 0x5c, 0x46, 0xca, 0x4a, + 0x29, 0xa0, 0x48, 0x5e, 0x20, 0xd2, 0x45, 0xff, 0x05, 0xde, 0x34, 0xaf, + 0x70, 0x4b, 0x81, 0x39, 0xe2, 0xca, 0x07, 0x57, 0x7c, 0xb6, 0x31, 0xdc, + 0x21, 0x29, 0xe2, 0xbe, 0x97, 0x0e, 0x77, 0x90, 0x14, 0x51, 0x40, 0xe1, + 0xbf, 0xe3, 0xcc, 0x1b, 0x19, 0x9c, 0x25, 0xca, 0xa7, 0x06, 0xb2, 0x53, + 0xdf, 0x23, 0xb2, 0xcf, 0x12, 0x19, 0xa3, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 1b:09:3b:78:60:96:da:37:bb:a4:51:94:46:c8:96:78 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority + Validity + Not Before: Nov 8 00:00:00 2006 GMT + Not After : Nov 7 23:59:59 2021 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b: + 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57: + 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8: + 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe: + 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d: + a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59: + 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49: + d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69: + 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96: + bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5: + f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02: + ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6: + f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19: + 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d: + 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95: + ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f: + 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8: + 25:15 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.verisign.com/pca3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.verisign.com/cps + + X509v3 Subject Key Identifier: + 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + Authority Information Access: + OCSP - URI:http://ocsp.verisign.com + + Signature Algorithm: sha1WithRSAEncryption + a3:cd:7d:1e:f7:c7:75:8d:48:e7:56:34:4c:00:90:75:a9:51: + a5:56:c1:6d:bc:fe:f5:53:22:e9:98:a2:ac:9a:7e:70:1e:b3: + 8e:3b:45:e3:86:95:31:da:6d:4c:fb:34:50:80:96:cd:24:f2: + 40:df:04:3f:e2:65:ce:34:22:61:15:ea:66:70:64:d2:f1:6e: + f3:ca:18:59:6a:41:46:7e:82:de:19:b0:70:31:56:69:0d:0c: + e6:1d:9d:71:58:dc:cc:de:62:f5:e1:7a:10:02:d8:7a:dc:3b: + fa:57:bd:c9:e9:8f:46:21:39:9f:51:65:4c:8e:3a:be:28:41: + 70:1d +-----BEGIN CERTIFICATE----- +MIIEkDCCA/mgAwIBAgIQGwk7eGCW2je7pFGURsiWeDANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT +LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv +ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8 +RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb +ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR +TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH +iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB +AAGjggFbMIIBVzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0 +dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9 +BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy +aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI +KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU +j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t +L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEAo819HvfHdY1I51Y0 +TACQdalRpVbBbbz+9VMi6ZiirJp+cB6zjjtF44aVMdptTPs0UICWzSTyQN8EP+Jl +zjQiYRXqZnBk0vFu88oYWWpBRn6C3hmwcDFWaQ0M5h2dcVjczN5i9eF6EALYetw7 ++le9yemPRiE5n1FlTI46vihBcB0= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert20[] = { + 0x30, 0x82, 0x04, 0x90, 0x30, 0x82, 0x03, 0xf9, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x1b, 0x09, 0x3b, 0x78, 0x60, 0x96, 0xda, 0x37, 0xbb, + 0xa4, 0x51, 0x94, 0x46, 0xc8, 0x96, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, + 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30, + 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, + 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, + 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, + 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30, + 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35, + 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c, + 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3, + 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22, + 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1, + 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb, + 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0, + 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85, + 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33, + 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51, + 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74, + 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0, + 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06, + 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff, + 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4, + 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19, + 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe, + 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47, + 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5, + 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14, + 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f, + 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5b, 0x30, 0x82, 0x01, 0x57, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, + 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, + 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, + 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, + 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, + 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, + 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, + 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, + 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0xa3, 0xcd, 0x7d, 0x1e, 0xf7, 0xc7, 0x75, 0x8d, 0x48, 0xe7, 0x56, 0x34, + 0x4c, 0x00, 0x90, 0x75, 0xa9, 0x51, 0xa5, 0x56, 0xc1, 0x6d, 0xbc, 0xfe, + 0xf5, 0x53, 0x22, 0xe9, 0x98, 0xa2, 0xac, 0x9a, 0x7e, 0x70, 0x1e, 0xb3, + 0x8e, 0x3b, 0x45, 0xe3, 0x86, 0x95, 0x31, 0xda, 0x6d, 0x4c, 0xfb, 0x34, + 0x50, 0x80, 0x96, 0xcd, 0x24, 0xf2, 0x40, 0xdf, 0x04, 0x3f, 0xe2, 0x65, + 0xce, 0x34, 0x22, 0x61, 0x15, 0xea, 0x66, 0x70, 0x64, 0xd2, 0xf1, 0x6e, + 0xf3, 0xca, 0x18, 0x59, 0x6a, 0x41, 0x46, 0x7e, 0x82, 0xde, 0x19, 0xb0, + 0x70, 0x31, 0x56, 0x69, 0x0d, 0x0c, 0xe6, 0x1d, 0x9d, 0x71, 0x58, 0xdc, + 0xcc, 0xde, 0x62, 0xf5, 0xe1, 0x7a, 0x10, 0x02, 0xd8, 0x7a, 0xdc, 0x3b, + 0xfa, 0x57, 0xbd, 0xc9, 0xe9, 0x8f, 0x46, 0x21, 0x39, 0x9f, 0x51, 0x65, + 0x4c, 0x8e, 0x3a, 0xbe, 0x28, 0x41, 0x70, 0x1d, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 01:fd:a3:eb:6e:ca:75:c8:88:43:8b:72:4b:cf:bc:91 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA + Validity + Not Before: Mar 8 12:00:00 2013 GMT + Not After : Mar 8 12:00:00 2023 GMT + Subject: C=US, O=DigiCert Inc, CN=DigiCert SHA2 Secure Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:dc:ae:58:90:4d:c1:c4:30:15:90:35:5b:6e:3c: + 82:15:f5:2c:5c:bd:e3:db:ff:71:43:fa:64:25:80: + d4:ee:18:a2:4d:f0:66:d0:0a:73:6e:11:98:36:17: + 64:af:37:9d:fd:fa:41:84:af:c7:af:8c:fe:1a:73: + 4d:cf:33:97:90:a2:96:87:53:83:2b:b9:a6:75:48: + 2d:1d:56:37:7b:da:31:32:1a:d7:ac:ab:06:f4:aa: + 5d:4b:b7:47:46:dd:2a:93:c3:90:2e:79:80:80:ef: + 13:04:6a:14:3b:b5:9b:92:be:c2:07:65:4e:fc:da: + fc:ff:7a:ae:dc:5c:7e:55:31:0c:e8:39:07:a4:d7: + be:2f:d3:0b:6a:d2:b1:df:5f:fe:57:74:53:3b:35: + 80:dd:ae:8e:44:98:b3:9f:0e:d3:da:e0:d7:f4:6b: + 29:ab:44:a7:4b:58:84:6d:92:4b:81:c3:da:73:8b: + 12:97:48:90:04:45:75:1a:dd:37:31:97:92:e8:cd: + 54:0d:3b:e4:c1:3f:39:5e:2e:b8:f3:5c:7e:10:8e: + 86:41:00:8d:45:66:47:b0:a1:65:ce:a0:aa:29:09: + 4e:f3:97:eb:e8:2e:ab:0f:72:a7:30:0e:fa:c7:f4: + fd:14:77:c3:a4:5b:28:57:c2:b3:f9:82:fd:b7:45: + 58:9b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl + + Full Name: + URI:http://crl4.digicert.com/DigiCertGlobalRootCA.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.digicert.com/CPS + + X509v3 Subject Key Identifier: + 0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2 + X509v3 Authority Key Identifier: + keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55 + + Signature Algorithm: sha256WithRSAEncryption + 23:3e:df:4b:d2:31:42:a5:b6:7e:42:5c:1a:44:cc:69:d1:68: + b4:5d:4b:e0:04:21:6c:4b:e2:6d:cc:b1:e0:97:8f:a6:53:09: + cd:aa:2a:65:e5:39:4f:1e:83:a5:6e:5c:98:a2:24:26:e6:fb: + a1:ed:93:c7:2e:02:c6:4d:4a:bf:b0:42:df:78:da:b3:a8:f9: + 6d:ff:21:85:53:36:60:4c:76:ce:ec:38:dc:d6:51:80:f0:c5: + d6:e5:d4:4d:27:64:ab:9b:c7:3e:71:fb:48:97:b8:33:6d:c9: + 13:07:ee:96:a2:1b:18:15:f6:5c:4c:40:ed:b3:c2:ec:ff:71: + c1:e3:47:ff:d4:b9:00:b4:37:42:da:20:c9:ea:6e:8a:ee:14: + 06:ae:7d:a2:59:98:88:a8:1b:6f:2d:f4:f2:c9:14:5f:26:cf: + 2c:8d:7e:ed:37:c0:a9:d5:39:b9:82:bf:19:0c:ea:34:af:00: + 21:68:f8:ad:73:e2:c9:32:da:38:25:0b:55:d3:9a:1d:f0:68: + 86:ed:2e:41:34:ef:7c:a5:50:1d:bf:3a:f9:d3:c1:08:0c:e6: + ed:1e:8a:58:25:e4:b8:77:ad:2d:6e:f5:52:dd:b4:74:8f:ab: + 49:2e:9d:3b:93:34:28:1f:78:ce:94:ea:c7:bd:d3:c9:6d:1c: + de:5c:32:f3 +-----BEGIN CERTIFICATE----- +MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg +U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83 +nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd +KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f +/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX +kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0 +/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C +AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY +aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6 +Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1 +oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD +QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v +d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh +xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB +CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl +5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA +8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC +2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit +c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0 +j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert21[] = { + 0x30, 0x82, 0x04, 0x94, 0x30, 0x82, 0x03, 0x7c, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x01, 0xfd, 0xa3, 0xeb, 0x6e, 0xca, 0x75, 0xc8, 0x88, + 0x43, 0x8b, 0x72, 0x4b, 0xcf, 0xbc, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x33, 0x30, 0x38, 0x31, + 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x33, + 0x30, 0x38, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4d, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, + 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, 0x32, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xdc, 0xae, 0x58, 0x90, 0x4d, 0xc1, 0xc4, 0x30, 0x15, 0x90, 0x35, + 0x5b, 0x6e, 0x3c, 0x82, 0x15, 0xf5, 0x2c, 0x5c, 0xbd, 0xe3, 0xdb, 0xff, + 0x71, 0x43, 0xfa, 0x64, 0x25, 0x80, 0xd4, 0xee, 0x18, 0xa2, 0x4d, 0xf0, + 0x66, 0xd0, 0x0a, 0x73, 0x6e, 0x11, 0x98, 0x36, 0x17, 0x64, 0xaf, 0x37, + 0x9d, 0xfd, 0xfa, 0x41, 0x84, 0xaf, 0xc7, 0xaf, 0x8c, 0xfe, 0x1a, 0x73, + 0x4d, 0xcf, 0x33, 0x97, 0x90, 0xa2, 0x96, 0x87, 0x53, 0x83, 0x2b, 0xb9, + 0xa6, 0x75, 0x48, 0x2d, 0x1d, 0x56, 0x37, 0x7b, 0xda, 0x31, 0x32, 0x1a, + 0xd7, 0xac, 0xab, 0x06, 0xf4, 0xaa, 0x5d, 0x4b, 0xb7, 0x47, 0x46, 0xdd, + 0x2a, 0x93, 0xc3, 0x90, 0x2e, 0x79, 0x80, 0x80, 0xef, 0x13, 0x04, 0x6a, + 0x14, 0x3b, 0xb5, 0x9b, 0x92, 0xbe, 0xc2, 0x07, 0x65, 0x4e, 0xfc, 0xda, + 0xfc, 0xff, 0x7a, 0xae, 0xdc, 0x5c, 0x7e, 0x55, 0x31, 0x0c, 0xe8, 0x39, + 0x07, 0xa4, 0xd7, 0xbe, 0x2f, 0xd3, 0x0b, 0x6a, 0xd2, 0xb1, 0xdf, 0x5f, + 0xfe, 0x57, 0x74, 0x53, 0x3b, 0x35, 0x80, 0xdd, 0xae, 0x8e, 0x44, 0x98, + 0xb3, 0x9f, 0x0e, 0xd3, 0xda, 0xe0, 0xd7, 0xf4, 0x6b, 0x29, 0xab, 0x44, + 0xa7, 0x4b, 0x58, 0x84, 0x6d, 0x92, 0x4b, 0x81, 0xc3, 0xda, 0x73, 0x8b, + 0x12, 0x97, 0x48, 0x90, 0x04, 0x45, 0x75, 0x1a, 0xdd, 0x37, 0x31, 0x97, + 0x92, 0xe8, 0xcd, 0x54, 0x0d, 0x3b, 0xe4, 0xc1, 0x3f, 0x39, 0x5e, 0x2e, + 0xb8, 0xf3, 0x5c, 0x7e, 0x10, 0x8e, 0x86, 0x41, 0x00, 0x8d, 0x45, 0x66, + 0x47, 0xb0, 0xa1, 0x65, 0xce, 0xa0, 0xaa, 0x29, 0x09, 0x4e, 0xf3, 0x97, + 0xeb, 0xe8, 0x2e, 0xab, 0x0f, 0x72, 0xa7, 0x30, 0x0e, 0xfa, 0xc7, 0xf4, + 0xfd, 0x14, 0x77, 0xc3, 0xa4, 0x5b, 0x28, 0x57, 0xc2, 0xb3, 0xf9, 0x82, + 0xfd, 0xb7, 0x45, 0x58, 0x9b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x5a, 0x30, 0x82, 0x01, 0x56, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, + 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30, + 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, + 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, + 0x43, 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, + 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x37, 0xa0, 0x35, + 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, + 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, + 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, + 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, + 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0f, 0x80, 0x61, 0x1c, 0x82, + 0x31, 0x61, 0xd5, 0x2f, 0x28, 0xe7, 0x8d, 0x46, 0x38, 0xb4, 0x2c, 0xe1, + 0xc6, 0xd9, 0xe2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, + 0x66, 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x23, 0x3e, 0xdf, 0x4b, + 0xd2, 0x31, 0x42, 0xa5, 0xb6, 0x7e, 0x42, 0x5c, 0x1a, 0x44, 0xcc, 0x69, + 0xd1, 0x68, 0xb4, 0x5d, 0x4b, 0xe0, 0x04, 0x21, 0x6c, 0x4b, 0xe2, 0x6d, + 0xcc, 0xb1, 0xe0, 0x97, 0x8f, 0xa6, 0x53, 0x09, 0xcd, 0xaa, 0x2a, 0x65, + 0xe5, 0x39, 0x4f, 0x1e, 0x83, 0xa5, 0x6e, 0x5c, 0x98, 0xa2, 0x24, 0x26, + 0xe6, 0xfb, 0xa1, 0xed, 0x93, 0xc7, 0x2e, 0x02, 0xc6, 0x4d, 0x4a, 0xbf, + 0xb0, 0x42, 0xdf, 0x78, 0xda, 0xb3, 0xa8, 0xf9, 0x6d, 0xff, 0x21, 0x85, + 0x53, 0x36, 0x60, 0x4c, 0x76, 0xce, 0xec, 0x38, 0xdc, 0xd6, 0x51, 0x80, + 0xf0, 0xc5, 0xd6, 0xe5, 0xd4, 0x4d, 0x27, 0x64, 0xab, 0x9b, 0xc7, 0x3e, + 0x71, 0xfb, 0x48, 0x97, 0xb8, 0x33, 0x6d, 0xc9, 0x13, 0x07, 0xee, 0x96, + 0xa2, 0x1b, 0x18, 0x15, 0xf6, 0x5c, 0x4c, 0x40, 0xed, 0xb3, 0xc2, 0xec, + 0xff, 0x71, 0xc1, 0xe3, 0x47, 0xff, 0xd4, 0xb9, 0x00, 0xb4, 0x37, 0x42, + 0xda, 0x20, 0xc9, 0xea, 0x6e, 0x8a, 0xee, 0x14, 0x06, 0xae, 0x7d, 0xa2, + 0x59, 0x98, 0x88, 0xa8, 0x1b, 0x6f, 0x2d, 0xf4, 0xf2, 0xc9, 0x14, 0x5f, + 0x26, 0xcf, 0x2c, 0x8d, 0x7e, 0xed, 0x37, 0xc0, 0xa9, 0xd5, 0x39, 0xb9, + 0x82, 0xbf, 0x19, 0x0c, 0xea, 0x34, 0xaf, 0x00, 0x21, 0x68, 0xf8, 0xad, + 0x73, 0xe2, 0xc9, 0x32, 0xda, 0x38, 0x25, 0x0b, 0x55, 0xd3, 0x9a, 0x1d, + 0xf0, 0x68, 0x86, 0xed, 0x2e, 0x41, 0x34, 0xef, 0x7c, 0xa5, 0x50, 0x1d, + 0xbf, 0x3a, 0xf9, 0xd3, 0xc1, 0x08, 0x0c, 0xe6, 0xed, 0x1e, 0x8a, 0x58, + 0x25, 0xe4, 0xb8, 0x77, 0xad, 0x2d, 0x6e, 0xf5, 0x52, 0xdd, 0xb4, 0x74, + 0x8f, 0xab, 0x49, 0x2e, 0x9d, 0x3b, 0x93, 0x34, 0x28, 0x1f, 0x78, 0xce, + 0x94, 0xea, 0xc7, 0xbd, 0xd3, 0xc9, 0x6d, 0x1c, 0xde, 0x5c, 0x32, 0xf3, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 0b:1d:b1:a9:19:f2:4c:3c:4e:fc:b5:7a:6a:4e:6c:bf + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority + Validity + Not Before: Aug 23 00:00:00 2012 GMT + Not After : Aug 22 23:59:59 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Extended Validation SSL CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9e:c6:21:cd:2e:3d:d0:bb:2a:4d:a4:7b:1f:a8: + 1a:c2:03:a6:ff:43:62:5b:bf:91:d1:66:52:a9:81: + 90:68:31:86:16:bb:1d:85:58:a9:7e:91:6a:1e:4c: + 31:ca:21:c4:be:70:1b:9f:8c:e4:05:2d:9c:ed:11: + 79:ad:8f:9c:25:86:4c:ba:f2:e5:62:79:8e:22:5f: + 85:7c:22:35:38:23:8d:80:3c:ac:cc:2d:fc:58:f2: + 35:bf:66:5b:eb:c1:24:f8:70:80:74:32:f9:46:de: + 32:19:80:8c:b7:e7:1a:a1:aa:64:98:8d:ca:ce:0e: + dc:6b:f7:e2:90:0a:6c:1c:a5:f4:90:32:52:e5:f1: + 00:42:31:91:48:42:89:a8:5d:7f:63:8d:31:b2:d6: + 48:5c:45:45:22:c9:c5:59:12:ab:41:94:ea:fe:9c: + 46:4d:9a:bc:9c:e0:e2:c6:46:b3:e6:7f:dc:f5:0f: + a3:13:45:86:6d:79:78:fc:e1:50:cf:09:86:e5:9f: + bf:cb:3a:d4:e0:b1:d4:ff:a8:3f:7d:62:1f:c0:6d: + 78:48:c3:d7:a3:a5:23:61:c5:3e:35:4d:b2:e5:f8: + fd:94:4b:bc:73:53:af:e3:9a:69:55:be:cb:67:ab: + e1:be:ef:1b:c2:4d:ac:cb:29:5c:bc:ed:b8:62:9d: + 10:e9 + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://EVSecure-ocsp.geotrust.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.geotrust.com/resources/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://EVSecure-crl.geotrust.com/GeoTrustPCA.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-253 + X509v3 Subject Key Identifier: + 6F:26:56:D9:5C:E7:F7:C9:04:20:F8:1E:BA:7C:91:27:2F:8C:FA:07 + X509v3 Authority Key Identifier: + keyid:2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92 + + Signature Algorithm: sha1WithRSAEncryption + 92:77:e9:57:c9:eb:c4:45:6f:c9:4c:6e:7d:00:12:71:a5:e3: + 39:fe:13:84:49:6c:e7:49:71:f5:2c:c7:c0:36:c2:08:58:f3: + 83:75:c5:72:d8:8d:78:f4:65:ea:8c:d5:e3:a5:0e:a9:ad:eb: + e3:a1:23:ae:93:b7:d8:75:75:4a:59:cb:f2:9e:db:40:bf:4e: + 89:fe:95:42:29:34:7b:f4:dd:6a:0d:74:5f:c7:11:13:2e:dd: + 11:6e:c6:e3:5b:b3:cf:a6:8d:e5:f7:67:7b:ba:b3:b3:69:70: + 14:b0:c2:99:b4:d2:76:5b:38:17:39:45:1b:82:f1:53:b8:3d: + 55:39:0b:7f:ff:98:ad:6e:96:9a:b6:6a:4c:7a:5e:bd:b1:86: + 12:9d:7c:2c:62:bb:09:93:5f:3f:d8:b5:8a:c3:49:28:0f:0b: + f9:39:22:1a:fe:5d:d3:e8:18:5f:9d:5f:b4:c0:20:c6:a9:49: + 0d:55:73:6a:09:7a:ff:a2:99:bf:d8:bb:91:dc:30:39:ae:28: + 4b:f6:c5:77:24:e8:d6:c6:a7:a0:4e:f2:a6:99:75:cd:dd:57: + dd:0a:47:92:cb:bb:b7:48:fa:21:f0:69:21:ff:e5:0c:aa:0c: + b1:ea:dd:05:1c:19:8e:d1:2a:79:68:02:5e:cc:38:e6:29:c4: + 77:f5:19:1c +-----BEGIN CERTIFICATE----- +MIIEmjCCA4KgAwIBAgIQCx2xqRnyTDxO/LV6ak5svzANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMjA4 +MjMwMDAwMDBaFw0yMjA4MjIyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBFeHRlbmRlZCBWYWxp +ZGF0aW9uIFNTTCBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAnsYhzS490LsqTaR7H6gawgOm/0NiW7+R0WZSqYGQaDGGFrsdhVipfpFqHkwx +yiHEvnAbn4zkBS2c7RF5rY+cJYZMuvLlYnmOIl+FfCI1OCONgDyszC38WPI1v2Zb +68Ek+HCAdDL5Rt4yGYCMt+caoapkmI3Kzg7ca/fikApsHKX0kDJS5fEAQjGRSEKJ +qF1/Y40xstZIXEVFIsnFWRKrQZTq/pxGTZq8nODixkaz5n/c9Q+jE0WGbXl4/OFQ +zwmG5Z+/yzrU4LHU/6g/fWIfwG14SMPXo6UjYcU+NU2y5fj9lEu8c1Ov45ppVb7L +Z6vhvu8bwk2syylcvO24Yp0Q6QIDAQABo4IBXjCCAVowPQYIKwYBBQUHAQEEMTAv +MC0GCCsGAQUFBzABhiFodHRwOi8vRVZTZWN1cmUtb2NzcC5nZW90cnVzdC5jb20w +EgYDVR0TAQH/BAgwBgEB/wIBADBGBgNVHSAEPzA9MDsGBFUdIAAwMzAxBggrBgEF +BQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczBBBgNV +HR8EOjA4MDagNKAyhjBodHRwOi8vRVZTZWN1cmUtY3JsLmdlb3RydXN0LmNvbS9H +ZW9UcnVzdFBDQS5jcmwwDgYDVR0PAQH/BAQDAgEGMCoGA1UdEQQjMCGkHzAdMRsw +GQYDVQQDExJWZXJpU2lnbk1QS0ktMi0yNTMwHQYDVR0OBBYEFG8mVtlc5/fJBCD4 +Hrp8kScvjPoHMB8GA1UdIwQYMBaAFCzVUEGXFYvwjzZhW0r7a9mZyTOSMA0GCSqG +SIb3DQEBBQUAA4IBAQCSd+lXyevERW/JTG59ABJxpeM5/hOESWznSXH1LMfANsII +WPODdcVy2I149GXqjNXjpQ6prevjoSOuk7fYdXVKWcvynttAv06J/pVCKTR79N1q +DXRfxxETLt0RbsbjW7PPpo3l92d7urOzaXAUsMKZtNJ2WzgXOUUbgvFTuD1VOQt/ +/5itbpaatmpMel69sYYSnXwsYrsJk18/2LWKw0koDwv5OSIa/l3T6BhfnV+0wCDG +qUkNVXNqCXr/opm/2LuR3DA5rihL9sV3JOjWxqegTvKmmXXN3VfdCkeSy7u3SPoh +8Gkh/+UMqgyx6t0FHBmO0Sp5aAJezDjmKcR39Rkc +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert22[] = { + 0x30, 0x82, 0x04, 0x9a, 0x30, 0x82, 0x03, 0x82, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x0b, 0x1d, 0xb1, 0xa9, 0x19, 0xf2, 0x4c, 0x3c, 0x4e, + 0xfc, 0xb5, 0x7a, 0x6a, 0x4e, 0x6c, 0xbf, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x58, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, 0x38, + 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, + 0x32, 0x30, 0x38, 0x32, 0x32, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, + 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, + 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0x9e, 0xc6, 0x21, 0xcd, 0x2e, 0x3d, 0xd0, 0xbb, 0x2a, + 0x4d, 0xa4, 0x7b, 0x1f, 0xa8, 0x1a, 0xc2, 0x03, 0xa6, 0xff, 0x43, 0x62, + 0x5b, 0xbf, 0x91, 0xd1, 0x66, 0x52, 0xa9, 0x81, 0x90, 0x68, 0x31, 0x86, + 0x16, 0xbb, 0x1d, 0x85, 0x58, 0xa9, 0x7e, 0x91, 0x6a, 0x1e, 0x4c, 0x31, + 0xca, 0x21, 0xc4, 0xbe, 0x70, 0x1b, 0x9f, 0x8c, 0xe4, 0x05, 0x2d, 0x9c, + 0xed, 0x11, 0x79, 0xad, 0x8f, 0x9c, 0x25, 0x86, 0x4c, 0xba, 0xf2, 0xe5, + 0x62, 0x79, 0x8e, 0x22, 0x5f, 0x85, 0x7c, 0x22, 0x35, 0x38, 0x23, 0x8d, + 0x80, 0x3c, 0xac, 0xcc, 0x2d, 0xfc, 0x58, 0xf2, 0x35, 0xbf, 0x66, 0x5b, + 0xeb, 0xc1, 0x24, 0xf8, 0x70, 0x80, 0x74, 0x32, 0xf9, 0x46, 0xde, 0x32, + 0x19, 0x80, 0x8c, 0xb7, 0xe7, 0x1a, 0xa1, 0xaa, 0x64, 0x98, 0x8d, 0xca, + 0xce, 0x0e, 0xdc, 0x6b, 0xf7, 0xe2, 0x90, 0x0a, 0x6c, 0x1c, 0xa5, 0xf4, + 0x90, 0x32, 0x52, 0xe5, 0xf1, 0x00, 0x42, 0x31, 0x91, 0x48, 0x42, 0x89, + 0xa8, 0x5d, 0x7f, 0x63, 0x8d, 0x31, 0xb2, 0xd6, 0x48, 0x5c, 0x45, 0x45, + 0x22, 0xc9, 0xc5, 0x59, 0x12, 0xab, 0x41, 0x94, 0xea, 0xfe, 0x9c, 0x46, + 0x4d, 0x9a, 0xbc, 0x9c, 0xe0, 0xe2, 0xc6, 0x46, 0xb3, 0xe6, 0x7f, 0xdc, + 0xf5, 0x0f, 0xa3, 0x13, 0x45, 0x86, 0x6d, 0x79, 0x78, 0xfc, 0xe1, 0x50, + 0xcf, 0x09, 0x86, 0xe5, 0x9f, 0xbf, 0xcb, 0x3a, 0xd4, 0xe0, 0xb1, 0xd4, + 0xff, 0xa8, 0x3f, 0x7d, 0x62, 0x1f, 0xc0, 0x6d, 0x78, 0x48, 0xc3, 0xd7, + 0xa3, 0xa5, 0x23, 0x61, 0xc5, 0x3e, 0x35, 0x4d, 0xb2, 0xe5, 0xf8, 0xfd, + 0x94, 0x4b, 0xbc, 0x73, 0x53, 0xaf, 0xe3, 0x9a, 0x69, 0x55, 0xbe, 0xcb, + 0x67, 0xab, 0xe1, 0xbe, 0xef, 0x1b, 0xc2, 0x4d, 0xac, 0xcb, 0x29, 0x5c, + 0xbc, 0xed, 0xb8, 0x62, 0x9d, 0x10, 0xe9, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x5e, 0x30, 0x82, 0x01, 0x5a, 0x30, 0x3d, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, + 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, + 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, + 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x46, 0x06, 0x03, 0x55, + 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55, 0x1d, + 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x41, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x3a, 0x30, 0x38, 0x30, 0x36, 0xa0, 0x34, 0xa0, 0x32, + 0x86, 0x30, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, + 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, + 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, + 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x32, + 0x35, 0x33, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x6f, 0x26, 0x56, 0xd9, 0x5c, 0xe7, 0xf7, 0xc9, 0x04, 0x20, 0xf8, + 0x1e, 0xba, 0x7c, 0x91, 0x27, 0x2f, 0x8c, 0xfa, 0x07, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x2c, 0xd5, + 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb, + 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x92, 0x77, 0xe9, 0x57, 0xc9, 0xeb, 0xc4, 0x45, 0x6f, 0xc9, + 0x4c, 0x6e, 0x7d, 0x00, 0x12, 0x71, 0xa5, 0xe3, 0x39, 0xfe, 0x13, 0x84, + 0x49, 0x6c, 0xe7, 0x49, 0x71, 0xf5, 0x2c, 0xc7, 0xc0, 0x36, 0xc2, 0x08, + 0x58, 0xf3, 0x83, 0x75, 0xc5, 0x72, 0xd8, 0x8d, 0x78, 0xf4, 0x65, 0xea, + 0x8c, 0xd5, 0xe3, 0xa5, 0x0e, 0xa9, 0xad, 0xeb, 0xe3, 0xa1, 0x23, 0xae, + 0x93, 0xb7, 0xd8, 0x75, 0x75, 0x4a, 0x59, 0xcb, 0xf2, 0x9e, 0xdb, 0x40, + 0xbf, 0x4e, 0x89, 0xfe, 0x95, 0x42, 0x29, 0x34, 0x7b, 0xf4, 0xdd, 0x6a, + 0x0d, 0x74, 0x5f, 0xc7, 0x11, 0x13, 0x2e, 0xdd, 0x11, 0x6e, 0xc6, 0xe3, + 0x5b, 0xb3, 0xcf, 0xa6, 0x8d, 0xe5, 0xf7, 0x67, 0x7b, 0xba, 0xb3, 0xb3, + 0x69, 0x70, 0x14, 0xb0, 0xc2, 0x99, 0xb4, 0xd2, 0x76, 0x5b, 0x38, 0x17, + 0x39, 0x45, 0x1b, 0x82, 0xf1, 0x53, 0xb8, 0x3d, 0x55, 0x39, 0x0b, 0x7f, + 0xff, 0x98, 0xad, 0x6e, 0x96, 0x9a, 0xb6, 0x6a, 0x4c, 0x7a, 0x5e, 0xbd, + 0xb1, 0x86, 0x12, 0x9d, 0x7c, 0x2c, 0x62, 0xbb, 0x09, 0x93, 0x5f, 0x3f, + 0xd8, 0xb5, 0x8a, 0xc3, 0x49, 0x28, 0x0f, 0x0b, 0xf9, 0x39, 0x22, 0x1a, + 0xfe, 0x5d, 0xd3, 0xe8, 0x18, 0x5f, 0x9d, 0x5f, 0xb4, 0xc0, 0x20, 0xc6, + 0xa9, 0x49, 0x0d, 0x55, 0x73, 0x6a, 0x09, 0x7a, 0xff, 0xa2, 0x99, 0xbf, + 0xd8, 0xbb, 0x91, 0xdc, 0x30, 0x39, 0xae, 0x28, 0x4b, 0xf6, 0xc5, 0x77, + 0x24, 0xe8, 0xd6, 0xc6, 0xa7, 0xa0, 0x4e, 0xf2, 0xa6, 0x99, 0x75, 0xcd, + 0xdd, 0x57, 0xdd, 0x0a, 0x47, 0x92, 0xcb, 0xbb, 0xb7, 0x48, 0xfa, 0x21, + 0xf0, 0x69, 0x21, 0xff, 0xe5, 0x0c, 0xaa, 0x0c, 0xb1, 0xea, 0xdd, 0x05, + 0x1c, 0x19, 0x8e, 0xd1, 0x2a, 0x79, 0x68, 0x02, 0x5e, 0xcc, 0x38, 0xe6, + 0x29, 0xc4, 0x77, 0xf5, 0x19, 0x1c, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3740804 (0x391484) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority + Validity + Not Before: Jan 1 07:00:00 2014 GMT + Not After : May 30 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bd:ed:c1:03:fc:f6:8f:fc:02:b1:6f:5b:9f:48: + d9:9d:79:e2:a2:b7:03:61:56:18:c3:47:b6:d7:ca: + 3d:35:2e:89:43:f7:a1:69:9b:de:8a:1a:fd:13:20: + 9c:b4:49:77:32:29:56:fd:b9:ec:8c:dd:22:fa:72: + dc:27:61:97:ee:f6:5a:84:ec:6e:19:b9:89:2c:dc: + 84:5b:d5:74:fb:6b:5f:c5:89:a5:10:52:89:46:55: + f4:b8:75:1c:e6:7f:e4:54:ae:4b:f8:55:72:57:02: + 19:f8:17:71:59:eb:1e:28:07:74:c5:9d:48:be:6c: + b4:f4:a4:b0:f3:64:37:79:92:c0:ec:46:5e:7f:e1: + 6d:53:4c:62:af:cd:1f:0b:63:bb:3a:9d:fb:fc:79: + 00:98:61:74:cf:26:82:40:63:f3:b2:72:6a:19:0d: + 99:ca:d4:0e:75:cc:37:fb:8b:89:c1:59:f1:62:7f: + 5f:b3:5f:65:30:f8:a7:b7:4d:76:5a:1e:76:5e:34: + c0:e8:96:56:99:8a:b3:f0:7f:a4:cd:bd:dc:32:31: + 7c:91:cf:e0:5f:11:f8:6b:aa:49:5c:d1:99:94:d1: + a2:e3:63:5b:09:76:b5:56:62:e1:4b:74:1d:96:d4: + 26:d4:08:04:59:d0:98:0e:0e:e6:de:fc:c3:ec:1f: + 90:f1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27 + X509v3 Authority Key Identifier: + keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7 + + Authority Information Access: + OCSP - URI:http://ocsp.starfieldtech.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.starfieldtech.com/sfroot.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.starfieldtech.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 85:63:c1:d9:dd:b9:ff:a9:bd:a6:19:dc:bf:13:3a:11:38:22: + 54:b1:ac:05:10:fb:7c:b3:96:3f:31:8b:66:ff:88:f3:e1:bf: + fb:c7:1f:00:ff:46:6a:8b:61:32:c9:01:51:76:fb:9a:c6:fa: + 20:51:c8:46:c4:98:d7:79:a3:e3:04:72:3f:8b:4d:34:53:67: + ec:33:2c:7b:e8:94:01:28:7c:3a:34:5b:02:77:16:8d:40:25: + 33:b0:bc:6c:97:d7:05:7a:ff:8c:85:ce:6f:a0:53:00:17:6e: + 1e:6c:bd:22:d7:0a:88:37:f6:7d:eb:99:41:ef:27:cb:8c:60: + 6b:4c:01:7e:65:50:0b:4f:b8:95:9a:9a:6e:34:fd:73:3a:33: + f1:91:d5:f3:4e:2d:74:e8:ef:d3:90:35:f1:06:68:64:d4:d0: + 13:fd:52:d3:c6:6d:c1:3a:8a:31:dd:05:26:35:4a:8c:65:b8: + 52:6b:81:ec:d2:9c:b5:34:10:97:9c:3e:c6:2f:ed:8e:42:42: + 24:2e:e9:73:9a:25:f9:11:f1:f2:23:69:cb:e5:94:69:a0:d2: + dc:b0:fc:44:89:ac:17:a8:cc:d5:37:77:16:c5:80:b9:0c:8f: + 57:02:55:99:85:7b:49:f0:2e:5b:a0:c2:57:53:5d:a2:e8:a6: + 37:c3:01:fa +-----BEGIN CERTIFICATE----- +MIIEoDCCA4igAwIBAgIDORSEMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAlVT +MSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQL +EylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NDAxMDEwNzAwMDBaFw0zMTA1MzAwNzAwMDBaMIGPMQswCQYDVQQGEwJVUzEQMA4G +A1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UEChMcU3Rh +cmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UEAxMpU3RhcmZpZWxkIFJv +b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC97cED/PaP/AKxb1ufSNmdeeKitwNhVhjDR7bXyj01LolD +96Fpm96KGv0TIJy0SXcyKVb9ueyM3SL6ctwnYZfu9lqE7G4ZuYks3IRb1XT7a1/F +iaUQUolGVfS4dRzmf+RUrkv4VXJXAhn4F3FZ6x4oB3TFnUi+bLT0pLDzZDd5ksDs +Rl5/4W1TTGKvzR8LY7s6nfv8eQCYYXTPJoJAY/OycmoZDZnK1A51zDf7i4nBWfFi +f1+zX2Uw+Ke3TXZaHnZeNMDollaZirPwf6TNvdwyMXyRz+BfEfhrqklc0ZmU0aLj +Y1sJdrVWYuFLdB2W1CbUCARZ0JgODube/MPsH5DxAgMBAAGjggEpMIIBJTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfAwyH6fZMH/E +fWijYqihzqsHWycwHwYDVR0jBBgwFoAUv1+30c7dH4b0W1Ws3NcQwg6piOcwOgYI +KwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0 +ZWNoLmNvbS8wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5zdGFyZmllbGR0 +ZWNoLmNvbS9zZnJvb3QuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF +BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQCFY8HZ3bn/qb2mGdy/EzoROCJUsawFEPt8s5Y/ +MYtm/4jz4b/7xx8A/0Zqi2EyyQFRdvuaxvogUchGxJjXeaPjBHI/i000U2fsMyx7 +6JQBKHw6NFsCdxaNQCUzsLxsl9cFev+Mhc5voFMAF24ebL0i1wqIN/Z965lB7yfL +jGBrTAF+ZVALT7iVmppuNP1zOjPxkdXzTi106O/TkDXxBmhk1NAT/VLTxm3BOoox +3QUmNUqMZbhSa4Hs0py1NBCXnD7GL+2OQkIkLulzmiX5EfHyI2nL5ZRpoNLcsPxE +iawXqMzVN3cWxYC5DI9XAlWZhXtJ8C5boMJXU12i6KY3wwH6 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert23[] = { + 0x30, 0x82, 0x04, 0xa0, 0x30, 0x82, 0x03, 0x88, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x39, 0x14, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x68, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, + 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x34, 0x30, 0x31, 0x30, 0x31, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, + 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x30, 0x81, 0x8f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, + 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, + 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, + 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, + 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, + 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, + 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xbd, 0xed, 0xc1, 0x03, 0xfc, 0xf6, 0x8f, 0xfc, 0x02, 0xb1, + 0x6f, 0x5b, 0x9f, 0x48, 0xd9, 0x9d, 0x79, 0xe2, 0xa2, 0xb7, 0x03, 0x61, + 0x56, 0x18, 0xc3, 0x47, 0xb6, 0xd7, 0xca, 0x3d, 0x35, 0x2e, 0x89, 0x43, + 0xf7, 0xa1, 0x69, 0x9b, 0xde, 0x8a, 0x1a, 0xfd, 0x13, 0x20, 0x9c, 0xb4, + 0x49, 0x77, 0x32, 0x29, 0x56, 0xfd, 0xb9, 0xec, 0x8c, 0xdd, 0x22, 0xfa, + 0x72, 0xdc, 0x27, 0x61, 0x97, 0xee, 0xf6, 0x5a, 0x84, 0xec, 0x6e, 0x19, + 0xb9, 0x89, 0x2c, 0xdc, 0x84, 0x5b, 0xd5, 0x74, 0xfb, 0x6b, 0x5f, 0xc5, + 0x89, 0xa5, 0x10, 0x52, 0x89, 0x46, 0x55, 0xf4, 0xb8, 0x75, 0x1c, 0xe6, + 0x7f, 0xe4, 0x54, 0xae, 0x4b, 0xf8, 0x55, 0x72, 0x57, 0x02, 0x19, 0xf8, + 0x17, 0x71, 0x59, 0xeb, 0x1e, 0x28, 0x07, 0x74, 0xc5, 0x9d, 0x48, 0xbe, + 0x6c, 0xb4, 0xf4, 0xa4, 0xb0, 0xf3, 0x64, 0x37, 0x79, 0x92, 0xc0, 0xec, + 0x46, 0x5e, 0x7f, 0xe1, 0x6d, 0x53, 0x4c, 0x62, 0xaf, 0xcd, 0x1f, 0x0b, + 0x63, 0xbb, 0x3a, 0x9d, 0xfb, 0xfc, 0x79, 0x00, 0x98, 0x61, 0x74, 0xcf, + 0x26, 0x82, 0x40, 0x63, 0xf3, 0xb2, 0x72, 0x6a, 0x19, 0x0d, 0x99, 0xca, + 0xd4, 0x0e, 0x75, 0xcc, 0x37, 0xfb, 0x8b, 0x89, 0xc1, 0x59, 0xf1, 0x62, + 0x7f, 0x5f, 0xb3, 0x5f, 0x65, 0x30, 0xf8, 0xa7, 0xb7, 0x4d, 0x76, 0x5a, + 0x1e, 0x76, 0x5e, 0x34, 0xc0, 0xe8, 0x96, 0x56, 0x99, 0x8a, 0xb3, 0xf0, + 0x7f, 0xa4, 0xcd, 0xbd, 0xdc, 0x32, 0x31, 0x7c, 0x91, 0xcf, 0xe0, 0x5f, + 0x11, 0xf8, 0x6b, 0xaa, 0x49, 0x5c, 0xd1, 0x99, 0x94, 0xd1, 0xa2, 0xe3, + 0x63, 0x5b, 0x09, 0x76, 0xb5, 0x56, 0x62, 0xe1, 0x4b, 0x74, 0x1d, 0x96, + 0xd4, 0x26, 0xd4, 0x08, 0x04, 0x59, 0xd0, 0x98, 0x0e, 0x0e, 0xe6, 0xde, + 0xfc, 0xc3, 0xec, 0x1f, 0x90, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x29, 0x30, 0x82, 0x01, 0x25, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x7c, 0x0c, 0x32, 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4, + 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1, 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0xbf, 0x5f, 0xb7, 0xd1, 0xce, 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55, 0xac, + 0xdc, 0xd7, 0x10, 0xc2, 0x0e, 0xa9, 0x88, 0xe7, 0x30, 0x3a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c, + 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, + 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x38, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0xa0, 0x2b, 0xa0, + 0x29, 0x86, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, + 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f, + 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20, + 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x85, 0x63, 0xc1, 0xd9, + 0xdd, 0xb9, 0xff, 0xa9, 0xbd, 0xa6, 0x19, 0xdc, 0xbf, 0x13, 0x3a, 0x11, + 0x38, 0x22, 0x54, 0xb1, 0xac, 0x05, 0x10, 0xfb, 0x7c, 0xb3, 0x96, 0x3f, + 0x31, 0x8b, 0x66, 0xff, 0x88, 0xf3, 0xe1, 0xbf, 0xfb, 0xc7, 0x1f, 0x00, + 0xff, 0x46, 0x6a, 0x8b, 0x61, 0x32, 0xc9, 0x01, 0x51, 0x76, 0xfb, 0x9a, + 0xc6, 0xfa, 0x20, 0x51, 0xc8, 0x46, 0xc4, 0x98, 0xd7, 0x79, 0xa3, 0xe3, + 0x04, 0x72, 0x3f, 0x8b, 0x4d, 0x34, 0x53, 0x67, 0xec, 0x33, 0x2c, 0x7b, + 0xe8, 0x94, 0x01, 0x28, 0x7c, 0x3a, 0x34, 0x5b, 0x02, 0x77, 0x16, 0x8d, + 0x40, 0x25, 0x33, 0xb0, 0xbc, 0x6c, 0x97, 0xd7, 0x05, 0x7a, 0xff, 0x8c, + 0x85, 0xce, 0x6f, 0xa0, 0x53, 0x00, 0x17, 0x6e, 0x1e, 0x6c, 0xbd, 0x22, + 0xd7, 0x0a, 0x88, 0x37, 0xf6, 0x7d, 0xeb, 0x99, 0x41, 0xef, 0x27, 0xcb, + 0x8c, 0x60, 0x6b, 0x4c, 0x01, 0x7e, 0x65, 0x50, 0x0b, 0x4f, 0xb8, 0x95, + 0x9a, 0x9a, 0x6e, 0x34, 0xfd, 0x73, 0x3a, 0x33, 0xf1, 0x91, 0xd5, 0xf3, + 0x4e, 0x2d, 0x74, 0xe8, 0xef, 0xd3, 0x90, 0x35, 0xf1, 0x06, 0x68, 0x64, + 0xd4, 0xd0, 0x13, 0xfd, 0x52, 0xd3, 0xc6, 0x6d, 0xc1, 0x3a, 0x8a, 0x31, + 0xdd, 0x05, 0x26, 0x35, 0x4a, 0x8c, 0x65, 0xb8, 0x52, 0x6b, 0x81, 0xec, + 0xd2, 0x9c, 0xb5, 0x34, 0x10, 0x97, 0x9c, 0x3e, 0xc6, 0x2f, 0xed, 0x8e, + 0x42, 0x42, 0x24, 0x2e, 0xe9, 0x73, 0x9a, 0x25, 0xf9, 0x11, 0xf1, 0xf2, + 0x23, 0x69, 0xcb, 0xe5, 0x94, 0x69, 0xa0, 0xd2, 0xdc, 0xb0, 0xfc, 0x44, + 0x89, 0xac, 0x17, 0xa8, 0xcc, 0xd5, 0x37, 0x77, 0x16, 0xc5, 0x80, 0xb9, + 0x0c, 0x8f, 0x57, 0x02, 0x55, 0x99, 0x85, 0x7b, 0x49, 0xf0, 0x2e, 0x5b, + 0xa0, 0xc2, 0x57, 0x53, 0x5d, 0xa2, 0xe8, 0xa6, 0x37, 0xc3, 0x01, 0xfa, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 28:1c:89:29:66:14:43:80:42:63:55:3a:32:40:ae:b3 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3 + Validity + Not Before: Jun 30 00:00:00 2015 GMT + Not After : Jun 29 23:59:59 2025 GMT + Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c0:9e:3a:0f:9a:b2:ba:d3:d2:dc:15:ec:d0:30: + 54:59:30:4d:40:51:ae:42:71:71:d2:8d:53:73:81: + fe:b8:e0:c4:96:c5:8e:7e:c2:f1:b7:63:4a:cf:a7: + 1e:3f:a8:e7:ce:53:a0:fa:2d:f7:d6:e6:ce:70:11: + a6:ee:e1:03:52:d2:68:de:3d:08:0d:87:fd:1c:d7: + 0b:97:62:6d:82:30:76:1b:47:3a:c4:f7:ce:ed:1d: + 7c:8c:b7:17:8e:53:80:1e:1d:0f:5d:8c:f9:90:e4: + 04:1e:02:7e:cb:b0:49:ef:da:52:25:fb:fb:67:ed: + dd:84:74:59:84:0e:f3:de:70:66:8d:e4:52:38:f7: + 53:5a:37:13:67:0b:3e:bb:a8:58:b7:2e:ed:ff:b7: + 5e:11:73:b9:77:45:52:67:46:ae:c4:dc:24:81:89: + 76:0a:ca:a1:6c:66:73:04:82:aa:f5:70:6c:5f:1b: + 9a:00:79:46:d6:7f:7a:26:17:30:cf:39:4b:2c:74: + d9:89:44:76:10:d0:ed:f7:8b:bb:89:05:75:4d:0b: + 0d:b3:da:e9:bf:f1:6a:7d:2a:11:db:1e:9f:8c:e3: + c4:06:69:e1:1d:88:45:39:d1:6e:55:d8:aa:b7:9b: + 6f:ea:f4:de:ac:17:11:92:5d:40:9b:83:7b:9a:e2: + f7:a9 + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 + CPS: https://www.geotrust.com/resources/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/GeoTrustPCA-G3.crl + + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + F3:B5:56:0C:C4:09:B0:B4:CF:1F:AA:F9:DD:23:56:F0:77:E8:A1:F9 + X509v3 Authority Key Identifier: + keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D + + Signature Algorithm: sha256WithRSAEncryption + c3:7e:d8:83:4b:04:4c:55:29:2a:4f:14:9d:9a:6e:de:90:70: + c1:a4:26:4c:88:8e:78:48:ef:bd:9c:b0:a0:f5:f0:66:fc:fe: + 59:26:e1:79:ef:c8:b7:60:64:a8:8b:47:ea:2f:e0:83:99:da: + 41:19:d7:c5:be:05:fa:f2:90:11:f0:0a:ff:6c:dc:05:b4:d8: + 06:6f:a4:6f:8d:be:20:2b:54:db:f9:a2:45:83:9a:1e:a5:21: + 89:35:1d:7c:20:5c:17:fd:04:2e:45:d8:b2:c6:f8:42:99:fc: + 54:08:4e:4b:80:5f:39:37:ba:95:4e:a6:37:0a:9e:93:5e:87: + 5b:e9:90:d6:a8:b6:65:08:8d:61:49:eb:83:20:a9:5d:1b:16: + 60:62:6b:2f:54:fb:5a:02:0d:7a:27:e2:4b:e1:05:14:c2:e4: + e9:f9:70:c0:d9:f7:34:65:0e:a2:91:4b:ac:28:f2:b7:08:0f: + 98:ca:d7:3e:70:b6:c8:0b:f1:8b:9c:51:f8:c6:10:6c:d2:53: + 4f:62:8c:11:00:3e:88:df:bf:e6:d2:cc:70:bd:ed:25:9c:fb: + dd:24:0a:bd:59:91:4a:42:03:38:12:71:32:88:76:a0:8e:7c: + bb:32:ef:88:2a:1b:d4:6a:6f:50:b9:52:67:8b:ab:30:fa:1f: + fd:e3:24:9a +-----BEGIN CERTIFICATE----- +MIIEpjCCA46gAwIBAgIQKByJKWYUQ4BCY1U6MkCuszANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTE1MDYzMDAwMDAwMFoXDTI1MDYyOTIzNTk1OVowRzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlk +U1NMIFNIQTI1NiBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAwJ46D5qyutPS3BXs0DBUWTBNQFGuQnFx0o1Tc4H+uODElsWOfsLxt2NKz6ce +P6jnzlOg+i331ubOcBGm7uEDUtJo3j0IDYf9HNcLl2JtgjB2G0c6xPfO7R18jLcX +jlOAHh0PXYz5kOQEHgJ+y7BJ79pSJfv7Z+3dhHRZhA7z3nBmjeRSOPdTWjcTZws+ +u6hYty7t/7deEXO5d0VSZ0auxNwkgYl2CsqhbGZzBIKq9XBsXxuaAHlG1n96Jhcw +zzlLLHTZiUR2ENDt94u7iQV1TQsNs9rpv/FqfSoR2x6fjOPEBmnhHYhFOdFuVdiq +t5tv6vTerBcRkl1Am4N7muL3qQIDAQABo4IBOjCCATYwLgYIKwYBBQUHAQEEIjAg +MB4GCCsGAQUFBzABhhJodHRwOi8vZy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB +/wIBADBJBgNVHSAEQjBAMD4GBmeBDAECATA0MDIGCCsGAQUFBwIBFiZodHRwczov +L3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA2BgNVHR8ELzAtMCugKaAn +hiVodHRwOi8vZy5zeW1jYi5jb20vR2VvVHJ1c3RQQ0EtRzMuY3JsMB0GA1UdJQQW +MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPO1VgzECbC0zx+q+d0jVvB36KH5MB8GA1UdIwQYMBaAFMR5yo6hTgMdHNxr2zFb +lD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQDDftiDSwRMVSkqTxSdmm7ekHDBpCZM +iI54SO+9nLCg9fBm/P5ZJuF578i3YGSoi0fqL+CDmdpBGdfFvgX68pAR8Ar/bNwF +tNgGb6Rvjb4gK1Tb+aJFg5oepSGJNR18IFwX/QQuRdiyxvhCmfxUCE5LgF85N7qV +TqY3Cp6TXodb6ZDWqLZlCI1hSeuDIKldGxZgYmsvVPtaAg16J+JL4QUUwuTp+XDA +2fc0ZQ6ikUusKPK3CA+Yytc+cLbIC/GLnFH4xhBs0lNPYowRAD6I37/m0sxwve0l +nPvdJAq9WZFKQgM4EnEyiHagjny7Mu+IKhvUam9QuVJni6sw+h/94ySa +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert24[] = { + 0x30, 0x82, 0x04, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x28, 0x1c, 0x89, 0x29, 0x66, 0x14, 0x43, 0x80, 0x42, + 0x63, 0x55, 0x3a, 0x32, 0x40, 0xae, 0xb3, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65, + 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, + 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, + 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x35, 0x30, 0x36, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x36, 0x32, 0x39, 0x32, 0x33, + 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, + 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, + 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xc0, 0x9e, 0x3a, 0x0f, 0x9a, 0xb2, 0xba, 0xd3, 0xd2, + 0xdc, 0x15, 0xec, 0xd0, 0x30, 0x54, 0x59, 0x30, 0x4d, 0x40, 0x51, 0xae, + 0x42, 0x71, 0x71, 0xd2, 0x8d, 0x53, 0x73, 0x81, 0xfe, 0xb8, 0xe0, 0xc4, + 0x96, 0xc5, 0x8e, 0x7e, 0xc2, 0xf1, 0xb7, 0x63, 0x4a, 0xcf, 0xa7, 0x1e, + 0x3f, 0xa8, 0xe7, 0xce, 0x53, 0xa0, 0xfa, 0x2d, 0xf7, 0xd6, 0xe6, 0xce, + 0x70, 0x11, 0xa6, 0xee, 0xe1, 0x03, 0x52, 0xd2, 0x68, 0xde, 0x3d, 0x08, + 0x0d, 0x87, 0xfd, 0x1c, 0xd7, 0x0b, 0x97, 0x62, 0x6d, 0x82, 0x30, 0x76, + 0x1b, 0x47, 0x3a, 0xc4, 0xf7, 0xce, 0xed, 0x1d, 0x7c, 0x8c, 0xb7, 0x17, + 0x8e, 0x53, 0x80, 0x1e, 0x1d, 0x0f, 0x5d, 0x8c, 0xf9, 0x90, 0xe4, 0x04, + 0x1e, 0x02, 0x7e, 0xcb, 0xb0, 0x49, 0xef, 0xda, 0x52, 0x25, 0xfb, 0xfb, + 0x67, 0xed, 0xdd, 0x84, 0x74, 0x59, 0x84, 0x0e, 0xf3, 0xde, 0x70, 0x66, + 0x8d, 0xe4, 0x52, 0x38, 0xf7, 0x53, 0x5a, 0x37, 0x13, 0x67, 0x0b, 0x3e, + 0xbb, 0xa8, 0x58, 0xb7, 0x2e, 0xed, 0xff, 0xb7, 0x5e, 0x11, 0x73, 0xb9, + 0x77, 0x45, 0x52, 0x67, 0x46, 0xae, 0xc4, 0xdc, 0x24, 0x81, 0x89, 0x76, + 0x0a, 0xca, 0xa1, 0x6c, 0x66, 0x73, 0x04, 0x82, 0xaa, 0xf5, 0x70, 0x6c, + 0x5f, 0x1b, 0x9a, 0x00, 0x79, 0x46, 0xd6, 0x7f, 0x7a, 0x26, 0x17, 0x30, + 0xcf, 0x39, 0x4b, 0x2c, 0x74, 0xd9, 0x89, 0x44, 0x76, 0x10, 0xd0, 0xed, + 0xf7, 0x8b, 0xbb, 0x89, 0x05, 0x75, 0x4d, 0x0b, 0x0d, 0xb3, 0xda, 0xe9, + 0xbf, 0xf1, 0x6a, 0x7d, 0x2a, 0x11, 0xdb, 0x1e, 0x9f, 0x8c, 0xe3, 0xc4, + 0x06, 0x69, 0xe1, 0x1d, 0x88, 0x45, 0x39, 0xd1, 0x6e, 0x55, 0xd8, 0xaa, + 0xb7, 0x9b, 0x6f, 0xea, 0xf4, 0xde, 0xac, 0x17, 0x11, 0x92, 0x5d, 0x40, + 0x9b, 0x83, 0x7b, 0x9a, 0xe2, 0xf7, 0xa9, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x3a, 0x30, 0x82, 0x01, 0x36, 0x30, 0x2e, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, + 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x49, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x42, 0x30, 0x40, 0x30, 0x3e, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, + 0x01, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x36, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, + 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, + 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xf3, 0xb5, 0x56, 0x0c, 0xc4, 0x09, 0xb0, 0xb4, 0xcf, 0x1f, 0xaa, + 0xf9, 0xdd, 0x23, 0x56, 0xf0, 0x77, 0xe8, 0xa1, 0xf9, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc4, 0x79, + 0xca, 0x8e, 0xa1, 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b, 0xdb, 0x31, 0x5b, + 0x94, 0x3e, 0x3f, 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0xc3, 0x7e, 0xd8, 0x83, 0x4b, 0x04, 0x4c, 0x55, 0x29, 0x2a, + 0x4f, 0x14, 0x9d, 0x9a, 0x6e, 0xde, 0x90, 0x70, 0xc1, 0xa4, 0x26, 0x4c, + 0x88, 0x8e, 0x78, 0x48, 0xef, 0xbd, 0x9c, 0xb0, 0xa0, 0xf5, 0xf0, 0x66, + 0xfc, 0xfe, 0x59, 0x26, 0xe1, 0x79, 0xef, 0xc8, 0xb7, 0x60, 0x64, 0xa8, + 0x8b, 0x47, 0xea, 0x2f, 0xe0, 0x83, 0x99, 0xda, 0x41, 0x19, 0xd7, 0xc5, + 0xbe, 0x05, 0xfa, 0xf2, 0x90, 0x11, 0xf0, 0x0a, 0xff, 0x6c, 0xdc, 0x05, + 0xb4, 0xd8, 0x06, 0x6f, 0xa4, 0x6f, 0x8d, 0xbe, 0x20, 0x2b, 0x54, 0xdb, + 0xf9, 0xa2, 0x45, 0x83, 0x9a, 0x1e, 0xa5, 0x21, 0x89, 0x35, 0x1d, 0x7c, + 0x20, 0x5c, 0x17, 0xfd, 0x04, 0x2e, 0x45, 0xd8, 0xb2, 0xc6, 0xf8, 0x42, + 0x99, 0xfc, 0x54, 0x08, 0x4e, 0x4b, 0x80, 0x5f, 0x39, 0x37, 0xba, 0x95, + 0x4e, 0xa6, 0x37, 0x0a, 0x9e, 0x93, 0x5e, 0x87, 0x5b, 0xe9, 0x90, 0xd6, + 0xa8, 0xb6, 0x65, 0x08, 0x8d, 0x61, 0x49, 0xeb, 0x83, 0x20, 0xa9, 0x5d, + 0x1b, 0x16, 0x60, 0x62, 0x6b, 0x2f, 0x54, 0xfb, 0x5a, 0x02, 0x0d, 0x7a, + 0x27, 0xe2, 0x4b, 0xe1, 0x05, 0x14, 0xc2, 0xe4, 0xe9, 0xf9, 0x70, 0xc0, + 0xd9, 0xf7, 0x34, 0x65, 0x0e, 0xa2, 0x91, 0x4b, 0xac, 0x28, 0xf2, 0xb7, + 0x08, 0x0f, 0x98, 0xca, 0xd7, 0x3e, 0x70, 0xb6, 0xc8, 0x0b, 0xf1, 0x8b, + 0x9c, 0x51, 0xf8, 0xc6, 0x10, 0x6c, 0xd2, 0x53, 0x4f, 0x62, 0x8c, 0x11, + 0x00, 0x3e, 0x88, 0xdf, 0xbf, 0xe6, 0xd2, 0xcc, 0x70, 0xbd, 0xed, 0x25, + 0x9c, 0xfb, 0xdd, 0x24, 0x0a, 0xbd, 0x59, 0x91, 0x4a, 0x42, 0x03, 0x38, + 0x12, 0x71, 0x32, 0x88, 0x76, 0xa0, 0x8e, 0x7c, 0xbb, 0x32, 0xef, 0x88, + 0x2a, 0x1b, 0xd4, 0x6a, 0x6f, 0x50, 0xb9, 0x52, 0x67, 0x8b, 0xab, 0x30, + 0xfa, 0x1f, 0xfd, 0xe3, 0x24, 0x9a, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 5d:72:fb:33:76:20:f6:4c:72:80:db:e9:12:81:ff:6a + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=thawte, Inc., CN=thawte EV SSL CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c4:dd:da:94:1e:32:b2:2e:a0:83:c0:a6:7d:5f: + 65:2d:fd:27:b8:73:0e:f8:0b:a9:d4:56:26:69:98: + 67:35:39:64:58:ce:82:6f:98:94:d1:8f:e0:90:d6: + ed:55:4b:98:4b:d7:10:59:34:02:1b:e7:51:31:51: + c4:38:c2:bc:db:03:5c:ca:e1:7c:dc:4f:59:97:ea: + 07:7f:0f:85:3e:92:ea:aa:a7:d9:be:01:41:e4:62: + 56:47:36:bd:57:91:e6:21:d3:f8:41:0b:d8:ba:e8: + ed:81:ad:70:c0:8b:6e:f3:89:6e:27:9e:a6:a6:73: + 59:bb:71:00:d4:4f:4b:48:e9:d5:c9:27:36:9c:7c: + 1c:02:aa:ac:bd:3b:d1:53:83:6a:1f:e6:08:47:33: + a7:b1:9f:02:be:9b:47:ed:33:04:dc:1c:80:27:d1: + 4a:33:a0:8c:eb:01:47:a1:32:90:64:7b:c4:e0:84: + c9:32:e9:dd:34:1f:8a:68:67:f3:ad:10:63:eb:ee: + 8a:9a:b1:2a:1b:26:74:a1:2a:b0:8f:fe:52:98:46: + 97:cf:a3:56:1c:6f:6e:99:97:8d:26:0e:a9:ec:c2: + 53:70:fc:7a:a5:19:49:bd:b5:17:82:55:de:97:e0: + 5d:62:84:81:f0:70:a8:34:53:4f:14:fd:3d:5d:3d: + 6f:b9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://t2.symcb.com + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.thawte.com/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://t1.symcb.com/ThawtePCA.crl + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-536 + X509v3 Subject Key Identifier: + F0:70:51:DA:D3:2A:91:4F:52:77:D7:86:77:74:0F:CE:71:1A:6C:22 + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha256WithRSAEncryption + a1:2e:94:3e:9b:16:f4:58:1a:6f:c1:fa:c1:7e:43:93:b2:c3: + f7:89:eb:13:62:5d:dd:cc:61:13:2b:1d:4e:88:79:11:62:14: + 37:30:46:ff:89:62:10:85:2a:87:1e:f8:e2:af:fe:93:02:93: + ca:f2:e9:46:03:6b:a1:1a:ac:d5:f0:80:1b:98:6f:b8:3a:50: + f8:54:71:06:03:e7:84:cc:8e:61:d2:5f:4d:0c:97:02:65:b5: + 8c:26:bc:05:98:f4:dc:c6:af:e4:57:7f:e3:dc:a1:d7:27:47: + 2a:e0:2c:3f:09:74:dc:5a:e5:b5:7c:fa:82:9a:15:fa:74:2b: + 84:2e:6b:ac:ef:35:a6:30:fa:47:4a:aa:36:44:f6:5a:91:07: + d3:e4:4e:97:3f:a6:53:d8:29:33:32:6f:8b:3d:b5:a5:0d:e5: + e4:8a:e8:f5:c0:fa:af:d8:37:28:27:c3:ed:34:31:d9:7c:a6: + af:4d:12:4f:d0:2b:92:9c:69:95:f2:28:a6:fe:a8:c6:e0:2c: + 4d:36:eb:11:34:d6:e1:81:99:9d:41:f2:e7:c5:57:05:0e:19: + ca:af:42:39:1f:a7:27:5e:e0:0a:17:b8:ae:47:ab:92:f1:8a: + 04:df:30:e0:bb:4f:8a:f9:1b:88:4f:03:b4:25:7a:78:de:2e: + 7d:29:d1:31 +-----BEGIN CERTIFICATE----- +MIIErzCCA5egAwIBAgIQXXL7M3Yg9kxygNvpEoH/ajANBgkqhkiG9w0BAQsFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx +MDMwMjM1OTU5WjBEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu +MR4wHAYDVQQDExV0aGF3dGUgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDE3dqUHjKyLqCDwKZ9X2Ut/Se4cw74C6nUViZpmGc1 +OWRYzoJvmJTRj+CQ1u1VS5hL1xBZNAIb51ExUcQ4wrzbA1zK4XzcT1mX6gd/D4U+ +kuqqp9m+AUHkYlZHNr1XkeYh0/hBC9i66O2BrXDAi27ziW4nnqamc1m7cQDUT0tI +6dXJJzacfBwCqqy9O9FTg2of5ghHM6exnwK+m0ftMwTcHIAn0UozoIzrAUehMpBk +e8TghMky6d00H4poZ/OtEGPr7oqasSobJnShKrCP/lKYRpfPo1Ycb26Zl40mDqns +wlNw/HqlGUm9tReCVd6X4F1ihIHwcKg0U08U/T1dPW+5AgMBAAGjggE1MIIBMTAS +BgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQj +MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly90Mi5zeW1jYi5jb20wOwYDVR0gBDQwMjAw +BgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3Bz +MDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly90MS5zeW1jYi5jb20vVGhhd3RlUENB +LmNybDApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01MzYw +HQYDVR0OBBYEFPBwUdrTKpFPUnfXhnd0D85xGmwiMB8GA1UdIwQYMBaAFHtbRc+v +zst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQChLpQ+mxb0WBpvwfrB +fkOTssP3iesTYl3dzGETKx1OiHkRYhQ3MEb/iWIQhSqHHvjir/6TApPK8ulGA2uh +GqzV8IAbmG+4OlD4VHEGA+eEzI5h0l9NDJcCZbWMJrwFmPTcxq/kV3/j3KHXJ0cq +4Cw/CXTcWuW1fPqCmhX6dCuELmus7zWmMPpHSqo2RPZakQfT5E6XP6ZT2CkzMm+L +PbWlDeXkiuj1wPqv2DcoJ8PtNDHZfKavTRJP0CuSnGmV8iim/qjG4CxNNusRNNbh +gZmdQfLnxVcFDhnKr0I5H6cnXuAKF7iuR6uS8YoE3zDgu0+K+RuITwO0JXp43i59 +KdEx +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert25[] = { + 0x30, 0x82, 0x04, 0xaf, 0x30, 0x82, 0x03, 0x97, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x5d, 0x72, 0xfb, 0x33, 0x76, 0x20, 0xf6, 0x4c, 0x72, + 0x80, 0xdb, 0xe9, 0x12, 0x81, 0xff, 0x6a, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, + 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x44, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c, + 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xc4, 0xdd, 0xda, 0x94, 0x1e, 0x32, 0xb2, + 0x2e, 0xa0, 0x83, 0xc0, 0xa6, 0x7d, 0x5f, 0x65, 0x2d, 0xfd, 0x27, 0xb8, + 0x73, 0x0e, 0xf8, 0x0b, 0xa9, 0xd4, 0x56, 0x26, 0x69, 0x98, 0x67, 0x35, + 0x39, 0x64, 0x58, 0xce, 0x82, 0x6f, 0x98, 0x94, 0xd1, 0x8f, 0xe0, 0x90, + 0xd6, 0xed, 0x55, 0x4b, 0x98, 0x4b, 0xd7, 0x10, 0x59, 0x34, 0x02, 0x1b, + 0xe7, 0x51, 0x31, 0x51, 0xc4, 0x38, 0xc2, 0xbc, 0xdb, 0x03, 0x5c, 0xca, + 0xe1, 0x7c, 0xdc, 0x4f, 0x59, 0x97, 0xea, 0x07, 0x7f, 0x0f, 0x85, 0x3e, + 0x92, 0xea, 0xaa, 0xa7, 0xd9, 0xbe, 0x01, 0x41, 0xe4, 0x62, 0x56, 0x47, + 0x36, 0xbd, 0x57, 0x91, 0xe6, 0x21, 0xd3, 0xf8, 0x41, 0x0b, 0xd8, 0xba, + 0xe8, 0xed, 0x81, 0xad, 0x70, 0xc0, 0x8b, 0x6e, 0xf3, 0x89, 0x6e, 0x27, + 0x9e, 0xa6, 0xa6, 0x73, 0x59, 0xbb, 0x71, 0x00, 0xd4, 0x4f, 0x4b, 0x48, + 0xe9, 0xd5, 0xc9, 0x27, 0x36, 0x9c, 0x7c, 0x1c, 0x02, 0xaa, 0xac, 0xbd, + 0x3b, 0xd1, 0x53, 0x83, 0x6a, 0x1f, 0xe6, 0x08, 0x47, 0x33, 0xa7, 0xb1, + 0x9f, 0x02, 0xbe, 0x9b, 0x47, 0xed, 0x33, 0x04, 0xdc, 0x1c, 0x80, 0x27, + 0xd1, 0x4a, 0x33, 0xa0, 0x8c, 0xeb, 0x01, 0x47, 0xa1, 0x32, 0x90, 0x64, + 0x7b, 0xc4, 0xe0, 0x84, 0xc9, 0x32, 0xe9, 0xdd, 0x34, 0x1f, 0x8a, 0x68, + 0x67, 0xf3, 0xad, 0x10, 0x63, 0xeb, 0xee, 0x8a, 0x9a, 0xb1, 0x2a, 0x1b, + 0x26, 0x74, 0xa1, 0x2a, 0xb0, 0x8f, 0xfe, 0x52, 0x98, 0x46, 0x97, 0xcf, + 0xa3, 0x56, 0x1c, 0x6f, 0x6e, 0x99, 0x97, 0x8d, 0x26, 0x0e, 0xa9, 0xec, + 0xc2, 0x53, 0x70, 0xfc, 0x7a, 0xa5, 0x19, 0x49, 0xbd, 0xb5, 0x17, 0x82, + 0x55, 0xde, 0x97, 0xe0, 0x5d, 0x62, 0x84, 0x81, 0xf0, 0x70, 0xa8, 0x34, + 0x53, 0x4f, 0x14, 0xfd, 0x3d, 0x5d, 0x3d, 0x6f, 0xb9, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x35, 0x30, 0x82, 0x01, 0x31, 0x30, 0x12, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, + 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, + 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, + 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, + 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, + 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, + 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x74, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, + 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, + 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x36, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf0, 0x70, + 0x51, 0xda, 0xd3, 0x2a, 0x91, 0x4f, 0x52, 0x77, 0xd7, 0x86, 0x77, 0x74, + 0x0f, 0xce, 0x71, 0x1a, 0x6c, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, + 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, + 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa1, + 0x2e, 0x94, 0x3e, 0x9b, 0x16, 0xf4, 0x58, 0x1a, 0x6f, 0xc1, 0xfa, 0xc1, + 0x7e, 0x43, 0x93, 0xb2, 0xc3, 0xf7, 0x89, 0xeb, 0x13, 0x62, 0x5d, 0xdd, + 0xcc, 0x61, 0x13, 0x2b, 0x1d, 0x4e, 0x88, 0x79, 0x11, 0x62, 0x14, 0x37, + 0x30, 0x46, 0xff, 0x89, 0x62, 0x10, 0x85, 0x2a, 0x87, 0x1e, 0xf8, 0xe2, + 0xaf, 0xfe, 0x93, 0x02, 0x93, 0xca, 0xf2, 0xe9, 0x46, 0x03, 0x6b, 0xa1, + 0x1a, 0xac, 0xd5, 0xf0, 0x80, 0x1b, 0x98, 0x6f, 0xb8, 0x3a, 0x50, 0xf8, + 0x54, 0x71, 0x06, 0x03, 0xe7, 0x84, 0xcc, 0x8e, 0x61, 0xd2, 0x5f, 0x4d, + 0x0c, 0x97, 0x02, 0x65, 0xb5, 0x8c, 0x26, 0xbc, 0x05, 0x98, 0xf4, 0xdc, + 0xc6, 0xaf, 0xe4, 0x57, 0x7f, 0xe3, 0xdc, 0xa1, 0xd7, 0x27, 0x47, 0x2a, + 0xe0, 0x2c, 0x3f, 0x09, 0x74, 0xdc, 0x5a, 0xe5, 0xb5, 0x7c, 0xfa, 0x82, + 0x9a, 0x15, 0xfa, 0x74, 0x2b, 0x84, 0x2e, 0x6b, 0xac, 0xef, 0x35, 0xa6, + 0x30, 0xfa, 0x47, 0x4a, 0xaa, 0x36, 0x44, 0xf6, 0x5a, 0x91, 0x07, 0xd3, + 0xe4, 0x4e, 0x97, 0x3f, 0xa6, 0x53, 0xd8, 0x29, 0x33, 0x32, 0x6f, 0x8b, + 0x3d, 0xb5, 0xa5, 0x0d, 0xe5, 0xe4, 0x8a, 0xe8, 0xf5, 0xc0, 0xfa, 0xaf, + 0xd8, 0x37, 0x28, 0x27, 0xc3, 0xed, 0x34, 0x31, 0xd9, 0x7c, 0xa6, 0xaf, + 0x4d, 0x12, 0x4f, 0xd0, 0x2b, 0x92, 0x9c, 0x69, 0x95, 0xf2, 0x28, 0xa6, + 0xfe, 0xa8, 0xc6, 0xe0, 0x2c, 0x4d, 0x36, 0xeb, 0x11, 0x34, 0xd6, 0xe1, + 0x81, 0x99, 0x9d, 0x41, 0xf2, 0xe7, 0xc5, 0x57, 0x05, 0x0e, 0x19, 0xca, + 0xaf, 0x42, 0x39, 0x1f, 0xa7, 0x27, 0x5e, 0xe0, 0x0a, 0x17, 0xb8, 0xae, + 0x47, 0xab, 0x92, 0xf1, 0x8a, 0x04, 0xdf, 0x30, 0xe0, 0xbb, 0x4f, 0x8a, + 0xf9, 0x1b, 0x88, 0x4f, 0x03, 0xb4, 0x25, 0x7a, 0x78, 0xde, 0x2e, 0x7d, + 0x29, 0xd1, 0x31, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:e1:e7:a4:dc:5c:f2:f3:6d:c0:2b:42:b8:5d:15:9f + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA + Validity + Not Before: Oct 22 12:00:00 2013 GMT + Not After : Oct 22 12:00:00 2028 GMT + Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b6:e0:2f:c2:24:06:c8:6d:04:5f:d7:ef:0a:64: + 06:b2:7d:22:26:65:16:ae:42:40:9b:ce:dc:9f:9f: + 76:07:3e:c3:30:55:87:19:b9:4f:94:0e:5a:94:1f: + 55:56:b4:c2:02:2a:af:d0:98:ee:0b:40:d7:c4:d0: + 3b:72:c8:14:9e:ef:90:b1:11:a9:ae:d2:c8:b8:43: + 3a:d9:0b:0b:d5:d5:95:f5:40:af:c8:1d:ed:4d:9c: + 5f:57:b7:86:50:68:99:f5:8a:da:d2:c7:05:1f:a8: + 97:c9:dc:a4:b1:82:84:2d:c6:ad:a5:9c:c7:19:82: + a6:85:0f:5e:44:58:2a:37:8f:fd:35:f1:0b:08:27: + 32:5a:f5:bb:8b:9e:a4:bd:51:d0:27:e2:dd:3b:42: + 33:a3:05:28:c4:bb:28:cc:9a:ac:2b:23:0d:78:c6: + 7b:e6:5e:71:b7:4a:3e:08:fb:81:b7:16:16:a1:9d: + 23:12:4d:e5:d7:92:08:ac:75:a4:9c:ba:cd:17:b2: + 1e:44:35:65:7f:53:25:39:d1:1c:0a:9a:63:1b:19: + 92:74:68:0a:37:c2:c2:52:48:cb:39:5a:a2:b6:e1: + 5d:c1:dd:a0:20:b8:21:a2:93:26:6f:14:4a:21:41: + c7:ed:6d:9b:f2:48:2f:f3:03:f5:a2:68:92:53:2f: + 5e:e3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.digicert.com/CPS + + X509v3 Subject Key Identifier: + 51:68:FF:90:AF:02:07:75:3C:CC:D9:65:64:62:A2:12:B8:59:72:3B + X509v3 Authority Key Identifier: + keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 + + Signature Algorithm: sha256WithRSAEncryption + 18:8a:95:89:03:e6:6d:df:5c:fc:1d:68:ea:4a:8f:83:d6:51: + 2f:8d:6b:44:16:9e:ac:63:f5:d2:6e:6c:84:99:8b:aa:81:71: + 84:5b:ed:34:4e:b0:b7:79:92:29:cc:2d:80:6a:f0:8e:20:e1: + 79:a4:fe:03:47:13:ea:f5:86:ca:59:71:7d:f4:04:96:6b:d3: + 59:58:3d:fe:d3:31:25:5c:18:38:84:a3:e6:9f:82:fd:8c:5b: + 98:31:4e:cd:78:9e:1a:fd:85:cb:49:aa:f2:27:8b:99:72:fc: + 3e:aa:d5:41:0b:da:d5:36:a1:bf:1c:6e:47:49:7f:5e:d9:48: + 7c:03:d9:fd:8b:49:a0:98:26:42:40:eb:d6:92:11:a4:64:0a: + 57:54:c4:f5:1d:d6:02:5e:6b:ac:ee:c4:80:9a:12:72:fa:56: + 93:d7:ff:bf:30:85:06:30:bf:0b:7f:4e:ff:57:05:9d:24:ed: + 85:c3:2b:fb:a6:75:a8:ac:2d:16:ef:7d:79:27:b2:eb:c2:9d: + 0b:07:ea:aa:85:d3:01:a3:20:28:41:59:43:28:d2:81:e3:aa: + f6:ec:7b:3b:77:b6:40:62:80:05:41:45:01:ef:17:06:3e:de: + c0:33:9b:67:d3:61:2e:72:87:e4:69:fc:12:00:57:40:1e:70: + f5:1e:c9:b4 +-----BEGIN CERTIFICATE----- +MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy +YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2 +4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC +Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1 +itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn +4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X +sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft +bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy +dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t +L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG +BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ +UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D +aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd +aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH +E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly +/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu +xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF +0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae +cPUeybQ= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert26[] = { + 0x30, 0x82, 0x04, 0xb1, 0x30, 0x82, 0x03, 0x99, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x04, 0xe1, 0xe7, 0xa4, 0xdc, 0x5c, 0xf2, 0xf3, 0x6d, + 0xc0, 0x2b, 0x42, 0xb8, 0x5d, 0x15, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32, + 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x70, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, + 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, + 0x32, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, + 0x61, 0x6e, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6, + 0xe0, 0x2f, 0xc2, 0x24, 0x06, 0xc8, 0x6d, 0x04, 0x5f, 0xd7, 0xef, 0x0a, + 0x64, 0x06, 0xb2, 0x7d, 0x22, 0x26, 0x65, 0x16, 0xae, 0x42, 0x40, 0x9b, + 0xce, 0xdc, 0x9f, 0x9f, 0x76, 0x07, 0x3e, 0xc3, 0x30, 0x55, 0x87, 0x19, + 0xb9, 0x4f, 0x94, 0x0e, 0x5a, 0x94, 0x1f, 0x55, 0x56, 0xb4, 0xc2, 0x02, + 0x2a, 0xaf, 0xd0, 0x98, 0xee, 0x0b, 0x40, 0xd7, 0xc4, 0xd0, 0x3b, 0x72, + 0xc8, 0x14, 0x9e, 0xef, 0x90, 0xb1, 0x11, 0xa9, 0xae, 0xd2, 0xc8, 0xb8, + 0x43, 0x3a, 0xd9, 0x0b, 0x0b, 0xd5, 0xd5, 0x95, 0xf5, 0x40, 0xaf, 0xc8, + 0x1d, 0xed, 0x4d, 0x9c, 0x5f, 0x57, 0xb7, 0x86, 0x50, 0x68, 0x99, 0xf5, + 0x8a, 0xda, 0xd2, 0xc7, 0x05, 0x1f, 0xa8, 0x97, 0xc9, 0xdc, 0xa4, 0xb1, + 0x82, 0x84, 0x2d, 0xc6, 0xad, 0xa5, 0x9c, 0xc7, 0x19, 0x82, 0xa6, 0x85, + 0x0f, 0x5e, 0x44, 0x58, 0x2a, 0x37, 0x8f, 0xfd, 0x35, 0xf1, 0x0b, 0x08, + 0x27, 0x32, 0x5a, 0xf5, 0xbb, 0x8b, 0x9e, 0xa4, 0xbd, 0x51, 0xd0, 0x27, + 0xe2, 0xdd, 0x3b, 0x42, 0x33, 0xa3, 0x05, 0x28, 0xc4, 0xbb, 0x28, 0xcc, + 0x9a, 0xac, 0x2b, 0x23, 0x0d, 0x78, 0xc6, 0x7b, 0xe6, 0x5e, 0x71, 0xb7, + 0x4a, 0x3e, 0x08, 0xfb, 0x81, 0xb7, 0x16, 0x16, 0xa1, 0x9d, 0x23, 0x12, + 0x4d, 0xe5, 0xd7, 0x92, 0x08, 0xac, 0x75, 0xa4, 0x9c, 0xba, 0xcd, 0x17, + 0xb2, 0x1e, 0x44, 0x35, 0x65, 0x7f, 0x53, 0x25, 0x39, 0xd1, 0x1c, 0x0a, + 0x9a, 0x63, 0x1b, 0x19, 0x92, 0x74, 0x68, 0x0a, 0x37, 0xc2, 0xc2, 0x52, + 0x48, 0xcb, 0x39, 0x5a, 0xa2, 0xb6, 0xe1, 0x5d, 0xc1, 0xdd, 0xa0, 0x20, + 0xb8, 0x21, 0xa2, 0x93, 0x26, 0x6f, 0x14, 0x4a, 0x21, 0x41, 0xc7, 0xed, + 0x6d, 0x9b, 0xf2, 0x48, 0x2f, 0xf3, 0x03, 0xf5, 0xa2, 0x68, 0x92, 0x53, + 0x2f, 0x5e, 0xe3, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x49, + 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, + 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, + 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, + 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, + 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, + 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x51, 0x68, 0xff, 0x90, 0xaf, 0x02, 0x07, 0x75, 0x3c, 0xcc, 0xd9, 0x65, + 0x64, 0x62, 0xa2, 0x12, 0xb8, 0x59, 0x72, 0x3b, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3, + 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02, + 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0x18, 0x8a, 0x95, 0x89, 0x03, 0xe6, 0x6d, 0xdf, 0x5c, 0xfc, 0x1d, + 0x68, 0xea, 0x4a, 0x8f, 0x83, 0xd6, 0x51, 0x2f, 0x8d, 0x6b, 0x44, 0x16, + 0x9e, 0xac, 0x63, 0xf5, 0xd2, 0x6e, 0x6c, 0x84, 0x99, 0x8b, 0xaa, 0x81, + 0x71, 0x84, 0x5b, 0xed, 0x34, 0x4e, 0xb0, 0xb7, 0x79, 0x92, 0x29, 0xcc, + 0x2d, 0x80, 0x6a, 0xf0, 0x8e, 0x20, 0xe1, 0x79, 0xa4, 0xfe, 0x03, 0x47, + 0x13, 0xea, 0xf5, 0x86, 0xca, 0x59, 0x71, 0x7d, 0xf4, 0x04, 0x96, 0x6b, + 0xd3, 0x59, 0x58, 0x3d, 0xfe, 0xd3, 0x31, 0x25, 0x5c, 0x18, 0x38, 0x84, + 0xa3, 0xe6, 0x9f, 0x82, 0xfd, 0x8c, 0x5b, 0x98, 0x31, 0x4e, 0xcd, 0x78, + 0x9e, 0x1a, 0xfd, 0x85, 0xcb, 0x49, 0xaa, 0xf2, 0x27, 0x8b, 0x99, 0x72, + 0xfc, 0x3e, 0xaa, 0xd5, 0x41, 0x0b, 0xda, 0xd5, 0x36, 0xa1, 0xbf, 0x1c, + 0x6e, 0x47, 0x49, 0x7f, 0x5e, 0xd9, 0x48, 0x7c, 0x03, 0xd9, 0xfd, 0x8b, + 0x49, 0xa0, 0x98, 0x26, 0x42, 0x40, 0xeb, 0xd6, 0x92, 0x11, 0xa4, 0x64, + 0x0a, 0x57, 0x54, 0xc4, 0xf5, 0x1d, 0xd6, 0x02, 0x5e, 0x6b, 0xac, 0xee, + 0xc4, 0x80, 0x9a, 0x12, 0x72, 0xfa, 0x56, 0x93, 0xd7, 0xff, 0xbf, 0x30, + 0x85, 0x06, 0x30, 0xbf, 0x0b, 0x7f, 0x4e, 0xff, 0x57, 0x05, 0x9d, 0x24, + 0xed, 0x85, 0xc3, 0x2b, 0xfb, 0xa6, 0x75, 0xa8, 0xac, 0x2d, 0x16, 0xef, + 0x7d, 0x79, 0x27, 0xb2, 0xeb, 0xc2, 0x9d, 0x0b, 0x07, 0xea, 0xaa, 0x85, + 0xd3, 0x01, 0xa3, 0x20, 0x28, 0x41, 0x59, 0x43, 0x28, 0xd2, 0x81, 0xe3, + 0xaa, 0xf6, 0xec, 0x7b, 0x3b, 0x77, 0xb6, 0x40, 0x62, 0x80, 0x05, 0x41, + 0x45, 0x01, 0xef, 0x17, 0x06, 0x3e, 0xde, 0xc0, 0x33, 0x9b, 0x67, 0xd3, + 0x61, 0x2e, 0x72, 0x87, 0xe4, 0x69, 0xfc, 0x12, 0x00, 0x57, 0x40, 0x1e, + 0x70, 0xf5, 0x1e, 0xc9, 0xb4, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 16:87:d6:88:6d:e2:30:06:85:23:3d:bf:11:bf:65:97 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=thawte, Inc., CN=thawte SSL CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b2:fc:06:fb:04:93:d2:ea:59:20:3b:44:85:97: + 52:39:e7:10:f0:7a:e0:b0:94:40:da:46:f8:0c:28: + bb:b9:ce:60:38:3f:d2:d8:11:42:1b:91:ad:49:ee: + 8f:c7:de:6c:de:37:6f:fd:8b:20:3c:6d:e7:74:d3: + dc:d5:24:88:41:80:89:ee:36:be:c4:d5:be:8d:53: + 13:aa:e4:a5:b8:93:0a:be:ec:da:cd:3c:d4:32:56: + ef:d0:4e:a0:b8:97:bb:39:50:1e:6e:65:c3:fd:b2: + ce:e0:59:a9:48:09:c6:fe:be:ae:fc:3e:3b:81:20: + 97:8b:8f:46:df:60:64:07:75:bb:1b:86:38:9f:47: + 7b:34:ce:a1:d1:97:ad:76:d8:9f:b7:26:db:79:80: + 36:48:f2:c5:37:f8:d9:32:ae:7c:a4:53:81:c7:99: + a1:54:38:2f:4f:75:a0:bb:5a:a5:bb:cd:ac:02:5b: + 19:02:d5:13:18:a7:ce:ac:74:55:12:05:8b:9b:a2: + 95:46:64:72:38:cd:5a:1b:3a:16:a7:be:71:99:8c: + 54:03:b8:96:6c:01:d3:3e:06:98:3f:21:81:3b:02: + 7e:00:47:53:01:1e:0e:46:43:fb:4b:2d:dc:0b:1a: + e8:2f:98:f8:7e:d1:99:ab:13:6c:a4:17:de:6f:f6: + 15:f5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://t1.symcb.com/ThawtePCA.crl + + Authority Information Access: + OCSP - URI:http://t2.symcb.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: https://www.thawte.com/cps + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-537 + X509v3 Subject Key Identifier: + C2:4F:48:57:FC:D1:4F:9A:C0:5D:38:7D:0E:05:DB:D9:2E:B5:52:60 + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha256WithRSAEncryption + 8d:06:de:43:c9:76:02:ca:d9:23:97:5e:f3:63:d7:7d:44:c2: + 0f:6b:0a:f5:07:e5:8b:b8:fa:e0:a3:fa:6b:80:92:b5:03:2c: + c5:37:e0:c2:e5:95:b5:92:70:18:28:42:94:ee:4b:77:6a:01: + 0f:8b:23:ec:56:4d:f4:00:69:e5:84:c8:e2:ea:de:5b:3e:f6: + 3c:07:3a:94:ca:6c:27:b1:cc:83:1a:60:71:27:d2:bf:02:f5: + 1e:44:d3:48:d5:a6:d3:76:21:00:9c:fa:98:64:eb:17:36:3f: + eb:1b:3c:3e:a6:b1:d9:58:06:0e:72:d9:68:be:f1:a7:20:d7: + 52:e4:a4:77:1f:71:70:9d:55:35:85:37:e1:1d:4d:94:c2:70: + 7f:95:40:6e:4b:7d:b2:b4:29:2a:03:79:c8:b9:4c:67:61:04: + a0:8b:27:ff:59:00:eb:55:7f:c6:b7:33:35:2d:5e:4e:ac:b8: + ea:12:c5:e8:f7:b9:ab:be:74:92:2c:b7:d9:4d:ca:84:2f:1c: + c2:f0:72:7c:b2:31:6e:cf:80:e5:88:07:36:51:7b:ba:61:af: + 6d:8d:23:5b:34:a3:95:bc:a2:31:7f:f2:f5:e7:b7:e8:ef:c4: + b5:27:32:e9:f7:9e:69:c7:2b:e8:be:bb:0c:aa:e7:ea:60:12: + ea:26:8a:78 +-----BEGIN CERTIFICATE----- +MIIEsjCCA5qgAwIBAgIQFofWiG3iMAaFIz2/Eb9llzANBgkqhkiG9w0BAQsFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx +MDMwMjM1OTU5WjBBMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu +MRswGQYDVQQDExJ0aGF3dGUgU1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCy/Ab7BJPS6lkgO0SFl1I55xDweuCwlEDaRvgMKLu5zmA4 +P9LYEUIbka1J7o/H3mzeN2/9iyA8bed009zVJIhBgInuNr7E1b6NUxOq5KW4kwq+ +7NrNPNQyVu/QTqC4l7s5UB5uZcP9ss7gWalICcb+vq78PjuBIJeLj0bfYGQHdbsb +hjifR3s0zqHRl6122J+3Jtt5gDZI8sU3+NkyrnykU4HHmaFUOC9PdaC7WqW7zawC +WxkC1RMYp86sdFUSBYubopVGZHI4zVobOhanvnGZjFQDuJZsAdM+Bpg/IYE7An4A +R1MBHg5GQ/tLLdwLGugvmPh+0ZmrE2ykF95v9hX1AgMBAAGjggE7MIIBNzASBgNV +HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAyBgNVHR8EKzApMCegJaAj +hiFodHRwOi8vdDEuc3ltY2IuY29tL1RoYXd0ZVBDQS5jcmwwLwYIKwYBBQUHAQEE +IzAhMB8GCCsGAQUFBzABhhNodHRwOi8vdDIuc3ltY2IuY29tMEEGA1UdIAQ6MDgw +NgYKYIZIAYb4RQEHNjAoMCYGCCsGAQUFBwIBFhpodHRwczovL3d3dy50aGF3dGUu +Y29tL2NwczApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01 +MzcwHQYDVR0OBBYEFMJPSFf80U+awF04fQ4F29kutVJgMB8GA1UdIwQYMBaAFHtb +Rc+vzst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQCNBt5DyXYCytkj +l17zY9d9RMIPawr1B+WLuPrgo/prgJK1AyzFN+DC5ZW1knAYKEKU7kt3agEPiyPs +Vk30AGnlhMji6t5bPvY8BzqUymwnscyDGmBxJ9K/AvUeRNNI1abTdiEAnPqYZOsX +Nj/rGzw+prHZWAYOctlovvGnINdS5KR3H3FwnVU1hTfhHU2UwnB/lUBuS32ytCkq +A3nIuUxnYQSgiyf/WQDrVX/GtzM1LV5OrLjqEsXo97mrvnSSLLfZTcqELxzC8HJ8 +sjFuz4DliAc2UXu6Ya9tjSNbNKOVvKIxf/L157fo78S1JzLp955pxyvovrsMqufq +YBLqJop4 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert27[] = { + 0x30, 0x82, 0x04, 0xb2, 0x30, 0x82, 0x03, 0x9a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x16, 0x87, 0xd6, 0x88, 0x6d, 0xe2, 0x30, 0x06, 0x85, + 0x23, 0x3d, 0xbf, 0x11, 0xbf, 0x65, 0x97, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, + 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x41, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x74, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xb2, 0xfc, 0x06, 0xfb, 0x04, 0x93, 0xd2, 0xea, 0x59, 0x20, + 0x3b, 0x44, 0x85, 0x97, 0x52, 0x39, 0xe7, 0x10, 0xf0, 0x7a, 0xe0, 0xb0, + 0x94, 0x40, 0xda, 0x46, 0xf8, 0x0c, 0x28, 0xbb, 0xb9, 0xce, 0x60, 0x38, + 0x3f, 0xd2, 0xd8, 0x11, 0x42, 0x1b, 0x91, 0xad, 0x49, 0xee, 0x8f, 0xc7, + 0xde, 0x6c, 0xde, 0x37, 0x6f, 0xfd, 0x8b, 0x20, 0x3c, 0x6d, 0xe7, 0x74, + 0xd3, 0xdc, 0xd5, 0x24, 0x88, 0x41, 0x80, 0x89, 0xee, 0x36, 0xbe, 0xc4, + 0xd5, 0xbe, 0x8d, 0x53, 0x13, 0xaa, 0xe4, 0xa5, 0xb8, 0x93, 0x0a, 0xbe, + 0xec, 0xda, 0xcd, 0x3c, 0xd4, 0x32, 0x56, 0xef, 0xd0, 0x4e, 0xa0, 0xb8, + 0x97, 0xbb, 0x39, 0x50, 0x1e, 0x6e, 0x65, 0xc3, 0xfd, 0xb2, 0xce, 0xe0, + 0x59, 0xa9, 0x48, 0x09, 0xc6, 0xfe, 0xbe, 0xae, 0xfc, 0x3e, 0x3b, 0x81, + 0x20, 0x97, 0x8b, 0x8f, 0x46, 0xdf, 0x60, 0x64, 0x07, 0x75, 0xbb, 0x1b, + 0x86, 0x38, 0x9f, 0x47, 0x7b, 0x34, 0xce, 0xa1, 0xd1, 0x97, 0xad, 0x76, + 0xd8, 0x9f, 0xb7, 0x26, 0xdb, 0x79, 0x80, 0x36, 0x48, 0xf2, 0xc5, 0x37, + 0xf8, 0xd9, 0x32, 0xae, 0x7c, 0xa4, 0x53, 0x81, 0xc7, 0x99, 0xa1, 0x54, + 0x38, 0x2f, 0x4f, 0x75, 0xa0, 0xbb, 0x5a, 0xa5, 0xbb, 0xcd, 0xac, 0x02, + 0x5b, 0x19, 0x02, 0xd5, 0x13, 0x18, 0xa7, 0xce, 0xac, 0x74, 0x55, 0x12, + 0x05, 0x8b, 0x9b, 0xa2, 0x95, 0x46, 0x64, 0x72, 0x38, 0xcd, 0x5a, 0x1b, + 0x3a, 0x16, 0xa7, 0xbe, 0x71, 0x99, 0x8c, 0x54, 0x03, 0xb8, 0x96, 0x6c, + 0x01, 0xd3, 0x3e, 0x06, 0x98, 0x3f, 0x21, 0x81, 0x3b, 0x02, 0x7e, 0x00, + 0x47, 0x53, 0x01, 0x1e, 0x0e, 0x46, 0x43, 0xfb, 0x4b, 0x2d, 0xdc, 0x0b, + 0x1a, 0xe8, 0x2f, 0x98, 0xf8, 0x7e, 0xd1, 0x99, 0xab, 0x13, 0x6c, 0xa4, + 0x17, 0xde, 0x6f, 0xf6, 0x15, 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x3b, 0x30, 0x82, 0x01, 0x37, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, + 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x32, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, + 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, 0x31, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, + 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x74, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38, 0x30, + 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, + 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55, + 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, + 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, + 0x33, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xc2, 0x4f, 0x48, 0x57, 0xfc, 0xd1, 0x4f, 0x9a, 0xc0, 0x5d, 0x38, + 0x7d, 0x0e, 0x05, 0xdb, 0xd9, 0x2e, 0xb5, 0x52, 0x60, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, + 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, + 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x8d, 0x06, 0xde, 0x43, 0xc9, 0x76, 0x02, 0xca, 0xd9, 0x23, + 0x97, 0x5e, 0xf3, 0x63, 0xd7, 0x7d, 0x44, 0xc2, 0x0f, 0x6b, 0x0a, 0xf5, + 0x07, 0xe5, 0x8b, 0xb8, 0xfa, 0xe0, 0xa3, 0xfa, 0x6b, 0x80, 0x92, 0xb5, + 0x03, 0x2c, 0xc5, 0x37, 0xe0, 0xc2, 0xe5, 0x95, 0xb5, 0x92, 0x70, 0x18, + 0x28, 0x42, 0x94, 0xee, 0x4b, 0x77, 0x6a, 0x01, 0x0f, 0x8b, 0x23, 0xec, + 0x56, 0x4d, 0xf4, 0x00, 0x69, 0xe5, 0x84, 0xc8, 0xe2, 0xea, 0xde, 0x5b, + 0x3e, 0xf6, 0x3c, 0x07, 0x3a, 0x94, 0xca, 0x6c, 0x27, 0xb1, 0xcc, 0x83, + 0x1a, 0x60, 0x71, 0x27, 0xd2, 0xbf, 0x02, 0xf5, 0x1e, 0x44, 0xd3, 0x48, + 0xd5, 0xa6, 0xd3, 0x76, 0x21, 0x00, 0x9c, 0xfa, 0x98, 0x64, 0xeb, 0x17, + 0x36, 0x3f, 0xeb, 0x1b, 0x3c, 0x3e, 0xa6, 0xb1, 0xd9, 0x58, 0x06, 0x0e, + 0x72, 0xd9, 0x68, 0xbe, 0xf1, 0xa7, 0x20, 0xd7, 0x52, 0xe4, 0xa4, 0x77, + 0x1f, 0x71, 0x70, 0x9d, 0x55, 0x35, 0x85, 0x37, 0xe1, 0x1d, 0x4d, 0x94, + 0xc2, 0x70, 0x7f, 0x95, 0x40, 0x6e, 0x4b, 0x7d, 0xb2, 0xb4, 0x29, 0x2a, + 0x03, 0x79, 0xc8, 0xb9, 0x4c, 0x67, 0x61, 0x04, 0xa0, 0x8b, 0x27, 0xff, + 0x59, 0x00, 0xeb, 0x55, 0x7f, 0xc6, 0xb7, 0x33, 0x35, 0x2d, 0x5e, 0x4e, + 0xac, 0xb8, 0xea, 0x12, 0xc5, 0xe8, 0xf7, 0xb9, 0xab, 0xbe, 0x74, 0x92, + 0x2c, 0xb7, 0xd9, 0x4d, 0xca, 0x84, 0x2f, 0x1c, 0xc2, 0xf0, 0x72, 0x7c, + 0xb2, 0x31, 0x6e, 0xcf, 0x80, 0xe5, 0x88, 0x07, 0x36, 0x51, 0x7b, 0xba, + 0x61, 0xaf, 0x6d, 0x8d, 0x23, 0x5b, 0x34, 0xa3, 0x95, 0xbc, 0xa2, 0x31, + 0x7f, 0xf2, 0xf5, 0xe7, 0xb7, 0xe8, 0xef, 0xc4, 0xb5, 0x27, 0x32, 0xe9, + 0xf7, 0x9e, 0x69, 0xc7, 0x2b, 0xe8, 0xbe, 0xbb, 0x0c, 0xaa, 0xe7, 0xea, + 0x60, 0x12, 0xea, 0x26, 0x8a, 0x78, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 0c:79:a9:44:b0:8c:11:95:20:92:61:5f:e2:6b:1d:83 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA + Validity + Not Before: Oct 22 12:00:00 2013 GMT + Not After : Oct 22 12:00:00 2028 GMT + Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d7:53:a4:04:51:f8:99:a6:16:48:4b:67:27:aa: + 93:49:d0:39:ed:0c:b0:b0:00:87:f1:67:28:86:85: + 8c:8e:63:da:bc:b1:40:38:e2:d3:f5:ec:a5:05:18: + b8:3d:3e:c5:99:17:32:ec:18:8c:fa:f1:0c:a6:64: + 21:85:cb:07:10:34:b0:52:88:2b:1f:68:9b:d2:b1: + 8f:12:b0:b3:d2:e7:88:1f:1f:ef:38:77:54:53:5f: + 80:79:3f:2e:1a:aa:a8:1e:4b:2b:0d:ab:b7:63:b9: + 35:b7:7d:14:bc:59:4b:df:51:4a:d2:a1:e2:0c:e2: + 90:82:87:6a:ae:ea:d7:64:d6:98:55:e8:fd:af:1a: + 50:6c:54:bc:11:f2:fd:4a:f2:9d:bb:7f:0e:f4:d5: + be:8e:16:89:12:55:d8:c0:71:34:ee:f6:dc:2d:ec: + c4:87:25:86:8d:d8:21:e4:b0:4d:0c:89:dc:39:26: + 17:dd:f6:d7:94:85:d8:04:21:70:9d:6f:6f:ff:5c: + ba:19:e1:45:cb:56:57:28:7e:1c:0d:41:57:aa:b7: + b8:27:bb:b1:e4:fa:2a:ef:21:23:75:1a:ad:2d:9b: + 86:35:8c:9c:77:b5:73:ad:d8:94:2d:e4:f3:0c:9d: + ee:c1:4e:62:7e:17:c0:71:9e:2c:de:f1:f9:10:28: + 19:33 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.digicert.com/CPS + + X509v3 Subject Key Identifier: + 3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F + X509v3 Authority Key Identifier: + keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 + + Signature Algorithm: sha256WithRSAEncryption + 9d:b6:d0:90:86:e1:86:02:ed:c5:a0:f0:34:1c:74:c1:8d:76: + cc:86:0a:a8:f0:4a:8a:42:d6:3f:c8:a9:4d:ad:7c:08:ad:e6: + b6:50:b8:a2:1a:4d:88:07:b1:29:21:dc:e7:da:c6:3c:21:e0: + e3:11:49:70:ac:7a:1d:01:a4:ca:11:3a:57:ab:7d:57:2a:40: + 74:fd:d3:1d:85:18:50:df:57:47:75:a1:7d:55:20:2e:47:37: + 50:72:8c:7f:82:1b:d2:62:8f:2d:03:5a:da:c3:c8:a1:ce:2c: + 52:a2:00:63:eb:73:ba:71:c8:49:27:23:97:64:85:9e:38:0e: + ad:63:68:3c:ba:52:81:58:79:a3:2c:0c:df:de:6d:eb:31:f2: + ba:a0:7c:6c:f1:2c:d4:e1:bd:77:84:37:03:ce:32:b5:c8:9a: + 81:1a:4a:92:4e:3b:46:9a:85:fe:83:a2:f9:9e:8c:a3:cc:0d: + 5e:b3:3d:cf:04:78:8f:14:14:7b:32:9c:c7:00:a6:5c:c4:b5: + a1:55:8d:5a:56:68:a4:22:70:aa:3c:81:71:d9:9d:a8:45:3b: + f4:e5:f6:a2:51:dd:c7:7b:62:e8:6f:0c:74:eb:b8:da:f8:bf: + 87:0d:79:50:91:90:9b:18:3b:91:59:27:f1:35:28:13:ab:26: + 7e:d5:f7:7a +-----BEGIN CERTIFICATE----- +MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW +YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY +uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/ +LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy +/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh +cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k +8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB +Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp +Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy +dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2 +MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j +b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW +gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh +hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg +4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa +2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs +1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1 +oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn +8TUoE6smftX3eg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert28[] = { + 0x30, 0x82, 0x04, 0xb6, 0x30, 0x82, 0x03, 0x9e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x0c, 0x79, 0xa9, 0x44, 0xb0, 0x8c, 0x11, 0x95, 0x20, + 0x92, 0x61, 0x5f, 0xe2, 0x6b, 0x1d, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32, + 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x75, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, + 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, + 0x32, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xd7, 0x53, 0xa4, 0x04, 0x51, 0xf8, 0x99, 0xa6, + 0x16, 0x48, 0x4b, 0x67, 0x27, 0xaa, 0x93, 0x49, 0xd0, 0x39, 0xed, 0x0c, + 0xb0, 0xb0, 0x00, 0x87, 0xf1, 0x67, 0x28, 0x86, 0x85, 0x8c, 0x8e, 0x63, + 0xda, 0xbc, 0xb1, 0x40, 0x38, 0xe2, 0xd3, 0xf5, 0xec, 0xa5, 0x05, 0x18, + 0xb8, 0x3d, 0x3e, 0xc5, 0x99, 0x17, 0x32, 0xec, 0x18, 0x8c, 0xfa, 0xf1, + 0x0c, 0xa6, 0x64, 0x21, 0x85, 0xcb, 0x07, 0x10, 0x34, 0xb0, 0x52, 0x88, + 0x2b, 0x1f, 0x68, 0x9b, 0xd2, 0xb1, 0x8f, 0x12, 0xb0, 0xb3, 0xd2, 0xe7, + 0x88, 0x1f, 0x1f, 0xef, 0x38, 0x77, 0x54, 0x53, 0x5f, 0x80, 0x79, 0x3f, + 0x2e, 0x1a, 0xaa, 0xa8, 0x1e, 0x4b, 0x2b, 0x0d, 0xab, 0xb7, 0x63, 0xb9, + 0x35, 0xb7, 0x7d, 0x14, 0xbc, 0x59, 0x4b, 0xdf, 0x51, 0x4a, 0xd2, 0xa1, + 0xe2, 0x0c, 0xe2, 0x90, 0x82, 0x87, 0x6a, 0xae, 0xea, 0xd7, 0x64, 0xd6, + 0x98, 0x55, 0xe8, 0xfd, 0xaf, 0x1a, 0x50, 0x6c, 0x54, 0xbc, 0x11, 0xf2, + 0xfd, 0x4a, 0xf2, 0x9d, 0xbb, 0x7f, 0x0e, 0xf4, 0xd5, 0xbe, 0x8e, 0x16, + 0x89, 0x12, 0x55, 0xd8, 0xc0, 0x71, 0x34, 0xee, 0xf6, 0xdc, 0x2d, 0xec, + 0xc4, 0x87, 0x25, 0x86, 0x8d, 0xd8, 0x21, 0xe4, 0xb0, 0x4d, 0x0c, 0x89, + 0xdc, 0x39, 0x26, 0x17, 0xdd, 0xf6, 0xd7, 0x94, 0x85, 0xd8, 0x04, 0x21, + 0x70, 0x9d, 0x6f, 0x6f, 0xff, 0x5c, 0xba, 0x19, 0xe1, 0x45, 0xcb, 0x56, + 0x57, 0x28, 0x7e, 0x1c, 0x0d, 0x41, 0x57, 0xaa, 0xb7, 0xb8, 0x27, 0xbb, + 0xb1, 0xe4, 0xfa, 0x2a, 0xef, 0x21, 0x23, 0x75, 0x1a, 0xad, 0x2d, 0x9b, + 0x86, 0x35, 0x8c, 0x9c, 0x77, 0xb5, 0x73, 0xad, 0xd8, 0x94, 0x2d, 0xe4, + 0xf3, 0x0c, 0x9d, 0xee, 0xc1, 0x4e, 0x62, 0x7e, 0x17, 0xc0, 0x71, 0x9e, + 0x2c, 0xde, 0xf1, 0xf9, 0x10, 0x28, 0x19, 0x33, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x01, 0x49, 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, + 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, + 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x02, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, + 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0, + 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, + 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, + 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, + 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, + 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, + 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3d, 0xd3, 0x50, 0xa5, 0xd6, 0xa0, 0xad, + 0xee, 0xf3, 0x4a, 0x60, 0x0a, 0x65, 0xd3, 0x21, 0xd4, 0xf8, 0xf8, 0xd6, + 0x0f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, + 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9d, 0xb6, 0xd0, 0x90, 0x86, 0xe1, + 0x86, 0x02, 0xed, 0xc5, 0xa0, 0xf0, 0x34, 0x1c, 0x74, 0xc1, 0x8d, 0x76, + 0xcc, 0x86, 0x0a, 0xa8, 0xf0, 0x4a, 0x8a, 0x42, 0xd6, 0x3f, 0xc8, 0xa9, + 0x4d, 0xad, 0x7c, 0x08, 0xad, 0xe6, 0xb6, 0x50, 0xb8, 0xa2, 0x1a, 0x4d, + 0x88, 0x07, 0xb1, 0x29, 0x21, 0xdc, 0xe7, 0xda, 0xc6, 0x3c, 0x21, 0xe0, + 0xe3, 0x11, 0x49, 0x70, 0xac, 0x7a, 0x1d, 0x01, 0xa4, 0xca, 0x11, 0x3a, + 0x57, 0xab, 0x7d, 0x57, 0x2a, 0x40, 0x74, 0xfd, 0xd3, 0x1d, 0x85, 0x18, + 0x50, 0xdf, 0x57, 0x47, 0x75, 0xa1, 0x7d, 0x55, 0x20, 0x2e, 0x47, 0x37, + 0x50, 0x72, 0x8c, 0x7f, 0x82, 0x1b, 0xd2, 0x62, 0x8f, 0x2d, 0x03, 0x5a, + 0xda, 0xc3, 0xc8, 0xa1, 0xce, 0x2c, 0x52, 0xa2, 0x00, 0x63, 0xeb, 0x73, + 0xba, 0x71, 0xc8, 0x49, 0x27, 0x23, 0x97, 0x64, 0x85, 0x9e, 0x38, 0x0e, + 0xad, 0x63, 0x68, 0x3c, 0xba, 0x52, 0x81, 0x58, 0x79, 0xa3, 0x2c, 0x0c, + 0xdf, 0xde, 0x6d, 0xeb, 0x31, 0xf2, 0xba, 0xa0, 0x7c, 0x6c, 0xf1, 0x2c, + 0xd4, 0xe1, 0xbd, 0x77, 0x84, 0x37, 0x03, 0xce, 0x32, 0xb5, 0xc8, 0x9a, + 0x81, 0x1a, 0x4a, 0x92, 0x4e, 0x3b, 0x46, 0x9a, 0x85, 0xfe, 0x83, 0xa2, + 0xf9, 0x9e, 0x8c, 0xa3, 0xcc, 0x0d, 0x5e, 0xb3, 0x3d, 0xcf, 0x04, 0x78, + 0x8f, 0x14, 0x14, 0x7b, 0x32, 0x9c, 0xc7, 0x00, 0xa6, 0x5c, 0xc4, 0xb5, + 0xa1, 0x55, 0x8d, 0x5a, 0x56, 0x68, 0xa4, 0x22, 0x70, 0xaa, 0x3c, 0x81, + 0x71, 0xd9, 0x9d, 0xa8, 0x45, 0x3b, 0xf4, 0xe5, 0xf6, 0xa2, 0x51, 0xdd, + 0xc7, 0x7b, 0x62, 0xe8, 0x6f, 0x0c, 0x74, 0xeb, 0xb8, 0xda, 0xf8, 0xbf, + 0x87, 0x0d, 0x79, 0x50, 0x91, 0x90, 0x9b, 0x18, 0x3b, 0x91, 0x59, 0x27, + 0xf1, 0x35, 0x28, 0x13, 0xab, 0x26, 0x7e, 0xd5, 0xf7, 0x7a, +};
diff --git a/quic/core/crypto/common_cert_set_2b.inc b/quic/core/crypto/common_cert_set_2b.inc new file mode 100644 index 0000000..17c6488 --- /dev/null +++ b/quic/core/crypto/common_cert_set_2b.inc
@@ -0,0 +1,5739 @@ +/* This file contains common certificates. It's designed to be #included in + * another file, in a namespace. */ +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 36:34:9e:18:c9:9c:26:69:b6:56:2e:6c:e5:ad:71:32 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2008 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA - G3 + Validity + Not Before: May 23 00:00:00 2013 GMT + Not After : May 22 23:59:59 2023 GMT + Subject: C=US, O=thawte, Inc., CN=thawte SHA256 SSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a3:63:2b:d4:ba:5d:38:ae:b0:cf:b9:4c:38:df: + 20:7d:f1:2b:47:71:1d:8b:68:f3:56:f9:9c:da:aa: + e5:84:26:de:a5:71:30:bc:f3:31:23:9d:e8:3b:80: + c8:66:57:75:b6:57:0e:db:93:f5:26:8e:70:ba:64: + 52:66:8a:2a:88:5c:44:18:4d:a8:a2:7c:bd:56:61: + 32:90:12:f9:35:87:48:60:b0:6e:90:67:44:01:8d: + e7:c9:0d:63:68:72:72:ab:63:3c:86:b8:1f:7d:ad: + 88:25:a7:6a:88:29:fb:59:c6:78:71:5f:2c:ba:89: + e6:d3:80:fd:57:ec:b9:51:5f:43:33:2e:7e:25:3b: + a4:04:d1:60:8c:b3:44:33:93:0c:ad:2a:b6:44:a2: + 19:3b:af:c4:90:6f:7b:05:87:86:9b:2c:6a:9d:2b: + 6c:77:c9:00:9f:c9:cf:ac:ed:3e:1b:f7:c3:f3:d9: + f8:6c:d4:a0:57:c4:fb:28:32:aa:33:f0:e6:ba:98: + df:e5:c2:4e:9c:74:bf:8a:48:c2:f2:1b:f0:77:40: + 41:07:04:b2:3a:d5:4c:c4:29:a9:11:40:3f:02:46: + f0:91:d5:d2:81:83:86:13:b3:31:ed:46:ab:a8:87: + 76:a9:99:7d:bc:cd:31:50:f4:a5:b5:dc:a5:32:b3: + 8b:8b + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.thawte.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: https://www.thawte.com/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.thawte.com/ThawtePCA-G3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-415 + X509v3 Subject Key Identifier: + 2B:9A:35:AE:01:18:38:30:E1:70:7A:05:E0:11:76:A3:CE:BD:90:14 + X509v3 Authority Key Identifier: + keyid:AD:6C:AA:94:60:9C:ED:E4:FF:FA:3E:0A:74:2B:63:03:F7:B6:59:BF + + Signature Algorithm: sha256WithRSAEncryption + 74:a6:56:e8:af:93:96:19:fb:26:f9:0d:b0:44:a5:cd:e9:7a: + 48:03:74:01:6c:13:71:b7:e0:82:90:99:62:23:e3:d6:99:af: + f0:c7:1e:9e:a8:18:21:db:b4:94:3f:34:56:1b:99:55:2f:8e: + f0:45:33:32:b7:72:c1:13:5b:34:d3:f5:60:e5:2e:18:d1:5c: + c5:6a:c1:aa:87:50:0c:1c:9d:64:2b:ff:1b:dc:d5:2e:61:0b: + e7:b9:b6:91:53:86:d9:03:2a:d1:3d:7b:4a:da:2b:07:be:29: + f2:60:42:a9:91:1a:0e:2e:3c:d1:7d:a5:13:14:02:fa:ee:8b: + 8d:b6:c8:b8:3e:56:81:57:21:24:3f:65:c3:b4:c9:ce:5c:8d: + 46:ac:53:f3:f9:55:74:c8:2b:fd:d2:78:70:f5:f8:11:e5:f4: + a7:ad:20:f5:9d:f1:ec:70:f6:13:ac:e6:8c:8d:db:3f:c6:f2: + 79:0e:ab:52:f2:cc:1b:79:27:cf:16:b3:d6:f3:c6:36:80:43: + ec:c5:94:f0:dd:90:8d:f8:c6:52:46:56:eb:74:47:be:a6:f3: + 19:ae:71:4c:c0:e1:e7:d4:cf:ed:d4:06:28:2a:11:3c:ba:d9: + 41:6e:00:e7:81:37:93:e4:da:62:c6:1d:67:6f:63:b4:14:86: + d9:a6:62:f0 +-----BEGIN CERTIFICATE----- +MIIEwjCCA6qgAwIBAgIQNjSeGMmcJmm2Vi5s5a1xMjANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0xMzA1MjMwMDAwMDBa +Fw0yMzA1MjIyMzU5NTlaMEMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUs +IEluYy4xHTAbBgNVBAMTFHRoYXd0ZSBTSEEyNTYgU1NMIENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2Mr1LpdOK6wz7lMON8gffErR3Edi2jzVvmc +2qrlhCbepXEwvPMxI53oO4DIZld1tlcO25P1Jo5wumRSZooqiFxEGE2oony9VmEy +kBL5NYdIYLBukGdEAY3nyQ1jaHJyq2M8hrgffa2IJadqiCn7WcZ4cV8suonm04D9 +V+y5UV9DMy5+JTukBNFgjLNEM5MMrSq2RKIZO6/EkG97BYeGmyxqnStsd8kAn8nP +rO0+G/fD89n4bNSgV8T7KDKqM/Dmupjf5cJOnHS/ikjC8hvwd0BBBwSyOtVMxCmp +EUA/AkbwkdXSgYOGE7Mx7UarqId2qZl9vM0xUPSltdylMrOLiwIDAQABo4IBRDCC +AUAwMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3 +dGUuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwQQYDVR0gBDowODA2BgpghkgBhvhF +AQc2MCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3BzMDcG +A1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVQQ0Et +RzMuY3JsMA4GA1UdDwEB/wQEAwIBBjAqBgNVHREEIzAhpB8wHTEbMBkGA1UEAxMS +VmVyaVNpZ25NUEtJLTItNDE1MB0GA1UdDgQWBBQrmjWuARg4MOFwegXgEXajzr2Q +FDAfBgNVHSMEGDAWgBStbKqUYJzt5P/6Pgp0K2MD97ZZvzANBgkqhkiG9w0BAQsF +AAOCAQEAdKZW6K+Tlhn7JvkNsESlzel6SAN0AWwTcbfggpCZYiPj1pmv8McenqgY +Idu0lD80VhuZVS+O8EUzMrdywRNbNNP1YOUuGNFcxWrBqodQDBydZCv/G9zVLmEL +57m2kVOG2QMq0T17StorB74p8mBCqZEaDi480X2lExQC+u6LjbbIuD5WgVchJD9l +w7TJzlyNRqxT8/lVdMgr/dJ4cPX4EeX0p60g9Z3x7HD2E6zmjI3bP8byeQ6rUvLM +G3knzxaz1vPGNoBD7MWU8N2QjfjGUkZW63RHvqbzGa5xTMDh59TP7dQGKCoRPLrZ +QW4A54E3k+TaYsYdZ29jtBSG2aZi8A== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert29[] = { + 0x30, 0x82, 0x04, 0xc2, 0x30, 0x82, 0x03, 0xaa, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x36, 0x34, 0x9e, 0x18, 0xc9, 0x9c, 0x26, 0x69, 0xb6, + 0x56, 0x2e, 0x6c, 0xe5, 0xad, 0x71, 0x32, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x38, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x1b, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x35, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, + 0x17, 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x32, 0x32, 0x33, 0x35, 0x39, + 0x35, 0x39, 0x5a, 0x30, 0x43, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x14, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, + 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x63, 0x2b, + 0xd4, 0xba, 0x5d, 0x38, 0xae, 0xb0, 0xcf, 0xb9, 0x4c, 0x38, 0xdf, 0x20, + 0x7d, 0xf1, 0x2b, 0x47, 0x71, 0x1d, 0x8b, 0x68, 0xf3, 0x56, 0xf9, 0x9c, + 0xda, 0xaa, 0xe5, 0x84, 0x26, 0xde, 0xa5, 0x71, 0x30, 0xbc, 0xf3, 0x31, + 0x23, 0x9d, 0xe8, 0x3b, 0x80, 0xc8, 0x66, 0x57, 0x75, 0xb6, 0x57, 0x0e, + 0xdb, 0x93, 0xf5, 0x26, 0x8e, 0x70, 0xba, 0x64, 0x52, 0x66, 0x8a, 0x2a, + 0x88, 0x5c, 0x44, 0x18, 0x4d, 0xa8, 0xa2, 0x7c, 0xbd, 0x56, 0x61, 0x32, + 0x90, 0x12, 0xf9, 0x35, 0x87, 0x48, 0x60, 0xb0, 0x6e, 0x90, 0x67, 0x44, + 0x01, 0x8d, 0xe7, 0xc9, 0x0d, 0x63, 0x68, 0x72, 0x72, 0xab, 0x63, 0x3c, + 0x86, 0xb8, 0x1f, 0x7d, 0xad, 0x88, 0x25, 0xa7, 0x6a, 0x88, 0x29, 0xfb, + 0x59, 0xc6, 0x78, 0x71, 0x5f, 0x2c, 0xba, 0x89, 0xe6, 0xd3, 0x80, 0xfd, + 0x57, 0xec, 0xb9, 0x51, 0x5f, 0x43, 0x33, 0x2e, 0x7e, 0x25, 0x3b, 0xa4, + 0x04, 0xd1, 0x60, 0x8c, 0xb3, 0x44, 0x33, 0x93, 0x0c, 0xad, 0x2a, 0xb6, + 0x44, 0xa2, 0x19, 0x3b, 0xaf, 0xc4, 0x90, 0x6f, 0x7b, 0x05, 0x87, 0x86, + 0x9b, 0x2c, 0x6a, 0x9d, 0x2b, 0x6c, 0x77, 0xc9, 0x00, 0x9f, 0xc9, 0xcf, + 0xac, 0xed, 0x3e, 0x1b, 0xf7, 0xc3, 0xf3, 0xd9, 0xf8, 0x6c, 0xd4, 0xa0, + 0x57, 0xc4, 0xfb, 0x28, 0x32, 0xaa, 0x33, 0xf0, 0xe6, 0xba, 0x98, 0xdf, + 0xe5, 0xc2, 0x4e, 0x9c, 0x74, 0xbf, 0x8a, 0x48, 0xc2, 0xf2, 0x1b, 0xf0, + 0x77, 0x40, 0x41, 0x07, 0x04, 0xb2, 0x3a, 0xd5, 0x4c, 0xc4, 0x29, 0xa9, + 0x11, 0x40, 0x3f, 0x02, 0x46, 0xf0, 0x91, 0xd5, 0xd2, 0x81, 0x83, 0x86, + 0x13, 0xb3, 0x31, 0xed, 0x46, 0xab, 0xa8, 0x87, 0x76, 0xa9, 0x99, 0x7d, + 0xbc, 0xcd, 0x31, 0x50, 0xf4, 0xa5, 0xb5, 0xdc, 0xa5, 0x32, 0xb3, 0x8b, + 0x8b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x44, 0x30, 0x82, + 0x01, 0x40, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, + 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, + 0x38, 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, + 0x01, 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x37, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, 0x30, 0x2e, 0x30, 0x2c, 0xa0, 0x2a, + 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2d, + 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2a, + 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, + 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, + 0x2d, 0x32, 0x2d, 0x34, 0x31, 0x35, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2b, 0x9a, 0x35, 0xae, 0x01, 0x18, 0x38, + 0x30, 0xe1, 0x70, 0x7a, 0x05, 0xe0, 0x11, 0x76, 0xa3, 0xce, 0xbd, 0x90, + 0x14, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xad, 0x6c, 0xaa, 0x94, 0x60, 0x9c, 0xed, 0xe4, 0xff, 0xfa, + 0x3e, 0x0a, 0x74, 0x2b, 0x63, 0x03, 0xf7, 0xb6, 0x59, 0xbf, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x74, 0xa6, 0x56, 0xe8, 0xaf, 0x93, + 0x96, 0x19, 0xfb, 0x26, 0xf9, 0x0d, 0xb0, 0x44, 0xa5, 0xcd, 0xe9, 0x7a, + 0x48, 0x03, 0x74, 0x01, 0x6c, 0x13, 0x71, 0xb7, 0xe0, 0x82, 0x90, 0x99, + 0x62, 0x23, 0xe3, 0xd6, 0x99, 0xaf, 0xf0, 0xc7, 0x1e, 0x9e, 0xa8, 0x18, + 0x21, 0xdb, 0xb4, 0x94, 0x3f, 0x34, 0x56, 0x1b, 0x99, 0x55, 0x2f, 0x8e, + 0xf0, 0x45, 0x33, 0x32, 0xb7, 0x72, 0xc1, 0x13, 0x5b, 0x34, 0xd3, 0xf5, + 0x60, 0xe5, 0x2e, 0x18, 0xd1, 0x5c, 0xc5, 0x6a, 0xc1, 0xaa, 0x87, 0x50, + 0x0c, 0x1c, 0x9d, 0x64, 0x2b, 0xff, 0x1b, 0xdc, 0xd5, 0x2e, 0x61, 0x0b, + 0xe7, 0xb9, 0xb6, 0x91, 0x53, 0x86, 0xd9, 0x03, 0x2a, 0xd1, 0x3d, 0x7b, + 0x4a, 0xda, 0x2b, 0x07, 0xbe, 0x29, 0xf2, 0x60, 0x42, 0xa9, 0x91, 0x1a, + 0x0e, 0x2e, 0x3c, 0xd1, 0x7d, 0xa5, 0x13, 0x14, 0x02, 0xfa, 0xee, 0x8b, + 0x8d, 0xb6, 0xc8, 0xb8, 0x3e, 0x56, 0x81, 0x57, 0x21, 0x24, 0x3f, 0x65, + 0xc3, 0xb4, 0xc9, 0xce, 0x5c, 0x8d, 0x46, 0xac, 0x53, 0xf3, 0xf9, 0x55, + 0x74, 0xc8, 0x2b, 0xfd, 0xd2, 0x78, 0x70, 0xf5, 0xf8, 0x11, 0xe5, 0xf4, + 0xa7, 0xad, 0x20, 0xf5, 0x9d, 0xf1, 0xec, 0x70, 0xf6, 0x13, 0xac, 0xe6, + 0x8c, 0x8d, 0xdb, 0x3f, 0xc6, 0xf2, 0x79, 0x0e, 0xab, 0x52, 0xf2, 0xcc, + 0x1b, 0x79, 0x27, 0xcf, 0x16, 0xb3, 0xd6, 0xf3, 0xc6, 0x36, 0x80, 0x43, + 0xec, 0xc5, 0x94, 0xf0, 0xdd, 0x90, 0x8d, 0xf8, 0xc6, 0x52, 0x46, 0x56, + 0xeb, 0x74, 0x47, 0xbe, 0xa6, 0xf3, 0x19, 0xae, 0x71, 0x4c, 0xc0, 0xe1, + 0xe7, 0xd4, 0xcf, 0xed, 0xd4, 0x06, 0x28, 0x2a, 0x11, 0x3c, 0xba, 0xd9, + 0x41, 0x6e, 0x00, 0xe7, 0x81, 0x37, 0x93, 0xe4, 0xda, 0x62, 0xc6, 0x1d, + 0x67, 0x6f, 0x63, 0xb4, 0x14, 0x86, 0xd9, 0xa6, 0x62, 0xf0, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 35:97:31:87:f3:87:3a:07:32:7e:ce:58:0c:9b:7e:da + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority + Validity + Not Before: Nov 8 00:00:00 2006 GMT + Not After : Nov 7 23:59:59 2021 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b: + 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57: + 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8: + 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe: + 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d: + a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59: + 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49: + d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69: + 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96: + bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5: + f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02: + ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6: + f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19: + 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d: + 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95: + ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f: + 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8: + 25:15 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.verisign.com/pca3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.verisign.com/cps + + X509v3 Subject Key Identifier: + 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + X509v3 Extended Key Usage: + Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1, TLS Web Server Authentication, TLS Web Client Authentication + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + Authority Information Access: + OCSP - URI:http://ocsp.verisign.com + + Signature Algorithm: sha1WithRSAEncryption + 0f:25:ae:48:ed:1b:33:85:4c:0c:b5:c2:d7:fe:4d:d6:83:28: + 4c:41:65:60:00:0b:77:48:71:82:fe:7f:db:5a:0e:20:cc:d2: + ea:47:bc:64:42:61:44:34:74:30:81:81:26:8a:4a:f7:44:5d: + 7e:34:80:a8:b8:83:e2:09:d7:6d:23:dd:89:ed:28:08:bd:63: + 5a:11:57:08:c4:9e:da:e2:68:28:af:dd:50:3c:ec:82:21:d8: + 00:c2:55:44:50:70:41:ad:83:17:79:ba:08:f3:2b:de:ed:34: + 1d:44:9e:d2:04:93:f4:cb:05:17:2d:09:2d:2d:63:ef:f6:26: + 0b:7b +-----BEGIN CERTIFICATE----- +MIIExjCCBC+gAwIBAgIQNZcxh/OHOgcyfs5YDJt+2jANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT +LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv +ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8 +RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb +ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR +TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH +iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB +AAGjggGRMIIBjTAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0 +dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9 +BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy +aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwNAYD +VR0lBC0wKwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBBggrBgEFBQcDAQYIKwYBBQUH +AwIwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUr +DgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNp +Z24uY29tL3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhho +dHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEADyWuSO0b +M4VMDLXC1/5N1oMoTEFlYAALd0hxgv5/21oOIMzS6ke8ZEJhRDR0MIGBJopK90Rd +fjSAqLiD4gnXbSPdie0oCL1jWhFXCMSe2uJoKK/dUDzsgiHYAMJVRFBwQa2DF3m6 +CPMr3u00HUSe0gST9MsFFy0JLS1j7/YmC3s= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert30[] = { + 0x30, 0x82, 0x04, 0xc6, 0x30, 0x82, 0x04, 0x2f, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x35, 0x97, 0x31, 0x87, 0xf3, 0x87, 0x3a, 0x07, 0x32, + 0x7e, 0xce, 0x58, 0x0c, 0x9b, 0x7e, 0xda, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, + 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30, + 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, + 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, + 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, + 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30, + 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35, + 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c, + 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3, + 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22, + 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1, + 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb, + 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0, + 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85, + 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33, + 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51, + 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74, + 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0, + 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06, + 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff, + 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4, + 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19, + 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe, + 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47, + 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5, + 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14, + 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f, + 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x91, 0x30, 0x82, 0x01, 0x8d, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, + 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, + 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, + 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x34, 0x06, 0x03, + 0x55, 0x1d, 0x25, 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, + 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x02, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, + 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, + 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, + 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, + 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, + 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, + 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, + 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, + 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x0f, 0x25, 0xae, 0x48, 0xed, 0x1b, + 0x33, 0x85, 0x4c, 0x0c, 0xb5, 0xc2, 0xd7, 0xfe, 0x4d, 0xd6, 0x83, 0x28, + 0x4c, 0x41, 0x65, 0x60, 0x00, 0x0b, 0x77, 0x48, 0x71, 0x82, 0xfe, 0x7f, + 0xdb, 0x5a, 0x0e, 0x20, 0xcc, 0xd2, 0xea, 0x47, 0xbc, 0x64, 0x42, 0x61, + 0x44, 0x34, 0x74, 0x30, 0x81, 0x81, 0x26, 0x8a, 0x4a, 0xf7, 0x44, 0x5d, + 0x7e, 0x34, 0x80, 0xa8, 0xb8, 0x83, 0xe2, 0x09, 0xd7, 0x6d, 0x23, 0xdd, + 0x89, 0xed, 0x28, 0x08, 0xbd, 0x63, 0x5a, 0x11, 0x57, 0x08, 0xc4, 0x9e, + 0xda, 0xe2, 0x68, 0x28, 0xaf, 0xdd, 0x50, 0x3c, 0xec, 0x82, 0x21, 0xd8, + 0x00, 0xc2, 0x55, 0x44, 0x50, 0x70, 0x41, 0xad, 0x83, 0x17, 0x79, 0xba, + 0x08, 0xf3, 0x2b, 0xde, 0xed, 0x34, 0x1d, 0x44, 0x9e, 0xd2, 0x04, 0x93, + 0xf4, 0xcb, 0x05, 0x17, 0x2d, 0x09, 0x2d, 0x2d, 0x63, 0xef, 0xf6, 0x26, + 0x0b, 0x7b, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 7 (0x7) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2 + Validity + Not Before: May 3 07:00:00 2011 GMT + Not After : May 3 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certs.godaddy.com/repository/, CN=Go Daddy Secure Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b9:e0:cb:10:d4:af:76:bd:d4:93:62:eb:30:64: + b8:81:08:6c:c3:04:d9:62:17:8e:2f:ff:3e:65:cf: + 8f:ce:62:e6:3c:52:1c:da:16:45:4b:55:ab:78:6b: + 63:83:62:90:ce:0f:69:6c:99:c8:1a:14:8b:4c:cc: + 45:33:ea:88:dc:9e:a3:af:2b:fe:80:61:9d:79:57: + c4:cf:2e:f4:3f:30:3c:5d:47:fc:9a:16:bc:c3:37: + 96:41:51:8e:11:4b:54:f8:28:be:d0:8c:be:f0:30: + 38:1e:f3:b0:26:f8:66:47:63:6d:de:71:26:47:8f: + 38:47:53:d1:46:1d:b4:e3:dc:00:ea:45:ac:bd:bc: + 71:d9:aa:6f:00:db:db:cd:30:3a:79:4f:5f:4c:47: + f8:1d:ef:5b:c2:c4:9d:60:3b:b1:b2:43:91:d8:a4: + 33:4e:ea:b3:d6:27:4f:ad:25:8a:a5:c6:f4:d5:d0: + a6:ae:74:05:64:57:88:b5:44:55:d4:2d:2a:3a:3e: + f8:b8:bd:e9:32:0a:02:94:64:c4:16:3a:50:f1:4a: + ae:e7:79:33:af:0c:20:07:7f:e8:df:04:39:c2:69: + 02:6c:63:52:fa:77:c1:1b:c8:74:87:c8:b9:93:18: + 50:54:35:4b:69:4e:bc:3b:d3:49:2e:1f:dc:c1:d2: + 52:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 40:C2:BD:27:8E:CC:34:83:30:A2:33:D7:FB:6C:B3:F0:B4:2C:80:CE + X509v3 Authority Key Identifier: + keyid:3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE + + Authority Information Access: + OCSP - URI:http://ocsp.godaddy.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.godaddy.com/gdroot-g2.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.godaddy.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 08:7e:6c:93:10:c8:38:b8:96:a9:90:4b:ff:a1:5f:4f:04:ef: + 6c:3e:9c:88:06:c9:50:8f:a6:73:f7:57:31:1b:be:bc:e4:2f: + db:f8:ba:d3:5b:e0:b4:e7:e6:79:62:0e:0c:a2:d7:6a:63:73: + 31:b5:f5:a8:48:a4:3b:08:2d:a2:5d:90:d7:b4:7c:25:4f:11: + 56:30:c4:b6:44:9d:7b:2c:9d:e5:5e:e6:ef:0c:61:aa:bf:e4: + 2a:1b:ee:84:9e:b8:83:7d:c1:43:ce:44:a7:13:70:0d:91:1f: + f4:c8:13:ad:83:60:d9:d8:72:a8:73:24:1e:b5:ac:22:0e:ca: + 17:89:62:58:44:1b:ab:89:25:01:00:0f:cd:c4:1b:62:db:51: + b4:d3:0f:51:2a:9b:f4:bc:73:fc:76:ce:36:a4:cd:d9:d8:2c: + ea:ae:9b:f5:2a:b2:90:d1:4d:75:18:8a:3f:8a:41:90:23:7d: + 5b:4b:fe:a4:03:58:9b:46:b2:c3:60:60:83:f8:7d:50:41:ce: + c2:a1:90:c3:bb:ef:02:2f:d2:15:54:ee:44:15:d9:0a:ae:a7: + 8a:33:ed:b1:2d:76:36:26:dc:04:eb:9f:f7:61:1f:15:dc:87: + 6f:ee:46:96:28:ad:a1:26:7d:0a:09:a7:2e:04:a3:8d:bc:f8: + bc:04:30:01 +-----BEGIN CERTIFICATE----- +MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3 +MDAwMFowgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UE +CxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQD +EypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzD +BNliF44v/z5lz4/OYuY8UhzaFkVLVat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOv +K/6AYZ15V8TPLvQ/MDxdR/yaFrzDN5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23e +cSZHjzhHU9FGHbTj3ADqRay9vHHZqm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HY +pDNO6rPWJ0+tJYqlxvTV0KaudAVkV4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7n +eTOvDCAHf+jfBDnCaQJsY1L6d8EbyHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMB +AAGjggEaMIIBFjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUQMK9J47MNIMwojPX+2yz8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv +9r0FQW4gwZTaD94wNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC5nb2RhZGR5LmNvbS8wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5n +b2RhZGR5LmNvbS9nZHJvb3QtZzIuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEG +CCsGAQUFBwIBFiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQAIfmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz +91cxG7685C/b+LrTW+C05+Z5Yg4MotdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2 +RJ17LJ3lXubvDGGqv+QqG+6EnriDfcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawi +DsoXiWJYRBuriSUBAA/NxBti21G00w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11 +GIo/ikGQI31bS/6kA1ibRrLDYGCD+H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2x +LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert31[] = { + 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x03, 0xb8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x83, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, + 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, + 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, + 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xb4, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, + 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, + 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, + 0x72, 0x74, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x2f, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x2a, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0xe0, 0xcb, 0x10, 0xd4, 0xaf, 0x76, + 0xbd, 0xd4, 0x93, 0x62, 0xeb, 0x30, 0x64, 0xb8, 0x81, 0x08, 0x6c, 0xc3, + 0x04, 0xd9, 0x62, 0x17, 0x8e, 0x2f, 0xff, 0x3e, 0x65, 0xcf, 0x8f, 0xce, + 0x62, 0xe6, 0x3c, 0x52, 0x1c, 0xda, 0x16, 0x45, 0x4b, 0x55, 0xab, 0x78, + 0x6b, 0x63, 0x83, 0x62, 0x90, 0xce, 0x0f, 0x69, 0x6c, 0x99, 0xc8, 0x1a, + 0x14, 0x8b, 0x4c, 0xcc, 0x45, 0x33, 0xea, 0x88, 0xdc, 0x9e, 0xa3, 0xaf, + 0x2b, 0xfe, 0x80, 0x61, 0x9d, 0x79, 0x57, 0xc4, 0xcf, 0x2e, 0xf4, 0x3f, + 0x30, 0x3c, 0x5d, 0x47, 0xfc, 0x9a, 0x16, 0xbc, 0xc3, 0x37, 0x96, 0x41, + 0x51, 0x8e, 0x11, 0x4b, 0x54, 0xf8, 0x28, 0xbe, 0xd0, 0x8c, 0xbe, 0xf0, + 0x30, 0x38, 0x1e, 0xf3, 0xb0, 0x26, 0xf8, 0x66, 0x47, 0x63, 0x6d, 0xde, + 0x71, 0x26, 0x47, 0x8f, 0x38, 0x47, 0x53, 0xd1, 0x46, 0x1d, 0xb4, 0xe3, + 0xdc, 0x00, 0xea, 0x45, 0xac, 0xbd, 0xbc, 0x71, 0xd9, 0xaa, 0x6f, 0x00, + 0xdb, 0xdb, 0xcd, 0x30, 0x3a, 0x79, 0x4f, 0x5f, 0x4c, 0x47, 0xf8, 0x1d, + 0xef, 0x5b, 0xc2, 0xc4, 0x9d, 0x60, 0x3b, 0xb1, 0xb2, 0x43, 0x91, 0xd8, + 0xa4, 0x33, 0x4e, 0xea, 0xb3, 0xd6, 0x27, 0x4f, 0xad, 0x25, 0x8a, 0xa5, + 0xc6, 0xf4, 0xd5, 0xd0, 0xa6, 0xae, 0x74, 0x05, 0x64, 0x57, 0x88, 0xb5, + 0x44, 0x55, 0xd4, 0x2d, 0x2a, 0x3a, 0x3e, 0xf8, 0xb8, 0xbd, 0xe9, 0x32, + 0x0a, 0x02, 0x94, 0x64, 0xc4, 0x16, 0x3a, 0x50, 0xf1, 0x4a, 0xae, 0xe7, + 0x79, 0x33, 0xaf, 0x0c, 0x20, 0x07, 0x7f, 0xe8, 0xdf, 0x04, 0x39, 0xc2, + 0x69, 0x02, 0x6c, 0x63, 0x52, 0xfa, 0x77, 0xc1, 0x1b, 0xc8, 0x74, 0x87, + 0xc8, 0xb9, 0x93, 0x18, 0x50, 0x54, 0x35, 0x4b, 0x69, 0x4e, 0xbc, 0x3b, + 0xd3, 0x49, 0x2e, 0x1f, 0xdc, 0xc1, 0xd2, 0x52, 0xfb, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1a, 0x30, 0x82, 0x01, 0x16, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x40, 0xc2, 0xbd, 0x27, 0x8e, 0xcc, + 0x34, 0x83, 0x30, 0xa2, 0x33, 0xd7, 0xfb, 0x6c, 0xb3, 0xf0, 0xb4, 0x2c, + 0x80, 0xce, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x3a, 0x9a, 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef, + 0xf6, 0xbd, 0x05, 0x41, 0x6e, 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, + 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, + 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x08, 0x7e, 0x6c, 0x93, + 0x10, 0xc8, 0x38, 0xb8, 0x96, 0xa9, 0x90, 0x4b, 0xff, 0xa1, 0x5f, 0x4f, + 0x04, 0xef, 0x6c, 0x3e, 0x9c, 0x88, 0x06, 0xc9, 0x50, 0x8f, 0xa6, 0x73, + 0xf7, 0x57, 0x31, 0x1b, 0xbe, 0xbc, 0xe4, 0x2f, 0xdb, 0xf8, 0xba, 0xd3, + 0x5b, 0xe0, 0xb4, 0xe7, 0xe6, 0x79, 0x62, 0x0e, 0x0c, 0xa2, 0xd7, 0x6a, + 0x63, 0x73, 0x31, 0xb5, 0xf5, 0xa8, 0x48, 0xa4, 0x3b, 0x08, 0x2d, 0xa2, + 0x5d, 0x90, 0xd7, 0xb4, 0x7c, 0x25, 0x4f, 0x11, 0x56, 0x30, 0xc4, 0xb6, + 0x44, 0x9d, 0x7b, 0x2c, 0x9d, 0xe5, 0x5e, 0xe6, 0xef, 0x0c, 0x61, 0xaa, + 0xbf, 0xe4, 0x2a, 0x1b, 0xee, 0x84, 0x9e, 0xb8, 0x83, 0x7d, 0xc1, 0x43, + 0xce, 0x44, 0xa7, 0x13, 0x70, 0x0d, 0x91, 0x1f, 0xf4, 0xc8, 0x13, 0xad, + 0x83, 0x60, 0xd9, 0xd8, 0x72, 0xa8, 0x73, 0x24, 0x1e, 0xb5, 0xac, 0x22, + 0x0e, 0xca, 0x17, 0x89, 0x62, 0x58, 0x44, 0x1b, 0xab, 0x89, 0x25, 0x01, + 0x00, 0x0f, 0xcd, 0xc4, 0x1b, 0x62, 0xdb, 0x51, 0xb4, 0xd3, 0x0f, 0x51, + 0x2a, 0x9b, 0xf4, 0xbc, 0x73, 0xfc, 0x76, 0xce, 0x36, 0xa4, 0xcd, 0xd9, + 0xd8, 0x2c, 0xea, 0xae, 0x9b, 0xf5, 0x2a, 0xb2, 0x90, 0xd1, 0x4d, 0x75, + 0x18, 0x8a, 0x3f, 0x8a, 0x41, 0x90, 0x23, 0x7d, 0x5b, 0x4b, 0xfe, 0xa4, + 0x03, 0x58, 0x9b, 0x46, 0xb2, 0xc3, 0x60, 0x60, 0x83, 0xf8, 0x7d, 0x50, + 0x41, 0xce, 0xc2, 0xa1, 0x90, 0xc3, 0xbb, 0xef, 0x02, 0x2f, 0xd2, 0x15, + 0x54, 0xee, 0x44, 0x15, 0xd9, 0x0a, 0xae, 0xa7, 0x8a, 0x33, 0xed, 0xb1, + 0x2d, 0x76, 0x36, 0x26, 0xdc, 0x04, 0xeb, 0x9f, 0xf7, 0x61, 0x1f, 0x15, + 0xdc, 0x87, 0x6f, 0xee, 0x46, 0x96, 0x28, 0xad, 0xa1, 0x26, 0x7d, 0x0a, + 0x09, 0xa7, 0x2e, 0x04, 0xa3, 0x8d, 0xbc, 0xf8, 0xbc, 0x04, 0x30, 0x01, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 0a:48:9e:88:53:7e:8a:a6:45:4d:6e:2c:4b:2a:eb:20 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2008 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA - G3 + Validity + Not Before: Apr 9 00:00:00 2013 GMT + Not After : Apr 8 23:59:59 2023 GMT + Subject: C=US, O=thawte, Inc., CN=thawte Extended Validation SHA256 SSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:f2:c4:bc:74:e8:25:f6:00:62:28:e3:4c:e8:b8: + df:13:9f:8b:07:37:ef:62:4a:f1:57:09:f6:82:e8: + 75:f0:0a:a9:27:cf:93:3b:ec:36:89:a5:6e:1d:d6: + 54:f3:b8:04:97:72:b4:69:25:cc:d1:42:0e:5b:d5: + 1c:7f:a2:60:6e:b1:52:1a:db:93:2f:bb:0b:0d:64: + 53:16:cb:1c:09:24:95:29:22:b4:8a:18:00:89:fe: + f7:1f:72:c8:e8:5c:2f:1a:1b:a2:18:b8:ef:18:5c: + cb:b5:db:3a:4e:db:0f:ae:df:c4:79:e3:1e:aa:5c: + a3:a4:e5:ac:61:9b:37:85:8f:48:75:1b:b9:d5:68: + 96:e9:27:79:70:57:23:1a:bb:6c:93:90:c7:45:d7: + 17:d2:37:2a:76:b3:cd:82:a9:4f:c0:03:7b:e1:3d: + 7a:7e:5b:b8:85:f2:f5:15:fb:70:a9:bd:f5:50:65: + 16:9d:e3:b6:6b:61:6e:a1:7a:9e:e8:0d:1c:f7:2a: + 8e:69:7e:43:30:8e:78:ce:ee:65:1e:3b:9b:87:1e: + 49:1c:f8:32:46:5d:28:46:79:2a:4e:27:5d:17:58: + a8:37:fe:a8:13:a9:69:15:df:36:22:89:75:ba:ca: + 01:40:2e:ed:9d:d7:0c:aa:31:ce:27:ae:57:d5:d2: + 51:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://ocsp.thawte.com + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.thawte.com/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.thawte.com/ThawtePCA-G3.crl + + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-374 + X509v3 Subject Key Identifier: + 3B:24:C8:31:A0:B7:5A:D0:6A:B8:D2:CA:07:74:CC:1E:24:D4:C4:DC + X509v3 Authority Key Identifier: + keyid:AD:6C:AA:94:60:9C:ED:E4:FF:FA:3E:0A:74:2B:63:03:F7:B6:59:BF + + Signature Algorithm: sha256WithRSAEncryption + 68:98:26:aa:d4:33:c9:ba:75:70:d4:9f:49:ad:d6:c1:54:dc: + ee:aa:56:1f:78:a7:f0:a1:a4:ee:0b:f9:12:af:df:a6:b8:ee: + c3:cb:35:13:6a:59:2a:f8:c9:e9:4c:2f:bc:b1:bc:2b:c2:02: + 30:e1:c3:be:c2:f0:81:8c:99:77:89:58:00:a3:cc:7f:a3:02: + 4c:53:b2:6e:36:4f:fe:df:87:76:b3:3f:ec:5a:62:50:b6:00: + 45:58:f2:87:ac:77:e6:d0:20:50:63:c5:e4:b2:70:15:18:90: + 05:7b:7b:af:2b:46:be:6b:4e:1f:53:fc:84:27:ae:83:d2:8d: + 47:53:a7:0e:1f:63:b5:ba:db:16:d8:6a:09:25:55:7d:8f:3d: + 4a:c1:83:f9:b3:b9:a7:04:5a:c8:f3:11:04:91:53:30:d9:52: + 87:cb:39:00:9c:ec:53:c3:02:09:7e:a7:36:8e:72:21:2f:23: + bb:4c:c6:47:a5:a1:ee:67:c4:2f:5c:3a:47:38:61:e2:c3:1e: + 37:92:9e:c8:2f:6b:fa:ef:d2:c3:cd:29:8d:98:f8:52:17:ed: + b5:53:3c:df:af:c9:1b:62:ad:df:02:ee:5d:34:f6:41:4b:cb: + c3:55:af:b1:cb:da:9c:73:d5:02:a8:2d:a7:ac:fc:e1:e5:07: + d0:51:e8:35 +-----BEGIN CERTIFICATE----- +MIIE0DCCA7igAwIBAgIQCkieiFN+iqZFTW4sSyrrIDANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0xMzA0MDkwMDAwMDBa +Fw0yMzA0MDgyMzU5NTlaMFcxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUs +IEluYy4xMTAvBgNVBAMTKHRoYXd0ZSBFeHRlbmRlZCBWYWxpZGF0aW9uIFNIQTI1 +NiBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDyxLx06CX2 +AGIo40zouN8Tn4sHN+9iSvFXCfaC6HXwCqknz5M77DaJpW4d1lTzuASXcrRpJczR +Qg5b1Rx/omBusVIa25MvuwsNZFMWyxwJJJUpIrSKGACJ/vcfcsjoXC8aG6IYuO8Y +XMu12zpO2w+u38R54x6qXKOk5axhmzeFj0h1G7nVaJbpJ3lwVyMau2yTkMdF1xfS +Nyp2s82CqU/AA3vhPXp+W7iF8vUV+3CpvfVQZRad47ZrYW6hep7oDRz3Ko5pfkMw +jnjO7mUeO5uHHkkc+DJGXShGeSpOJ10XWKg3/qgTqWkV3zYiiXW6ygFALu2d1wyq +Mc4nrlfV0lH7AgMBAAGjggE+MIIBOjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud +DwEB/wQEAwIBBjAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v +Y3NwLnRoYXd0ZS5jb20wOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEW +Gmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3BzMDcGA1UdHwQwMC4wLKAqoCiGJmh0 +dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVQQ0EtRzMuY3JsMCoGA1UdEQQjMCGk +HzAdMRswGQYDVQQDExJWZXJpU2lnbk1QS0ktMi0zNzQwHQYDVR0OBBYEFDskyDGg +t1rQarjSygd0zB4k1MTcMB8GA1UdIwQYMBaAFK1sqpRgnO3k//o+CnQrYwP3tlm/ +MA0GCSqGSIb3DQEBCwUAA4IBAQBomCaq1DPJunVw1J9JrdbBVNzuqlYfeKfwoaTu +C/kSr9+muO7DyzUTalkq+MnpTC+8sbwrwgIw4cO+wvCBjJl3iVgAo8x/owJMU7Ju +Nk/+34d2sz/sWmJQtgBFWPKHrHfm0CBQY8XksnAVGJAFe3uvK0a+a04fU/yEJ66D +0o1HU6cOH2O1utsW2GoJJVV9jz1KwYP5s7mnBFrI8xEEkVMw2VKHyzkAnOxTwwIJ +fqc2jnIhLyO7TMZHpaHuZ8QvXDpHOGHiwx43kp7IL2v679LDzSmNmPhSF+21Uzzf +r8kbYq3fAu5dNPZBS8vDVa+xy9qcc9UCqC2nrPzh5QfQUeg1 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert32[] = { + 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x03, 0xb8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x0a, 0x48, 0x9e, 0x88, 0x53, 0x7e, 0x8a, 0xa6, 0x45, + 0x4d, 0x6e, 0x2c, 0x4b, 0x2a, 0xeb, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x38, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x1b, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x34, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, + 0x17, 0x0d, 0x32, 0x33, 0x30, 0x34, 0x30, 0x38, 0x32, 0x33, 0x35, 0x39, + 0x35, 0x39, 0x5a, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x28, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, + 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xf2, 0xc4, 0xbc, 0x74, 0xe8, 0x25, 0xf6, + 0x00, 0x62, 0x28, 0xe3, 0x4c, 0xe8, 0xb8, 0xdf, 0x13, 0x9f, 0x8b, 0x07, + 0x37, 0xef, 0x62, 0x4a, 0xf1, 0x57, 0x09, 0xf6, 0x82, 0xe8, 0x75, 0xf0, + 0x0a, 0xa9, 0x27, 0xcf, 0x93, 0x3b, 0xec, 0x36, 0x89, 0xa5, 0x6e, 0x1d, + 0xd6, 0x54, 0xf3, 0xb8, 0x04, 0x97, 0x72, 0xb4, 0x69, 0x25, 0xcc, 0xd1, + 0x42, 0x0e, 0x5b, 0xd5, 0x1c, 0x7f, 0xa2, 0x60, 0x6e, 0xb1, 0x52, 0x1a, + 0xdb, 0x93, 0x2f, 0xbb, 0x0b, 0x0d, 0x64, 0x53, 0x16, 0xcb, 0x1c, 0x09, + 0x24, 0x95, 0x29, 0x22, 0xb4, 0x8a, 0x18, 0x00, 0x89, 0xfe, 0xf7, 0x1f, + 0x72, 0xc8, 0xe8, 0x5c, 0x2f, 0x1a, 0x1b, 0xa2, 0x18, 0xb8, 0xef, 0x18, + 0x5c, 0xcb, 0xb5, 0xdb, 0x3a, 0x4e, 0xdb, 0x0f, 0xae, 0xdf, 0xc4, 0x79, + 0xe3, 0x1e, 0xaa, 0x5c, 0xa3, 0xa4, 0xe5, 0xac, 0x61, 0x9b, 0x37, 0x85, + 0x8f, 0x48, 0x75, 0x1b, 0xb9, 0xd5, 0x68, 0x96, 0xe9, 0x27, 0x79, 0x70, + 0x57, 0x23, 0x1a, 0xbb, 0x6c, 0x93, 0x90, 0xc7, 0x45, 0xd7, 0x17, 0xd2, + 0x37, 0x2a, 0x76, 0xb3, 0xcd, 0x82, 0xa9, 0x4f, 0xc0, 0x03, 0x7b, 0xe1, + 0x3d, 0x7a, 0x7e, 0x5b, 0xb8, 0x85, 0xf2, 0xf5, 0x15, 0xfb, 0x70, 0xa9, + 0xbd, 0xf5, 0x50, 0x65, 0x16, 0x9d, 0xe3, 0xb6, 0x6b, 0x61, 0x6e, 0xa1, + 0x7a, 0x9e, 0xe8, 0x0d, 0x1c, 0xf7, 0x2a, 0x8e, 0x69, 0x7e, 0x43, 0x30, + 0x8e, 0x78, 0xce, 0xee, 0x65, 0x1e, 0x3b, 0x9b, 0x87, 0x1e, 0x49, 0x1c, + 0xf8, 0x32, 0x46, 0x5d, 0x28, 0x46, 0x79, 0x2a, 0x4e, 0x27, 0x5d, 0x17, + 0x58, 0xa8, 0x37, 0xfe, 0xa8, 0x13, 0xa9, 0x69, 0x15, 0xdf, 0x36, 0x22, + 0x89, 0x75, 0xba, 0xca, 0x01, 0x40, 0x2e, 0xed, 0x9d, 0xd7, 0x0c, 0xaa, + 0x31, 0xce, 0x27, 0xae, 0x57, 0xd5, 0xd2, 0x51, 0xfb, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x3e, 0x30, 0x82, 0x01, 0x3a, 0x30, 0x12, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x32, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x26, + 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, + 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, + 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, + 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, + 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x70, 0x73, 0x30, 0x37, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, + 0x30, 0x2e, 0x30, 0x2c, 0xa0, 0x2a, 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, + 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, + 0x74, 0x65, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4, + 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, + 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x33, 0x37, 0x34, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3b, 0x24, 0xc8, 0x31, 0xa0, + 0xb7, 0x5a, 0xd0, 0x6a, 0xb8, 0xd2, 0xca, 0x07, 0x74, 0xcc, 0x1e, 0x24, + 0xd4, 0xc4, 0xdc, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0xad, 0x6c, 0xaa, 0x94, 0x60, 0x9c, 0xed, 0xe4, + 0xff, 0xfa, 0x3e, 0x0a, 0x74, 0x2b, 0x63, 0x03, 0xf7, 0xb6, 0x59, 0xbf, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x68, 0x98, 0x26, 0xaa, + 0xd4, 0x33, 0xc9, 0xba, 0x75, 0x70, 0xd4, 0x9f, 0x49, 0xad, 0xd6, 0xc1, + 0x54, 0xdc, 0xee, 0xaa, 0x56, 0x1f, 0x78, 0xa7, 0xf0, 0xa1, 0xa4, 0xee, + 0x0b, 0xf9, 0x12, 0xaf, 0xdf, 0xa6, 0xb8, 0xee, 0xc3, 0xcb, 0x35, 0x13, + 0x6a, 0x59, 0x2a, 0xf8, 0xc9, 0xe9, 0x4c, 0x2f, 0xbc, 0xb1, 0xbc, 0x2b, + 0xc2, 0x02, 0x30, 0xe1, 0xc3, 0xbe, 0xc2, 0xf0, 0x81, 0x8c, 0x99, 0x77, + 0x89, 0x58, 0x00, 0xa3, 0xcc, 0x7f, 0xa3, 0x02, 0x4c, 0x53, 0xb2, 0x6e, + 0x36, 0x4f, 0xfe, 0xdf, 0x87, 0x76, 0xb3, 0x3f, 0xec, 0x5a, 0x62, 0x50, + 0xb6, 0x00, 0x45, 0x58, 0xf2, 0x87, 0xac, 0x77, 0xe6, 0xd0, 0x20, 0x50, + 0x63, 0xc5, 0xe4, 0xb2, 0x70, 0x15, 0x18, 0x90, 0x05, 0x7b, 0x7b, 0xaf, + 0x2b, 0x46, 0xbe, 0x6b, 0x4e, 0x1f, 0x53, 0xfc, 0x84, 0x27, 0xae, 0x83, + 0xd2, 0x8d, 0x47, 0x53, 0xa7, 0x0e, 0x1f, 0x63, 0xb5, 0xba, 0xdb, 0x16, + 0xd8, 0x6a, 0x09, 0x25, 0x55, 0x7d, 0x8f, 0x3d, 0x4a, 0xc1, 0x83, 0xf9, + 0xb3, 0xb9, 0xa7, 0x04, 0x5a, 0xc8, 0xf3, 0x11, 0x04, 0x91, 0x53, 0x30, + 0xd9, 0x52, 0x87, 0xcb, 0x39, 0x00, 0x9c, 0xec, 0x53, 0xc3, 0x02, 0x09, + 0x7e, 0xa7, 0x36, 0x8e, 0x72, 0x21, 0x2f, 0x23, 0xbb, 0x4c, 0xc6, 0x47, + 0xa5, 0xa1, 0xee, 0x67, 0xc4, 0x2f, 0x5c, 0x3a, 0x47, 0x38, 0x61, 0xe2, + 0xc3, 0x1e, 0x37, 0x92, 0x9e, 0xc8, 0x2f, 0x6b, 0xfa, 0xef, 0xd2, 0xc3, + 0xcd, 0x29, 0x8d, 0x98, 0xf8, 0x52, 0x17, 0xed, 0xb5, 0x53, 0x3c, 0xdf, + 0xaf, 0xc9, 0x1b, 0x62, 0xad, 0xdf, 0x02, 0xee, 0x5d, 0x34, 0xf6, 0x41, + 0x4b, 0xcb, 0xc3, 0x55, 0xaf, 0xb1, 0xcb, 0xda, 0x9c, 0x73, 0xd5, 0x02, + 0xa8, 0x2d, 0xa7, 0xac, 0xfc, 0xe1, 0xe5, 0x07, 0xd0, 0x51, 0xe8, 0x35, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 25:0c:e8:e0:30:61:2e:9f:2b:89:f7:05:4d:7c:f8:fd + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority + Validity + Not Before: Nov 8 00:00:00 2006 GMT + Not After : Nov 7 23:59:59 2021 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b: + 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57: + 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8: + 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe: + 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d: + a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59: + 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49: + d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69: + 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96: + bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5: + f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02: + ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6: + f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19: + 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d: + 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95: + ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f: + 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8: + 25:15 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.verisign.com/pca3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.verisign.com/cps + + X509v3 Subject Key Identifier: + 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + Authority Information Access: + OCSP - URI:http://ocsp.verisign.com + + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1 + Signature Algorithm: sha1WithRSAEncryption + 13:02:dd:f8:e8:86:00:f2:5a:f8:f8:20:0c:59:88:62:07:ce: + ce:f7:4e:f9:bb:59:a1:98:e5:e1:38:dd:4e:bc:66:18:d3:ad: + eb:18:f2:0d:c9:6d:3e:4a:94:20:c3:3c:ba:bd:65:54:c6:af: + 44:b3:10:ad:2c:6b:3e:ab:d7:07:b6:b8:81:63:c5:f9:5e:2e: + e5:2a:67:ce:cd:33:0c:2a:d7:89:56:03:23:1f:b3:be:e8:3a: + 08:59:b4:ec:45:35:f7:8a:5b:ff:66:cf:50:af:c6:6d:57:8d: + 19:78:b7:b9:a2:d1:57:ea:1f:9a:4b:af:ba:c9:8e:12:7e:c6: + bd:ff +-----BEGIN CERTIFICATE----- +MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT +LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv +ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8 +RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb +ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR +TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH +iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB +AAGjggGbMIIBlzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0 +dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9 +BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy +aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI +KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU +j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t +L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC52ZXJpc2lnbi5jb20wPgYDVR0lBDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMC +BggrBgEFBQcDAwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUA +A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K +lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ +tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/ +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert33[] = { + 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x04, 0x39, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x25, 0x0c, 0xe8, 0xe0, 0x30, 0x61, 0x2e, 0x9f, 0x2b, + 0x89, 0xf7, 0x05, 0x4d, 0x7c, 0xf8, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, + 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30, + 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, + 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, + 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, + 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30, + 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35, + 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c, + 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3, + 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22, + 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1, + 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb, + 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0, + 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85, + 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33, + 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51, + 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74, + 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0, + 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06, + 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff, + 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4, + 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19, + 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe, + 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47, + 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5, + 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14, + 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f, + 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x9b, 0x30, 0x82, 0x01, 0x97, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, + 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, + 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, + 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, + 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, + 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, + 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, + 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, + 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d, 0x25, + 0x04, 0x37, 0x30, 0x35, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60, + 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x81, 0x81, 0x00, 0x13, 0x02, 0xdd, 0xf8, 0xe8, 0x86, 0x00, 0xf2, + 0x5a, 0xf8, 0xf8, 0x20, 0x0c, 0x59, 0x88, 0x62, 0x07, 0xce, 0xce, 0xf7, + 0x4e, 0xf9, 0xbb, 0x59, 0xa1, 0x98, 0xe5, 0xe1, 0x38, 0xdd, 0x4e, 0xbc, + 0x66, 0x18, 0xd3, 0xad, 0xeb, 0x18, 0xf2, 0x0d, 0xc9, 0x6d, 0x3e, 0x4a, + 0x94, 0x20, 0xc3, 0x3c, 0xba, 0xbd, 0x65, 0x54, 0xc6, 0xaf, 0x44, 0xb3, + 0x10, 0xad, 0x2c, 0x6b, 0x3e, 0xab, 0xd7, 0x07, 0xb6, 0xb8, 0x81, 0x63, + 0xc5, 0xf9, 0x5e, 0x2e, 0xe5, 0x2a, 0x67, 0xce, 0xcd, 0x33, 0x0c, 0x2a, + 0xd7, 0x89, 0x56, 0x03, 0x23, 0x1f, 0xb3, 0xbe, 0xe8, 0x3a, 0x08, 0x59, + 0xb4, 0xec, 0x45, 0x35, 0xf7, 0x8a, 0x5b, 0xff, 0x66, 0xcf, 0x50, 0xaf, + 0xc6, 0x6d, 0x57, 0x8d, 0x19, 0x78, 0xb7, 0xb9, 0xa2, 0xd1, 0x57, 0xea, + 0x1f, 0x9a, 0x4b, 0xaf, 0xba, 0xc9, 0x8e, 0x12, 0x7e, 0xc6, 0xbd, 0xff, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 2c:69:e1:2f:6a:67:0b:d9:9d:d2:0f:91:9e:f0:9e:51 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Jun 10 00:00:00 2014 GMT + Not After : Jun 9 23:59:59 2024 GMT + Subject: C=US, O=thawte, Inc., OU=Domain Validated SSL, CN=thawte DV SSL CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ea:94:07:85:c8:41:2c:f6:83:12:6c:92:5f:ab: + 1f:00:d4:96:6f:74:cd:2e:11:e9:6c:0f:39:01:b9: + 48:90:40:39:4d:c4:a2:c8:79:6a:a5:9a:bd:91:44: + 65:77:54:ad:ff:25:5f:ee:42:fb:b3:02:0f:ea:5d: + 7a:dd:1a:54:9e:d7:73:42:9b:cc:79:5f:c5:4d:f4: + b7:0b:18:39:20:7a:dd:50:01:5d:34:45:5f:4c:11: + 0e:f5:87:26:26:b4:b0:f3:7e:71:a0:31:71:50:89: + 68:5a:63:8a:14:62:e5:8c:3a:16:55:0d:3e:eb:aa: + 80:1d:71:7a:e3:87:07:ab:bd:a2:74:cd:da:08:01: + 9d:1b:cc:27:88:8c:47:d4:69:25:42:d6:bb:50:6d: + 85:50:d0:48:82:0d:08:9f:e9:23:e3:42:c6:3c:98: + b8:bb:6e:c5:70:13:df:19:1d:01:fd:d2:b5:4e:e6: + 62:f4:07:fa:6b:7d:11:77:c4:62:4f:40:4e:a5:78: + 97:ab:2c:4d:0c:a7:7c:c3:c4:50:32:9f:d0:70:9b: + 0f:ff:ff:75:59:34:85:ad:49:d5:35:ee:4f:5b:d4: + d4:36:95:a0:7e:e8:c5:a1:1c:bd:13:4e:7d:ee:63: + 6a:96:19:99:c8:a7:2a:00:e6:51:8d:46:eb:30:58: + e8:2d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: https://www.thawte.com/cps + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://t.symcd.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://t.symcb.com/ThawtePCA.crl + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-698 + X509v3 Subject Key Identifier: + 9F:B8:C1:A9:6C:F2:F5:C0:22:2A:94:ED:5C:99:AC:D4:EC:D7:C6:07 + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha256WithRSAEncryption + 53:54:f2:47:a8:02:d7:ef:aa:35:78:be:4a:08:0d:90:18:4b: + 6d:9e:2a:53:2b:e9:54:17:77:74:29:7e:d0:37:07:05:b8:e4: + fa:b8:b4:63:98:44:dc:c6:4f:81:06:8c:3a:be:c7:30:57:c6: + 70:fc:d6:93:19:9f:c3:55:d7:3e:1f:72:8a:9d:30:5a:35:97: + 32:cb:63:e4:c6:72:df:fb:68:ca:69:2f:db:cd:50:38:3e:2b: + bb:ab:3b:82:c7:fd:4b:9b:bd:7c:41:98:ef:01:53:d8:35:8f: + 25:c9:03:06:e6:9c:57:c1:51:0f:9e:f6:7d:93:4d:f8:76:c8: + 3a:6b:f4:c4:8f:33:32:7f:9d:21:84:34:d9:a7:f9:92:fa:41: + 91:61:84:05:9d:a3:79:46:ce:67:e7:81:f2:5e:ac:4c:bc:a8: + ab:6a:6d:15:e2:9c:4e:5a:d9:63:80:bc:f7:42:eb:9a:44:c6: + 8c:6b:06:36:b4:8b:32:89:de:c2:f1:a8:26:aa:a9:ac:ff:ea: + 71:a6:e7:8c:41:fa:17:35:bb:b3:87:31:a9:93:c2:c8:58:e1: + 0a:4e:95:83:9c:b9:ed:3b:a5:ef:08:e0:74:f9:c3:1b:e6:07: + a3:ee:07:d7:42:22:79:21:a0:a1:d4:1d:26:d3:d0:d6:a6:5d: + 2b:41:c0:79 +-----BEGIN CERTIFICATE----- +MIIE0jCCA7qgAwIBAgIQLGnhL2pnC9md0g+RnvCeUTANBgkqhkiG9w0BAQsFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTQwNjEwMDAwMDAwWhcNMjQw +NjA5MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu +MR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEeMBwGA1UEAxMVdGhhd3Rl +IERWIFNTTCBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +6pQHhchBLPaDEmySX6sfANSWb3TNLhHpbA85AblIkEA5TcSiyHlqpZq9kURld1St +/yVf7kL7swIP6l163RpUntdzQpvMeV/FTfS3Cxg5IHrdUAFdNEVfTBEO9YcmJrSw +835xoDFxUIloWmOKFGLljDoWVQ0+66qAHXF644cHq72idM3aCAGdG8wniIxH1Gkl +Qta7UG2FUNBIgg0In+kj40LGPJi4u27FcBPfGR0B/dK1TuZi9Af6a30Rd8RiT0BO +pXiXqyxNDKd8w8RQMp/QcJsP//91WTSFrUnVNe5PW9TUNpWgfujFoRy9E0597mNq +lhmZyKcqAOZRjUbrMFjoLQIDAQABo4IBOTCCATUwEgYDVR0TAQH/BAgwBgEB/wIB +ADBBBgNVHSAEOjA4MDYGCmCGSAGG+EUBBzYwKDAmBggrBgEFBQcCARYaaHR0cHM6 +Ly93d3cudGhhd3RlLmNvbS9jcHMwDgYDVR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEB +BCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL3Quc3ltY2QuY29tMDEGA1UdHwQqMCgw +JqAkoCKGIGh0dHA6Ly90LnN5bWNiLmNvbS9UaGF3dGVQQ0EuY3JsMCkGA1UdEQQi +MCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTY5ODAdBgNVHQ4EFgQUn7jB +qWzy9cAiKpTtXJms1OzXxgcwHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutX +SFAwDQYJKoZIhvcNAQELBQADggEBAFNU8keoAtfvqjV4vkoIDZAYS22eKlMr6VQX +d3QpftA3BwW45Pq4tGOYRNzGT4EGjDq+xzBXxnD81pMZn8NV1z4fcoqdMFo1lzLL +Y+TGct/7aMppL9vNUDg+K7urO4LH/UubvXxBmO8BU9g1jyXJAwbmnFfBUQ+e9n2T +Tfh2yDpr9MSPMzJ/nSGENNmn+ZL6QZFhhAWdo3lGzmfngfJerEy8qKtqbRXinE5a +2WOAvPdC65pExoxrBja0izKJ3sLxqCaqqaz/6nGm54xB+hc1u7OHMamTwshY4QpO +lYOcue07pe8I4HT5wxvmB6PuB9dCInkhoKHUHSbT0NamXStBwHk= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert34[] = { + 0x30, 0x82, 0x04, 0xd2, 0x30, 0x82, 0x03, 0xba, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x2c, 0x69, 0xe1, 0x2f, 0x6a, 0x67, 0x0b, 0xd9, 0x9d, + 0xd2, 0x0f, 0x91, 0x9e, 0xf0, 0x9e, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, + 0x36, 0x30, 0x39, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x63, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x14, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, + 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xea, 0x94, 0x07, 0x85, 0xc8, 0x41, 0x2c, 0xf6, 0x83, 0x12, 0x6c, 0x92, + 0x5f, 0xab, 0x1f, 0x00, 0xd4, 0x96, 0x6f, 0x74, 0xcd, 0x2e, 0x11, 0xe9, + 0x6c, 0x0f, 0x39, 0x01, 0xb9, 0x48, 0x90, 0x40, 0x39, 0x4d, 0xc4, 0xa2, + 0xc8, 0x79, 0x6a, 0xa5, 0x9a, 0xbd, 0x91, 0x44, 0x65, 0x77, 0x54, 0xad, + 0xff, 0x25, 0x5f, 0xee, 0x42, 0xfb, 0xb3, 0x02, 0x0f, 0xea, 0x5d, 0x7a, + 0xdd, 0x1a, 0x54, 0x9e, 0xd7, 0x73, 0x42, 0x9b, 0xcc, 0x79, 0x5f, 0xc5, + 0x4d, 0xf4, 0xb7, 0x0b, 0x18, 0x39, 0x20, 0x7a, 0xdd, 0x50, 0x01, 0x5d, + 0x34, 0x45, 0x5f, 0x4c, 0x11, 0x0e, 0xf5, 0x87, 0x26, 0x26, 0xb4, 0xb0, + 0xf3, 0x7e, 0x71, 0xa0, 0x31, 0x71, 0x50, 0x89, 0x68, 0x5a, 0x63, 0x8a, + 0x14, 0x62, 0xe5, 0x8c, 0x3a, 0x16, 0x55, 0x0d, 0x3e, 0xeb, 0xaa, 0x80, + 0x1d, 0x71, 0x7a, 0xe3, 0x87, 0x07, 0xab, 0xbd, 0xa2, 0x74, 0xcd, 0xda, + 0x08, 0x01, 0x9d, 0x1b, 0xcc, 0x27, 0x88, 0x8c, 0x47, 0xd4, 0x69, 0x25, + 0x42, 0xd6, 0xbb, 0x50, 0x6d, 0x85, 0x50, 0xd0, 0x48, 0x82, 0x0d, 0x08, + 0x9f, 0xe9, 0x23, 0xe3, 0x42, 0xc6, 0x3c, 0x98, 0xb8, 0xbb, 0x6e, 0xc5, + 0x70, 0x13, 0xdf, 0x19, 0x1d, 0x01, 0xfd, 0xd2, 0xb5, 0x4e, 0xe6, 0x62, + 0xf4, 0x07, 0xfa, 0x6b, 0x7d, 0x11, 0x77, 0xc4, 0x62, 0x4f, 0x40, 0x4e, + 0xa5, 0x78, 0x97, 0xab, 0x2c, 0x4d, 0x0c, 0xa7, 0x7c, 0xc3, 0xc4, 0x50, + 0x32, 0x9f, 0xd0, 0x70, 0x9b, 0x0f, 0xff, 0xff, 0x75, 0x59, 0x34, 0x85, + 0xad, 0x49, 0xd5, 0x35, 0xee, 0x4f, 0x5b, 0xd4, 0xd4, 0x36, 0x95, 0xa0, + 0x7e, 0xe8, 0xc5, 0xa1, 0x1c, 0xbd, 0x13, 0x4e, 0x7d, 0xee, 0x63, 0x6a, + 0x96, 0x19, 0x99, 0xc8, 0xa7, 0x2a, 0x00, 0xe6, 0x51, 0x8d, 0x46, 0xeb, + 0x30, 0x58, 0xe8, 0x2d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, + 0x39, 0x30, 0x82, 0x01, 0x35, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38, + 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, + 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, 0x30, 0x28, 0x30, + 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, + 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, + 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x36, 0x39, 0x38, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9f, 0xb8, 0xc1, + 0xa9, 0x6c, 0xf2, 0xf5, 0xc0, 0x22, 0x2a, 0x94, 0xed, 0x5c, 0x99, 0xac, + 0xd4, 0xec, 0xd7, 0xc6, 0x07, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, + 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, + 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, + 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x53, 0x54, + 0xf2, 0x47, 0xa8, 0x02, 0xd7, 0xef, 0xaa, 0x35, 0x78, 0xbe, 0x4a, 0x08, + 0x0d, 0x90, 0x18, 0x4b, 0x6d, 0x9e, 0x2a, 0x53, 0x2b, 0xe9, 0x54, 0x17, + 0x77, 0x74, 0x29, 0x7e, 0xd0, 0x37, 0x07, 0x05, 0xb8, 0xe4, 0xfa, 0xb8, + 0xb4, 0x63, 0x98, 0x44, 0xdc, 0xc6, 0x4f, 0x81, 0x06, 0x8c, 0x3a, 0xbe, + 0xc7, 0x30, 0x57, 0xc6, 0x70, 0xfc, 0xd6, 0x93, 0x19, 0x9f, 0xc3, 0x55, + 0xd7, 0x3e, 0x1f, 0x72, 0x8a, 0x9d, 0x30, 0x5a, 0x35, 0x97, 0x32, 0xcb, + 0x63, 0xe4, 0xc6, 0x72, 0xdf, 0xfb, 0x68, 0xca, 0x69, 0x2f, 0xdb, 0xcd, + 0x50, 0x38, 0x3e, 0x2b, 0xbb, 0xab, 0x3b, 0x82, 0xc7, 0xfd, 0x4b, 0x9b, + 0xbd, 0x7c, 0x41, 0x98, 0xef, 0x01, 0x53, 0xd8, 0x35, 0x8f, 0x25, 0xc9, + 0x03, 0x06, 0xe6, 0x9c, 0x57, 0xc1, 0x51, 0x0f, 0x9e, 0xf6, 0x7d, 0x93, + 0x4d, 0xf8, 0x76, 0xc8, 0x3a, 0x6b, 0xf4, 0xc4, 0x8f, 0x33, 0x32, 0x7f, + 0x9d, 0x21, 0x84, 0x34, 0xd9, 0xa7, 0xf9, 0x92, 0xfa, 0x41, 0x91, 0x61, + 0x84, 0x05, 0x9d, 0xa3, 0x79, 0x46, 0xce, 0x67, 0xe7, 0x81, 0xf2, 0x5e, + 0xac, 0x4c, 0xbc, 0xa8, 0xab, 0x6a, 0x6d, 0x15, 0xe2, 0x9c, 0x4e, 0x5a, + 0xd9, 0x63, 0x80, 0xbc, 0xf7, 0x42, 0xeb, 0x9a, 0x44, 0xc6, 0x8c, 0x6b, + 0x06, 0x36, 0xb4, 0x8b, 0x32, 0x89, 0xde, 0xc2, 0xf1, 0xa8, 0x26, 0xaa, + 0xa9, 0xac, 0xff, 0xea, 0x71, 0xa6, 0xe7, 0x8c, 0x41, 0xfa, 0x17, 0x35, + 0xbb, 0xb3, 0x87, 0x31, 0xa9, 0x93, 0xc2, 0xc8, 0x58, 0xe1, 0x0a, 0x4e, + 0x95, 0x83, 0x9c, 0xb9, 0xed, 0x3b, 0xa5, 0xef, 0x08, 0xe0, 0x74, 0xf9, + 0xc3, 0x1b, 0xe6, 0x07, 0xa3, 0xee, 0x07, 0xd7, 0x42, 0x22, 0x79, 0x21, + 0xa0, 0xa1, 0xd4, 0x1d, 0x26, 0xd3, 0xd0, 0xd6, 0xa6, 0x5d, 0x2b, 0x41, + 0xc0, 0x79, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 4f:e3:e2:65:21:07:ab:20:37:41:6e:48:70:ce:d2:c2 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root + Validity + Not Before: May 25 00:00:00 2010 GMT + Not After : May 30 10:48:38 2020 GMT + Subject: C=US, O=Trusted Secure Certificate Authority, CN=Trusted Secure Certificate Authority + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:80:0b:42:c6:06:6c:cf:22:b3:1a:9e:11:2e:42: + 6e:39:bf:e8:12:af:3c:42:21:12:95:40:5d:32:b1: + 6d:1c:21:d1:34:e5:4f:a8:d1:43:a2:26:4e:30:7d: + 73:44:2c:73:aa:c5:4d:66:01:19:d2:ea:50:59:65: + d0:68:9d:05:a0:7c:a1:79:53:d0:21:90:59:0e:37: + db:1e:dc:92:a7:8b:0d:c4:f5:f8:e6:ff:b5:35:1a: + da:a8:b6:9b:20:85:65:c4:a2:4d:df:f3:94:4d:63: + 7e:ee:89:07:af:fe:e1:ba:00:15:2d:c6:77:8e:a3: + fe:ad:cf:26:54:5a:df:fc:d2:de:c2:ad:f6:b2:23: + fd:a8:83:e5:65:bd:27:f7:27:1a:18:59:6a:9e:14: + f6:b4:86:ff:1c:58:14:43:73:96:24:bf:10:43:d5: + 5c:89:f0:ce:f7:e1:96:16:5e:18:4a:27:28:90:80: + 18:fc:32:fe:f4:c7:b8:d6:82:3d:35:af:bb:4a:1c: + 5b:05:78:f6:fd:55:3e:82:74:b2:73:b8:89:4e:f7: + 1b:85:9a:d8:ca:b1:5a:b1:00:20:41:14:30:2b:14: + 24:ed:37:0e:32:3e:23:88:39:7e:b9:d9:38:03:e2: + 4c:d9:0d:43:41:33:10:eb:30:72:53:88:f7:52:9b: + 4f:81 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A + + X509v3 Subject Key Identifier: + CC:03:5B:96:5A:9E:16:CC:26:1E:BD:A3:70:FB:E3:CB:79:19:FC:4D + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6449.1.2.2.8 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl + + Authority Information Access: + CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c + CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt + OCSP - URI:http://ocsp.usertrust.com + + Signature Algorithm: sha1WithRSAEncryption + 7b:f0:fc:a1:28:47:bc:2b:b4:04:73:3f:4b:dd:1e:d1:b9:cd: + 1c:ed:7d:e5:e8:cb:51:f4:92:bf:dd:9c:0d:5c:6e:1d:95:ed: + 5b:70:50:89:d4:67:9a:15:54:d1:90:0a:fa:09:68:06:18:bb: + d7:27:e4:93:ff:43:48:81:3b:c8:59:49:35:ea:ac:b6:ae:46: + b5:d4:f3:b8:c3:c6:e4:91:bf:c9:34:fd:7e:d0:59:6e:61:a1: + 1f:48:63:54:b2:7d:46:bf:c8:fa:c3:bf:48:58:98:f6:69:84: + a7:16:69:08:27:a4:22:cb:a2:2c:c8:df:6e:a9:ee:f8:41:df: + 1b:a8:b7:f3:e3:ae:ce:a3:fe:d9:27:60:50:3f:04:7d:7a:44: + ea:76:42:5c:d3:55:46:ef:27:c5:6a:4a:80:e7:35:a0:91:c6: + 1b:a6:86:9c:5a:3b:04:83:54:34:d7:d1:88:a6:36:e9:7f:40: + 27:da:56:0a:50:21:9d:29:8b:a0:84:ec:fe:71:23:53:04:18: + 19:70:67:86:44:95:72:40:55:f6:dd:a3:b4:3d:2d:09:60:a5: + e7:5f:fc:ac:3b:ec:0c:91:9f:f8:ee:6a:ba:b2:3c:fd:95:7d: + 9a:07:f4:b0:65:43:a2:f6:df:7d:b8:21:49:84:04:ee:bd:ce: + 53:8f:0f:29 +-----BEGIN CERTIFICATE----- +MIIE5DCCA8ygAwIBAgIQT+PiZSEHqyA3QW5IcM7SwjANBgkqhkiG9w0BAQUFADBv +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk +ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF +eHRlcm5hbCBDQSBSb290MB4XDTEwMDUyNTAwMDAwMFoXDTIwMDUzMDEwNDgzOFow +azELMAkGA1UEBhMCVVMxLTArBgNVBAoTJFRydXN0ZWQgU2VjdXJlIENlcnRpZmlj +YXRlIEF1dGhvcml0eTEtMCsGA1UEAxMkVHJ1c3RlZCBTZWN1cmUgQ2VydGlmaWNh +dGUgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgAtC +xgZszyKzGp4RLkJuOb/oEq88QiESlUBdMrFtHCHRNOVPqNFDoiZOMH1zRCxzqsVN +ZgEZ0upQWWXQaJ0FoHyheVPQIZBZDjfbHtySp4sNxPX45v+1NRraqLabIIVlxKJN +3/OUTWN+7okHr/7hugAVLcZ3jqP+rc8mVFrf/NLewq32siP9qIPlZb0n9ycaGFlq +nhT2tIb/HFgUQ3OWJL8QQ9VcifDO9+GWFl4YSicokIAY/DL+9Me41oI9Na+7Shxb +BXj2/VU+gnSyc7iJTvcbhZrYyrFasQAgQRQwKxQk7TcOMj4jiDl+udk4A+JM2Q1D +QTMQ6zByU4j3UptPgQIDAQABo4IBfjCCAXowHwYDVR0jBBgwFoAUrb2YejS0Jvf6 +xCZU7wO94CTLVBowHQYDVR0OBBYEFMwDW5ZanhbMJh69o3D748t5GfxNMA4GA1Ud +DwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMBgGA1UdIAQRMA8wDQYLKwYB +BAGyMQECAggwRAYDVR0fBD0wOzA5oDegNYYzaHR0cDovL2NybC51c2VydHJ1c3Qu +Y29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMIGzBggrBgEFBQcBAQSBpjCB +ozA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0 +RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsGAQUFBzAChi1odHRwOi8vY3J0LnVzZXJ0 +cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0NDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6 +Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEFBQADggEBAHvw/KEoR7wr +tARzP0vdHtG5zRztfeXoy1H0kr/dnA1cbh2V7VtwUInUZ5oVVNGQCvoJaAYYu9cn +5JP/Q0iBO8hZSTXqrLauRrXU87jDxuSRv8k0/X7QWW5hoR9IY1SyfUa/yPrDv0hY +mPZphKcWaQgnpCLLoizI326p7vhB3xuot/Pjrs6j/tknYFA/BH16ROp2QlzTVUbv +J8VqSoDnNaCRxhumhpxaOwSDVDTX0YimNul/QCfaVgpQIZ0pi6CE7P5xI1MEGBlw +Z4ZElXJAVfbdo7Q9LQlgpedf/Kw77AyRn/juarqyPP2VfZoH9LBlQ6L23324IUmE +BO69zlOPDyk= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert35[] = { + 0x30, 0x82, 0x04, 0xe4, 0x30, 0x82, 0x03, 0xcc, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x4f, 0xe3, 0xe2, 0x65, 0x21, 0x07, 0xab, 0x20, 0x37, + 0x41, 0x6e, 0x48, 0x70, 0xce, 0xd2, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53, + 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, + 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31, + 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64, + 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52, + 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x35, 0x32, + 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, + 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30, + 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x24, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, + 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x80, 0x0b, 0x42, + 0xc6, 0x06, 0x6c, 0xcf, 0x22, 0xb3, 0x1a, 0x9e, 0x11, 0x2e, 0x42, 0x6e, + 0x39, 0xbf, 0xe8, 0x12, 0xaf, 0x3c, 0x42, 0x21, 0x12, 0x95, 0x40, 0x5d, + 0x32, 0xb1, 0x6d, 0x1c, 0x21, 0xd1, 0x34, 0xe5, 0x4f, 0xa8, 0xd1, 0x43, + 0xa2, 0x26, 0x4e, 0x30, 0x7d, 0x73, 0x44, 0x2c, 0x73, 0xaa, 0xc5, 0x4d, + 0x66, 0x01, 0x19, 0xd2, 0xea, 0x50, 0x59, 0x65, 0xd0, 0x68, 0x9d, 0x05, + 0xa0, 0x7c, 0xa1, 0x79, 0x53, 0xd0, 0x21, 0x90, 0x59, 0x0e, 0x37, 0xdb, + 0x1e, 0xdc, 0x92, 0xa7, 0x8b, 0x0d, 0xc4, 0xf5, 0xf8, 0xe6, 0xff, 0xb5, + 0x35, 0x1a, 0xda, 0xa8, 0xb6, 0x9b, 0x20, 0x85, 0x65, 0xc4, 0xa2, 0x4d, + 0xdf, 0xf3, 0x94, 0x4d, 0x63, 0x7e, 0xee, 0x89, 0x07, 0xaf, 0xfe, 0xe1, + 0xba, 0x00, 0x15, 0x2d, 0xc6, 0x77, 0x8e, 0xa3, 0xfe, 0xad, 0xcf, 0x26, + 0x54, 0x5a, 0xdf, 0xfc, 0xd2, 0xde, 0xc2, 0xad, 0xf6, 0xb2, 0x23, 0xfd, + 0xa8, 0x83, 0xe5, 0x65, 0xbd, 0x27, 0xf7, 0x27, 0x1a, 0x18, 0x59, 0x6a, + 0x9e, 0x14, 0xf6, 0xb4, 0x86, 0xff, 0x1c, 0x58, 0x14, 0x43, 0x73, 0x96, + 0x24, 0xbf, 0x10, 0x43, 0xd5, 0x5c, 0x89, 0xf0, 0xce, 0xf7, 0xe1, 0x96, + 0x16, 0x5e, 0x18, 0x4a, 0x27, 0x28, 0x90, 0x80, 0x18, 0xfc, 0x32, 0xfe, + 0xf4, 0xc7, 0xb8, 0xd6, 0x82, 0x3d, 0x35, 0xaf, 0xbb, 0x4a, 0x1c, 0x5b, + 0x05, 0x78, 0xf6, 0xfd, 0x55, 0x3e, 0x82, 0x74, 0xb2, 0x73, 0xb8, 0x89, + 0x4e, 0xf7, 0x1b, 0x85, 0x9a, 0xd8, 0xca, 0xb1, 0x5a, 0xb1, 0x00, 0x20, + 0x41, 0x14, 0x30, 0x2b, 0x14, 0x24, 0xed, 0x37, 0x0e, 0x32, 0x3e, 0x23, + 0x88, 0x39, 0x7e, 0xb9, 0xd9, 0x38, 0x03, 0xe2, 0x4c, 0xd9, 0x0d, 0x43, + 0x41, 0x33, 0x10, 0xeb, 0x30, 0x72, 0x53, 0x88, 0xf7, 0x52, 0x9b, 0x4f, + 0x81, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x7e, 0x30, 0x82, + 0x01, 0x7a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, + 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xcc, 0x03, + 0x5b, 0x96, 0x5a, 0x9e, 0x16, 0xcc, 0x26, 0x1e, 0xbd, 0xa3, 0x70, 0xfb, + 0xe3, 0xcb, 0x79, 0x19, 0xfc, 0x4d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x18, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b, 0x06, 0x01, + 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02, 0x08, 0x30, 0x44, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0, + 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, + 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81, + 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, + 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, + 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43, + 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x7b, 0xf0, 0xfc, 0xa1, 0x28, 0x47, 0xbc, 0x2b, + 0xb4, 0x04, 0x73, 0x3f, 0x4b, 0xdd, 0x1e, 0xd1, 0xb9, 0xcd, 0x1c, 0xed, + 0x7d, 0xe5, 0xe8, 0xcb, 0x51, 0xf4, 0x92, 0xbf, 0xdd, 0x9c, 0x0d, 0x5c, + 0x6e, 0x1d, 0x95, 0xed, 0x5b, 0x70, 0x50, 0x89, 0xd4, 0x67, 0x9a, 0x15, + 0x54, 0xd1, 0x90, 0x0a, 0xfa, 0x09, 0x68, 0x06, 0x18, 0xbb, 0xd7, 0x27, + 0xe4, 0x93, 0xff, 0x43, 0x48, 0x81, 0x3b, 0xc8, 0x59, 0x49, 0x35, 0xea, + 0xac, 0xb6, 0xae, 0x46, 0xb5, 0xd4, 0xf3, 0xb8, 0xc3, 0xc6, 0xe4, 0x91, + 0xbf, 0xc9, 0x34, 0xfd, 0x7e, 0xd0, 0x59, 0x6e, 0x61, 0xa1, 0x1f, 0x48, + 0x63, 0x54, 0xb2, 0x7d, 0x46, 0xbf, 0xc8, 0xfa, 0xc3, 0xbf, 0x48, 0x58, + 0x98, 0xf6, 0x69, 0x84, 0xa7, 0x16, 0x69, 0x08, 0x27, 0xa4, 0x22, 0xcb, + 0xa2, 0x2c, 0xc8, 0xdf, 0x6e, 0xa9, 0xee, 0xf8, 0x41, 0xdf, 0x1b, 0xa8, + 0xb7, 0xf3, 0xe3, 0xae, 0xce, 0xa3, 0xfe, 0xd9, 0x27, 0x60, 0x50, 0x3f, + 0x04, 0x7d, 0x7a, 0x44, 0xea, 0x76, 0x42, 0x5c, 0xd3, 0x55, 0x46, 0xef, + 0x27, 0xc5, 0x6a, 0x4a, 0x80, 0xe7, 0x35, 0xa0, 0x91, 0xc6, 0x1b, 0xa6, + 0x86, 0x9c, 0x5a, 0x3b, 0x04, 0x83, 0x54, 0x34, 0xd7, 0xd1, 0x88, 0xa6, + 0x36, 0xe9, 0x7f, 0x40, 0x27, 0xda, 0x56, 0x0a, 0x50, 0x21, 0x9d, 0x29, + 0x8b, 0xa0, 0x84, 0xec, 0xfe, 0x71, 0x23, 0x53, 0x04, 0x18, 0x19, 0x70, + 0x67, 0x86, 0x44, 0x95, 0x72, 0x40, 0x55, 0xf6, 0xdd, 0xa3, 0xb4, 0x3d, + 0x2d, 0x09, 0x60, 0xa5, 0xe7, 0x5f, 0xfc, 0xac, 0x3b, 0xec, 0x0c, 0x91, + 0x9f, 0xf8, 0xee, 0x6a, 0xba, 0xb2, 0x3c, 0xfd, 0x95, 0x7d, 0x9a, 0x07, + 0xf4, 0xb0, 0x65, 0x43, 0xa2, 0xf6, 0xdf, 0x7d, 0xb8, 0x21, 0x49, 0x84, + 0x04, 0xee, 0xbd, 0xce, 0x53, 0x8f, 0x0f, 0x29, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 946072060 (0x3863e9fc) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048) + Validity + Not Before: Dec 10 20:43:54 2009 GMT + Not After : Dec 10 21:13:54 2019 GMT + Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/rpa is incorporated by reference, OU=(c) 2009 Entrust, Inc., CN=Entrust Certification Authority - L1C + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:97:a3:2d:3c:9e:de:05:da:13:c2:11:8d:9d:8e: + e3:7f:c7:4b:7e:5a:9f:b3:ff:62:ab:73:c8:28:6b: + ba:10:64:82:87:13:cd:57:18:ff:28:ce:c0:e6:0e: + 06:91:50:29:83:d1:f2:c3:2a:db:d8:db:4e:04:cc: + 00:eb:8b:b6:96:dc:bc:aa:fa:52:77:04:c1:db:19: + e4:ae:9c:fd:3c:8b:03:ef:4d:bc:1a:03:65:f9:c1: + b1:3f:72:86:f2:38:aa:19:ae:10:88:78:28:da:75: + c3:3d:02:82:02:9c:b9:c1:65:77:76:24:4c:98:f7: + 6d:31:38:fb:db:fe:db:37:02:76:a1:18:97:a6:cc: + de:20:09:49:36:24:69:42:f6:e4:37:62:f1:59:6d: + a9:3c:ed:34:9c:a3:8e:db:dc:3a:d7:f7:0a:6f:ef: + 2e:d8:d5:93:5a:7a:ed:08:49:68:e2:41:e3:5a:90: + c1:86:55:fc:51:43:9d:e0:b2:c4:67:b4:cb:32:31: + 25:f0:54:9f:4b:d1:6f:db:d4:dd:fc:af:5e:6c:78: + 90:95:de:ca:3a:48:b9:79:3c:9b:19:d6:75:05:a0: + f9:88:d7:c1:e8:a5:09:e4:1a:15:dc:87:23:aa:b2: + 75:8c:63:25:87:d8:f8:3d:a6:c2:cc:66:ff:a5:66: + 68:55 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.entrust.net/2048ca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/rpa + + X509v3 Subject Key Identifier: + 1E:F1:AB:89:06:F8:49:0F:01:33:77:EE:14:7A:EE:19:7C:93:28:4D + X509v3 Authority Key Identifier: + keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70 + + Signature Algorithm: sha1WithRSAEncryption + 07:f6:5f:82:84:7f:80:40:c7:90:34:46:42:24:03:ce:2f:ab: + ba:83:9e:25:73:0d:ed:ac:05:69:c6:87:ed:a3:5c:f2:57:c1: + b1:49:76:9a:4d:f2:3f:dd:e4:0e:fe:0b:3e:b9:98:d9:32:95: + 1d:32:f4:01:ee:9c:c8:c8:e5:3f:e0:53:76:62:fc:dd:ab:6d: + 3d:94:90:f2:c0:b3:3c:98:27:36:5e:28:97:22:fc:1b:40:d3: + 2b:0d:ad:b5:57:6d:df:0f:e3:4b:ef:73:02:10:65:fa:1b:d0: + ac:31:d5:e3:0f:e8:ba:32:30:83:ee:4a:d0:bf:df:22:90:7a: + be:ec:3a:1b:c4:49:04:1d:f1:ae:80:77:3c:42:08:db:a7:3b: + 28:a6:80:01:03:e6:39:a3:eb:df:80:59:1b:f3:2c:be:dc:72: + 44:79:a0:6c:07:a5:6d:4d:44:8e:42:68:ca:94:7c:2e:36:ba: + 85:9e:cd:aa:c4:5e:3c:54:be:fe:2f:ea:69:9d:1c:1e:29:9b: + 96:d8:c8:fe:51:90:f1:24:a6:90:06:b3:f0:29:a2:ff:78:2e: + 77:5c:45:21:d9:44:00:31:f3:be:32:4f:f5:0a:32:0d:fc:fc: + ba:16:76:56:b2:d6:48:92:f2:8b:a6:3e:b7:ac:5c:69:ea:0b: + 3f:66:45:b9 +-----BEGIN CERTIFICATE----- +MIIE8jCCA9qgAwIBAgIEOGPp/DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0wOTEyMTAyMDQzNTRaFw0xOTEy +MTAyMTEzNTRaMIGxMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5j +LjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L3JwYSBpcyBpbmNvcnBvcmF0ZWQg +YnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwOSBFbnRydXN0LCBJbmMuMS4w +LAYDVQQDEyVFbnRydXN0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gTDFDMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl6MtPJ7eBdoTwhGNnY7jf8dL +flqfs/9iq3PIKGu6EGSChxPNVxj/KM7A5g4GkVApg9Hywyrb2NtOBMwA64u2lty8 +qvpSdwTB2xnkrpz9PIsD7028GgNl+cGxP3KG8jiqGa4QiHgo2nXDPQKCApy5wWV3 +diRMmPdtMTj72/7bNwJ2oRiXpszeIAlJNiRpQvbkN2LxWW2pPO00nKOO29w61/cK +b+8u2NWTWnrtCElo4kHjWpDBhlX8UUOd4LLEZ7TLMjEl8FSfS9Fv29Td/K9ebHiQ +ld7KOki5eTybGdZ1BaD5iNfB6KUJ5BoV3IcjqrJ1jGMlh9j4PabCzGb/pWZoVQID +AQABo4IBCzCCAQcwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wMwYI +KwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5l +dDAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLmVudHJ1c3QubmV0LzIwNDhj +YS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly93 +d3cuZW50cnVzdC5uZXQvcnBhMB0GA1UdDgQWBBQe8auJBvhJDwEzd+4Ueu4ZfJMo +TTAfBgNVHSMEGDAWgBRV5IHREYC+2Im5CKMx+aEkCRa5cDANBgkqhkiG9w0BAQUF +AAOCAQEAB/ZfgoR/gEDHkDRGQiQDzi+ruoOeJXMN7awFacaH7aNc8lfBsUl2mk3y +P93kDv4LPrmY2TKVHTL0Ae6cyMjlP+BTdmL83attPZSQ8sCzPJgnNl4olyL8G0DT +Kw2ttVdt3w/jS+9zAhBl+hvQrDHV4w/oujIwg+5K0L/fIpB6vuw6G8RJBB3xroB3 +PEII26c7KKaAAQPmOaPr34BZG/MsvtxyRHmgbAelbU1EjkJoypR8Lja6hZ7NqsRe +PFS+/i/qaZ0cHimbltjI/lGQ8SSmkAaz8Cmi/3gud1xFIdlEADHzvjJP9QoyDfz8 +uhZ2VrLWSJLyi6Y+t6xcaeoLP2ZFuQ== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert36[] = { + 0x30, 0x82, 0x04, 0xf2, 0x30, 0x82, 0x03, 0xda, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x38, 0x63, 0xe9, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, + 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31, + 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77, + 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x2f, 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69, + 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, + 0x66, 0x2e, 0x20, 0x28, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, + 0x69, 0x61, 0x62, 0x2e, 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, + 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, + 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x33, 0x30, 0x31, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38, + 0x29, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x31, 0x32, 0x31, 0x30, 0x32, + 0x30, 0x34, 0x33, 0x35, 0x34, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, + 0x31, 0x30, 0x32, 0x31, 0x31, 0x33, 0x35, 0x34, 0x5a, 0x30, 0x81, 0xb1, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, + 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, + 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x20, 0x69, 0x73, 0x20, 0x69, + 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2e, 0x30, + 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x4c, 0x31, 0x43, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde, + 0x05, 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, 0x7f, 0xc7, 0x4b, + 0x7e, 0x5a, 0x9f, 0xb3, 0xff, 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba, + 0x10, 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, 0x28, 0xce, 0xc0, + 0xe6, 0x0e, 0x06, 0x91, 0x50, 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb, + 0xd8, 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, 0x96, 0xdc, 0xbc, + 0xaa, 0xfa, 0x52, 0x77, 0x04, 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd, + 0x3c, 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, 0xf9, 0xc1, 0xb1, + 0x3f, 0x72, 0x86, 0xf2, 0x38, 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28, + 0xda, 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, 0xc1, 0x65, 0x77, + 0x76, 0x24, 0x4c, 0x98, 0xf7, 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb, + 0x37, 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, 0x20, 0x09, 0x49, + 0x36, 0x24, 0x69, 0x42, 0xf6, 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9, + 0x3c, 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, 0xd7, 0xf7, 0x0a, + 0x6f, 0xef, 0x2e, 0xd8, 0xd5, 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68, + 0xe2, 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, 0x51, 0x43, 0x9d, + 0xe0, 0xb2, 0xc4, 0x67, 0xb4, 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f, + 0x4b, 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, 0x6c, 0x78, 0x90, + 0x95, 0xde, 0xca, 0x3a, 0x48, 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75, + 0x05, 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, 0xe4, 0x1a, 0x15, + 0xdc, 0x87, 0x23, 0xaa, 0xb2, 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8, + 0x3d, 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, 0x55, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x0b, 0x30, 0x82, 0x01, 0x07, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x33, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, + 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, + 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x32, 0x30, 0x34, 0x38, 0x63, + 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, + 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, + 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, + 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x1e, 0xf1, 0xab, 0x89, 0x06, 0xf8, 0x49, + 0x0f, 0x01, 0x33, 0x77, 0xee, 0x14, 0x7a, 0xee, 0x19, 0x7c, 0x93, 0x28, + 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x55, 0xe4, 0x81, 0xd1, 0x11, 0x80, 0xbe, 0xd8, 0x89, 0xb9, + 0x08, 0xa3, 0x31, 0xf9, 0xa1, 0x24, 0x09, 0x16, 0xb9, 0x70, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x07, 0xf6, 0x5f, 0x82, 0x84, 0x7f, + 0x80, 0x40, 0xc7, 0x90, 0x34, 0x46, 0x42, 0x24, 0x03, 0xce, 0x2f, 0xab, + 0xba, 0x83, 0x9e, 0x25, 0x73, 0x0d, 0xed, 0xac, 0x05, 0x69, 0xc6, 0x87, + 0xed, 0xa3, 0x5c, 0xf2, 0x57, 0xc1, 0xb1, 0x49, 0x76, 0x9a, 0x4d, 0xf2, + 0x3f, 0xdd, 0xe4, 0x0e, 0xfe, 0x0b, 0x3e, 0xb9, 0x98, 0xd9, 0x32, 0x95, + 0x1d, 0x32, 0xf4, 0x01, 0xee, 0x9c, 0xc8, 0xc8, 0xe5, 0x3f, 0xe0, 0x53, + 0x76, 0x62, 0xfc, 0xdd, 0xab, 0x6d, 0x3d, 0x94, 0x90, 0xf2, 0xc0, 0xb3, + 0x3c, 0x98, 0x27, 0x36, 0x5e, 0x28, 0x97, 0x22, 0xfc, 0x1b, 0x40, 0xd3, + 0x2b, 0x0d, 0xad, 0xb5, 0x57, 0x6d, 0xdf, 0x0f, 0xe3, 0x4b, 0xef, 0x73, + 0x02, 0x10, 0x65, 0xfa, 0x1b, 0xd0, 0xac, 0x31, 0xd5, 0xe3, 0x0f, 0xe8, + 0xba, 0x32, 0x30, 0x83, 0xee, 0x4a, 0xd0, 0xbf, 0xdf, 0x22, 0x90, 0x7a, + 0xbe, 0xec, 0x3a, 0x1b, 0xc4, 0x49, 0x04, 0x1d, 0xf1, 0xae, 0x80, 0x77, + 0x3c, 0x42, 0x08, 0xdb, 0xa7, 0x3b, 0x28, 0xa6, 0x80, 0x01, 0x03, 0xe6, + 0x39, 0xa3, 0xeb, 0xdf, 0x80, 0x59, 0x1b, 0xf3, 0x2c, 0xbe, 0xdc, 0x72, + 0x44, 0x79, 0xa0, 0x6c, 0x07, 0xa5, 0x6d, 0x4d, 0x44, 0x8e, 0x42, 0x68, + 0xca, 0x94, 0x7c, 0x2e, 0x36, 0xba, 0x85, 0x9e, 0xcd, 0xaa, 0xc4, 0x5e, + 0x3c, 0x54, 0xbe, 0xfe, 0x2f, 0xea, 0x69, 0x9d, 0x1c, 0x1e, 0x29, 0x9b, + 0x96, 0xd8, 0xc8, 0xfe, 0x51, 0x90, 0xf1, 0x24, 0xa6, 0x90, 0x06, 0xb3, + 0xf0, 0x29, 0xa2, 0xff, 0x78, 0x2e, 0x77, 0x5c, 0x45, 0x21, 0xd9, 0x44, + 0x00, 0x31, 0xf3, 0xbe, 0x32, 0x4f, 0xf5, 0x0a, 0x32, 0x0d, 0xfc, 0xfc, + 0xba, 0x16, 0x76, 0x56, 0xb2, 0xd6, 0x48, 0x92, 0xf2, 0x8b, 0xa6, 0x3e, + 0xb7, 0xac, 0x5c, 0x69, 0xea, 0x0b, 0x3f, 0x66, 0x45, 0xb9, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 16:90:c3:29:b6:78:06:07:51:1f:05:b0:34:48:46:cb + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root + Validity + Not Before: Apr 16 00:00:00 2010 GMT + Not After : May 30 10:48:38 2020 GMT + Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO High-Assurance Secure Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e7:87:da:c0:77:e4:bb:3a:fa:6a:24:c8:80:41: + ac:d2:16:13:15:3d:fa:f7:f8:2a:76:dc:a8:2d:39: + 08:ce:48:4a:be:0f:7d:f0:de:ba:bb:47:d5:bd:2d: + d7:1b:ab:0f:20:81:23:08:72:b1:c0:11:95:0d:e6: + ea:a9:87:ff:c7:6e:1e:4f:66:32:ba:53:bc:05:aa: + 1c:2c:0c:ef:4d:37:47:6b:10:0c:db:c5:a0:98:7e: + 58:db:37:d6:ae:e9:06:bd:d7:a8:65:f3:37:b9:c7: + 6d:ce:77:c7:26:e0:d7:74:1f:a6:98:16:bb:0c:6b: + c8:be:77:d0:ef:58:a7:29:a0:b9:b8:69:05:36:cb: + b2:da:58:a3:0b:75:ad:3d:8b:22:82:20:3e:70:86: + 99:1c:b9:4f:cf:77:a4:07:1a:23:63:d1:38:56:84: + ec:bf:8f:c5:4e:f4:18:96:9b:1a:e8:93:ec:8d:af: + 15:9c:24:f0:5a:3b:e8:0f:b9:a8:5a:01:d3:b2:1c: + 60:c9:9c:52:04:dd:92:a7:fe:0c:ac:e2:45:8d:03: + 61:bc:79:e0:77:2e:87:41:3c:58:5f:cb:f5:c5:77: + f2:58:c8:4d:28:d0:9a:fa:f3:73:09:24:68:74:bc: + 20:4c:d8:2c:b0:aa:e8:d9:4e:6d:f2:8c:24:d3:93: + 5d:91 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A + + X509v3 Subject Key Identifier: + 3F:D5:B5:D0:D6:44:79:50:4A:17:A3:9B:8C:4A:DC:B8:B0:22:64:6B + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl + + Authority Information Access: + CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c + CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt + OCSP - URI:http://ocsp.usertrust.com + + Signature Algorithm: sha1WithRSAEncryption + 13:85:1f:52:80:18:c9:53:f7:fe:2e:1a:af:cc:d9:0b:3c:c2: + d3:85:81:10:f0:28:8d:b9:40:7e:2c:9e:8f:d6:36:86:0a:4c: + 14:2d:d6:97:43:92:41:19:37:4b:96:9e:eb:a9:30:79:12:95: + b3:02:36:57:ed:2b:b9:1d:98:1a:a3:18:0a:3f:9b:39:8b:cd: + a1:49:29:4c:2f:f9:d0:95:8c:c8:4d:95:ba:a8:43:cf:33:aa: + 25:2a:5a:0e:aa:27:c9:4e:6b:b1:e6:73:1f:b3:74:04:c3:f3: + 4c:e2:a8:eb:67:b7:5d:b8:08:05:1a:56:9a:54:29:85:f5:29: + 4e:80:3b:95:d0:7b:53:96:11:56:c1:02:d3:ea:b2:7f:ca:8f: + 9c:70:4a:14:8d:5a:b9:16:60:75:d6:cd:27:1e:16:cd:5b:33: + 8e:79:40:cf:28:48:e7:dc:71:16:4e:74:91:75:b9:2a:8c:f1: + 70:ac:26:dd:04:b9:40:c2:85:de:1c:93:40:d0:cc:6e:c3:9b: + aa:ef:60:65:df:60:22:f0:5a:a5:7a:a2:2f:e4:70:73:ee:3c: + d4:26:2b:68:07:c1:20:7a:e8:98:5a:3e:7b:9f:02:8b:62:c0: + 85:81:80:60:35:7e:a5:1d:0c:d2:9c:df:62:45:0d:db:fc:37: + fb:f5:25:22 +-----BEGIN CERTIFICATE----- +MIIE/DCCA+SgAwIBAgIQFpDDKbZ4BgdRHwWwNEhGyzANBgkqhkiG9w0BAQUFADBv +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk +ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF +eHRlcm5hbCBDQSBSb290MB4XDTEwMDQxNjAwMDAwMFoXDTIwMDUzMDEwNDgzOFow +gYkxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMS8wLQYD +VQQDEyZDT01PRE8gSGlnaC1Bc3N1cmFuY2UgU2VjdXJlIFNlcnZlciBDQTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOeH2sB35Ls6+mokyIBBrNIWExU9 ++vf4KnbcqC05CM5ISr4PffDeurtH1b0t1xurDyCBIwhyscARlQ3m6qmH/8duHk9m +MrpTvAWqHCwM7003R2sQDNvFoJh+WNs31q7pBr3XqGXzN7nHbc53xybg13QfppgW +uwxryL530O9YpymgubhpBTbLstpYowt1rT2LIoIgPnCGmRy5T893pAcaI2PROFaE +7L+PxU70GJabGuiT7I2vFZwk8Fo76A+5qFoB07IcYMmcUgTdkqf+DKziRY0DYbx5 +4Hcuh0E8WF/L9cV38ljITSjQmvrzcwkkaHS8IEzYLLCq6NlObfKMJNOTXZECAwEA +AaOCAXcwggFzMB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8DveAky1QaMB0GA1Ud +DgQWBBQ/1bXQ1kR5UEoXo5uMSty4sCJkazAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T +AQH/BAgwBgEB/wIBADARBgNVHSAECjAIMAYGBFUdIAAwRAYDVR0fBD0wOzA5oDeg +NYYzaHR0cDovL2NybC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJv +b3QuY3JsMIGzBggrBgEFBQcBAQSBpjCBozA/BggrBgEFBQcwAoYzaHR0cDovL2Ny +dC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsG +AQUFBzAChi1odHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0ND +QS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJ +KoZIhvcNAQEFBQADggEBABOFH1KAGMlT9/4uGq/M2Qs8wtOFgRDwKI25QH4sno/W +NoYKTBQt1pdDkkEZN0uWnuupMHkSlbMCNlftK7kdmBqjGAo/mzmLzaFJKUwv+dCV +jMhNlbqoQ88zqiUqWg6qJ8lOa7Hmcx+zdATD80ziqOtnt124CAUaVppUKYX1KU6A +O5XQe1OWEVbBAtPqsn/Kj5xwShSNWrkWYHXWzSceFs1bM455QM8oSOfccRZOdJF1 +uSqM8XCsJt0EuUDChd4ck0DQzG7Dm6rvYGXfYCLwWqV6oi/kcHPuPNQmK2gHwSB6 +6JhaPnufAotiwIWBgGA1fqUdDNKc32JFDdv8N/v1JSI= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert37[] = { + 0x30, 0x82, 0x04, 0xfc, 0x30, 0x82, 0x03, 0xe4, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x16, 0x90, 0xc3, 0x29, 0xb6, 0x78, 0x06, 0x07, 0x51, + 0x1f, 0x05, 0xb0, 0x34, 0x48, 0x46, 0xcb, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53, + 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, + 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31, + 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64, + 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52, + 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x34, 0x31, + 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, + 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30, + 0x81, 0x89, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, + 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, + 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x26, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, + 0x48, 0x69, 0x67, 0x68, 0x2d, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, + 0x63, 0x65, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xe7, 0x87, 0xda, 0xc0, 0x77, 0xe4, 0xbb, 0x3a, + 0xfa, 0x6a, 0x24, 0xc8, 0x80, 0x41, 0xac, 0xd2, 0x16, 0x13, 0x15, 0x3d, + 0xfa, 0xf7, 0xf8, 0x2a, 0x76, 0xdc, 0xa8, 0x2d, 0x39, 0x08, 0xce, 0x48, + 0x4a, 0xbe, 0x0f, 0x7d, 0xf0, 0xde, 0xba, 0xbb, 0x47, 0xd5, 0xbd, 0x2d, + 0xd7, 0x1b, 0xab, 0x0f, 0x20, 0x81, 0x23, 0x08, 0x72, 0xb1, 0xc0, 0x11, + 0x95, 0x0d, 0xe6, 0xea, 0xa9, 0x87, 0xff, 0xc7, 0x6e, 0x1e, 0x4f, 0x66, + 0x32, 0xba, 0x53, 0xbc, 0x05, 0xaa, 0x1c, 0x2c, 0x0c, 0xef, 0x4d, 0x37, + 0x47, 0x6b, 0x10, 0x0c, 0xdb, 0xc5, 0xa0, 0x98, 0x7e, 0x58, 0xdb, 0x37, + 0xd6, 0xae, 0xe9, 0x06, 0xbd, 0xd7, 0xa8, 0x65, 0xf3, 0x37, 0xb9, 0xc7, + 0x6d, 0xce, 0x77, 0xc7, 0x26, 0xe0, 0xd7, 0x74, 0x1f, 0xa6, 0x98, 0x16, + 0xbb, 0x0c, 0x6b, 0xc8, 0xbe, 0x77, 0xd0, 0xef, 0x58, 0xa7, 0x29, 0xa0, + 0xb9, 0xb8, 0x69, 0x05, 0x36, 0xcb, 0xb2, 0xda, 0x58, 0xa3, 0x0b, 0x75, + 0xad, 0x3d, 0x8b, 0x22, 0x82, 0x20, 0x3e, 0x70, 0x86, 0x99, 0x1c, 0xb9, + 0x4f, 0xcf, 0x77, 0xa4, 0x07, 0x1a, 0x23, 0x63, 0xd1, 0x38, 0x56, 0x84, + 0xec, 0xbf, 0x8f, 0xc5, 0x4e, 0xf4, 0x18, 0x96, 0x9b, 0x1a, 0xe8, 0x93, + 0xec, 0x8d, 0xaf, 0x15, 0x9c, 0x24, 0xf0, 0x5a, 0x3b, 0xe8, 0x0f, 0xb9, + 0xa8, 0x5a, 0x01, 0xd3, 0xb2, 0x1c, 0x60, 0xc9, 0x9c, 0x52, 0x04, 0xdd, + 0x92, 0xa7, 0xfe, 0x0c, 0xac, 0xe2, 0x45, 0x8d, 0x03, 0x61, 0xbc, 0x79, + 0xe0, 0x77, 0x2e, 0x87, 0x41, 0x3c, 0x58, 0x5f, 0xcb, 0xf5, 0xc5, 0x77, + 0xf2, 0x58, 0xc8, 0x4d, 0x28, 0xd0, 0x9a, 0xfa, 0xf3, 0x73, 0x09, 0x24, + 0x68, 0x74, 0xbc, 0x20, 0x4c, 0xd8, 0x2c, 0xb0, 0xaa, 0xe8, 0xd9, 0x4e, + 0x6d, 0xf2, 0x8c, 0x24, 0xd3, 0x93, 0x5d, 0x91, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x01, 0x77, 0x30, 0x82, 0x01, 0x73, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd, + 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03, + 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3f, 0xd5, 0xb5, 0xd0, 0xd6, 0x44, 0x79, + 0x50, 0x4a, 0x17, 0xa3, 0x9b, 0x8c, 0x4a, 0xdc, 0xb8, 0xb0, 0x22, 0x64, + 0x6b, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08, + 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0, + 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, + 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81, + 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, + 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, + 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43, + 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x13, 0x85, 0x1f, 0x52, 0x80, 0x18, 0xc9, 0x53, + 0xf7, 0xfe, 0x2e, 0x1a, 0xaf, 0xcc, 0xd9, 0x0b, 0x3c, 0xc2, 0xd3, 0x85, + 0x81, 0x10, 0xf0, 0x28, 0x8d, 0xb9, 0x40, 0x7e, 0x2c, 0x9e, 0x8f, 0xd6, + 0x36, 0x86, 0x0a, 0x4c, 0x14, 0x2d, 0xd6, 0x97, 0x43, 0x92, 0x41, 0x19, + 0x37, 0x4b, 0x96, 0x9e, 0xeb, 0xa9, 0x30, 0x79, 0x12, 0x95, 0xb3, 0x02, + 0x36, 0x57, 0xed, 0x2b, 0xb9, 0x1d, 0x98, 0x1a, 0xa3, 0x18, 0x0a, 0x3f, + 0x9b, 0x39, 0x8b, 0xcd, 0xa1, 0x49, 0x29, 0x4c, 0x2f, 0xf9, 0xd0, 0x95, + 0x8c, 0xc8, 0x4d, 0x95, 0xba, 0xa8, 0x43, 0xcf, 0x33, 0xaa, 0x25, 0x2a, + 0x5a, 0x0e, 0xaa, 0x27, 0xc9, 0x4e, 0x6b, 0xb1, 0xe6, 0x73, 0x1f, 0xb3, + 0x74, 0x04, 0xc3, 0xf3, 0x4c, 0xe2, 0xa8, 0xeb, 0x67, 0xb7, 0x5d, 0xb8, + 0x08, 0x05, 0x1a, 0x56, 0x9a, 0x54, 0x29, 0x85, 0xf5, 0x29, 0x4e, 0x80, + 0x3b, 0x95, 0xd0, 0x7b, 0x53, 0x96, 0x11, 0x56, 0xc1, 0x02, 0xd3, 0xea, + 0xb2, 0x7f, 0xca, 0x8f, 0x9c, 0x70, 0x4a, 0x14, 0x8d, 0x5a, 0xb9, 0x16, + 0x60, 0x75, 0xd6, 0xcd, 0x27, 0x1e, 0x16, 0xcd, 0x5b, 0x33, 0x8e, 0x79, + 0x40, 0xcf, 0x28, 0x48, 0xe7, 0xdc, 0x71, 0x16, 0x4e, 0x74, 0x91, 0x75, + 0xb9, 0x2a, 0x8c, 0xf1, 0x70, 0xac, 0x26, 0xdd, 0x04, 0xb9, 0x40, 0xc2, + 0x85, 0xde, 0x1c, 0x93, 0x40, 0xd0, 0xcc, 0x6e, 0xc3, 0x9b, 0xaa, 0xef, + 0x60, 0x65, 0xdf, 0x60, 0x22, 0xf0, 0x5a, 0xa5, 0x7a, 0xa2, 0x2f, 0xe4, + 0x70, 0x73, 0xee, 0x3c, 0xd4, 0x26, 0x2b, 0x68, 0x07, 0xc1, 0x20, 0x7a, + 0xe8, 0x98, 0x5a, 0x3e, 0x7b, 0x9f, 0x02, 0x8b, 0x62, 0xc0, 0x85, 0x81, + 0x80, 0x60, 0x35, 0x7e, 0xa5, 0x1d, 0x0c, 0xd2, 0x9c, 0xdf, 0x62, 0x45, + 0x0d, 0xdb, 0xfc, 0x37, 0xfb, 0xf5, 0x25, 0x22, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1372799044 (0x51d34044) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority + Validity + Not Before: Sep 22 17:14:57 2014 GMT + Not After : Sep 23 01:31:53 2024 GMT + Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ba:84:b6:72:db:9e:0c:6b:e2:99:e9:30:01:a7: + 76:ea:32:b8:95:41:1a:c9:da:61:4e:58:72:cf:fe: + f6:82:79:bf:73:61:06:0a:a5:27:d8:b3:5f:d3:45: + 4e:1c:72:d6:4e:32:f2:72:8a:0f:f7:83:19:d0:6a: + 80:80:00:45:1e:b0:c7:e7:9a:bf:12:57:27:1c:a3: + 68:2f:0a:87:bd:6a:6b:0e:5e:65:f3:1c:77:d5:d4: + 85:8d:70:21:b4:b3:32:e7:8b:a2:d5:86:39:02:b1: + b8:d2:47:ce:e4:c9:49:c4:3b:a7:de:fb:54:7d:57: + be:f0:e8:6e:c2:79:b2:3a:0b:55:e2:50:98:16:32: + 13:5c:2f:78:56:c1:c2:94:b3:f2:5a:e4:27:9a:9f: + 24:d7:c6:ec:d0:9b:25:82:e3:cc:c2:c4:45:c5:8c: + 97:7a:06:6b:2a:11:9f:a9:0a:6e:48:3b:6f:db:d4: + 11:19:42:f7:8f:07:bf:f5:53:5f:9c:3e:f4:17:2c: + e6:69:ac:4e:32:4c:62:77:ea:b7:e8:e5:bb:34:bc: + 19:8b:ae:9c:51:e7:b7:7e:b5:53:b1:33:22:e5:6d: + cf:70:3c:1a:fa:e2:9b:67:b6:83:f4:8d:a5:af:62: + 4c:4d:e0:58:ac:64:34:12:03:f8:b6:8d:94:63:24: + a4:71 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:1 + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.entrust.net/rootca1.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/CPS + + X509v3 Subject Key Identifier: + 6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB + X509v3 Authority Key Identifier: + keyid:68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D + + Signature Algorithm: sha256WithRSAEncryption + 69:33:83:fc:28:7a:6f:7d:ef:9d:55:eb:c5:3e:7a:9d:75:b3: + cc:c3:38:36:d9:34:a2:28:68:18:ea:1e:69:d3:bd:e7:d0:77: + da:b8:00:83:4e:4a:cf:6f:d1:f1:c1:22:3f:74:e4:f7:98:49: + 9e:9b:b6:9e:e1:db:98:77:2d:56:34:b1:a8:3c:d9:fd:c0:cd: + c7:bf:05:03:d4:02:c5:f1:e5:c6:da:08:a5:13:c7:62:23:11: + d1:61:30:1d:60:84:45:ef:79:a8:c6:26:93:a4:b7:cd:34:b8: + 69:c5:13:f6:91:b3:c9:45:73:76:b6:92:f6:76:0a:5b:e1:03: + 47:b7:e9:29:4c:91:32:23:37:4a:9c:35:d8:78:fd:1d:1f:e4: + 83:89:24:80:ad:b7:f9:cf:e4:5d:a5:d4:71:c4:85:5b:70:1f: + db:3f:1c:01:eb:1a:45:26:31:14:cc:65:bf:67:de:ca:cc:33: + 65:e5:41:91:d7:37:be:41:1a:96:9d:e6:8a:97:9d:a7:ce:ac: + 4e:9a:3d:bd:01:a0:6a:d9:4f:22:00:8b:44:d5:69:62:7b:2e: + eb:cc:ba:e7:92:7d:69:67:3d:fc:b8:7c:de:41:87:d0:69:ea: + ba:0a:18:7a:1a:95:43:b3:79:71:28:76:6d:a1:fb:57:4a:ec: + 4d:c8:0e:10 +-----BEGIN CERTIFICATE----- +MIIE/zCCA+egAwIBAgIEUdNARDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDkyMjE3MTQ1N1oXDTI0MDkyMzAx +MzE1M1owgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgw +JgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQL +EzAoYykgMjAwOSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9u +bHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoS2ctueDGvi +mekwAad26jK4lUEaydphTlhyz/72gnm/c2EGCqUn2LNf00VOHHLWTjLycooP94MZ +0GqAgABFHrDH55q/ElcnHKNoLwqHvWprDl5l8xx31dSFjXAhtLMy54ui1YY5ArG4 +0kfO5MlJxDun3vtUfVe+8OhuwnmyOgtV4lCYFjITXC94VsHClLPyWuQnmp8k18bs +0JslguPMwsRFxYyXegZrKhGfqQpuSDtv29QRGUL3jwe/9VNfnD70FyzmaaxOMkxi +d+q36OW7NLwZi66cUee3frVTsTMi5W3PcDwa+uKbZ7aD9I2lr2JMTeBYrGQ0EgP4 +to2UYySkcQIDAQABo4IBDzCCAQswDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQEwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz +cC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1 +c3QubmV0L3Jvb3RjYTEuY3JsMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYGCCsGAQUF +BwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NQUzAdBgNVHQ4EFgQUanImetAe +733nO2lR1GyNn5ASZqswHwYDVR0jBBgwFoAUaJDkZ6SmU4DHhmak8fdLQ/uEvW0w +DQYJKoZIhvcNAQELBQADggEBAGkzg/woem99751V68U+ep11s8zDODbZNKIoaBjq +HmnTvefQd9q4AINOSs9v0fHBIj905PeYSZ6btp7h25h3LVY0sag82f3Azce/BQPU +AsXx5cbaCKUTx2IjEdFhMB1ghEXveajGJpOkt800uGnFE/aRs8lFc3a2kvZ2Clvh +A0e36SlMkTIjN0qcNdh4/R0f5IOJJICtt/nP5F2l1HHEhVtwH9s/HAHrGkUmMRTM +Zb9n3srMM2XlQZHXN75BGpad5oqXnafOrE6aPb0BoGrZTyIAi0TVaWJ7LuvMuueS +fWlnPfy4fN5Bh9Bp6roKGHoalUOzeXEodm2h+1dK7E3IDhA= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert38[] = { + 0x30, 0x82, 0x04, 0xff, 0x30, 0x82, 0x03, 0xe7, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x51, 0xd3, 0x40, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xb0, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x30, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x73, 0x20, + 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, + 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x45, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d, + 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x45, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x34, 0x30, 0x39, 0x32, 0x32, 0x31, 0x37, 0x31, 0x34, 0x35, + 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x39, 0x32, 0x33, 0x30, 0x31, + 0x33, 0x31, 0x35, 0x33, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, + 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28, 0x30, + 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65, 0x20, + 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74, 0x65, + 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45, + 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, + 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xba, 0x84, 0xb6, 0x72, 0xdb, 0x9e, 0x0c, 0x6b, 0xe2, + 0x99, 0xe9, 0x30, 0x01, 0xa7, 0x76, 0xea, 0x32, 0xb8, 0x95, 0x41, 0x1a, + 0xc9, 0xda, 0x61, 0x4e, 0x58, 0x72, 0xcf, 0xfe, 0xf6, 0x82, 0x79, 0xbf, + 0x73, 0x61, 0x06, 0x0a, 0xa5, 0x27, 0xd8, 0xb3, 0x5f, 0xd3, 0x45, 0x4e, + 0x1c, 0x72, 0xd6, 0x4e, 0x32, 0xf2, 0x72, 0x8a, 0x0f, 0xf7, 0x83, 0x19, + 0xd0, 0x6a, 0x80, 0x80, 0x00, 0x45, 0x1e, 0xb0, 0xc7, 0xe7, 0x9a, 0xbf, + 0x12, 0x57, 0x27, 0x1c, 0xa3, 0x68, 0x2f, 0x0a, 0x87, 0xbd, 0x6a, 0x6b, + 0x0e, 0x5e, 0x65, 0xf3, 0x1c, 0x77, 0xd5, 0xd4, 0x85, 0x8d, 0x70, 0x21, + 0xb4, 0xb3, 0x32, 0xe7, 0x8b, 0xa2, 0xd5, 0x86, 0x39, 0x02, 0xb1, 0xb8, + 0xd2, 0x47, 0xce, 0xe4, 0xc9, 0x49, 0xc4, 0x3b, 0xa7, 0xde, 0xfb, 0x54, + 0x7d, 0x57, 0xbe, 0xf0, 0xe8, 0x6e, 0xc2, 0x79, 0xb2, 0x3a, 0x0b, 0x55, + 0xe2, 0x50, 0x98, 0x16, 0x32, 0x13, 0x5c, 0x2f, 0x78, 0x56, 0xc1, 0xc2, + 0x94, 0xb3, 0xf2, 0x5a, 0xe4, 0x27, 0x9a, 0x9f, 0x24, 0xd7, 0xc6, 0xec, + 0xd0, 0x9b, 0x25, 0x82, 0xe3, 0xcc, 0xc2, 0xc4, 0x45, 0xc5, 0x8c, 0x97, + 0x7a, 0x06, 0x6b, 0x2a, 0x11, 0x9f, 0xa9, 0x0a, 0x6e, 0x48, 0x3b, 0x6f, + 0xdb, 0xd4, 0x11, 0x19, 0x42, 0xf7, 0x8f, 0x07, 0xbf, 0xf5, 0x53, 0x5f, + 0x9c, 0x3e, 0xf4, 0x17, 0x2c, 0xe6, 0x69, 0xac, 0x4e, 0x32, 0x4c, 0x62, + 0x77, 0xea, 0xb7, 0xe8, 0xe5, 0xbb, 0x34, 0xbc, 0x19, 0x8b, 0xae, 0x9c, + 0x51, 0xe7, 0xb7, 0x7e, 0xb5, 0x53, 0xb1, 0x33, 0x22, 0xe5, 0x6d, 0xcf, + 0x70, 0x3c, 0x1a, 0xfa, 0xe2, 0x9b, 0x67, 0xb6, 0x83, 0xf4, 0x8d, 0xa5, + 0xaf, 0x62, 0x4c, 0x4d, 0xe0, 0x58, 0xac, 0x64, 0x34, 0x12, 0x03, 0xf8, + 0xb6, 0x8d, 0x94, 0x63, 0x24, 0xa4, 0x71, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x0f, 0x30, 0x82, 0x01, 0x0b, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x33, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, + 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, + 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63, + 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, + 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, 0x1e, + 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, 0x12, + 0x66, 0xab, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x68, 0x90, 0xe4, 0x67, 0xa4, 0xa6, 0x53, 0x80, 0xc7, + 0x86, 0x66, 0xa4, 0xf1, 0xf7, 0x4b, 0x43, 0xfb, 0x84, 0xbd, 0x6d, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x33, 0x83, 0xfc, 0x28, + 0x7a, 0x6f, 0x7d, 0xef, 0x9d, 0x55, 0xeb, 0xc5, 0x3e, 0x7a, 0x9d, 0x75, + 0xb3, 0xcc, 0xc3, 0x38, 0x36, 0xd9, 0x34, 0xa2, 0x28, 0x68, 0x18, 0xea, + 0x1e, 0x69, 0xd3, 0xbd, 0xe7, 0xd0, 0x77, 0xda, 0xb8, 0x00, 0x83, 0x4e, + 0x4a, 0xcf, 0x6f, 0xd1, 0xf1, 0xc1, 0x22, 0x3f, 0x74, 0xe4, 0xf7, 0x98, + 0x49, 0x9e, 0x9b, 0xb6, 0x9e, 0xe1, 0xdb, 0x98, 0x77, 0x2d, 0x56, 0x34, + 0xb1, 0xa8, 0x3c, 0xd9, 0xfd, 0xc0, 0xcd, 0xc7, 0xbf, 0x05, 0x03, 0xd4, + 0x02, 0xc5, 0xf1, 0xe5, 0xc6, 0xda, 0x08, 0xa5, 0x13, 0xc7, 0x62, 0x23, + 0x11, 0xd1, 0x61, 0x30, 0x1d, 0x60, 0x84, 0x45, 0xef, 0x79, 0xa8, 0xc6, + 0x26, 0x93, 0xa4, 0xb7, 0xcd, 0x34, 0xb8, 0x69, 0xc5, 0x13, 0xf6, 0x91, + 0xb3, 0xc9, 0x45, 0x73, 0x76, 0xb6, 0x92, 0xf6, 0x76, 0x0a, 0x5b, 0xe1, + 0x03, 0x47, 0xb7, 0xe9, 0x29, 0x4c, 0x91, 0x32, 0x23, 0x37, 0x4a, 0x9c, + 0x35, 0xd8, 0x78, 0xfd, 0x1d, 0x1f, 0xe4, 0x83, 0x89, 0x24, 0x80, 0xad, + 0xb7, 0xf9, 0xcf, 0xe4, 0x5d, 0xa5, 0xd4, 0x71, 0xc4, 0x85, 0x5b, 0x70, + 0x1f, 0xdb, 0x3f, 0x1c, 0x01, 0xeb, 0x1a, 0x45, 0x26, 0x31, 0x14, 0xcc, + 0x65, 0xbf, 0x67, 0xde, 0xca, 0xcc, 0x33, 0x65, 0xe5, 0x41, 0x91, 0xd7, + 0x37, 0xbe, 0x41, 0x1a, 0x96, 0x9d, 0xe6, 0x8a, 0x97, 0x9d, 0xa7, 0xce, + 0xac, 0x4e, 0x9a, 0x3d, 0xbd, 0x01, 0xa0, 0x6a, 0xd9, 0x4f, 0x22, 0x00, + 0x8b, 0x44, 0xd5, 0x69, 0x62, 0x7b, 0x2e, 0xeb, 0xcc, 0xba, 0xe7, 0x92, + 0x7d, 0x69, 0x67, 0x3d, 0xfc, 0xb8, 0x7c, 0xde, 0x41, 0x87, 0xd0, 0x69, + 0xea, 0xba, 0x0a, 0x18, 0x7a, 0x1a, 0x95, 0x43, 0xb3, 0x79, 0x71, 0x28, + 0x76, 0x6d, 0xa1, 0xfb, 0x57, 0x4a, 0xec, 0x4d, 0xc8, 0x0e, 0x10, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 7 (0x7) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2 + Validity + Not Before: May 3 07:00:00 2011 GMT + Not After : May 3 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certs.starfieldtech.com/repository/, CN=Starfield Secure Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e5:90:66:4b:ec:f9:46:71:a9:20:83:be:e9:6c: + bf:4a:c9:48:69:81:75:4e:6d:24:f6:cb:17:13:f8: + b0:71:59:84:7a:6b:2b:85:a4:34:b5:16:e5:cb:cc: + e9:41:70:2c:a4:2e:d6:fa:32:7d:e1:a8:de:94:10: + ac:31:c1:c0:d8:6a:ff:59:27:ab:76:d6:fc:0b:74: + 6b:b8:a7:ae:3f:c4:54:f4:b4:31:44:dd:93:56:8c: + a4:4c:5e:9b:89:cb:24:83:9b:e2:57:7d:b7:d8:12: + 1f:c9:85:6d:f4:d1:80:f1:50:9b:87:ae:d4:0b:10: + 05:fb:27:ba:28:6d:17:e9:0e:d6:4d:b9:39:55:06: + ff:0a:24:05:7e:2f:c6:1d:72:6c:d4:8b:29:8c:57: + 7d:da:d9:eb:66:1a:d3:4f:a7:df:7f:52:c4:30:c5: + a5:c9:0e:02:c5:53:bf:77:38:68:06:24:c3:66:c8: + 37:7e:30:1e:45:71:23:35:ff:90:d8:2a:9d:8d:e7: + b0:92:4d:3c:7f:2a:0a:93:dc:cd:16:46:65:f7:60: + 84:8b:76:4b:91:27:73:14:92:e0:ea:ee:8f:16:ea: + 8d:0e:3e:76:17:bf:7d:89:80:80:44:43:e7:2d:e0: + 43:09:75:da:36:e8:ad:db:89:3a:f5:5d:12:8e:23: + 04:83 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 25:45:81:68:50:26:38:3D:3B:2D:2C:BE:CD:6A:D9:B6:3D:B3:66:63 + X509v3 Authority Key Identifier: + keyid:7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27 + + Authority Information Access: + OCSP - URI:http://ocsp.starfieldtech.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.starfieldtech.com/sfroot-g2.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.starfieldtech.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 56:65:ca:fe:f3:3f:0a:a8:93:8b:18:c7:de:43:69:13:34:20: + be:4e:5f:78:a8:6b:9c:db:6a:4d:41:db:c1:13:ec:dc:31:00: + 22:5e:f7:00:9e:0c:e0:34:65:34:f9:b1:3a:4e:48:c8:12:81: + 88:5c:5b:3e:08:53:7a:f7:1a:64:df:b8:50:61:cc:53:51:40: + 29:4b:c2:f4:ae:3a:5f:e4:ca:ad:26:cc:4e:61:43:e5:fd:57: + a6:37:70:ce:43:2b:b0:94:c3:92:e9:e1:5f:aa:10:49:b7:69: + e4:e0:d0:1f:64:a4:2b:cd:1f:6f:a0:f8:84:24:18:ce:79:3d: + a9:91:bf:54:18:13:89:99:54:11:0d:55:c5:26:0b:79:4f:5a: + 1c:6e:f9:63:db:14:80:a4:07:ab:fa:b2:a5:b9:88:dd:91:fe: + 65:3b:a4:a3:79:be:89:4d:e1:d0:b0:f4:c8:17:0c:0a:96:14: + 7c:09:b7:6c:e1:c2:d8:55:d4:18:a0:aa:41:69:70:24:a3:b9: + ef:e9:5a:dc:3e:eb:94:4a:f0:b7:de:5f:0e:76:fa:fb:fb:69: + 03:45:40:50:ee:72:0c:a4:12:86:81:cd:13:d1:4e:c4:3c:ca: + 4e:0d:d2:26:f1:00:b7:b4:a6:a2:e1:6e:7a:81:fd:30:ac:7a: + 1f:c7:59:7b +-----BEGIN CERTIFICATE----- +MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw +MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk +dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg +Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF +pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE +3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV +Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+ +MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX +v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+ +zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB +BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo +LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo +LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF +BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN +QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0 +rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO +eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ +sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ +7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert39[] = { + 0x30, 0x82, 0x05, 0x00, 0x30, 0x82, 0x03, 0xe8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x8f, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, + 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, + 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, + 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xc6, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, + 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, + 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, + 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, + 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x31, 0x34, 0x30, 0x32, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b, 0x53, 0x74, 0x61, 0x72, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, + 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, + 0x90, 0x66, 0x4b, 0xec, 0xf9, 0x46, 0x71, 0xa9, 0x20, 0x83, 0xbe, 0xe9, + 0x6c, 0xbf, 0x4a, 0xc9, 0x48, 0x69, 0x81, 0x75, 0x4e, 0x6d, 0x24, 0xf6, + 0xcb, 0x17, 0x13, 0xf8, 0xb0, 0x71, 0x59, 0x84, 0x7a, 0x6b, 0x2b, 0x85, + 0xa4, 0x34, 0xb5, 0x16, 0xe5, 0xcb, 0xcc, 0xe9, 0x41, 0x70, 0x2c, 0xa4, + 0x2e, 0xd6, 0xfa, 0x32, 0x7d, 0xe1, 0xa8, 0xde, 0x94, 0x10, 0xac, 0x31, + 0xc1, 0xc0, 0xd8, 0x6a, 0xff, 0x59, 0x27, 0xab, 0x76, 0xd6, 0xfc, 0x0b, + 0x74, 0x6b, 0xb8, 0xa7, 0xae, 0x3f, 0xc4, 0x54, 0xf4, 0xb4, 0x31, 0x44, + 0xdd, 0x93, 0x56, 0x8c, 0xa4, 0x4c, 0x5e, 0x9b, 0x89, 0xcb, 0x24, 0x83, + 0x9b, 0xe2, 0x57, 0x7d, 0xb7, 0xd8, 0x12, 0x1f, 0xc9, 0x85, 0x6d, 0xf4, + 0xd1, 0x80, 0xf1, 0x50, 0x9b, 0x87, 0xae, 0xd4, 0x0b, 0x10, 0x05, 0xfb, + 0x27, 0xba, 0x28, 0x6d, 0x17, 0xe9, 0x0e, 0xd6, 0x4d, 0xb9, 0x39, 0x55, + 0x06, 0xff, 0x0a, 0x24, 0x05, 0x7e, 0x2f, 0xc6, 0x1d, 0x72, 0x6c, 0xd4, + 0x8b, 0x29, 0x8c, 0x57, 0x7d, 0xda, 0xd9, 0xeb, 0x66, 0x1a, 0xd3, 0x4f, + 0xa7, 0xdf, 0x7f, 0x52, 0xc4, 0x30, 0xc5, 0xa5, 0xc9, 0x0e, 0x02, 0xc5, + 0x53, 0xbf, 0x77, 0x38, 0x68, 0x06, 0x24, 0xc3, 0x66, 0xc8, 0x37, 0x7e, + 0x30, 0x1e, 0x45, 0x71, 0x23, 0x35, 0xff, 0x90, 0xd8, 0x2a, 0x9d, 0x8d, + 0xe7, 0xb0, 0x92, 0x4d, 0x3c, 0x7f, 0x2a, 0x0a, 0x93, 0xdc, 0xcd, 0x16, + 0x46, 0x65, 0xf7, 0x60, 0x84, 0x8b, 0x76, 0x4b, 0x91, 0x27, 0x73, 0x14, + 0x92, 0xe0, 0xea, 0xee, 0x8f, 0x16, 0xea, 0x8d, 0x0e, 0x3e, 0x76, 0x17, + 0xbf, 0x7d, 0x89, 0x80, 0x80, 0x44, 0x43, 0xe7, 0x2d, 0xe0, 0x43, 0x09, + 0x75, 0xda, 0x36, 0xe8, 0xad, 0xdb, 0x89, 0x3a, 0xf5, 0x5d, 0x12, 0x8e, + 0x23, 0x04, 0x83, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x2c, + 0x30, 0x82, 0x01, 0x28, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x25, 0x45, 0x81, 0x68, 0x50, 0x26, 0x38, 0x3d, 0x3b, 0x2d, 0x2c, 0xbe, + 0xcd, 0x6a, 0xd9, 0xb6, 0x3d, 0xb3, 0x66, 0x63, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7c, 0x0c, 0x32, + 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4, 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1, + 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1e, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0xa0, 0x2e, 0xa0, 0x2c, 0x86, 0x2a, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f, 0x6f, 0x74, 0x2d, + 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20, + 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x56, 0x65, 0xca, 0xfe, + 0xf3, 0x3f, 0x0a, 0xa8, 0x93, 0x8b, 0x18, 0xc7, 0xde, 0x43, 0x69, 0x13, + 0x34, 0x20, 0xbe, 0x4e, 0x5f, 0x78, 0xa8, 0x6b, 0x9c, 0xdb, 0x6a, 0x4d, + 0x41, 0xdb, 0xc1, 0x13, 0xec, 0xdc, 0x31, 0x00, 0x22, 0x5e, 0xf7, 0x00, + 0x9e, 0x0c, 0xe0, 0x34, 0x65, 0x34, 0xf9, 0xb1, 0x3a, 0x4e, 0x48, 0xc8, + 0x12, 0x81, 0x88, 0x5c, 0x5b, 0x3e, 0x08, 0x53, 0x7a, 0xf7, 0x1a, 0x64, + 0xdf, 0xb8, 0x50, 0x61, 0xcc, 0x53, 0x51, 0x40, 0x29, 0x4b, 0xc2, 0xf4, + 0xae, 0x3a, 0x5f, 0xe4, 0xca, 0xad, 0x26, 0xcc, 0x4e, 0x61, 0x43, 0xe5, + 0xfd, 0x57, 0xa6, 0x37, 0x70, 0xce, 0x43, 0x2b, 0xb0, 0x94, 0xc3, 0x92, + 0xe9, 0xe1, 0x5f, 0xaa, 0x10, 0x49, 0xb7, 0x69, 0xe4, 0xe0, 0xd0, 0x1f, + 0x64, 0xa4, 0x2b, 0xcd, 0x1f, 0x6f, 0xa0, 0xf8, 0x84, 0x24, 0x18, 0xce, + 0x79, 0x3d, 0xa9, 0x91, 0xbf, 0x54, 0x18, 0x13, 0x89, 0x99, 0x54, 0x11, + 0x0d, 0x55, 0xc5, 0x26, 0x0b, 0x79, 0x4f, 0x5a, 0x1c, 0x6e, 0xf9, 0x63, + 0xdb, 0x14, 0x80, 0xa4, 0x07, 0xab, 0xfa, 0xb2, 0xa5, 0xb9, 0x88, 0xdd, + 0x91, 0xfe, 0x65, 0x3b, 0xa4, 0xa3, 0x79, 0xbe, 0x89, 0x4d, 0xe1, 0xd0, + 0xb0, 0xf4, 0xc8, 0x17, 0x0c, 0x0a, 0x96, 0x14, 0x7c, 0x09, 0xb7, 0x6c, + 0xe1, 0xc2, 0xd8, 0x55, 0xd4, 0x18, 0xa0, 0xaa, 0x41, 0x69, 0x70, 0x24, + 0xa3, 0xb9, 0xef, 0xe9, 0x5a, 0xdc, 0x3e, 0xeb, 0x94, 0x4a, 0xf0, 0xb7, + 0xde, 0x5f, 0x0e, 0x76, 0xfa, 0xfb, 0xfb, 0x69, 0x03, 0x45, 0x40, 0x50, + 0xee, 0x72, 0x0c, 0xa4, 0x12, 0x86, 0x81, 0xcd, 0x13, 0xd1, 0x4e, 0xc4, + 0x3c, 0xca, 0x4e, 0x0d, 0xd2, 0x26, 0xf1, 0x00, 0xb7, 0xb4, 0xa6, 0xa2, + 0xe1, 0x6e, 0x7a, 0x81, 0xfd, 0x30, 0xac, 0x7a, 0x1f, 0xc7, 0x59, 0x7b, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1372807406 (0x51d360ee) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2 + Validity + Not Before: Oct 22 17:05:14 2014 GMT + Not After : Oct 23 07:33:22 2024 GMT + Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Certification Authority - L1K + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:3f:96:d0:4d:b9:2f:44:e7:db:39:5e:9b:50: + ee:5c:a5:61:da:41:67:53:09:aa:00:9a:8e:57:7f: + 29:6b:db:c7:e1:21:24:aa:3a:d0:8d:47:23:d2:ed: + 72:16:f0:91:21:d2:5d:b7:b8:4b:a8:83:8f:b7:91: + 32:68:cf:ce:25:93:2c:b2:7d:97:c8:fe:c1:b4:17: + ba:09:9e:03:90:93:7b:7c:49:83:22:68:8a:9b:de: + 47:c3:31:98:7a:2e:7d:40:0b:d2:ef:3e:d3:b2:8c: + aa:8f:48:a9:ff:00:e8:29:58:06:f7:b6:93:5a:94: + 73:26:26:ad:58:0e:e5:42:b8:d5:ea:73:79:64:68: + 53:25:b8:84:cf:94:7a:ae:06:45:0c:a3:6b:4d:d0: + c6:be:ea:18:a4:36:f0:92:b2:ba:1c:88:8f:3a:52: + 7f:f7:5e:6d:83:1c:9d:f0:1f:e5:c3:d6:dd:a5:78: + 92:3d:b0:6d:2c:ea:c9:cf:94:41:19:71:44:68:ba: + 47:3c:04:e9:5d:ba:3e:f0:35:f7:15:b6:9e:f2:2e: + 15:1e:3f:47:c8:c8:38:a7:73:45:5d:4d:b0:3b:b1: + 8e:17:29:37:ea:dd:05:01:22:bb:94:36:2a:8d:5b: + 35:fe:53:19:2f:08:46:c1:2a:b3:1a:62:1d:4e:2b: + d9:1b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: + CA:TRUE, pathlen:0 + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.entrust.net/g2ca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/rpa + + X509v3 Subject Key Identifier: + 82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF + X509v3 Authority Key Identifier: + keyid:6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB + + Signature Algorithm: sha256WithRSAEncryption + 3f:1c:1a:5b:ff:40:22:1d:8f:35:0c:2d:aa:99:27:ab:c0:11: + 32:70:d7:36:28:69:a5:8d:b1:27:99:42:be:c4:93:eb:48:57: + 43:71:23:c4:e5:4e:ad:ae:43:6f:92:76:c5:19:ef:ca:bc:6f: + 42:4c:16:9a:86:a9:04:38:c7:65:f0:f5:0c:e0:4a:df:a2:fa: + ce:1a:11:a8:9c:69:2f:1b:df:ea:e2:32:f3:ce:4c:bc:46:0c: + c0:89:80:d1:87:6b:a2:cf:6b:d4:7f:fd:f5:60:52:67:57:a0: + 6d:d1:64:41:14:6d:34:62:ed:06:6c:24:f2:06:bc:28:02:af: + 03:2d:c2:33:05:fb:cb:aa:16:e8:65:10:43:f5:69:5c:e3:81: + 58:99:cd:6b:d3:b8:c7:7b:19:55:c9:40:ce:79:55:b8:73:89: + e9:5c:40:66:43:12:7f:07:b8:65:56:d5:8d:c3:a7:f5:b1:b6: + 65:9e:c0:83:36:7f:16:45:3c:74:4b:93:8a:3c:f1:2b:f5:35: + 70:73:7b:e7:82:04:b1:18:98:0e:d4:9c:6f:1a:fc:fc:a7:33: + a5:bb:bb:18:f3:6b:7a:5d:32:87:f7:6d:25:e4:e2:76:86:21: + 1e:11:46:cd:76:0e:6f:4f:a4:21:71:0a:84:a7:2d:36:a9:48: + 22:51:7e:82 +-----BEGIN CERTIFICATE----- +MIIFAzCCA+ugAwIBAgIEUdNg7jANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMTQxMDIyMTcw +NTE0WhcNMjQxMDIzMDczMzIyWjCBujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eSAtIEwxSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANo/ltBNuS9E59s5XptQ7lylYdpBZ1MJqgCajld/KWvbx+EhJKo60I1HI9Ltchbw +kSHSXbe4S6iDj7eRMmjPziWTLLJ9l8j+wbQXugmeA5CTe3xJgyJoipveR8MxmHou +fUAL0u8+07KMqo9Iqf8A6ClYBve2k1qUcyYmrVgO5UK41epzeWRoUyW4hM+Ueq4G +RQyja03Qxr7qGKQ28JKyuhyIjzpSf/debYMcnfAf5cPW3aV4kj2wbSzqyc+UQRlx +RGi6RzwE6V26PvA19xW2nvIuFR4/R8jIOKdzRV1NsDuxjhcpN+rdBQEiu5Q2Ko1b +Nf5TGS8IRsEqsxpiHU4r2RsCAwEAAaOCAQkwggEFMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMECDAGAQH/AgEAMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0 +cDovL29jc3AuZW50cnVzdC5uZXQwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2Ny +bC5lbnRydXN0Lm5ldC9nMmNhLmNybDA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggr +BgEFBQcCARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwHQYDVR0OBBYEFIKi +cHTdvFM/z3vU981/p2DGCky/MB8GA1UdIwQYMBaAFGpyJnrQHu995ztpUdRsjZ+Q +EmarMA0GCSqGSIb3DQEBCwUAA4IBAQA/HBpb/0AiHY81DC2qmSerwBEycNc2KGml +jbEnmUK+xJPrSFdDcSPE5U6trkNvknbFGe/KvG9CTBaahqkEOMdl8PUM4ErfovrO +GhGonGkvG9/q4jLzzky8RgzAiYDRh2uiz2vUf/31YFJnV6Bt0WRBFG00Yu0GbCTy +BrwoAq8DLcIzBfvLqhboZRBD9Wlc44FYmc1r07jHexlVyUDOeVW4c4npXEBmQxJ/ +B7hlVtWNw6f1sbZlnsCDNn8WRTx0S5OKPPEr9TVwc3vnggSxGJgO1JxvGvz8pzOl +u7sY82t6XTKH920l5OJ2hiEeEUbNdg5vT6QhcQqEpy02qUgiUX6C +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert40[] = { + 0x30, 0x82, 0x05, 0x03, 0x30, 0x82, 0x03, 0xeb, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x51, 0xd3, 0x60, 0xee, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xbe, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x1f, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, + 0x61, 0x6c, 0x2d, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, + 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, + 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x32, 0x32, 0x31, 0x37, 0x30, + 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x31, 0x30, 0x32, 0x33, + 0x30, 0x37, 0x33, 0x33, 0x32, 0x32, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, + 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, + 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x32, + 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, + 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, + 0x20, 0x4c, 0x31, 0x4b, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xda, 0x3f, 0x96, 0xd0, 0x4d, 0xb9, 0x2f, 0x44, 0xe7, 0xdb, 0x39, + 0x5e, 0x9b, 0x50, 0xee, 0x5c, 0xa5, 0x61, 0xda, 0x41, 0x67, 0x53, 0x09, + 0xaa, 0x00, 0x9a, 0x8e, 0x57, 0x7f, 0x29, 0x6b, 0xdb, 0xc7, 0xe1, 0x21, + 0x24, 0xaa, 0x3a, 0xd0, 0x8d, 0x47, 0x23, 0xd2, 0xed, 0x72, 0x16, 0xf0, + 0x91, 0x21, 0xd2, 0x5d, 0xb7, 0xb8, 0x4b, 0xa8, 0x83, 0x8f, 0xb7, 0x91, + 0x32, 0x68, 0xcf, 0xce, 0x25, 0x93, 0x2c, 0xb2, 0x7d, 0x97, 0xc8, 0xfe, + 0xc1, 0xb4, 0x17, 0xba, 0x09, 0x9e, 0x03, 0x90, 0x93, 0x7b, 0x7c, 0x49, + 0x83, 0x22, 0x68, 0x8a, 0x9b, 0xde, 0x47, 0xc3, 0x31, 0x98, 0x7a, 0x2e, + 0x7d, 0x40, 0x0b, 0xd2, 0xef, 0x3e, 0xd3, 0xb2, 0x8c, 0xaa, 0x8f, 0x48, + 0xa9, 0xff, 0x00, 0xe8, 0x29, 0x58, 0x06, 0xf7, 0xb6, 0x93, 0x5a, 0x94, + 0x73, 0x26, 0x26, 0xad, 0x58, 0x0e, 0xe5, 0x42, 0xb8, 0xd5, 0xea, 0x73, + 0x79, 0x64, 0x68, 0x53, 0x25, 0xb8, 0x84, 0xcf, 0x94, 0x7a, 0xae, 0x06, + 0x45, 0x0c, 0xa3, 0x6b, 0x4d, 0xd0, 0xc6, 0xbe, 0xea, 0x18, 0xa4, 0x36, + 0xf0, 0x92, 0xb2, 0xba, 0x1c, 0x88, 0x8f, 0x3a, 0x52, 0x7f, 0xf7, 0x5e, + 0x6d, 0x83, 0x1c, 0x9d, 0xf0, 0x1f, 0xe5, 0xc3, 0xd6, 0xdd, 0xa5, 0x78, + 0x92, 0x3d, 0xb0, 0x6d, 0x2c, 0xea, 0xc9, 0xcf, 0x94, 0x41, 0x19, 0x71, + 0x44, 0x68, 0xba, 0x47, 0x3c, 0x04, 0xe9, 0x5d, 0xba, 0x3e, 0xf0, 0x35, + 0xf7, 0x15, 0xb6, 0x9e, 0xf2, 0x2e, 0x15, 0x1e, 0x3f, 0x47, 0xc8, 0xc8, + 0x38, 0xa7, 0x73, 0x45, 0x5d, 0x4d, 0xb0, 0x3b, 0xb1, 0x8e, 0x17, 0x29, + 0x37, 0xea, 0xdd, 0x05, 0x01, 0x22, 0xbb, 0x94, 0x36, 0x2a, 0x8d, 0x5b, + 0x35, 0xfe, 0x53, 0x19, 0x2f, 0x08, 0x46, 0xc1, 0x2a, 0xb3, 0x1a, 0x62, + 0x1d, 0x4e, 0x2b, 0xd9, 0x1b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x09, 0x30, 0x82, 0x01, 0x05, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, + 0x02, 0x01, 0x00, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x30, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, + 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x2f, 0x67, 0x32, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x82, 0xa2, + 0x70, 0x74, 0xdd, 0xbc, 0x53, 0x3f, 0xcf, 0x7b, 0xd4, 0xf7, 0xcd, 0x7f, + 0xa7, 0x60, 0xc6, 0x0a, 0x4c, 0xbf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, + 0x1e, 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, + 0x12, 0x66, 0xab, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3f, + 0x1c, 0x1a, 0x5b, 0xff, 0x40, 0x22, 0x1d, 0x8f, 0x35, 0x0c, 0x2d, 0xaa, + 0x99, 0x27, 0xab, 0xc0, 0x11, 0x32, 0x70, 0xd7, 0x36, 0x28, 0x69, 0xa5, + 0x8d, 0xb1, 0x27, 0x99, 0x42, 0xbe, 0xc4, 0x93, 0xeb, 0x48, 0x57, 0x43, + 0x71, 0x23, 0xc4, 0xe5, 0x4e, 0xad, 0xae, 0x43, 0x6f, 0x92, 0x76, 0xc5, + 0x19, 0xef, 0xca, 0xbc, 0x6f, 0x42, 0x4c, 0x16, 0x9a, 0x86, 0xa9, 0x04, + 0x38, 0xc7, 0x65, 0xf0, 0xf5, 0x0c, 0xe0, 0x4a, 0xdf, 0xa2, 0xfa, 0xce, + 0x1a, 0x11, 0xa8, 0x9c, 0x69, 0x2f, 0x1b, 0xdf, 0xea, 0xe2, 0x32, 0xf3, + 0xce, 0x4c, 0xbc, 0x46, 0x0c, 0xc0, 0x89, 0x80, 0xd1, 0x87, 0x6b, 0xa2, + 0xcf, 0x6b, 0xd4, 0x7f, 0xfd, 0xf5, 0x60, 0x52, 0x67, 0x57, 0xa0, 0x6d, + 0xd1, 0x64, 0x41, 0x14, 0x6d, 0x34, 0x62, 0xed, 0x06, 0x6c, 0x24, 0xf2, + 0x06, 0xbc, 0x28, 0x02, 0xaf, 0x03, 0x2d, 0xc2, 0x33, 0x05, 0xfb, 0xcb, + 0xaa, 0x16, 0xe8, 0x65, 0x10, 0x43, 0xf5, 0x69, 0x5c, 0xe3, 0x81, 0x58, + 0x99, 0xcd, 0x6b, 0xd3, 0xb8, 0xc7, 0x7b, 0x19, 0x55, 0xc9, 0x40, 0xce, + 0x79, 0x55, 0xb8, 0x73, 0x89, 0xe9, 0x5c, 0x40, 0x66, 0x43, 0x12, 0x7f, + 0x07, 0xb8, 0x65, 0x56, 0xd5, 0x8d, 0xc3, 0xa7, 0xf5, 0xb1, 0xb6, 0x65, + 0x9e, 0xc0, 0x83, 0x36, 0x7f, 0x16, 0x45, 0x3c, 0x74, 0x4b, 0x93, 0x8a, + 0x3c, 0xf1, 0x2b, 0xf5, 0x35, 0x70, 0x73, 0x7b, 0xe7, 0x82, 0x04, 0xb1, + 0x18, 0x98, 0x0e, 0xd4, 0x9c, 0x6f, 0x1a, 0xfc, 0xfc, 0xa7, 0x33, 0xa5, + 0xbb, 0xbb, 0x18, 0xf3, 0x6b, 0x7a, 0x5d, 0x32, 0x87, 0xf7, 0x6d, 0x25, + 0xe4, 0xe2, 0x76, 0x86, 0x21, 0x1e, 0x11, 0x46, 0xcd, 0x76, 0x0e, 0x6f, + 0x4f, 0xa4, 0x21, 0x71, 0x0a, 0x84, 0xa7, 0x2d, 0x36, 0xa9, 0x48, 0x22, + 0x51, 0x7e, 0x82, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120038507 (0x727a46b) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Validity + Not Before: Apr 2 14:36:10 2014 GMT + Not After : Apr 2 14:35:52 2021 GMT + Subject: C=NL, L=Amsterdam, O=Verizon Enterprise Solutions, OU=Cybertrust, CN=Verizon Akamai SureServer CA G14-SHA2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:dd:6e:9e:02:69:02:b5:a3:99:2e:08:64:32:6a: + 59:f3:c6:9e:a6:20:07:d2:48:d1:a8:93:c7:ea:47: + 8f:83:39:40:d7:20:5d:8d:9a:ba:ab:d8:70:ec:9d: + 88:d1:bd:62:f6:db:ec:9d:5e:35:01:76:03:23:e5: + 6f:d2:af:46:35:59:5a:5c:d1:a8:23:c1:eb:e9:20: + d4:49:d6:3f:00:d8:a8:22:de:43:79:81:ac:e9:a4: + 92:f5:77:70:05:1e:5c:b6:a0:f7:90:a4:cd:ab:28: + 2c:90:c2:e7:0f:c3:af:1c:47:59:d5:84:2e:df:26: + 07:45:23:5a:c6:e8:90:c8:85:4b:8c:16:1e:60:f9: + 01:13:f1:14:1f:e6:e8:14:ed:c5:d2:6f:63:28:6e: + 72:8c:49:ae:08:72:c7:93:95:b4:0b:0c:ae:8f:9a: + 67:84:f5:57:1b:db:81:d7:17:9d:41:11:43:19:bd: + 6d:4a:85:ed:8f:70:25:ab:66:ab:f6:fa:6d:1c:3c: + ab:ed:17:bd:56:84:e1:db:75:33:b2:28:4b:99:8e: + f9:4b:82:33:50:9f:92:53:ed:fa:ad:0f:95:9c:a3: + f2:cb:60:f0:77:1d:c9:01:8b:5f:2d:86:be:bf:36: + b8:24:96:13:7c:c1:86:5a:6c:c1:48:2a:7f:3e:93: + 60:c5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:2 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6334.1.50 + CPS: https://secure.omniroot.com/repository + + Authority Information Access: + OCSP - URI:http://ocsp.omniroot.com/baltimoreroot + CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.crt + CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.der + + X509v3 Key Usage: critical + Digital Signature, Non Repudiation, Certificate Sign, CRL Sign + X509v3 Authority Key Identifier: + keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl + + X509v3 Subject Key Identifier: + F8:BD:FA:AF:73:77:C6:C7:1B:F9:4B:4D:11:A7:D1:33:AF:AF:72:11 + Signature Algorithm: sha256WithRSAEncryption + 80:d9:7a:ed:72:05:37:8f:61:aa:73:7c:9a:6a:fc:fe:01:e2: + 19:81:70:07:25:32:b0:f0:6f:3b:c7:6a:28:3d:e4:51:87:e6: + 7e:82:ec:ae:48:a7:b1:77:38:c2:d6:56:af:8f:f2:01:fc:65: + 65:10:09:f7:74:29:b5:0e:92:ee:90:98:d1:88:a2:65:b7:cd: + 9c:0e:a7:86:98:28:bc:ae:15:83:b6:1a:d7:1d:ec:19:da:7a: + 8e:40:f9:99:15:d5:7d:a5:ba:ab:fd:26:98:6e:9c:41:3b:b6: + 81:18:ec:70:48:d7:6e:7f:a6:e1:77:25:d6:dd:62:e8:52:f3: + 8c:16:39:67:e2:22:0d:77:2e:fb:11:6c:e4:dd:38:b4:27:5f: + 03:a8:3d:44:e2:f2:84:4b:84:fd:56:a6:9e:4d:7b:a2:16:4f: + 07:f5:34:24:72:a5:a2:fa:16:66:2a:a4:4a:0e:c8:0d:27:44: + 9c:77:d4:12:10:87:d2:00:2c:7a:bb:8e:88:22:91:15:be:a2: + 59:ca:34:e0:1c:61:94:86:20:33:cd:e7:4c:5d:3b:92:3e:cb: + d6:2d:ea:54:fa:fb:af:54:f5:a8:c5:0b:ca:8b:87:00:e6:9f: + e6:95:bf:b7:c4:a3:59:f5:16:6c:5f:3e:69:55:80:39:f6:75: + 50:14:3e:32 +-----BEGIN CERTIFICATE----- +MIIFHzCCBAegAwIBAgIEByekazANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDQwMjE0MzYxMFoX +DTIxMDQwMjE0MzU1MlowgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJk +YW0xJTAjBgNVBAoTHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNV +BAsTCkN5YmVydHJ1c3QxLjAsBgNVBAMTJVZlcml6b24gQWthbWFpIFN1cmVTZXJ2 +ZXIgQ0EgRzE0LVNIQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDd +bp4CaQK1o5kuCGQyalnzxp6mIAfSSNGok8fqR4+DOUDXIF2Nmrqr2HDsnYjRvWL2 +2+ydXjUBdgMj5W/Sr0Y1WVpc0agjwevpINRJ1j8A2Kgi3kN5gazppJL1d3AFHly2 +oPeQpM2rKCyQwucPw68cR1nVhC7fJgdFI1rG6JDIhUuMFh5g+QET8RQf5ugU7cXS +b2MobnKMSa4IcseTlbQLDK6PmmeE9Vcb24HXF51BEUMZvW1Khe2PcCWrZqv2+m0c +PKvtF71WhOHbdTOyKEuZjvlLgjNQn5JT7fqtD5Wco/LLYPB3HckBi18thr6/Nrgk +lhN8wYZabMFIKn8+k2DFAgMBAAGjggG3MIIBszASBgNVHRMBAf8ECDAGAQH/AgEC +MEwGA1UdIARFMEMwQQYJKwYBBAGxPgEyMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v +c2VjdXJlLm9tbmlyb290LmNvbS9yZXBvc2l0b3J5MIG6BggrBgEFBQcBAQSBrTCB +qjAyBggrBgEFBQcwAYYmaHR0cDovL29jc3Aub21uaXJvb3QuY29tL2JhbHRpbW9y +ZXJvb3QwOQYIKwYBBQUHMAKGLWh0dHBzOi8vY2FjZXJ0Lm9tbmlyb290LmNvbS9i +YWx0aW1vcmVyb290LmNydDA5BggrBgEFBQcwAoYtaHR0cHM6Ly9jYWNlcnQub21u +aXJvb3QuY29tL2JhbHRpbW9yZXJvb3QuZGVyMA4GA1UdDwEB/wQEAwIBxjAfBgNV +HSMEGDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DBCBgNVHR8EOzA5MDegNaAzhjFo +dHRwOi8vY2RwMS5wdWJsaWMtdHJ1c3QuY29tL0NSTC9PbW5pcm9vdDIwMjUuY3Js +MB0GA1UdDgQWBBT4vfqvc3fGxxv5S00Rp9Ezr69yETANBgkqhkiG9w0BAQsFAAOC +AQEAgNl67XIFN49hqnN8mmr8/gHiGYFwByUysPBvO8dqKD3kUYfmfoLsrkinsXc4 +wtZWr4/yAfxlZRAJ93QptQ6S7pCY0YiiZbfNnA6nhpgovK4Vg7Ya1x3sGdp6jkD5 +mRXVfaW6q/0mmG6cQTu2gRjscEjXbn+m4Xcl1t1i6FLzjBY5Z+IiDXcu+xFs5N04 +tCdfA6g9ROLyhEuE/Vamnk17ohZPB/U0JHKlovoWZiqkSg7IDSdEnHfUEhCH0gAs +eruOiCKRFb6iWco04BxhlIYgM83nTF07kj7L1i3qVPr7r1T1qMULyouHAOaf5pW/ +t8SjWfUWbF8+aVWAOfZ1UBQ+Mg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert41[] = { + 0x30, 0x82, 0x05, 0x1f, 0x30, 0x82, 0x04, 0x07, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0xa4, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, + 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x36, 0x31, 0x30, 0x5a, 0x17, + 0x0d, 0x32, 0x31, 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x35, 0x35, + 0x32, 0x5a, 0x30, 0x81, 0x8d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x13, 0x09, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64, + 0x61, 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x1c, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x45, 0x6e, 0x74, + 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x20, 0x53, 0x6f, 0x6c, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x25, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x41, 0x6b, 0x61, + 0x6d, 0x61, 0x69, 0x20, 0x53, 0x75, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x47, 0x31, 0x34, 0x2d, 0x53, 0x48, + 0x41, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdd, + 0x6e, 0x9e, 0x02, 0x69, 0x02, 0xb5, 0xa3, 0x99, 0x2e, 0x08, 0x64, 0x32, + 0x6a, 0x59, 0xf3, 0xc6, 0x9e, 0xa6, 0x20, 0x07, 0xd2, 0x48, 0xd1, 0xa8, + 0x93, 0xc7, 0xea, 0x47, 0x8f, 0x83, 0x39, 0x40, 0xd7, 0x20, 0x5d, 0x8d, + 0x9a, 0xba, 0xab, 0xd8, 0x70, 0xec, 0x9d, 0x88, 0xd1, 0xbd, 0x62, 0xf6, + 0xdb, 0xec, 0x9d, 0x5e, 0x35, 0x01, 0x76, 0x03, 0x23, 0xe5, 0x6f, 0xd2, + 0xaf, 0x46, 0x35, 0x59, 0x5a, 0x5c, 0xd1, 0xa8, 0x23, 0xc1, 0xeb, 0xe9, + 0x20, 0xd4, 0x49, 0xd6, 0x3f, 0x00, 0xd8, 0xa8, 0x22, 0xde, 0x43, 0x79, + 0x81, 0xac, 0xe9, 0xa4, 0x92, 0xf5, 0x77, 0x70, 0x05, 0x1e, 0x5c, 0xb6, + 0xa0, 0xf7, 0x90, 0xa4, 0xcd, 0xab, 0x28, 0x2c, 0x90, 0xc2, 0xe7, 0x0f, + 0xc3, 0xaf, 0x1c, 0x47, 0x59, 0xd5, 0x84, 0x2e, 0xdf, 0x26, 0x07, 0x45, + 0x23, 0x5a, 0xc6, 0xe8, 0x90, 0xc8, 0x85, 0x4b, 0x8c, 0x16, 0x1e, 0x60, + 0xf9, 0x01, 0x13, 0xf1, 0x14, 0x1f, 0xe6, 0xe8, 0x14, 0xed, 0xc5, 0xd2, + 0x6f, 0x63, 0x28, 0x6e, 0x72, 0x8c, 0x49, 0xae, 0x08, 0x72, 0xc7, 0x93, + 0x95, 0xb4, 0x0b, 0x0c, 0xae, 0x8f, 0x9a, 0x67, 0x84, 0xf5, 0x57, 0x1b, + 0xdb, 0x81, 0xd7, 0x17, 0x9d, 0x41, 0x11, 0x43, 0x19, 0xbd, 0x6d, 0x4a, + 0x85, 0xed, 0x8f, 0x70, 0x25, 0xab, 0x66, 0xab, 0xf6, 0xfa, 0x6d, 0x1c, + 0x3c, 0xab, 0xed, 0x17, 0xbd, 0x56, 0x84, 0xe1, 0xdb, 0x75, 0x33, 0xb2, + 0x28, 0x4b, 0x99, 0x8e, 0xf9, 0x4b, 0x82, 0x33, 0x50, 0x9f, 0x92, 0x53, + 0xed, 0xfa, 0xad, 0x0f, 0x95, 0x9c, 0xa3, 0xf2, 0xcb, 0x60, 0xf0, 0x77, + 0x1d, 0xc9, 0x01, 0x8b, 0x5f, 0x2d, 0x86, 0xbe, 0xbf, 0x36, 0xb8, 0x24, + 0x96, 0x13, 0x7c, 0xc1, 0x86, 0x5a, 0x6c, 0xc1, 0x48, 0x2a, 0x7f, 0x3e, + 0x93, 0x60, 0xc5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xb7, + 0x30, 0x82, 0x01, 0xb3, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02, + 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, + 0x41, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x32, + 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, + 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x81, 0xba, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xad, 0x30, 0x81, + 0xaa, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x01, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, + 0x73, 0x70, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, + 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d, + 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, + 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, + 0x2e, 0x63, 0x72, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, + 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, + 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x2e, + 0x64, 0x65, 0x72, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0xc6, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, + 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, + 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, + 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf8, + 0xbd, 0xfa, 0xaf, 0x73, 0x77, 0xc6, 0xc7, 0x1b, 0xf9, 0x4b, 0x4d, 0x11, + 0xa7, 0xd1, 0x33, 0xaf, 0xaf, 0x72, 0x11, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x01, 0x00, 0x80, 0xd9, 0x7a, 0xed, 0x72, 0x05, 0x37, 0x8f, 0x61, + 0xaa, 0x73, 0x7c, 0x9a, 0x6a, 0xfc, 0xfe, 0x01, 0xe2, 0x19, 0x81, 0x70, + 0x07, 0x25, 0x32, 0xb0, 0xf0, 0x6f, 0x3b, 0xc7, 0x6a, 0x28, 0x3d, 0xe4, + 0x51, 0x87, 0xe6, 0x7e, 0x82, 0xec, 0xae, 0x48, 0xa7, 0xb1, 0x77, 0x38, + 0xc2, 0xd6, 0x56, 0xaf, 0x8f, 0xf2, 0x01, 0xfc, 0x65, 0x65, 0x10, 0x09, + 0xf7, 0x74, 0x29, 0xb5, 0x0e, 0x92, 0xee, 0x90, 0x98, 0xd1, 0x88, 0xa2, + 0x65, 0xb7, 0xcd, 0x9c, 0x0e, 0xa7, 0x86, 0x98, 0x28, 0xbc, 0xae, 0x15, + 0x83, 0xb6, 0x1a, 0xd7, 0x1d, 0xec, 0x19, 0xda, 0x7a, 0x8e, 0x40, 0xf9, + 0x99, 0x15, 0xd5, 0x7d, 0xa5, 0xba, 0xab, 0xfd, 0x26, 0x98, 0x6e, 0x9c, + 0x41, 0x3b, 0xb6, 0x81, 0x18, 0xec, 0x70, 0x48, 0xd7, 0x6e, 0x7f, 0xa6, + 0xe1, 0x77, 0x25, 0xd6, 0xdd, 0x62, 0xe8, 0x52, 0xf3, 0x8c, 0x16, 0x39, + 0x67, 0xe2, 0x22, 0x0d, 0x77, 0x2e, 0xfb, 0x11, 0x6c, 0xe4, 0xdd, 0x38, + 0xb4, 0x27, 0x5f, 0x03, 0xa8, 0x3d, 0x44, 0xe2, 0xf2, 0x84, 0x4b, 0x84, + 0xfd, 0x56, 0xa6, 0x9e, 0x4d, 0x7b, 0xa2, 0x16, 0x4f, 0x07, 0xf5, 0x34, + 0x24, 0x72, 0xa5, 0xa2, 0xfa, 0x16, 0x66, 0x2a, 0xa4, 0x4a, 0x0e, 0xc8, + 0x0d, 0x27, 0x44, 0x9c, 0x77, 0xd4, 0x12, 0x10, 0x87, 0xd2, 0x00, 0x2c, + 0x7a, 0xbb, 0x8e, 0x88, 0x22, 0x91, 0x15, 0xbe, 0xa2, 0x59, 0xca, 0x34, + 0xe0, 0x1c, 0x61, 0x94, 0x86, 0x20, 0x33, 0xcd, 0xe7, 0x4c, 0x5d, 0x3b, + 0x92, 0x3e, 0xcb, 0xd6, 0x2d, 0xea, 0x54, 0xfa, 0xfb, 0xaf, 0x54, 0xf5, + 0xa8, 0xc5, 0x0b, 0xca, 0x8b, 0x87, 0x00, 0xe6, 0x9f, 0xe6, 0x95, 0xbf, + 0xb7, 0xc4, 0xa3, 0x59, 0xf5, 0x16, 0x6c, 0x5f, 0x3e, 0x69, 0x55, 0x80, + 0x39, 0xf6, 0x75, 0x50, 0x14, 0x3e, 0x32, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 7e:e1:4a:6f:6f:ef:f2:d3:7f:3f:ad:65:4d:3a:da:b4 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 EV SSL CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d8:a1:65:74:23:e8:2b:64:e2:32:d7:33:37:3d: + 8e:f5:34:16:48:dd:4f:7f:87:1c:f8:44:23:13:8e: + fb:11:d8:44:5a:18:71:8e:60:16:26:92:9b:fd:17: + 0b:e1:71:70:42:fe:bf:fa:1c:c0:aa:a3:a7:b5:71: + e8:ff:18:83:f6:df:10:0a:13:62:c8:3d:9c:a7:de: + 2e:3f:0c:d9:1d:e7:2e:fb:2a:ce:c8:9a:7f:87:bf: + d8:4c:04:15:32:c9:d1:cc:95:71:a0:4e:28:4f:84: + d9:35:fb:e3:86:6f:94:53:e6:72:8a:63:67:2e:be: + 69:f6:f7:6e:8e:9c:60:04:eb:29:fa:c4:47:42:d2: + 78:98:e3:ec:0b:a5:92:dc:b7:9a:bd:80:64:2b:38: + 7c:38:09:5b:66:f6:2d:95:7a:86:b2:34:2e:85:9e: + 90:0e:5f:b7:5d:a4:51:72:46:70:13:bf:67:f2:b6: + a7:4d:14:1e:6c:b9:53:ee:23:1a:4e:8d:48:55:43: + 41:b1:89:75:6a:40:28:c5:7d:dd:d2:6e:d2:02:19: + 2f:7b:24:94:4b:eb:f1:1a:a9:9b:e3:23:9a:ea:fa: + 33:ab:0a:2c:b7:f4:60:08:dd:9f:1c:cd:dd:2d:01: + 66:80:af:b3:2f:29:1d:23:b8:8a:e1:a1:70:07:0c: + 34:0f + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://s2.symcb.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.symauth.com/cps + User Notice: + Explicit Text: http://www.symauth.com/rpa + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://s1.symcb.com/pca3-g5.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-533 + X509v3 Subject Key Identifier: + 01:59:AB:E7:DD:3A:0B:59:A6:64:63:D6:CF:20:07:57:D5:91:E7:6A + X509v3 Authority Key Identifier: + keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + + Signature Algorithm: sha256WithRSAEncryption + 42:01:55:7b:d0:16:1a:5d:58:e8:bb:9b:a8:4d:d7:f3:d7:eb: + 13:94:86:d6:7f:21:0b:47:bc:57:9b:92:5d:4f:05:9f:38:a4: + 10:7c:cf:83:be:06:43:46:8d:08:bc:6a:d7:10:a6:fa:ab:af: + 2f:61:a8:63:f2:65:df:7f:4c:88:12:88:4f:b3:69:d9:ff:27: + c0:0a:97:91:8f:56:fb:89:c4:a8:bb:92:2d:1b:73:b0:c6:ab: + 36:f4:96:6c:20:08:ef:0a:1e:66:24:45:4f:67:00:40:c8:07: + 54:74:33:3b:a6:ad:bb:23:9f:66:ed:a2:44:70:34:fb:0e:ea: + 01:fd:cf:78:74:df:a7:ad:55:b7:5f:4d:f6:d6:3f:e0:86:ce: + 24:c7:42:a9:13:14:44:35:4b:b6:df:c9:60:ac:0c:7f:d9:93: + 21:4b:ee:9c:e4:49:02:98:d3:60:7b:5c:bc:d5:30:2f:07:ce: + 44:42:c4:0b:99:fe:e6:9f:fc:b0:78:86:51:6d:d1:2c:9d:c6: + 96:fb:85:82:bb:04:2f:f7:62:80:ef:62:da:7f:f6:0e:ac:90: + b8:56:bd:79:3f:f2:80:6e:a3:d9:b9:0f:5d:3a:07:1d:91:93: + 86:4b:29:4c:e1:dc:b5:e1:e0:33:9d:b3:cb:36:91:4b:fe:a1: + b4:ee:f0:f9 +-----BEGIN CERTIFICATE----- +MIIFKzCCBBOgAwIBAgIQfuFKb2/v8tN/P61lTTratDANBgkqhkiG9w0BAQsFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB3MQsw +CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV +BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIENs +YXNzIDMgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDYoWV0I+grZOIy1zM3PY71NBZI3U9/hxz4RCMTjvsR2ERaGHGOYBYmkpv9 +FwvhcXBC/r/6HMCqo6e1cej/GIP23xAKE2LIPZyn3i4/DNkd5y77Ks7Imn+Hv9hM +BBUyydHMlXGgTihPhNk1++OGb5RT5nKKY2cuvmn2926OnGAE6yn6xEdC0niY4+wL +pZLct5q9gGQrOHw4CVtm9i2VeoayNC6FnpAOX7ddpFFyRnATv2fytqdNFB5suVPu +IxpOjUhVQ0GxiXVqQCjFfd3SbtICGS97JJRL6/EaqZvjI5rq+jOrCiy39GAI3Z8c +zd0tAWaAr7MvKR0juIrhoXAHDDQPAgMBAAGjggFdMIIBWTAvBggrBgEFBQcBAQQj +MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wEgYDVR0TAQH/BAgw +BgEB/wIBADBlBgNVHSAEXjBcMFoGBFUdIAAwUjAmBggrBgEFBQcCARYaaHR0cDov +L3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIwHBoaaHR0cDovL3d3dy5z +eW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3MxLnN5bWNi +LmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx +GjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTMzMB0GA1UdDgQWBBQBWavn3ToLWaZk +Y9bPIAdX1ZHnajAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq +hkiG9w0BAQsFAAOCAQEAQgFVe9AWGl1Y6LubqE3X89frE5SG1n8hC0e8V5uSXU8F +nzikEHzPg74GQ0aNCLxq1xCm+quvL2GoY/Jl339MiBKIT7Np2f8nwAqXkY9W+4nE +qLuSLRtzsMarNvSWbCAI7woeZiRFT2cAQMgHVHQzO6atuyOfZu2iRHA0+w7qAf3P +eHTfp61Vt19N9tY/4IbOJMdCqRMURDVLtt/JYKwMf9mTIUvunORJApjTYHtcvNUw +LwfORELEC5n+5p/8sHiGUW3RLJ3GlvuFgrsEL/digO9i2n/2DqyQuFa9eT/ygG6j +2bkPXToHHZGThkspTOHcteHgM52zyzaRS/6htO7w+Q== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert42[] = { + 0x30, 0x82, 0x05, 0x2b, 0x30, 0x82, 0x04, 0x13, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x7e, 0xe1, 0x4a, 0x6f, 0x6f, 0xef, 0xf2, 0xd3, 0x7f, + 0x3f, 0xad, 0x65, 0x4d, 0x3a, 0xda, 0xb4, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x77, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d, + 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x1f, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c, + 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xd8, 0xa1, 0x65, 0x74, 0x23, 0xe8, 0x2b, + 0x64, 0xe2, 0x32, 0xd7, 0x33, 0x37, 0x3d, 0x8e, 0xf5, 0x34, 0x16, 0x48, + 0xdd, 0x4f, 0x7f, 0x87, 0x1c, 0xf8, 0x44, 0x23, 0x13, 0x8e, 0xfb, 0x11, + 0xd8, 0x44, 0x5a, 0x18, 0x71, 0x8e, 0x60, 0x16, 0x26, 0x92, 0x9b, 0xfd, + 0x17, 0x0b, 0xe1, 0x71, 0x70, 0x42, 0xfe, 0xbf, 0xfa, 0x1c, 0xc0, 0xaa, + 0xa3, 0xa7, 0xb5, 0x71, 0xe8, 0xff, 0x18, 0x83, 0xf6, 0xdf, 0x10, 0x0a, + 0x13, 0x62, 0xc8, 0x3d, 0x9c, 0xa7, 0xde, 0x2e, 0x3f, 0x0c, 0xd9, 0x1d, + 0xe7, 0x2e, 0xfb, 0x2a, 0xce, 0xc8, 0x9a, 0x7f, 0x87, 0xbf, 0xd8, 0x4c, + 0x04, 0x15, 0x32, 0xc9, 0xd1, 0xcc, 0x95, 0x71, 0xa0, 0x4e, 0x28, 0x4f, + 0x84, 0xd9, 0x35, 0xfb, 0xe3, 0x86, 0x6f, 0x94, 0x53, 0xe6, 0x72, 0x8a, + 0x63, 0x67, 0x2e, 0xbe, 0x69, 0xf6, 0xf7, 0x6e, 0x8e, 0x9c, 0x60, 0x04, + 0xeb, 0x29, 0xfa, 0xc4, 0x47, 0x42, 0xd2, 0x78, 0x98, 0xe3, 0xec, 0x0b, + 0xa5, 0x92, 0xdc, 0xb7, 0x9a, 0xbd, 0x80, 0x64, 0x2b, 0x38, 0x7c, 0x38, + 0x09, 0x5b, 0x66, 0xf6, 0x2d, 0x95, 0x7a, 0x86, 0xb2, 0x34, 0x2e, 0x85, + 0x9e, 0x90, 0x0e, 0x5f, 0xb7, 0x5d, 0xa4, 0x51, 0x72, 0x46, 0x70, 0x13, + 0xbf, 0x67, 0xf2, 0xb6, 0xa7, 0x4d, 0x14, 0x1e, 0x6c, 0xb9, 0x53, 0xee, + 0x23, 0x1a, 0x4e, 0x8d, 0x48, 0x55, 0x43, 0x41, 0xb1, 0x89, 0x75, 0x6a, + 0x40, 0x28, 0xc5, 0x7d, 0xdd, 0xd2, 0x6e, 0xd2, 0x02, 0x19, 0x2f, 0x7b, + 0x24, 0x94, 0x4b, 0xeb, 0xf1, 0x1a, 0xa9, 0x9b, 0xe3, 0x23, 0x9a, 0xea, + 0xfa, 0x33, 0xab, 0x0a, 0x2c, 0xb7, 0xf4, 0x60, 0x08, 0xdd, 0x9f, 0x1c, + 0xcd, 0xdd, 0x2d, 0x01, 0x66, 0x80, 0xaf, 0xb3, 0x2f, 0x29, 0x1d, 0x23, + 0xb8, 0x8a, 0xe1, 0xa1, 0x70, 0x07, 0x0c, 0x34, 0x0f, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5d, 0x30, 0x82, 0x01, 0x59, 0x30, 0x2f, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, + 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, + 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, + 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x65, 0x06, 0x03, 0x55, + 0x1d, 0x20, 0x04, 0x5e, 0x30, 0x5c, 0x30, 0x5a, 0x06, 0x04, 0x55, 0x1d, + 0x20, 0x00, 0x30, 0x52, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, + 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, + 0x70, 0x61, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, + 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, + 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29, 0x06, 0x03, + 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, + 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, + 0x35, 0x33, 0x33, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x01, 0x59, 0xab, 0xe7, 0xdd, 0x3a, 0x0b, 0x59, 0xa6, 0x64, + 0x63, 0xd6, 0xcf, 0x20, 0x07, 0x57, 0xd5, 0x91, 0xe7, 0x6a, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f, + 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43, + 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x01, 0x00, 0x42, 0x01, 0x55, 0x7b, 0xd0, 0x16, 0x1a, 0x5d, 0x58, + 0xe8, 0xbb, 0x9b, 0xa8, 0x4d, 0xd7, 0xf3, 0xd7, 0xeb, 0x13, 0x94, 0x86, + 0xd6, 0x7f, 0x21, 0x0b, 0x47, 0xbc, 0x57, 0x9b, 0x92, 0x5d, 0x4f, 0x05, + 0x9f, 0x38, 0xa4, 0x10, 0x7c, 0xcf, 0x83, 0xbe, 0x06, 0x43, 0x46, 0x8d, + 0x08, 0xbc, 0x6a, 0xd7, 0x10, 0xa6, 0xfa, 0xab, 0xaf, 0x2f, 0x61, 0xa8, + 0x63, 0xf2, 0x65, 0xdf, 0x7f, 0x4c, 0x88, 0x12, 0x88, 0x4f, 0xb3, 0x69, + 0xd9, 0xff, 0x27, 0xc0, 0x0a, 0x97, 0x91, 0x8f, 0x56, 0xfb, 0x89, 0xc4, + 0xa8, 0xbb, 0x92, 0x2d, 0x1b, 0x73, 0xb0, 0xc6, 0xab, 0x36, 0xf4, 0x96, + 0x6c, 0x20, 0x08, 0xef, 0x0a, 0x1e, 0x66, 0x24, 0x45, 0x4f, 0x67, 0x00, + 0x40, 0xc8, 0x07, 0x54, 0x74, 0x33, 0x3b, 0xa6, 0xad, 0xbb, 0x23, 0x9f, + 0x66, 0xed, 0xa2, 0x44, 0x70, 0x34, 0xfb, 0x0e, 0xea, 0x01, 0xfd, 0xcf, + 0x78, 0x74, 0xdf, 0xa7, 0xad, 0x55, 0xb7, 0x5f, 0x4d, 0xf6, 0xd6, 0x3f, + 0xe0, 0x86, 0xce, 0x24, 0xc7, 0x42, 0xa9, 0x13, 0x14, 0x44, 0x35, 0x4b, + 0xb6, 0xdf, 0xc9, 0x60, 0xac, 0x0c, 0x7f, 0xd9, 0x93, 0x21, 0x4b, 0xee, + 0x9c, 0xe4, 0x49, 0x02, 0x98, 0xd3, 0x60, 0x7b, 0x5c, 0xbc, 0xd5, 0x30, + 0x2f, 0x07, 0xce, 0x44, 0x42, 0xc4, 0x0b, 0x99, 0xfe, 0xe6, 0x9f, 0xfc, + 0xb0, 0x78, 0x86, 0x51, 0x6d, 0xd1, 0x2c, 0x9d, 0xc6, 0x96, 0xfb, 0x85, + 0x82, 0xbb, 0x04, 0x2f, 0xf7, 0x62, 0x80, 0xef, 0x62, 0xda, 0x7f, 0xf6, + 0x0e, 0xac, 0x90, 0xb8, 0x56, 0xbd, 0x79, 0x3f, 0xf2, 0x80, 0x6e, 0xa3, + 0xd9, 0xb9, 0x0f, 0x5d, 0x3a, 0x07, 0x1d, 0x91, 0x93, 0x86, 0x4b, 0x29, + 0x4c, 0xe1, 0xdc, 0xb5, 0xe1, 0xe0, 0x33, 0x9d, 0xb3, 0xcb, 0x36, 0x91, + 0x4b, 0xfe, 0xa1, 0xb4, 0xee, 0xf0, 0xf9, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 51:3f:b9:74:38:70:b7:34:40:41:8d:30:93:06:99:ff + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 Secure Server CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b2:d8:05:ca:1c:74:2d:b5:17:56:39:c5:4a:52: + 09:96:e8:4b:d8:0c:f1:68:9f:9a:42:28:62:c3:a5: + 30:53:7e:55:11:82:5b:03:7a:0d:2f:e1:79:04:c9: + b4:96:77:19:81:01:94:59:f9:bc:f7:7a:99:27:82: + 2d:b7:83:dd:5a:27:7f:b2:03:7a:9c:53:25:e9:48: + 1f:46:4f:c8:9d:29:f8:be:79:56:f6:f7:fd:d9:3a: + 68:da:8b:4b:82:33:41:12:c3:c8:3c:cc:d6:96:7a: + 84:21:1a:22:04:03:27:17:8b:1c:68:61:93:0f:0e: + 51:80:33:1d:b4:b5:ce:eb:7e:d0:62:ac:ee:b3:7b: + 01:74:ef:69:35:eb:ca:d5:3d:a9:ee:97:98:ca:8d: + aa:44:0e:25:99:4a:15:96:a4:ce:6d:02:54:1f:2a: + 6a:26:e2:06:3a:63:48:ac:b4:4c:d1:75:93:50:ff: + 13:2f:d6:da:e1:c6:18:f5:9f:c9:25:5d:f3:00:3a: + de:26:4d:b4:29:09:cd:0f:3d:23:6f:16:4a:81:16: + fb:f2:83:10:c3:b8:d6:d8:55:32:3d:f1:bd:0f:bd: + 8c:52:95:4a:16:97:7a:52:21:63:75:2f:16:f9:c4: + 66:be:f5:b5:09:d8:ff:27:00:cd:44:7c:6f:4b:3f: + b0:f7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://s1.symcb.com/pca3-g5.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://s2.symcb.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.symauth.com/cps + User Notice: + Explicit Text: http://www.symauth.com/rpa + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-534 + X509v3 Subject Key Identifier: + 5F:60:CF:61:90:55:DF:84:43:14:8A:60:2A:B2:F5:7A:F4:43:18:EF + X509v3 Authority Key Identifier: + keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + + Signature Algorithm: sha256WithRSAEncryption + 5e:94:56:49:dd:8e:2d:65:f5:c1:36:51:b6:03:e3:da:9e:73: + 19:f2:1f:59:ab:58:7e:6c:26:05:2c:fa:81:d7:5c:23:17:22: + 2c:37:93:f7:86:ec:85:e6:b0:a3:fd:1f:e2:32:a8:45:6f:e1: + d9:fb:b9:af:d2:70:a0:32:42:65:bf:84:fe:16:2a:8f:3f:c5: + a6:d6:a3:93:7d:43:e9:74:21:91:35:28:f4:63:e9:2e:ed:f7: + f5:5c:7f:4b:9a:b5:20:e9:0a:bd:e0:45:10:0c:14:94:9a:5d: + a5:e3:4b:91:e8:24:9b:46:40:65:f4:22:72:cd:99:f8:88:11: + f5:f3:7f:e6:33:82:e6:a8:c5:7e:fe:d0:08:e2:25:58:08:71: + 68:e6:cd:a2:e6:14:de:4e:52:24:2d:fd:e5:79:13:53:e7:5e: + 2f:2d:4d:1b:6d:40:15:52:2b:f7:87:89:78:12:81:6e:d9:4d: + aa:2d:78:d4:c2:2c:3d:08:5f:87:91:9e:1f:0e:b0:de:30:52: + 64:86:89:aa:9d:66:9c:0e:76:0c:80:f2:74:d8:2a:f8:b8:3a: + ce:d7:d6:0f:11:be:6b:ab:14:f5:bd:41:a0:22:63:89:f1:ba: + 0f:6f:29:63:66:2d:3f:ac:8c:72:c5:fb:c7:e4:d4:0f:f2:3b: + 4f:8c:29:c7 +-----BEGIN CERTIFICATE----- +MIIFODCCBCCgAwIBAgIQUT+5dDhwtzRAQY0wkwaZ/zANBgkqhkiG9w0BAQsFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB+MQsw +CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV +BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVjIENs +YXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAstgFyhx0LbUXVjnFSlIJluhL2AzxaJ+aQihiw6UwU35VEYJb +A3oNL+F5BMm0lncZgQGUWfm893qZJ4Itt4PdWid/sgN6nFMl6UgfRk/InSn4vnlW +9vf92Tpo2otLgjNBEsPIPMzWlnqEIRoiBAMnF4scaGGTDw5RgDMdtLXO637QYqzu +s3sBdO9pNevK1T2p7peYyo2qRA4lmUoVlqTObQJUHypqJuIGOmNIrLRM0XWTUP8T +L9ba4cYY9Z/JJV3zADreJk20KQnNDz0jbxZKgRb78oMQw7jW2FUyPfG9D72MUpVK +Fpd6UiFjdS8W+cRmvvW1Cdj/JwDNRHxvSz+w9wIDAQABo4IBYzCCAV8wEgYDVR0T +AQH/BAgwBgEB/wIBADAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vczEuc3ltY2Iu +Y29tL3BjYTMtZzUuY3JsMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQjMCEw +HwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wawYDVR0gBGQwYjBgBgpg +hkgBhvhFAQc2MFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20v +Y3BzMCgGCCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20vcnBhMCkG +A1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTUzNDAdBgNVHQ4E +FgQUX2DPYZBV34RDFIpgKrL1evRDGO8wHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnz +Qzn6Aq8zMTMwDQYJKoZIhvcNAQELBQADggEBAF6UVkndji1l9cE2UbYD49qecxny +H1mrWH5sJgUs+oHXXCMXIiw3k/eG7IXmsKP9H+IyqEVv4dn7ua/ScKAyQmW/hP4W +Ko8/xabWo5N9Q+l0IZE1KPRj6S7t9/Vcf0uatSDpCr3gRRAMFJSaXaXjS5HoJJtG +QGX0InLNmfiIEfXzf+YzguaoxX7+0AjiJVgIcWjmzaLmFN5OUiQt/eV5E1PnXi8t +TRttQBVSK/eHiXgSgW7ZTaoteNTCLD0IX4eRnh8OsN4wUmSGiaqdZpwOdgyA8nTY +Kvi4Os7X1g8RvmurFPW9QaAiY4nxug9vKWNmLT+sjHLF+8fk1A/yO0+MKcc= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert43[] = { + 0x30, 0x82, 0x05, 0x38, 0x30, 0x82, 0x04, 0x20, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x51, 0x3f, 0xb9, 0x74, 0x38, 0x70, 0xb7, 0x34, 0x40, + 0x41, 0x8d, 0x30, 0x93, 0x06, 0x99, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x7e, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d, + 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x26, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, + 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d, + 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xb2, 0xd8, 0x05, 0xca, 0x1c, 0x74, 0x2d, 0xb5, 0x17, 0x56, 0x39, 0xc5, + 0x4a, 0x52, 0x09, 0x96, 0xe8, 0x4b, 0xd8, 0x0c, 0xf1, 0x68, 0x9f, 0x9a, + 0x42, 0x28, 0x62, 0xc3, 0xa5, 0x30, 0x53, 0x7e, 0x55, 0x11, 0x82, 0x5b, + 0x03, 0x7a, 0x0d, 0x2f, 0xe1, 0x79, 0x04, 0xc9, 0xb4, 0x96, 0x77, 0x19, + 0x81, 0x01, 0x94, 0x59, 0xf9, 0xbc, 0xf7, 0x7a, 0x99, 0x27, 0x82, 0x2d, + 0xb7, 0x83, 0xdd, 0x5a, 0x27, 0x7f, 0xb2, 0x03, 0x7a, 0x9c, 0x53, 0x25, + 0xe9, 0x48, 0x1f, 0x46, 0x4f, 0xc8, 0x9d, 0x29, 0xf8, 0xbe, 0x79, 0x56, + 0xf6, 0xf7, 0xfd, 0xd9, 0x3a, 0x68, 0xda, 0x8b, 0x4b, 0x82, 0x33, 0x41, + 0x12, 0xc3, 0xc8, 0x3c, 0xcc, 0xd6, 0x96, 0x7a, 0x84, 0x21, 0x1a, 0x22, + 0x04, 0x03, 0x27, 0x17, 0x8b, 0x1c, 0x68, 0x61, 0x93, 0x0f, 0x0e, 0x51, + 0x80, 0x33, 0x1d, 0xb4, 0xb5, 0xce, 0xeb, 0x7e, 0xd0, 0x62, 0xac, 0xee, + 0xb3, 0x7b, 0x01, 0x74, 0xef, 0x69, 0x35, 0xeb, 0xca, 0xd5, 0x3d, 0xa9, + 0xee, 0x97, 0x98, 0xca, 0x8d, 0xaa, 0x44, 0x0e, 0x25, 0x99, 0x4a, 0x15, + 0x96, 0xa4, 0xce, 0x6d, 0x02, 0x54, 0x1f, 0x2a, 0x6a, 0x26, 0xe2, 0x06, + 0x3a, 0x63, 0x48, 0xac, 0xb4, 0x4c, 0xd1, 0x75, 0x93, 0x50, 0xff, 0x13, + 0x2f, 0xd6, 0xda, 0xe1, 0xc6, 0x18, 0xf5, 0x9f, 0xc9, 0x25, 0x5d, 0xf3, + 0x00, 0x3a, 0xde, 0x26, 0x4d, 0xb4, 0x29, 0x09, 0xcd, 0x0f, 0x3d, 0x23, + 0x6f, 0x16, 0x4a, 0x81, 0x16, 0xfb, 0xf2, 0x83, 0x10, 0xc3, 0xb8, 0xd6, + 0xd8, 0x55, 0x32, 0x3d, 0xf1, 0xbd, 0x0f, 0xbd, 0x8c, 0x52, 0x95, 0x4a, + 0x16, 0x97, 0x7a, 0x52, 0x21, 0x63, 0x75, 0x2f, 0x16, 0xf9, 0xc4, 0x66, + 0xbe, 0xf5, 0xb5, 0x09, 0xd8, 0xff, 0x27, 0x00, 0xcd, 0x44, 0x7c, 0x6f, + 0x4b, 0x3f, 0xb0, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, + 0x63, 0x30, 0x82, 0x01, 0x5f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, + 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, + 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, + 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x32, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x6b, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x64, 0x30, 0x62, 0x30, 0x60, 0x06, 0x0a, 0x60, + 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x52, 0x30, + 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, + 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, + 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x29, 0x06, + 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, + 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, + 0x2d, 0x35, 0x33, 0x34, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x5f, 0x60, 0xcf, 0x61, 0x90, 0x55, 0xdf, 0x84, 0x43, + 0x14, 0x8a, 0x60, 0x2a, 0xb2, 0xf5, 0x7a, 0xf4, 0x43, 0x18, 0xef, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x5e, 0x94, 0x56, 0x49, 0xdd, 0x8e, 0x2d, 0x65, + 0xf5, 0xc1, 0x36, 0x51, 0xb6, 0x03, 0xe3, 0xda, 0x9e, 0x73, 0x19, 0xf2, + 0x1f, 0x59, 0xab, 0x58, 0x7e, 0x6c, 0x26, 0x05, 0x2c, 0xfa, 0x81, 0xd7, + 0x5c, 0x23, 0x17, 0x22, 0x2c, 0x37, 0x93, 0xf7, 0x86, 0xec, 0x85, 0xe6, + 0xb0, 0xa3, 0xfd, 0x1f, 0xe2, 0x32, 0xa8, 0x45, 0x6f, 0xe1, 0xd9, 0xfb, + 0xb9, 0xaf, 0xd2, 0x70, 0xa0, 0x32, 0x42, 0x65, 0xbf, 0x84, 0xfe, 0x16, + 0x2a, 0x8f, 0x3f, 0xc5, 0xa6, 0xd6, 0xa3, 0x93, 0x7d, 0x43, 0xe9, 0x74, + 0x21, 0x91, 0x35, 0x28, 0xf4, 0x63, 0xe9, 0x2e, 0xed, 0xf7, 0xf5, 0x5c, + 0x7f, 0x4b, 0x9a, 0xb5, 0x20, 0xe9, 0x0a, 0xbd, 0xe0, 0x45, 0x10, 0x0c, + 0x14, 0x94, 0x9a, 0x5d, 0xa5, 0xe3, 0x4b, 0x91, 0xe8, 0x24, 0x9b, 0x46, + 0x40, 0x65, 0xf4, 0x22, 0x72, 0xcd, 0x99, 0xf8, 0x88, 0x11, 0xf5, 0xf3, + 0x7f, 0xe6, 0x33, 0x82, 0xe6, 0xa8, 0xc5, 0x7e, 0xfe, 0xd0, 0x08, 0xe2, + 0x25, 0x58, 0x08, 0x71, 0x68, 0xe6, 0xcd, 0xa2, 0xe6, 0x14, 0xde, 0x4e, + 0x52, 0x24, 0x2d, 0xfd, 0xe5, 0x79, 0x13, 0x53, 0xe7, 0x5e, 0x2f, 0x2d, + 0x4d, 0x1b, 0x6d, 0x40, 0x15, 0x52, 0x2b, 0xf7, 0x87, 0x89, 0x78, 0x12, + 0x81, 0x6e, 0xd9, 0x4d, 0xaa, 0x2d, 0x78, 0xd4, 0xc2, 0x2c, 0x3d, 0x08, + 0x5f, 0x87, 0x91, 0x9e, 0x1f, 0x0e, 0xb0, 0xde, 0x30, 0x52, 0x64, 0x86, + 0x89, 0xaa, 0x9d, 0x66, 0x9c, 0x0e, 0x76, 0x0c, 0x80, 0xf2, 0x74, 0xd8, + 0x2a, 0xf8, 0xb8, 0x3a, 0xce, 0xd7, 0xd6, 0x0f, 0x11, 0xbe, 0x6b, 0xab, + 0x14, 0xf5, 0xbd, 0x41, 0xa0, 0x22, 0x63, 0x89, 0xf1, 0xba, 0x0f, 0x6f, + 0x29, 0x63, 0x66, 0x2d, 0x3f, 0xac, 0x8c, 0x72, 0xc5, 0xfb, 0xc7, 0xe4, + 0xd4, 0x0f, 0xf2, 0x3b, 0x4f, 0x8c, 0x29, 0xc7, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120036009 (0x7279aa9) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Validity + Not Before: Dec 19 20:07:32 2013 GMT + Not After : Dec 19 20:06:55 2017 GMT + Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24: + 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72: + 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88: + 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1: + d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03: + e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42: + f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe: + 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29: + e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18: + 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51: + af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41: + 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a: + 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a: + 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94: + 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67: + 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae: + ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be: + ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34: + 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f: + e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29: + 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d: + 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08: + 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf: + ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2: + 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08: + 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0: + 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70: + 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7: + d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09: + c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18: + 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33: + a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13: + 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01: + a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41: + 4a:a8:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6334.1.0 + CPS: http://cybertrust.omniroot.com/repository.cfm + + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Authority Key Identifier: + keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl + + X509v3 Subject Key Identifier: + 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5 + Signature Algorithm: sha256WithRSAEncryption + 76:85:c5:23:31:1f:b4:73:ea:a0:bc:a5:ed:df:45:43:6a:7f: + 69:20:1b:80:b2:fb:1c:dd:aa:7f:88:d3:31:41:36:f7:fb:fb: + 6b:ad:98:8c:78:1f:9d:11:67:3a:cd:4b:ec:a8:bc:9d:15:19: + c4:3b:0b:a7:93:ce:e8:fc:9d:5b:e8:1f:cb:56:ae:76:43:2b: + c7:13:51:77:41:a8:66:4c:5f:a7:d1:d7:aa:75:c5:1b:29:4c: + c9:f4:6d:a1:5e:a1:85:93:16:c2:cb:3b:ab:14:7d:44:fd:da: + 25:29:86:2a:fe:63:20:ca:d2:0b:c2:34:15:bb:af:5b:7f:8a: + e0:aa:ed:45:a6:ea:79:db:d8:35:66:54:43:de:37:33:d1:e4: + e0:cd:57:ca:71:b0:7d:e9:16:77:64:e8:59:97:b9:d5:2e:d1: + b4:91:da:77:71:f3:4a:0f:48:d2:34:99:60:95:37:ac:1f:01: + cd:10:9d:e8:2a:a5:20:c7:50:9b:b3:6c:49:78:2b:58:92:64: + 89:b8:95:36:a8:34:aa:f0:41:d2:95:5a:24:54:97:4d:6e:05: + c4:95:ad:c4:7a:a3:39:fb:79:06:8a:9b:a6:4f:d9:22:fa:44: + 4e:36:f3:c9:0f:a6:39:e7:80:b2:5e:bf:bd:39:d1:46:e5:55: + 47:db:bc:6e +-----BEGIN CERTIFICATE----- +MIIFhjCCBG6gAwIBAgIEByeaqTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEzMTIxOTIwMDczMloX +DTE3MTIxOTIwMDY1NVowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n +dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y +YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0 +IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3 +p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu +e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA +iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R +PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg +lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU +mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R +DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV +S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq +qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE +OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o +/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCASAwggEcMBIGA1Ud +EwEB/wQIMAYBAf8CAQAwUwYDVR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEF +BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku +Y2ZtMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH +AwIwHwYDVR0jBBgwFoAU5Z1ZMIJHWMys+ghUNoZ7OrUETfAwQgYDVR0fBDswOTA3 +oDWgM4YxaHR0cDovL2NkcDEucHVibGljLXRydXN0LmNvbS9DUkwvT21uaXJvb3Qy +MDI1LmNybDAdBgNVHQ4EFgQUUa8kJpz0aCJXgCYrO0ZiFXsezKUwDQYJKoZIhvcN +AQELBQADggEBAHaFxSMxH7Rz6qC8pe3fRUNqf2kgG4Cy+xzdqn+I0zFBNvf7+2ut +mIx4H50RZzrNS+yovJ0VGcQ7C6eTzuj8nVvoH8tWrnZDK8cTUXdBqGZMX6fR16p1 +xRspTMn0baFeoYWTFsLLO6sUfUT92iUphir+YyDK0gvCNBW7r1t/iuCq7UWm6nnb +2DVmVEPeNzPR5ODNV8pxsH3pFndk6FmXudUu0bSR2ndx80oPSNI0mWCVN6wfAc0Q +negqpSDHUJuzbEl4K1iSZIm4lTaoNKrwQdKVWiRUl01uBcSVrcR6ozn7eQaKm6ZP +2SL6RE4288kPpjnngLJev7050UblVUfbvG4= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert44[] = { + 0x30, 0x82, 0x05, 0x86, 0x30, 0x82, 0x04, 0x6e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0x9a, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, + 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x37, 0x33, 0x32, 0x5a, 0x17, + 0x0d, 0x31, 0x37, 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x36, 0x35, + 0x35, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, + 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, + 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, + 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, + 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37, + 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37, + 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d, + 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10, + 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae, + 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30, + 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2, + 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77, + 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0, + 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9, + 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09, + 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57, + 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1, + 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb, + 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8, + 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21, + 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60, + 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb, + 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4, + 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a, + 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14, + 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae, + 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca, + 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99, + 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11, + 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b, + 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57, + 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02, + 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5, + 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c, + 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb, + 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90, + 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa, + 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d, + 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae, + 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86, + 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4, + 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a, + 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6, + 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8, + 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68, + 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0, + 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10, + 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, + 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, + 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, + 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x02, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, + 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, + 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, + 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, + 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, + 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4, + 0x68, 0x22, 0x57, 0x80, 0x26, 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e, + 0xcc, 0xa5, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x76, 0x85, + 0xc5, 0x23, 0x31, 0x1f, 0xb4, 0x73, 0xea, 0xa0, 0xbc, 0xa5, 0xed, 0xdf, + 0x45, 0x43, 0x6a, 0x7f, 0x69, 0x20, 0x1b, 0x80, 0xb2, 0xfb, 0x1c, 0xdd, + 0xaa, 0x7f, 0x88, 0xd3, 0x31, 0x41, 0x36, 0xf7, 0xfb, 0xfb, 0x6b, 0xad, + 0x98, 0x8c, 0x78, 0x1f, 0x9d, 0x11, 0x67, 0x3a, 0xcd, 0x4b, 0xec, 0xa8, + 0xbc, 0x9d, 0x15, 0x19, 0xc4, 0x3b, 0x0b, 0xa7, 0x93, 0xce, 0xe8, 0xfc, + 0x9d, 0x5b, 0xe8, 0x1f, 0xcb, 0x56, 0xae, 0x76, 0x43, 0x2b, 0xc7, 0x13, + 0x51, 0x77, 0x41, 0xa8, 0x66, 0x4c, 0x5f, 0xa7, 0xd1, 0xd7, 0xaa, 0x75, + 0xc5, 0x1b, 0x29, 0x4c, 0xc9, 0xf4, 0x6d, 0xa1, 0x5e, 0xa1, 0x85, 0x93, + 0x16, 0xc2, 0xcb, 0x3b, 0xab, 0x14, 0x7d, 0x44, 0xfd, 0xda, 0x25, 0x29, + 0x86, 0x2a, 0xfe, 0x63, 0x20, 0xca, 0xd2, 0x0b, 0xc2, 0x34, 0x15, 0xbb, + 0xaf, 0x5b, 0x7f, 0x8a, 0xe0, 0xaa, 0xed, 0x45, 0xa6, 0xea, 0x79, 0xdb, + 0xd8, 0x35, 0x66, 0x54, 0x43, 0xde, 0x37, 0x33, 0xd1, 0xe4, 0xe0, 0xcd, + 0x57, 0xca, 0x71, 0xb0, 0x7d, 0xe9, 0x16, 0x77, 0x64, 0xe8, 0x59, 0x97, + 0xb9, 0xd5, 0x2e, 0xd1, 0xb4, 0x91, 0xda, 0x77, 0x71, 0xf3, 0x4a, 0x0f, + 0x48, 0xd2, 0x34, 0x99, 0x60, 0x95, 0x37, 0xac, 0x1f, 0x01, 0xcd, 0x10, + 0x9d, 0xe8, 0x2a, 0xa5, 0x20, 0xc7, 0x50, 0x9b, 0xb3, 0x6c, 0x49, 0x78, + 0x2b, 0x58, 0x92, 0x64, 0x89, 0xb8, 0x95, 0x36, 0xa8, 0x34, 0xaa, 0xf0, + 0x41, 0xd2, 0x95, 0x5a, 0x24, 0x54, 0x97, 0x4d, 0x6e, 0x05, 0xc4, 0x95, + 0xad, 0xc4, 0x7a, 0xa3, 0x39, 0xfb, 0x79, 0x06, 0x8a, 0x9b, 0xa6, 0x4f, + 0xd9, 0x22, 0xfa, 0x44, 0x4e, 0x36, 0xf3, 0xc9, 0x0f, 0xa6, 0x39, 0xe7, + 0x80, 0xb2, 0x5e, 0xbf, 0xbd, 0x39, 0xd1, 0x46, 0xe5, 0x55, 0x47, 0xdb, + 0xbc, 0x6e, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 67:3f:33:4f:21:53:36:52:c3:5e:15:d2:fd:b3:02:0f + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign + Validity + Not Before: Aug 8 01:00:05 2009 GMT + Not After : Aug 8 01:00:05 2024 GMT + Subject: C=CN, O=WoSign CA Limited, CN=WoSign Class 3 OV Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bc:89:be:61:51:53:c8:2b:96:75:b3:5a:d3:0e: + 34:fe:4a:c2:9f:a3:18:83:a2:ac:e3:2e:5e:93:79: + 0b:13:49:5e:93:b2:8f:84:10:ed:91:8f:82:ba:ad: + 67:df:33:1b:ae:84:f2:55:b0:5b:f4:b3:9e:bc:e6: + 04:0f:1d:ef:04:5a:a8:0b:ec:12:6d:56:19:64:70: + 49:0f:57:92:f3:5f:21:a6:4d:b4:d2:96:2b:3c:32: + b3:ef:8f:59:0b:14:ba:6e:a2:9e:71:db:f2:88:3f: + 28:3b:ec:ce:be:47:ac:45:c7:8a:9e:fa:61:93:c5: + 49:17:b6:46:b6:f7:99:16:8c:1c:6e:31:ae:69:ce: + ed:c6:24:92:70:a1:cb:96:c3:6c:16:d0:ee:cc:4f: + 86:33:b3:41:e6:3d:3d:db:0e:8c:33:74:bb:c3:fc: + 0b:a7:fc:d1:71:e2:c1:0c:d4:f7:ba:3e:80:90:d4: + 48:eb:a2:83:70:d8:db:30:07:29:89:f9:81:21:2c: + ff:eb:47:f6:7a:6d:43:96:67:17:3e:f3:e2:73:51: + c7:76:1e:e9:1c:a0:ec:11:1a:b1:cf:1e:2d:9c:55: + ee:3b:c6:2d:ae:dc:66:65:91:a2:66:9c:ac:82:f1: + a4:17:b5:d7:43:83:c3:88:a0:64:de:ca:72:45:dc: + 38:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Client Authentication, TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crls1.wosign.com/ca1.crl + + Authority Information Access: + OCSP - URI:http://ocsp1.wosign.com/ca1 + CA Issuers - URI:http://aia1.wosign.com/ca1-class3-server.cer + + X509v3 Subject Key Identifier: + 62:2E:81:D9:E3:42:79:14:A3:CD:D9:54:8A:6E:F8:DE:95:AA:8F:98 + X509v3 Authority Key Identifier: + keyid:E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E + + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.36305.1.3.2 + CPS: http://www.wosign.com/policy/ + + Signature Algorithm: sha1WithRSAEncryption + ab:70:aa:64:c4:0b:34:91:b9:63:20:5e:b0:9c:21:ff:25:79: + 6c:57:4e:56:44:58:83:b9:00:ce:2d:65:a8:6d:95:38:ea:82: + 2d:55:18:60:12:7e:1a:1d:6b:62:34:2c:d9:cd:17:00:43:84: + 3e:ad:bc:ff:26:85:1f:4a:a7:46:13:b0:7d:3b:0b:d9:4b:9d: + b0:cf:8d:f4:05:cb:12:29:fe:e1:97:c7:b7:c7:aa:53:7e:39: + 2d:9d:f6:d4:5e:b7:8c:15:6a:81:d2:37:1a:43:0e:cb:e6:30: + 21:43:83:69:0f:ef:6b:cd:10:f9:84:60:cf:89:e9:88:10:01: + af:09:f3:48:bb:07:09:75:01:84:fa:b1:1e:51:19:8f:c6:c9: + 85:65:16:5f:e0:56:7e:b7:bf:40:c2:d4:d0:05:1f:93:63:c9: + 24:08:3b:91:b2:35:e1:a4:8f:35:db:24:58:75:39:e4:dd:10: + 1a:b0:df:13:12:73:9e:6d:e7:67:3c:db:1c:1c:dd:10:dd:cc: + f4:07:09:b9:2e:e5:75:6d:97:b7:60:5b:89:70:81:d2:26:d8: + c6:09:2b:b2:05:7f:c4:b8:14:41:1e:07:f0:48:41:63:cb:0c: + aa:45:7e:84:f9:33:b3:58:87:bc:b1:d6:c2:65:c7:57:c6:95: + e8:85:90:b0:62:50:f5:ee:12:f1:d8:7e:73:cb:c0:c3:a0:25: + 17:23:37:91:ba:63:bd:84:af:f3:89:e0:51:c2:73:35:6d:63: + 86:21:f2:73:bd:c2:47:e0:4d:7e:46:37:4b:d0:f7:61:2a:c7: + 94:50:25:36:e8:ae:da:2e:1f:b8:08:b2:55:7c:6b:66:43:8f: + 02:1d:dd:a7:eb:98:00:a7:25:74:f5:93:1b:6d:26:bb:1d:e5: + b7:fc:21:25:26:d1:77:1b:a8:6e:aa:c3:4b:64:51:7f:91:0e: + 41:5c:19:83:a1:a8:1f:94:99:43:0f:99:db:18:dc:21:6f:76: + d1:9e:ea:a3:76:e0:f0:09:bc:b9:b4:f7:43:6c:1f:d3:2a:86: + 6a:2f:e0:6c:f1:83:39:d7:70:db:a2:91:ab:54:be:f4:47:88: + 8c:f0:10:d2:e4:ad:eb:7e:b1:ba:08:4b:67:04:a3:f2:e9:90: + 2b:81:e3:74:76:3d:00:9d:d2:bb:fc:a5:a0:15:1c:28:df:10: + 4f:47:d7:33:46:9d:b2:57:d2:c6:1f:fb:e4:59:4a:2b:28:a9: + 13:dd:b9:e9:93:b4:88:ee:e2:5b:a0:07:25:fe:8a:2e:78:e4: + b4:e1:d5:1d:f6:1a:3a:e3:1c:01:2a:1e:a1:86:54:9e:49:dc: + c9:59:e3:0d:6d:5a:13:36 +-----BEGIN CERTIFICATE----- +MIIFozCCA4ugAwIBAgIQZz8zTyFTNlLDXhXS/bMCDzANBgkqhkiG9w0BAQUFADBV +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV +BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw +MTAwMDVaFw0yNDA4MDgwMTAwMDVaME8xCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX +b1NpZ24gQ0EgTGltaXRlZDEkMCIGA1UEAxMbV29TaWduIENsYXNzIDMgT1YgU2Vy +dmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvIm+YVFTyCuW +dbNa0w40/krCn6MYg6Ks4y5ek3kLE0lek7KPhBDtkY+Cuq1n3zMbroTyVbBb9LOe +vOYEDx3vBFqoC+wSbVYZZHBJD1eS818hpk200pYrPDKz749ZCxS6bqKecdvyiD8o +O+zOvkesRceKnvphk8VJF7ZGtveZFowcbjGuac7txiSScKHLlsNsFtDuzE+GM7NB +5j092w6MM3S7w/wLp/zRceLBDNT3uj6AkNRI66KDcNjbMAcpifmBISz/60f2em1D +lmcXPvPic1HHdh7pHKDsERqxzx4tnFXuO8YtrtxmZZGiZpysgvGkF7XXQ4PDiKBk +3spyRdw4+wIDAQABo4IBczCCAW8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdJQQWMBQG +CCsGAQUFBwMCBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMDAGA1UdHwQp +MCcwJaAjoCGGH2h0dHA6Ly9jcmxzMS53b3NpZ24uY29tL2NhMS5jcmwwcQYIKwYB +BQUHAQEEZTBjMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcDEud29zaWduLmNvbS9j +YTEwOAYIKwYBBQUHMAKGLGh0dHA6Ly9haWExLndvc2lnbi5jb20vY2ExLWNsYXNz +My1zZXJ2ZXIuY2VyMB0GA1UdDgQWBBRiLoHZ40J5FKPN2VSKbvjelaqPmDAfBgNV +HSMEGDAWgBThZs8O0fGzS7cGIBT+hxLV9v77PjBFBgNVHSAEPjA8MDoGCysGAQQB +gptRAQMCMCswKQYIKwYBBQUHAgEWHWh0dHA6Ly93d3cud29zaWduLmNvbS9wb2xp +Y3kvMA0GCSqGSIb3DQEBBQUAA4ICAQCrcKpkxAs0kbljIF6wnCH/JXlsV05WRFiD +uQDOLWWobZU46oItVRhgEn4aHWtiNCzZzRcAQ4Q+rbz/JoUfSqdGE7B9OwvZS52w +z430BcsSKf7hl8e3x6pTfjktnfbUXreMFWqB0jcaQw7L5jAhQ4NpD+9rzRD5hGDP +iemIEAGvCfNIuwcJdQGE+rEeURmPxsmFZRZf4FZ+t79AwtTQBR+TY8kkCDuRsjXh +pI812yRYdTnk3RAasN8TEnOebednPNscHN0Q3cz0Bwm5LuV1bZe3YFuJcIHSJtjG +CSuyBX/EuBRBHgfwSEFjywyqRX6E+TOzWIe8sdbCZcdXxpXohZCwYlD17hLx2H5z +y8DDoCUXIzeRumO9hK/zieBRwnM1bWOGIfJzvcJH4E1+RjdL0PdhKseUUCU26K7a +Lh+4CLJVfGtmQ48CHd2n65gApyV09ZMbbSa7HeW3/CElJtF3G6huqsNLZFF/kQ5B +XBmDoagflJlDD5nbGNwhb3bRnuqjduDwCby5tPdDbB/TKoZqL+Bs8YM513DbopGr +VL70R4iM8BDS5K3rfrG6CEtnBKPy6ZArgeN0dj0AndK7/KWgFRwo3xBPR9czRp2y +V9LGH/vkWUorKKkT3bnpk7SI7uJboAcl/ooueOS04dUd9ho64xwBKh6hhlSeSdzJ +WeMNbVoTNg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert45[] = { + 0x30, 0x82, 0x05, 0xa3, 0x30, 0x82, 0x03, 0x8b, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x67, 0x3f, 0x33, 0x4f, 0x21, 0x53, 0x36, 0x52, 0xc3, + 0x5e, 0x15, 0xd2, 0xfd, 0xb3, 0x02, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x55, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, + 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, + 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67, + 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x38, 0x30, 0x38, 0x30, + 0x31, 0x30, 0x30, 0x30, 0x35, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x38, + 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x35, 0x5a, 0x30, 0x4f, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57, + 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x65, 0x64, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x1b, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x4f, 0x56, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xbc, 0x89, 0xbe, 0x61, 0x51, 0x53, 0xc8, 0x2b, 0x96, + 0x75, 0xb3, 0x5a, 0xd3, 0x0e, 0x34, 0xfe, 0x4a, 0xc2, 0x9f, 0xa3, 0x18, + 0x83, 0xa2, 0xac, 0xe3, 0x2e, 0x5e, 0x93, 0x79, 0x0b, 0x13, 0x49, 0x5e, + 0x93, 0xb2, 0x8f, 0x84, 0x10, 0xed, 0x91, 0x8f, 0x82, 0xba, 0xad, 0x67, + 0xdf, 0x33, 0x1b, 0xae, 0x84, 0xf2, 0x55, 0xb0, 0x5b, 0xf4, 0xb3, 0x9e, + 0xbc, 0xe6, 0x04, 0x0f, 0x1d, 0xef, 0x04, 0x5a, 0xa8, 0x0b, 0xec, 0x12, + 0x6d, 0x56, 0x19, 0x64, 0x70, 0x49, 0x0f, 0x57, 0x92, 0xf3, 0x5f, 0x21, + 0xa6, 0x4d, 0xb4, 0xd2, 0x96, 0x2b, 0x3c, 0x32, 0xb3, 0xef, 0x8f, 0x59, + 0x0b, 0x14, 0xba, 0x6e, 0xa2, 0x9e, 0x71, 0xdb, 0xf2, 0x88, 0x3f, 0x28, + 0x3b, 0xec, 0xce, 0xbe, 0x47, 0xac, 0x45, 0xc7, 0x8a, 0x9e, 0xfa, 0x61, + 0x93, 0xc5, 0x49, 0x17, 0xb6, 0x46, 0xb6, 0xf7, 0x99, 0x16, 0x8c, 0x1c, + 0x6e, 0x31, 0xae, 0x69, 0xce, 0xed, 0xc6, 0x24, 0x92, 0x70, 0xa1, 0xcb, + 0x96, 0xc3, 0x6c, 0x16, 0xd0, 0xee, 0xcc, 0x4f, 0x86, 0x33, 0xb3, 0x41, + 0xe6, 0x3d, 0x3d, 0xdb, 0x0e, 0x8c, 0x33, 0x74, 0xbb, 0xc3, 0xfc, 0x0b, + 0xa7, 0xfc, 0xd1, 0x71, 0xe2, 0xc1, 0x0c, 0xd4, 0xf7, 0xba, 0x3e, 0x80, + 0x90, 0xd4, 0x48, 0xeb, 0xa2, 0x83, 0x70, 0xd8, 0xdb, 0x30, 0x07, 0x29, + 0x89, 0xf9, 0x81, 0x21, 0x2c, 0xff, 0xeb, 0x47, 0xf6, 0x7a, 0x6d, 0x43, + 0x96, 0x67, 0x17, 0x3e, 0xf3, 0xe2, 0x73, 0x51, 0xc7, 0x76, 0x1e, 0xe9, + 0x1c, 0xa0, 0xec, 0x11, 0x1a, 0xb1, 0xcf, 0x1e, 0x2d, 0x9c, 0x55, 0xee, + 0x3b, 0xc6, 0x2d, 0xae, 0xdc, 0x66, 0x65, 0x91, 0xa2, 0x66, 0x9c, 0xac, + 0x82, 0xf1, 0xa4, 0x17, 0xb5, 0xd7, 0x43, 0x83, 0xc3, 0x88, 0xa0, 0x64, + 0xde, 0xca, 0x72, 0x45, 0xdc, 0x38, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x73, 0x30, 0x82, 0x01, 0x6f, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, + 0x02, 0x01, 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, + 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x31, 0x2e, 0x77, + 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, + 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x71, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x65, 0x30, 0x63, 0x30, 0x27, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x31, 0x2e, + 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x61, 0x31, 0x30, 0x38, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x02, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, + 0x69, 0x61, 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x31, 0x2d, 0x63, 0x6c, 0x61, 0x73, 0x73, + 0x33, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x65, 0x72, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62, + 0x2e, 0x81, 0xd9, 0xe3, 0x42, 0x79, 0x14, 0xa3, 0xcd, 0xd9, 0x54, 0x8a, + 0x6e, 0xf8, 0xde, 0x95, 0xaa, 0x8f, 0x98, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe1, 0x66, 0xcf, 0x0e, + 0xd1, 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14, 0xfe, 0x87, 0x12, 0xd5, + 0xf6, 0xfe, 0xfb, 0x3e, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x9b, 0x51, 0x01, 0x03, 0x02, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1d, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x6f, 0x73, + 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0xab, + 0x70, 0xaa, 0x64, 0xc4, 0x0b, 0x34, 0x91, 0xb9, 0x63, 0x20, 0x5e, 0xb0, + 0x9c, 0x21, 0xff, 0x25, 0x79, 0x6c, 0x57, 0x4e, 0x56, 0x44, 0x58, 0x83, + 0xb9, 0x00, 0xce, 0x2d, 0x65, 0xa8, 0x6d, 0x95, 0x38, 0xea, 0x82, 0x2d, + 0x55, 0x18, 0x60, 0x12, 0x7e, 0x1a, 0x1d, 0x6b, 0x62, 0x34, 0x2c, 0xd9, + 0xcd, 0x17, 0x00, 0x43, 0x84, 0x3e, 0xad, 0xbc, 0xff, 0x26, 0x85, 0x1f, + 0x4a, 0xa7, 0x46, 0x13, 0xb0, 0x7d, 0x3b, 0x0b, 0xd9, 0x4b, 0x9d, 0xb0, + 0xcf, 0x8d, 0xf4, 0x05, 0xcb, 0x12, 0x29, 0xfe, 0xe1, 0x97, 0xc7, 0xb7, + 0xc7, 0xaa, 0x53, 0x7e, 0x39, 0x2d, 0x9d, 0xf6, 0xd4, 0x5e, 0xb7, 0x8c, + 0x15, 0x6a, 0x81, 0xd2, 0x37, 0x1a, 0x43, 0x0e, 0xcb, 0xe6, 0x30, 0x21, + 0x43, 0x83, 0x69, 0x0f, 0xef, 0x6b, 0xcd, 0x10, 0xf9, 0x84, 0x60, 0xcf, + 0x89, 0xe9, 0x88, 0x10, 0x01, 0xaf, 0x09, 0xf3, 0x48, 0xbb, 0x07, 0x09, + 0x75, 0x01, 0x84, 0xfa, 0xb1, 0x1e, 0x51, 0x19, 0x8f, 0xc6, 0xc9, 0x85, + 0x65, 0x16, 0x5f, 0xe0, 0x56, 0x7e, 0xb7, 0xbf, 0x40, 0xc2, 0xd4, 0xd0, + 0x05, 0x1f, 0x93, 0x63, 0xc9, 0x24, 0x08, 0x3b, 0x91, 0xb2, 0x35, 0xe1, + 0xa4, 0x8f, 0x35, 0xdb, 0x24, 0x58, 0x75, 0x39, 0xe4, 0xdd, 0x10, 0x1a, + 0xb0, 0xdf, 0x13, 0x12, 0x73, 0x9e, 0x6d, 0xe7, 0x67, 0x3c, 0xdb, 0x1c, + 0x1c, 0xdd, 0x10, 0xdd, 0xcc, 0xf4, 0x07, 0x09, 0xb9, 0x2e, 0xe5, 0x75, + 0x6d, 0x97, 0xb7, 0x60, 0x5b, 0x89, 0x70, 0x81, 0xd2, 0x26, 0xd8, 0xc6, + 0x09, 0x2b, 0xb2, 0x05, 0x7f, 0xc4, 0xb8, 0x14, 0x41, 0x1e, 0x07, 0xf0, + 0x48, 0x41, 0x63, 0xcb, 0x0c, 0xaa, 0x45, 0x7e, 0x84, 0xf9, 0x33, 0xb3, + 0x58, 0x87, 0xbc, 0xb1, 0xd6, 0xc2, 0x65, 0xc7, 0x57, 0xc6, 0x95, 0xe8, + 0x85, 0x90, 0xb0, 0x62, 0x50, 0xf5, 0xee, 0x12, 0xf1, 0xd8, 0x7e, 0x73, + 0xcb, 0xc0, 0xc3, 0xa0, 0x25, 0x17, 0x23, 0x37, 0x91, 0xba, 0x63, 0xbd, + 0x84, 0xaf, 0xf3, 0x89, 0xe0, 0x51, 0xc2, 0x73, 0x35, 0x6d, 0x63, 0x86, + 0x21, 0xf2, 0x73, 0xbd, 0xc2, 0x47, 0xe0, 0x4d, 0x7e, 0x46, 0x37, 0x4b, + 0xd0, 0xf7, 0x61, 0x2a, 0xc7, 0x94, 0x50, 0x25, 0x36, 0xe8, 0xae, 0xda, + 0x2e, 0x1f, 0xb8, 0x08, 0xb2, 0x55, 0x7c, 0x6b, 0x66, 0x43, 0x8f, 0x02, + 0x1d, 0xdd, 0xa7, 0xeb, 0x98, 0x00, 0xa7, 0x25, 0x74, 0xf5, 0x93, 0x1b, + 0x6d, 0x26, 0xbb, 0x1d, 0xe5, 0xb7, 0xfc, 0x21, 0x25, 0x26, 0xd1, 0x77, + 0x1b, 0xa8, 0x6e, 0xaa, 0xc3, 0x4b, 0x64, 0x51, 0x7f, 0x91, 0x0e, 0x41, + 0x5c, 0x19, 0x83, 0xa1, 0xa8, 0x1f, 0x94, 0x99, 0x43, 0x0f, 0x99, 0xdb, + 0x18, 0xdc, 0x21, 0x6f, 0x76, 0xd1, 0x9e, 0xea, 0xa3, 0x76, 0xe0, 0xf0, + 0x09, 0xbc, 0xb9, 0xb4, 0xf7, 0x43, 0x6c, 0x1f, 0xd3, 0x2a, 0x86, 0x6a, + 0x2f, 0xe0, 0x6c, 0xf1, 0x83, 0x39, 0xd7, 0x70, 0xdb, 0xa2, 0x91, 0xab, + 0x54, 0xbe, 0xf4, 0x47, 0x88, 0x8c, 0xf0, 0x10, 0xd2, 0xe4, 0xad, 0xeb, + 0x7e, 0xb1, 0xba, 0x08, 0x4b, 0x67, 0x04, 0xa3, 0xf2, 0xe9, 0x90, 0x2b, + 0x81, 0xe3, 0x74, 0x76, 0x3d, 0x00, 0x9d, 0xd2, 0xbb, 0xfc, 0xa5, 0xa0, + 0x15, 0x1c, 0x28, 0xdf, 0x10, 0x4f, 0x47, 0xd7, 0x33, 0x46, 0x9d, 0xb2, + 0x57, 0xd2, 0xc6, 0x1f, 0xfb, 0xe4, 0x59, 0x4a, 0x2b, 0x28, 0xa9, 0x13, + 0xdd, 0xb9, 0xe9, 0x93, 0xb4, 0x88, 0xee, 0xe2, 0x5b, 0xa0, 0x07, 0x25, + 0xfe, 0x8a, 0x2e, 0x78, 0xe4, 0xb4, 0xe1, 0xd5, 0x1d, 0xf6, 0x1a, 0x3a, + 0xe3, 0x1c, 0x01, 0x2a, 0x1e, 0xa1, 0x86, 0x54, 0x9e, 0x49, 0xdc, 0xc9, + 0x59, 0xe3, 0x0d, 0x6d, 0x5a, 0x13, 0x36, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120040007 (0x727aa47) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Validity + Not Before: May 7 17:04:09 2014 GMT + Not After : May 7 17:03:30 2018 GMT + Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24: + 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72: + 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88: + 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1: + d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03: + e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42: + f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe: + 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29: + e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18: + 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51: + af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41: + 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a: + 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a: + 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94: + 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67: + 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae: + ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be: + ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34: + 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f: + e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29: + 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d: + 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08: + 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf: + ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2: + 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08: + 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0: + 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70: + 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7: + d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09: + c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18: + 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33: + a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13: + 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01: + a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41: + 4a:a8:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6334.1.0 + CPS: http://cybertrust.omniroot.com/repository.cfm + Policy: 1.3.6.1.4.1.311.42.1 + + Authority Information Access: + OCSP - URI:http://ocsp.omniroot.com/baltimoreroot + + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication, OCSP Signing + X509v3 Authority Key Identifier: + keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl + + X509v3 Subject Key Identifier: + 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5 + Signature Algorithm: sha256WithRSAEncryption + 69:62:f6:84:91:00:c4:6f:82:7b:24:e1:42:a2:a5:8b:82:5c: + a7:c5:44:cb:e7:52:76:63:d3:76:9e:78:e2:69:35:b1:38:ba: + b0:96:c6:1f:ac:7b:c6:b2:65:77:8b:7d:8d:ae:64:b9:a5:8c: + 17:ca:58:65:c3:ad:82:f5:c5:a2:f5:01:13:93:c6:7e:44:e5: + c4:61:fa:03:b6:56:c1:72:e1:c8:28:c5:69:21:8f:ac:6e:fd: + 7f:43:83:36:b8:c0:d6:a0:28:fe:1a:45:be:fd:93:8c:8d:a4: + 64:79:1f:14:db:a1:9f:21:dc:c0:4e:7b:17:22:17:b1:b6:3c: + d3:9b:e2:0a:a3:7e:99:b0:c1:ac:d8:f4:86:df:3c:da:7d:14: + 9c:40:c1:7c:d2:18:6f:f1:4f:26:45:09:95:94:5c:da:d0:98: + f8:f4:4c:82:96:10:de:ac:30:cb:2b:ae:f9:92:ea:bf:79:03: + fc:1e:3f:ac:09:a4:3f:65:fd:91:4f:96:24:a7:ce:b4:4e:6a: + 96:29:17:ae:c0:a8:df:17:22:f4:17:e3:dc:1c:39:06:56:10: + ea:ea:b5:74:17:3c:4e:dd:7e:91:0a:a8:0b:78:07:a7:31:44: + 08:31:ab:18:84:0f:12:9c:e7:de:84:2c:e9:6d:93:45:bf:a8: + c1:3f:34:dc +-----BEGIN CERTIFICATE----- +MIIF4TCCBMmgAwIBAgIEByeqRzANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDUwNzE3MDQwOVoX +DTE4MDUwNzE3MDMzMFowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n +dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y +YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0 +IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3 +p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu +e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA +iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R +PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg +lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU +mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R +DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV +S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq +qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE +OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o +/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCAXswggF3MBIGA1Ud +EwEB/wQIMAYBAf8CAQAwYAYDVR0gBFkwVzBIBgkrBgEEAbE+AQAwOzA5BggrBgEF +BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku +Y2ZtMAsGCSsGAQQBgjcqATBCBggrBgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGGJmh0 +dHA6Ly9vY3NwLm9tbmlyb290LmNvbS9iYWx0aW1vcmVyb290MA4GA1UdDwEB/wQE +AwIBhjAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMJMB8G +A1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaGezq1BE3wMEIGA1UdHwQ7MDkwN6A1oDOG +MWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVzdC5jb20vQ1JML09tbmlyb290MjAyNS5j +cmwwHQYDVR0OBBYEFFGvJCac9GgiV4AmKztGYhV7HsylMA0GCSqGSIb3DQEBCwUA +A4IBAQBpYvaEkQDEb4J7JOFCoqWLglynxUTL51J2Y9N2nnjiaTWxOLqwlsYfrHvG +smV3i32NrmS5pYwXylhlw62C9cWi9QETk8Z+ROXEYfoDtlbBcuHIKMVpIY+sbv1/ +Q4M2uMDWoCj+GkW+/ZOMjaRkeR8U26GfIdzATnsXIhextjzTm+IKo36ZsMGs2PSG +3zzafRScQMF80hhv8U8mRQmVlFza0Jj49EyClhDerDDLK675kuq/eQP8Hj+sCaQ/ +Zf2RT5Ykp860TmqWKReuwKjfFyL0F+PcHDkGVhDq6rV0FzxO3X6RCqgLeAenMUQI +MasYhA8SnOfehCzpbZNFv6jBPzTc +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert46[] = { + 0x30, 0x82, 0x05, 0xe1, 0x30, 0x82, 0x04, 0xc9, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0xaa, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, + 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x34, 0x30, 0x39, 0x5a, 0x17, + 0x0d, 0x31, 0x38, 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x33, 0x33, + 0x30, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, + 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, + 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, + 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, + 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37, + 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37, + 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d, + 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10, + 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae, + 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30, + 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2, + 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77, + 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0, + 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9, + 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09, + 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57, + 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1, + 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb, + 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8, + 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21, + 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60, + 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb, + 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4, + 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a, + 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14, + 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae, + 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca, + 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99, + 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11, + 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b, + 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57, + 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02, + 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5, + 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c, + 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb, + 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90, + 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa, + 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d, + 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae, + 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86, + 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4, + 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a, + 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6, + 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8, + 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68, + 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0, + 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10, + 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x7b, 0x30, 0x82, 0x01, 0x77, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x60, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x59, 0x30, + 0x57, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, + 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, + 0x63, 0x66, 0x6d, 0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x2a, 0x01, 0x30, 0x42, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x01, 0x01, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x26, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x6f, 0x6d, + 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, + 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x86, 0x30, 0x27, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, + 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, + 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, + 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, + 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, + 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4, 0x68, 0x22, 0x57, 0x80, 0x26, + 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e, 0xcc, 0xa5, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x62, 0xf6, 0x84, 0x91, 0x00, 0xc4, + 0x6f, 0x82, 0x7b, 0x24, 0xe1, 0x42, 0xa2, 0xa5, 0x8b, 0x82, 0x5c, 0xa7, + 0xc5, 0x44, 0xcb, 0xe7, 0x52, 0x76, 0x63, 0xd3, 0x76, 0x9e, 0x78, 0xe2, + 0x69, 0x35, 0xb1, 0x38, 0xba, 0xb0, 0x96, 0xc6, 0x1f, 0xac, 0x7b, 0xc6, + 0xb2, 0x65, 0x77, 0x8b, 0x7d, 0x8d, 0xae, 0x64, 0xb9, 0xa5, 0x8c, 0x17, + 0xca, 0x58, 0x65, 0xc3, 0xad, 0x82, 0xf5, 0xc5, 0xa2, 0xf5, 0x01, 0x13, + 0x93, 0xc6, 0x7e, 0x44, 0xe5, 0xc4, 0x61, 0xfa, 0x03, 0xb6, 0x56, 0xc1, + 0x72, 0xe1, 0xc8, 0x28, 0xc5, 0x69, 0x21, 0x8f, 0xac, 0x6e, 0xfd, 0x7f, + 0x43, 0x83, 0x36, 0xb8, 0xc0, 0xd6, 0xa0, 0x28, 0xfe, 0x1a, 0x45, 0xbe, + 0xfd, 0x93, 0x8c, 0x8d, 0xa4, 0x64, 0x79, 0x1f, 0x14, 0xdb, 0xa1, 0x9f, + 0x21, 0xdc, 0xc0, 0x4e, 0x7b, 0x17, 0x22, 0x17, 0xb1, 0xb6, 0x3c, 0xd3, + 0x9b, 0xe2, 0x0a, 0xa3, 0x7e, 0x99, 0xb0, 0xc1, 0xac, 0xd8, 0xf4, 0x86, + 0xdf, 0x3c, 0xda, 0x7d, 0x14, 0x9c, 0x40, 0xc1, 0x7c, 0xd2, 0x18, 0x6f, + 0xf1, 0x4f, 0x26, 0x45, 0x09, 0x95, 0x94, 0x5c, 0xda, 0xd0, 0x98, 0xf8, + 0xf4, 0x4c, 0x82, 0x96, 0x10, 0xde, 0xac, 0x30, 0xcb, 0x2b, 0xae, 0xf9, + 0x92, 0xea, 0xbf, 0x79, 0x03, 0xfc, 0x1e, 0x3f, 0xac, 0x09, 0xa4, 0x3f, + 0x65, 0xfd, 0x91, 0x4f, 0x96, 0x24, 0xa7, 0xce, 0xb4, 0x4e, 0x6a, 0x96, + 0x29, 0x17, 0xae, 0xc0, 0xa8, 0xdf, 0x17, 0x22, 0xf4, 0x17, 0xe3, 0xdc, + 0x1c, 0x39, 0x06, 0x56, 0x10, 0xea, 0xea, 0xb5, 0x74, 0x17, 0x3c, 0x4e, + 0xdd, 0x7e, 0x91, 0x0a, 0xa8, 0x0b, 0x78, 0x07, 0xa7, 0x31, 0x44, 0x08, + 0x31, 0xab, 0x18, 0x84, 0x0f, 0x12, 0x9c, 0xe7, 0xde, 0x84, 0x2c, 0xe9, + 0x6d, 0x93, 0x45, 0xbf, 0xa8, 0xc1, 0x3f, 0x34, 0xdc, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 6e:cc:7a:a5:a7:03:20:09:b8:ce:bc:f4:e9:52:d4:91 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Validity + Not Before: Feb 8 00:00:00 2010 GMT + Not After : Feb 7 23:59:59 2020 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)10, CN=VeriSign Class 3 Secure Server CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b1:87:84:1f:c2:0c:45:f5:bc:ab:25:97:a7:ad: + a2:3e:9c:ba:f6:c1:39:b8:8b:ca:c2:ac:56:c6:e5: + bb:65:8e:44:4f:4d:ce:6f:ed:09:4a:d4:af:4e:10: + 9c:68:8b:2e:95:7b:89:9b:13:ca:e2:34:34:c1:f3: + 5b:f3:49:7b:62:83:48:81:74:d1:88:78:6c:02:53: + f9:bc:7f:43:26:57:58:33:83:3b:33:0a:17:b0:d0: + 4e:91:24:ad:86:7d:64:12:dc:74:4a:34:a1:1d:0a: + ea:96:1d:0b:15:fc:a3:4b:3b:ce:63:88:d0:f8:2d: + 0c:94:86:10:ca:b6:9a:3d:ca:eb:37:9c:00:48:35: + 86:29:50:78:e8:45:63:cd:19:41:4f:f5:95:ec:7b: + 98:d4:c4:71:b3:50:be:28:b3:8f:a0:b9:53:9c:f5: + ca:2c:23:a9:fd:14:06:e8:18:b4:9a:e8:3c:6e:81: + fd:e4:cd:35:36:b3:51:d3:69:ec:12:ba:56:6e:6f: + 9b:57:c5:8b:14:e7:0e:c7:9c:ed:4a:54:6a:c9:4d: + c5:bf:11:b1:ae:1c:67:81:cb:44:55:33:99:7f:24: + 9b:3f:53:45:7f:86:1a:f3:3c:fa:6d:7f:81:f5:b8: + 4a:d3:f5:85:37:1c:b5:a6:d0:09:e4:18:7b:38:4e: + fa:0f + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.verisign.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.23.3 + CPS: https://www.verisign.com/cps + User Notice: + Explicit Text: https://www.verisign.com/rpa + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.verisign.com/pca3-g5.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-6 + X509v3 Subject Key Identifier: + 0D:44:5C:16:53:44:C1:82:7E:1D:20:AB:25:F4:01:63:D8:BE:79:A5 + X509v3 Authority Key Identifier: + keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + + Signature Algorithm: sha1WithRSAEncryption + 0c:83:24:ef:dd:c3:0c:d9:58:9c:fe:36:b6:eb:8a:80:4b:d1: + a3:f7:9d:f3:cc:53:ef:82:9e:a3:a1:e6:97:c1:58:9d:75:6c: + e0:1d:1b:4c:fa:d1:c1:2d:05:c0:ea:6e:b2:22:70:55:d9:20: + 33:40:33:07:c2:65:83:fa:8f:43:37:9b:ea:0e:9a:6c:70:ee: + f6:9c:80:3b:d9:37:f4:7a:6d:ec:d0:18:7d:49:4a:ca:99:c7: + 19:28:a2:be:d8:77:24:f7:85:26:86:6d:87:05:40:41:67:d1: + 27:3a:ed:dc:48:1d:22:cd:0b:0b:8b:bc:f4:b1:7b:fd:b4:99: + a8:e9:76:2a:e1:1a:2d:87:6e:74:d3:88:dd:1e:22:c6:df:16: + b6:2b:82:14:0a:94:5c:f2:50:ec:af:ce:ff:62:37:0d:ad:65: + d3:06:41:53:ed:02:14:c8:b5:58:28:a1:ac:e0:5b:ec:b3:7f: + 95:4a:fb:03:c8:ad:26:db:e6:66:78:12:4a:d9:9f:42:fb:e1: + 98:e6:42:83:9b:8f:8f:67:24:e8:61:19:b5:dd:cd:b5:0b:26: + 05:8e:c3:6e:c4:c8:75:b8:46:cf:e2:18:06:5e:a9:ae:a8:81: + 9a:47:16:de:0c:28:6c:25:27:b9:de:b7:84:58:c6:1f:38:1e: + a4:c4:cb:66 +-----BEGIN CERTIFICATE----- +MIIF7DCCBNSgAwIBAgIQbsx6pacDIAm4zrz06VLUkTANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg +aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy +aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG +5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8 +f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK +tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo +GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV +M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggHfMIIB +2zA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlz +aWduLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4 +RQEHFwMwVjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2Nw +czAqBggrBgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMDQG +A1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMtZzUu +Y3JsMA4GA1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglp +bWFnZS9naWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNo +dHRwOi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0w +GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+ +HSCrJfQBY9i+eaUwHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJ +KoZIhvcNAQEFBQADggEBAAyDJO/dwwzZWJz+NrbrioBL0aP3nfPMU++CnqOh5pfB +WJ11bOAdG0z60cEtBcDqbrIicFXZIDNAMwfCZYP6j0M3m+oOmmxw7vacgDvZN/R6 +bezQGH1JSsqZxxkoor7YdyT3hSaGbYcFQEFn0Sc67dxIHSLNCwuLvPSxe/20majp +dirhGi2HbnTTiN0eIsbfFrYrghQKlFzyUOyvzv9iNw2tZdMGQVPtAhTItVgooazg +W+yzf5VK+wPIrSbb5mZ4EkrZn0L74ZjmQoObj49nJOhhGbXdzbULJgWOw27EyHW4 +Rs/iGAZeqa6ogZpHFt4MKGwlJ7net4RYxh84HqTEy2Y= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert47[] = { + 0x30, 0x82, 0x05, 0xec, 0x30, 0x82, 0x04, 0xd4, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x6e, 0xcc, 0x7a, 0xa5, 0xa7, 0x03, 0x20, 0x09, 0xb8, + 0xce, 0xbc, 0xf4, 0xe9, 0x52, 0xd4, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x32, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xb5, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, + 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x31, 0x30, 0x31, 0x2f, + 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x56, 0x65, 0x72, + 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, + 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb1, 0x87, 0x84, 0x1f, + 0xc2, 0x0c, 0x45, 0xf5, 0xbc, 0xab, 0x25, 0x97, 0xa7, 0xad, 0xa2, 0x3e, + 0x9c, 0xba, 0xf6, 0xc1, 0x39, 0xb8, 0x8b, 0xca, 0xc2, 0xac, 0x56, 0xc6, + 0xe5, 0xbb, 0x65, 0x8e, 0x44, 0x4f, 0x4d, 0xce, 0x6f, 0xed, 0x09, 0x4a, + 0xd4, 0xaf, 0x4e, 0x10, 0x9c, 0x68, 0x8b, 0x2e, 0x95, 0x7b, 0x89, 0x9b, + 0x13, 0xca, 0xe2, 0x34, 0x34, 0xc1, 0xf3, 0x5b, 0xf3, 0x49, 0x7b, 0x62, + 0x83, 0x48, 0x81, 0x74, 0xd1, 0x88, 0x78, 0x6c, 0x02, 0x53, 0xf9, 0xbc, + 0x7f, 0x43, 0x26, 0x57, 0x58, 0x33, 0x83, 0x3b, 0x33, 0x0a, 0x17, 0xb0, + 0xd0, 0x4e, 0x91, 0x24, 0xad, 0x86, 0x7d, 0x64, 0x12, 0xdc, 0x74, 0x4a, + 0x34, 0xa1, 0x1d, 0x0a, 0xea, 0x96, 0x1d, 0x0b, 0x15, 0xfc, 0xa3, 0x4b, + 0x3b, 0xce, 0x63, 0x88, 0xd0, 0xf8, 0x2d, 0x0c, 0x94, 0x86, 0x10, 0xca, + 0xb6, 0x9a, 0x3d, 0xca, 0xeb, 0x37, 0x9c, 0x00, 0x48, 0x35, 0x86, 0x29, + 0x50, 0x78, 0xe8, 0x45, 0x63, 0xcd, 0x19, 0x41, 0x4f, 0xf5, 0x95, 0xec, + 0x7b, 0x98, 0xd4, 0xc4, 0x71, 0xb3, 0x50, 0xbe, 0x28, 0xb3, 0x8f, 0xa0, + 0xb9, 0x53, 0x9c, 0xf5, 0xca, 0x2c, 0x23, 0xa9, 0xfd, 0x14, 0x06, 0xe8, + 0x18, 0xb4, 0x9a, 0xe8, 0x3c, 0x6e, 0x81, 0xfd, 0xe4, 0xcd, 0x35, 0x36, + 0xb3, 0x51, 0xd3, 0x69, 0xec, 0x12, 0xba, 0x56, 0x6e, 0x6f, 0x9b, 0x57, + 0xc5, 0x8b, 0x14, 0xe7, 0x0e, 0xc7, 0x9c, 0xed, 0x4a, 0x54, 0x6a, 0xc9, + 0x4d, 0xc5, 0xbf, 0x11, 0xb1, 0xae, 0x1c, 0x67, 0x81, 0xcb, 0x44, 0x55, + 0x33, 0x99, 0x7f, 0x24, 0x9b, 0x3f, 0x53, 0x45, 0x7f, 0x86, 0x1a, 0xf3, + 0x3c, 0xfa, 0x6d, 0x7f, 0x81, 0xf5, 0xb8, 0x4a, 0xd3, 0xf5, 0x85, 0x37, + 0x1c, 0xb5, 0xa6, 0xd0, 0x09, 0xe4, 0x18, 0x7b, 0x38, 0x4e, 0xfa, 0x0f, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xdf, 0x30, 0x82, 0x01, + 0xdb, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, + 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, + 0x02, 0x01, 0x00, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x69, + 0x30, 0x67, 0x30, 0x65, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, + 0x45, 0x01, 0x07, 0x17, 0x03, 0x30, 0x56, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, + 0x73, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x02, 0x30, 0x1e, 0x1a, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, + 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x34, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, + 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, + 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, + 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, + 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, + 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, + 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x28, + 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30, + 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, + 0x2d, 0x32, 0x2d, 0x36, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x0d, 0x44, 0x5c, 0x16, 0x53, 0x44, 0xc1, 0x82, 0x7e, + 0x1d, 0x20, 0xab, 0x25, 0xf4, 0x01, 0x63, 0xd8, 0xbe, 0x79, 0xa5, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x0c, 0x83, 0x24, 0xef, 0xdd, 0xc3, 0x0c, 0xd9, + 0x58, 0x9c, 0xfe, 0x36, 0xb6, 0xeb, 0x8a, 0x80, 0x4b, 0xd1, 0xa3, 0xf7, + 0x9d, 0xf3, 0xcc, 0x53, 0xef, 0x82, 0x9e, 0xa3, 0xa1, 0xe6, 0x97, 0xc1, + 0x58, 0x9d, 0x75, 0x6c, 0xe0, 0x1d, 0x1b, 0x4c, 0xfa, 0xd1, 0xc1, 0x2d, + 0x05, 0xc0, 0xea, 0x6e, 0xb2, 0x22, 0x70, 0x55, 0xd9, 0x20, 0x33, 0x40, + 0x33, 0x07, 0xc2, 0x65, 0x83, 0xfa, 0x8f, 0x43, 0x37, 0x9b, 0xea, 0x0e, + 0x9a, 0x6c, 0x70, 0xee, 0xf6, 0x9c, 0x80, 0x3b, 0xd9, 0x37, 0xf4, 0x7a, + 0x6d, 0xec, 0xd0, 0x18, 0x7d, 0x49, 0x4a, 0xca, 0x99, 0xc7, 0x19, 0x28, + 0xa2, 0xbe, 0xd8, 0x77, 0x24, 0xf7, 0x85, 0x26, 0x86, 0x6d, 0x87, 0x05, + 0x40, 0x41, 0x67, 0xd1, 0x27, 0x3a, 0xed, 0xdc, 0x48, 0x1d, 0x22, 0xcd, + 0x0b, 0x0b, 0x8b, 0xbc, 0xf4, 0xb1, 0x7b, 0xfd, 0xb4, 0x99, 0xa8, 0xe9, + 0x76, 0x2a, 0xe1, 0x1a, 0x2d, 0x87, 0x6e, 0x74, 0xd3, 0x88, 0xdd, 0x1e, + 0x22, 0xc6, 0xdf, 0x16, 0xb6, 0x2b, 0x82, 0x14, 0x0a, 0x94, 0x5c, 0xf2, + 0x50, 0xec, 0xaf, 0xce, 0xff, 0x62, 0x37, 0x0d, 0xad, 0x65, 0xd3, 0x06, + 0x41, 0x53, 0xed, 0x02, 0x14, 0xc8, 0xb5, 0x58, 0x28, 0xa1, 0xac, 0xe0, + 0x5b, 0xec, 0xb3, 0x7f, 0x95, 0x4a, 0xfb, 0x03, 0xc8, 0xad, 0x26, 0xdb, + 0xe6, 0x66, 0x78, 0x12, 0x4a, 0xd9, 0x9f, 0x42, 0xfb, 0xe1, 0x98, 0xe6, + 0x42, 0x83, 0x9b, 0x8f, 0x8f, 0x67, 0x24, 0xe8, 0x61, 0x19, 0xb5, 0xdd, + 0xcd, 0xb5, 0x0b, 0x26, 0x05, 0x8e, 0xc3, 0x6e, 0xc4, 0xc8, 0x75, 0xb8, + 0x46, 0xcf, 0xe2, 0x18, 0x06, 0x5e, 0xa9, 0xae, 0xa8, 0x81, 0x9a, 0x47, + 0x16, 0xde, 0x0c, 0x28, 0x6c, 0x25, 0x27, 0xb9, 0xde, 0xb7, 0x84, 0x58, + 0xc6, 0x1f, 0x38, 0x1e, 0xa4, 0xc4, 0xcb, 0x66, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 2c:48:dd:93:0d:f5:59:8e:f9:3c:99:54:7a:60:ed:43 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Validity + Not Before: Nov 8 00:00:00 2006 GMT + Not After : Nov 7 23:59:59 2016 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)06, CN=VeriSign Class 3 Extended Validation SSL SGC CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bd:56:88:ba:88:34:64:64:cf:cd:ca:b0:ee:e7: + 19:73:c5:72:d9:bb:45:bc:b5:a8:ff:83:be:1c:03: + db:ed:89:b7:2e:10:1a:25:bc:55:ca:41:a1:9f:0b: + cf:19:5e:70:b9:5e:39:4b:9e:31:1c:5f:87:ae:2a: + aa:a8:2b:a2:1b:3b:10:23:5f:13:b1:dd:08:8c:4e: + 14:da:83:81:e3:b5:8c:e3:68:ed:24:67:ce:56:b6: + ac:9b:73:96:44:db:8a:8c:b3:d6:f0:71:93:8e:db: + 71:54:4a:eb:73:59:6a:8f:70:51:2c:03:9f:97:d1: + cc:11:7a:bc:62:0d:95:2a:c9:1c:75:57:e9:f5:c7: + ea:ba:84:35:cb:c7:85:5a:7e:e4:4d:e1:11:97:7d: + 0e:20:34:45:db:f1:a2:09:eb:eb:3d:9e:b8:96:43: + 5e:34:4b:08:25:1e:43:1a:a2:d9:b7:8a:01:34:3d: + c3:f8:e5:af:4f:8c:ff:cd:65:f0:23:4e:c5:97:b3: + 5c:da:90:1c:82:85:0d:06:0d:c1:22:b6:7b:28:a4: + 03:c3:4c:53:d1:58:bc:72:bc:08:39:fc:a0:76:a8: + a8:e9:4b:6e:88:3d:e3:b3:31:25:8c:73:29:48:0e: + 32:79:06:ed:3d:43:f4:f6:e4:e9:fc:7d:be:8e:08: + d5:1f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 4E:43:C8:1D:76:EF:37:53:7A:4F:F2:58:6F:94:F3:38:E2:D5:BD:DF + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.verisign.com/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://EVSecure-crl.verisign.com/pca3-g5.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Netscape Cert Type: + SSL CA, S/MIME CA + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + X509v3 Subject Alternative Name: + DirName:/CN=Class3CA2048-1-48 + X509v3 Authority Key Identifier: + keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + + Authority Information Access: + OCSP - URI:http://EVSecure-ocsp.verisign.com + + X509v3 Extended Key Usage: + Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1, TLS Web Server Authentication, TLS Web Client Authentication + Signature Algorithm: sha1WithRSAEncryption + 27:74:a6:34:ea:1d:9d:e1:53:d6:1c:9d:0c:a7:5b:4c:a9:67: + f2:f0:32:b7:01:0f:fb:42:18:38:de:e4:ee:49:c8:13:c9:0b: + ec:04:c3:40:71:18:72:76:43:02:23:5d:ab:7b:c8:48:14:1a: + c8:7b:1d:fc:f6:0a:9f:36:a1:d2:09:73:71:66:96:75:51:34: + bf:99:30:51:67:9d:54:b7:26:45:ac:73:08:23:86:26:99:71: + f4:8e:d7:ea:39:9b:06:09:23:bf:62:dd:a8:c4:b6:7d:a4:89: + 07:3e:f3:6d:ae:40:59:50:79:97:37:3d:32:78:7d:b2:63:4b: + f9:ea:08:69:0e:13:ed:e8:cf:bb:ac:05:86:ca:22:cf:88:62: + 5d:3c:22:49:d8:63:d5:24:a6:bd:ef:5c:e3:cc:20:3b:22:ea: + fc:44:c6:a8:e5:1f:e1:86:cd:0c:4d:8f:93:53:d9:7f:ee:a1: + 08:a7:b3:30:96:49:70:6e:a3:6c:3d:d0:63:ef:25:66:63:cc: + aa:b7:18:17:4e:ea:70:76:f6:ba:42:a6:80:37:09:4e:9f:66: + 88:2e:6b:33:66:c8:c0:71:a4:41:eb:5a:e3:fc:14:2e:4b:88: + fd:ae:6e:5b:65:e9:27:e4:bf:e4:b0:23:c1:b2:7d:5b:62:25: + d7:3e:10:d4 +-----BEGIN CERTIFICATE----- +MIIGHjCCBQagAwIBAgIQLEjdkw31WY75PJlUemDtQzANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMTYxMTA3MjM1OTU5WjCBvjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg +aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMvVmVy +aVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9Voi6iDRkZM/NyrDu5xlzxXLZ +u0W8taj/g74cA9vtibcuEBolvFXKQaGfC88ZXnC5XjlLnjEcX4euKqqoK6IbOxAj +XxOx3QiMThTag4HjtYzjaO0kZ85Wtqybc5ZE24qMs9bwcZOO23FUSutzWWqPcFEs +A5+X0cwRerxiDZUqyRx1V+n1x+q6hDXLx4VafuRN4RGXfQ4gNEXb8aIJ6+s9nriW +Q140SwglHkMaotm3igE0PcP45a9PjP/NZfAjTsWXs1zakByChQ0GDcEitnsopAPD +TFPRWLxyvAg5/KB2qKjpS26IPeOzMSWMcylIDjJ5Bu09Q/T25On8fb6OCNUfAgMB +AAGjggIIMIICBDAdBgNVHQ4EFgQUTkPIHXbvN1N6T/JYb5TzOOLVvd8wEgYDVR0T +AQH/BAgwBgEB/wIBADA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYc +aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczA9BgNVHR8ENjA0MDKgMKAuhixo +dHRwOi8vRVZTZWN1cmUtY3JsLnZlcmlzaWduLmNvbS9wY2EzLWc1LmNybDAOBgNV +HQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEGMG0GCCsGAQUFBwEMBGEwX6Fd +oFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrU +SBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMCkG +A1UdEQQiMCCkHjAcMRowGAYDVQQDExFDbGFzczNDQTIwNDgtMS00ODAfBgNVHSME +GDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzA9BggrBgEFBQcBAQQxMC8wLQYIKwYB +BQUHMAGGIWh0dHA6Ly9FVlNlY3VyZS1vY3NwLnZlcmlzaWduLmNvbTA0BgNVHSUE +LTArBglghkgBhvhCBAEGCmCGSAGG+EUBCAEGCCsGAQUFBwMBBggrBgEFBQcDAjAN +BgkqhkiG9w0BAQUFAAOCAQEAJ3SmNOodneFT1hydDKdbTKln8vAytwEP+0IYON7k +7knIE8kL7ATDQHEYcnZDAiNdq3vISBQayHsd/PYKnzah0glzcWaWdVE0v5kwUWed +VLcmRaxzCCOGJplx9I7X6jmbBgkjv2LdqMS2faSJBz7zba5AWVB5lzc9Mnh9smNL ++eoIaQ4T7ejPu6wFhsoiz4hiXTwiSdhj1SSmve9c48wgOyLq/ETGqOUf4YbNDE2P +k1PZf+6hCKezMJZJcG6jbD3QY+8lZmPMqrcYF07qcHb2ukKmgDcJTp9miC5rM2bI +wHGkQeta4/wULkuI/a5uW2XpJ+S/5LAjwbJ9W2Il1z4Q1A== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert48[] = { + 0x30, 0x82, 0x06, 0x1e, 0x30, 0x82, 0x05, 0x06, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x2c, 0x48, 0xdd, 0x93, 0x0d, 0xf5, 0x59, 0x8e, 0xf9, + 0x3c, 0x99, 0x54, 0x7a, 0x60, 0xed, 0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, + 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, + 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x36, 0x31, 0x38, + 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x56, 0x65, 0x72, + 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, + 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53, + 0x4c, 0x20, 0x53, 0x47, 0x43, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0x56, 0x88, 0xba, 0x88, 0x34, 0x64, + 0x64, 0xcf, 0xcd, 0xca, 0xb0, 0xee, 0xe7, 0x19, 0x73, 0xc5, 0x72, 0xd9, + 0xbb, 0x45, 0xbc, 0xb5, 0xa8, 0xff, 0x83, 0xbe, 0x1c, 0x03, 0xdb, 0xed, + 0x89, 0xb7, 0x2e, 0x10, 0x1a, 0x25, 0xbc, 0x55, 0xca, 0x41, 0xa1, 0x9f, + 0x0b, 0xcf, 0x19, 0x5e, 0x70, 0xb9, 0x5e, 0x39, 0x4b, 0x9e, 0x31, 0x1c, + 0x5f, 0x87, 0xae, 0x2a, 0xaa, 0xa8, 0x2b, 0xa2, 0x1b, 0x3b, 0x10, 0x23, + 0x5f, 0x13, 0xb1, 0xdd, 0x08, 0x8c, 0x4e, 0x14, 0xda, 0x83, 0x81, 0xe3, + 0xb5, 0x8c, 0xe3, 0x68, 0xed, 0x24, 0x67, 0xce, 0x56, 0xb6, 0xac, 0x9b, + 0x73, 0x96, 0x44, 0xdb, 0x8a, 0x8c, 0xb3, 0xd6, 0xf0, 0x71, 0x93, 0x8e, + 0xdb, 0x71, 0x54, 0x4a, 0xeb, 0x73, 0x59, 0x6a, 0x8f, 0x70, 0x51, 0x2c, + 0x03, 0x9f, 0x97, 0xd1, 0xcc, 0x11, 0x7a, 0xbc, 0x62, 0x0d, 0x95, 0x2a, + 0xc9, 0x1c, 0x75, 0x57, 0xe9, 0xf5, 0xc7, 0xea, 0xba, 0x84, 0x35, 0xcb, + 0xc7, 0x85, 0x5a, 0x7e, 0xe4, 0x4d, 0xe1, 0x11, 0x97, 0x7d, 0x0e, 0x20, + 0x34, 0x45, 0xdb, 0xf1, 0xa2, 0x09, 0xeb, 0xeb, 0x3d, 0x9e, 0xb8, 0x96, + 0x43, 0x5e, 0x34, 0x4b, 0x08, 0x25, 0x1e, 0x43, 0x1a, 0xa2, 0xd9, 0xb7, + 0x8a, 0x01, 0x34, 0x3d, 0xc3, 0xf8, 0xe5, 0xaf, 0x4f, 0x8c, 0xff, 0xcd, + 0x65, 0xf0, 0x23, 0x4e, 0xc5, 0x97, 0xb3, 0x5c, 0xda, 0x90, 0x1c, 0x82, + 0x85, 0x0d, 0x06, 0x0d, 0xc1, 0x22, 0xb6, 0x7b, 0x28, 0xa4, 0x03, 0xc3, + 0x4c, 0x53, 0xd1, 0x58, 0xbc, 0x72, 0xbc, 0x08, 0x39, 0xfc, 0xa0, 0x76, + 0xa8, 0xa8, 0xe9, 0x4b, 0x6e, 0x88, 0x3d, 0xe3, 0xb3, 0x31, 0x25, 0x8c, + 0x73, 0x29, 0x48, 0x0e, 0x32, 0x79, 0x06, 0xed, 0x3d, 0x43, 0xf4, 0xf6, + 0xe4, 0xe9, 0xfc, 0x7d, 0xbe, 0x8e, 0x08, 0xd5, 0x1f, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x02, 0x08, 0x30, 0x82, 0x02, 0x04, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x4e, 0x43, 0xc8, + 0x1d, 0x76, 0xef, 0x37, 0x53, 0x7a, 0x4f, 0xf2, 0x58, 0x6f, 0x94, 0xf3, + 0x38, 0xe2, 0xd5, 0xbd, 0xdf, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, + 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x70, 0x73, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x36, 0x30, 0x34, 0x30, 0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, + 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, + 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, + 0x11, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, + 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, + 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, + 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, + 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, + 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, + 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x29, 0x06, + 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x33, 0x43, 0x41, 0x32, 0x30, 0x34, 0x38, 0x2d, + 0x31, 0x2d, 0x34, 0x38, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, + 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, + 0x33, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f, + 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x2d, 0x30, 0x2b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, + 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, + 0x08, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x27, 0x74, 0xa6, 0x34, 0xea, 0x1d, + 0x9d, 0xe1, 0x53, 0xd6, 0x1c, 0x9d, 0x0c, 0xa7, 0x5b, 0x4c, 0xa9, 0x67, + 0xf2, 0xf0, 0x32, 0xb7, 0x01, 0x0f, 0xfb, 0x42, 0x18, 0x38, 0xde, 0xe4, + 0xee, 0x49, 0xc8, 0x13, 0xc9, 0x0b, 0xec, 0x04, 0xc3, 0x40, 0x71, 0x18, + 0x72, 0x76, 0x43, 0x02, 0x23, 0x5d, 0xab, 0x7b, 0xc8, 0x48, 0x14, 0x1a, + 0xc8, 0x7b, 0x1d, 0xfc, 0xf6, 0x0a, 0x9f, 0x36, 0xa1, 0xd2, 0x09, 0x73, + 0x71, 0x66, 0x96, 0x75, 0x51, 0x34, 0xbf, 0x99, 0x30, 0x51, 0x67, 0x9d, + 0x54, 0xb7, 0x26, 0x45, 0xac, 0x73, 0x08, 0x23, 0x86, 0x26, 0x99, 0x71, + 0xf4, 0x8e, 0xd7, 0xea, 0x39, 0x9b, 0x06, 0x09, 0x23, 0xbf, 0x62, 0xdd, + 0xa8, 0xc4, 0xb6, 0x7d, 0xa4, 0x89, 0x07, 0x3e, 0xf3, 0x6d, 0xae, 0x40, + 0x59, 0x50, 0x79, 0x97, 0x37, 0x3d, 0x32, 0x78, 0x7d, 0xb2, 0x63, 0x4b, + 0xf9, 0xea, 0x08, 0x69, 0x0e, 0x13, 0xed, 0xe8, 0xcf, 0xbb, 0xac, 0x05, + 0x86, 0xca, 0x22, 0xcf, 0x88, 0x62, 0x5d, 0x3c, 0x22, 0x49, 0xd8, 0x63, + 0xd5, 0x24, 0xa6, 0xbd, 0xef, 0x5c, 0xe3, 0xcc, 0x20, 0x3b, 0x22, 0xea, + 0xfc, 0x44, 0xc6, 0xa8, 0xe5, 0x1f, 0xe1, 0x86, 0xcd, 0x0c, 0x4d, 0x8f, + 0x93, 0x53, 0xd9, 0x7f, 0xee, 0xa1, 0x08, 0xa7, 0xb3, 0x30, 0x96, 0x49, + 0x70, 0x6e, 0xa3, 0x6c, 0x3d, 0xd0, 0x63, 0xef, 0x25, 0x66, 0x63, 0xcc, + 0xaa, 0xb7, 0x18, 0x17, 0x4e, 0xea, 0x70, 0x76, 0xf6, 0xba, 0x42, 0xa6, + 0x80, 0x37, 0x09, 0x4e, 0x9f, 0x66, 0x88, 0x2e, 0x6b, 0x33, 0x66, 0xc8, + 0xc0, 0x71, 0xa4, 0x41, 0xeb, 0x5a, 0xe3, 0xfc, 0x14, 0x2e, 0x4b, 0x88, + 0xfd, 0xae, 0x6e, 0x5b, 0x65, 0xe9, 0x27, 0xe4, 0xbf, 0xe4, 0xb0, 0x23, + 0xc1, 0xb2, 0x7d, 0x5b, 0x62, 0x25, 0xd7, 0x3e, 0x10, 0xd4, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 64:1b:e8:20:ce:02:08:13:f3:2d:4d:2d:95:d6:7e:67 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Validity + Not Before: Feb 8 00:00:00 2010 GMT + Not After : Feb 7 23:59:59 2020 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)10, CN=VeriSign Class 3 International Server CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:99:d6:9c:62:f0:15:f4:81:9a:41:08:59:8f:13: + 9d:17:c9:9f:51:dc:da:b1:52:ef:ff:e3:41:dd:e0: + df:c4:28:c6:e3:ad:79:1f:27:10:98:b8:bb:20:97: + c1:28:44:41:0f:ea:a9:a8:52:cf:4d:4e:1b:8b:bb: + b5:c4:76:d9:cc:56:06:ee:b3:55:20:2a:de:15:8d: + 71:cb:54:c8:6f:17:cd:89:00:e4:dc:ff:e1:c0:1f: + 68:71:e9:c7:29:2e:7e:bc:3b:fc:e5:bb:ab:26:54: + 8b:66:90:cd:f6:92:b9:31:24:80:bc:9e:6c:d5:fc: + 7e:d2:e1:4b:8c:dc:42:fa:44:4b:5f:f8:18:b5:2e: + 30:f4:3d:12:98:d3:62:05:73:54:a6:9c:a2:1d:be: + 52:83:3a:07:46:c4:3b:02:56:21:bf:f2:51:4f:d0: + a6:99:39:e9:ae:a5:3f:89:9b:9c:7d:fe:4d:60:07: + 25:20:f7:bb:d7:69:83:2b:82:93:43:37:d9:83:41: + 1b:6b:0b:ab:4a:66:84:4f:4a:8e:de:7e:34:99:8e: + 68:d6:ca:39:06:9b:4c:b3:9a:48:4d:13:46:b4:58: + 21:04:c4:fb:a0:4d:ac:2e:4b:62:12:e3:fb:4d:f6: + c9:51:00:01:1f:fc:1e:6a:81:2a:38:e0:b9:4f:d6: + 2d:45 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.23.3 + CPS: https://www.verisign.com/cps + User Notice: + Explicit Text: https://www.verisign.com/rpa + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1 + Authority Information Access: + OCSP - URI:http://ocsp.verisign.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.verisign.com/pca3-g5.crl + + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-7 + X509v3 Subject Key Identifier: + D7:9B:7C:D8:22:A0:15:F7:DD:AD:5F:CE:29:9B:58:C3:BC:46:00:B5 + X509v3 Authority Key Identifier: + keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + + Signature Algorithm: sha1WithRSAEncryption + 71:b5:7d:73:52:4a:dd:d7:4d:34:2b:2e:af:94:46:a5:49:50: + 02:4f:f8:2f:17:70:f2:13:dc:1f:21:86:aa:c2:4f:7c:37:3c: + d4:46:78:ae:5d:78:6f:d1:ba:5a:bc:10:ab:58:36:c5:8c:62: + 15:45:60:17:21:e2:d5:42:a8:77:a1:55:d8:43:04:51:f6:6e: + ba:48:e6:5d:4c:b7:44:d3:3e:a4:d5:d6:33:9a:9f:0d:e6:d7: + 4e:96:44:95:5a:6c:d6:a3:16:53:0e:98:43:ce:a4:b8:c3:66: + 7a:05:5c:62:10:e8:1b:12:db:7d:2e:76:50:ff:df:d7:6b:1b: + cc:8a:cc:71:fa:b3:40:56:7c:33:7a:77:94:5b:f5:0b:53:fb: + 0e:5f:bc:68:fb:af:2a:ee:30:37:79:16:93:25:7f:4d:10:ff: + 57:fb:bf:6e:3b:33:21:de:79:dc:86:17:59:2d:43:64:b7:a6: + 66:87:ea:bc:96:46:19:1a:86:8b:6f:d7:b7:49:00:5b:db:a3: + bf:29:9a:ee:f7:d3:33:ae:a3:f4:9e:4c:ca:5e:69:d4:1b:ad: + b7:90:77:6a:d8:59:6f:79:ab:01:fa:55:f0:8a:21:66:e5:65: + 6e:fd:7c:d3:df:1e:eb:7e:3f:06:90:fb:19:0b:d3:06:02:1b: + 78:43:99:a8 +-----BEGIN CERTIFICATE----- +MIIGKTCCBRGgAwIBAgIQZBvoIM4CCBPzLU0tldZ+ZzANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBvDEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg +aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDE2MDQGA1UEAxMtVmVy +aVNpZ24gQ2xhc3MgMyBJbnRlcm5hdGlvbmFsIFNlcnZlciBDQSAtIEczMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmdacYvAV9IGaQQhZjxOdF8mfUdza +sVLv/+NB3eDfxCjG4615HycQmLi7IJfBKERBD+qpqFLPTU4bi7u1xHbZzFYG7rNV +ICreFY1xy1TIbxfNiQDk3P/hwB9ocenHKS5+vDv85burJlSLZpDN9pK5MSSAvJ5s +1fx+0uFLjNxC+kRLX/gYtS4w9D0SmNNiBXNUppyiHb5SgzoHRsQ7AlYhv/JRT9Cm +mTnprqU/iZucff5NYAclIPe712mDK4KTQzfZg0EbawurSmaET0qO3n40mY5o1so5 +BptMs5pITRNGtFghBMT7oE2sLktiEuP7TfbJUQABH/weaoEqOOC5T9YtRQIDAQAB +o4ICFTCCAhEwEgYDVR0TAQH/BAgwBgEB/wIBADBwBgNVHSAEaTBnMGUGC2CGSAGG ++EUBBxcDMFYwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9j +cHMwKgYIKwYBBQUHAgIwHhocaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYTAO +BgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDov +L2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwNAYDVR0lBC0wKwYIKwYBBQUH +AwEGCCsGAQUFBwMCBglghkgBhvhCBAEGCmCGSAGG+EUBCAEwNAYIKwYBBQUHAQEE +KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wNAYDVR0f +BC0wKzApoCegJYYjaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy1nNS5jcmww +KAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFZlcmlTaWduTVBLSS0yLTcwHQYDVR0O +BBYEFNebfNgioBX33a1fzimbWMO8RgC1MB8GA1UdIwQYMBaAFH/TZafC3ey78DAJ +80M5+gKvMzEzMA0GCSqGSIb3DQEBBQUAA4IBAQBxtX1zUkrd1000Ky6vlEalSVAC +T/gvF3DyE9wfIYaqwk98NzzURniuXXhv0bpavBCrWDbFjGIVRWAXIeLVQqh3oVXY +QwRR9m66SOZdTLdE0z6k1dYzmp8N5tdOlkSVWmzWoxZTDphDzqS4w2Z6BVxiEOgb +Ett9LnZQ/9/XaxvMisxx+rNAVnwzeneUW/ULU/sOX7xo+68q7jA3eRaTJX9NEP9X ++79uOzMh3nnchhdZLUNkt6Zmh+q8lkYZGoaLb9e3SQBb26O/KZru99MzrqP0nkzK +XmnUG623kHdq2FlveasB+lXwiiFm5WVu/XzT3x7rfj8GkPsZC9MGAht4Q5mo +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert49[] = { + 0x30, 0x82, 0x06, 0x29, 0x30, 0x82, 0x05, 0x11, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x64, 0x1b, 0xe8, 0x20, 0xce, 0x02, 0x08, 0x13, 0xf3, + 0x2d, 0x4d, 0x2d, 0x95, 0xd6, 0x7e, 0x67, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x32, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xbc, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, + 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x31, 0x30, 0x31, 0x36, + 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x56, 0x65, 0x72, + 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, + 0x33, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, + 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0x99, 0xd6, 0x9c, 0x62, 0xf0, 0x15, 0xf4, 0x81, 0x9a, + 0x41, 0x08, 0x59, 0x8f, 0x13, 0x9d, 0x17, 0xc9, 0x9f, 0x51, 0xdc, 0xda, + 0xb1, 0x52, 0xef, 0xff, 0xe3, 0x41, 0xdd, 0xe0, 0xdf, 0xc4, 0x28, 0xc6, + 0xe3, 0xad, 0x79, 0x1f, 0x27, 0x10, 0x98, 0xb8, 0xbb, 0x20, 0x97, 0xc1, + 0x28, 0x44, 0x41, 0x0f, 0xea, 0xa9, 0xa8, 0x52, 0xcf, 0x4d, 0x4e, 0x1b, + 0x8b, 0xbb, 0xb5, 0xc4, 0x76, 0xd9, 0xcc, 0x56, 0x06, 0xee, 0xb3, 0x55, + 0x20, 0x2a, 0xde, 0x15, 0x8d, 0x71, 0xcb, 0x54, 0xc8, 0x6f, 0x17, 0xcd, + 0x89, 0x00, 0xe4, 0xdc, 0xff, 0xe1, 0xc0, 0x1f, 0x68, 0x71, 0xe9, 0xc7, + 0x29, 0x2e, 0x7e, 0xbc, 0x3b, 0xfc, 0xe5, 0xbb, 0xab, 0x26, 0x54, 0x8b, + 0x66, 0x90, 0xcd, 0xf6, 0x92, 0xb9, 0x31, 0x24, 0x80, 0xbc, 0x9e, 0x6c, + 0xd5, 0xfc, 0x7e, 0xd2, 0xe1, 0x4b, 0x8c, 0xdc, 0x42, 0xfa, 0x44, 0x4b, + 0x5f, 0xf8, 0x18, 0xb5, 0x2e, 0x30, 0xf4, 0x3d, 0x12, 0x98, 0xd3, 0x62, + 0x05, 0x73, 0x54, 0xa6, 0x9c, 0xa2, 0x1d, 0xbe, 0x52, 0x83, 0x3a, 0x07, + 0x46, 0xc4, 0x3b, 0x02, 0x56, 0x21, 0xbf, 0xf2, 0x51, 0x4f, 0xd0, 0xa6, + 0x99, 0x39, 0xe9, 0xae, 0xa5, 0x3f, 0x89, 0x9b, 0x9c, 0x7d, 0xfe, 0x4d, + 0x60, 0x07, 0x25, 0x20, 0xf7, 0xbb, 0xd7, 0x69, 0x83, 0x2b, 0x82, 0x93, + 0x43, 0x37, 0xd9, 0x83, 0x41, 0x1b, 0x6b, 0x0b, 0xab, 0x4a, 0x66, 0x84, + 0x4f, 0x4a, 0x8e, 0xde, 0x7e, 0x34, 0x99, 0x8e, 0x68, 0xd6, 0xca, 0x39, + 0x06, 0x9b, 0x4c, 0xb3, 0x9a, 0x48, 0x4d, 0x13, 0x46, 0xb4, 0x58, 0x21, + 0x04, 0xc4, 0xfb, 0xa0, 0x4d, 0xac, 0x2e, 0x4b, 0x62, 0x12, 0xe3, 0xfb, + 0x4d, 0xf6, 0xc9, 0x51, 0x00, 0x01, 0x1f, 0xfc, 0x1e, 0x6a, 0x81, 0x2a, + 0x38, 0xe0, 0xb9, 0x4f, 0xd6, 0x2d, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x02, 0x15, 0x30, 0x82, 0x02, 0x11, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x69, 0x30, 0x67, 0x30, 0x65, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, + 0xf8, 0x45, 0x01, 0x07, 0x17, 0x03, 0x30, 0x56, 0x30, 0x28, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, + 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x70, 0x73, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x02, 0x30, 0x1e, 0x1a, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, + 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, + 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, + 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, + 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, + 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, + 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, + 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, + 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x25, + 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, + 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, + 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, + 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x28, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, + 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x10, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, + 0x49, 0x2d, 0x32, 0x2d, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xd7, 0x9b, 0x7c, 0xd8, 0x22, 0xa0, 0x15, 0xf7, + 0xdd, 0xad, 0x5f, 0xce, 0x29, 0x9b, 0x58, 0xc3, 0xbc, 0x46, 0x00, 0xb5, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, + 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x71, 0xb5, 0x7d, 0x73, 0x52, 0x4a, 0xdd, + 0xd7, 0x4d, 0x34, 0x2b, 0x2e, 0xaf, 0x94, 0x46, 0xa5, 0x49, 0x50, 0x02, + 0x4f, 0xf8, 0x2f, 0x17, 0x70, 0xf2, 0x13, 0xdc, 0x1f, 0x21, 0x86, 0xaa, + 0xc2, 0x4f, 0x7c, 0x37, 0x3c, 0xd4, 0x46, 0x78, 0xae, 0x5d, 0x78, 0x6f, + 0xd1, 0xba, 0x5a, 0xbc, 0x10, 0xab, 0x58, 0x36, 0xc5, 0x8c, 0x62, 0x15, + 0x45, 0x60, 0x17, 0x21, 0xe2, 0xd5, 0x42, 0xa8, 0x77, 0xa1, 0x55, 0xd8, + 0x43, 0x04, 0x51, 0xf6, 0x6e, 0xba, 0x48, 0xe6, 0x5d, 0x4c, 0xb7, 0x44, + 0xd3, 0x3e, 0xa4, 0xd5, 0xd6, 0x33, 0x9a, 0x9f, 0x0d, 0xe6, 0xd7, 0x4e, + 0x96, 0x44, 0x95, 0x5a, 0x6c, 0xd6, 0xa3, 0x16, 0x53, 0x0e, 0x98, 0x43, + 0xce, 0xa4, 0xb8, 0xc3, 0x66, 0x7a, 0x05, 0x5c, 0x62, 0x10, 0xe8, 0x1b, + 0x12, 0xdb, 0x7d, 0x2e, 0x76, 0x50, 0xff, 0xdf, 0xd7, 0x6b, 0x1b, 0xcc, + 0x8a, 0xcc, 0x71, 0xfa, 0xb3, 0x40, 0x56, 0x7c, 0x33, 0x7a, 0x77, 0x94, + 0x5b, 0xf5, 0x0b, 0x53, 0xfb, 0x0e, 0x5f, 0xbc, 0x68, 0xfb, 0xaf, 0x2a, + 0xee, 0x30, 0x37, 0x79, 0x16, 0x93, 0x25, 0x7f, 0x4d, 0x10, 0xff, 0x57, + 0xfb, 0xbf, 0x6e, 0x3b, 0x33, 0x21, 0xde, 0x79, 0xdc, 0x86, 0x17, 0x59, + 0x2d, 0x43, 0x64, 0xb7, 0xa6, 0x66, 0x87, 0xea, 0xbc, 0x96, 0x46, 0x19, + 0x1a, 0x86, 0x8b, 0x6f, 0xd7, 0xb7, 0x49, 0x00, 0x5b, 0xdb, 0xa3, 0xbf, + 0x29, 0x9a, 0xee, 0xf7, 0xd3, 0x33, 0xae, 0xa3, 0xf4, 0x9e, 0x4c, 0xca, + 0x5e, 0x69, 0xd4, 0x1b, 0xad, 0xb7, 0x90, 0x77, 0x6a, 0xd8, 0x59, 0x6f, + 0x79, 0xab, 0x01, 0xfa, 0x55, 0xf0, 0x8a, 0x21, 0x66, 0xe5, 0x65, 0x6e, + 0xfd, 0x7c, 0xd3, 0xdf, 0x1e, 0xeb, 0x7e, 0x3f, 0x06, 0x90, 0xfb, 0x19, + 0x0b, 0xd3, 0x06, 0x02, 0x1b, 0x78, 0x43, 0x99, 0xa8, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 26 (0x1a) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority + Validity + Not Before: Oct 24 20:57:09 2007 GMT + Not After : Oct 24 20:57:09 2017 GMT + Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Class 2 Primary Intermediate Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e2:4f:39:2f:a1:8c:9a:85:ad:08:0e:08:3e:57: + f2:88:01:21:1b:94:a9:6c:e2:b8:db:aa:19:18:46: + 3a:52:a1:f5:0f:f4:6e:8c:ea:96:8c:96:87:79:13: + 40:51:2f:22:f2:0c:8b:87:0f:65:df:71:74:34:43: + 55:b1:35:09:9b:d9:bc:1f:fa:eb:42:d0:97:40:72: + b7:43:96:3d:ba:96:9d:5d:50:02:1c:9b:91:8d:9c: + c0:ac:d7:bb:2f:17:d7:cb:3e:82:9d:73:eb:07:42: + 92:b2:cd:64:b3:74:55:1b:b4:4b:86:21:2c:f7:78: + 87:32:e0:16:e4:da:bd:4c:95:ea:a4:0a:7e:b6:0a: + 0d:2e:8a:cf:55:ab:c3:e5:dd:41:8a:4e:e6:6f:65: + 6c:b2:40:cf:17:5d:b9:c3:6a:0b:27:11:84:77:61: + f6:c2:7c:ed:c0:8d:78:14:18:99:81:99:75:63:b7: + e8:53:d3:ba:61:e9:0e:fa:a2:30:f3:46:a2:b9:c9: + 1f:6c:80:5a:40:ac:27:ed:48:47:33:b0:54:c6:46: + 1a:f3:35:61:c1:02:29:90:54:7e:64:4d:c4:30:52: + 02:82:d7:df:ce:21:6e:18:91:d7:b8:ab:8c:27:17: + b5:f0:a3:01:2f:8e:d2:2e:87:3a:3d:b4:29:67:8a: + c4:03 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 11:DB:23:45:FD:54:CC:6A:71:6F:84:8A:03:D7:BE:F7:01:2F:26:86 + X509v3 Authority Key Identifier: + keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2 + + Authority Information Access: + OCSP - URI:http://ocsp.startssl.com/ca + CA Issuers - URI:http://www.startssl.com/sfsca.crt + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.startssl.com/sfsca.crl + + Full Name: + URI:http://crl.startssl.com/sfsca.crl + + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.23223.1.2.1 + CPS: http://www.startssl.com/policy.pdf + CPS: http://www.startssl.com/intermediate.pdf + + Signature Algorithm: sha1WithRSAEncryption + 9d:07:e1:ee:90:76:31:67:16:45:70:8c:cb:84:8b:4b:57:68: + 44:a5:89:c1:f2:7e:cb:28:8b:f5:e7:70:77:d5:b6:f4:0b:21: + 60:a5:a1:74:73:24:22:80:d6:d8:ba:8d:a2:62:5d:09:35:42: + 29:fb:39:63:45:0b:a4:b0:38:1a:68:f4:95:13:cc:e0:43:94: + ec:eb:39:1a:ec:57:29:d9:99:6d:f5:84:cd:8e:73:ae:c9:dc: + 6a:fa:9e:9d:16:64:93:08:c7:1c:c2:89:54:9e:77:80:90:f6: + b9:29:76:eb:13:67:48:59:f8:2e:3a:31:b8:c9:d3:88:e5:5f: + 4e:d2:19:3d:43:8e:d7:92:ff:cf:38:b6:e1:5b:8a:53:1d:ce: + ac:b4:76:2f:d8:f7:40:63:d5:ee:69:f3:45:7d:a0:62:c1:61: + c3:75:ed:b2:7b:4d:ac:21:27:30:4e:59:46:6a:93:17:ca:c8: + 39:2d:01:73:65:5b:e9:41:9b:11:17:9c:c8:c8:4a:ef:a1:76: + 60:2d:ae:93:ff:0c:d5:33:13:9f:4f:13:ce:dd:86:f1:fc:f8: + 35:54:15:a8:5b:e7:85:7e:fa:37:09:ff:8b:b8:31:49:9e:0d: + 6e:de:b4:d2:12:2d:b8:ed:c8:c3:f1:b6:42:a0:4c:97:79:df: + fe:c3:a3:9f:a1:f4:6d:2c:84:77:a4:a2:05:e1:17:ff:31:dd: + 9a:f3:b8:7a:c3:52:c2:11:11:b7:50:31:8a:7f:cc:e7:5a:89: + cc:f7:86:9a:61:92:4f:2f:94:b6:98:c7:78:e0:62:4b:43:7d: + 3c:de:d6:9a:b4:10:a1:40:9c:4b:2a:dc:b8:d0:d4:9e:fd:f1: + 84:78:1b:0e:57:8f:69:54:42:68:7b:ea:a0:ef:75:0f:07:a2: + 8c:73:99:ab:55:f5:07:09:d2:af:38:03:6a:90:03:0c:2f:8f: + e2:e8:43:c2:31:e9:6f:ad:87:e5:8d:bd:4e:2c:89:4b:51:e6: + 9c:4c:54:76:c0:12:81:53:9b:ec:a0:fc:2c:9c:da:18:95:6e: + 1e:38:26:42:27:78:60:08:df:7f:6d:32:e8:d8:c0:6f:1f:eb: + 26:75:9f:93:fc:7b:1b:fe:35:90:dc:53:a3:07:a6:3f:83:55: + 0a:2b:4e:62:82:25:ce:66:30:5d:2c:e0:f9:19:1b:75:b9:9d: + 98:56:a6:83:27:7a:d1:8f:8d:59:93:fc:3f:73:d7:2e:b4:2c: + 95:d8:8b:f7:c9:7e:c7:fc:9d:ac:72:04:1f:d2:cc:17:f4:ed: + 34:60:9b:9e:4a:97:04:fe:dd:72:0e:57:54:51:06:70:4d:ef: + aa:1c:a4:82:e0:33:c7:f4 +-----BEGIN CERTIFICATE----- +MIIGNDCCBBygAwIBAgIBGjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcxMDI0MjA1NzA5WjCB +jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT +IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0 +YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4k85L6GMmoWtCA4IPlfyiAEh +G5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l33F0NENVsTUJm9m8 +H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL +hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGE +d2H2wnztwI14FBiZgZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa +8zVhwQIpkFR+ZE3EMFICgtffziFuGJHXuKuMJxe18KMBL47SLoc6PbQpZ4rEAwID +AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov +L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0 +YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3 +dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0 +c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu +BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0 +BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl +LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAnQfh7pB2MWcWRXCMy4SLS1doRKWJwfJ+ +yyiL9edwd9W29AshYKWhdHMkIoDW2LqNomJdCTVCKfs5Y0ULpLA4Gmj0lRPM4EOU +7Os5GuxXKdmZbfWEzY5zrsncavqenRZkkwjHHMKJVJ53gJD2uSl26xNnSFn4Ljox +uMnTiOVfTtIZPUOO15L/zzi24VuKUx3OrLR2L9j3QGPV7mnzRX2gYsFhw3XtsntN +rCEnME5ZRmqTF8rIOS0Bc2Vb6UGbERecyMhK76F2YC2uk/8M1TMTn08Tzt2G8fz4 +NVQVqFvnhX76Nwn/i7gxSZ4Nbt600hItuO3Iw/G2QqBMl3nf/sOjn6H0bSyEd6Si +BeEX/zHdmvO4esNSwhERt1Axin/M51qJzPeGmmGSTy+UtpjHeOBiS0N9PN7WmrQQ +oUCcSyrcuNDUnv3xhHgbDlePaVRCaHvqoO91DweijHOZq1X1BwnSrzgDapADDC+P +4uhDwjHpb62H5Y29TiyJS1HmnExUdsASgVOb7KD8LJzaGJVuHjgmQid4YAjff20y +6NjAbx/rJnWfk/x7G/41kNxTowemP4NVCitOYoIlzmYwXSzg+RkbdbmdmFamgyd6 +0Y+NWZP8P3PXLrQsldiL98l+x/ydrHIEH9LMF/TtNGCbnkqXBP7dcg5XVFEGcE3v +qhykguAzx/Q= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert50[] = { + 0x30, 0x82, 0x06, 0x34, 0x30, 0x82, 0x04, 0x1c, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, + 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, + 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x31, 0x30, 0x32, 0x34, + 0x32, 0x30, 0x35, 0x37, 0x30, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x31, + 0x30, 0x32, 0x34, 0x32, 0x30, 0x35, 0x37, 0x30, 0x39, 0x5a, 0x30, 0x81, + 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, + 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, + 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, + 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe2, 0x4f, 0x39, 0x2f, 0xa1, 0x8c, + 0x9a, 0x85, 0xad, 0x08, 0x0e, 0x08, 0x3e, 0x57, 0xf2, 0x88, 0x01, 0x21, + 0x1b, 0x94, 0xa9, 0x6c, 0xe2, 0xb8, 0xdb, 0xaa, 0x19, 0x18, 0x46, 0x3a, + 0x52, 0xa1, 0xf5, 0x0f, 0xf4, 0x6e, 0x8c, 0xea, 0x96, 0x8c, 0x96, 0x87, + 0x79, 0x13, 0x40, 0x51, 0x2f, 0x22, 0xf2, 0x0c, 0x8b, 0x87, 0x0f, 0x65, + 0xdf, 0x71, 0x74, 0x34, 0x43, 0x55, 0xb1, 0x35, 0x09, 0x9b, 0xd9, 0xbc, + 0x1f, 0xfa, 0xeb, 0x42, 0xd0, 0x97, 0x40, 0x72, 0xb7, 0x43, 0x96, 0x3d, + 0xba, 0x96, 0x9d, 0x5d, 0x50, 0x02, 0x1c, 0x9b, 0x91, 0x8d, 0x9c, 0xc0, + 0xac, 0xd7, 0xbb, 0x2f, 0x17, 0xd7, 0xcb, 0x3e, 0x82, 0x9d, 0x73, 0xeb, + 0x07, 0x42, 0x92, 0xb2, 0xcd, 0x64, 0xb3, 0x74, 0x55, 0x1b, 0xb4, 0x4b, + 0x86, 0x21, 0x2c, 0xf7, 0x78, 0x87, 0x32, 0xe0, 0x16, 0xe4, 0xda, 0xbd, + 0x4c, 0x95, 0xea, 0xa4, 0x0a, 0x7e, 0xb6, 0x0a, 0x0d, 0x2e, 0x8a, 0xcf, + 0x55, 0xab, 0xc3, 0xe5, 0xdd, 0x41, 0x8a, 0x4e, 0xe6, 0x6f, 0x65, 0x6c, + 0xb2, 0x40, 0xcf, 0x17, 0x5d, 0xb9, 0xc3, 0x6a, 0x0b, 0x27, 0x11, 0x84, + 0x77, 0x61, 0xf6, 0xc2, 0x7c, 0xed, 0xc0, 0x8d, 0x78, 0x14, 0x18, 0x99, + 0x81, 0x99, 0x75, 0x63, 0xb7, 0xe8, 0x53, 0xd3, 0xba, 0x61, 0xe9, 0x0e, + 0xfa, 0xa2, 0x30, 0xf3, 0x46, 0xa2, 0xb9, 0xc9, 0x1f, 0x6c, 0x80, 0x5a, + 0x40, 0xac, 0x27, 0xed, 0x48, 0x47, 0x33, 0xb0, 0x54, 0xc6, 0x46, 0x1a, + 0xf3, 0x35, 0x61, 0xc1, 0x02, 0x29, 0x90, 0x54, 0x7e, 0x64, 0x4d, 0xc4, + 0x30, 0x52, 0x02, 0x82, 0xd7, 0xdf, 0xce, 0x21, 0x6e, 0x18, 0x91, 0xd7, + 0xb8, 0xab, 0x8c, 0x27, 0x17, 0xb5, 0xf0, 0xa3, 0x01, 0x2f, 0x8e, 0xd2, + 0x2e, 0x87, 0x3a, 0x3d, 0xb4, 0x29, 0x67, 0x8a, 0xc4, 0x03, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xad, 0x30, 0x82, 0x01, 0xa9, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, + 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x11, 0xdb, 0x23, 0x45, 0xfd, + 0x54, 0xcc, 0x6a, 0x71, 0x6f, 0x84, 0x8a, 0x03, 0xd7, 0xbe, 0xf7, 0x01, + 0x2f, 0x26, 0x86, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, 0x1a, 0xa4, 0x40, 0x5b, 0xa5, + 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, 0x43, 0xd0, 0x41, 0xae, 0xf2, + 0x30, 0x66, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x5a, 0x30, 0x58, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, + 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x30, 0x2d, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x21, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, + 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x5b, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x54, 0x30, 0x52, 0x30, 0x27, 0xa0, 0x25, 0xa0, + 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63, + 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x80, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x79, 0x30, 0x77, 0x30, 0x75, 0x06, 0x0b, 0x2b, 0x06, 0x01, + 0x04, 0x01, 0x81, 0xb5, 0x37, 0x01, 0x02, 0x01, 0x30, 0x66, 0x30, 0x2e, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x22, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70, 0x64, 0x66, 0x30, 0x34, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x28, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x64, 0x66, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, + 0x9d, 0x07, 0xe1, 0xee, 0x90, 0x76, 0x31, 0x67, 0x16, 0x45, 0x70, 0x8c, + 0xcb, 0x84, 0x8b, 0x4b, 0x57, 0x68, 0x44, 0xa5, 0x89, 0xc1, 0xf2, 0x7e, + 0xcb, 0x28, 0x8b, 0xf5, 0xe7, 0x70, 0x77, 0xd5, 0xb6, 0xf4, 0x0b, 0x21, + 0x60, 0xa5, 0xa1, 0x74, 0x73, 0x24, 0x22, 0x80, 0xd6, 0xd8, 0xba, 0x8d, + 0xa2, 0x62, 0x5d, 0x09, 0x35, 0x42, 0x29, 0xfb, 0x39, 0x63, 0x45, 0x0b, + 0xa4, 0xb0, 0x38, 0x1a, 0x68, 0xf4, 0x95, 0x13, 0xcc, 0xe0, 0x43, 0x94, + 0xec, 0xeb, 0x39, 0x1a, 0xec, 0x57, 0x29, 0xd9, 0x99, 0x6d, 0xf5, 0x84, + 0xcd, 0x8e, 0x73, 0xae, 0xc9, 0xdc, 0x6a, 0xfa, 0x9e, 0x9d, 0x16, 0x64, + 0x93, 0x08, 0xc7, 0x1c, 0xc2, 0x89, 0x54, 0x9e, 0x77, 0x80, 0x90, 0xf6, + 0xb9, 0x29, 0x76, 0xeb, 0x13, 0x67, 0x48, 0x59, 0xf8, 0x2e, 0x3a, 0x31, + 0xb8, 0xc9, 0xd3, 0x88, 0xe5, 0x5f, 0x4e, 0xd2, 0x19, 0x3d, 0x43, 0x8e, + 0xd7, 0x92, 0xff, 0xcf, 0x38, 0xb6, 0xe1, 0x5b, 0x8a, 0x53, 0x1d, 0xce, + 0xac, 0xb4, 0x76, 0x2f, 0xd8, 0xf7, 0x40, 0x63, 0xd5, 0xee, 0x69, 0xf3, + 0x45, 0x7d, 0xa0, 0x62, 0xc1, 0x61, 0xc3, 0x75, 0xed, 0xb2, 0x7b, 0x4d, + 0xac, 0x21, 0x27, 0x30, 0x4e, 0x59, 0x46, 0x6a, 0x93, 0x17, 0xca, 0xc8, + 0x39, 0x2d, 0x01, 0x73, 0x65, 0x5b, 0xe9, 0x41, 0x9b, 0x11, 0x17, 0x9c, + 0xc8, 0xc8, 0x4a, 0xef, 0xa1, 0x76, 0x60, 0x2d, 0xae, 0x93, 0xff, 0x0c, + 0xd5, 0x33, 0x13, 0x9f, 0x4f, 0x13, 0xce, 0xdd, 0x86, 0xf1, 0xfc, 0xf8, + 0x35, 0x54, 0x15, 0xa8, 0x5b, 0xe7, 0x85, 0x7e, 0xfa, 0x37, 0x09, 0xff, + 0x8b, 0xb8, 0x31, 0x49, 0x9e, 0x0d, 0x6e, 0xde, 0xb4, 0xd2, 0x12, 0x2d, + 0xb8, 0xed, 0xc8, 0xc3, 0xf1, 0xb6, 0x42, 0xa0, 0x4c, 0x97, 0x79, 0xdf, + 0xfe, 0xc3, 0xa3, 0x9f, 0xa1, 0xf4, 0x6d, 0x2c, 0x84, 0x77, 0xa4, 0xa2, + 0x05, 0xe1, 0x17, 0xff, 0x31, 0xdd, 0x9a, 0xf3, 0xb8, 0x7a, 0xc3, 0x52, + 0xc2, 0x11, 0x11, 0xb7, 0x50, 0x31, 0x8a, 0x7f, 0xcc, 0xe7, 0x5a, 0x89, + 0xcc, 0xf7, 0x86, 0x9a, 0x61, 0x92, 0x4f, 0x2f, 0x94, 0xb6, 0x98, 0xc7, + 0x78, 0xe0, 0x62, 0x4b, 0x43, 0x7d, 0x3c, 0xde, 0xd6, 0x9a, 0xb4, 0x10, + 0xa1, 0x40, 0x9c, 0x4b, 0x2a, 0xdc, 0xb8, 0xd0, 0xd4, 0x9e, 0xfd, 0xf1, + 0x84, 0x78, 0x1b, 0x0e, 0x57, 0x8f, 0x69, 0x54, 0x42, 0x68, 0x7b, 0xea, + 0xa0, 0xef, 0x75, 0x0f, 0x07, 0xa2, 0x8c, 0x73, 0x99, 0xab, 0x55, 0xf5, + 0x07, 0x09, 0xd2, 0xaf, 0x38, 0x03, 0x6a, 0x90, 0x03, 0x0c, 0x2f, 0x8f, + 0xe2, 0xe8, 0x43, 0xc2, 0x31, 0xe9, 0x6f, 0xad, 0x87, 0xe5, 0x8d, 0xbd, + 0x4e, 0x2c, 0x89, 0x4b, 0x51, 0xe6, 0x9c, 0x4c, 0x54, 0x76, 0xc0, 0x12, + 0x81, 0x53, 0x9b, 0xec, 0xa0, 0xfc, 0x2c, 0x9c, 0xda, 0x18, 0x95, 0x6e, + 0x1e, 0x38, 0x26, 0x42, 0x27, 0x78, 0x60, 0x08, 0xdf, 0x7f, 0x6d, 0x32, + 0xe8, 0xd8, 0xc0, 0x6f, 0x1f, 0xeb, 0x26, 0x75, 0x9f, 0x93, 0xfc, 0x7b, + 0x1b, 0xfe, 0x35, 0x90, 0xdc, 0x53, 0xa3, 0x07, 0xa6, 0x3f, 0x83, 0x55, + 0x0a, 0x2b, 0x4e, 0x62, 0x82, 0x25, 0xce, 0x66, 0x30, 0x5d, 0x2c, 0xe0, + 0xf9, 0x19, 0x1b, 0x75, 0xb9, 0x9d, 0x98, 0x56, 0xa6, 0x83, 0x27, 0x7a, + 0xd1, 0x8f, 0x8d, 0x59, 0x93, 0xfc, 0x3f, 0x73, 0xd7, 0x2e, 0xb4, 0x2c, + 0x95, 0xd8, 0x8b, 0xf7, 0xc9, 0x7e, 0xc7, 0xfc, 0x9d, 0xac, 0x72, 0x04, + 0x1f, 0xd2, 0xcc, 0x17, 0xf4, 0xed, 0x34, 0x60, 0x9b, 0x9e, 0x4a, 0x97, + 0x04, 0xfe, 0xdd, 0x72, 0x0e, 0x57, 0x54, 0x51, 0x06, 0x70, 0x4d, 0xef, + 0xaa, 0x1c, 0xa4, 0x82, 0xe0, 0x33, 0xc7, 0xf4, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA + Validity + Not Before: Apr 2 12:00:00 2008 GMT + Not After : Apr 3 00:00:00 2022 GMT + Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e: + fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9: + 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29: + 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f: + 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f: + a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf: + 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42: + 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12: + a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a: + 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03: + af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a: + aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd: + 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f: + e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed: + cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28: + 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c: + 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e: + 5e:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Certificate Policies: + Policy: 2.16.840.1.114412.1.3.0.2 + CPS: http://www.digicert.com/ssl-cps-repository.htm + User Notice: + Explicit Text: + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + Full Name: + URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + X509v3 Authority Key Identifier: + keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 + + X509v3 Subject Key Identifier: + 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7 + Signature Algorithm: sha1WithRSAEncryption + 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43: + ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7: + fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3: + 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a: + 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4: + 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a: + 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d: + 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d: + f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e: + 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd: + 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d: + 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4: + 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d: + c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3: + 5a:76:f7:61 +-----BEGIN CERTIFICATE----- +MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR +CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv +KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 +BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf +1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs +zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d +32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w +ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 +LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH +AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy +AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj +AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg +AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ +AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt +AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj +AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl +AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm +MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB +hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln +aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl +cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME +GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB +INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a +vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j +CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X +dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE +JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY +Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert51[] = { + 0x30, 0x82, 0x06, 0x58, 0x30, 0x82, 0x05, 0x40, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x0a, 0x5f, 0x11, 0x4d, 0x03, 0x5b, 0x17, 0x91, 0x17, + 0xd2, 0xef, 0xd4, 0x03, 0x8c, 0x3f, 0x3b, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x34, 0x30, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x30, + 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, + 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67, + 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, + 0x43, 0x41, 0x2d, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xbf, 0x61, 0x0a, 0x29, 0x10, 0x1f, 0x5e, 0xfe, 0x34, 0x37, 0x51, + 0x08, 0xf8, 0x1e, 0xfb, 0x22, 0xed, 0x61, 0xbe, 0x0b, 0x0d, 0x70, 0x4c, + 0x50, 0x63, 0x26, 0x75, 0x15, 0xb9, 0x41, 0x88, 0x97, 0xb6, 0xf0, 0xa0, + 0x15, 0xbb, 0x08, 0x60, 0xe0, 0x42, 0xe8, 0x05, 0x29, 0x10, 0x87, 0x36, + 0x8a, 0x28, 0x65, 0xa8, 0xef, 0x31, 0x07, 0x74, 0x6d, 0x36, 0x97, 0x2f, + 0x28, 0x46, 0x66, 0x04, 0xc7, 0x2a, 0x79, 0x26, 0x7a, 0x99, 0xd5, 0x8e, + 0xc3, 0x6d, 0x4f, 0xa0, 0x5e, 0xad, 0xbc, 0x3d, 0x91, 0xc2, 0x59, 0x7b, + 0x5e, 0x36, 0x6c, 0xc0, 0x53, 0xcf, 0x00, 0x08, 0x32, 0x3e, 0x10, 0x64, + 0x58, 0x10, 0x13, 0x69, 0xc7, 0x0c, 0xee, 0x9c, 0x42, 0x51, 0x00, 0xf9, + 0x05, 0x44, 0xee, 0x24, 0xce, 0x7a, 0x1f, 0xed, 0x8c, 0x11, 0xbd, 0x12, + 0xa8, 0xf3, 0x15, 0xf4, 0x1c, 0x7a, 0x31, 0x69, 0x01, 0x1b, 0xa7, 0xe6, + 0x5d, 0xc0, 0x9a, 0x6c, 0x7e, 0x09, 0x9e, 0xe7, 0x52, 0x44, 0x4a, 0x10, + 0x3a, 0x23, 0xe4, 0x9b, 0xb6, 0x03, 0xaf, 0xa8, 0x9c, 0xb4, 0x5b, 0x9f, + 0xd4, 0x4b, 0xad, 0x92, 0x8c, 0xce, 0xb5, 0x11, 0x2a, 0xaa, 0x37, 0x18, + 0x8d, 0xb4, 0xc2, 0xb8, 0xd8, 0x5c, 0x06, 0x8c, 0xf8, 0xff, 0x23, 0xbd, + 0x35, 0x5e, 0xd4, 0x7c, 0x3e, 0x7e, 0x83, 0x0e, 0x91, 0x96, 0x05, 0x98, + 0xc3, 0xb2, 0x1f, 0xe3, 0xc8, 0x65, 0xeb, 0xa9, 0x7b, 0x5d, 0xa0, 0x2c, + 0xcc, 0xfc, 0x3c, 0xd9, 0x6d, 0xed, 0xcc, 0xfa, 0x4b, 0x43, 0x8c, 0xc9, + 0xd4, 0xb8, 0xa5, 0x61, 0x1c, 0xb2, 0x40, 0xb6, 0x28, 0x12, 0xdf, 0xb9, + 0xf8, 0x5f, 0xfe, 0xd3, 0xb2, 0xc9, 0xef, 0x3d, 0xb4, 0x1e, 0x4b, 0x7c, + 0x1c, 0x4c, 0x99, 0x36, 0x9e, 0x3d, 0xeb, 0xec, 0xa7, 0x68, 0x5e, 0x1d, + 0xdf, 0x67, 0x6e, 0x5e, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x02, 0xfa, 0x30, 0x82, 0x02, 0xf6, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x82, + 0x01, 0xc6, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x82, 0x01, 0xbd, 0x30, + 0x82, 0x01, 0xb9, 0x30, 0x82, 0x01, 0xb5, 0x06, 0x0b, 0x60, 0x86, 0x48, + 0x01, 0x86, 0xfd, 0x6c, 0x01, 0x03, 0x00, 0x02, 0x30, 0x82, 0x01, 0xa4, + 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, + 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d, 0x72, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68, 0x74, 0x6d, + 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52, 0x00, 0x41, + 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, + 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, + 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, + 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, + 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, + 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75, 0x00, 0x74, + 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63, 0x00, 0x63, + 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x63, + 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74, + 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69, 0x00, 0x67, + 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x20, + 0x00, 0x43, 0x00, 0x50, 0x00, 0x2f, 0x00, 0x43, 0x00, 0x50, 0x00, 0x53, + 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x74, + 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6c, + 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, 0x00, 0x50, + 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x41, + 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, + 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68, 0x00, 0x69, + 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x6d, + 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x61, + 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, + 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x61, + 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x63, + 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, + 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x62, + 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65, 0x00, 0x2e, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x34, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, + 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x81, 0x8f, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81, + 0x87, 0x30, 0x81, 0x84, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, + 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, + 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, + 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40, + 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, + 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, + 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, + 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, + 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, + 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, + 0xc3, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x50, 0xea, 0x73, 0x89, 0xdb, 0x29, 0xfb, 0x10, 0x8f, 0x9e, 0xe5, 0x01, + 0x20, 0xd4, 0xde, 0x79, 0x99, 0x48, 0x83, 0xf7, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x1e, 0xe2, 0xa5, 0x48, 0x9e, 0x6c, 0xdb, 0x53, + 0x38, 0x0f, 0xef, 0xa6, 0x1a, 0x2a, 0xac, 0xe2, 0x03, 0x43, 0xed, 0x9a, + 0xbc, 0x3e, 0x8e, 0x75, 0x1b, 0xf0, 0xfd, 0x2e, 0x22, 0x59, 0xac, 0x13, + 0xc0, 0x61, 0xe2, 0xe7, 0xfa, 0xe9, 0x99, 0xcd, 0x87, 0x09, 0x75, 0x54, + 0x28, 0xbf, 0x46, 0x60, 0xdc, 0xbe, 0x51, 0x2c, 0x92, 0xf3, 0x1b, 0x91, + 0x7c, 0x31, 0x08, 0x70, 0xe2, 0x37, 0xb9, 0xc1, 0x5b, 0xa8, 0xbd, 0xa3, + 0x0b, 0x00, 0xfb, 0x1a, 0x15, 0xfd, 0x03, 0xad, 0x58, 0x6a, 0xc5, 0xc7, + 0x24, 0x99, 0x48, 0x47, 0x46, 0x31, 0x1e, 0x92, 0xef, 0xb4, 0x5f, 0x4e, + 0x34, 0xc7, 0x90, 0xbf, 0x31, 0xc1, 0xf8, 0xb1, 0x84, 0x86, 0xd0, 0x9c, + 0x01, 0xaa, 0xdf, 0x8a, 0x56, 0x06, 0xce, 0x3a, 0xe9, 0x0e, 0xae, 0x97, + 0x74, 0x5d, 0xd7, 0x71, 0x9a, 0x42, 0x74, 0x5f, 0xde, 0x8d, 0x43, 0x7c, + 0xde, 0xe9, 0x55, 0xed, 0x69, 0x00, 0xcb, 0x05, 0xe0, 0x7a, 0x61, 0x61, + 0x33, 0xd1, 0x19, 0x4d, 0xf9, 0x08, 0xee, 0xa0, 0x39, 0xc5, 0x25, 0x35, + 0xb7, 0x2b, 0xc4, 0x0f, 0xb2, 0xdd, 0xf1, 0xa5, 0xb7, 0x0e, 0x24, 0xc4, + 0x26, 0x28, 0x8d, 0x79, 0x77, 0xf5, 0x2f, 0xf0, 0x57, 0xba, 0x7c, 0x07, + 0xd4, 0xe1, 0xfc, 0xcd, 0x5a, 0x30, 0x57, 0x7e, 0x86, 0x10, 0x47, 0xdd, + 0x31, 0x1f, 0xd7, 0xfc, 0xa2, 0xc2, 0xbf, 0x30, 0x7c, 0x5d, 0x24, 0xaa, + 0xe8, 0xf9, 0xae, 0x5f, 0x6a, 0x74, 0xc2, 0xce, 0x6b, 0xb3, 0x46, 0xd8, + 0x21, 0xbe, 0x29, 0xd4, 0x8e, 0x5e, 0x15, 0xd6, 0x42, 0x4a, 0xe7, 0x32, + 0x6f, 0xa4, 0xb1, 0x6b, 0x51, 0x83, 0x58, 0xbe, 0x3f, 0x6d, 0xc7, 0xfb, + 0xda, 0x03, 0x21, 0xcb, 0x6a, 0x16, 0x19, 0x4e, 0x0a, 0xf0, 0xad, 0x84, + 0xca, 0x5d, 0x94, 0xb3, 0x5a, 0x76, 0xf7, 0x61, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 7250751724796726 (0x19c28530e93b36) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority + Validity + Not Before: Sep 17 22:46:36 2006 GMT + Not After : Dec 31 23:59:59 2019 GMT + Subject: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:bd:ca:8d:ac:b8:91:15:56:97:7b:6b:5c:7a:c2: + de:6b:d9:a1:b0:c3:10:23:fa:a7:a1:b2:cc:31:fa: + 3e:d9:a6:29:6f:16:3d:e0:6b:f8:b8:40:5f:db:39: + a8:00:7a:8b:a0:4d:54:7d:c2:22:78:fc:8e:09:b8: + a8:85:d7:cc:95:97:4b:74:d8:9e:7e:f0:00:e4:0e: + 89:ae:49:28:44:1a:10:99:32:0f:25:88:53:a4:0d: + b3:0f:12:08:16:0b:03:71:27:1c:7f:e1:db:d2:fd: + 67:68:c4:05:5d:0a:0e:5d:70:d7:d8:97:a0:bc:53: + 41:9a:91:8d:f4:9e:36:66:7a:7e:56:c1:90:5f:e6: + b1:68:20:36:a4:8c:24:2c:2c:47:0b:59:76:66:30: + b5:be:de:ed:8f:f8:9d:d3:bb:01:30:e6:f2:f3:0e: + e0:2c:92:80:f3:85:f9:28:8a:b4:54:2e:9a:ed:f7: + 76:fc:15:68:16:eb:4a:6c:eb:2e:12:8f:d4:cf:fe: + 0c:c7:5c:1d:0b:7e:05:32:be:5e:b0:09:2a:42:d5: + c9:4e:90:b3:59:0d:bb:7a:7e:cd:d5:08:5a:b4:7f: + d8:1c:69:11:f9:27:0f:7b:06:af:54:83:18:7b:e1: + dd:54:7a:51:68:6e:77:fc:c6:bf:52:4a:66:46:a1: + b2:67:1a:bb:a3:4f:77:a0:be:5d:ff:fc:56:0b:43: + 72:77:90:ca:9e:f9:f2:39:f5:0d:a9:f4:ea:d7:e7: + b3:10:2f:30:42:37:21:cc:30:70:c9:86:98:0f:cc: + 58:4d:83:bb:7d:e5:1a:a5:37:8d:b6:ac:32:97:00: + 3a:63:71:24:1e:9e:37:c4:ff:74:d4:37:c0:e2:fe: + 88:46:60:11:dd:08:3f:50:36:ab:b8:7a:a4:95:62: + 6a:6e:b0:ca:6a:21:5a:69:f3:f3:fb:1d:70:39:95: + f3:a7:6e:a6:81:89:a1:88:c5:3b:71:ca:a3:52:ee: + 83:bb:fd:a0:77:f4:e4:6f:e7:42:db:6d:4a:99:8a: + 34:48:bc:17:dc:e4:80:08:22:b6:f2:31:c0:3f:04: + 3e:eb:9f:20:79:d6:b8:06:64:64:02:31:d7:a9:cd: + 52:fb:84:45:69:09:00:2a:dc:55:8b:c4:06:46:4b: + c0:4a:1d:09:5b:39:28:fd:a9:ab:ce:00:f9:2e:48: + 4b:26:e6:30:4c:a5:58:ca:b4:44:82:4f:e7:91:1e: + 33:c3:b0:93:ff:11:fc:81:d2:ca:1f:71:29:dd:76: + 4f:92:25:af:1d:81:b7:0f:2f:8c:c3:06:cc:2f:27: + a3:4a:e4:0e:99:ba:7c:1e:45:1f:7f:aa:19:45:96: + fd:fc:3d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:2 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E + X509v3 Authority Key Identifier: + keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2 + + Authority Information Access: + OCSP - URI:http://ocsp.startssl.com/ca + CA Issuers - URI:http://aia.startssl.com/certs/ca.crt + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.startssl.com/sfsca.crl + + Signature Algorithm: sha256WithRSAEncryption + b6:6d:f8:70:fb:e2:0d:4c:98:b3:07:49:15:f5:04:c4:6c:ca: + ca:f5:68:a0:08:fe:12:6d:9c:04:06:c9:ad:9a:91:52:3e:78: + c4:5c:ee:9f:54:1d:ee:e3:f1:5e:30:c9:49:e1:39:e0:a6:9d: + 36:6c:57:fa:e6:34:4f:55:e8:87:a8:2c:dd:05:f1:58:12:91: + e8:ca:ce:28:78:8f:df:07:85:01:a5:dc:45:96:05:d4:80:b2: + 2b:05:9a:cb:9a:a5:8b:e0:3a:67:e6:73:47:be:4a:fd:27:b1: + 88:ef:e6:ca:cf:8d:0e:26:9f:fa:5f:57:78:ad:6d:fe:ae:9b: + 35:08:b1:c3:ba:c1:00:4a:4b:7d:14:bd:f7:f1:d3:55:18:ac: + d0:33:70:88:6d:c4:09:71:14:a6:2b:4f:88:81:e7:0b:00:37: + a9:15:7d:7e:d7:01:96:3f:2f:af:7b:62:ae:0a:4a:bf:4b:39: + 2e:35:10:8b:fe:04:39:e4:3c:3a:0c:09:56:40:3a:b5:f4:c2: + 68:0c:b5:f9:52:cd:ee:9d:f8:98:fc:78:e7:58:47:8f:1c:73: + 58:69:33:ab:ff:dd:df:8e:24:01:77:98:19:3a:b0:66:79:bc: + e1:08:a3:0e:4f:c1:04:b3:f3:01:c8:eb:d3:59:1c:35:d2:93: + 1e:70:65:82:7f:db:cf:fb:c8:99:12:60:c3:44:6f:3a:80:4b: + d7:be:21:aa:14:7a:64:cb:dd:37:43:45:5b:32:2e:45:f0:d9: + 59:1f:6b:18:f0:7c:e9:55:36:19:61:5f:b5:7d:f1:8d:bd:88: + e4:75:4b:98:dd:27:b0:e4:84:44:2a:61:84:57:05:82:11:1f: + aa:35:58:f3:20:0e:af:59:ef:fa:55:72:72:0d:26:d0:9b:53: + 49:ac:ce:37:2e:65:61:ff:f6:ec:1b:ea:f6:f1:a6:d3:d1:b5: + 7b:be:35:f4:22:c1:bc:8d:01:bd:68:5e:83:0d:2f:ec:d6:da: + 63:0c:27:d1:54:3e:e4:a8:d3:ce:4b:32:b8:91:94:ff:fb:5b: + 49:2d:75:18:a8:ba:71:9a:3b:ae:d9:c0:a9:4f:87:91:ed:8b: + 7b:6b:20:98:89:39:83:4f:80:c4:69:cc:17:c9:c8:4e:be:e4: + a9:a5:81:76:70:06:04:32:cd:83:65:f4:bc:7d:3e:13:bc:d2: + e8:6f:63:aa:b5:3b:da:8d:86:32:82:78:9d:d9:cc:ff:bf:57: + 64:74:ed:28:3d:44:62:15:61:4b:f7:94:b0:0d:2a:67:1c:f0: + cb:9b:a5:92:bf:f8:41:5a:c1:3d:60:ed:9f:bb:b8:6d:9b:ce: + a9:6a:16:3f:7e:ea:06:f1 +-----BEGIN CERTIFICATE----- +MIIGXDCCBESgAwIBAgIHGcKFMOk7NjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQG +EwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERp +Z2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MjI0NjM2WhcNMTkxMjMxMjM1 +OTU5WjBVMQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQx +KjAoBgNVBAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL3Kjay4kRVWl3trXHrC3mvZobDD +ECP6p6GyzDH6PtmmKW8WPeBr+LhAX9s5qAB6i6BNVH3CInj8jgm4qIXXzJWXS3TY +nn7wAOQOia5JKEQaEJkyDyWIU6QNsw8SCBYLA3EnHH/h29L9Z2jEBV0KDl1w19iX +oLxTQZqRjfSeNmZ6flbBkF/msWggNqSMJCwsRwtZdmYwtb7e7Y/4ndO7ATDm8vMO +4CySgPOF+SiKtFQumu33dvwVaBbrSmzrLhKP1M/+DMdcHQt+BTK+XrAJKkLVyU6Q +s1kNu3p+zdUIWrR/2BxpEfknD3sGr1SDGHvh3VR6UWhud/zGv1JKZkahsmcau6NP +d6C+Xf/8VgtDcneQyp758jn1Dan06tfnsxAvMEI3IcwwcMmGmA/MWE2Du33lGqU3 +jbasMpcAOmNxJB6eN8T/dNQ3wOL+iEZgEd0IP1A2q7h6pJViam6wymohWmnz8/sd +cDmV86dupoGJoYjFO3HKo1Lug7v9oHf05G/nQtttSpmKNEi8F9zkgAgitvIxwD8E +PuufIHnWuAZkZAIx16nNUvuERWkJACrcVYvEBkZLwEodCVs5KP2pq84A+S5ISybm +MEylWMq0RIJP55EeM8Owk/8R/IHSyh9xKd12T5Ilrx2Btw8vjMMGzC8no0rkDpm6 +fB5FH3+qGUWW/fw9AgMBAAGjggEHMIIBAzASBgNVHRMBAf8ECDAGAQH/AgECMA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU4WbPDtHxs0u3BiAU/ocS1fb++z4wHwYD +VR0jBBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIwaQYIKwYBBQUHAQEEXTBbMCcG +CCsGAQUFBzABhhtodHRwOi8vb2NzcC5zdGFydHNzbC5jb20vY2EwMAYIKwYBBQUH +MAKGJGh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2NlcnRzL2NhLmNydDAyBgNVHR8E +KzApMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwDQYJ +KoZIhvcNAQELBQADggIBALZt+HD74g1MmLMHSRX1BMRsysr1aKAI/hJtnAQGya2a +kVI+eMRc7p9UHe7j8V4wyUnhOeCmnTZsV/rmNE9V6IeoLN0F8VgSkejKzih4j98H +hQGl3EWWBdSAsisFmsuapYvgOmfmc0e+Sv0nsYjv5srPjQ4mn/pfV3itbf6umzUI +scO6wQBKS30Uvffx01UYrNAzcIhtxAlxFKYrT4iB5wsAN6kVfX7XAZY/L697Yq4K +Sr9LOS41EIv+BDnkPDoMCVZAOrX0wmgMtflSze6d+Jj8eOdYR48cc1hpM6v/3d+O +JAF3mBk6sGZ5vOEIow5PwQSz8wHI69NZHDXSkx5wZYJ/28/7yJkSYMNEbzqAS9e+ +IaoUemTL3TdDRVsyLkXw2VkfaxjwfOlVNhlhX7V98Y29iOR1S5jdJ7DkhEQqYYRX +BYIRH6o1WPMgDq9Z7/pVcnINJtCbU0mszjcuZWH/9uwb6vbxptPRtXu+NfQiwbyN +Ab1oXoMNL+zW2mMMJ9FUPuSo085LMriRlP/7W0ktdRiounGaO67ZwKlPh5Hti3tr +IJiJOYNPgMRpzBfJyE6+5KmlgXZwBgQyzYNl9Lx9PhO80uhvY6q1O9qNhjKCeJ3Z +zP+/V2R07Sg9RGIVYUv3lLANKmcc8MubpZK/+EFawT1g7Z+7uG2bzqlqFj9+6gbx +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert52[] = { + 0x30, 0x82, 0x06, 0x5c, 0x30, 0x82, 0x04, 0x44, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x07, 0x19, 0xc2, 0x85, 0x30, 0xe9, 0x3b, 0x36, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, + 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, + 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, + 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, + 0x30, 0x36, 0x30, 0x39, 0x31, 0x37, 0x32, 0x32, 0x34, 0x36, 0x33, 0x36, + 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, + 0x39, 0x35, 0x39, 0x5a, 0x30, 0x55, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, + 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, + 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66, + 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x30, 0x82, 0x02, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, + 0x82, 0x02, 0x01, 0x00, 0xbd, 0xca, 0x8d, 0xac, 0xb8, 0x91, 0x15, 0x56, + 0x97, 0x7b, 0x6b, 0x5c, 0x7a, 0xc2, 0xde, 0x6b, 0xd9, 0xa1, 0xb0, 0xc3, + 0x10, 0x23, 0xfa, 0xa7, 0xa1, 0xb2, 0xcc, 0x31, 0xfa, 0x3e, 0xd9, 0xa6, + 0x29, 0x6f, 0x16, 0x3d, 0xe0, 0x6b, 0xf8, 0xb8, 0x40, 0x5f, 0xdb, 0x39, + 0xa8, 0x00, 0x7a, 0x8b, 0xa0, 0x4d, 0x54, 0x7d, 0xc2, 0x22, 0x78, 0xfc, + 0x8e, 0x09, 0xb8, 0xa8, 0x85, 0xd7, 0xcc, 0x95, 0x97, 0x4b, 0x74, 0xd8, + 0x9e, 0x7e, 0xf0, 0x00, 0xe4, 0x0e, 0x89, 0xae, 0x49, 0x28, 0x44, 0x1a, + 0x10, 0x99, 0x32, 0x0f, 0x25, 0x88, 0x53, 0xa4, 0x0d, 0xb3, 0x0f, 0x12, + 0x08, 0x16, 0x0b, 0x03, 0x71, 0x27, 0x1c, 0x7f, 0xe1, 0xdb, 0xd2, 0xfd, + 0x67, 0x68, 0xc4, 0x05, 0x5d, 0x0a, 0x0e, 0x5d, 0x70, 0xd7, 0xd8, 0x97, + 0xa0, 0xbc, 0x53, 0x41, 0x9a, 0x91, 0x8d, 0xf4, 0x9e, 0x36, 0x66, 0x7a, + 0x7e, 0x56, 0xc1, 0x90, 0x5f, 0xe6, 0xb1, 0x68, 0x20, 0x36, 0xa4, 0x8c, + 0x24, 0x2c, 0x2c, 0x47, 0x0b, 0x59, 0x76, 0x66, 0x30, 0xb5, 0xbe, 0xde, + 0xed, 0x8f, 0xf8, 0x9d, 0xd3, 0xbb, 0x01, 0x30, 0xe6, 0xf2, 0xf3, 0x0e, + 0xe0, 0x2c, 0x92, 0x80, 0xf3, 0x85, 0xf9, 0x28, 0x8a, 0xb4, 0x54, 0x2e, + 0x9a, 0xed, 0xf7, 0x76, 0xfc, 0x15, 0x68, 0x16, 0xeb, 0x4a, 0x6c, 0xeb, + 0x2e, 0x12, 0x8f, 0xd4, 0xcf, 0xfe, 0x0c, 0xc7, 0x5c, 0x1d, 0x0b, 0x7e, + 0x05, 0x32, 0xbe, 0x5e, 0xb0, 0x09, 0x2a, 0x42, 0xd5, 0xc9, 0x4e, 0x90, + 0xb3, 0x59, 0x0d, 0xbb, 0x7a, 0x7e, 0xcd, 0xd5, 0x08, 0x5a, 0xb4, 0x7f, + 0xd8, 0x1c, 0x69, 0x11, 0xf9, 0x27, 0x0f, 0x7b, 0x06, 0xaf, 0x54, 0x83, + 0x18, 0x7b, 0xe1, 0xdd, 0x54, 0x7a, 0x51, 0x68, 0x6e, 0x77, 0xfc, 0xc6, + 0xbf, 0x52, 0x4a, 0x66, 0x46, 0xa1, 0xb2, 0x67, 0x1a, 0xbb, 0xa3, 0x4f, + 0x77, 0xa0, 0xbe, 0x5d, 0xff, 0xfc, 0x56, 0x0b, 0x43, 0x72, 0x77, 0x90, + 0xca, 0x9e, 0xf9, 0xf2, 0x39, 0xf5, 0x0d, 0xa9, 0xf4, 0xea, 0xd7, 0xe7, + 0xb3, 0x10, 0x2f, 0x30, 0x42, 0x37, 0x21, 0xcc, 0x30, 0x70, 0xc9, 0x86, + 0x98, 0x0f, 0xcc, 0x58, 0x4d, 0x83, 0xbb, 0x7d, 0xe5, 0x1a, 0xa5, 0x37, + 0x8d, 0xb6, 0xac, 0x32, 0x97, 0x00, 0x3a, 0x63, 0x71, 0x24, 0x1e, 0x9e, + 0x37, 0xc4, 0xff, 0x74, 0xd4, 0x37, 0xc0, 0xe2, 0xfe, 0x88, 0x46, 0x60, + 0x11, 0xdd, 0x08, 0x3f, 0x50, 0x36, 0xab, 0xb8, 0x7a, 0xa4, 0x95, 0x62, + 0x6a, 0x6e, 0xb0, 0xca, 0x6a, 0x21, 0x5a, 0x69, 0xf3, 0xf3, 0xfb, 0x1d, + 0x70, 0x39, 0x95, 0xf3, 0xa7, 0x6e, 0xa6, 0x81, 0x89, 0xa1, 0x88, 0xc5, + 0x3b, 0x71, 0xca, 0xa3, 0x52, 0xee, 0x83, 0xbb, 0xfd, 0xa0, 0x77, 0xf4, + 0xe4, 0x6f, 0xe7, 0x42, 0xdb, 0x6d, 0x4a, 0x99, 0x8a, 0x34, 0x48, 0xbc, + 0x17, 0xdc, 0xe4, 0x80, 0x08, 0x22, 0xb6, 0xf2, 0x31, 0xc0, 0x3f, 0x04, + 0x3e, 0xeb, 0x9f, 0x20, 0x79, 0xd6, 0xb8, 0x06, 0x64, 0x64, 0x02, 0x31, + 0xd7, 0xa9, 0xcd, 0x52, 0xfb, 0x84, 0x45, 0x69, 0x09, 0x00, 0x2a, 0xdc, + 0x55, 0x8b, 0xc4, 0x06, 0x46, 0x4b, 0xc0, 0x4a, 0x1d, 0x09, 0x5b, 0x39, + 0x28, 0xfd, 0xa9, 0xab, 0xce, 0x00, 0xf9, 0x2e, 0x48, 0x4b, 0x26, 0xe6, + 0x30, 0x4c, 0xa5, 0x58, 0xca, 0xb4, 0x44, 0x82, 0x4f, 0xe7, 0x91, 0x1e, + 0x33, 0xc3, 0xb0, 0x93, 0xff, 0x11, 0xfc, 0x81, 0xd2, 0xca, 0x1f, 0x71, + 0x29, 0xdd, 0x76, 0x4f, 0x92, 0x25, 0xaf, 0x1d, 0x81, 0xb7, 0x0f, 0x2f, + 0x8c, 0xc3, 0x06, 0xcc, 0x2f, 0x27, 0xa3, 0x4a, 0xe4, 0x0e, 0x99, 0xba, + 0x7c, 0x1e, 0x45, 0x1f, 0x7f, 0xaa, 0x19, 0x45, 0x96, 0xfd, 0xfc, 0x3d, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x07, 0x30, 0x82, 0x01, + 0x03, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xe1, 0x66, 0xcf, 0x0e, 0xd1, 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14, + 0xfe, 0x87, 0x12, 0xd5, 0xf6, 0xfe, 0xfb, 0x3e, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, + 0x1a, 0xa4, 0x40, 0x5b, 0xa5, 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, + 0x43, 0xd0, 0x41, 0xae, 0xf2, 0x30, 0x69, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x5d, 0x30, 0x5b, 0x30, 0x27, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x61, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x02, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, + 0x69, 0x61, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x63, 0x61, + 0x2e, 0x63, 0x72, 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, + 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x02, 0x01, 0x00, 0xb6, 0x6d, 0xf8, 0x70, 0xfb, 0xe2, 0x0d, 0x4c, + 0x98, 0xb3, 0x07, 0x49, 0x15, 0xf5, 0x04, 0xc4, 0x6c, 0xca, 0xca, 0xf5, + 0x68, 0xa0, 0x08, 0xfe, 0x12, 0x6d, 0x9c, 0x04, 0x06, 0xc9, 0xad, 0x9a, + 0x91, 0x52, 0x3e, 0x78, 0xc4, 0x5c, 0xee, 0x9f, 0x54, 0x1d, 0xee, 0xe3, + 0xf1, 0x5e, 0x30, 0xc9, 0x49, 0xe1, 0x39, 0xe0, 0xa6, 0x9d, 0x36, 0x6c, + 0x57, 0xfa, 0xe6, 0x34, 0x4f, 0x55, 0xe8, 0x87, 0xa8, 0x2c, 0xdd, 0x05, + 0xf1, 0x58, 0x12, 0x91, 0xe8, 0xca, 0xce, 0x28, 0x78, 0x8f, 0xdf, 0x07, + 0x85, 0x01, 0xa5, 0xdc, 0x45, 0x96, 0x05, 0xd4, 0x80, 0xb2, 0x2b, 0x05, + 0x9a, 0xcb, 0x9a, 0xa5, 0x8b, 0xe0, 0x3a, 0x67, 0xe6, 0x73, 0x47, 0xbe, + 0x4a, 0xfd, 0x27, 0xb1, 0x88, 0xef, 0xe6, 0xca, 0xcf, 0x8d, 0x0e, 0x26, + 0x9f, 0xfa, 0x5f, 0x57, 0x78, 0xad, 0x6d, 0xfe, 0xae, 0x9b, 0x35, 0x08, + 0xb1, 0xc3, 0xba, 0xc1, 0x00, 0x4a, 0x4b, 0x7d, 0x14, 0xbd, 0xf7, 0xf1, + 0xd3, 0x55, 0x18, 0xac, 0xd0, 0x33, 0x70, 0x88, 0x6d, 0xc4, 0x09, 0x71, + 0x14, 0xa6, 0x2b, 0x4f, 0x88, 0x81, 0xe7, 0x0b, 0x00, 0x37, 0xa9, 0x15, + 0x7d, 0x7e, 0xd7, 0x01, 0x96, 0x3f, 0x2f, 0xaf, 0x7b, 0x62, 0xae, 0x0a, + 0x4a, 0xbf, 0x4b, 0x39, 0x2e, 0x35, 0x10, 0x8b, 0xfe, 0x04, 0x39, 0xe4, + 0x3c, 0x3a, 0x0c, 0x09, 0x56, 0x40, 0x3a, 0xb5, 0xf4, 0xc2, 0x68, 0x0c, + 0xb5, 0xf9, 0x52, 0xcd, 0xee, 0x9d, 0xf8, 0x98, 0xfc, 0x78, 0xe7, 0x58, + 0x47, 0x8f, 0x1c, 0x73, 0x58, 0x69, 0x33, 0xab, 0xff, 0xdd, 0xdf, 0x8e, + 0x24, 0x01, 0x77, 0x98, 0x19, 0x3a, 0xb0, 0x66, 0x79, 0xbc, 0xe1, 0x08, + 0xa3, 0x0e, 0x4f, 0xc1, 0x04, 0xb3, 0xf3, 0x01, 0xc8, 0xeb, 0xd3, 0x59, + 0x1c, 0x35, 0xd2, 0x93, 0x1e, 0x70, 0x65, 0x82, 0x7f, 0xdb, 0xcf, 0xfb, + 0xc8, 0x99, 0x12, 0x60, 0xc3, 0x44, 0x6f, 0x3a, 0x80, 0x4b, 0xd7, 0xbe, + 0x21, 0xaa, 0x14, 0x7a, 0x64, 0xcb, 0xdd, 0x37, 0x43, 0x45, 0x5b, 0x32, + 0x2e, 0x45, 0xf0, 0xd9, 0x59, 0x1f, 0x6b, 0x18, 0xf0, 0x7c, 0xe9, 0x55, + 0x36, 0x19, 0x61, 0x5f, 0xb5, 0x7d, 0xf1, 0x8d, 0xbd, 0x88, 0xe4, 0x75, + 0x4b, 0x98, 0xdd, 0x27, 0xb0, 0xe4, 0x84, 0x44, 0x2a, 0x61, 0x84, 0x57, + 0x05, 0x82, 0x11, 0x1f, 0xaa, 0x35, 0x58, 0xf3, 0x20, 0x0e, 0xaf, 0x59, + 0xef, 0xfa, 0x55, 0x72, 0x72, 0x0d, 0x26, 0xd0, 0x9b, 0x53, 0x49, 0xac, + 0xce, 0x37, 0x2e, 0x65, 0x61, 0xff, 0xf6, 0xec, 0x1b, 0xea, 0xf6, 0xf1, + 0xa6, 0xd3, 0xd1, 0xb5, 0x7b, 0xbe, 0x35, 0xf4, 0x22, 0xc1, 0xbc, 0x8d, + 0x01, 0xbd, 0x68, 0x5e, 0x83, 0x0d, 0x2f, 0xec, 0xd6, 0xda, 0x63, 0x0c, + 0x27, 0xd1, 0x54, 0x3e, 0xe4, 0xa8, 0xd3, 0xce, 0x4b, 0x32, 0xb8, 0x91, + 0x94, 0xff, 0xfb, 0x5b, 0x49, 0x2d, 0x75, 0x18, 0xa8, 0xba, 0x71, 0x9a, + 0x3b, 0xae, 0xd9, 0xc0, 0xa9, 0x4f, 0x87, 0x91, 0xed, 0x8b, 0x7b, 0x6b, + 0x20, 0x98, 0x89, 0x39, 0x83, 0x4f, 0x80, 0xc4, 0x69, 0xcc, 0x17, 0xc9, + 0xc8, 0x4e, 0xbe, 0xe4, 0xa9, 0xa5, 0x81, 0x76, 0x70, 0x06, 0x04, 0x32, + 0xcd, 0x83, 0x65, 0xf4, 0xbc, 0x7d, 0x3e, 0x13, 0xbc, 0xd2, 0xe8, 0x6f, + 0x63, 0xaa, 0xb5, 0x3b, 0xda, 0x8d, 0x86, 0x32, 0x82, 0x78, 0x9d, 0xd9, + 0xcc, 0xff, 0xbf, 0x57, 0x64, 0x74, 0xed, 0x28, 0x3d, 0x44, 0x62, 0x15, + 0x61, 0x4b, 0xf7, 0x94, 0xb0, 0x0d, 0x2a, 0x67, 0x1c, 0xf0, 0xcb, 0x9b, + 0xa5, 0x92, 0xbf, 0xf8, 0x41, 0x5a, 0xc1, 0x3d, 0x60, 0xed, 0x9f, 0xbb, + 0xb8, 0x6d, 0x9b, 0xce, 0xa9, 0x6a, 0x16, 0x3f, 0x7e, 0xea, 0x06, 0xf1, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 03:37:b9:28:34:7c:60:a6:ae:c5:ad:b1:21:7f:38:60 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA + Validity + Not Before: Nov 9 12:00:00 2007 GMT + Not After : Nov 10 00:00:00 2021 GMT + Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31: + 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af: + 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01: + 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10: + 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a: + 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a: + db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0: + 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db: + 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58: + 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3: + ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47: + f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8: + 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11: + 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df: + ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32: + b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88: + 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0: + 3e:a7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping + X509v3 Certificate Policies: + Policy: 2.16.840.1.114412.2.1 + CPS: http://www.digicert.com/ssl-cps-repository.htm + User Notice: + Explicit Text: + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + Full Name: + URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + X509v3 Subject Key Identifier: + 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5 + X509v3 Authority Key Identifier: + keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 + + Signature Algorithm: sha1WithRSAEncryption + 4c:7a:17:87:28:5d:17:bc:b2:32:73:bf:cd:2e:f5:58:31:1d: + f0:b1:71:54:9c:d6:9b:67:93:db:2f:03:3e:16:6f:1e:03:c9: + 53:84:a3:56:60:1e:78:94:1b:a2:a8:6f:a3:a4:8b:52:91:d7: + dd:5c:95:bb:ef:b5:16:49:e9:a5:42:4f:34:f2:47:ff:ae:81: + 7f:13:54:b7:20:c4:70:15:cb:81:0a:81:cb:74:57:dc:9c:df: + 24:a4:29:0c:18:f0:1c:e4:ae:07:33:ec:f1:49:3e:55:cf:6e: + 4f:0d:54:7b:d3:c9:e8:15:48:d4:c5:bb:dc:35:1c:77:45:07: + 48:45:85:bd:d7:7e:53:b8:c0:16:d9:95:cd:8b:8d:7d:c9:60: + 4f:d1:a2:9b:e3:d0:30:d6:b4:73:36:e6:d2:f9:03:b2:e3:a4: + f5:e5:b8:3e:04:49:00:ba:2e:a6:4a:72:83:72:9d:f7:0b:8c: + a9:89:e7:b3:d7:64:1f:d6:e3:60:cb:03:c4:dc:88:e9:9d:25: + 01:00:71:cb:03:b4:29:60:25:8f:f9:46:d1:7b:71:ae:cd:53: + 12:5b:84:8e:c2:0f:c7:ed:93:19:d9:c9:fa:8f:58:34:76:32: + 2f:ae:e1:50:14:61:d4:a8:58:a3:c8:30:13:23:ef:c6:25:8c: + 36:8f:1c:80 +-----BEGIN CERTIFICATE----- +MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ +PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC +7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw +PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 +4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo +LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U +pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy +BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH +AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH +AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o +dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 +AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 +AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp +AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl +AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo +AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg +AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg +AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB +gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy +dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy +dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw +gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB +c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 +LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE +FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI +Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU +nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/ +roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU +xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+ +BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu +zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert53[] = { + 0x30, 0x82, 0x06, 0xe6, 0x30, 0x82, 0x05, 0xce, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x03, 0x37, 0xb9, 0x28, 0x34, 0x7c, 0x60, 0xa6, 0xae, + 0xc5, 0xad, 0xb1, 0x21, 0x7f, 0x38, 0x60, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x31, 0x31, 0x30, 0x39, 0x31, 0x32, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x69, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, + 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1f, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67, + 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, + 0x45, 0x56, 0x20, 0x43, 0x41, 0x2d, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xf3, 0x96, 0x62, 0xd8, 0x75, 0x6e, 0x19, 0xff, + 0x3f, 0x34, 0x7c, 0x49, 0x4f, 0x31, 0x7e, 0x0d, 0x04, 0x4e, 0x99, 0x81, + 0xe2, 0xb3, 0x85, 0x55, 0x91, 0x30, 0xb1, 0xc0, 0xaf, 0x70, 0xbb, 0x2c, + 0xa8, 0xe7, 0x18, 0xaa, 0x3f, 0x78, 0xf7, 0x90, 0x68, 0x52, 0x86, 0x01, + 0x88, 0x97, 0xe2, 0x3b, 0x06, 0x65, 0x90, 0xaa, 0xbd, 0x65, 0x76, 0xc2, + 0xec, 0xbe, 0x10, 0x5b, 0x37, 0x78, 0x83, 0x60, 0x75, 0x45, 0xc6, 0xbd, + 0x74, 0xaa, 0xb6, 0x9f, 0xa4, 0x3a, 0x01, 0x50, 0x17, 0xc4, 0x39, 0x69, + 0xb9, 0xf1, 0x4f, 0xef, 0x82, 0xc1, 0xca, 0xf3, 0x4a, 0xdb, 0xcc, 0x9e, + 0x50, 0x4f, 0x4d, 0x40, 0xa3, 0x3a, 0x90, 0xe7, 0x86, 0x66, 0xbc, 0xf0, + 0x3e, 0x76, 0x28, 0x4c, 0xd1, 0x75, 0x80, 0x9e, 0x6a, 0x35, 0x14, 0x35, + 0x03, 0x9e, 0xdb, 0x0c, 0x8c, 0xc2, 0x28, 0xad, 0x50, 0xb2, 0xce, 0xf6, + 0x91, 0xa3, 0xc3, 0xa5, 0x0a, 0x58, 0x49, 0xf6, 0x75, 0x44, 0x6c, 0xba, + 0xf9, 0xce, 0xe9, 0xab, 0x3a, 0x02, 0xe0, 0x4d, 0xf3, 0xac, 0xe2, 0x7a, + 0xe0, 0x60, 0x22, 0x05, 0x3c, 0x82, 0xd3, 0x52, 0xe2, 0xf3, 0x9c, 0x47, + 0xf8, 0x3b, 0xd8, 0xb2, 0x4b, 0x93, 0x56, 0x4a, 0xbf, 0x70, 0xab, 0x3e, + 0xe9, 0x68, 0xc8, 0x1d, 0x8f, 0x58, 0x1d, 0x2a, 0x4d, 0x5e, 0x27, 0x3d, + 0xad, 0x0a, 0x59, 0x2f, 0x5a, 0x11, 0x20, 0x40, 0xd9, 0x68, 0x04, 0x68, + 0x2d, 0xf4, 0xc0, 0x84, 0x0b, 0x0a, 0x1b, 0x78, 0xdf, 0xed, 0x1a, 0x58, + 0xdc, 0xfb, 0x41, 0x5a, 0x6d, 0x6b, 0xf2, 0xed, 0x1c, 0xee, 0x5c, 0x32, + 0xb6, 0x5c, 0xec, 0xd7, 0xa6, 0x03, 0x32, 0xa6, 0xe8, 0xde, 0xb7, 0x28, + 0x27, 0x59, 0x88, 0x80, 0xff, 0x7b, 0xad, 0x89, 0x58, 0xd5, 0x1e, 0x14, + 0xa4, 0xf2, 0xb0, 0x70, 0xd4, 0xa0, 0x3e, 0xa7, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x03, 0x85, 0x30, 0x82, 0x03, 0x81, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x86, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x34, 0x30, 0x32, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x04, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x08, 0x30, 0x82, 0x01, 0xc4, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x82, 0x01, 0xbb, 0x30, 0x82, 0x01, 0xb7, 0x30, 0x82, 0x01, 0xb3, 0x06, + 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6c, 0x02, 0x01, 0x30, 0x82, + 0x01, 0xa4, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d, + 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68, + 0x74, 0x6d, 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52, + 0x00, 0x41, 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, + 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74, + 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, + 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, + 0x00, 0x6e, 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75, + 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63, + 0x00, 0x63, 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, + 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, + 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69, + 0x00, 0x67, 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, + 0x00, 0x20, 0x00, 0x45, 0x00, 0x56, 0x00, 0x20, 0x00, 0x43, 0x00, 0x50, + 0x00, 0x53, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, + 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65, + 0x00, 0x6c, 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, + 0x00, 0x50, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, + 0x00, 0x41, 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d, + 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68, + 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, + 0x00, 0x6d, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, + 0x00, 0x61, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, + 0x00, 0x79, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, + 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e, + 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, + 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68, + 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20, + 0x00, 0x62, 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66, + 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65, + 0x00, 0x2e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x81, + 0x83, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x77, 0x30, 0x75, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4d, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x41, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, + 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x41, 0x43, 0x65, 0x72, + 0x74, 0x73, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, + 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, + 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x81, 0x8f, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81, 0x87, 0x30, + 0x81, 0x84, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, + 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, + 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, + 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40, 0xa0, 0x3e, + 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, + 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, + 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x4c, 0x58, 0xcb, 0x25, 0xf0, 0x41, 0x4f, 0x52, 0xf4, 0x28, 0xc8, + 0x81, 0x43, 0x9b, 0xa6, 0xa8, 0xa0, 0xe6, 0x92, 0xe5, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, + 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, + 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x4c, 0x7a, 0x17, 0x87, 0x28, 0x5d, 0x17, 0xbc, 0xb2, 0x32, + 0x73, 0xbf, 0xcd, 0x2e, 0xf5, 0x58, 0x31, 0x1d, 0xf0, 0xb1, 0x71, 0x54, + 0x9c, 0xd6, 0x9b, 0x67, 0x93, 0xdb, 0x2f, 0x03, 0x3e, 0x16, 0x6f, 0x1e, + 0x03, 0xc9, 0x53, 0x84, 0xa3, 0x56, 0x60, 0x1e, 0x78, 0x94, 0x1b, 0xa2, + 0xa8, 0x6f, 0xa3, 0xa4, 0x8b, 0x52, 0x91, 0xd7, 0xdd, 0x5c, 0x95, 0xbb, + 0xef, 0xb5, 0x16, 0x49, 0xe9, 0xa5, 0x42, 0x4f, 0x34, 0xf2, 0x47, 0xff, + 0xae, 0x81, 0x7f, 0x13, 0x54, 0xb7, 0x20, 0xc4, 0x70, 0x15, 0xcb, 0x81, + 0x0a, 0x81, 0xcb, 0x74, 0x57, 0xdc, 0x9c, 0xdf, 0x24, 0xa4, 0x29, 0x0c, + 0x18, 0xf0, 0x1c, 0xe4, 0xae, 0x07, 0x33, 0xec, 0xf1, 0x49, 0x3e, 0x55, + 0xcf, 0x6e, 0x4f, 0x0d, 0x54, 0x7b, 0xd3, 0xc9, 0xe8, 0x15, 0x48, 0xd4, + 0xc5, 0xbb, 0xdc, 0x35, 0x1c, 0x77, 0x45, 0x07, 0x48, 0x45, 0x85, 0xbd, + 0xd7, 0x7e, 0x53, 0xb8, 0xc0, 0x16, 0xd9, 0x95, 0xcd, 0x8b, 0x8d, 0x7d, + 0xc9, 0x60, 0x4f, 0xd1, 0xa2, 0x9b, 0xe3, 0xd0, 0x30, 0xd6, 0xb4, 0x73, + 0x36, 0xe6, 0xd2, 0xf9, 0x03, 0xb2, 0xe3, 0xa4, 0xf5, 0xe5, 0xb8, 0x3e, + 0x04, 0x49, 0x00, 0xba, 0x2e, 0xa6, 0x4a, 0x72, 0x83, 0x72, 0x9d, 0xf7, + 0x0b, 0x8c, 0xa9, 0x89, 0xe7, 0xb3, 0xd7, 0x64, 0x1f, 0xd6, 0xe3, 0x60, + 0xcb, 0x03, 0xc4, 0xdc, 0x88, 0xe9, 0x9d, 0x25, 0x01, 0x00, 0x71, 0xcb, + 0x03, 0xb4, 0x29, 0x60, 0x25, 0x8f, 0xf9, 0x46, 0xd1, 0x7b, 0x71, 0xae, + 0xcd, 0x53, 0x12, 0x5b, 0x84, 0x8e, 0xc2, 0x0f, 0xc7, 0xed, 0x93, 0x19, + 0xd9, 0xc9, 0xfa, 0x8f, 0x58, 0x34, 0x76, 0x32, 0x2f, 0xae, 0xe1, 0x50, + 0x14, 0x61, 0xd4, 0xa8, 0x58, 0xa3, 0xc8, 0x30, 0x13, 0x23, 0xef, 0xc6, + 0x25, 0x8c, 0x36, 0x8f, 0x1c, 0x80, +};
diff --git a/quic/core/crypto/common_cert_set_3.c b/quic/core/crypto/common_cert_set_3.c new file mode 100644 index 0000000..876d00c --- /dev/null +++ b/quic/core/crypto/common_cert_set_3.c
@@ -0,0 +1,118 @@ +/* This file contains common certificates. It's designed to be #included in + * another file, in a namespace. */ + +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_3a.inc" +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_3b.inc" + +static const size_t kNumCerts = 52; +static const unsigned char* const kCerts[] = { + kDERCert0, + kDERCert1, + kDERCert2, + kDERCert3, + kDERCert4, + kDERCert5, + kDERCert6, + kDERCert7, + kDERCert8, + kDERCert9, + kDERCert10, + kDERCert11, + kDERCert12, + kDERCert13, + kDERCert14, + kDERCert15, + kDERCert16, + kDERCert17, + kDERCert18, + kDERCert19, + kDERCert20, + kDERCert21, + kDERCert22, + kDERCert23, + kDERCert24, + kDERCert25, + kDERCert26, + kDERCert27, + kDERCert28, + kDERCert29, + kDERCert30, + kDERCert31, + kDERCert32, + kDERCert33, + kDERCert34, + kDERCert35, + kDERCert36, + kDERCert37, + kDERCert38, + kDERCert39, + kDERCert40, + kDERCert41, + kDERCert42, + kDERCert43, + kDERCert44, + kDERCert45, + kDERCert46, + kDERCert47, + kDERCert48, + kDERCert49, + kDERCert50, + kDERCert51, +}; + +static const size_t kLens[] = { + 897, + 911, + 1012, + 1049, + 1065, + 1096, + 1097, + 1101, + 1105, + 1105, + 1107, + 1117, + 1127, + 1133, + 1136, + 1138, + 1139, + 1145, + 1149, + 1153, + 1167, + 1172, + 1174, + 1174, + 1176, + 1188, + 1194, + 1196, + 1203, + 1205, + 1206, + 1208, + 1209, + 1210, + 1222, + 1227, + 1236, + 1236, + 1238, + 1283, + 1284, + 1287, + 1298, + 1315, + 1327, + 1340, + 1357, + 1418, + 1447, + 1509, + 1513, + 1632, +}; + +static const uint64_t kHash = UINT64_C(0x918215a28680ed7e);
diff --git a/quic/core/crypto/common_cert_set_3a.inc b/quic/core/crypto/common_cert_set_3a.inc new file mode 100644 index 0000000..d5bff19 --- /dev/null +++ b/quic/core/crypto/common_cert_set_3a.inc
@@ -0,0 +1,5033 @@ +/* This file contains common certificates. It's designed to be #included in + * another file, in a namespace. */ + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1227750 (0x12bbe6) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority + Validity + Not Before: May 21 04:00:00 2002 GMT + Not After : Aug 21 04:00:00 2018 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df: + 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8: + 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29: + bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4: + 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3: + ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92: + 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d: + 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14: + 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd: + d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6: + d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5: + 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39: + 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05: + 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2: + fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32: + eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07: + 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b: + e4:f9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4 + + X509v3 Subject Key Identifier: + C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/crls/secureca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.geotrust.com/resources/repository + + Signature Algorithm: sha1WithRSAEncryption + 76:e1:12:6e:4e:4b:16:12:86:30:06:b2:81:08:cf:f0:08:c7: + c7:71:7e:66:ee:c2:ed:d4:3b:1f:ff:f0:f0:c8:4e:d6:43:38: + b0:b9:30:7d:18:d0:55:83:a2:6a:cb:36:11:9c:e8:48:66:a3: + 6d:7f:b8:13:d4:47:fe:8b:5a:5c:73:fc:ae:d9:1b:32:19:38: + ab:97:34:14:aa:96:d2:eb:a3:1c:14:08:49:b6:bb:e5:91:ef: + 83:36:eb:1d:56:6f:ca:da:bc:73:63:90:e4:7f:7b:3e:22:cb: + 3d:07:ed:5f:38:74:9c:e3:03:50:4e:a1:af:98:ee:61:f2:84: + 3f:12 +-----BEGIN CERTIFICATE----- +MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT +MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 +aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw +WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE +AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m +OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu +T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c +JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR +Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz +PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm +aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM +TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g +LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO +BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv +dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB +AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL +NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W +b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert0[] = { + 0x30, 0x82, 0x03, 0x7d, 0x30, 0x82, 0x02, 0xe6, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x12, 0xbb, 0xe6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45, + 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x32, 0x30, + 0x35, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, + 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, + 0x5a, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x12, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0xcc, 0x18, 0x63, 0x30, 0xfd, + 0xf4, 0x17, 0x23, 0x1a, 0x56, 0x7e, 0x5b, 0xdf, 0x3c, 0x6c, 0x38, 0xe4, + 0x71, 0xb7, 0x78, 0x91, 0xd4, 0xbc, 0xa1, 0xd8, 0x4c, 0xf8, 0xa8, 0x43, + 0xb6, 0x03, 0xe9, 0x4d, 0x21, 0x07, 0x08, 0x88, 0xda, 0x58, 0x2f, 0x66, + 0x39, 0x29, 0xbd, 0x05, 0x78, 0x8b, 0x9d, 0x38, 0xe8, 0x05, 0xb7, 0x6a, + 0x7e, 0x71, 0xa4, 0xe6, 0xc4, 0x60, 0xa6, 0xb0, 0xef, 0x80, 0xe4, 0x89, + 0x28, 0x0f, 0x9e, 0x25, 0xd6, 0xed, 0x83, 0xf3, 0xad, 0xa6, 0x91, 0xc7, + 0x98, 0xc9, 0x42, 0x18, 0x35, 0x14, 0x9d, 0xad, 0x98, 0x46, 0x92, 0x2e, + 0x4f, 0xca, 0xf1, 0x87, 0x43, 0xc1, 0x16, 0x95, 0x57, 0x2d, 0x50, 0xef, + 0x89, 0x2d, 0x80, 0x7a, 0x57, 0xad, 0xf2, 0xee, 0x5f, 0x6b, 0xd2, 0x00, + 0x8d, 0xb9, 0x14, 0xf8, 0x14, 0x15, 0x35, 0xd9, 0xc0, 0x46, 0xa3, 0x7b, + 0x72, 0xc8, 0x91, 0xbf, 0xc9, 0x55, 0x2b, 0xcd, 0xd0, 0x97, 0x3e, 0x9c, + 0x26, 0x64, 0xcc, 0xdf, 0xce, 0x83, 0x19, 0x71, 0xca, 0x4e, 0xe6, 0xd4, + 0xd5, 0x7b, 0xa9, 0x19, 0xcd, 0x55, 0xde, 0xc8, 0xec, 0xd2, 0x5e, 0x38, + 0x53, 0xe5, 0x5c, 0x4f, 0x8c, 0x2d, 0xfe, 0x50, 0x23, 0x36, 0xfc, 0x66, + 0xe6, 0xcb, 0x8e, 0xa4, 0x39, 0x19, 0x00, 0xb7, 0x95, 0x02, 0x39, 0x91, + 0x0b, 0x0e, 0xfe, 0x38, 0x2e, 0xd1, 0x1d, 0x05, 0x9a, 0xf6, 0x4d, 0x3e, + 0x6f, 0x0f, 0x07, 0x1d, 0xaf, 0x2c, 0x1e, 0x8f, 0x60, 0x39, 0xe2, 0xfa, + 0x36, 0x53, 0x13, 0x39, 0xd4, 0x5e, 0x26, 0x2b, 0xdb, 0x3d, 0xa8, 0x14, + 0xbd, 0x32, 0xeb, 0x18, 0x03, 0x28, 0x52, 0x04, 0x71, 0xe5, 0xab, 0x33, + 0x3d, 0xe1, 0x38, 0xbb, 0x07, 0x36, 0x84, 0x62, 0x9c, 0x79, 0xea, 0x16, + 0x30, 0xf4, 0x5f, 0xc0, 0x2b, 0xe8, 0x71, 0x6b, 0xe4, 0xf9, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xf0, 0x30, 0x81, 0xed, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, + 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, + 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, + 0x2d, 0xa0, 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, + 0x63, 0x75, 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4e, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, + 0x00, 0x76, 0xe1, 0x12, 0x6e, 0x4e, 0x4b, 0x16, 0x12, 0x86, 0x30, 0x06, + 0xb2, 0x81, 0x08, 0xcf, 0xf0, 0x08, 0xc7, 0xc7, 0x71, 0x7e, 0x66, 0xee, + 0xc2, 0xed, 0xd4, 0x3b, 0x1f, 0xff, 0xf0, 0xf0, 0xc8, 0x4e, 0xd6, 0x43, + 0x38, 0xb0, 0xb9, 0x30, 0x7d, 0x18, 0xd0, 0x55, 0x83, 0xa2, 0x6a, 0xcb, + 0x36, 0x11, 0x9c, 0xe8, 0x48, 0x66, 0xa3, 0x6d, 0x7f, 0xb8, 0x13, 0xd4, + 0x47, 0xfe, 0x8b, 0x5a, 0x5c, 0x73, 0xfc, 0xae, 0xd9, 0x1b, 0x32, 0x19, + 0x38, 0xab, 0x97, 0x34, 0x14, 0xaa, 0x96, 0xd2, 0xeb, 0xa3, 0x1c, 0x14, + 0x08, 0x49, 0xb6, 0xbb, 0xe5, 0x91, 0xef, 0x83, 0x36, 0xeb, 0x1d, 0x56, + 0x6f, 0xca, 0xda, 0xbc, 0x73, 0x63, 0x90, 0xe4, 0x7f, 0x7b, 0x3e, 0x22, + 0xcb, 0x3d, 0x07, 0xed, 0x5f, 0x38, 0x74, 0x9c, 0xe3, 0x03, 0x50, 0x4e, + 0xa1, 0xaf, 0x98, 0xee, 0x61, 0xf2, 0x84, 0x3f, 0x12, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 880226 (0xd6e62) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority + Validity + Not Before: Nov 27 00:00:00 2006 GMT + Not After : Aug 21 16:15:00 2018 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:be:b8:15:7b:ff:d4:7c:7d:67:ad:83:64:7b:c8: + 42:53:2d:df:f6:84:08:20:61:d6:01:59:6a:9c:44: + 11:af:ef:76:fd:95:7e:ce:61:30:bb:7a:83:5f:02: + bd:01:66:ca:ee:15:8d:6f:a1:30:9c:bd:a1:85:9e: + 94:3a:f3:56:88:00:31:cf:d8:ee:6a:96:02:d9:ed: + 03:8c:fb:75:6d:e7:ea:b8:55:16:05:16:9a:f4:e0: + 5e:b1:88:c0:64:85:5c:15:4d:88:c7:b7:ba:e0:75: + e9:ad:05:3d:9d:c7:89:48:e0:bb:28:c8:03:e1:30: + 93:64:5e:52:c0:59:70:22:35:57:88:8a:f1:95:0a: + 83:d7:bc:31:73:01:34:ed:ef:46:71:e0:6b:02:a8: + 35:72:6b:97:9b:66:e0:cb:1c:79:5f:d8:1a:04:68: + 1e:47:02:e6:9d:60:e2:36:97:01:df:ce:35:92:df: + be:67:c7:6d:77:59:3b:8f:9d:d6:90:15:94:bc:42: + 34:10:c1:39:f9:b1:27:3e:7e:d6:8a:75:c5:b2:af: + 96:d3:a2:de:9b:e4:98:be:7d:e1:e9:81:ad:b6:6f: + fc:d7:0e:da:e0:34:b0:0d:1a:77:e7:e3:08:98:ef: + 58:fa:9c:84:b7:36:af:c2:df:ac:d2:f4:10:06:70: + 71:35 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92 + X509v3 Authority Key Identifier: + keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4 + + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/crls/secureca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.geotrust.com/resources/cps + + Signature Algorithm: sha1WithRSAEncryption + af:f3:0e:d6:72:ab:c7:a9:97:ca:2a:6b:84:39:de:79:a9:f0: + 81:e5:08:67:ab:d7:2f:20:02:01:71:0c:04:22:c9:1e:88:95: + 03:c9:49:3a:af:67:08:49:b0:d5:08:f5:20:3d:80:91:a0:c5: + 87:a3:fb:c9:a3:17:91:f9:a8:2f:ae:e9:0f:df:96:72:0f:75: + 17:80:5d:78:01:4d:9f:1f:6d:7b:d8:f5:42:38:23:1a:99:93: + f4:83:be:3b:35:74:e7:37:13:35:7a:ac:b4:b6:90:82:6c:27: + a4:e0:ec:9e:35:bd:bf:e5:29:a1:47:9f:5b:32:fc:e9:99:7d: + 2b:39 +-----BEGIN CERTIFICATE----- +MIIDizCCAvSgAwIBAgIDDW5iMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT +MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 +aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMTI3MDAwMDAwWhcNMTgwODIxMTYxNTAw +WjBYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UE +AxMoR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64FXv/1Hx9Z62DZHvIQlMt3/aE +CCBh1gFZapxEEa/vdv2Vfs5hMLt6g18CvQFmyu4VjW+hMJy9oYWelDrzVogAMc/Y +7mqWAtntA4z7dW3n6rhVFgUWmvTgXrGIwGSFXBVNiMe3uuB16a0FPZ3HiUjguyjI +A+Ewk2ReUsBZcCI1V4iK8ZUKg9e8MXMBNO3vRnHgawKoNXJrl5tm4MsceV/YGgRo +HkcC5p1g4jaXAd/ONZLfvmfHbXdZO4+d1pAVlLxCNBDBOfmxJz5+1op1xbKvltOi +3pvkmL594emBrbZv/NcO2uA0sA0ad+fjCJjvWPqchLc2r8LfrNL0EAZwcTUCAwEA +AaOB6DCB5TAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFCzVUEGXFYvwjzZhW0r7 +a9mZyTOSMB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMA8GA1UdEwEB +/wQFMAMBAf8wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j +b20vY3Jscy9zZWN1cmVjYS5jcmwwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYB +BQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwDQYJ +KoZIhvcNAQEFBQADgYEAr/MO1nKrx6mXyiprhDneeanwgeUIZ6vXLyACAXEMBCLJ +HoiVA8lJOq9nCEmw1Qj1ID2AkaDFh6P7yaMXkfmoL67pD9+Wcg91F4BdeAFNnx9t +e9j1QjgjGpmT9IO+OzV05zcTNXqstLaQgmwnpODsnjW9v+UpoUefWzL86Zl9Kzk= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert1[] = { + 0x30, 0x82, 0x03, 0x8b, 0x30, 0x82, 0x02, 0xf4, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x0d, 0x6e, 0x62, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45, + 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, + 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, + 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x31, 0x36, 0x31, 0x35, 0x30, 0x30, + 0x5a, 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xbe, 0xb8, 0x15, 0x7b, 0xff, 0xd4, 0x7c, 0x7d, + 0x67, 0xad, 0x83, 0x64, 0x7b, 0xc8, 0x42, 0x53, 0x2d, 0xdf, 0xf6, 0x84, + 0x08, 0x20, 0x61, 0xd6, 0x01, 0x59, 0x6a, 0x9c, 0x44, 0x11, 0xaf, 0xef, + 0x76, 0xfd, 0x95, 0x7e, 0xce, 0x61, 0x30, 0xbb, 0x7a, 0x83, 0x5f, 0x02, + 0xbd, 0x01, 0x66, 0xca, 0xee, 0x15, 0x8d, 0x6f, 0xa1, 0x30, 0x9c, 0xbd, + 0xa1, 0x85, 0x9e, 0x94, 0x3a, 0xf3, 0x56, 0x88, 0x00, 0x31, 0xcf, 0xd8, + 0xee, 0x6a, 0x96, 0x02, 0xd9, 0xed, 0x03, 0x8c, 0xfb, 0x75, 0x6d, 0xe7, + 0xea, 0xb8, 0x55, 0x16, 0x05, 0x16, 0x9a, 0xf4, 0xe0, 0x5e, 0xb1, 0x88, + 0xc0, 0x64, 0x85, 0x5c, 0x15, 0x4d, 0x88, 0xc7, 0xb7, 0xba, 0xe0, 0x75, + 0xe9, 0xad, 0x05, 0x3d, 0x9d, 0xc7, 0x89, 0x48, 0xe0, 0xbb, 0x28, 0xc8, + 0x03, 0xe1, 0x30, 0x93, 0x64, 0x5e, 0x52, 0xc0, 0x59, 0x70, 0x22, 0x35, + 0x57, 0x88, 0x8a, 0xf1, 0x95, 0x0a, 0x83, 0xd7, 0xbc, 0x31, 0x73, 0x01, + 0x34, 0xed, 0xef, 0x46, 0x71, 0xe0, 0x6b, 0x02, 0xa8, 0x35, 0x72, 0x6b, + 0x97, 0x9b, 0x66, 0xe0, 0xcb, 0x1c, 0x79, 0x5f, 0xd8, 0x1a, 0x04, 0x68, + 0x1e, 0x47, 0x02, 0xe6, 0x9d, 0x60, 0xe2, 0x36, 0x97, 0x01, 0xdf, 0xce, + 0x35, 0x92, 0xdf, 0xbe, 0x67, 0xc7, 0x6d, 0x77, 0x59, 0x3b, 0x8f, 0x9d, + 0xd6, 0x90, 0x15, 0x94, 0xbc, 0x42, 0x34, 0x10, 0xc1, 0x39, 0xf9, 0xb1, + 0x27, 0x3e, 0x7e, 0xd6, 0x8a, 0x75, 0xc5, 0xb2, 0xaf, 0x96, 0xd3, 0xa2, + 0xde, 0x9b, 0xe4, 0x98, 0xbe, 0x7d, 0xe1, 0xe9, 0x81, 0xad, 0xb6, 0x6f, + 0xfc, 0xd7, 0x0e, 0xda, 0xe0, 0x34, 0xb0, 0x0d, 0x1a, 0x77, 0xe7, 0xe3, + 0x08, 0x98, 0xef, 0x58, 0xfa, 0x9c, 0x84, 0xb7, 0x36, 0xaf, 0xc2, 0xdf, + 0xac, 0xd2, 0xf4, 0x10, 0x06, 0x70, 0x71, 0x35, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x81, 0xe8, 0x30, 0x81, 0xe5, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2c, 0xd5, + 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb, + 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b, + 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98, + 0x90, 0x9f, 0xd4, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3a, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, + 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55, + 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x81, 0x81, 0x00, 0xaf, 0xf3, 0x0e, 0xd6, 0x72, 0xab, 0xc7, 0xa9, 0x97, + 0xca, 0x2a, 0x6b, 0x84, 0x39, 0xde, 0x79, 0xa9, 0xf0, 0x81, 0xe5, 0x08, + 0x67, 0xab, 0xd7, 0x2f, 0x20, 0x02, 0x01, 0x71, 0x0c, 0x04, 0x22, 0xc9, + 0x1e, 0x88, 0x95, 0x03, 0xc9, 0x49, 0x3a, 0xaf, 0x67, 0x08, 0x49, 0xb0, + 0xd5, 0x08, 0xf5, 0x20, 0x3d, 0x80, 0x91, 0xa0, 0xc5, 0x87, 0xa3, 0xfb, + 0xc9, 0xa3, 0x17, 0x91, 0xf9, 0xa8, 0x2f, 0xae, 0xe9, 0x0f, 0xdf, 0x96, + 0x72, 0x0f, 0x75, 0x17, 0x80, 0x5d, 0x78, 0x01, 0x4d, 0x9f, 0x1f, 0x6d, + 0x7b, 0xd8, 0xf5, 0x42, 0x38, 0x23, 0x1a, 0x99, 0x93, 0xf4, 0x83, 0xbe, + 0x3b, 0x35, 0x74, 0xe7, 0x37, 0x13, 0x35, 0x7a, 0xac, 0xb4, 0xb6, 0x90, + 0x82, 0x6c, 0x27, 0xa4, 0xe0, 0xec, 0x9e, 0x35, 0xbd, 0xbf, 0xe5, 0x29, + 0xa1, 0x47, 0x9f, 0x5b, 0x32, 0xfc, 0xe9, 0x99, 0x7d, 0x2b, 0x39, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146066 (0x23a92) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Apr 1 00:00:00 2015 GMT + Not After : Dec 31 23:59:59 2017 GMT + Subject: C=US, O=Google Inc, CN=Google Internet Authority G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9c:2a:04:77:5c:d8:50:91:3a:06:a3:82:e0:d8: + 50:48:bc:89:3f:f1:19:70:1a:88:46:7e:e0:8f:c5: + f1:89:ce:21:ee:5a:fe:61:0d:b7:32:44:89:a0:74: + 0b:53:4f:55:a4:ce:82:62:95:ee:eb:59:5f:c6:e1: + 05:80:12:c4:5e:94:3f:bc:5b:48:38:f4:53:f7:24: + e6:fb:91:e9:15:c4:cf:f4:53:0d:f4:4a:fc:9f:54: + de:7d:be:a0:6b:6f:87:c0:d0:50:1f:28:30:03:40: + da:08:73:51:6c:7f:ff:3a:3c:a7:37:06:8e:bd:4b: + 11:04:eb:7d:24:de:e6:f9:fc:31:71:fb:94:d5:60: + f3:2e:4a:af:42:d2:cb:ea:c4:6a:1a:b2:cc:53:dd: + 15:4b:8b:1f:c8:19:61:1f:cd:9d:a8:3e:63:2b:84: + 35:69:65:84:c8:19:c5:46:22:f8:53:95:be:e3:80: + 4a:10:c6:2a:ec:ba:97:20:11:c7:39:99:10:04:a0: + f0:61:7a:95:25:8c:4e:52:75:e2:b6:ed:08:ca:14: + fc:ce:22:6a:b3:4e:cf:46:03:97:97:03:7e:c0:b1: + de:7b:af:45:33:cf:ba:3e:71:b7:de:f4:25:25:c2: + 0d:35:89:9d:9d:fb:0e:11:79:89:1e:37:c5:af:8e: + 72:69 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + 4A:DD:06:16:1B:BC:F6:68:B5:76:F5:81:B6:BB:62:1A:BA:5A:81:2F + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.11129.2.5.1 + + Signature Algorithm: sha256WithRSAEncryption + 08:4e:04:a7:80:7f:10:16:43:5e:02:ad:d7:42:80:f4:b0:8e: + d2:ae:b3:eb:11:7d:90:84:18:7d:e7:90:15:fb:49:7f:a8:99: + 05:91:bb:7a:c9:d6:3c:37:18:09:9a:b6:c7:92:20:07:35:33: + 09:e4:28:63:72:0d:b4:e0:32:9c:87:98:c4:1b:76:89:67:c1: + 50:58:b0:13:aa:13:1a:1b:32:a5:be:ea:11:95:4c:48:63:49: + e9:99:5d:20:37:cc:fe:2a:69:51:16:95:4b:a9:de:49:82:c0: + 10:70:f4:2c:f3:ec:bc:24:24:d0:4e:ac:a5:d9:5e:1e:6d:92: + c1:a7:ac:48:35:81:f9:e5:e4:9c:65:69:cd:87:a4:41:50:3f: + 2e:57:a5:91:51:12:58:0e:8c:09:a1:ac:7a:a4:12:a5:27:f3: + 9a:10:97:7d:55:03:06:f7:66:58:5f:5f:64:e1:ab:5d:6d:a5: + 39:48:75:98:4c:29:5a:3a:8d:d3:2b:ca:9c:55:04:bf:f4:e6: + 14:d5:80:ac:26:ed:17:89:a6:93:6c:5c:a4:cc:b8:f0:66:8e: + 64:e3:7d:9a:e2:00:b3:49:c7:e4:0a:aa:dd:5b:83:c7:70:90: + 46:4e:be:d0:db:59:96:6c:2e:f5:16:36:de:71:cc:01:c2:12: + c1:21:c6:16 +-----BEGIN CERTIFICATE----- +MIID8DCCAtigAwIBAgIDAjqSMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTUwNDAxMDAwMDAwWhcNMTcxMjMxMjM1OTU5WjBJMQswCQYDVQQG +EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy +bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP +VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv +h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE +ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ +EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC +DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7 +qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD +VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov +L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig +JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ +MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEACE4Ep4B/EBZDXgKt +10KA9LCO0q6z6xF9kIQYfeeQFftJf6iZBZG7esnWPDcYCZq2x5IgBzUzCeQoY3IN +tOAynIeYxBt2iWfBUFiwE6oTGhsypb7qEZVMSGNJ6ZldIDfM/ippURaVS6neSYLA +EHD0LPPsvCQk0E6spdleHm2SwaesSDWB+eXknGVpzYekQVA/LlelkVESWA6MCaGs +eqQSpSfzmhCXfVUDBvdmWF9fZOGrXW2lOUh1mEwpWjqN0yvKnFUEv/TmFNWArCbt +F4mmk2xcpMy48GaOZON9muIAs0nH5Aqq3VuDx3CQRk6+0NtZlmwu9RY23nHMAcIS +wSHGFg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert2[] = { + 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, + 0x34, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, + 0x31, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, + 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, + 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3, + 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a, + 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a, + 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f, + 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1, + 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4, + 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53, + 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f, + 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73, + 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b, + 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb, + 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4, + 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19, + 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65, + 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80, + 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99, + 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75, + 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e, + 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf, + 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2, + 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37, + 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, + 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81, + 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, + 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, + 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10, + 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, + 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x08, 0x4e, 0x04, 0xa7, 0x80, 0x7f, 0x10, 0x16, 0x43, 0x5e, 0x02, 0xad, + 0xd7, 0x42, 0x80, 0xf4, 0xb0, 0x8e, 0xd2, 0xae, 0xb3, 0xeb, 0x11, 0x7d, + 0x90, 0x84, 0x18, 0x7d, 0xe7, 0x90, 0x15, 0xfb, 0x49, 0x7f, 0xa8, 0x99, + 0x05, 0x91, 0xbb, 0x7a, 0xc9, 0xd6, 0x3c, 0x37, 0x18, 0x09, 0x9a, 0xb6, + 0xc7, 0x92, 0x20, 0x07, 0x35, 0x33, 0x09, 0xe4, 0x28, 0x63, 0x72, 0x0d, + 0xb4, 0xe0, 0x32, 0x9c, 0x87, 0x98, 0xc4, 0x1b, 0x76, 0x89, 0x67, 0xc1, + 0x50, 0x58, 0xb0, 0x13, 0xaa, 0x13, 0x1a, 0x1b, 0x32, 0xa5, 0xbe, 0xea, + 0x11, 0x95, 0x4c, 0x48, 0x63, 0x49, 0xe9, 0x99, 0x5d, 0x20, 0x37, 0xcc, + 0xfe, 0x2a, 0x69, 0x51, 0x16, 0x95, 0x4b, 0xa9, 0xde, 0x49, 0x82, 0xc0, + 0x10, 0x70, 0xf4, 0x2c, 0xf3, 0xec, 0xbc, 0x24, 0x24, 0xd0, 0x4e, 0xac, + 0xa5, 0xd9, 0x5e, 0x1e, 0x6d, 0x92, 0xc1, 0xa7, 0xac, 0x48, 0x35, 0x81, + 0xf9, 0xe5, 0xe4, 0x9c, 0x65, 0x69, 0xcd, 0x87, 0xa4, 0x41, 0x50, 0x3f, + 0x2e, 0x57, 0xa5, 0x91, 0x51, 0x12, 0x58, 0x0e, 0x8c, 0x09, 0xa1, 0xac, + 0x7a, 0xa4, 0x12, 0xa5, 0x27, 0xf3, 0x9a, 0x10, 0x97, 0x7d, 0x55, 0x03, + 0x06, 0xf7, 0x66, 0x58, 0x5f, 0x5f, 0x64, 0xe1, 0xab, 0x5d, 0x6d, 0xa5, + 0x39, 0x48, 0x75, 0x98, 0x4c, 0x29, 0x5a, 0x3a, 0x8d, 0xd3, 0x2b, 0xca, + 0x9c, 0x55, 0x04, 0xbf, 0xf4, 0xe6, 0x14, 0xd5, 0x80, 0xac, 0x26, 0xed, + 0x17, 0x89, 0xa6, 0x93, 0x6c, 0x5c, 0xa4, 0xcc, 0xb8, 0xf0, 0x66, 0x8e, + 0x64, 0xe3, 0x7d, 0x9a, 0xe2, 0x00, 0xb3, 0x49, 0xc7, 0xe4, 0x0a, 0xaa, + 0xdd, 0x5b, 0x83, 0xc7, 0x70, 0x90, 0x46, 0x4e, 0xbe, 0xd0, 0xdb, 0x59, + 0x96, 0x6c, 0x2e, 0xf5, 0x16, 0x36, 0xde, 0x71, 0xcc, 0x01, 0xc2, 0x12, + 0xc1, 0x21, 0xc6, 0x16, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120033005 (0x7278eed) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root + Validity + Not Before: Apr 18 16:36:18 2012 GMT + Not After : Aug 13 16:35:17 2018 GMT + Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79: + d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a: + 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2: + 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01: + 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7: + 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6: + 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c: + a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70: + 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77: + d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae: + 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18: + 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85: + ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9: + 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5: + c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a: + ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0: + 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27: + 1a:39 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:3 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://cybertrust.omniroot.com/repository + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Authority Key Identifier: + DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root + serial:01:A5 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl + + Signature Algorithm: sha1WithRSAEncryption + 93:1d:fe:8b:ae:46:ec:cb:a9:0f:ab:e5:ef:ca:b2:68:16:68: + d8:8f:fa:13:a9:af:b3:cb:2d:e7:4b:6e:8e:69:2a:c2:2b:10: + 0a:8d:f6:ae:73:b6:b9:fb:14:fd:5f:6d:b8:50:b6:c4:8a:d6: + 40:7e:d7:c3:cb:73:dc:c9:5d:5b:af:b0:41:b5:37:eb:ea:dc: + 20:91:c4:34:6a:f4:a1:f3:96:9d:37:86:97:e1:71:a4:dd:7d: + fa:44:84:94:ae:d7:09:04:22:76:0f:64:51:35:a9:24:0f:f9: + 0b:db:32:da:c2:fe:c1:b9:2a:5c:7a:27:13:ca:b1:48:3a:71: + d0:43 +-----BEGIN CERTIFICATE----- +MIIEFTCCA36gAwIBAgIEByeO7TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MB4XDTEyMDQxODE2MzYxOFoXDTE4MDgxMzE2MzUxN1owWjELMAkG +A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz +dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO +KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn +c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP +wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg +kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc +B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAUcw +ggFDMBIGA1UdEwEB/wQIMAYBAf8CAQMwSgYDVR0gBEMwQTA/BgRVHSAAMDcwNQYI +KwYBBQUHAgEWKWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0 +b3J5MA4GA1UdDwEB/wQEAwIBBjCBiQYDVR0jBIGBMH+heaR3MHUxCzAJBgNVBAYT +AlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJl +clRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg +R2xvYmFsIFJvb3SCAgGlMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVi +bGljLXRydXN0LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwDQYJKoZIhvcN +AQEFBQADgYEAkx3+i65G7MupD6vl78qyaBZo2I/6E6mvs8st50tujmkqwisQCo32 +rnO2ufsU/V9tuFC2xIrWQH7Xw8tz3MldW6+wQbU36+rcIJHENGr0ofOWnTeGl+Fx +pN19+kSElK7XCQQidg9kUTWpJA/5C9sy2sL+wbkqXHonE8qxSDpx0EM= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert3[] = { + 0x30, 0x82, 0x04, 0x15, 0x30, 0x82, 0x03, 0x7e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0x8e, 0xed, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, + 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, + 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x32, 0x30, 0x34, 0x31, 0x38, 0x31, 0x36, 0x33, 0x36, 0x31, + 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x33, 0x31, 0x36, + 0x33, 0x35, 0x31, 0x37, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69, + 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79, + 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, + 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, + 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04, + 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79, + 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e, + 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d, + 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12, + 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88, + 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7, + 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5, + 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab, + 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5, + 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f, + 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77, + 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0, + 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd, + 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0, + 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4, + 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9, + 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c, + 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c, + 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b, + 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76, + 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27, + 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x47, 0x30, + 0x82, 0x01, 0x43, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30, + 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f, + 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x37, 0x30, 0x35, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x29, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x81, 0x89, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77, + 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, + 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, + 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82, + 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e, + 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52, + 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x93, 0x1d, 0xfe, + 0x8b, 0xae, 0x46, 0xec, 0xcb, 0xa9, 0x0f, 0xab, 0xe5, 0xef, 0xca, 0xb2, + 0x68, 0x16, 0x68, 0xd8, 0x8f, 0xfa, 0x13, 0xa9, 0xaf, 0xb3, 0xcb, 0x2d, + 0xe7, 0x4b, 0x6e, 0x8e, 0x69, 0x2a, 0xc2, 0x2b, 0x10, 0x0a, 0x8d, 0xf6, + 0xae, 0x73, 0xb6, 0xb9, 0xfb, 0x14, 0xfd, 0x5f, 0x6d, 0xb8, 0x50, 0xb6, + 0xc4, 0x8a, 0xd6, 0x40, 0x7e, 0xd7, 0xc3, 0xcb, 0x73, 0xdc, 0xc9, 0x5d, + 0x5b, 0xaf, 0xb0, 0x41, 0xb5, 0x37, 0xeb, 0xea, 0xdc, 0x20, 0x91, 0xc4, + 0x34, 0x6a, 0xf4, 0xa1, 0xf3, 0x96, 0x9d, 0x37, 0x86, 0x97, 0xe1, 0x71, + 0xa4, 0xdd, 0x7d, 0xfa, 0x44, 0x84, 0x94, 0xae, 0xd7, 0x09, 0x04, 0x22, + 0x76, 0x0f, 0x64, 0x51, 0x35, 0xa9, 0x24, 0x0f, 0xf9, 0x0b, 0xdb, 0x32, + 0xda, 0xc2, 0xfe, 0xc1, 0xb9, 0x2a, 0x5c, 0x7a, 0x27, 0x13, 0xca, 0xb1, + 0x48, 0x3a, 0x71, 0xd0, 0x43, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146039 (0x23a77) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Aug 29 21:39:32 2014 GMT + Not After : May 20 21:39:32 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:54:9b:d9:58:5d:1e:2c:56:c6:d5:e8:7f:f4: + 7d:16:03:ff:d0:8b:5a:e4:8e:a7:dd:54:2e:d4:04: + c0:5d:98:9c:8d:90:0f:bc:10:65:5f:da:9a:d6:44: + 7c:c0:9f:b5:e9:4a:8c:0b:06:43:04:bb:f4:96:e2: + 26:f6:61:01:91:66:31:22:c3:34:34:5f:3f:3f:91: + 2f:44:5f:dc:c7:14:b6:03:9f:86:4b:0e:a3:ff:a0: + 80:02:83:c3:d3:1f:69:52:d6:9d:64:0f:c9:83:e7: + 1b:c4:70:ac:94:e7:c3:a4:6a:2c:bd:b8:9e:69:d8: + be:0a:8f:16:63:5a:68:71:80:7b:30:de:15:04:bf: + cc:d3:bf:3e:48:05:55:7a:b3:d7:10:0c:03:fc:9b: + fd:08:a7:8c:8c:db:a7:8e:f1:1e:63:dc:b3:01:2f: + 7f:af:57:c3:3c:48:a7:83:68:21:a7:2f:e7:a7:3f: + f0:b5:0c:fc:f5:84:d1:53:bc:0e:72:4f:60:0c:42: + b8:98:ad:19:88:57:d7:04:ec:87:bf:7e:87:4e:a3: + 21:f9:53:fd:36:98:48:8d:d6:f8:bb:48:f2:29:c8: + 64:d1:cc:54:48:53:8b:af:b7:65:1e:bf:29:33:29: + d9:29:60:48:f8:ff:91:bc:57:58:e5:35:2e:bb:69: + b6:59 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + C3:9C:F3:FC:D3:46:08:34:BB:CE:46:7F:A0:7C:5B:F3:E2:08:CB:59 + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + Signature Algorithm: sha256WithRSAEncryption + a3:58:1e:c6:43:32:ac:ac:2f:93:78:b7:ea:ae:54:40:47:2d: + 7e:78:8d:50:f6:f8:66:ac:d6:4f:73:d6:44:ef:af:0b:cc:5b: + c1:f4:4f:9a:8f:49:7e:60:af:c2:27:c7:16:f1:fb:93:81:90: + a9:7c:ef:6f:7e:6e:45:94:16:84:bd:ec:49:f1:c4:0e:f4:af: + 04:59:83:87:0f:2c:3b:97:c3:5a:12:9b:7b:04:35:7b:a3:95: + 33:08:7b:93:71:22:42:b3:a9:d9:6f:4f:81:92:fc:07:b6:79: + bc:84:4a:9d:77:09:f1:c5:89:f2:f0:b4:9c:54:aa:12:7b:0d: + ba:4f:ef:93:19:ec:ef:7d:4e:61:a3:8e:76:9c:59:cf:8c:94: + b1:84:97:f7:1a:b9:07:b8:b2:c6:4f:13:79:db:bf:4f:51:1b: + 7f:69:0d:51:2a:c1:d6:15:ff:37:51:34:65:51:f4:1e:be:38: + 6a:ec:0e:ab:bf:3d:7b:39:05:7b:f4:f3:fb:1a:a1:d0:c8:7e: + 4e:64:8d:cd:8c:61:55:90:fe:3a:ca:5d:25:0f:f8:1d:a3:4a: + 74:56:4f:1a:55:40:70:75:25:a6:33:2e:ba:4b:a5:5d:53:9a: + 0d:30:e1:8d:5f:61:2c:af:cc:ef:b0:99:a1:80:ff:0b:f2:62: + 4c:70:26:98 +-----BEGIN CERTIFICATE----- +MIIEJTCCAw2gAwIBAgIDAjp3MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTQwODI5MjEzOTMyWhcNMjIwNTIwMjEzOTMyWjBHMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXUmFwaWRTU0wg +U0hBMjU2IENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv +VJvZWF0eLFbG1eh/9H0WA//Qi1rkjqfdVC7UBMBdmJyNkA+8EGVf2prWRHzAn7Xp +SowLBkMEu/SW4ib2YQGRZjEiwzQ0Xz8/kS9EX9zHFLYDn4ZLDqP/oIACg8PTH2lS +1p1kD8mD5xvEcKyU58Okaiy9uJ5p2L4KjxZjWmhxgHsw3hUEv8zTvz5IBVV6s9cQ +DAP8m/0Ip4yM26eO8R5j3LMBL3+vV8M8SKeDaCGnL+enP/C1DPz1hNFTvA5yT2AM +QriYrRmIV9cE7Ie/fodOoyH5U/02mEiN1vi7SPIpyGTRzFRIU4uvt2UevykzKdkp +YEj4/5G8V1jlNS67abZZAgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7 +qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUw5zz/NNGCDS7zkZ/oHxb8+IIy1kwEgYD +VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig +JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF +BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARF +MEMwQQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3Ry +dXN0LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQCjWB7GQzKs +rC+TeLfqrlRARy1+eI1Q9vhmrNZPc9ZE768LzFvB9E+aj0l+YK/CJ8cW8fuTgZCp +fO9vfm5FlBaEvexJ8cQO9K8EWYOHDyw7l8NaEpt7BDV7o5UzCHuTcSJCs6nZb0+B +kvwHtnm8hEqddwnxxYny8LScVKoSew26T++TGezvfU5ho452nFnPjJSxhJf3GrkH +uLLGTxN5279PURt/aQ1RKsHWFf83UTRlUfQevjhq7A6rvz17OQV79PP7GqHQyH5O +ZI3NjGFVkP46yl0lD/gdo0p0Vk8aVUBwdSWmMy66S6VdU5oNMOGNX2Esr8zvsJmh +gP8L8mJMcCaY +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert4[] = { + 0x30, 0x82, 0x04, 0x25, 0x30, 0x82, 0x03, 0x0d, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x77, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x38, 0x32, 0x39, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32, + 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20, + 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, + 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, + 0x54, 0x9b, 0xd9, 0x58, 0x5d, 0x1e, 0x2c, 0x56, 0xc6, 0xd5, 0xe8, 0x7f, + 0xf4, 0x7d, 0x16, 0x03, 0xff, 0xd0, 0x8b, 0x5a, 0xe4, 0x8e, 0xa7, 0xdd, + 0x54, 0x2e, 0xd4, 0x04, 0xc0, 0x5d, 0x98, 0x9c, 0x8d, 0x90, 0x0f, 0xbc, + 0x10, 0x65, 0x5f, 0xda, 0x9a, 0xd6, 0x44, 0x7c, 0xc0, 0x9f, 0xb5, 0xe9, + 0x4a, 0x8c, 0x0b, 0x06, 0x43, 0x04, 0xbb, 0xf4, 0x96, 0xe2, 0x26, 0xf6, + 0x61, 0x01, 0x91, 0x66, 0x31, 0x22, 0xc3, 0x34, 0x34, 0x5f, 0x3f, 0x3f, + 0x91, 0x2f, 0x44, 0x5f, 0xdc, 0xc7, 0x14, 0xb6, 0x03, 0x9f, 0x86, 0x4b, + 0x0e, 0xa3, 0xff, 0xa0, 0x80, 0x02, 0x83, 0xc3, 0xd3, 0x1f, 0x69, 0x52, + 0xd6, 0x9d, 0x64, 0x0f, 0xc9, 0x83, 0xe7, 0x1b, 0xc4, 0x70, 0xac, 0x94, + 0xe7, 0xc3, 0xa4, 0x6a, 0x2c, 0xbd, 0xb8, 0x9e, 0x69, 0xd8, 0xbe, 0x0a, + 0x8f, 0x16, 0x63, 0x5a, 0x68, 0x71, 0x80, 0x7b, 0x30, 0xde, 0x15, 0x04, + 0xbf, 0xcc, 0xd3, 0xbf, 0x3e, 0x48, 0x05, 0x55, 0x7a, 0xb3, 0xd7, 0x10, + 0x0c, 0x03, 0xfc, 0x9b, 0xfd, 0x08, 0xa7, 0x8c, 0x8c, 0xdb, 0xa7, 0x8e, + 0xf1, 0x1e, 0x63, 0xdc, 0xb3, 0x01, 0x2f, 0x7f, 0xaf, 0x57, 0xc3, 0x3c, + 0x48, 0xa7, 0x83, 0x68, 0x21, 0xa7, 0x2f, 0xe7, 0xa7, 0x3f, 0xf0, 0xb5, + 0x0c, 0xfc, 0xf5, 0x84, 0xd1, 0x53, 0xbc, 0x0e, 0x72, 0x4f, 0x60, 0x0c, + 0x42, 0xb8, 0x98, 0xad, 0x19, 0x88, 0x57, 0xd7, 0x04, 0xec, 0x87, 0xbf, + 0x7e, 0x87, 0x4e, 0xa3, 0x21, 0xf9, 0x53, 0xfd, 0x36, 0x98, 0x48, 0x8d, + 0xd6, 0xf8, 0xbb, 0x48, 0xf2, 0x29, 0xc8, 0x64, 0xd1, 0xcc, 0x54, 0x48, + 0x53, 0x8b, 0xaf, 0xb7, 0x65, 0x1e, 0xbf, 0x29, 0x33, 0x29, 0xd9, 0x29, + 0x60, 0x48, 0xf8, 0xff, 0x91, 0xbc, 0x57, 0x58, 0xe5, 0x35, 0x2e, 0xbb, + 0x69, 0xb6, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, + 0x30, 0x82, 0x01, 0x19, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xc3, 0x9c, 0xf3, 0xfc, 0xd3, 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, 0x7f, + 0xa0, 0x7c, 0x5b, 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, + 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, + 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, + 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, + 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x58, 0x1e, 0xc6, 0x43, 0x32, 0xac, + 0xac, 0x2f, 0x93, 0x78, 0xb7, 0xea, 0xae, 0x54, 0x40, 0x47, 0x2d, 0x7e, + 0x78, 0x8d, 0x50, 0xf6, 0xf8, 0x66, 0xac, 0xd6, 0x4f, 0x73, 0xd6, 0x44, + 0xef, 0xaf, 0x0b, 0xcc, 0x5b, 0xc1, 0xf4, 0x4f, 0x9a, 0x8f, 0x49, 0x7e, + 0x60, 0xaf, 0xc2, 0x27, 0xc7, 0x16, 0xf1, 0xfb, 0x93, 0x81, 0x90, 0xa9, + 0x7c, 0xef, 0x6f, 0x7e, 0x6e, 0x45, 0x94, 0x16, 0x84, 0xbd, 0xec, 0x49, + 0xf1, 0xc4, 0x0e, 0xf4, 0xaf, 0x04, 0x59, 0x83, 0x87, 0x0f, 0x2c, 0x3b, + 0x97, 0xc3, 0x5a, 0x12, 0x9b, 0x7b, 0x04, 0x35, 0x7b, 0xa3, 0x95, 0x33, + 0x08, 0x7b, 0x93, 0x71, 0x22, 0x42, 0xb3, 0xa9, 0xd9, 0x6f, 0x4f, 0x81, + 0x92, 0xfc, 0x07, 0xb6, 0x79, 0xbc, 0x84, 0x4a, 0x9d, 0x77, 0x09, 0xf1, + 0xc5, 0x89, 0xf2, 0xf0, 0xb4, 0x9c, 0x54, 0xaa, 0x12, 0x7b, 0x0d, 0xba, + 0x4f, 0xef, 0x93, 0x19, 0xec, 0xef, 0x7d, 0x4e, 0x61, 0xa3, 0x8e, 0x76, + 0x9c, 0x59, 0xcf, 0x8c, 0x94, 0xb1, 0x84, 0x97, 0xf7, 0x1a, 0xb9, 0x07, + 0xb8, 0xb2, 0xc6, 0x4f, 0x13, 0x79, 0xdb, 0xbf, 0x4f, 0x51, 0x1b, 0x7f, + 0x69, 0x0d, 0x51, 0x2a, 0xc1, 0xd6, 0x15, 0xff, 0x37, 0x51, 0x34, 0x65, + 0x51, 0xf4, 0x1e, 0xbe, 0x38, 0x6a, 0xec, 0x0e, 0xab, 0xbf, 0x3d, 0x7b, + 0x39, 0x05, 0x7b, 0xf4, 0xf3, 0xfb, 0x1a, 0xa1, 0xd0, 0xc8, 0x7e, 0x4e, + 0x64, 0x8d, 0xcd, 0x8c, 0x61, 0x55, 0x90, 0xfe, 0x3a, 0xca, 0x5d, 0x25, + 0x0f, 0xf8, 0x1d, 0xa3, 0x4a, 0x74, 0x56, 0x4f, 0x1a, 0x55, 0x40, 0x70, + 0x75, 0x25, 0xa6, 0x33, 0x2e, 0xba, 0x4b, 0xa5, 0x5d, 0x53, 0x9a, 0x0d, + 0x30, 0xe1, 0x8d, 0x5f, 0x61, 0x2c, 0xaf, 0xcc, 0xef, 0xb0, 0x99, 0xa1, + 0x80, 0xff, 0x0b, 0xf2, 0x62, 0x4c, 0x70, 0x26, 0x98, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146040 (0x23a78) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Aug 29 22:24:58 2014 GMT + Not After : May 20 22:24:58 2022 GMT + Subject: C=US, O=GeoTrust Inc., OU=Domain Validated SSL, CN=GeoTrust DV SSL CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:df:41:94:7a:da:f7:e4:31:43:b6:ea:01:1b:5c: + ce:63:ea:fa:6d:a3:d9:6a:ee:2d:9a:75:f9:d5:9c: + 5b:bd:34:df:d8:1c:c9:6d:d8:04:88:da:6e:b5:b7: + b5:f0:30:ae:40:d6:5d:fa:c4:53:c1:d4:22:9d:04: + 4e:11:a6:95:d5:45:7c:41:05:58:e0:4c:dd:f9:ee: + 55:bd:5f:46:dc:ad:13:08:9d:2c:e4:f7:82:e6:07: + 2b:9e:0e:8c:34:a1:ce:c4:a1:e0:81:70:86:00:06: + 3f:2d:ea:7c:9b:28:ae:1b:28:8b:39:09:d3:e7:f0: + 45:a4:b1:ba:11:67:90:55:7b:8f:de:ed:38:5c:a1: + e1:e3:83:c4:c3:72:91:4f:98:ee:1c:c2:80:aa:64: + a5:3e:83:62:1c:cc:e0:9e:f8:5a:c0:13:12:7d:a2: + a7:8b:a3:e7:9f:2a:d7:9b:ca:cb:ed:97:01:9c:28: + 84:51:04:50:41:bc:b4:fc:78:e9:1b:cf:14:ea:1f: + 0f:fc:2e:01:32:8d:b6:35:cb:0a:18:3b:ec:5a:3e: + 3c:1b:d3:99:43:1e:2f:f7:bd:f3:5b:12:b9:07:5e: + ed:3e:d1:a9:87:cc:77:72:27:d4:d9:75:a2:63:4b: + 93:36:bd:e5:5c:d7:bf:5f:79:0d:b3:32:a7:0b:b2: + 63:23 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + 0B:50:EC:77:EF:2A:9B:FF:EC:03:A1:0A:FF:AD:C6:E4:2A:18:C7:3E + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + Signature Algorithm: sha256WithRSAEncryption + 33:24:d5:90:aa:29:0c:35:b9:2f:c3:c7:42:93:c0:c6:10:4b: + 03:08:76:84:10:a2:e0:e7:53:12:27:f2:0a:da:7f:3a:dc:fd: + 5c:79:5a:8f:17:74:43:53:b1:d5:d1:5d:59:b9:a6:84:64:ca: + f1:3a:0a:59:96:10:bf:a9:81:57:8b:5c:87:dc:7f:e3:e4:bb: + 05:7a:a0:32:09:13:4e:10:81:28:1f:9c:03:62:bc:f4:01:b5: + 29:83:46:07:b9:e7:b8:5d:c8:e9:d1:dd:ad:3b:f8:34:db:c1: + d1:95:a9:91:18:ed:3c:2c:37:11:4d:cc:fe:53:3e:50:43:f9: + c3:56:41:ac:53:9b:6c:05:b2:9a:e2:e0:59:57:30:32:b6:26: + 4e:13:25:cd:fa:48:70:0f:75:55:60:11:f5:3b:d5:5e:5a:3c: + 8b:5b:0f:0f:62:42:48:61:85:8b:10:f4:c1:88:bf:7f:5f:8a: + c2:d7:cd:2b:94:5c:1f:34:4a:08:af:eb:ae:89:a8:48:75:55: + 95:1d:bb:c0:9a:01:b9:f4:03:22:3e:d4:e6:52:30:0d:67:b9: + c0:91:fd:2d:4c:30:8e:bd:8c:a5:04:91:bb:a4:ab:7f:0f:d8: + 6f:f0:66:00:c9:a3:5c:f5:b0:8f:83:e6:9c:5a:e6:b6:b9:c5: + bc:be:e4:02 +-----BEGIN CERTIFICATE----- +MIIERDCCAyygAwIBAgIDAjp4MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTQwODI5MjIyNDU4WhcNMjIwNTIwMjIyNDU4WjBmMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh +bGlkYXRlZCBTU0wxIDAeBgNVBAMTF0dlb1RydXN0IERWIFNTTCBDQSAtIEc0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA30GUetr35DFDtuoBG1zOY+r6 +baPZau4tmnX51ZxbvTTf2BzJbdgEiNputbe18DCuQNZd+sRTwdQinQROEaaV1UV8 +QQVY4Ezd+e5VvV9G3K0TCJ0s5PeC5gcrng6MNKHOxKHggXCGAAY/Lep8myiuGyiL +OQnT5/BFpLG6EWeQVXuP3u04XKHh44PEw3KRT5juHMKAqmSlPoNiHMzgnvhawBMS +faKni6PnnyrXm8rL7ZcBnCiEUQRQQby0/HjpG88U6h8P/C4BMo22NcsKGDvsWj48 +G9OZQx4v973zWxK5B17tPtGph8x3cifU2XWiY0uTNr3lXNe/X3kNszKnC7JjIwID +AQABo4IBHTCCARkwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4wHQYD +VR0OBBYEFAtQ7HfvKpv/7AOhCv+txuQqGMc+MBIGA1UdEwEB/wQIMAYBAf8CAQAw +DgYDVR0PAQH/BAQDAgEGMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9nLnN5bWNi +LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAuBggrBgEFBQcBAQQiMCAwHgYIKwYBBQUH +MAGGEmh0dHA6Ly9nLnN5bWNkLmNvbTBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYw +MzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2Vz +L2NwczANBgkqhkiG9w0BAQsFAAOCAQEAMyTVkKopDDW5L8PHQpPAxhBLAwh2hBCi +4OdTEifyCtp/Otz9XHlajxd0Q1Ox1dFdWbmmhGTK8ToKWZYQv6mBV4tch9x/4+S7 +BXqgMgkTThCBKB+cA2K89AG1KYNGB7nnuF3I6dHdrTv4NNvB0ZWpkRjtPCw3EU3M +/lM+UEP5w1ZBrFObbAWymuLgWVcwMrYmThMlzfpIcA91VWAR9TvVXlo8i1sPD2JC +SGGFixD0wYi/f1+KwtfNK5RcHzRKCK/rromoSHVVlR27wJoBufQDIj7U5lIwDWe5 +wJH9LUwwjr2MpQSRu6Srfw/Yb/BmAMmjXPWwj4PmnFrmtrnFvL7kAg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert5[] = { + 0x30, 0x82, 0x04, 0x44, 0x30, 0x82, 0x03, 0x2c, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x38, 0x32, 0x39, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38, + 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x14, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, + 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x47, 0x65, + 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, + 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdf, 0x41, 0x94, 0x7a, 0xda, 0xf7, + 0xe4, 0x31, 0x43, 0xb6, 0xea, 0x01, 0x1b, 0x5c, 0xce, 0x63, 0xea, 0xfa, + 0x6d, 0xa3, 0xd9, 0x6a, 0xee, 0x2d, 0x9a, 0x75, 0xf9, 0xd5, 0x9c, 0x5b, + 0xbd, 0x34, 0xdf, 0xd8, 0x1c, 0xc9, 0x6d, 0xd8, 0x04, 0x88, 0xda, 0x6e, + 0xb5, 0xb7, 0xb5, 0xf0, 0x30, 0xae, 0x40, 0xd6, 0x5d, 0xfa, 0xc4, 0x53, + 0xc1, 0xd4, 0x22, 0x9d, 0x04, 0x4e, 0x11, 0xa6, 0x95, 0xd5, 0x45, 0x7c, + 0x41, 0x05, 0x58, 0xe0, 0x4c, 0xdd, 0xf9, 0xee, 0x55, 0xbd, 0x5f, 0x46, + 0xdc, 0xad, 0x13, 0x08, 0x9d, 0x2c, 0xe4, 0xf7, 0x82, 0xe6, 0x07, 0x2b, + 0x9e, 0x0e, 0x8c, 0x34, 0xa1, 0xce, 0xc4, 0xa1, 0xe0, 0x81, 0x70, 0x86, + 0x00, 0x06, 0x3f, 0x2d, 0xea, 0x7c, 0x9b, 0x28, 0xae, 0x1b, 0x28, 0x8b, + 0x39, 0x09, 0xd3, 0xe7, 0xf0, 0x45, 0xa4, 0xb1, 0xba, 0x11, 0x67, 0x90, + 0x55, 0x7b, 0x8f, 0xde, 0xed, 0x38, 0x5c, 0xa1, 0xe1, 0xe3, 0x83, 0xc4, + 0xc3, 0x72, 0x91, 0x4f, 0x98, 0xee, 0x1c, 0xc2, 0x80, 0xaa, 0x64, 0xa5, + 0x3e, 0x83, 0x62, 0x1c, 0xcc, 0xe0, 0x9e, 0xf8, 0x5a, 0xc0, 0x13, 0x12, + 0x7d, 0xa2, 0xa7, 0x8b, 0xa3, 0xe7, 0x9f, 0x2a, 0xd7, 0x9b, 0xca, 0xcb, + 0xed, 0x97, 0x01, 0x9c, 0x28, 0x84, 0x51, 0x04, 0x50, 0x41, 0xbc, 0xb4, + 0xfc, 0x78, 0xe9, 0x1b, 0xcf, 0x14, 0xea, 0x1f, 0x0f, 0xfc, 0x2e, 0x01, + 0x32, 0x8d, 0xb6, 0x35, 0xcb, 0x0a, 0x18, 0x3b, 0xec, 0x5a, 0x3e, 0x3c, + 0x1b, 0xd3, 0x99, 0x43, 0x1e, 0x2f, 0xf7, 0xbd, 0xf3, 0x5b, 0x12, 0xb9, + 0x07, 0x5e, 0xed, 0x3e, 0xd1, 0xa9, 0x87, 0xcc, 0x77, 0x72, 0x27, 0xd4, + 0xd9, 0x75, 0xa2, 0x63, 0x4b, 0x93, 0x36, 0xbd, 0xe5, 0x5c, 0xd7, 0xbf, + 0x5f, 0x79, 0x0d, 0xb3, 0x32, 0xa7, 0x0b, 0xb2, 0x63, 0x23, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11, + 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0x50, 0xec, 0x77, 0xef, + 0x2a, 0x9b, 0xff, 0xec, 0x03, 0xa1, 0x0a, 0xff, 0xad, 0xc6, 0xe4, 0x2a, + 0x18, 0xc7, 0x3e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e, + 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x2e, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, + 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, + 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4c, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, + 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, + 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x33, 0x24, 0xd5, 0x90, 0xaa, 0x29, 0x0c, 0x35, 0xb9, 0x2f, 0xc3, 0xc7, + 0x42, 0x93, 0xc0, 0xc6, 0x10, 0x4b, 0x03, 0x08, 0x76, 0x84, 0x10, 0xa2, + 0xe0, 0xe7, 0x53, 0x12, 0x27, 0xf2, 0x0a, 0xda, 0x7f, 0x3a, 0xdc, 0xfd, + 0x5c, 0x79, 0x5a, 0x8f, 0x17, 0x74, 0x43, 0x53, 0xb1, 0xd5, 0xd1, 0x5d, + 0x59, 0xb9, 0xa6, 0x84, 0x64, 0xca, 0xf1, 0x3a, 0x0a, 0x59, 0x96, 0x10, + 0xbf, 0xa9, 0x81, 0x57, 0x8b, 0x5c, 0x87, 0xdc, 0x7f, 0xe3, 0xe4, 0xbb, + 0x05, 0x7a, 0xa0, 0x32, 0x09, 0x13, 0x4e, 0x10, 0x81, 0x28, 0x1f, 0x9c, + 0x03, 0x62, 0xbc, 0xf4, 0x01, 0xb5, 0x29, 0x83, 0x46, 0x07, 0xb9, 0xe7, + 0xb8, 0x5d, 0xc8, 0xe9, 0xd1, 0xdd, 0xad, 0x3b, 0xf8, 0x34, 0xdb, 0xc1, + 0xd1, 0x95, 0xa9, 0x91, 0x18, 0xed, 0x3c, 0x2c, 0x37, 0x11, 0x4d, 0xcc, + 0xfe, 0x53, 0x3e, 0x50, 0x43, 0xf9, 0xc3, 0x56, 0x41, 0xac, 0x53, 0x9b, + 0x6c, 0x05, 0xb2, 0x9a, 0xe2, 0xe0, 0x59, 0x57, 0x30, 0x32, 0xb6, 0x26, + 0x4e, 0x13, 0x25, 0xcd, 0xfa, 0x48, 0x70, 0x0f, 0x75, 0x55, 0x60, 0x11, + 0xf5, 0x3b, 0xd5, 0x5e, 0x5a, 0x3c, 0x8b, 0x5b, 0x0f, 0x0f, 0x62, 0x42, + 0x48, 0x61, 0x85, 0x8b, 0x10, 0xf4, 0xc1, 0x88, 0xbf, 0x7f, 0x5f, 0x8a, + 0xc2, 0xd7, 0xcd, 0x2b, 0x94, 0x5c, 0x1f, 0x34, 0x4a, 0x08, 0xaf, 0xeb, + 0xae, 0x89, 0xa8, 0x48, 0x75, 0x55, 0x95, 0x1d, 0xbb, 0xc0, 0x9a, 0x01, + 0xb9, 0xf4, 0x03, 0x22, 0x3e, 0xd4, 0xe6, 0x52, 0x30, 0x0d, 0x67, 0xb9, + 0xc0, 0x91, 0xfd, 0x2d, 0x4c, 0x30, 0x8e, 0xbd, 0x8c, 0xa5, 0x04, 0x91, + 0xbb, 0xa4, 0xab, 0x7f, 0x0f, 0xd8, 0x6f, 0xf0, 0x66, 0x00, 0xc9, 0xa3, + 0x5c, 0xf5, 0xb0, 0x8f, 0x83, 0xe6, 0x9c, 0x5a, 0xe6, 0xb6, 0xb9, 0xc5, + 0xbc, 0xbe, 0xe4, 0x02, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 33:65:50:08:79:ad:73:e2:30:b9:e0:1d:0d:7f:ac:91 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com + Validity + Not Before: Nov 17 00:00:00 2006 GMT + Not After : Dec 30 23:59:59 2020 GMT + Subject: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ac:a0:f0:fb:80:59:d4:9c:c7:a4:cf:9d:a1:59: + 73:09:10:45:0c:0d:2c:6e:68:f1:6c:5b:48:68:49: + 59:37:fc:0b:33:19:c2:77:7f:cc:10:2d:95:34:1c: + e6:eb:4d:09:a7:1c:d2:b8:c9:97:36:02:b7:89:d4: + 24:5f:06:c0:cc:44:94:94:8d:02:62:6f:eb:5a:dd: + 11:8d:28:9a:5c:84:90:10:7a:0d:bd:74:66:2f:6a: + 38:a0:e2:d5:54:44:eb:1d:07:9f:07:ba:6f:ee:e9: + fd:4e:0b:29:f5:3e:84:a0:01:f1:9c:ab:f8:1c:7e: + 89:a4:e8:a1:d8:71:65:0d:a3:51:7b:ee:bc:d2:22: + 60:0d:b9:5b:9d:df:ba:fc:51:5b:0b:af:98:b2:e9: + 2e:e9:04:e8:62:87:de:2b:c8:d7:4e:c1:4c:64:1e: + dd:cf:87:58:ba:4a:4f:ca:68:07:1d:1c:9d:4a:c6: + d5:2f:91:cc:7c:71:72:1c:c5:c0:67:eb:32:fd:c9: + 92:5c:94:da:85:c0:9b:bf:53:7d:2b:09:f4:8c:9d: + 91:1f:97:6a:52:cb:de:09:36:a4:77:d8:7b:87:50: + 44:d5:3e:6e:29:69:fb:39:49:26:1e:09:a5:80:7b: + 40:2d:eb:e8:27:85:c9:fe:61:fd:7e:e6:7c:97:1d: + d5:9d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.thawte.com/cps + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.thawte.com/ThawtePremiumServerCA.crl + + Signature Algorithm: sha1WithRSAEncryption + 84:a8:4c:c9:3e:2a:bc:9a:e2:cc:8f:0b:b2:25:77:c4:61:89: + 89:63:5a:d4:a3:15:40:d4:fb:5e:3f:b4:43:ea:63:17:2b:6b: + 99:74:9e:09:a8:dd:d4:56:15:2e:7a:79:31:5f:63:96:53:1b: + 34:d9:15:ea:4f:6d:70:ca:be:f6:82:a9:ed:da:85:77:cc:76: + 1c:6a:81:0a:21:d8:41:99:7f:5e:2e:82:c1:e8:aa:f7:93:81: + 05:aa:92:b4:1f:b7:9a:c0:07:17:f5:cb:c6:b4:4c:0e:d7:56: + dc:71:20:74:38:d6:74:c6:d6:8f:6b:af:8b:8d:a0:6c:29:0b: + 61:e0 +-----BEGIN CERTIFICATE----- +MIIERTCCA66gAwIBAgIQM2VQCHmtc+IwueAdDX+skTANBgkqhkiG9w0BAQUFADCB +zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ +Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE +CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh +d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl +cnZlckB0aGF3dGUuY29tMB4XDTA2MTExNzAwMDAwMFoXDTIwMTIzMDIzNTk1OVow +gakxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xKDAmBgNVBAsT +H0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xODA2BgNVBAsTLyhjKSAy +MDA2IHRoYXd0ZSwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYD +VQQDExZ0aGF3dGUgUHJpbWFyeSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEArKDw+4BZ1JzHpM+doVlzCRBFDA0sbmjxbFtIaElZN/wLMxnC +d3/MEC2VNBzm600JpxzSuMmXNgK3idQkXwbAzESUlI0CYm/rWt0RjSiaXISQEHoN +vXRmL2o4oOLVVETrHQefB7pv7un9Tgsp9T6EoAHxnKv4HH6JpOih2HFlDaNRe+68 +0iJgDblbnd+6/FFbC6+Ysuku6QToYofeK8jXTsFMZB7dz4dYukpPymgHHRydSsbV +L5HMfHFyHMXAZ+sy/cmSXJTahcCbv1N9Kwn0jJ2RH5dqUsveCTakd9h7h1BE1T5u +KWn7OUkmHgmlgHtALevoJ4XJ/mH9fuZ8lx3VnQIDAQABo4HCMIG/MA8GA1UdEwEB +/wQFMAMBAf8wOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBz +Oi8vd3d3LnRoYXd0ZS5jb20vY3BzMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +e1tFz6/Oy3r9MZIaarbzRutXSFAwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL2Ny +bC50aGF3dGUuY29tL1RoYXd0ZVByZW1pdW1TZXJ2ZXJDQS5jcmwwDQYJKoZIhvcN +AQEFBQADgYEAhKhMyT4qvJrizI8LsiV3xGGJiWNa1KMVQNT7Xj+0Q+pjFytrmXSe +Cajd1FYVLnp5MV9jllMbNNkV6k9tcMq+9oKp7dqFd8x2HGqBCiHYQZl/Xi6Cweiq +95OBBaqStB+3msAHF/XLxrRMDtdW3HEgdDjWdMbWj2uvi42gbCkLYeA= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert6[] = { + 0x30, 0x82, 0x04, 0x45, 0x30, 0x82, 0x03, 0xae, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x33, 0x65, 0x50, 0x08, 0x79, 0xad, 0x73, 0xe2, 0x30, + 0xb9, 0xe0, 0x1d, 0x0d, 0x7f, 0xac, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xce, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x5a, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0c, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x43, 0x61, 0x70, + 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, + 0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31, 0x1d, 0x30, + 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77, + 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61, + 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30, + 0x26, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, + 0x16, 0x19, 0x70, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31, + 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, + 0x31, 0x32, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, + 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, + 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, + 0x30, 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, + 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, + 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, + 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xac, 0xa0, 0xf0, 0xfb, 0x80, 0x59, 0xd4, 0x9c, 0xc7, 0xa4, 0xcf, 0x9d, + 0xa1, 0x59, 0x73, 0x09, 0x10, 0x45, 0x0c, 0x0d, 0x2c, 0x6e, 0x68, 0xf1, + 0x6c, 0x5b, 0x48, 0x68, 0x49, 0x59, 0x37, 0xfc, 0x0b, 0x33, 0x19, 0xc2, + 0x77, 0x7f, 0xcc, 0x10, 0x2d, 0x95, 0x34, 0x1c, 0xe6, 0xeb, 0x4d, 0x09, + 0xa7, 0x1c, 0xd2, 0xb8, 0xc9, 0x97, 0x36, 0x02, 0xb7, 0x89, 0xd4, 0x24, + 0x5f, 0x06, 0xc0, 0xcc, 0x44, 0x94, 0x94, 0x8d, 0x02, 0x62, 0x6f, 0xeb, + 0x5a, 0xdd, 0x11, 0x8d, 0x28, 0x9a, 0x5c, 0x84, 0x90, 0x10, 0x7a, 0x0d, + 0xbd, 0x74, 0x66, 0x2f, 0x6a, 0x38, 0xa0, 0xe2, 0xd5, 0x54, 0x44, 0xeb, + 0x1d, 0x07, 0x9f, 0x07, 0xba, 0x6f, 0xee, 0xe9, 0xfd, 0x4e, 0x0b, 0x29, + 0xf5, 0x3e, 0x84, 0xa0, 0x01, 0xf1, 0x9c, 0xab, 0xf8, 0x1c, 0x7e, 0x89, + 0xa4, 0xe8, 0xa1, 0xd8, 0x71, 0x65, 0x0d, 0xa3, 0x51, 0x7b, 0xee, 0xbc, + 0xd2, 0x22, 0x60, 0x0d, 0xb9, 0x5b, 0x9d, 0xdf, 0xba, 0xfc, 0x51, 0x5b, + 0x0b, 0xaf, 0x98, 0xb2, 0xe9, 0x2e, 0xe9, 0x04, 0xe8, 0x62, 0x87, 0xde, + 0x2b, 0xc8, 0xd7, 0x4e, 0xc1, 0x4c, 0x64, 0x1e, 0xdd, 0xcf, 0x87, 0x58, + 0xba, 0x4a, 0x4f, 0xca, 0x68, 0x07, 0x1d, 0x1c, 0x9d, 0x4a, 0xc6, 0xd5, + 0x2f, 0x91, 0xcc, 0x7c, 0x71, 0x72, 0x1c, 0xc5, 0xc0, 0x67, 0xeb, 0x32, + 0xfd, 0xc9, 0x92, 0x5c, 0x94, 0xda, 0x85, 0xc0, 0x9b, 0xbf, 0x53, 0x7d, + 0x2b, 0x09, 0xf4, 0x8c, 0x9d, 0x91, 0x1f, 0x97, 0x6a, 0x52, 0xcb, 0xde, + 0x09, 0x36, 0xa4, 0x77, 0xd8, 0x7b, 0x87, 0x50, 0x44, 0xd5, 0x3e, 0x6e, + 0x29, 0x69, 0xfb, 0x39, 0x49, 0x26, 0x1e, 0x09, 0xa5, 0x80, 0x7b, 0x40, + 0x2d, 0xeb, 0xe8, 0x27, 0x85, 0xc9, 0xfe, 0x61, 0xfd, 0x7e, 0xe6, 0x7c, + 0x97, 0x1d, 0xd5, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xc2, + 0x30, 0x81, 0xbf, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3b, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, + 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, + 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x40, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xa0, 0x33, 0xa0, + 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65, 0x6d, 0x69, + 0x75, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x84, 0xa8, 0x4c, + 0xc9, 0x3e, 0x2a, 0xbc, 0x9a, 0xe2, 0xcc, 0x8f, 0x0b, 0xb2, 0x25, 0x77, + 0xc4, 0x61, 0x89, 0x89, 0x63, 0x5a, 0xd4, 0xa3, 0x15, 0x40, 0xd4, 0xfb, + 0x5e, 0x3f, 0xb4, 0x43, 0xea, 0x63, 0x17, 0x2b, 0x6b, 0x99, 0x74, 0x9e, + 0x09, 0xa8, 0xdd, 0xd4, 0x56, 0x15, 0x2e, 0x7a, 0x79, 0x31, 0x5f, 0x63, + 0x96, 0x53, 0x1b, 0x34, 0xd9, 0x15, 0xea, 0x4f, 0x6d, 0x70, 0xca, 0xbe, + 0xf6, 0x82, 0xa9, 0xed, 0xda, 0x85, 0x77, 0xcc, 0x76, 0x1c, 0x6a, 0x81, + 0x0a, 0x21, 0xd8, 0x41, 0x99, 0x7f, 0x5e, 0x2e, 0x82, 0xc1, 0xe8, 0xaa, + 0xf7, 0x93, 0x81, 0x05, 0xaa, 0x92, 0xb4, 0x1f, 0xb7, 0x9a, 0xc0, 0x07, + 0x17, 0xf5, 0xcb, 0xc6, 0xb4, 0x4c, 0x0e, 0xd7, 0x56, 0xdc, 0x71, 0x20, + 0x74, 0x38, 0xd6, 0x74, 0xc6, 0xd6, 0x8f, 0x6b, 0xaf, 0x8b, 0x8d, 0xa0, + 0x6c, 0x29, 0x0b, 0x61, 0xe0, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 06:7f:94:57:85:87:e8:ac:77:de:b2:53:32:5b:bc:99:8b:56:0d + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Amazon, CN=Amazon Root CA 1 + Validity + Not Before: Oct 22 00:00:00 2015 GMT + Not After : Oct 19 00:00:00 2025 GMT + Subject: C=US, O=Amazon, OU=Server CA 1B, CN=Amazon + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c2:4e:16:67:dd:ce:bc:6a:c8:37:5a:ec:3a:30: + b0:1d:e6:d1:12:e8:12:28:48:cc:e8:29:c1:b9:6e: + 53:d5:a3:eb:03:39:1a:cc:77:87:f6:01:b9:d9:70: + cc:cf:6b:8d:e3:e3:03:71:86:99:6d:cb:a6:94:2a: + 4e:13:d6:a7:bd:04:ec:0a:16:3c:0a:eb:39:b1:c4: + b5:58:a3:b6:c7:56:25:ec:3e:52:7a:a8:e3:29:16: + 07:b9:6e:50:cf:fb:5f:31:f8:1d:ba:03:4a:62:89: + 03:ae:3e:47:f2:0f:27:91:e3:14:20:85:f8:fa:e9: + 8a:35:f5:5f:9e:99:4d:e7:6b:37:ef:a4:50:3e:44: + ec:fa:5a:85:66:07:9c:7e:17:6a:55:f3:17:8a:35: + 1e:ee:e9:ac:c3:75:4e:58:55:7d:53:6b:0a:6b:9b: + 14:42:d7:e5:ac:01:89:b3:ea:a3:fe:cf:c0:2b:0c: + 84:c2:d8:53:15:cb:67:f0:d0:88:ca:3a:d1:17:73: + f5:5f:9a:d4:c5:72:1e:7e:01:f1:98:30:63:2a:aa: + f2:7a:2d:c5:e2:02:1a:86:e5:32:3e:0e:bd:11:b4: + cf:3c:93:ef:17:50:10:9e:43:c2:06:2a:e0:0d:68: + be:d3:88:8b:4a:65:8c:4a:d4:c3:2e:4c:9b:55:f4: + 86:e5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 59:A4:66:06:52:A0:7B:95:92:3C:A3:94:07:27:96:74:5B:F9:3D:D0 + X509v3 Authority Key Identifier: + keyid:84:18:CC:85:34:EC:BC:0C:94:94:2E:08:59:9C:C7:B2:10:4E:0A:08 + + Authority Information Access: + OCSP - URI:http://ocsp.rootca1.amazontrust.com + CA Issuers - URI:http://crt.rootca1.amazontrust.com/rootca1.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.rootca1.amazontrust.com/rootca1.crl + + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 + + Signature Algorithm: sha256WithRSAEncryption + 85:92:be:35:bb:79:cf:a3:81:42:1c:e4:e3:63:73:53:39:52: + 35:e7:d1:ad:fd:ae:99:8a:ac:89:12:2f:bb:e7:6f:9a:d5:4e: + 72:ea:20:30:61:f9:97:b2:cd:a5:27:02:45:a8:ca:76:3e:98: + 4a:83:9e:b6:e6:45:e0:f2:43:f6:08:de:6d:e8:6e:db:31:07: + 13:f0:2f:31:0d:93:6d:61:37:7b:58:f0:fc:51:98:91:28:02: + 4f:05:76:b7:d3:f0:1b:c2:e6:5e:d0:66:85:11:0f:2e:81:c6: + 10:81:29:fe:20:60:48:f3:f2:f0:84:13:53:65:35:15:11:6b: + 82:51:40:55:57:5f:18:b5:b0:22:3e:ad:f2:5e:a3:01:e3:c3: + b3:f9:cb:41:5a:e6:52:91:bb:e4:36:87:4f:2d:a9:a4:07:68: + 35:ba:94:72:cd:0e:ea:0e:7d:57:f2:79:fc:37:c5:7b:60:9e: + b2:eb:c0:2d:90:77:0d:49:10:27:a5:38:ad:c4:12:a3:b4:a3: + c8:48:b3:15:0b:1e:e2:e2:19:dc:c4:76:52:c8:bc:8a:41:78: + 70:d9:6d:97:b3:4a:8b:78:2d:5e:b4:0f:a3:4c:60:ca:e1:47: + cb:78:2d:12:17:b1:52:8b:ca:39:2c:bd:b5:2f:c2:33:02:96: + ab:da:94:7f +-----BEGIN CERTIFICATE----- +MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MTAyMjAwMDAwMFoXDTI1MTAxOTAwMDAwMFowRjEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEVMBMGA1UECxMMU2VydmVyIENB +IDFCMQ8wDQYDVQQDEwZBbWF6b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDCThZn3c68asg3Wuw6MLAd5tES6BIoSMzoKcG5blPVo+sDORrMd4f2AbnZ +cMzPa43j4wNxhplty6aUKk4T1qe9BOwKFjwK6zmxxLVYo7bHViXsPlJ6qOMpFge5 +blDP+18x+B26A0piiQOuPkfyDyeR4xQghfj66Yo19V+emU3nazfvpFA+ROz6WoVm +B5x+F2pV8xeKNR7u6azDdU5YVX1TawprmxRC1+WsAYmz6qP+z8ArDITC2FMVy2fw +0IjKOtEXc/VfmtTFch5+AfGYMGMqqvJ6LcXiAhqG5TI+Dr0RtM88k+8XUBCeQ8IG +KuANaL7TiItKZYxK1MMuTJtV9IblAgMBAAGjggE7MIIBNzASBgNVHRMBAf8ECDAG +AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUWaRmBlKge5WSPKOUByeW +dFv5PdAwHwYDVR0jBBgwFoAUhBjMhTTsvAyUlC4IWZzHshBOCggwewYIKwYBBQUH +AQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5yb290Y2ExLmFtYXpvbnRy +dXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDovL2NydC5yb290Y2ExLmFtYXpvbnRy +dXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3Js +LnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jvb3RjYTEuY3JsMBMGA1UdIAQMMAow +CAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IBAQCFkr41u3nPo4FCHOTjY3NTOVI1 +59Gt/a6ZiqyJEi+752+a1U5y6iAwYfmXss2lJwJFqMp2PphKg5625kXg8kP2CN5t +6G7bMQcT8C8xDZNtYTd7WPD8UZiRKAJPBXa30/AbwuZe0GaFEQ8ugcYQgSn+IGBI +8/LwhBNTZTUVEWuCUUBVV18YtbAiPq3yXqMB48Oz+ctBWuZSkbvkNodPLamkB2g1 +upRyzQ7qDn1X8nn8N8V7YJ6y68AtkHcNSRAnpTitxBKjtKPISLMVCx7i4hncxHZS +yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/ +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert7[] = { + 0x30, 0x82, 0x04, 0x49, 0x30, 0x82, 0x03, 0x31, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x13, 0x06, 0x7f, 0x94, 0x57, 0x85, 0x87, 0xe8, 0xac, 0x77, + 0xde, 0xb2, 0x53, 0x32, 0x5b, 0xbc, 0x99, 0x8b, 0x56, 0x0d, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x39, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x06, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x19, 0x30, + 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6d, 0x61, 0x7a, + 0x6f, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x31, 0x30, 0x32, 0x32, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x31, 0x30, 0x31, + 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x46, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d, + 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, + 0x20, 0x31, 0x42, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x06, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xc2, 0x4e, 0x16, 0x67, 0xdd, 0xce, 0xbc, + 0x6a, 0xc8, 0x37, 0x5a, 0xec, 0x3a, 0x30, 0xb0, 0x1d, 0xe6, 0xd1, 0x12, + 0xe8, 0x12, 0x28, 0x48, 0xcc, 0xe8, 0x29, 0xc1, 0xb9, 0x6e, 0x53, 0xd5, + 0xa3, 0xeb, 0x03, 0x39, 0x1a, 0xcc, 0x77, 0x87, 0xf6, 0x01, 0xb9, 0xd9, + 0x70, 0xcc, 0xcf, 0x6b, 0x8d, 0xe3, 0xe3, 0x03, 0x71, 0x86, 0x99, 0x6d, + 0xcb, 0xa6, 0x94, 0x2a, 0x4e, 0x13, 0xd6, 0xa7, 0xbd, 0x04, 0xec, 0x0a, + 0x16, 0x3c, 0x0a, 0xeb, 0x39, 0xb1, 0xc4, 0xb5, 0x58, 0xa3, 0xb6, 0xc7, + 0x56, 0x25, 0xec, 0x3e, 0x52, 0x7a, 0xa8, 0xe3, 0x29, 0x16, 0x07, 0xb9, + 0x6e, 0x50, 0xcf, 0xfb, 0x5f, 0x31, 0xf8, 0x1d, 0xba, 0x03, 0x4a, 0x62, + 0x89, 0x03, 0xae, 0x3e, 0x47, 0xf2, 0x0f, 0x27, 0x91, 0xe3, 0x14, 0x20, + 0x85, 0xf8, 0xfa, 0xe9, 0x8a, 0x35, 0xf5, 0x5f, 0x9e, 0x99, 0x4d, 0xe7, + 0x6b, 0x37, 0xef, 0xa4, 0x50, 0x3e, 0x44, 0xec, 0xfa, 0x5a, 0x85, 0x66, + 0x07, 0x9c, 0x7e, 0x17, 0x6a, 0x55, 0xf3, 0x17, 0x8a, 0x35, 0x1e, 0xee, + 0xe9, 0xac, 0xc3, 0x75, 0x4e, 0x58, 0x55, 0x7d, 0x53, 0x6b, 0x0a, 0x6b, + 0x9b, 0x14, 0x42, 0xd7, 0xe5, 0xac, 0x01, 0x89, 0xb3, 0xea, 0xa3, 0xfe, + 0xcf, 0xc0, 0x2b, 0x0c, 0x84, 0xc2, 0xd8, 0x53, 0x15, 0xcb, 0x67, 0xf0, + 0xd0, 0x88, 0xca, 0x3a, 0xd1, 0x17, 0x73, 0xf5, 0x5f, 0x9a, 0xd4, 0xc5, + 0x72, 0x1e, 0x7e, 0x01, 0xf1, 0x98, 0x30, 0x63, 0x2a, 0xaa, 0xf2, 0x7a, + 0x2d, 0xc5, 0xe2, 0x02, 0x1a, 0x86, 0xe5, 0x32, 0x3e, 0x0e, 0xbd, 0x11, + 0xb4, 0xcf, 0x3c, 0x93, 0xef, 0x17, 0x50, 0x10, 0x9e, 0x43, 0xc2, 0x06, + 0x2a, 0xe0, 0x0d, 0x68, 0xbe, 0xd3, 0x88, 0x8b, 0x4a, 0x65, 0x8c, 0x4a, + 0xd4, 0xc3, 0x2e, 0x4c, 0x9b, 0x55, 0xf4, 0x86, 0xe5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x3b, 0x30, 0x82, 0x01, 0x37, 0x30, 0x12, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x59, 0xa4, 0x66, + 0x06, 0x52, 0xa0, 0x7b, 0x95, 0x92, 0x3c, 0xa3, 0x94, 0x07, 0x27, 0x96, + 0x74, 0x5b, 0xf9, 0x3d, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, + 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x84, 0x18, 0xcc, 0x85, 0x34, 0xec, + 0xbc, 0x0c, 0x94, 0x94, 0x2e, 0x08, 0x59, 0x9c, 0xc7, 0xb2, 0x10, 0x4e, + 0x0a, 0x08, 0x30, 0x7b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x04, 0x6f, 0x30, 0x6d, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x74, + 0x63, 0x61, 0x31, 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3a, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2e, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x72, 0x6f, 0x6f, 0x74, + 0x63, 0x61, 0x31, 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, + 0x63, 0x61, 0x31, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x3f, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x38, 0x30, 0x36, 0x30, 0x34, 0xa0, 0x32, 0xa0, 0x30, + 0x86, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x31, 0x2e, 0x61, 0x6d, 0x61, + 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0c, 0x30, 0x0a, 0x30, + 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x01, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x85, 0x92, 0xbe, 0x35, 0xbb, 0x79, 0xcf, + 0xa3, 0x81, 0x42, 0x1c, 0xe4, 0xe3, 0x63, 0x73, 0x53, 0x39, 0x52, 0x35, + 0xe7, 0xd1, 0xad, 0xfd, 0xae, 0x99, 0x8a, 0xac, 0x89, 0x12, 0x2f, 0xbb, + 0xe7, 0x6f, 0x9a, 0xd5, 0x4e, 0x72, 0xea, 0x20, 0x30, 0x61, 0xf9, 0x97, + 0xb2, 0xcd, 0xa5, 0x27, 0x02, 0x45, 0xa8, 0xca, 0x76, 0x3e, 0x98, 0x4a, + 0x83, 0x9e, 0xb6, 0xe6, 0x45, 0xe0, 0xf2, 0x43, 0xf6, 0x08, 0xde, 0x6d, + 0xe8, 0x6e, 0xdb, 0x31, 0x07, 0x13, 0xf0, 0x2f, 0x31, 0x0d, 0x93, 0x6d, + 0x61, 0x37, 0x7b, 0x58, 0xf0, 0xfc, 0x51, 0x98, 0x91, 0x28, 0x02, 0x4f, + 0x05, 0x76, 0xb7, 0xd3, 0xf0, 0x1b, 0xc2, 0xe6, 0x5e, 0xd0, 0x66, 0x85, + 0x11, 0x0f, 0x2e, 0x81, 0xc6, 0x10, 0x81, 0x29, 0xfe, 0x20, 0x60, 0x48, + 0xf3, 0xf2, 0xf0, 0x84, 0x13, 0x53, 0x65, 0x35, 0x15, 0x11, 0x6b, 0x82, + 0x51, 0x40, 0x55, 0x57, 0x5f, 0x18, 0xb5, 0xb0, 0x22, 0x3e, 0xad, 0xf2, + 0x5e, 0xa3, 0x01, 0xe3, 0xc3, 0xb3, 0xf9, 0xcb, 0x41, 0x5a, 0xe6, 0x52, + 0x91, 0xbb, 0xe4, 0x36, 0x87, 0x4f, 0x2d, 0xa9, 0xa4, 0x07, 0x68, 0x35, + 0xba, 0x94, 0x72, 0xcd, 0x0e, 0xea, 0x0e, 0x7d, 0x57, 0xf2, 0x79, 0xfc, + 0x37, 0xc5, 0x7b, 0x60, 0x9e, 0xb2, 0xeb, 0xc0, 0x2d, 0x90, 0x77, 0x0d, + 0x49, 0x10, 0x27, 0xa5, 0x38, 0xad, 0xc4, 0x12, 0xa3, 0xb4, 0xa3, 0xc8, + 0x48, 0xb3, 0x15, 0x0b, 0x1e, 0xe2, 0xe2, 0x19, 0xdc, 0xc4, 0x76, 0x52, + 0xc8, 0xbc, 0x8a, 0x41, 0x78, 0x70, 0xd9, 0x6d, 0x97, 0xb3, 0x4a, 0x8b, + 0x78, 0x2d, 0x5e, 0xb4, 0x0f, 0xa3, 0x4c, 0x60, 0xca, 0xe1, 0x47, 0xcb, + 0x78, 0x2d, 0x12, 0x17, 0xb1, 0x52, 0x8b, 0xca, 0x39, 0x2c, 0xbd, 0xb5, + 0x2f, 0xc2, 0x33, 0x02, 0x96, 0xab, 0xda, 0x94, 0x7f, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146033 (0x23a71) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Dec 11 23:45:51 2013 GMT + Not After : May 20 23:45:51 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bb:58:c1:12:01:2e:97:d8:7d:18:aa:c8:c2:e5: + 85:e2:17:6c:60:2e:c9:8d:31:05:39:1a:06:98:56: + dd:54:d7:11:8c:59:5b:3d:b1:54:ae:4b:21:85:32: + 16:5f:54:86:e6:d9:b1:d8:60:89:6b:58:be:72:da: + a0:00:42:76:b1:27:59:4c:cd:e3:ba:d4:5c:d9:a6: + 7f:bb:2b:75:d5:46:44:bd:ec:40:5c:59:b7:dd:59: + 9f:f1:6a:f7:06:fc:d6:2f:19:8a:95:12:ba:9a:ca: + d5:30:d2:38:fc:19:3b:5b:15:3b:36:d0:43:4d:d1: + 65:a1:d4:8b:c1:60:41:b3:d6:70:17:cc:39:c0:9c: + 0c:a0:3d:b7:11:22:4e:ce:d9:a9:7a:d2:2a:62:9c: + a0:0b:4e:2a:d7:c3:61:5a:85:dd:5c:10:b9:54:3d: + 2d:03:f8:49:f0:bc:92:b7:b7:9c:31:c7:e9:b8:aa: + 82:0b:05:b9:31:cd:08:5b:bb:22:0b:f6:9c:8e:8a: + 55:1c:76:43:76:f0:e2:6e:f0:df:a8:29:75:e7:c8: + a4:87:8b:6a:f1:bb:08:c9:36:18:65:ee:50:43:b8: + 5d:72:d5:28:39:e1:53:3e:25:2c:da:2b:4f:dd:8a: + 9e:50:50:e0:6f:9a:c4:d5:19:26:89:01:75:73:09: + 9b:3b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + 97:C2:27:50:9E:C2:C9:EC:0C:88:32:C8:7C:AD:E2:A6:01:4F:DA:6F + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g1.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g2.symcb.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-569 + Signature Algorithm: sha256WithRSAEncryption + 35:eb:e1:8b:20:56:94:ba:7a:bd:79:a9:f6:e3:fe:6e:38:b4: + 32:c1:a3:db:58:56:20:3e:7d:c7:3a:b1:67:69:d5:79:14:1b: + f6:fa:ec:60:f2:79:cd:0a:0c:60:8a:74:4c:a3:93:2a:a0:f0: + 51:7f:cd:e9:f9:92:fd:96:ab:45:f5:62:3d:3f:60:46:50:13: + 3d:20:13:18:2e:94:46:ae:d5:21:fe:43:a1:c9:23:fe:53:c4: + bf:1a:d8:ac:3a:ca:de:66:97:23:ae:d3:df:4a:4d:73:1f:6f: + 31:a2:51:04:16:6a:00:eb:f9:8d:43:81:f0:50:a1:1f:a6:ca: + 3a:f3:28:3c:5f:51:ac:d7:0a:45:77:4b:0e:52:62:1b:d8:38: + 51:a0:92:2d:3f:90:6e:c8:7e:40:9f:20:46:15:5d:e0:50:7c: + e1:76:af:5e:ed:11:d3:2f:13:b9:b8:25:a4:af:58:09:af:35: + b4:62:54:85:e3:48:de:bc:d2:90:7a:7a:a4:84:0d:a3:42:f2: + 51:c0:d4:ad:53:65:5d:6c:f8:3f:1f:06:f2:4f:cb:97:a0:4a: + 59:c6:78:d1:e8:03:b9:85:6d:2c:ba:e1:5f:b6:ad:2b:3e:25: + 79:c5:8b:56:d5:e3:09:80:ea:c1:27:c2:d9:0e:ec:47:0a:e9: + d0:ca:fc:d8 +-----BEGIN CERTIFICATE----- +MIIETTCCAzWgAwIBAgIDAjpxMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTMxMjExMjM0NTUxWhcNMjIwNTIwMjM0NTUxWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSUmFwaWRTU0wg +U0hBMjU2IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1jBEgEu +l9h9GKrIwuWF4hdsYC7JjTEFORoGmFbdVNcRjFlbPbFUrkshhTIWX1SG5tmx2GCJ +a1i+ctqgAEJ2sSdZTM3jutRc2aZ/uyt11UZEvexAXFm33Vmf8Wr3BvzWLxmKlRK6 +msrVMNI4/Bk7WxU7NtBDTdFlodSLwWBBs9ZwF8w5wJwMoD23ESJOztmpetIqYpyg +C04q18NhWoXdXBC5VD0tA/hJ8LySt7ecMcfpuKqCCwW5Mc0IW7siC/acjopVHHZD +dvDibvDfqCl158ikh4tq8bsIyTYYZe5QQ7hdctUoOeFTPiUs2itP3YqeUFDgb5rE +1RkmiQF1cwmbOwIDAQABo4IBSjCCAUYwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwR +fap9ZbjKzE4wHQYDVR0OBBYEFJfCJ1CewsnsDIgyyHyt4qYBT9pvMBIGA1UdEwEB +/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDYGA1UdHwQvMC0wK6ApoCeGJWh0 +dHA6Ly9nMS5zeW1jYi5jb20vY3Jscy9ndGdsb2JhbC5jcmwwLwYIKwYBBQUHAQEE +IzAhMB8GCCsGAQUFBzABhhNodHRwOi8vZzIuc3ltY2IuY29tMEwGA1UdIARFMEMw +QQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0 +LmNvbS9yZXNvdXJjZXMvY3BzMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1h +bnRlY1BLSS0xLTU2OTANBgkqhkiG9w0BAQsFAAOCAQEANevhiyBWlLp6vXmp9uP+ +bji0MsGj21hWID59xzqxZ2nVeRQb9vrsYPJ5zQoMYIp0TKOTKqDwUX/N6fmS/Zar +RfViPT9gRlATPSATGC6URq7VIf5Dockj/lPEvxrYrDrK3maXI67T30pNcx9vMaJR +BBZqAOv5jUOB8FChH6bKOvMoPF9RrNcKRXdLDlJiG9g4UaCSLT+Qbsh+QJ8gRhVd +4FB84XavXu0R0y8TubglpK9YCa81tGJUheNI3rzSkHp6pIQNo0LyUcDUrVNlXWz4 +Px8G8k/Ll6BKWcZ40egDuYVtLLrhX7atKz4lecWLVtXjCYDqwSfC2Q7sRwrp0Mr8 +2A== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert8[] = { + 0x30, 0x82, 0x04, 0x4d, 0x30, 0x82, 0x03, 0x35, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, + 0x32, 0x31, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x31, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x33, 0x34, 0x35, 0x35, 0x31, + 0x5a, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x12, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20, + 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbb, 0x58, 0xc1, 0x12, 0x01, 0x2e, + 0x97, 0xd8, 0x7d, 0x18, 0xaa, 0xc8, 0xc2, 0xe5, 0x85, 0xe2, 0x17, 0x6c, + 0x60, 0x2e, 0xc9, 0x8d, 0x31, 0x05, 0x39, 0x1a, 0x06, 0x98, 0x56, 0xdd, + 0x54, 0xd7, 0x11, 0x8c, 0x59, 0x5b, 0x3d, 0xb1, 0x54, 0xae, 0x4b, 0x21, + 0x85, 0x32, 0x16, 0x5f, 0x54, 0x86, 0xe6, 0xd9, 0xb1, 0xd8, 0x60, 0x89, + 0x6b, 0x58, 0xbe, 0x72, 0xda, 0xa0, 0x00, 0x42, 0x76, 0xb1, 0x27, 0x59, + 0x4c, 0xcd, 0xe3, 0xba, 0xd4, 0x5c, 0xd9, 0xa6, 0x7f, 0xbb, 0x2b, 0x75, + 0xd5, 0x46, 0x44, 0xbd, 0xec, 0x40, 0x5c, 0x59, 0xb7, 0xdd, 0x59, 0x9f, + 0xf1, 0x6a, 0xf7, 0x06, 0xfc, 0xd6, 0x2f, 0x19, 0x8a, 0x95, 0x12, 0xba, + 0x9a, 0xca, 0xd5, 0x30, 0xd2, 0x38, 0xfc, 0x19, 0x3b, 0x5b, 0x15, 0x3b, + 0x36, 0xd0, 0x43, 0x4d, 0xd1, 0x65, 0xa1, 0xd4, 0x8b, 0xc1, 0x60, 0x41, + 0xb3, 0xd6, 0x70, 0x17, 0xcc, 0x39, 0xc0, 0x9c, 0x0c, 0xa0, 0x3d, 0xb7, + 0x11, 0x22, 0x4e, 0xce, 0xd9, 0xa9, 0x7a, 0xd2, 0x2a, 0x62, 0x9c, 0xa0, + 0x0b, 0x4e, 0x2a, 0xd7, 0xc3, 0x61, 0x5a, 0x85, 0xdd, 0x5c, 0x10, 0xb9, + 0x54, 0x3d, 0x2d, 0x03, 0xf8, 0x49, 0xf0, 0xbc, 0x92, 0xb7, 0xb7, 0x9c, + 0x31, 0xc7, 0xe9, 0xb8, 0xaa, 0x82, 0x0b, 0x05, 0xb9, 0x31, 0xcd, 0x08, + 0x5b, 0xbb, 0x22, 0x0b, 0xf6, 0x9c, 0x8e, 0x8a, 0x55, 0x1c, 0x76, 0x43, + 0x76, 0xf0, 0xe2, 0x6e, 0xf0, 0xdf, 0xa8, 0x29, 0x75, 0xe7, 0xc8, 0xa4, + 0x87, 0x8b, 0x6a, 0xf1, 0xbb, 0x08, 0xc9, 0x36, 0x18, 0x65, 0xee, 0x50, + 0x43, 0xb8, 0x5d, 0x72, 0xd5, 0x28, 0x39, 0xe1, 0x53, 0x3e, 0x25, 0x2c, + 0xda, 0x2b, 0x4f, 0xdd, 0x8a, 0x9e, 0x50, 0x50, 0xe0, 0x6f, 0x9a, 0xc4, + 0xd5, 0x19, 0x26, 0x89, 0x01, 0x75, 0x73, 0x09, 0x9b, 0x3b, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4a, 0x30, 0x82, 0x01, 0x46, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11, + 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x97, 0xc2, 0x27, 0x50, 0x9e, + 0xc2, 0xc9, 0xec, 0x0c, 0x88, 0x32, 0xc8, 0x7c, 0xad, 0xe2, 0xa6, 0x01, + 0x4f, 0xda, 0x6f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2f, + 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, + 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, + 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, + 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, + 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, + 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, + 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, 0x36, + 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x35, 0xeb, 0xe1, + 0x8b, 0x20, 0x56, 0x94, 0xba, 0x7a, 0xbd, 0x79, 0xa9, 0xf6, 0xe3, 0xfe, + 0x6e, 0x38, 0xb4, 0x32, 0xc1, 0xa3, 0xdb, 0x58, 0x56, 0x20, 0x3e, 0x7d, + 0xc7, 0x3a, 0xb1, 0x67, 0x69, 0xd5, 0x79, 0x14, 0x1b, 0xf6, 0xfa, 0xec, + 0x60, 0xf2, 0x79, 0xcd, 0x0a, 0x0c, 0x60, 0x8a, 0x74, 0x4c, 0xa3, 0x93, + 0x2a, 0xa0, 0xf0, 0x51, 0x7f, 0xcd, 0xe9, 0xf9, 0x92, 0xfd, 0x96, 0xab, + 0x45, 0xf5, 0x62, 0x3d, 0x3f, 0x60, 0x46, 0x50, 0x13, 0x3d, 0x20, 0x13, + 0x18, 0x2e, 0x94, 0x46, 0xae, 0xd5, 0x21, 0xfe, 0x43, 0xa1, 0xc9, 0x23, + 0xfe, 0x53, 0xc4, 0xbf, 0x1a, 0xd8, 0xac, 0x3a, 0xca, 0xde, 0x66, 0x97, + 0x23, 0xae, 0xd3, 0xdf, 0x4a, 0x4d, 0x73, 0x1f, 0x6f, 0x31, 0xa2, 0x51, + 0x04, 0x16, 0x6a, 0x00, 0xeb, 0xf9, 0x8d, 0x43, 0x81, 0xf0, 0x50, 0xa1, + 0x1f, 0xa6, 0xca, 0x3a, 0xf3, 0x28, 0x3c, 0x5f, 0x51, 0xac, 0xd7, 0x0a, + 0x45, 0x77, 0x4b, 0x0e, 0x52, 0x62, 0x1b, 0xd8, 0x38, 0x51, 0xa0, 0x92, + 0x2d, 0x3f, 0x90, 0x6e, 0xc8, 0x7e, 0x40, 0x9f, 0x20, 0x46, 0x15, 0x5d, + 0xe0, 0x50, 0x7c, 0xe1, 0x76, 0xaf, 0x5e, 0xed, 0x11, 0xd3, 0x2f, 0x13, + 0xb9, 0xb8, 0x25, 0xa4, 0xaf, 0x58, 0x09, 0xaf, 0x35, 0xb4, 0x62, 0x54, + 0x85, 0xe3, 0x48, 0xde, 0xbc, 0xd2, 0x90, 0x7a, 0x7a, 0xa4, 0x84, 0x0d, + 0xa3, 0x42, 0xf2, 0x51, 0xc0, 0xd4, 0xad, 0x53, 0x65, 0x5d, 0x6c, 0xf8, + 0x3f, 0x1f, 0x06, 0xf2, 0x4f, 0xcb, 0x97, 0xa0, 0x4a, 0x59, 0xc6, 0x78, + 0xd1, 0xe8, 0x03, 0xb9, 0x85, 0x6d, 0x2c, 0xba, 0xe1, 0x5f, 0xb6, 0xad, + 0x2b, 0x3e, 0x25, 0x79, 0xc5, 0x8b, 0x56, 0xd5, 0xe3, 0x09, 0x80, 0xea, + 0xc1, 0x27, 0xc2, 0xd9, 0x0e, 0xec, 0x47, 0x0a, 0xe9, 0xd0, 0xca, 0xfc, + 0xd8, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:00:00:00:00:01:44:4e:f0:36:31 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA + Validity + Not Before: Feb 20 10:00:00 2014 GMT + Not After : Feb 20 10:00:00 2024 GMT + Subject: C=BE, O=GlobalSign nv-sa, CN=AlphaSSL CA - SHA256 - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:01:ec:e4:ec:73:60:fb:7e:8f:6a:b7:c6:17: + e3:92:64:32:d4:ac:00:d9:a2:0f:b9:ed:ee:6b:8a: + 86:ca:92:67:d9:74:d7:5d:47:02:3c:8f:40:d6:9e: + 6d:14:cd:c3:da:29:39:a7:0f:05:0a:68:a2:66:1a: + 1e:c4:b2:8b:76:58:e5:ab:5d:1d:8f:40:b3:39:8b: + ef:1e:83:7d:22:d0:e3:a9:00:2e:ec:53:cf:62:19: + 85:44:28:4c:c0:27:cb:7b:0e:ec:10:64:00:10:a4: + 05:cc:a0:72:be:41:6c:31:5b:48:e4:b1:ec:b9:23: + eb:55:4d:d0:7d:62:4a:a5:b4:a5:a4:59:85:c5:25: + 91:a6:fe:a6:09:9f:06:10:6d:8f:81:0c:64:40:5e: + 73:00:9a:e0:2e:65:98:54:10:00:70:98:c8:e1:ed: + 34:5f:d8:9c:c7:0d:c0:d6:23:59:45:fc:fe:55:7a: + 86:ee:94:60:22:f1:ae:d1:e6:55:46:f6:99:c5:1b: + 08:74:5f:ac:b0:64:84:8f:89:38:1c:a1:a7:90:21: + 4f:02:6e:bd:e0:61:67:d4:f8:42:87:0f:0a:f7:c9: + 04:6d:2a:a9:2f:ef:42:a5:df:dd:a3:53:db:98:1e: + 81:f9:9a:72:7b:5a:de:4f:3e:7f:a2:58:a0:e2:17: + ad:67 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + F5:CD:D5:3C:08:50:F9:6A:4F:3A:B7:97:DA:56:83:E6:69:D2:68:F7 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.alphassl.com/repository/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.globalsign.net/root.crl + + Authority Information Access: + OCSP - URI:http://ocsp.globalsign.com/rootr1 + + X509v3 Authority Key Identifier: + keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + + Signature Algorithm: sha256WithRSAEncryption + 60:40:68:16:47:e7:16:8d:db:5c:a1:56:2a:cb:f4:5c:9b:b0: + 1e:a2:4b:f5:cb:02:3f:f8:0b:a1:f2:a7:42:d4:b7:4c:eb:e3: + 66:80:f3:25:43:78:2e:1b:17:56:07:52:18:cb:d1:a8:ec:e6: + fb:73:3e:a4:62:8c:80:b4:d2:c5:12:73:a3:d3:fa:02:38:be: + 63:3d:84:b8:99:c1:f1:ba:f7:9f:c3:40:d1:58:18:53:c1:62: + dd:af:18:42:7f:34:4e:c5:43:d5:71:b0:30:00:c7:e3:90:ae: + 3f:57:86:97:ce:ea:0c:12:8e:22:70:e3:66:a7:54:7f:2e:28: + cb:d4:54:d0:b3:1e:62:67:08:f9:27:e1:cb:e3:66:b8:24:1b: + 89:6a:89:44:65:f2:d9:4c:d2:58:1c:8c:4e:c0:95:a1:d4:ef: + 67:2f:38:20:e8:2e:ff:96:51:f0:ba:d8:3d:92:70:47:65:1c: + 9e:73:72:b4:60:0c:5c:e2:d1:73:76:e0:af:4e:e2:e5:37:a5: + 45:2f:8a:23:3e:87:c7:30:e6:31:38:7c:f4:dd:52:ca:f3:53: + 04:25:57:56:66:94:e8:0b:ee:e6:03:14:4e:ee:fd:6d:94:64: + 9e:5e:ce:79:d4:b2:a6:cf:40:b1:44:a8:3e:87:19:5e:e9:f8: + 21:16:59:53 +-----BEGIN CERTIFICATE----- +MIIETTCCAzWgAwIBAgILBAAAAAABRE7wNjEwDQYJKoZIhvcNAQELBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw +MDBaFw0yNDAyMjAxMDAwMDBaMEwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMSIwIAYDVQQDExlBbHBoYVNTTCBDQSAtIFNIQTI1NiAtIEcy +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gHs5OxzYPt+j2q3xhfj +kmQy1KwA2aIPue3ua4qGypJn2XTXXUcCPI9A1p5tFM3D2ik5pw8FCmiiZhoexLKL +dljlq10dj0CzOYvvHoN9ItDjqQAu7FPPYhmFRChMwCfLew7sEGQAEKQFzKByvkFs +MVtI5LHsuSPrVU3QfWJKpbSlpFmFxSWRpv6mCZ8GEG2PgQxkQF5zAJrgLmWYVBAA +cJjI4e00X9icxw3A1iNZRfz+VXqG7pRgIvGu0eZVRvaZxRsIdF+ssGSEj4k4HKGn +kCFPAm694GFn1PhChw8K98kEbSqpL+9Cpd/do1PbmB6B+Zpye1reTz5/olig4het +ZwIDAQABo4IBIzCCAR8wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C +AQAwHQYDVR0OBBYEFPXN1TwIUPlqTzq3l9pWg+Zp0mj3MEUGA1UdIAQ+MDwwOgYE +VR0gADAyMDAGCCsGAQUFBwIBFiRodHRwczovL3d3dy5hbHBoYXNzbC5jb20vcmVw +b3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWdu +Lm5ldC9yb290LmNybDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6 +Ly9vY3NwLmdsb2JhbHNpZ24uY29tL3Jvb3RyMTAfBgNVHSMEGDAWgBRge2YaRQ2X +yolQL30EzTSo//z9SzANBgkqhkiG9w0BAQsFAAOCAQEAYEBoFkfnFo3bXKFWKsv0 +XJuwHqJL9csCP/gLofKnQtS3TOvjZoDzJUN4LhsXVgdSGMvRqOzm+3M+pGKMgLTS +xRJzo9P6Aji+Yz2EuJnB8br3n8NA0VgYU8Fi3a8YQn80TsVD1XGwMADH45CuP1eG +l87qDBKOInDjZqdUfy4oy9RU0LMeYmcI+Sfhy+NmuCQbiWqJRGXy2UzSWByMTsCV +odTvZy84IOgu/5ZR8LrYPZJwR2UcnnNytGAMXOLRc3bgr07i5TelRS+KIz6HxzDm +MTh89N1SyvNTBCVXVmaU6Avu5gMUTu79bZRknl7OedSyps9AsUSoPocZXun4IRZZ +Uw== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert9[] = { + 0x30, 0x82, 0x04, 0x4d, 0x30, 0x82, 0x03, 0x35, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0, + 0x36, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, + 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4c, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, + 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, + 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x41, + 0x6c, 0x70, 0x68, 0x61, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, + 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0x01, 0xec, + 0xe4, 0xec, 0x73, 0x60, 0xfb, 0x7e, 0x8f, 0x6a, 0xb7, 0xc6, 0x17, 0xe3, + 0x92, 0x64, 0x32, 0xd4, 0xac, 0x00, 0xd9, 0xa2, 0x0f, 0xb9, 0xed, 0xee, + 0x6b, 0x8a, 0x86, 0xca, 0x92, 0x67, 0xd9, 0x74, 0xd7, 0x5d, 0x47, 0x02, + 0x3c, 0x8f, 0x40, 0xd6, 0x9e, 0x6d, 0x14, 0xcd, 0xc3, 0xda, 0x29, 0x39, + 0xa7, 0x0f, 0x05, 0x0a, 0x68, 0xa2, 0x66, 0x1a, 0x1e, 0xc4, 0xb2, 0x8b, + 0x76, 0x58, 0xe5, 0xab, 0x5d, 0x1d, 0x8f, 0x40, 0xb3, 0x39, 0x8b, 0xef, + 0x1e, 0x83, 0x7d, 0x22, 0xd0, 0xe3, 0xa9, 0x00, 0x2e, 0xec, 0x53, 0xcf, + 0x62, 0x19, 0x85, 0x44, 0x28, 0x4c, 0xc0, 0x27, 0xcb, 0x7b, 0x0e, 0xec, + 0x10, 0x64, 0x00, 0x10, 0xa4, 0x05, 0xcc, 0xa0, 0x72, 0xbe, 0x41, 0x6c, + 0x31, 0x5b, 0x48, 0xe4, 0xb1, 0xec, 0xb9, 0x23, 0xeb, 0x55, 0x4d, 0xd0, + 0x7d, 0x62, 0x4a, 0xa5, 0xb4, 0xa5, 0xa4, 0x59, 0x85, 0xc5, 0x25, 0x91, + 0xa6, 0xfe, 0xa6, 0x09, 0x9f, 0x06, 0x10, 0x6d, 0x8f, 0x81, 0x0c, 0x64, + 0x40, 0x5e, 0x73, 0x00, 0x9a, 0xe0, 0x2e, 0x65, 0x98, 0x54, 0x10, 0x00, + 0x70, 0x98, 0xc8, 0xe1, 0xed, 0x34, 0x5f, 0xd8, 0x9c, 0xc7, 0x0d, 0xc0, + 0xd6, 0x23, 0x59, 0x45, 0xfc, 0xfe, 0x55, 0x7a, 0x86, 0xee, 0x94, 0x60, + 0x22, 0xf1, 0xae, 0xd1, 0xe6, 0x55, 0x46, 0xf6, 0x99, 0xc5, 0x1b, 0x08, + 0x74, 0x5f, 0xac, 0xb0, 0x64, 0x84, 0x8f, 0x89, 0x38, 0x1c, 0xa1, 0xa7, + 0x90, 0x21, 0x4f, 0x02, 0x6e, 0xbd, 0xe0, 0x61, 0x67, 0xd4, 0xf8, 0x42, + 0x87, 0x0f, 0x0a, 0xf7, 0xc9, 0x04, 0x6d, 0x2a, 0xa9, 0x2f, 0xef, 0x42, + 0xa5, 0xdf, 0xdd, 0xa3, 0x53, 0xdb, 0x98, 0x1e, 0x81, 0xf9, 0x9a, 0x72, + 0x7b, 0x5a, 0xde, 0x4f, 0x3e, 0x7f, 0xa2, 0x58, 0xa0, 0xe2, 0x17, 0xad, + 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x23, 0x30, 0x82, + 0x01, 0x1f, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xf5, 0xcd, 0xd5, 0x3c, 0x08, 0x50, 0xf9, 0x6a, 0x4f, 0x3a, 0xb7, + 0x97, 0xda, 0x56, 0x83, 0xe6, 0x69, 0xd2, 0x68, 0xf7, 0x30, 0x45, 0x06, + 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x04, + 0x55, 0x1d, 0x20, 0x00, 0x30, 0x32, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x24, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, + 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, + 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, + 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, + 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, + 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, + 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x60, 0x40, 0x68, + 0x16, 0x47, 0xe7, 0x16, 0x8d, 0xdb, 0x5c, 0xa1, 0x56, 0x2a, 0xcb, 0xf4, + 0x5c, 0x9b, 0xb0, 0x1e, 0xa2, 0x4b, 0xf5, 0xcb, 0x02, 0x3f, 0xf8, 0x0b, + 0xa1, 0xf2, 0xa7, 0x42, 0xd4, 0xb7, 0x4c, 0xeb, 0xe3, 0x66, 0x80, 0xf3, + 0x25, 0x43, 0x78, 0x2e, 0x1b, 0x17, 0x56, 0x07, 0x52, 0x18, 0xcb, 0xd1, + 0xa8, 0xec, 0xe6, 0xfb, 0x73, 0x3e, 0xa4, 0x62, 0x8c, 0x80, 0xb4, 0xd2, + 0xc5, 0x12, 0x73, 0xa3, 0xd3, 0xfa, 0x02, 0x38, 0xbe, 0x63, 0x3d, 0x84, + 0xb8, 0x99, 0xc1, 0xf1, 0xba, 0xf7, 0x9f, 0xc3, 0x40, 0xd1, 0x58, 0x18, + 0x53, 0xc1, 0x62, 0xdd, 0xaf, 0x18, 0x42, 0x7f, 0x34, 0x4e, 0xc5, 0x43, + 0xd5, 0x71, 0xb0, 0x30, 0x00, 0xc7, 0xe3, 0x90, 0xae, 0x3f, 0x57, 0x86, + 0x97, 0xce, 0xea, 0x0c, 0x12, 0x8e, 0x22, 0x70, 0xe3, 0x66, 0xa7, 0x54, + 0x7f, 0x2e, 0x28, 0xcb, 0xd4, 0x54, 0xd0, 0xb3, 0x1e, 0x62, 0x67, 0x08, + 0xf9, 0x27, 0xe1, 0xcb, 0xe3, 0x66, 0xb8, 0x24, 0x1b, 0x89, 0x6a, 0x89, + 0x44, 0x65, 0xf2, 0xd9, 0x4c, 0xd2, 0x58, 0x1c, 0x8c, 0x4e, 0xc0, 0x95, + 0xa1, 0xd4, 0xef, 0x67, 0x2f, 0x38, 0x20, 0xe8, 0x2e, 0xff, 0x96, 0x51, + 0xf0, 0xba, 0xd8, 0x3d, 0x92, 0x70, 0x47, 0x65, 0x1c, 0x9e, 0x73, 0x72, + 0xb4, 0x60, 0x0c, 0x5c, 0xe2, 0xd1, 0x73, 0x76, 0xe0, 0xaf, 0x4e, 0xe2, + 0xe5, 0x37, 0xa5, 0x45, 0x2f, 0x8a, 0x23, 0x3e, 0x87, 0xc7, 0x30, 0xe6, + 0x31, 0x38, 0x7c, 0xf4, 0xdd, 0x52, 0xca, 0xf3, 0x53, 0x04, 0x25, 0x57, + 0x56, 0x66, 0x94, 0xe8, 0x0b, 0xee, 0xe6, 0x03, 0x14, 0x4e, 0xee, 0xfd, + 0x6d, 0x94, 0x64, 0x9e, 0x5e, 0xce, 0x79, 0xd4, 0xb2, 0xa6, 0xcf, 0x40, + 0xb1, 0x44, 0xa8, 0x3e, 0x87, 0x19, 0x5e, 0xe9, 0xf8, 0x21, 0x16, 0x59, + 0x53, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146031 (0x23a6f) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Nov 5 21:36:50 2013 GMT + Not After : May 20 21:36:50 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e3:be:7e:0a:86:a3:cf:6b:6d:3d:2b:a1:97:ad: + 49:24:4d:d7:77:b9:34:79:08:a5:9e:a2:9e:de:47: + 12:92:3d:7e:ea:19:86:b1:e8:4f:3d:5f:f7:d0:a7: + 77:9a:5b:1f:0a:03:b5:19:53:db:a5:21:94:69:63: + 9d:6a:4c:91:0c:10:47:be:11:fa:6c:86:25:b7:ab: + 04:68:42:38:09:65:f0:14:da:19:9e:fa:6b:0b:ab: + 62:ef:8d:a7:ef:63:70:23:a8:af:81:f3:d1:6e:88: + 67:53:ec:12:a4:29:75:8a:a7:f2:57:3d:a2:83:98: + 97:f2:0a:7d:d4:e7:43:6e:30:78:62:22:59:59:b8: + 71:27:45:aa:0f:66:c6:55:3f:fa:32:17:2b:31:8f: + 46:a0:fa:69:14:7c:9d:9f:5a:e2:eb:33:4e:10:a6: + b3:ed:77:63:d8:c3:9e:f4:dd:df:79:9a:7a:d4:ee: + de:dd:9a:cc:c3:b7:a9:5d:cc:11:3a:07:bb:6f:97: + a4:01:23:47:95:1f:a3:77:fa:58:92:c6:c7:d0:bd: + cf:93:18:42:b7:7e:f7:9e:65:ea:d5:3b:ca:ed:ac: + c5:70:a1:fe:d4:10:9a:f0:12:04:44:ac:1a:5b:78: + 50:45:57:4c:6f:bd:80:cb:81:5c:2d:b3:bc:76:a1: + 1e:65 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + D2:6F:F7:96:F4:85:3F:72:3C:30:7D:23:DA:85:78:9B:A3:7C:5A:7C + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g1.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g2.symcb.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-539 + Signature Algorithm: sha256WithRSAEncryption + a0:d4:f7:2c:fb:74:0b:7f:64:f1:cd:43:6a:9f:62:53:1c:02: + 7c:98:90:a2:ee:4f:68:d4:20:1a:73:12:3e:77:b3:50:eb:72: + bc:ee:88:be:7f:17:ea:77:8f:83:61:95:4f:84:a1:cb:32:4f: + 6c:21:be:d2:69:96:7d:63:bd:dc:2b:a8:1f:d0:13:84:70:fe: + f6:35:95:89:f9:a6:77:b0:46:c8:bb:b7:13:f5:c9:60:69:d6: + 4c:fe:d2:8e:ef:d3:60:c1:80:80:e1:e7:fb:8b:6f:21:79:4a: + e0:dc:a9:1b:c1:b7:fb:c3:49:59:5c:b5:77:07:44:d4:97:fc: + 49:00:89:6f:06:4e:01:70:19:ac:2f:11:c0:e2:e6:0f:2f:86: + 4b:8d:7b:c3:b9:a7:2e:f4:f1:ac:16:3e:39:49:51:9e:17:4b: + 4f:10:3a:5b:a5:a8:92:6f:fd:fa:d6:0b:03:4d:47:56:57:19: + f3:cb:6b:f5:f3:d6:cf:b0:f5:f5:a3:11:d2:20:53:13:34:37: + 05:2c:43:5a:63:df:8d:40:d6:85:1e:51:e9:51:17:1e:03:56: + c9:f1:30:ad:e7:9b:11:a2:b9:d0:31:81:9b:68:b1:d9:e8:f3: + e6:94:7e:c7:ae:13:2f:87:ed:d0:25:b0:68:f9:de:08:5a:f3: + 29:cc:d4:92 +-----BEGIN CERTIFICATE----- +MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTMxMTA1MjEzNjUwWhcNMjIwNTIwMjEzNjUwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +U1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjvn4K +hqPPa209K6GXrUkkTdd3uTR5CKWeop7eRxKSPX7qGYax6E89X/fQp3eaWx8KA7UZ +U9ulIZRpY51qTJEMEEe+EfpshiW3qwRoQjgJZfAU2hme+msLq2LvjafvY3AjqK+B +89FuiGdT7BKkKXWKp/JXPaKDmJfyCn3U50NuMHhiIllZuHEnRaoPZsZVP/oyFysx +j0ag+mkUfJ2fWuLrM04QprPtd2PYw5703d95mnrU7t7dmszDt6ldzBE6B7tvl6QB +I0eVH6N3+liSxsfQvc+TGEK3fveeZerVO8rtrMVwof7UEJrwEgRErBpbeFBFV0xv +vYDLgVwts7x2oR5lAgMBAAGjggFKMIIBRjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjAdBgNVHQ4EFgQU0m/3lvSFP3I8MH0j2oV4m6N8WnwwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNgYDVR0fBC8wLTAroCmgJ4Yl +aHR0cDovL2cxLnN5bWNiLmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAvBggrBgEFBQcB +AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9nMi5zeW1jYi5jb20wTAYDVR0gBEUw +QzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1 +c3QuY29tL3Jlc291cmNlcy9jcHMwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5 +bWFudGVjUEtJLTEtNTM5MA0GCSqGSIb3DQEBCwUAA4IBAQCg1Pcs+3QLf2TxzUNq +n2JTHAJ8mJCi7k9o1CAacxI+d7NQ63K87oi+fxfqd4+DYZVPhKHLMk9sIb7SaZZ9 +Y73cK6gf0BOEcP72NZWJ+aZ3sEbIu7cT9clgadZM/tKO79NgwYCA4ef7i28heUrg +3Kkbwbf7w0lZXLV3B0TUl/xJAIlvBk4BcBmsLxHA4uYPL4ZLjXvDuacu9PGsFj45 +SVGeF0tPEDpbpaiSb/361gsDTUdWVxnzy2v189bPsPX1oxHSIFMTNDcFLENaY9+N +QNaFHlHpURceA1bJ8TCt55sRornQMYGbaLHZ6PPmlH7HrhMvh+3QJbBo+d4IWvMp +zNSS +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert10[] = { + 0x30, 0x82, 0x04, 0x4f, 0x30, 0x82, 0x03, 0x37, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x6f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, + 0x31, 0x30, 0x35, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30, + 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe3, 0xbe, 0x7e, 0x0a, + 0x86, 0xa3, 0xcf, 0x6b, 0x6d, 0x3d, 0x2b, 0xa1, 0x97, 0xad, 0x49, 0x24, + 0x4d, 0xd7, 0x77, 0xb9, 0x34, 0x79, 0x08, 0xa5, 0x9e, 0xa2, 0x9e, 0xde, + 0x47, 0x12, 0x92, 0x3d, 0x7e, 0xea, 0x19, 0x86, 0xb1, 0xe8, 0x4f, 0x3d, + 0x5f, 0xf7, 0xd0, 0xa7, 0x77, 0x9a, 0x5b, 0x1f, 0x0a, 0x03, 0xb5, 0x19, + 0x53, 0xdb, 0xa5, 0x21, 0x94, 0x69, 0x63, 0x9d, 0x6a, 0x4c, 0x91, 0x0c, + 0x10, 0x47, 0xbe, 0x11, 0xfa, 0x6c, 0x86, 0x25, 0xb7, 0xab, 0x04, 0x68, + 0x42, 0x38, 0x09, 0x65, 0xf0, 0x14, 0xda, 0x19, 0x9e, 0xfa, 0x6b, 0x0b, + 0xab, 0x62, 0xef, 0x8d, 0xa7, 0xef, 0x63, 0x70, 0x23, 0xa8, 0xaf, 0x81, + 0xf3, 0xd1, 0x6e, 0x88, 0x67, 0x53, 0xec, 0x12, 0xa4, 0x29, 0x75, 0x8a, + 0xa7, 0xf2, 0x57, 0x3d, 0xa2, 0x83, 0x98, 0x97, 0xf2, 0x0a, 0x7d, 0xd4, + 0xe7, 0x43, 0x6e, 0x30, 0x78, 0x62, 0x22, 0x59, 0x59, 0xb8, 0x71, 0x27, + 0x45, 0xaa, 0x0f, 0x66, 0xc6, 0x55, 0x3f, 0xfa, 0x32, 0x17, 0x2b, 0x31, + 0x8f, 0x46, 0xa0, 0xfa, 0x69, 0x14, 0x7c, 0x9d, 0x9f, 0x5a, 0xe2, 0xeb, + 0x33, 0x4e, 0x10, 0xa6, 0xb3, 0xed, 0x77, 0x63, 0xd8, 0xc3, 0x9e, 0xf4, + 0xdd, 0xdf, 0x79, 0x9a, 0x7a, 0xd4, 0xee, 0xde, 0xdd, 0x9a, 0xcc, 0xc3, + 0xb7, 0xa9, 0x5d, 0xcc, 0x11, 0x3a, 0x07, 0xbb, 0x6f, 0x97, 0xa4, 0x01, + 0x23, 0x47, 0x95, 0x1f, 0xa3, 0x77, 0xfa, 0x58, 0x92, 0xc6, 0xc7, 0xd0, + 0xbd, 0xcf, 0x93, 0x18, 0x42, 0xb7, 0x7e, 0xf7, 0x9e, 0x65, 0xea, 0xd5, + 0x3b, 0xca, 0xed, 0xac, 0xc5, 0x70, 0xa1, 0xfe, 0xd4, 0x10, 0x9a, 0xf0, + 0x12, 0x04, 0x44, 0xac, 0x1a, 0x5b, 0x78, 0x50, 0x45, 0x57, 0x4c, 0x6f, + 0xbd, 0x80, 0xcb, 0x81, 0x5c, 0x2d, 0xb3, 0xbc, 0x76, 0xa1, 0x1e, 0x65, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4a, 0x30, 0x82, 0x01, + 0x46, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, + 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd2, 0x6f, 0xf7, + 0x96, 0xf4, 0x85, 0x3f, 0x72, 0x3c, 0x30, 0x7d, 0x23, 0xda, 0x85, 0x78, + 0x9b, 0xa3, 0x7c, 0x5a, 0x7c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79, + 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, + 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, + 0x6c, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, + 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, + 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, + 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, + 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, + 0x35, 0x33, 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa0, + 0xd4, 0xf7, 0x2c, 0xfb, 0x74, 0x0b, 0x7f, 0x64, 0xf1, 0xcd, 0x43, 0x6a, + 0x9f, 0x62, 0x53, 0x1c, 0x02, 0x7c, 0x98, 0x90, 0xa2, 0xee, 0x4f, 0x68, + 0xd4, 0x20, 0x1a, 0x73, 0x12, 0x3e, 0x77, 0xb3, 0x50, 0xeb, 0x72, 0xbc, + 0xee, 0x88, 0xbe, 0x7f, 0x17, 0xea, 0x77, 0x8f, 0x83, 0x61, 0x95, 0x4f, + 0x84, 0xa1, 0xcb, 0x32, 0x4f, 0x6c, 0x21, 0xbe, 0xd2, 0x69, 0x96, 0x7d, + 0x63, 0xbd, 0xdc, 0x2b, 0xa8, 0x1f, 0xd0, 0x13, 0x84, 0x70, 0xfe, 0xf6, + 0x35, 0x95, 0x89, 0xf9, 0xa6, 0x77, 0xb0, 0x46, 0xc8, 0xbb, 0xb7, 0x13, + 0xf5, 0xc9, 0x60, 0x69, 0xd6, 0x4c, 0xfe, 0xd2, 0x8e, 0xef, 0xd3, 0x60, + 0xc1, 0x80, 0x80, 0xe1, 0xe7, 0xfb, 0x8b, 0x6f, 0x21, 0x79, 0x4a, 0xe0, + 0xdc, 0xa9, 0x1b, 0xc1, 0xb7, 0xfb, 0xc3, 0x49, 0x59, 0x5c, 0xb5, 0x77, + 0x07, 0x44, 0xd4, 0x97, 0xfc, 0x49, 0x00, 0x89, 0x6f, 0x06, 0x4e, 0x01, + 0x70, 0x19, 0xac, 0x2f, 0x11, 0xc0, 0xe2, 0xe6, 0x0f, 0x2f, 0x86, 0x4b, + 0x8d, 0x7b, 0xc3, 0xb9, 0xa7, 0x2e, 0xf4, 0xf1, 0xac, 0x16, 0x3e, 0x39, + 0x49, 0x51, 0x9e, 0x17, 0x4b, 0x4f, 0x10, 0x3a, 0x5b, 0xa5, 0xa8, 0x92, + 0x6f, 0xfd, 0xfa, 0xd6, 0x0b, 0x03, 0x4d, 0x47, 0x56, 0x57, 0x19, 0xf3, + 0xcb, 0x6b, 0xf5, 0xf3, 0xd6, 0xcf, 0xb0, 0xf5, 0xf5, 0xa3, 0x11, 0xd2, + 0x20, 0x53, 0x13, 0x34, 0x37, 0x05, 0x2c, 0x43, 0x5a, 0x63, 0xdf, 0x8d, + 0x40, 0xd6, 0x85, 0x1e, 0x51, 0xe9, 0x51, 0x17, 0x1e, 0x03, 0x56, 0xc9, + 0xf1, 0x30, 0xad, 0xe7, 0x9b, 0x11, 0xa2, 0xb9, 0xd0, 0x31, 0x81, 0x9b, + 0x68, 0xb1, 0xd9, 0xe8, 0xf3, 0xe6, 0x94, 0x7e, 0xc7, 0xae, 0x13, 0x2f, + 0x87, 0xed, 0xd0, 0x25, 0xb0, 0x68, 0xf9, 0xde, 0x08, 0x5a, 0xf3, 0x29, + 0xcc, 0xd4, 0x92, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146019 (0x23a63) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Aug 27 20:40:40 2012 GMT + Not After : May 20 20:40:40 2022 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b9:27:f9:4f:d8:f6:b7:15:3f:8f:cd:ce:d6:8d: + 1c:6b:fd:7f:da:54:21:4e:03:d8:ca:d0:72:52:15: + b8:c9:82:5b:58:79:84:ff:24:72:6f:f2:69:7f:bc: + 96:d9:9a:7a:c3:3e:a9:cf:50:22:13:0e:86:19:db: + e8:49:ef:8b:e6:d6:47:f2:fd:73:45:08:ae:8f:ac: + 5e:b6:f8:9e:7c:f7:10:ff:92:43:66:ef:1c:d4:ee: + a1:46:88:11:89:49:79:7a:25:ce:4b:6a:f0:d7:1c: + 76:1a:29:3c:c9:e4:fd:1e:85:dc:e0:31:65:05:47: + 16:ac:0a:07:4b:2e:70:5e:6b:06:a7:6b:3a:6c:af: + 05:12:c4:b2:11:25:d6:3e:97:29:f0:83:6c:57:1c: + d8:a5:ef:cc:ec:fd:d6:12:f1:3f:db:40:b4:ae:0f: + 18:d3:c5:af:40:92:5d:07:5e:4e:fe:62:17:37:89: + e9:8b:74:26:a2:ed:b8:0a:e7:6c:15:5b:35:90:72: + dd:d8:4d:21:d4:40:23:5c:8f:ee:80:31:16:ab:68: + 55:f4:0e:3b:54:e9:04:4d:f0:cc:4e:81:5e:e9:6f: + 52:69:4e:be:a6:16:6d:42:f5:51:ff:e0:0b:56:3c: + 98:4f:73:8f:0e:6f:1a:23:f1:c9:c8:d9:df:bc:ec: + 52:d7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + 11:4A:D0:73:39:D5:5B:69:08:5C:BA:3D:BF:64:9A:A8:8B:1C:55:BC + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://ocsp.geotrust.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-254 + Signature Algorithm: sha1WithRSAEncryption + 3c:e5:3d:5a:1b:a2:37:2a:e3:46:cf:36:96:18:3c:7b:f1:84: + c5:57:86:77:40:9d:35:f0:12:f0:78:18:fb:22:a4:de:98:4b: + 78:81:e6:4d:86:e3:91:0f:42:e3:b9:dc:a0:d6:ff:a9:f8:b1: + 79:97:99:d1:c3:6c:42:a5:92:94:e0:5d:0c:33:18:25:c9:2b: + 95:53:e0:e5:a9:0c:7d:47:fe:7f:51:31:44:5e:f7:2a:1e:35: + a2:94:32:f7:c9:ee:c0:b6:c6:9a:ac:de:99:21:6a:23:a0:38: + 64:ee:a3:c4:88:73:32:3b:50:ce:bf:ad:d3:75:1e:a6:f4:e9: + f9:42:6b:60:b2:dd:45:fd:5d:57:08:ce:2d:50:e6:12:32:16: + 13:8a:f2:94:a2:9b:47:a8:86:7f:d9:98:e5:f7:e5:76:74:64: + d8:91:bc:84:16:28:d8:25:44:30:7e:82:d8:ac:b1:e4:c0:e4: + 15:6c:db:b6:24:27:02:2a:01:12:85:ba:31:88:58:47:74:e3: + b8:d2:64:a6:c3:32:59:2e:29:4b:45:f1:5b:89:49:2e:82:9a: + c6:18:15:44:d0:2e:64:01:15:68:38:f9:f6:f9:66:03:0c:55: + 1b:9d:bf:00:40:ae:f0:48:27:4c:e0:80:5e:2d:b9:2a:15:7a: + bc:66:f8:35 +-----BEGIN CERTIFICATE----- +MIIEWTCCA0GgAwIBAgIDAjpjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTIwODI3MjA0MDQwWhcNMjIwNTIwMjA0MDQwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +U1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5J/lP +2Pa3FT+Pzc7WjRxr/X/aVCFOA9jK0HJSFbjJgltYeYT/JHJv8ml/vJbZmnrDPqnP +UCITDoYZ2+hJ74vm1kfy/XNFCK6PrF62+J589xD/kkNm7xzU7qFGiBGJSXl6Jc5L +avDXHHYaKTzJ5P0ehdzgMWUFRxasCgdLLnBeawanazpsrwUSxLIRJdY+lynwg2xX +HNil78zs/dYS8T/bQLSuDxjTxa9Akl0HXk7+Yhc3iemLdCai7bgK52wVWzWQct3Y +TSHUQCNcj+6AMRaraFX0DjtU6QRN8MxOgV7pb1JpTr6mFm1C9VH/4AtWPJhPc48O +bxoj8cnI2d+87FLXAgMBAAGjggFUMIIBUDAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjAdBgNVHQ4EFgQUEUrQcznVW2kIXLo9v2SaqIscVbwwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2gK4Yp +aHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYB +BQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20w +TAYDVR0gBEUwQzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93 +d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwKgYDVR0RBCMwIaQfMB0xGzAZ +BgNVBAMTElZlcmlTaWduTVBLSS0yLTI1NDANBgkqhkiG9w0BAQUFAAOCAQEAPOU9 +WhuiNyrjRs82lhg8e/GExVeGd0CdNfAS8HgY+yKk3phLeIHmTYbjkQ9C47ncoNb/ +qfixeZeZ0cNsQqWSlOBdDDMYJckrlVPg5akMfUf+f1ExRF73Kh41opQy98nuwLbG +mqzemSFqI6A4ZO6jxIhzMjtQzr+t03UepvTp+UJrYLLdRf1dVwjOLVDmEjIWE4ry +lKKbR6iGf9mY5ffldnRk2JG8hBYo2CVEMH6C2Kyx5MDkFWzbtiQnAioBEoW6MYhY +R3TjuNJkpsMyWS4pS0XxW4lJLoKaxhgVRNAuZAEVaDj59vlmAwxVG52/AECu8Egn +TOCAXi25KhV6vGb4NQ== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert11[] = { + 0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x41, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x63, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, + 0x38, 0x32, 0x37, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30, + 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x27, 0xf9, 0x4f, + 0xd8, 0xf6, 0xb7, 0x15, 0x3f, 0x8f, 0xcd, 0xce, 0xd6, 0x8d, 0x1c, 0x6b, + 0xfd, 0x7f, 0xda, 0x54, 0x21, 0x4e, 0x03, 0xd8, 0xca, 0xd0, 0x72, 0x52, + 0x15, 0xb8, 0xc9, 0x82, 0x5b, 0x58, 0x79, 0x84, 0xff, 0x24, 0x72, 0x6f, + 0xf2, 0x69, 0x7f, 0xbc, 0x96, 0xd9, 0x9a, 0x7a, 0xc3, 0x3e, 0xa9, 0xcf, + 0x50, 0x22, 0x13, 0x0e, 0x86, 0x19, 0xdb, 0xe8, 0x49, 0xef, 0x8b, 0xe6, + 0xd6, 0x47, 0xf2, 0xfd, 0x73, 0x45, 0x08, 0xae, 0x8f, 0xac, 0x5e, 0xb6, + 0xf8, 0x9e, 0x7c, 0xf7, 0x10, 0xff, 0x92, 0x43, 0x66, 0xef, 0x1c, 0xd4, + 0xee, 0xa1, 0x46, 0x88, 0x11, 0x89, 0x49, 0x79, 0x7a, 0x25, 0xce, 0x4b, + 0x6a, 0xf0, 0xd7, 0x1c, 0x76, 0x1a, 0x29, 0x3c, 0xc9, 0xe4, 0xfd, 0x1e, + 0x85, 0xdc, 0xe0, 0x31, 0x65, 0x05, 0x47, 0x16, 0xac, 0x0a, 0x07, 0x4b, + 0x2e, 0x70, 0x5e, 0x6b, 0x06, 0xa7, 0x6b, 0x3a, 0x6c, 0xaf, 0x05, 0x12, + 0xc4, 0xb2, 0x11, 0x25, 0xd6, 0x3e, 0x97, 0x29, 0xf0, 0x83, 0x6c, 0x57, + 0x1c, 0xd8, 0xa5, 0xef, 0xcc, 0xec, 0xfd, 0xd6, 0x12, 0xf1, 0x3f, 0xdb, + 0x40, 0xb4, 0xae, 0x0f, 0x18, 0xd3, 0xc5, 0xaf, 0x40, 0x92, 0x5d, 0x07, + 0x5e, 0x4e, 0xfe, 0x62, 0x17, 0x37, 0x89, 0xe9, 0x8b, 0x74, 0x26, 0xa2, + 0xed, 0xb8, 0x0a, 0xe7, 0x6c, 0x15, 0x5b, 0x35, 0x90, 0x72, 0xdd, 0xd8, + 0x4d, 0x21, 0xd4, 0x40, 0x23, 0x5c, 0x8f, 0xee, 0x80, 0x31, 0x16, 0xab, + 0x68, 0x55, 0xf4, 0x0e, 0x3b, 0x54, 0xe9, 0x04, 0x4d, 0xf0, 0xcc, 0x4e, + 0x81, 0x5e, 0xe9, 0x6f, 0x52, 0x69, 0x4e, 0xbe, 0xa6, 0x16, 0x6d, 0x42, + 0xf5, 0x51, 0xff, 0xe0, 0x0b, 0x56, 0x3c, 0x98, 0x4f, 0x73, 0x8f, 0x0e, + 0x6f, 0x1a, 0x23, 0xf1, 0xc9, 0xc8, 0xd9, 0xdf, 0xbc, 0xec, 0x52, 0xd7, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x54, 0x30, 0x82, 0x01, + 0x50, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, + 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x11, 0x4a, 0xd0, + 0x73, 0x39, 0xd5, 0x5b, 0x69, 0x08, 0x5c, 0xba, 0x3d, 0xbf, 0x64, 0x9a, + 0xa8, 0x8b, 0x1c, 0x55, 0xbc, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, + 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, + 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, + 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, + 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, + 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, + 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x32, 0x35, + 0x34, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0xe5, 0x3d, + 0x5a, 0x1b, 0xa2, 0x37, 0x2a, 0xe3, 0x46, 0xcf, 0x36, 0x96, 0x18, 0x3c, + 0x7b, 0xf1, 0x84, 0xc5, 0x57, 0x86, 0x77, 0x40, 0x9d, 0x35, 0xf0, 0x12, + 0xf0, 0x78, 0x18, 0xfb, 0x22, 0xa4, 0xde, 0x98, 0x4b, 0x78, 0x81, 0xe6, + 0x4d, 0x86, 0xe3, 0x91, 0x0f, 0x42, 0xe3, 0xb9, 0xdc, 0xa0, 0xd6, 0xff, + 0xa9, 0xf8, 0xb1, 0x79, 0x97, 0x99, 0xd1, 0xc3, 0x6c, 0x42, 0xa5, 0x92, + 0x94, 0xe0, 0x5d, 0x0c, 0x33, 0x18, 0x25, 0xc9, 0x2b, 0x95, 0x53, 0xe0, + 0xe5, 0xa9, 0x0c, 0x7d, 0x47, 0xfe, 0x7f, 0x51, 0x31, 0x44, 0x5e, 0xf7, + 0x2a, 0x1e, 0x35, 0xa2, 0x94, 0x32, 0xf7, 0xc9, 0xee, 0xc0, 0xb6, 0xc6, + 0x9a, 0xac, 0xde, 0x99, 0x21, 0x6a, 0x23, 0xa0, 0x38, 0x64, 0xee, 0xa3, + 0xc4, 0x88, 0x73, 0x32, 0x3b, 0x50, 0xce, 0xbf, 0xad, 0xd3, 0x75, 0x1e, + 0xa6, 0xf4, 0xe9, 0xf9, 0x42, 0x6b, 0x60, 0xb2, 0xdd, 0x45, 0xfd, 0x5d, + 0x57, 0x08, 0xce, 0x2d, 0x50, 0xe6, 0x12, 0x32, 0x16, 0x13, 0x8a, 0xf2, + 0x94, 0xa2, 0x9b, 0x47, 0xa8, 0x86, 0x7f, 0xd9, 0x98, 0xe5, 0xf7, 0xe5, + 0x76, 0x74, 0x64, 0xd8, 0x91, 0xbc, 0x84, 0x16, 0x28, 0xd8, 0x25, 0x44, + 0x30, 0x7e, 0x82, 0xd8, 0xac, 0xb1, 0xe4, 0xc0, 0xe4, 0x15, 0x6c, 0xdb, + 0xb6, 0x24, 0x27, 0x02, 0x2a, 0x01, 0x12, 0x85, 0xba, 0x31, 0x88, 0x58, + 0x47, 0x74, 0xe3, 0xb8, 0xd2, 0x64, 0xa6, 0xc3, 0x32, 0x59, 0x2e, 0x29, + 0x4b, 0x45, 0xf1, 0x5b, 0x89, 0x49, 0x2e, 0x82, 0x9a, 0xc6, 0x18, 0x15, + 0x44, 0xd0, 0x2e, 0x64, 0x01, 0x15, 0x68, 0x38, 0xf9, 0xf6, 0xf9, 0x66, + 0x03, 0x0c, 0x55, 0x1b, 0x9d, 0xbf, 0x00, 0x40, 0xae, 0xf0, 0x48, 0x27, + 0x4c, 0xe0, 0x80, 0x5e, 0x2d, 0xb9, 0x2a, 0x15, 0x7a, 0xbc, 0x66, 0xf8, + 0x35, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:00:00:00:00:01:44:4e:f0:3e:20 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA + Validity + Not Before: Feb 20 10:00:00 2014 GMT + Not After : Feb 20 10:00:00 2024 GMT + Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Domain Validation CA - SHA256 - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a9:dd:cc:0e:b3:e2:32:39:dd:49:22:a8:13:69: + 93:87:88:e1:0c:ee:71:7d:bd:90:87:96:5d:59:f2: + cc:b3:d2:58:57:57:f9:46:ef:6c:26:d8:36:42:8e: + 7e:30:b3:2f:9a:3e:53:7b:1f:6e:b6:a2:4c:45:1f: + 3c:d3:15:93:1c:89:ed:3c:f4:57:de:ca:bd:ec:06: + 9a:6a:2a:a0:19:52:7f:51:d1:74:39:08:9f:ab:eb: + d7:86:13:15:97:ae:36:c3:54:66:0e:5a:f2:a0:73: + 85:31:e3:b2:64:14:6a:ff:a5:a2:8e:24:bb:bd:85: + 52:15:a2:79:ee:f0:b5:ee:3d:b8:f4:7d:80:bc:d9: + 90:35:65:b8:17:a9:ad:b3:98:9f:a0:7e:7d:6e:fb: + 3f:ad:7c:c2:1b:59:36:96:da:37:32:4b:4b:5d:35: + 02:63:8e:db:a7:cf:62:ee:cc:2e:d4:8d:c9:bd:3c: + 6a:91:72:a2:22:a7:72:2d:20:d1:fa:ca:37:da:18: + 98:e6:16:24:71:25:4b:c4:e5:7b:89:52:09:02:fd: + 59:2b:04:6e:ca:07:81:d4:b3:da:da:db:e3:cc:80: + a8:56:07:06:7c:96:08:37:9d:db:38:b6:62:34:91: + 62:07:74:01:38:d8:72:30:e2:eb:90:71:26:62:c0: + 57:f3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + EA:4E:7C:D4:80:2D:E5:15:81:86:26:8C:82:6D:C0:98:A4:CF:97:0F + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.globalsign.com/repository/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.globalsign.net/root.crl + + Authority Information Access: + OCSP - URI:http://ocsp.globalsign.com/rootr1 + + X509v3 Authority Key Identifier: + keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + + Signature Algorithm: sha256WithRSAEncryption + d7:45:9e:a0:dc:e0:e3:61:5a:0b:7d:77:84:17:2d:65:5a:82: + 9a:8d:a3:27:2a:85:f7:c9:ef:e9:86:fd:d4:47:cd:01:52:96: + c5:43:bd:37:b1:e1:b8:f2:a9:d2:8a:11:84:71:91:15:89:dc: + 02:9d:0b:cb:6c:33:85:34:28:9e:20:b2:b1:97:dc:6d:0b:10: + c1:3c:cd:5f:ea:5d:d7:98:31:c5:34:99:5c:00:61:55:c4:1b: + 02:5b:c5:e3:89:c8:b4:b8:6f:1e:38:f2:56:26:e9:41:ef:3d: + cd:ac:99:4f:59:4a:57:2d:4b:7d:ae:c7:88:fb:d6:98:3b:f5: + e5:f0:e8:89:89:b9:8b:03:cb:5a:23:1f:a4:fd:b8:ea:fb:2e: + 9d:ae:6a:73:09:bc:fc:d5:a0:b5:44:82:ab:44:91:2e:50:2e: + 57:c1:43:d8:91:04:8b:e9:11:2e:5f:b4:3f:79:df:1e:fb:3f: + 30:00:8b:53:e3:b7:2c:1d:3b:4d:8b:dc:e4:64:1d:04:58:33: + af:1b:55:e7:ab:0c:bf:30:04:74:e4:f3:0e:2f:30:39:8d:4b: + 04:8c:1e:75:66:66:49:e0:be:40:34:c7:5c:5a:51:92:ba:12: + 3c:52:d5:04:82:55:2d:67:a5:df:b7:95:7c:ee:3f:c3:08:ba: + 04:be:c0:46 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgILBAAAAAABRE7wPiAwDQYJKoZIhvcNAQELBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw +MDBaFw0yNDAyMjAxMDAwMDBaMGAxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMTYwNAYDVQQDEy1HbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0 +aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCp3cwOs+IyOd1JIqgTaZOHiOEM7nF9vZCHll1Z8syz0lhXV/lG72wm2DZC +jn4wsy+aPlN7H262okxFHzzTFZMcie089Ffeyr3sBppqKqAZUn9R0XQ5CJ+r69eG +ExWXrjbDVGYOWvKgc4Ux47JkFGr/paKOJLu9hVIVonnu8LXuPbj0fYC82ZA1ZbgX +qa2zmJ+gfn1u+z+tfMIbWTaW2jcyS0tdNQJjjtunz2LuzC7Ujcm9PGqRcqIip3It +INH6yjfaGJjmFiRxJUvE5XuJUgkC/VkrBG7KB4HUs9ra2+PMgKhWBwZ8lgg3nds4 +tmI0kWIHdAE42HIw4uuQcSZiwFfzAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMC +AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6k581IAt5RWBhiaMgm3A +mKTPlw8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v +d3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSG +Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEE +MTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290 +cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEL +BQADggEBANdFnqDc4ONhWgt9d4QXLWVagpqNoycqhffJ7+mG/dRHzQFSlsVDvTex +4bjyqdKKEYRxkRWJ3AKdC8tsM4U0KJ4gsrGX3G0LEME8zV/qXdeYMcU0mVwAYVXE +GwJbxeOJyLS4bx448lYm6UHvPc2smU9ZSlctS32ux4j71pg79eXw6ImJuYsDy1oj +H6T9uOr7Lp2uanMJvPzVoLVEgqtEkS5QLlfBQ9iRBIvpES5ftD953x77PzAAi1Pj +tywdO02L3ORkHQRYM68bVeerDL8wBHTk8w4vMDmNSwSMHnVmZkngvkA0x1xaUZK6 +EjxS1QSCVS1npd+3lXzuP8MIugS+wEY= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert12[] = { + 0x30, 0x82, 0x04, 0x63, 0x30, 0x82, 0x03, 0x4b, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0, + 0x3e, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, + 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x60, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, + 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, + 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, + 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xa9, 0xdd, 0xcc, 0x0e, 0xb3, 0xe2, 0x32, + 0x39, 0xdd, 0x49, 0x22, 0xa8, 0x13, 0x69, 0x93, 0x87, 0x88, 0xe1, 0x0c, + 0xee, 0x71, 0x7d, 0xbd, 0x90, 0x87, 0x96, 0x5d, 0x59, 0xf2, 0xcc, 0xb3, + 0xd2, 0x58, 0x57, 0x57, 0xf9, 0x46, 0xef, 0x6c, 0x26, 0xd8, 0x36, 0x42, + 0x8e, 0x7e, 0x30, 0xb3, 0x2f, 0x9a, 0x3e, 0x53, 0x7b, 0x1f, 0x6e, 0xb6, + 0xa2, 0x4c, 0x45, 0x1f, 0x3c, 0xd3, 0x15, 0x93, 0x1c, 0x89, 0xed, 0x3c, + 0xf4, 0x57, 0xde, 0xca, 0xbd, 0xec, 0x06, 0x9a, 0x6a, 0x2a, 0xa0, 0x19, + 0x52, 0x7f, 0x51, 0xd1, 0x74, 0x39, 0x08, 0x9f, 0xab, 0xeb, 0xd7, 0x86, + 0x13, 0x15, 0x97, 0xae, 0x36, 0xc3, 0x54, 0x66, 0x0e, 0x5a, 0xf2, 0xa0, + 0x73, 0x85, 0x31, 0xe3, 0xb2, 0x64, 0x14, 0x6a, 0xff, 0xa5, 0xa2, 0x8e, + 0x24, 0xbb, 0xbd, 0x85, 0x52, 0x15, 0xa2, 0x79, 0xee, 0xf0, 0xb5, 0xee, + 0x3d, 0xb8, 0xf4, 0x7d, 0x80, 0xbc, 0xd9, 0x90, 0x35, 0x65, 0xb8, 0x17, + 0xa9, 0xad, 0xb3, 0x98, 0x9f, 0xa0, 0x7e, 0x7d, 0x6e, 0xfb, 0x3f, 0xad, + 0x7c, 0xc2, 0x1b, 0x59, 0x36, 0x96, 0xda, 0x37, 0x32, 0x4b, 0x4b, 0x5d, + 0x35, 0x02, 0x63, 0x8e, 0xdb, 0xa7, 0xcf, 0x62, 0xee, 0xcc, 0x2e, 0xd4, + 0x8d, 0xc9, 0xbd, 0x3c, 0x6a, 0x91, 0x72, 0xa2, 0x22, 0xa7, 0x72, 0x2d, + 0x20, 0xd1, 0xfa, 0xca, 0x37, 0xda, 0x18, 0x98, 0xe6, 0x16, 0x24, 0x71, + 0x25, 0x4b, 0xc4, 0xe5, 0x7b, 0x89, 0x52, 0x09, 0x02, 0xfd, 0x59, 0x2b, + 0x04, 0x6e, 0xca, 0x07, 0x81, 0xd4, 0xb3, 0xda, 0xda, 0xdb, 0xe3, 0xcc, + 0x80, 0xa8, 0x56, 0x07, 0x06, 0x7c, 0x96, 0x08, 0x37, 0x9d, 0xdb, 0x38, + 0xb6, 0x62, 0x34, 0x91, 0x62, 0x07, 0x74, 0x01, 0x38, 0xd8, 0x72, 0x30, + 0xe2, 0xeb, 0x90, 0x71, 0x26, 0x62, 0xc0, 0x57, 0xf3, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25, 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xea, 0x4e, 0x7c, + 0xd4, 0x80, 0x2d, 0xe5, 0x15, 0x81, 0x86, 0x26, 0x8c, 0x82, 0x6d, 0xc0, + 0x98, 0xa4, 0xcf, 0x97, 0x0f, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, + 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, + 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, + 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, + 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, + 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, + 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, + 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, + 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xd7, 0x45, 0x9e, 0xa0, 0xdc, + 0xe0, 0xe3, 0x61, 0x5a, 0x0b, 0x7d, 0x77, 0x84, 0x17, 0x2d, 0x65, 0x5a, + 0x82, 0x9a, 0x8d, 0xa3, 0x27, 0x2a, 0x85, 0xf7, 0xc9, 0xef, 0xe9, 0x86, + 0xfd, 0xd4, 0x47, 0xcd, 0x01, 0x52, 0x96, 0xc5, 0x43, 0xbd, 0x37, 0xb1, + 0xe1, 0xb8, 0xf2, 0xa9, 0xd2, 0x8a, 0x11, 0x84, 0x71, 0x91, 0x15, 0x89, + 0xdc, 0x02, 0x9d, 0x0b, 0xcb, 0x6c, 0x33, 0x85, 0x34, 0x28, 0x9e, 0x20, + 0xb2, 0xb1, 0x97, 0xdc, 0x6d, 0x0b, 0x10, 0xc1, 0x3c, 0xcd, 0x5f, 0xea, + 0x5d, 0xd7, 0x98, 0x31, 0xc5, 0x34, 0x99, 0x5c, 0x00, 0x61, 0x55, 0xc4, + 0x1b, 0x02, 0x5b, 0xc5, 0xe3, 0x89, 0xc8, 0xb4, 0xb8, 0x6f, 0x1e, 0x38, + 0xf2, 0x56, 0x26, 0xe9, 0x41, 0xef, 0x3d, 0xcd, 0xac, 0x99, 0x4f, 0x59, + 0x4a, 0x57, 0x2d, 0x4b, 0x7d, 0xae, 0xc7, 0x88, 0xfb, 0xd6, 0x98, 0x3b, + 0xf5, 0xe5, 0xf0, 0xe8, 0x89, 0x89, 0xb9, 0x8b, 0x03, 0xcb, 0x5a, 0x23, + 0x1f, 0xa4, 0xfd, 0xb8, 0xea, 0xfb, 0x2e, 0x9d, 0xae, 0x6a, 0x73, 0x09, + 0xbc, 0xfc, 0xd5, 0xa0, 0xb5, 0x44, 0x82, 0xab, 0x44, 0x91, 0x2e, 0x50, + 0x2e, 0x57, 0xc1, 0x43, 0xd8, 0x91, 0x04, 0x8b, 0xe9, 0x11, 0x2e, 0x5f, + 0xb4, 0x3f, 0x79, 0xdf, 0x1e, 0xfb, 0x3f, 0x30, 0x00, 0x8b, 0x53, 0xe3, + 0xb7, 0x2c, 0x1d, 0x3b, 0x4d, 0x8b, 0xdc, 0xe4, 0x64, 0x1d, 0x04, 0x58, + 0x33, 0xaf, 0x1b, 0x55, 0xe7, 0xab, 0x0c, 0xbf, 0x30, 0x04, 0x74, 0xe4, + 0xf3, 0x0e, 0x2f, 0x30, 0x39, 0x8d, 0x4b, 0x04, 0x8c, 0x1e, 0x75, 0x66, + 0x66, 0x49, 0xe0, 0xbe, 0x40, 0x34, 0xc7, 0x5c, 0x5a, 0x51, 0x92, 0xba, + 0x12, 0x3c, 0x52, 0xd5, 0x04, 0x82, 0x55, 0x2d, 0x67, 0xa5, 0xdf, 0xb7, + 0x95, 0x7c, 0xee, 0x3f, 0xc3, 0x08, 0xba, 0x04, 0xbe, 0xc0, 0x46, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:00:00:00:00:01:44:4e:f0:42:47 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA + Validity + Not Before: Feb 20 10:00:00 2014 GMT + Not After : Feb 20 10:00:00 2024 GMT + Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Organization Validation CA - SHA256 - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c7:0e:6c:3f:23:93:7f:cc:70:a5:9d:20:c3:0e: + 53:3f:7e:c0:4e:c2:98:49:ca:47:d5:23:ef:03:34: + 85:74:c8:a3:02:2e:46:5c:0b:7d:c9:88:9d:4f:8b: + f0:f8:9c:6c:8c:55:35:db:bf:f2:b3:ea:fb:e3:56: + e7:4a:46:d9:13:22:ca:36:d5:9b:c1:a8:e3:96:43: + 93:f2:0c:bc:e6:f9:e6:e8:99:c8:63:48:78:7f:57: + 36:69:1a:19:1d:5a:d1:d4:7d:c2:9c:d4:7f:e1:80: + 12:ae:7a:ea:88:ea:57:d8:ca:0a:0a:3a:12:49:a2: + 62:19:7a:0d:24:f7:37:eb:b4:73:92:7b:05:23:9b: + 12:b5:ce:eb:29:df:a4:14:02:b9:01:a5:d4:a6:9c: + 43:64:88:de:f8:7e:fe:e3:f5:1e:e5:fe:dc:a3:a8: + e4:66:31:d9:4c:25:e9:18:b9:89:59:09:ae:e9:9d: + 1c:6d:37:0f:4a:1e:35:20:28:e2:af:d4:21:8b:01: + c4:45:ad:6e:2b:63:ab:92:6b:61:0a:4d:20:ed:73: + ba:7c:ce:fe:16:b5:db:9f:80:f0:d6:8b:6c:d9:08: + 79:4a:4f:78:65:da:92:bc:be:35:f9:b3:c4:f9:27: + 80:4e:ff:96:52:e6:02:20:e1:07:73:e9:5d:2b:bd: + b2:f1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + 96:DE:61:F1:BD:1C:16:29:53:1C:C0:CC:7D:3B:83:00:40:E6:1A:7C + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.globalsign.com/repository/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.globalsign.net/root.crl + + Authority Information Access: + OCSP - URI:http://ocsp.globalsign.com/rootr1 + + X509v3 Authority Key Identifier: + keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + + Signature Algorithm: sha256WithRSAEncryption + 46:2a:ee:5e:bd:ae:01:60:37:31:11:86:71:74:b6:46:49:c8: + 10:16:fe:2f:62:23:17:ab:1f:87:f8:82:ed:ca:df:0e:2c:df: + 64:75:8e:e5:18:72:a7:8c:3a:8b:c9:ac:a5:77:50:f7:ef:9e: + a4:e0:a0:8f:14:57:a3:2a:5f:ec:7e:6d:10:e6:ba:8d:b0:08: + 87:76:0e:4c:b2:d9:51:bb:11:02:f2:5c:dd:1c:bd:f3:55:96: + 0f:d4:06:c0:fc:e2:23:8a:24:70:d3:bb:f0:79:1a:a7:61:70: + 83:8a:af:06:c5:20:d8:a1:63:d0:6c:ae:4f:32:d7:ae:7c:18: + 45:75:05:29:77:df:42:40:64:64:86:be:2a:76:09:31:6f:1d: + 24:f4:99:d0:85:fe:f2:21:08:f9:c6:f6:f1:d0:59:ed:d6:56: + 3c:08:28:03:67:ba:f0:f9:f1:90:16:47:ae:67:e6:bc:80:48: + e9:42:76:34:97:55:69:24:0e:83:d6:a0:2d:b4:f5:f3:79:8a: + 49:28:74:1a:41:a1:c2:d3:24:88:35:30:60:94:17:b4:e1:04: + 22:31:3d:3b:2f:17:06:b2:b8:9d:86:2b:5a:69:ef:83:f5:4b: + c4:aa:b4:2a:f8:7c:a1:b1:85:94:8c:f4:0c:87:0c:f4:ac:40: + f8:59:49:98 +-----BEGIN CERTIFICATE----- +MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw +MDBaFw0yNDAyMjAxMDAwMDBaMGYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMTwwOgYDVQQDEzNHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBW +YWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDHDmw/I5N/zHClnSDDDlM/fsBOwphJykfVI+8DNIV0yKMCLkZc +C33JiJ1Pi/D4nGyMVTXbv/Kz6vvjVudKRtkTIso21ZvBqOOWQ5PyDLzm+ebomchj +SHh/VzZpGhkdWtHUfcKc1H/hgBKueuqI6lfYygoKOhJJomIZeg0k9zfrtHOSewUj +mxK1zusp36QUArkBpdSmnENkiN74fv7j9R7l/tyjqORmMdlMJekYuYlZCa7pnRxt +Nw9KHjUgKOKv1CGLAcRFrW4rY6uSa2EKTSDtc7p8zv4WtdufgPDWi2zZCHlKT3hl +2pK8vjX5s8T5J4BO/5ZS5gIg4Qdz6V0rvbLxAgMBAAGjggElMIIBITAOBgNVHQ8B +Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlt5h8b0cFilT +HMDMfTuDAEDmGnwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0 +dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCow +KKAmoCSGImh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYB +BQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNv +bS9yb290cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZI +hvcNAQELBQADggEBAEYq7l69rgFgNzERhnF0tkZJyBAW/i9iIxerH4f4gu3K3w4s +32R1juUYcqeMOovJrKV3UPfvnqTgoI8UV6MqX+x+bRDmuo2wCId2Dkyy2VG7EQLy +XN0cvfNVlg/UBsD84iOKJHDTu/B5GqdhcIOKrwbFINihY9Bsrk8y1658GEV1BSl3 +30JAZGSGvip2CTFvHST0mdCF/vIhCPnG9vHQWe3WVjwIKANnuvD58ZAWR65n5ryA +SOlCdjSXVWkkDoPWoC209fN5ikkodBpBocLTJIg1MGCUF7ThBCIxPTsvFwayuJ2G +K1pp74P1S8SqtCr4fKGxhZSM9AyHDPSsQPhZSZg= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert13[] = { + 0x30, 0x82, 0x04, 0x69, 0x30, 0x82, 0x03, 0x51, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0, + 0x42, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, + 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, + 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, + 0x31, 0x3c, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x33, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x4f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, + 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, + 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc7, + 0x0e, 0x6c, 0x3f, 0x23, 0x93, 0x7f, 0xcc, 0x70, 0xa5, 0x9d, 0x20, 0xc3, + 0x0e, 0x53, 0x3f, 0x7e, 0xc0, 0x4e, 0xc2, 0x98, 0x49, 0xca, 0x47, 0xd5, + 0x23, 0xef, 0x03, 0x34, 0x85, 0x74, 0xc8, 0xa3, 0x02, 0x2e, 0x46, 0x5c, + 0x0b, 0x7d, 0xc9, 0x88, 0x9d, 0x4f, 0x8b, 0xf0, 0xf8, 0x9c, 0x6c, 0x8c, + 0x55, 0x35, 0xdb, 0xbf, 0xf2, 0xb3, 0xea, 0xfb, 0xe3, 0x56, 0xe7, 0x4a, + 0x46, 0xd9, 0x13, 0x22, 0xca, 0x36, 0xd5, 0x9b, 0xc1, 0xa8, 0xe3, 0x96, + 0x43, 0x93, 0xf2, 0x0c, 0xbc, 0xe6, 0xf9, 0xe6, 0xe8, 0x99, 0xc8, 0x63, + 0x48, 0x78, 0x7f, 0x57, 0x36, 0x69, 0x1a, 0x19, 0x1d, 0x5a, 0xd1, 0xd4, + 0x7d, 0xc2, 0x9c, 0xd4, 0x7f, 0xe1, 0x80, 0x12, 0xae, 0x7a, 0xea, 0x88, + 0xea, 0x57, 0xd8, 0xca, 0x0a, 0x0a, 0x3a, 0x12, 0x49, 0xa2, 0x62, 0x19, + 0x7a, 0x0d, 0x24, 0xf7, 0x37, 0xeb, 0xb4, 0x73, 0x92, 0x7b, 0x05, 0x23, + 0x9b, 0x12, 0xb5, 0xce, 0xeb, 0x29, 0xdf, 0xa4, 0x14, 0x02, 0xb9, 0x01, + 0xa5, 0xd4, 0xa6, 0x9c, 0x43, 0x64, 0x88, 0xde, 0xf8, 0x7e, 0xfe, 0xe3, + 0xf5, 0x1e, 0xe5, 0xfe, 0xdc, 0xa3, 0xa8, 0xe4, 0x66, 0x31, 0xd9, 0x4c, + 0x25, 0xe9, 0x18, 0xb9, 0x89, 0x59, 0x09, 0xae, 0xe9, 0x9d, 0x1c, 0x6d, + 0x37, 0x0f, 0x4a, 0x1e, 0x35, 0x20, 0x28, 0xe2, 0xaf, 0xd4, 0x21, 0x8b, + 0x01, 0xc4, 0x45, 0xad, 0x6e, 0x2b, 0x63, 0xab, 0x92, 0x6b, 0x61, 0x0a, + 0x4d, 0x20, 0xed, 0x73, 0xba, 0x7c, 0xce, 0xfe, 0x16, 0xb5, 0xdb, 0x9f, + 0x80, 0xf0, 0xd6, 0x8b, 0x6c, 0xd9, 0x08, 0x79, 0x4a, 0x4f, 0x78, 0x65, + 0xda, 0x92, 0xbc, 0xbe, 0x35, 0xf9, 0xb3, 0xc4, 0xf9, 0x27, 0x80, 0x4e, + 0xff, 0x96, 0x52, 0xe6, 0x02, 0x20, 0xe1, 0x07, 0x73, 0xe9, 0x5d, 0x2b, + 0xbd, 0xb2, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25, + 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x96, 0xde, 0x61, 0xf1, 0xbd, 0x1c, 0x16, 0x29, 0x53, + 0x1c, 0xc0, 0xcc, 0x7d, 0x3b, 0x83, 0x00, 0x40, 0xe6, 0x1a, 0x7c, 0x30, + 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, + 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, + 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, + 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, + 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, + 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0x46, 0x2a, 0xee, 0x5e, 0xbd, 0xae, 0x01, 0x60, 0x37, 0x31, 0x11, + 0x86, 0x71, 0x74, 0xb6, 0x46, 0x49, 0xc8, 0x10, 0x16, 0xfe, 0x2f, 0x62, + 0x23, 0x17, 0xab, 0x1f, 0x87, 0xf8, 0x82, 0xed, 0xca, 0xdf, 0x0e, 0x2c, + 0xdf, 0x64, 0x75, 0x8e, 0xe5, 0x18, 0x72, 0xa7, 0x8c, 0x3a, 0x8b, 0xc9, + 0xac, 0xa5, 0x77, 0x50, 0xf7, 0xef, 0x9e, 0xa4, 0xe0, 0xa0, 0x8f, 0x14, + 0x57, 0xa3, 0x2a, 0x5f, 0xec, 0x7e, 0x6d, 0x10, 0xe6, 0xba, 0x8d, 0xb0, + 0x08, 0x87, 0x76, 0x0e, 0x4c, 0xb2, 0xd9, 0x51, 0xbb, 0x11, 0x02, 0xf2, + 0x5c, 0xdd, 0x1c, 0xbd, 0xf3, 0x55, 0x96, 0x0f, 0xd4, 0x06, 0xc0, 0xfc, + 0xe2, 0x23, 0x8a, 0x24, 0x70, 0xd3, 0xbb, 0xf0, 0x79, 0x1a, 0xa7, 0x61, + 0x70, 0x83, 0x8a, 0xaf, 0x06, 0xc5, 0x20, 0xd8, 0xa1, 0x63, 0xd0, 0x6c, + 0xae, 0x4f, 0x32, 0xd7, 0xae, 0x7c, 0x18, 0x45, 0x75, 0x05, 0x29, 0x77, + 0xdf, 0x42, 0x40, 0x64, 0x64, 0x86, 0xbe, 0x2a, 0x76, 0x09, 0x31, 0x6f, + 0x1d, 0x24, 0xf4, 0x99, 0xd0, 0x85, 0xfe, 0xf2, 0x21, 0x08, 0xf9, 0xc6, + 0xf6, 0xf1, 0xd0, 0x59, 0xed, 0xd6, 0x56, 0x3c, 0x08, 0x28, 0x03, 0x67, + 0xba, 0xf0, 0xf9, 0xf1, 0x90, 0x16, 0x47, 0xae, 0x67, 0xe6, 0xbc, 0x80, + 0x48, 0xe9, 0x42, 0x76, 0x34, 0x97, 0x55, 0x69, 0x24, 0x0e, 0x83, 0xd6, + 0xa0, 0x2d, 0xb4, 0xf5, 0xf3, 0x79, 0x8a, 0x49, 0x28, 0x74, 0x1a, 0x41, + 0xa1, 0xc2, 0xd3, 0x24, 0x88, 0x35, 0x30, 0x60, 0x94, 0x17, 0xb4, 0xe1, + 0x04, 0x22, 0x31, 0x3d, 0x3b, 0x2f, 0x17, 0x06, 0xb2, 0xb8, 0x9d, 0x86, + 0x2b, 0x5a, 0x69, 0xef, 0x83, 0xf5, 0x4b, 0xc4, 0xaa, 0xb4, 0x2a, 0xf8, + 0x7c, 0xa1, 0xb1, 0x85, 0x94, 0x8c, 0xf4, 0x0c, 0x87, 0x0c, 0xf4, 0xac, + 0x40, 0xf8, 0x59, 0x49, 0x98, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 4d:5f:2c:34:08:b2:4c:20:cd:6d:50:7e:24:4d:c9:ec + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Feb 8 00:00:00 2010 GMT + Not After : Feb 7 23:59:59 2020 GMT + Subject: C=US, O=Thawte, Inc., CN=Thawte SSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:99:e4:85:5b:76:49:7d:2f:05:d8:c5:ac:c8:c8: + a9:d3:dc:98:e6:d7:34:a6:2f:0c:f2:22:26:d8:a3: + c9:14:4c:8f:05:a4:45:e8:14:0c:58:90:05:1a:b7: + c5:c1:06:a5:80:af:bb:1d:49:6b:52:34:88:c3:59: + e7:ef:6b:c4:27:41:8c:2b:66:1d:d0:e0:a3:97:98: + 19:34:4b:41:d5:98:d5:c7:05:ad:a2:e4:d7:ed:0c: + ad:4f:c1:b5:b0:21:fd:3e:50:53:b2:c4:90:d0:d4: + 30:67:6c:9a:f1:0e:74:c4:c2:dc:8a:e8:97:ff:c9: + 92:ae:01:8a:56:0a:98:32:b0:00:23:ec:90:1a:60: + c3:ed:bb:3a:cb:0f:63:9f:0d:44:c9:52:e1:25:96: + bf:ed:50:95:89:7f:56:14:b1:b7:61:1d:1c:07:8c: + 3a:2c:f7:ff:80:de:39:45:d5:af:1a:d1:78:d8:c7: + 71:6a:a3:19:a7:32:50:21:e9:f2:0e:a1:c6:13:03: + 44:48:d1:66:a8:52:57:d7:11:b4:93:8b:e5:99:9f: + 5d:e7:78:51:e5:4d:f6:b7:59:b4:76:b5:09:37:4d: + 06:38:13:7a:1c:08:98:5c:c4:48:4a:cb:52:a0:a9: + f8:b1:9d:8e:7b:79:b0:20:2f:3c:96:a8:11:62:47: + bb:11 + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.thawte.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.thawte.com/ThawtePCA.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-9 + X509v3 Subject Key Identifier: + A7:A2:83:BB:34:45:40:3D:FC:D5:30:4F:12:B9:3E:A1:01:9F:F6:DB + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha1WithRSAEncryption + 80:22:80:e0:6c:c8:95:16:d7:57:26:87:f3:72:34:db:c6:72: + 56:27:3e:d3:96:f6:2e:25:91:a5:3e:33:97:a7:4b:e5:2f:fb: + 25:7d:2f:07:61:fa:6f:83:74:4c:4c:53:72:20:a4:7a:cf:51: + 51:56:81:88:b0:6d:1f:36:2c:c8:2b:b1:88:99:c1:fe:44:ab: + 48:51:7c:d8:f2:44:64:2a:d8:71:a7:fb:1a:2f:f9:19:8d:34: + b2:23:bf:c4:4c:55:1d:8e:44:e8:aa:5d:9a:dd:9f:fd:03:c7: + ba:24:43:8d:2d:47:44:db:f6:d8:98:c8:b2:f9:da:ef:ed:29: + 5c:69:12:fa:d1:23:96:0f:bf:9c:0d:f2:79:45:53:37:9a:56: + 2f:e8:57:10:70:f6:ee:89:0c:49:89:9a:c1:23:f5:c2:2a:cc: + 41:cf:22:ab:65:6e:b7:94:82:6d:2f:40:5f:58:de:eb:95:2b: + a6:72:68:52:19:91:2a:ae:75:9d:4e:92:e6:ca:de:54:ea:18: + ab:25:3c:e6:64:a6:79:1f:26:7d:61:ed:7d:d2:e5:71:55:d8: + 93:17:7c:14:38:30:3c:df:86:e3:4c:ad:49:e3:97:59:ce:1b: + 9b:2b:ce:dc:65:d4:0b:28:6b:4e:84:46:51:44:f7:33:08:2d: + 58:97:21:ae +-----BEGIN CERTIFICATE----- +MIIEbDCCA1SgAwIBAgIQTV8sNAiyTCDNbVB+JE3J7DANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTAwMjA4MDAwMDAwWhcNMjAw +MjA3MjM1OTU5WjA8MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMu +MRYwFAYDVQQDEw1UaGF3dGUgU1NMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAmeSFW3ZJfS8F2MWsyMip09yY5tc0pi8M8iIm2KPJFEyPBaRF6BQM +WJAFGrfFwQalgK+7HUlrUjSIw1nn72vEJ0GMK2Yd0OCjl5gZNEtB1ZjVxwWtouTX +7QytT8G1sCH9PlBTssSQ0NQwZ2ya8Q50xMLciuiX/8mSrgGKVgqYMrAAI+yQGmDD +7bs6yw9jnw1EyVLhJZa/7VCViX9WFLG3YR0cB4w6LPf/gN45RdWvGtF42MdxaqMZ +pzJQIenyDqHGEwNESNFmqFJX1xG0k4vlmZ9d53hR5U32t1m0drUJN00GOBN6HAiY +XMRISstSoKn4sZ2Oe3mwIC88lqgRYke7EQIDAQABo4H7MIH4MDIGCCsGAQUFBwEB +BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTASBgNVHRMB +Af8ECDAGAQH/AgEAMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudGhhd3Rl +LmNvbS9UaGF3dGVQQ0EuY3JsMA4GA1UdDwEB/wQEAwIBBjAoBgNVHREEITAfpB0w +GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItOTAdBgNVHQ4EFgQUp6KDuzRFQD38 +1TBPErk+oQGf9tswHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutXSFAwDQYJ +KoZIhvcNAQEFBQADggEBAIAigOBsyJUW11cmh/NyNNvGclYnPtOW9i4lkaU+M5en +S+Uv+yV9Lwdh+m+DdExMU3IgpHrPUVFWgYiwbR82LMgrsYiZwf5Eq0hRfNjyRGQq +2HGn+xov+RmNNLIjv8RMVR2OROiqXZrdn/0Dx7okQ40tR0Tb9tiYyLL52u/tKVxp +EvrRI5YPv5wN8nlFUzeaVi/oVxBw9u6JDEmJmsEj9cIqzEHPIqtlbreUgm0vQF9Y +3uuVK6ZyaFIZkSqudZ1OkubK3lTqGKslPOZkpnkfJn1h7X3S5XFV2JMXfBQ4MDzf +huNMrUnjl1nOG5srztxl1Asoa06ERlFE9zMILViXIa4= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert14[] = { + 0x30, 0x82, 0x04, 0x6c, 0x30, 0x82, 0x03, 0x54, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x4d, 0x5f, 0x2c, 0x34, 0x08, 0xb2, 0x4c, 0x20, 0xcd, + 0x6d, 0x50, 0x7e, 0x24, 0x4d, 0xc9, 0xec, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, + 0x32, 0x30, 0x37, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x3c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0d, 0x54, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xe4, 0x85, + 0x5b, 0x76, 0x49, 0x7d, 0x2f, 0x05, 0xd8, 0xc5, 0xac, 0xc8, 0xc8, 0xa9, + 0xd3, 0xdc, 0x98, 0xe6, 0xd7, 0x34, 0xa6, 0x2f, 0x0c, 0xf2, 0x22, 0x26, + 0xd8, 0xa3, 0xc9, 0x14, 0x4c, 0x8f, 0x05, 0xa4, 0x45, 0xe8, 0x14, 0x0c, + 0x58, 0x90, 0x05, 0x1a, 0xb7, 0xc5, 0xc1, 0x06, 0xa5, 0x80, 0xaf, 0xbb, + 0x1d, 0x49, 0x6b, 0x52, 0x34, 0x88, 0xc3, 0x59, 0xe7, 0xef, 0x6b, 0xc4, + 0x27, 0x41, 0x8c, 0x2b, 0x66, 0x1d, 0xd0, 0xe0, 0xa3, 0x97, 0x98, 0x19, + 0x34, 0x4b, 0x41, 0xd5, 0x98, 0xd5, 0xc7, 0x05, 0xad, 0xa2, 0xe4, 0xd7, + 0xed, 0x0c, 0xad, 0x4f, 0xc1, 0xb5, 0xb0, 0x21, 0xfd, 0x3e, 0x50, 0x53, + 0xb2, 0xc4, 0x90, 0xd0, 0xd4, 0x30, 0x67, 0x6c, 0x9a, 0xf1, 0x0e, 0x74, + 0xc4, 0xc2, 0xdc, 0x8a, 0xe8, 0x97, 0xff, 0xc9, 0x92, 0xae, 0x01, 0x8a, + 0x56, 0x0a, 0x98, 0x32, 0xb0, 0x00, 0x23, 0xec, 0x90, 0x1a, 0x60, 0xc3, + 0xed, 0xbb, 0x3a, 0xcb, 0x0f, 0x63, 0x9f, 0x0d, 0x44, 0xc9, 0x52, 0xe1, + 0x25, 0x96, 0xbf, 0xed, 0x50, 0x95, 0x89, 0x7f, 0x56, 0x14, 0xb1, 0xb7, + 0x61, 0x1d, 0x1c, 0x07, 0x8c, 0x3a, 0x2c, 0xf7, 0xff, 0x80, 0xde, 0x39, + 0x45, 0xd5, 0xaf, 0x1a, 0xd1, 0x78, 0xd8, 0xc7, 0x71, 0x6a, 0xa3, 0x19, + 0xa7, 0x32, 0x50, 0x21, 0xe9, 0xf2, 0x0e, 0xa1, 0xc6, 0x13, 0x03, 0x44, + 0x48, 0xd1, 0x66, 0xa8, 0x52, 0x57, 0xd7, 0x11, 0xb4, 0x93, 0x8b, 0xe5, + 0x99, 0x9f, 0x5d, 0xe7, 0x78, 0x51, 0xe5, 0x4d, 0xf6, 0xb7, 0x59, 0xb4, + 0x76, 0xb5, 0x09, 0x37, 0x4d, 0x06, 0x38, 0x13, 0x7a, 0x1c, 0x08, 0x98, + 0x5c, 0xc4, 0x48, 0x4a, 0xcb, 0x52, 0xa0, 0xa9, 0xf8, 0xb1, 0x9d, 0x8e, + 0x7b, 0x79, 0xb0, 0x20, 0x2f, 0x3c, 0x96, 0xa8, 0x11, 0x62, 0x47, 0xbb, + 0x11, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xfb, 0x30, 0x81, 0xf8, + 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, + 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, + 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, + 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x28, + 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30, + 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, + 0x2d, 0x32, 0x2d, 0x39, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0xa7, 0xa2, 0x83, 0xbb, 0x34, 0x45, 0x40, 0x3d, 0xfc, + 0xd5, 0x30, 0x4f, 0x12, 0xb9, 0x3e, 0xa1, 0x01, 0x9f, 0xf6, 0xdb, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, + 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x80, 0x22, 0x80, 0xe0, 0x6c, 0xc8, 0x95, 0x16, + 0xd7, 0x57, 0x26, 0x87, 0xf3, 0x72, 0x34, 0xdb, 0xc6, 0x72, 0x56, 0x27, + 0x3e, 0xd3, 0x96, 0xf6, 0x2e, 0x25, 0x91, 0xa5, 0x3e, 0x33, 0x97, 0xa7, + 0x4b, 0xe5, 0x2f, 0xfb, 0x25, 0x7d, 0x2f, 0x07, 0x61, 0xfa, 0x6f, 0x83, + 0x74, 0x4c, 0x4c, 0x53, 0x72, 0x20, 0xa4, 0x7a, 0xcf, 0x51, 0x51, 0x56, + 0x81, 0x88, 0xb0, 0x6d, 0x1f, 0x36, 0x2c, 0xc8, 0x2b, 0xb1, 0x88, 0x99, + 0xc1, 0xfe, 0x44, 0xab, 0x48, 0x51, 0x7c, 0xd8, 0xf2, 0x44, 0x64, 0x2a, + 0xd8, 0x71, 0xa7, 0xfb, 0x1a, 0x2f, 0xf9, 0x19, 0x8d, 0x34, 0xb2, 0x23, + 0xbf, 0xc4, 0x4c, 0x55, 0x1d, 0x8e, 0x44, 0xe8, 0xaa, 0x5d, 0x9a, 0xdd, + 0x9f, 0xfd, 0x03, 0xc7, 0xba, 0x24, 0x43, 0x8d, 0x2d, 0x47, 0x44, 0xdb, + 0xf6, 0xd8, 0x98, 0xc8, 0xb2, 0xf9, 0xda, 0xef, 0xed, 0x29, 0x5c, 0x69, + 0x12, 0xfa, 0xd1, 0x23, 0x96, 0x0f, 0xbf, 0x9c, 0x0d, 0xf2, 0x79, 0x45, + 0x53, 0x37, 0x9a, 0x56, 0x2f, 0xe8, 0x57, 0x10, 0x70, 0xf6, 0xee, 0x89, + 0x0c, 0x49, 0x89, 0x9a, 0xc1, 0x23, 0xf5, 0xc2, 0x2a, 0xcc, 0x41, 0xcf, + 0x22, 0xab, 0x65, 0x6e, 0xb7, 0x94, 0x82, 0x6d, 0x2f, 0x40, 0x5f, 0x58, + 0xde, 0xeb, 0x95, 0x2b, 0xa6, 0x72, 0x68, 0x52, 0x19, 0x91, 0x2a, 0xae, + 0x75, 0x9d, 0x4e, 0x92, 0xe6, 0xca, 0xde, 0x54, 0xea, 0x18, 0xab, 0x25, + 0x3c, 0xe6, 0x64, 0xa6, 0x79, 0x1f, 0x26, 0x7d, 0x61, 0xed, 0x7d, 0xd2, + 0xe5, 0x71, 0x55, 0xd8, 0x93, 0x17, 0x7c, 0x14, 0x38, 0x30, 0x3c, 0xdf, + 0x86, 0xe3, 0x4c, 0xad, 0x49, 0xe3, 0x97, 0x59, 0xce, 0x1b, 0x9b, 0x2b, + 0xce, 0xdc, 0x65, 0xd4, 0x0b, 0x28, 0x6b, 0x4e, 0x84, 0x46, 0x51, 0x44, + 0xf7, 0x33, 0x08, 0x2d, 0x58, 0x97, 0x21, 0xae, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 6e:8a:90:eb:cf:f0:44:8a:72:0d:08:05:d0:82:a5:44 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust EV SSL CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d9:b4:05:f2:38:67:0f:09:e7:7c:f5:63:2a:e5: + b9:5e:a8:11:ae:75:71:d9:4c:84:67:ad:89:5d:fc: + 28:3d:2a:b0:a5:d5:d4:e6:30:0a:84:d4:e4:18:cb: + 85:37:c5:46:71:eb:1c:7b:69:db:65:69:8c:30:05: + 3e:07:e1:6f:3c:c1:0b:61:e6:38:44:fc:bc:8c:2f: + 4e:75:57:f5:96:99:7c:3e:87:1f:0f:90:4b:70:c3: + 3f:39:45:3b:3a:6b:cb:bb:7b:40:54:d1:8b:4b:a1: + 72:d2:04:e9:e0:72:1a:93:11:7a:2f:f1:ab:9d:9c: + 98:58:ae:2c:ea:77:5f:2f:2e:87:af:b8:6b:e3:e2: + e2:3f:d6:3d:e0:96:44:df:11:55:63:52:2f:f4:26: + 78:c4:0f:20:4d:0a:c0:68:70:15:86:38:ee:b7:76: + 88:ab:18:8f:4f:35:1e:d4:8c:c9:db:7e:3d:44:d4: + 36:8c:c1:37:b5:59:5b:87:f9:e9:f1:d4:c5:28:bd: + 1d:dc:cc:96:72:d1:7a:a1:a7:20:b5:b8:af:f8:6e: + a5:60:7b:2b:8d:1f:ee:f4:2b:d6:69:cd:af:ca:80: + 58:29:e8:4c:00:20:8a:49:0a:6e:8e:8c:a8:d1:00: + 12:84:b6:c5:e2:95:a2:c0:3b:a4:6b:f0:82:d0:96: + 5d:25 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://g2.symcb.com + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.geotrust.com/resources/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g1.symcb.com/GeoTrustPCA.crl + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-538 + X509v3 Subject Key Identifier: + DE:CF:5C:50:B7:AE:02:1F:15:17:AA:16:E8:0D:B5:28:9D:6A:5A:F3 + X509v3 Authority Key Identifier: + keyid:2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92 + + Signature Algorithm: sha256WithRSAEncryption + b4:8e:bd:07:b9:9a:85:ec:3b:67:bd:07:60:61:e6:84:d1:d4: + ef:eb:1b:ba:0b:82:4b:95:64:b6:66:53:23:bd:b7:84:dd:e4: + 7b:8d:09:da:cf:b2:f5:f1:c3:bf:87:84:be:4e:a6:a8:c2:e7: + 12:39:28:34:e0:a4:56:44:40:0c:9f:88:a3:15:d3:e8:d3:5e: + e3:1c:04:60:fb:69:36:4f:6a:7e:0c:2a:28:c1:f3:aa:58:0e: + 6c:ce:1d:07:c3:4a:c0:9c:8d:c3:74:b1:ae:82:f0:1a:e1:f9: + 4e:29:bd:46:de:b7:1d:f9:7d:db:d9:0f:84:cb:92:45:cc:1c: + b3:18:f6:a0:cf:71:6f:0c:2e:9b:d2:2d:b3:99:93:83:44:ac: + 15:aa:9b:2e:67:ec:4f:88:69:05:56:7b:8b:b2:43:a9:3a:6c: + 1c:13:33:25:1b:fd:a8:c8:57:02:fb:1c:e0:d1:bd:3b:56:44: + 65:c3:63:f5:1b:ef:ec:30:d9:e3:6e:2e:13:e9:39:08:2a:0c: + 72:f3:9a:cc:f6:27:29:84:d3:ef:4c:c7:84:11:65:1f:c6:e3: + 81:03:db:87:cc:78:f7:b5:9d:96:3e:6a:7f:bc:11:85:7a:75: + e6:41:7d:0d:cf:f9:e5:85:69:25:8f:c7:8d:07:2d:f8:69:0f: + cb:41:53:00 +-----BEGIN CERTIFICATE----- +MIIEbjCCA1agAwIBAgIQboqQ68/wRIpyDQgF0IKlRDANBgkqhkiG9w0BAQsFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMzEw +MzEwMDAwMDBaFw0yMzEwMzAyMzU5NTlaMEcxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMSAwHgYDVQQDExdHZW9UcnVzdCBFViBTU0wgQ0EgLSBH +NDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANm0BfI4Zw8J53z1Yyrl +uV6oEa51cdlMhGetiV38KD0qsKXV1OYwCoTU5BjLhTfFRnHrHHtp22VpjDAFPgfh +bzzBC2HmOET8vIwvTnVX9ZaZfD6HHw+QS3DDPzlFOzpry7t7QFTRi0uhctIE6eBy +GpMRei/xq52cmFiuLOp3Xy8uh6+4a+Pi4j/WPeCWRN8RVWNSL/QmeMQPIE0KwGhw +FYY47rd2iKsYj081HtSMydt+PUTUNozBN7VZW4f56fHUxSi9HdzMlnLReqGnILW4 +r/hupWB7K40f7vQr1mnNr8qAWCnoTAAgikkKbo6MqNEAEoS2xeKVosA7pGvwgtCW +XSUCAwEAAaOCAUMwggE/MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQD +AgEGMC8GCCsGAQUFBwEBBCMwITAfBggrBgEFBQcwAYYTaHR0cDovL2cyLnN5bWNi +LmNvbTBHBgNVHSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93 +d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2cxLnN5bWNiLmNvbS9HZW9UcnVzdFBDQS5jcmwwKQYDVR0RBCIwIKQe +MBwxGjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTM4MB0GA1UdDgQWBBTez1xQt64C +HxUXqhboDbUonWpa8zAfBgNVHSMEGDAWgBQs1VBBlxWL8I82YVtK+2vZmckzkjAN +BgkqhkiG9w0BAQsFAAOCAQEAtI69B7mahew7Z70HYGHmhNHU7+sbuguCS5VktmZT +I723hN3ke40J2s+y9fHDv4eEvk6mqMLnEjkoNOCkVkRADJ+IoxXT6NNe4xwEYPtp +Nk9qfgwqKMHzqlgObM4dB8NKwJyNw3SxroLwGuH5Tim9Rt63Hfl929kPhMuSRcwc +sxj2oM9xbwwum9Its5mTg0SsFaqbLmfsT4hpBVZ7i7JDqTpsHBMzJRv9qMhXAvsc +4NG9O1ZEZcNj9Rvv7DDZ424uE+k5CCoMcvOazPYnKYTT70zHhBFlH8bjgQPbh8x4 +97Wdlj5qf7wRhXp15kF9Dc/55YVpJY/HjQct+GkPy0FTAA== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert15[] = { + 0x30, 0x82, 0x04, 0x6e, 0x30, 0x82, 0x03, 0x56, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x6e, 0x8a, 0x90, 0xeb, 0xcf, 0xf0, 0x44, 0x8a, 0x72, + 0x0d, 0x08, 0x05, 0xd0, 0x82, 0xa5, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x58, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, + 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, + 0x33, 0x31, 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, + 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, + 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, + 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, + 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd9, 0xb4, + 0x05, 0xf2, 0x38, 0x67, 0x0f, 0x09, 0xe7, 0x7c, 0xf5, 0x63, 0x2a, 0xe5, + 0xb9, 0x5e, 0xa8, 0x11, 0xae, 0x75, 0x71, 0xd9, 0x4c, 0x84, 0x67, 0xad, + 0x89, 0x5d, 0xfc, 0x28, 0x3d, 0x2a, 0xb0, 0xa5, 0xd5, 0xd4, 0xe6, 0x30, + 0x0a, 0x84, 0xd4, 0xe4, 0x18, 0xcb, 0x85, 0x37, 0xc5, 0x46, 0x71, 0xeb, + 0x1c, 0x7b, 0x69, 0xdb, 0x65, 0x69, 0x8c, 0x30, 0x05, 0x3e, 0x07, 0xe1, + 0x6f, 0x3c, 0xc1, 0x0b, 0x61, 0xe6, 0x38, 0x44, 0xfc, 0xbc, 0x8c, 0x2f, + 0x4e, 0x75, 0x57, 0xf5, 0x96, 0x99, 0x7c, 0x3e, 0x87, 0x1f, 0x0f, 0x90, + 0x4b, 0x70, 0xc3, 0x3f, 0x39, 0x45, 0x3b, 0x3a, 0x6b, 0xcb, 0xbb, 0x7b, + 0x40, 0x54, 0xd1, 0x8b, 0x4b, 0xa1, 0x72, 0xd2, 0x04, 0xe9, 0xe0, 0x72, + 0x1a, 0x93, 0x11, 0x7a, 0x2f, 0xf1, 0xab, 0x9d, 0x9c, 0x98, 0x58, 0xae, + 0x2c, 0xea, 0x77, 0x5f, 0x2f, 0x2e, 0x87, 0xaf, 0xb8, 0x6b, 0xe3, 0xe2, + 0xe2, 0x3f, 0xd6, 0x3d, 0xe0, 0x96, 0x44, 0xdf, 0x11, 0x55, 0x63, 0x52, + 0x2f, 0xf4, 0x26, 0x78, 0xc4, 0x0f, 0x20, 0x4d, 0x0a, 0xc0, 0x68, 0x70, + 0x15, 0x86, 0x38, 0xee, 0xb7, 0x76, 0x88, 0xab, 0x18, 0x8f, 0x4f, 0x35, + 0x1e, 0xd4, 0x8c, 0xc9, 0xdb, 0x7e, 0x3d, 0x44, 0xd4, 0x36, 0x8c, 0xc1, + 0x37, 0xb5, 0x59, 0x5b, 0x87, 0xf9, 0xe9, 0xf1, 0xd4, 0xc5, 0x28, 0xbd, + 0x1d, 0xdc, 0xcc, 0x96, 0x72, 0xd1, 0x7a, 0xa1, 0xa7, 0x20, 0xb5, 0xb8, + 0xaf, 0xf8, 0x6e, 0xa5, 0x60, 0x7b, 0x2b, 0x8d, 0x1f, 0xee, 0xf4, 0x2b, + 0xd6, 0x69, 0xcd, 0xaf, 0xca, 0x80, 0x58, 0x29, 0xe8, 0x4c, 0x00, 0x20, + 0x8a, 0x49, 0x0a, 0x6e, 0x8e, 0x8c, 0xa8, 0xd1, 0x00, 0x12, 0x84, 0xb6, + 0xc5, 0xe2, 0x95, 0xa2, 0xc0, 0x3b, 0xa4, 0x6b, 0xf0, 0x82, 0xd0, 0x96, + 0x5d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x43, 0x30, + 0x82, 0x01, 0x3f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, + 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79, + 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, + 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, + 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x38, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0xde, 0xcf, 0x5c, 0x50, 0xb7, 0xae, 0x02, + 0x1f, 0x15, 0x17, 0xaa, 0x16, 0xe8, 0x0d, 0xb5, 0x28, 0x9d, 0x6a, 0x5a, + 0xf3, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x2c, 0xd5, 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, + 0x61, 0x5b, 0x4a, 0xfb, 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xb4, 0x8e, 0xbd, 0x07, 0xb9, 0x9a, + 0x85, 0xec, 0x3b, 0x67, 0xbd, 0x07, 0x60, 0x61, 0xe6, 0x84, 0xd1, 0xd4, + 0xef, 0xeb, 0x1b, 0xba, 0x0b, 0x82, 0x4b, 0x95, 0x64, 0xb6, 0x66, 0x53, + 0x23, 0xbd, 0xb7, 0x84, 0xdd, 0xe4, 0x7b, 0x8d, 0x09, 0xda, 0xcf, 0xb2, + 0xf5, 0xf1, 0xc3, 0xbf, 0x87, 0x84, 0xbe, 0x4e, 0xa6, 0xa8, 0xc2, 0xe7, + 0x12, 0x39, 0x28, 0x34, 0xe0, 0xa4, 0x56, 0x44, 0x40, 0x0c, 0x9f, 0x88, + 0xa3, 0x15, 0xd3, 0xe8, 0xd3, 0x5e, 0xe3, 0x1c, 0x04, 0x60, 0xfb, 0x69, + 0x36, 0x4f, 0x6a, 0x7e, 0x0c, 0x2a, 0x28, 0xc1, 0xf3, 0xaa, 0x58, 0x0e, + 0x6c, 0xce, 0x1d, 0x07, 0xc3, 0x4a, 0xc0, 0x9c, 0x8d, 0xc3, 0x74, 0xb1, + 0xae, 0x82, 0xf0, 0x1a, 0xe1, 0xf9, 0x4e, 0x29, 0xbd, 0x46, 0xde, 0xb7, + 0x1d, 0xf9, 0x7d, 0xdb, 0xd9, 0x0f, 0x84, 0xcb, 0x92, 0x45, 0xcc, 0x1c, + 0xb3, 0x18, 0xf6, 0xa0, 0xcf, 0x71, 0x6f, 0x0c, 0x2e, 0x9b, 0xd2, 0x2d, + 0xb3, 0x99, 0x93, 0x83, 0x44, 0xac, 0x15, 0xaa, 0x9b, 0x2e, 0x67, 0xec, + 0x4f, 0x88, 0x69, 0x05, 0x56, 0x7b, 0x8b, 0xb2, 0x43, 0xa9, 0x3a, 0x6c, + 0x1c, 0x13, 0x33, 0x25, 0x1b, 0xfd, 0xa8, 0xc8, 0x57, 0x02, 0xfb, 0x1c, + 0xe0, 0xd1, 0xbd, 0x3b, 0x56, 0x44, 0x65, 0xc3, 0x63, 0xf5, 0x1b, 0xef, + 0xec, 0x30, 0xd9, 0xe3, 0x6e, 0x2e, 0x13, 0xe9, 0x39, 0x08, 0x2a, 0x0c, + 0x72, 0xf3, 0x9a, 0xcc, 0xf6, 0x27, 0x29, 0x84, 0xd3, 0xef, 0x4c, 0xc7, + 0x84, 0x11, 0x65, 0x1f, 0xc6, 0xe3, 0x81, 0x03, 0xdb, 0x87, 0xcc, 0x78, + 0xf7, 0xb5, 0x9d, 0x96, 0x3e, 0x6a, 0x7f, 0xbc, 0x11, 0x85, 0x7a, 0x75, + 0xe6, 0x41, 0x7d, 0x0d, 0xcf, 0xf9, 0xe5, 0x85, 0x69, 0x25, 0x8f, 0xc7, + 0x8d, 0x07, 0x2d, 0xf8, 0x69, 0x0f, 0xcb, 0x41, 0x53, 0x00, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 146035 (0x23a73) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA + Validity + Not Before: Jun 11 22:02:59 2014 GMT + Not After : May 20 22:02:59 2022 GMT + Subject: C=US, O=GeoTrust Inc., OU=Domain Validated SSL, CN=GeoTrust DV SSL CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b3:44:3a:6c:b0:ae:cb:14:f9:8c:19:74:34:5c: + a9:69:e3:88:53:77:a5:a7:ff:bd:d1:3c:0d:27:e4: + de:ad:7f:bc:d1:90:58:93:d6:a6:da:39:9c:ad:e1: + 0e:56:46:ee:95:9e:10:68:4c:9c:2b:f6:6a:3a:8b: + 80:81:87:06:57:25:1a:56:52:94:dd:90:eb:67:3b: + de:fa:ae:36:68:d3:62:69:f6:6c:82:24:44:4f:87: + 5c:98:11:95:64:6b:e8:0c:d1:dd:e6:27:97:ae:cc: + e2:91:6a:41:12:b6:ab:e5:cc:6e:cc:23:b8:63:8a: + 1f:31:93:2d:06:c4:f7:e8:3d:58:cd:97:08:46:6c: + 7b:74:c0:f8:fc:31:3b:a7:7f:d7:8f:b0:c9:15:63: + 50:7a:12:4d:f5:12:1e:a3:7e:55:e3:75:b7:ea:1e: + ea:31:2c:08:4e:d8:cb:43:74:89:24:bc:d2:0e:1e: + f0:db:05:24:f6:8a:bf:10:27:84:41:1a:f6:18:53: + ee:91:d0:54:17:d3:7d:3e:7e:b2:7d:a8:bf:db:b9: + 21:2a:f0:89:b9:08:6e:5a:b3:5e:ea:82:b8:7e:27: + 0b:cc:56:73:81:05:4f:e3:96:2d:71:d5:78:a7:60: + c3:d7:ec:aa:39:1a:05:39:82:81:e0:15:2c:35:d1: + ee:25 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E + + X509v3 Subject Key Identifier: + AD:65:22:85:90:D0:3B:E3:A1:49:8B:37:F9:F1:0B:1D:5F:17:A0:77 + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/crls/gtglobal.crl + + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-699 + Signature Algorithm: sha256WithRSAEncryption + 4e:27:b8:1a:c7:3b:dc:5d:bb:9e:1a:35:23:1e:88:55:90:d1: + ec:86:9c:88:b7:e0:1f:67:87:e2:7c:b5:43:03:0e:b6:02:e8: + e0:ff:86:84:19:71:e9:f2:4b:f5:9e:2e:2e:5e:db:ab:d6:1c: + 4e:c4:3e:b8:2c:78:86:71:10:ae:8d:c5:70:bf:a4:f9:89:e6: + b4:ed:e8:4b:ed:7c:09:2a:09:08:06:3e:d4:e1:de:82:92:0c: + 34:30:35:0a:c1:60:75:ca:b6:55:6b:aa:00:42:cb:3f:fb:10: + e1:fb:85:c1:21:90:72:2b:6e:c0:e8:9d:d9:b5:5a:50:8e:34: + 1e:bb:38:a7:3c:31:bd:7a:f2:43:8b:eb:16:ca:ad:9b:de:6b: + 1e:f8:4f:b6:5e:4a:29:1f:7a:14:ee:91:f4:94:4f:a4:bd:9b: + 76:7a:bc:f1:51:7a:96:a8:81:0e:83:87:3f:8b:ae:5e:32:9b: + 34:9e:b2:e7:db:2f:ec:02:a0:e1:fd:51:52:fe:2c:db:36:ba: + c1:d6:5e:4b:58:6d:de:c6:e1:e1:fa:9a:03:2c:5b:a2:e1:b3: + 9b:f9:36:ec:c1:73:fa:33:12:66:95:e3:69:10:b6:d7:aa:33: + fa:f6:9d:41:6d:96:2a:ba:be:83:31:41:7f:0c:0a:d2:69:d6: + fc:35:4c:c3 +-----BEGIN CERTIFICATE----- +MIIEbzCCA1egAwIBAgIDAjpzMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTQwNjExMjIwMjU5WhcNMjIwNTIwMjIwMjU5WjBmMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh +bGlkYXRlZCBTU0wxIDAeBgNVBAMTF0dlb1RydXN0IERWIFNTTCBDQSAtIEczMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs0Q6bLCuyxT5jBl0NFypaeOI +U3elp/+90TwNJ+TerX+80ZBYk9am2jmcreEOVkbulZ4QaEycK/ZqOouAgYcGVyUa +VlKU3ZDrZzve+q42aNNiafZsgiRET4dcmBGVZGvoDNHd5ieXrszikWpBErar5cxu +zCO4Y4ofMZMtBsT36D1YzZcIRmx7dMD4/DE7p3/Xj7DJFWNQehJN9RIeo35V43W3 +6h7qMSwITtjLQ3SJJLzSDh7w2wUk9oq/ECeEQRr2GFPukdBUF9N9Pn6yfai/27kh +KvCJuQhuWrNe6oK4ficLzFZzgQVP45YtcdV4p2DD1+yqORoFOYKB4BUsNdHuJQID +AQABo4IBSDCCAUQwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4wHQYD +VR0OBBYEFK1lIoWQ0DvjoUmLN/nxCx1fF6B3MBIGA1UdEwEB/wQIMAYBAf8CAQAw +DgYDVR0PAQH/BAQDAgEGMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9nLnN5bWNi +LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAuBggrBgEFBQcBAQQiMCAwHgYIKwYBBQUH +MAGGEmh0dHA6Ly9nLnN5bWNkLmNvbTBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYw +MzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2Vz +L2NwczApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS02OTkw +DQYJKoZIhvcNAQELBQADggEBAE4nuBrHO9xdu54aNSMeiFWQ0eyGnIi34B9nh+J8 +tUMDDrYC6OD/hoQZcenyS/WeLi5e26vWHE7EPrgseIZxEK6NxXC/pPmJ5rTt6Evt +fAkqCQgGPtTh3oKSDDQwNQrBYHXKtlVrqgBCyz/7EOH7hcEhkHIrbsDondm1WlCO +NB67OKc8Mb168kOL6xbKrZveax74T7ZeSikfehTukfSUT6S9m3Z6vPFRepaogQ6D +hz+Lrl4ymzSesufbL+wCoOH9UVL+LNs2usHWXktYbd7G4eH6mgMsW6Lhs5v5NuzB +c/ozEmaV42kQtteqM/r2nUFtliq6voMxQX8MCtJp1vw1TMM= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert16[] = { + 0x30, 0x82, 0x04, 0x6f, 0x30, 0x82, 0x03, 0x57, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x36, 0x31, 0x31, 0x32, 0x32, 0x30, 0x32, 0x35, 0x39, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x32, 0x30, 0x32, 0x35, 0x39, + 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x14, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, + 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x47, 0x65, + 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, + 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb3, 0x44, 0x3a, 0x6c, 0xb0, 0xae, + 0xcb, 0x14, 0xf9, 0x8c, 0x19, 0x74, 0x34, 0x5c, 0xa9, 0x69, 0xe3, 0x88, + 0x53, 0x77, 0xa5, 0xa7, 0xff, 0xbd, 0xd1, 0x3c, 0x0d, 0x27, 0xe4, 0xde, + 0xad, 0x7f, 0xbc, 0xd1, 0x90, 0x58, 0x93, 0xd6, 0xa6, 0xda, 0x39, 0x9c, + 0xad, 0xe1, 0x0e, 0x56, 0x46, 0xee, 0x95, 0x9e, 0x10, 0x68, 0x4c, 0x9c, + 0x2b, 0xf6, 0x6a, 0x3a, 0x8b, 0x80, 0x81, 0x87, 0x06, 0x57, 0x25, 0x1a, + 0x56, 0x52, 0x94, 0xdd, 0x90, 0xeb, 0x67, 0x3b, 0xde, 0xfa, 0xae, 0x36, + 0x68, 0xd3, 0x62, 0x69, 0xf6, 0x6c, 0x82, 0x24, 0x44, 0x4f, 0x87, 0x5c, + 0x98, 0x11, 0x95, 0x64, 0x6b, 0xe8, 0x0c, 0xd1, 0xdd, 0xe6, 0x27, 0x97, + 0xae, 0xcc, 0xe2, 0x91, 0x6a, 0x41, 0x12, 0xb6, 0xab, 0xe5, 0xcc, 0x6e, + 0xcc, 0x23, 0xb8, 0x63, 0x8a, 0x1f, 0x31, 0x93, 0x2d, 0x06, 0xc4, 0xf7, + 0xe8, 0x3d, 0x58, 0xcd, 0x97, 0x08, 0x46, 0x6c, 0x7b, 0x74, 0xc0, 0xf8, + 0xfc, 0x31, 0x3b, 0xa7, 0x7f, 0xd7, 0x8f, 0xb0, 0xc9, 0x15, 0x63, 0x50, + 0x7a, 0x12, 0x4d, 0xf5, 0x12, 0x1e, 0xa3, 0x7e, 0x55, 0xe3, 0x75, 0xb7, + 0xea, 0x1e, 0xea, 0x31, 0x2c, 0x08, 0x4e, 0xd8, 0xcb, 0x43, 0x74, 0x89, + 0x24, 0xbc, 0xd2, 0x0e, 0x1e, 0xf0, 0xdb, 0x05, 0x24, 0xf6, 0x8a, 0xbf, + 0x10, 0x27, 0x84, 0x41, 0x1a, 0xf6, 0x18, 0x53, 0xee, 0x91, 0xd0, 0x54, + 0x17, 0xd3, 0x7d, 0x3e, 0x7e, 0xb2, 0x7d, 0xa8, 0xbf, 0xdb, 0xb9, 0x21, + 0x2a, 0xf0, 0x89, 0xb9, 0x08, 0x6e, 0x5a, 0xb3, 0x5e, 0xea, 0x82, 0xb8, + 0x7e, 0x27, 0x0b, 0xcc, 0x56, 0x73, 0x81, 0x05, 0x4f, 0xe3, 0x96, 0x2d, + 0x71, 0xd5, 0x78, 0xa7, 0x60, 0xc3, 0xd7, 0xec, 0xaa, 0x39, 0x1a, 0x05, + 0x39, 0x82, 0x81, 0xe0, 0x15, 0x2c, 0x35, 0xd1, 0xee, 0x25, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x48, 0x30, 0x82, 0x01, 0x44, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11, + 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xad, 0x65, 0x22, 0x85, 0x90, + 0xd0, 0x3b, 0xe3, 0xa1, 0x49, 0x8b, 0x37, 0xf9, 0xf1, 0x0b, 0x1d, 0x5f, + 0x17, 0xa0, 0x77, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e, + 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x2e, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, + 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, + 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4c, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, + 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, + 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, + 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x36, 0x39, 0x39, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4e, 0x27, 0xb8, 0x1a, 0xc7, + 0x3b, 0xdc, 0x5d, 0xbb, 0x9e, 0x1a, 0x35, 0x23, 0x1e, 0x88, 0x55, 0x90, + 0xd1, 0xec, 0x86, 0x9c, 0x88, 0xb7, 0xe0, 0x1f, 0x67, 0x87, 0xe2, 0x7c, + 0xb5, 0x43, 0x03, 0x0e, 0xb6, 0x02, 0xe8, 0xe0, 0xff, 0x86, 0x84, 0x19, + 0x71, 0xe9, 0xf2, 0x4b, 0xf5, 0x9e, 0x2e, 0x2e, 0x5e, 0xdb, 0xab, 0xd6, + 0x1c, 0x4e, 0xc4, 0x3e, 0xb8, 0x2c, 0x78, 0x86, 0x71, 0x10, 0xae, 0x8d, + 0xc5, 0x70, 0xbf, 0xa4, 0xf9, 0x89, 0xe6, 0xb4, 0xed, 0xe8, 0x4b, 0xed, + 0x7c, 0x09, 0x2a, 0x09, 0x08, 0x06, 0x3e, 0xd4, 0xe1, 0xde, 0x82, 0x92, + 0x0c, 0x34, 0x30, 0x35, 0x0a, 0xc1, 0x60, 0x75, 0xca, 0xb6, 0x55, 0x6b, + 0xaa, 0x00, 0x42, 0xcb, 0x3f, 0xfb, 0x10, 0xe1, 0xfb, 0x85, 0xc1, 0x21, + 0x90, 0x72, 0x2b, 0x6e, 0xc0, 0xe8, 0x9d, 0xd9, 0xb5, 0x5a, 0x50, 0x8e, + 0x34, 0x1e, 0xbb, 0x38, 0xa7, 0x3c, 0x31, 0xbd, 0x7a, 0xf2, 0x43, 0x8b, + 0xeb, 0x16, 0xca, 0xad, 0x9b, 0xde, 0x6b, 0x1e, 0xf8, 0x4f, 0xb6, 0x5e, + 0x4a, 0x29, 0x1f, 0x7a, 0x14, 0xee, 0x91, 0xf4, 0x94, 0x4f, 0xa4, 0xbd, + 0x9b, 0x76, 0x7a, 0xbc, 0xf1, 0x51, 0x7a, 0x96, 0xa8, 0x81, 0x0e, 0x83, + 0x87, 0x3f, 0x8b, 0xae, 0x5e, 0x32, 0x9b, 0x34, 0x9e, 0xb2, 0xe7, 0xdb, + 0x2f, 0xec, 0x02, 0xa0, 0xe1, 0xfd, 0x51, 0x52, 0xfe, 0x2c, 0xdb, 0x36, + 0xba, 0xc1, 0xd6, 0x5e, 0x4b, 0x58, 0x6d, 0xde, 0xc6, 0xe1, 0xe1, 0xfa, + 0x9a, 0x03, 0x2c, 0x5b, 0xa2, 0xe1, 0xb3, 0x9b, 0xf9, 0x36, 0xec, 0xc1, + 0x73, 0xfa, 0x33, 0x12, 0x66, 0x95, 0xe3, 0x69, 0x10, 0xb6, 0xd7, 0xaa, + 0x33, 0xfa, 0xf6, 0x9d, 0x41, 0x6d, 0x96, 0x2a, 0xba, 0xbe, 0x83, 0x31, + 0x41, 0x7f, 0x0c, 0x0a, 0xd2, 0x69, 0xd6, 0xfc, 0x35, 0x4c, 0xc3, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 12037640545166866303 (0xa70e4a4c3482b77f) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority + Validity + Not Before: Sep 2 00:00:00 2009 GMT + Not After : Jun 28 17:39:16 2034 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Services Root Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d5:0c:3a:c4:2a:f9:4e:e2:f5:be:19:97:5f:8e: + 88:53:b1:1f:3f:cb:cf:9f:20:13:6d:29:3a:c8:0f: + 7d:3c:f7:6b:76:38:63:d9:36:60:a8:9b:5e:5c:00: + 80:b2:2f:59:7f:f6:87:f9:25:43:86:e7:69:1b:52: + 9a:90:e1:71:e3:d8:2d:0d:4e:6f:f6:c8:49:d9:b6: + f3:1a:56:ae:2b:b6:74:14:eb:cf:fb:26:e3:1a:ba: + 1d:96:2e:6a:3b:58:94:89:47:56:ff:25:a0:93:70: + 53:83:da:84:74:14:c3:67:9e:04:68:3a:df:8e:40: + 5a:1d:4a:4e:cf:43:91:3b:e7:56:d6:00:70:cb:52: + ee:7b:7d:ae:3a:e7:bc:31:f9:45:f6:c2:60:cf:13: + 59:02:2b:80:cc:34:47:df:b9:de:90:65:6d:02:cf: + 2c:91:a6:a6:e7:de:85:18:49:7c:66:4e:a3:3a:6d: + a9:b5:ee:34:2e:ba:0d:03:b8:33:df:47:eb:b1:6b: + 8d:25:d9:9b:ce:81:d1:45:46:32:96:70:87:de:02: + 0e:49:43:85:b6:6c:73:bb:64:ea:61:41:ac:c9:d4: + 54:df:87:2f:c7:22:b2:26:cc:9f:59:54:68:9f:fc: + be:2a:2f:c4:55:1c:75:40:60:17:85:02:55:39:8b: + 7f:05 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 9C:5F:00:DF:AA:01:D7:30:2B:38:88:A2:B8:6D:4A:9C:F2:11:91:83 + X509v3 Authority Key Identifier: + keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7 + + Authority Information Access: + OCSP - URI:http://o.ss2.us/ + CA Issuers - URI:http://x.ss2.us/x.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://s.ss2.us/r.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + + Signature Algorithm: sha256WithRSAEncryption + 23:1d:e3:8a:57:ca:7d:e9:17:79:4c:f1:1e:55:fd:cc:53:6e: + 3e:47:0f:df:c6:55:f2:b2:04:36:ed:80:1f:53:c4:5d:34:28: + 6b:be:c7:55:fc:67:ea:cb:3f:7f:90:b2:33:cd:1b:58:10:82: + 02:f8:f8:2f:f5:13:60:d4:05:ce:f1:81:08:c1:dd:a7:75:97: + 4f:18:b9:6d:de:f7:93:91:08:ba:7e:40:2c:ed:c1:ea:bb:76: + 9e:33:06:77:1d:0d:08:7f:53:dd:1b:64:ab:82:27:f1:69:d5: + 4d:5e:ae:f4:a1:c3:75:a7:58:44:2d:f2:3c:70:98:ac:ba:69: + b6:95:77:7f:0f:31:5e:2c:fc:a0:87:3a:47:69:f0:79:5f:f4: + 14:54:a4:95:5e:11:78:12:60:27:ce:9f:c2:77:ff:23:53:77: + 5d:ba:ff:ea:59:e7:db:cf:af:92:96:ef:24:9a:35:10:7a:9c: + 91:c6:0e:7d:99:f6:3f:19:df:f5:72:54:e1:15:a9:07:59:7b: + 83:bf:52:2e:46:8c:b2:00:64:76:1c:48:d3:d8:79:e8:6e:56: + cc:ae:2c:03:90:d7:19:38:99:e4:ca:09:19:5b:ff:07:96:b0: + a8:7f:34:49:df:56:a9:f7:b0:5f:ed:33:ed:8c:47:b7:30:03: + 5d:f4:03:8c +-----BEGIN CERTIFICATE----- +MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV +BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw +MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV +UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp +ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/ +y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N +Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo +Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C +zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J +Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB +AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O +BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV +rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u +c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud +HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG +BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G +VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1 +l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt +8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ +59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu +VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert17[] = { + 0x30, 0x82, 0x04, 0x75, 0x30, 0x82, 0x03, 0x5d, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xa7, 0x0e, 0x4a, 0x4c, 0x34, 0x82, 0xb7, 0x7f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x68, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, + 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, + 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, + 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x39, 0x30, 0x32, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x34, 0x30, 0x36, + 0x32, 0x38, 0x31, 0x37, 0x33, 0x39, 0x31, 0x36, 0x5a, 0x30, 0x81, 0x98, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, + 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, + 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, + 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x3b, 0x30, 0x39, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xd5, 0x0c, 0x3a, 0xc4, 0x2a, 0xf9, 0x4e, + 0xe2, 0xf5, 0xbe, 0x19, 0x97, 0x5f, 0x8e, 0x88, 0x53, 0xb1, 0x1f, 0x3f, + 0xcb, 0xcf, 0x9f, 0x20, 0x13, 0x6d, 0x29, 0x3a, 0xc8, 0x0f, 0x7d, 0x3c, + 0xf7, 0x6b, 0x76, 0x38, 0x63, 0xd9, 0x36, 0x60, 0xa8, 0x9b, 0x5e, 0x5c, + 0x00, 0x80, 0xb2, 0x2f, 0x59, 0x7f, 0xf6, 0x87, 0xf9, 0x25, 0x43, 0x86, + 0xe7, 0x69, 0x1b, 0x52, 0x9a, 0x90, 0xe1, 0x71, 0xe3, 0xd8, 0x2d, 0x0d, + 0x4e, 0x6f, 0xf6, 0xc8, 0x49, 0xd9, 0xb6, 0xf3, 0x1a, 0x56, 0xae, 0x2b, + 0xb6, 0x74, 0x14, 0xeb, 0xcf, 0xfb, 0x26, 0xe3, 0x1a, 0xba, 0x1d, 0x96, + 0x2e, 0x6a, 0x3b, 0x58, 0x94, 0x89, 0x47, 0x56, 0xff, 0x25, 0xa0, 0x93, + 0x70, 0x53, 0x83, 0xda, 0x84, 0x74, 0x14, 0xc3, 0x67, 0x9e, 0x04, 0x68, + 0x3a, 0xdf, 0x8e, 0x40, 0x5a, 0x1d, 0x4a, 0x4e, 0xcf, 0x43, 0x91, 0x3b, + 0xe7, 0x56, 0xd6, 0x00, 0x70, 0xcb, 0x52, 0xee, 0x7b, 0x7d, 0xae, 0x3a, + 0xe7, 0xbc, 0x31, 0xf9, 0x45, 0xf6, 0xc2, 0x60, 0xcf, 0x13, 0x59, 0x02, + 0x2b, 0x80, 0xcc, 0x34, 0x47, 0xdf, 0xb9, 0xde, 0x90, 0x65, 0x6d, 0x02, + 0xcf, 0x2c, 0x91, 0xa6, 0xa6, 0xe7, 0xde, 0x85, 0x18, 0x49, 0x7c, 0x66, + 0x4e, 0xa3, 0x3a, 0x6d, 0xa9, 0xb5, 0xee, 0x34, 0x2e, 0xba, 0x0d, 0x03, + 0xb8, 0x33, 0xdf, 0x47, 0xeb, 0xb1, 0x6b, 0x8d, 0x25, 0xd9, 0x9b, 0xce, + 0x81, 0xd1, 0x45, 0x46, 0x32, 0x96, 0x70, 0x87, 0xde, 0x02, 0x0e, 0x49, + 0x43, 0x85, 0xb6, 0x6c, 0x73, 0xbb, 0x64, 0xea, 0x61, 0x41, 0xac, 0xc9, + 0xd4, 0x54, 0xdf, 0x87, 0x2f, 0xc7, 0x22, 0xb2, 0x26, 0xcc, 0x9f, 0x59, + 0x54, 0x68, 0x9f, 0xfc, 0xbe, 0x2a, 0x2f, 0xc4, 0x55, 0x1c, 0x75, 0x40, + 0x60, 0x17, 0x85, 0x02, 0x55, 0x39, 0x8b, 0x7f, 0x05, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x81, 0xf0, 0x30, 0x81, 0xed, 0x30, 0x0f, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, + 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x9c, 0x5f, 0x00, 0xdf, 0xaa, 0x01, 0xd7, 0x30, + 0x2b, 0x38, 0x88, 0xa2, 0xb8, 0x6d, 0x4a, 0x9c, 0xf2, 0x11, 0x91, 0x83, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xbf, 0x5f, 0xb7, 0xd1, 0xce, 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55, + 0xac, 0xdc, 0xd7, 0x10, 0xc2, 0x0e, 0xa9, 0x88, 0xe7, 0x30, 0x4f, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x43, 0x30, + 0x41, 0x30, 0x1c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x01, 0x86, 0x10, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x2e, + 0x73, 0x73, 0x32, 0x2e, 0x75, 0x73, 0x2f, 0x30, 0x21, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x15, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x78, 0x2e, 0x73, 0x73, 0x32, 0x2e, 0x75, 0x73, + 0x2f, 0x78, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x26, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x1f, 0x30, 0x1d, 0x30, 0x1b, 0xa0, 0x19, 0xa0, 0x17, 0x86, + 0x15, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x2e, 0x73, 0x73, + 0x32, 0x2e, 0x75, 0x73, 0x2f, 0x72, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08, 0x30, 0x06, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0x23, 0x1d, 0xe3, 0x8a, 0x57, 0xca, 0x7d, 0xe9, 0x17, 0x79, 0x4c, + 0xf1, 0x1e, 0x55, 0xfd, 0xcc, 0x53, 0x6e, 0x3e, 0x47, 0x0f, 0xdf, 0xc6, + 0x55, 0xf2, 0xb2, 0x04, 0x36, 0xed, 0x80, 0x1f, 0x53, 0xc4, 0x5d, 0x34, + 0x28, 0x6b, 0xbe, 0xc7, 0x55, 0xfc, 0x67, 0xea, 0xcb, 0x3f, 0x7f, 0x90, + 0xb2, 0x33, 0xcd, 0x1b, 0x58, 0x10, 0x82, 0x02, 0xf8, 0xf8, 0x2f, 0xf5, + 0x13, 0x60, 0xd4, 0x05, 0xce, 0xf1, 0x81, 0x08, 0xc1, 0xdd, 0xa7, 0x75, + 0x97, 0x4f, 0x18, 0xb9, 0x6d, 0xde, 0xf7, 0x93, 0x91, 0x08, 0xba, 0x7e, + 0x40, 0x2c, 0xed, 0xc1, 0xea, 0xbb, 0x76, 0x9e, 0x33, 0x06, 0x77, 0x1d, + 0x0d, 0x08, 0x7f, 0x53, 0xdd, 0x1b, 0x64, 0xab, 0x82, 0x27, 0xf1, 0x69, + 0xd5, 0x4d, 0x5e, 0xae, 0xf4, 0xa1, 0xc3, 0x75, 0xa7, 0x58, 0x44, 0x2d, + 0xf2, 0x3c, 0x70, 0x98, 0xac, 0xba, 0x69, 0xb6, 0x95, 0x77, 0x7f, 0x0f, + 0x31, 0x5e, 0x2c, 0xfc, 0xa0, 0x87, 0x3a, 0x47, 0x69, 0xf0, 0x79, 0x5f, + 0xf4, 0x14, 0x54, 0xa4, 0x95, 0x5e, 0x11, 0x78, 0x12, 0x60, 0x27, 0xce, + 0x9f, 0xc2, 0x77, 0xff, 0x23, 0x53, 0x77, 0x5d, 0xba, 0xff, 0xea, 0x59, + 0xe7, 0xdb, 0xcf, 0xaf, 0x92, 0x96, 0xef, 0x24, 0x9a, 0x35, 0x10, 0x7a, + 0x9c, 0x91, 0xc6, 0x0e, 0x7d, 0x99, 0xf6, 0x3f, 0x19, 0xdf, 0xf5, 0x72, + 0x54, 0xe1, 0x15, 0xa9, 0x07, 0x59, 0x7b, 0x83, 0xbf, 0x52, 0x2e, 0x46, + 0x8c, 0xb2, 0x00, 0x64, 0x76, 0x1c, 0x48, 0xd3, 0xd8, 0x79, 0xe8, 0x6e, + 0x56, 0xcc, 0xae, 0x2c, 0x03, 0x90, 0xd7, 0x19, 0x38, 0x99, 0xe4, 0xca, + 0x09, 0x19, 0x5b, 0xff, 0x07, 0x96, 0xb0, 0xa8, 0x7f, 0x34, 0x49, 0xdf, + 0x56, 0xa9, 0xf7, 0xb0, 0x5f, 0xed, 0x33, 0xed, 0x8c, 0x47, 0xb7, 0x30, + 0x03, 0x5d, 0xf4, 0x03, 0x8c, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120038006 (0x727a276) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Validity + Not Before: Feb 27 18:09:27 2014 GMT + Not After : Jun 9 17:07:29 2020 GMT + Subject: C=JP, O=Cybertrust Japan Co., Ltd., CN=Cybertrust Japan Public CA G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:94:56:a3:45:44:54:aa:60:64:bf:b8:57:9f:4e: + db:d4:79:68:5f:13:05:f4:3f:cd:25:dd:3c:5e:58: + 77:1c:9d:e6:9f:e3:32:49:ef:02:3a:34:53:8d:52: + e5:e3:39:66:1f:e7:33:61:b6:27:c6:24:55:50:27: + 02:65:f0:b0:8c:41:8d:30:5e:47:5b:82:6f:c7:9c: + a3:28:43:6d:58:7b:c8:15:98:4e:25:6f:cb:76:27: + 5b:0b:2c:2c:b5:98:23:e7:8b:7c:fd:77:1a:c4:52: + ba:5d:19:ee:78:21:4d:21:9a:d9:12:7c:33:15:6b: + 1a:c9:81:ea:da:da:57:b7:d5:2f:ce:1f:4b:fc:b4: + 33:e0:a0:c9:94:27:bb:27:40:b6:90:db:ac:9e:75: + a6:11:2b:49:19:2d:c3:c2:43:07:09:bb:3d:6e:88: + a3:e3:8a:c5:d2:86:f6:65:5b:34:c3:9f:4c:02:e5: + 09:ba:2c:c6:76:66:eb:d1:76:25:f4:30:13:fb:58: + 60:a8:58:e3:51:6f:4b:08:04:61:8d:ac:a9:30:2f: + 52:41:a3:22:c1:33:59:ab:7b:59:f9:93:67:4b:c9: + 89:75:52:ef:29:49:34:93:1c:9c:93:73:9c:19:ce: + 5c:18:cd:4c:09:27:c1:3f:f5:49:ec:f4:e2:df:4b: + af:8f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6334.1.0 + CPS: http://cybertrust.omniroot.com/repository.cfm + + Authority Information Access: + OCSP - URI:http://ocsp.omniroot.com/baltimoreroot + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Authority Key Identifier: + keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl + + X509v3 Subject Key Identifier: + 73:A8:08:53:29:B8:15:FB:99:80:E5:C5:37:D8:F8:39:7B:A4:13:06 + Signature Algorithm: sha256WithRSAEncryption + 68:df:fe:72:54:4e:1b:fb:5c:6e:5a:45:46:cf:42:be:b2:02: + 9c:9d:90:6a:09:2e:b7:36:64:24:b6:b1:e2:48:67:ce:17:46: + 9b:23:75:78:11:f6:c6:09:38:42:62:96:97:30:7b:51:77:df: + 33:b5:00:51:29:d5:24:fe:b7:98:a2:ac:6c:a1:13:7f:ca:f3: + b7:a6:52:c2:16:0d:ec:3a:bf:a3:37:77:4f:ae:7b:55:1d:46: + e9:10:da:c3:b4:05:5c:5b:f6:48:21:00:89:f4:bb:38:8e:1e: + 33:f3:49:97:81:31:6c:16:74:08:91:17:c0:d3:25:b3:bc:c1: + 15:b5:a4:cd:84:4d:b9:c8:eb:c5:59:42:10:14:25:79:f8:db: + b6:d0:e6:d3:a0:14:7c:17:1c:20:1e:ed:99:90:65:c0:41:71: + c3:ab:3f:29:41:67:f9:e2:d1:98:e3:f8:df:3a:b8:ca:a3:6f: + 68:8b:6c:9f:6e:88:7c:9d:41:5c:ba:cb:19:05:83:9c:99:f4: + 1a:d2:24:69:57:0a:0f:7a:c3:1b:2c:4b:06:d3:2a:97:7e:07: + b0:f9:20:5a:b5:92:4b:5b:a8:eb:eb:36:33:47:36:da:72:9c: + bf:68:45:81:31:be:d2:fd:3b:e9:72:d5:70:dd:a6:de:5f:0d: + b6:5e:00:49 +-----BEGIN CERTIFICATE----- +MIIEeTCCA2GgAwIBAgIEByeidjANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDIyNzE4MDkyN1oX +DTIwMDYwOTE3MDcyOVowWjELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1 +c3QgSmFwYW4gQ28uLCBMdGQuMSYwJAYDVQQDEx1DeWJlcnRydXN0IEphcGFuIFB1 +YmxpYyBDQSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJRWo0VE +VKpgZL+4V59O29R5aF8TBfQ/zSXdPF5Ydxyd5p/jMknvAjo0U41S5eM5Zh/nM2G2 +J8YkVVAnAmXwsIxBjTBeR1uCb8ecoyhDbVh7yBWYTiVvy3YnWwssLLWYI+eLfP13 +GsRSul0Z7nghTSGa2RJ8MxVrGsmB6traV7fVL84fS/y0M+CgyZQnuydAtpDbrJ51 +phErSRktw8JDBwm7PW6Io+OKxdKG9mVbNMOfTALlCbosxnZm69F2JfQwE/tYYKhY +41FvSwgEYY2sqTAvUkGjIsEzWat7WfmTZ0vJiXVS7ylJNJMcnJNznBnOXBjNTAkn +wT/1Sez04t9Lr48CAwEAAaOCAUUwggFBMBIGA1UdEwEB/wQIMAYBAf8CAQAwUwYD +VR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEFBQcCARYtaHR0cDovL2N5YmVy +dHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnkuY2ZtMEIGCCsGAQUFBwEBBDYw +NDAyBggrBgEFBQcwAYYmaHR0cDovL29jc3Aub21uaXJvb3QuY29tL2JhbHRpbW9y +ZXJvb3QwDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaG +ezq1BE3wMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVz +dC5jb20vQ1JML09tbmlyb290MjAyNS5jcmwwHQYDVR0OBBYEFHOoCFMpuBX7mYDl +xTfY+Dl7pBMGMA0GCSqGSIb3DQEBCwUAA4IBAQBo3/5yVE4b+1xuWkVGz0K+sgKc +nZBqCS63NmQktrHiSGfOF0abI3V4EfbGCThCYpaXMHtRd98ztQBRKdUk/reYoqxs +oRN/yvO3plLCFg3sOr+jN3dPrntVHUbpENrDtAVcW/ZIIQCJ9Ls4jh4z80mXgTFs +FnQIkRfA0yWzvMEVtaTNhE25yOvFWUIQFCV5+Nu20ObToBR8FxwgHu2ZkGXAQXHD +qz8pQWf54tGY4/jfOrjKo29oi2yfboh8nUFcussZBYOcmfQa0iRpVwoPesMbLEsG +0yqXfgew+SBatZJLW6jr6zYzRzbacpy/aEWBMb7S/TvpctVw3abeXw22XgBJ +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert18[] = { + 0x30, 0x82, 0x04, 0x79, 0x30, 0x82, 0x03, 0x61, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0xa2, 0x76, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, + 0x30, 0x32, 0x32, 0x37, 0x31, 0x38, 0x30, 0x39, 0x32, 0x37, 0x5a, 0x17, + 0x0d, 0x32, 0x30, 0x30, 0x36, 0x30, 0x39, 0x31, 0x37, 0x30, 0x37, 0x32, + 0x39, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x1a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x20, 0x43, 0x6f, 0x2e, + 0x2c, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x1d, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x20, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x43, 0x41, 0x20, 0x47, 0x33, 0x30, 0x82, + 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, + 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x94, 0x56, 0xa3, 0x45, 0x44, + 0x54, 0xaa, 0x60, 0x64, 0xbf, 0xb8, 0x57, 0x9f, 0x4e, 0xdb, 0xd4, 0x79, + 0x68, 0x5f, 0x13, 0x05, 0xf4, 0x3f, 0xcd, 0x25, 0xdd, 0x3c, 0x5e, 0x58, + 0x77, 0x1c, 0x9d, 0xe6, 0x9f, 0xe3, 0x32, 0x49, 0xef, 0x02, 0x3a, 0x34, + 0x53, 0x8d, 0x52, 0xe5, 0xe3, 0x39, 0x66, 0x1f, 0xe7, 0x33, 0x61, 0xb6, + 0x27, 0xc6, 0x24, 0x55, 0x50, 0x27, 0x02, 0x65, 0xf0, 0xb0, 0x8c, 0x41, + 0x8d, 0x30, 0x5e, 0x47, 0x5b, 0x82, 0x6f, 0xc7, 0x9c, 0xa3, 0x28, 0x43, + 0x6d, 0x58, 0x7b, 0xc8, 0x15, 0x98, 0x4e, 0x25, 0x6f, 0xcb, 0x76, 0x27, + 0x5b, 0x0b, 0x2c, 0x2c, 0xb5, 0x98, 0x23, 0xe7, 0x8b, 0x7c, 0xfd, 0x77, + 0x1a, 0xc4, 0x52, 0xba, 0x5d, 0x19, 0xee, 0x78, 0x21, 0x4d, 0x21, 0x9a, + 0xd9, 0x12, 0x7c, 0x33, 0x15, 0x6b, 0x1a, 0xc9, 0x81, 0xea, 0xda, 0xda, + 0x57, 0xb7, 0xd5, 0x2f, 0xce, 0x1f, 0x4b, 0xfc, 0xb4, 0x33, 0xe0, 0xa0, + 0xc9, 0x94, 0x27, 0xbb, 0x27, 0x40, 0xb6, 0x90, 0xdb, 0xac, 0x9e, 0x75, + 0xa6, 0x11, 0x2b, 0x49, 0x19, 0x2d, 0xc3, 0xc2, 0x43, 0x07, 0x09, 0xbb, + 0x3d, 0x6e, 0x88, 0xa3, 0xe3, 0x8a, 0xc5, 0xd2, 0x86, 0xf6, 0x65, 0x5b, + 0x34, 0xc3, 0x9f, 0x4c, 0x02, 0xe5, 0x09, 0xba, 0x2c, 0xc6, 0x76, 0x66, + 0xeb, 0xd1, 0x76, 0x25, 0xf4, 0x30, 0x13, 0xfb, 0x58, 0x60, 0xa8, 0x58, + 0xe3, 0x51, 0x6f, 0x4b, 0x08, 0x04, 0x61, 0x8d, 0xac, 0xa9, 0x30, 0x2f, + 0x52, 0x41, 0xa3, 0x22, 0xc1, 0x33, 0x59, 0xab, 0x7b, 0x59, 0xf9, 0x93, + 0x67, 0x4b, 0xc9, 0x89, 0x75, 0x52, 0xef, 0x29, 0x49, 0x34, 0x93, 0x1c, + 0x9c, 0x93, 0x73, 0x9c, 0x19, 0xce, 0x5c, 0x18, 0xcd, 0x4c, 0x09, 0x27, + 0xc1, 0x3f, 0xf5, 0x49, 0xec, 0xf4, 0xe2, 0xdf, 0x4b, 0xaf, 0x8f, 0x02, + 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x45, 0x30, 0x82, 0x01, 0x41, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x53, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b, + 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, + 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x42, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x36, 0x30, + 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x01, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, + 0x73, 0x70, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, + 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, + 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, + 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, + 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, + 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, + 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, + 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x73, 0xa8, 0x08, 0x53, 0x29, 0xb8, 0x15, 0xfb, 0x99, 0x80, 0xe5, + 0xc5, 0x37, 0xd8, 0xf8, 0x39, 0x7b, 0xa4, 0x13, 0x06, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x68, 0xdf, 0xfe, 0x72, 0x54, 0x4e, 0x1b, + 0xfb, 0x5c, 0x6e, 0x5a, 0x45, 0x46, 0xcf, 0x42, 0xbe, 0xb2, 0x02, 0x9c, + 0x9d, 0x90, 0x6a, 0x09, 0x2e, 0xb7, 0x36, 0x64, 0x24, 0xb6, 0xb1, 0xe2, + 0x48, 0x67, 0xce, 0x17, 0x46, 0x9b, 0x23, 0x75, 0x78, 0x11, 0xf6, 0xc6, + 0x09, 0x38, 0x42, 0x62, 0x96, 0x97, 0x30, 0x7b, 0x51, 0x77, 0xdf, 0x33, + 0xb5, 0x00, 0x51, 0x29, 0xd5, 0x24, 0xfe, 0xb7, 0x98, 0xa2, 0xac, 0x6c, + 0xa1, 0x13, 0x7f, 0xca, 0xf3, 0xb7, 0xa6, 0x52, 0xc2, 0x16, 0x0d, 0xec, + 0x3a, 0xbf, 0xa3, 0x37, 0x77, 0x4f, 0xae, 0x7b, 0x55, 0x1d, 0x46, 0xe9, + 0x10, 0xda, 0xc3, 0xb4, 0x05, 0x5c, 0x5b, 0xf6, 0x48, 0x21, 0x00, 0x89, + 0xf4, 0xbb, 0x38, 0x8e, 0x1e, 0x33, 0xf3, 0x49, 0x97, 0x81, 0x31, 0x6c, + 0x16, 0x74, 0x08, 0x91, 0x17, 0xc0, 0xd3, 0x25, 0xb3, 0xbc, 0xc1, 0x15, + 0xb5, 0xa4, 0xcd, 0x84, 0x4d, 0xb9, 0xc8, 0xeb, 0xc5, 0x59, 0x42, 0x10, + 0x14, 0x25, 0x79, 0xf8, 0xdb, 0xb6, 0xd0, 0xe6, 0xd3, 0xa0, 0x14, 0x7c, + 0x17, 0x1c, 0x20, 0x1e, 0xed, 0x99, 0x90, 0x65, 0xc0, 0x41, 0x71, 0xc3, + 0xab, 0x3f, 0x29, 0x41, 0x67, 0xf9, 0xe2, 0xd1, 0x98, 0xe3, 0xf8, 0xdf, + 0x3a, 0xb8, 0xca, 0xa3, 0x6f, 0x68, 0x8b, 0x6c, 0x9f, 0x6e, 0x88, 0x7c, + 0x9d, 0x41, 0x5c, 0xba, 0xcb, 0x19, 0x05, 0x83, 0x9c, 0x99, 0xf4, 0x1a, + 0xd2, 0x24, 0x69, 0x57, 0x0a, 0x0f, 0x7a, 0xc3, 0x1b, 0x2c, 0x4b, 0x06, + 0xd3, 0x2a, 0x97, 0x7e, 0x07, 0xb0, 0xf9, 0x20, 0x5a, 0xb5, 0x92, 0x4b, + 0x5b, 0xa8, 0xeb, 0xeb, 0x36, 0x33, 0x47, 0x36, 0xda, 0x72, 0x9c, 0xbf, + 0x68, 0x45, 0x81, 0x31, 0xbe, 0xd2, 0xfd, 0x3b, 0xe9, 0x72, 0xd5, 0x70, + 0xdd, 0xa6, 0xde, 0x5f, 0x0d, 0xb6, 0x5e, 0x00, 0x49, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1828629 (0x1be715) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority + Validity + Not Before: Jan 1 07:00:00 2014 GMT + Not After : May 30 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bf:71:62:08:f1:fa:59:34:f7:1b:c9:18:a3:f7: + 80:49:58:e9:22:83:13:a6:c5:20:43:01:3b:84:f1: + e6:85:49:9f:27:ea:f6:84:1b:4e:a0:b4:db:70:98: + c7:32:01:b1:05:3e:07:4e:ee:f4:fa:4f:2f:59:30: + 22:e7:ab:19:56:6b:e2:80:07:fc:f3:16:75:80:39: + 51:7b:e5:f9:35:b6:74:4e:a9:8d:82:13:e4:b6:3f: + a9:03:83:fa:a2:be:8a:15:6a:7f:de:0b:c3:b6:19: + 14:05:ca:ea:c3:a8:04:94:3b:46:7c:32:0d:f3:00: + 66:22:c8:8d:69:6d:36:8c:11:18:b7:d3:b2:1c:60: + b4:38:fa:02:8c:ce:d3:dd:46:07:de:0a:3e:eb:5d: + 7c:c8:7c:fb:b0:2b:53:a4:92:62:69:51:25:05:61: + 1a:44:81:8c:2c:a9:43:96:23:df:ac:3a:81:9a:0e: + 29:c5:1c:a9:e9:5d:1e:b6:9e:9e:30:0a:39:ce:f1: + 88:80:fb:4b:5d:cc:32:ec:85:62:43:25:34:02:56: + 27:01:91:b4:3b:70:2a:3f:6e:b1:e8:9c:88:01:7d: + 9f:d4:f9:db:53:6d:60:9d:bf:2c:e7:58:ab:b8:5f: + 46:fc:ce:c4:1b:03:3c:09:eb:49:31:5c:69:46:b3: + e0:47 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE + X509v3 Authority Key Identifier: + keyid:D2:C4:B0:D2:91:D4:4C:11:71:B3:61:CB:3D:A1:FE:DD:A8:6A:D4:E3 + + Authority Information Access: + OCSP - URI:http://ocsp.godaddy.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.godaddy.com/gdroot.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.godaddy.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 59:0b:53:bd:92:86:11:a7:24:7b:ed:5b:31:cf:1d:1f:6c:70: + c5:b8:6e:be:4e:bb:f6:be:97:50:e1:30:7f:ba:28:5c:62:94: + c2:e3:7e:33:f7:fb:42:76:85:db:95:1c:8c:22:58:75:09:0c: + 88:65:67:39:0a:16:09:c5:a0:38:97:a4:c5:23:93:3f:b4:18: + a6:01:06:44:91:e3:a7:69:27:b4:5a:25:7f:3a:b7:32:cd:dd: + 84:ff:2a:38:29:33:a4:dd:67:b2:85:fe:a1:88:20:1c:50:89: + c8:dc:2a:f6:42:03:37:4c:e6:88:df:d5:af:24:f2:b1:c3:df: + cc:b5:ec:e0:99:5e:b7:49:54:20:3c:94:18:0c:c7:1c:52:18: + 49:a4:6d:e1:b3:58:0b:c9:d8:ec:d9:ae:1c:32:8e:28:70:0d: + e2:fe:a6:17:9e:84:0f:bd:57:70:b3:5a:e9:1f:a0:86:53:bb: + ef:7c:ff:69:0b:e0:48:c3:b7:93:0b:c8:0a:54:c4:ac:5d:14: + 67:37:6c:ca:a5:2f:31:08:37:aa:6e:6f:8c:bc:9b:e2:57:5d: + 24:81:af:97:97:9c:84:ad:6c:ac:37:4c:66:f3:61:91:11:20: + e4:be:30:9f:7a:a4:29:09:b0:e1:34:5f:64:77:18:40:51:df: + 8c:30:a6:af +-----BEGIN CERTIFICATE----- +MIIEfTCCA2WgAwIBAgIDG+cVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVT +MSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIEluYy4xMTAvBgNVBAsTKEdv +IERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMTAx +MDcwMDAwWhcNMzEwNTMwMDcwMDAwWjCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHku +Y29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1 +dGhvcml0eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3Fi +CPH6WTT3G8kYo/eASVjpIoMTpsUgQwE7hPHmhUmfJ+r2hBtOoLTbcJjHMgGxBT4H +Tu70+k8vWTAi56sZVmvigAf88xZ1gDlRe+X5NbZ0TqmNghPktj+pA4P6or6KFWp/ +3gvDthkUBcrqw6gElDtGfDIN8wBmIsiNaW02jBEYt9OyHGC0OPoCjM7T3UYH3go+ +6118yHz7sCtTpJJiaVElBWEaRIGMLKlDliPfrDqBmg4pxRyp6V0etp6eMAo5zvGI +gPtLXcwy7IViQyU0AlYnAZG0O3AqP26x6JyIAX2f1PnbU21gnb8s51iruF9G/M7E +GwM8CetJMVxpRrPgRwIDAQABo4IBFzCCARMwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9BUFuIMGU2g/eMB8GA1Ud +IwQYMBaAFNLEsNKR1EwRcbNhyz2h/t2oatTjMDQGCCsGAQUFBwEBBCgwJjAkBggr +BgEFBQcwAYYYaHR0cDovL29jc3AuZ29kYWRkeS5jb20vMDIGA1UdHwQrMCkwJ6Al +oCOGIWh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Ryb290LmNybDBGBgNVHSAEPzA9 +MDsGBFUdIAAwMzAxBggrBgEFBQcCARYlaHR0cHM6Ly9jZXJ0cy5nb2RhZGR5LmNv +bS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAWQtTvZKGEacke+1bMc8d +H2xwxbhuvk679r6XUOEwf7ooXGKUwuN+M/f7QnaF25UcjCJYdQkMiGVnOQoWCcWg +OJekxSOTP7QYpgEGRJHjp2kntFolfzq3Ms3dhP8qOCkzpN1nsoX+oYggHFCJyNwq +9kIDN0zmiN/VryTyscPfzLXs4Jlet0lUIDyUGAzHHFIYSaRt4bNYC8nY7NmuHDKO +KHAN4v6mF56ED71XcLNa6R+ghlO773z/aQvgSMO3kwvIClTErF0UZzdsyqUvMQg3 +qm5vjLyb4lddJIGvl5echK1srDdMZvNhkREg5L4wn3qkKQmw4TRfZHcYQFHfjDCm +rw== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert19[] = { + 0x30, 0x82, 0x04, 0x7d, 0x30, 0x82, 0x03, 0x65, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x1b, 0xe7, 0x15, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x63, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x54, + 0x68, 0x65, 0x20, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x47, 0x6f, + 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x31, 0x30, 0x31, + 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, + 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, + 0x83, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, + 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, + 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44, + 0x61, 0x64, 0x64, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf, 0x71, 0x62, + 0x08, 0xf1, 0xfa, 0x59, 0x34, 0xf7, 0x1b, 0xc9, 0x18, 0xa3, 0xf7, 0x80, + 0x49, 0x58, 0xe9, 0x22, 0x83, 0x13, 0xa6, 0xc5, 0x20, 0x43, 0x01, 0x3b, + 0x84, 0xf1, 0xe6, 0x85, 0x49, 0x9f, 0x27, 0xea, 0xf6, 0x84, 0x1b, 0x4e, + 0xa0, 0xb4, 0xdb, 0x70, 0x98, 0xc7, 0x32, 0x01, 0xb1, 0x05, 0x3e, 0x07, + 0x4e, 0xee, 0xf4, 0xfa, 0x4f, 0x2f, 0x59, 0x30, 0x22, 0xe7, 0xab, 0x19, + 0x56, 0x6b, 0xe2, 0x80, 0x07, 0xfc, 0xf3, 0x16, 0x75, 0x80, 0x39, 0x51, + 0x7b, 0xe5, 0xf9, 0x35, 0xb6, 0x74, 0x4e, 0xa9, 0x8d, 0x82, 0x13, 0xe4, + 0xb6, 0x3f, 0xa9, 0x03, 0x83, 0xfa, 0xa2, 0xbe, 0x8a, 0x15, 0x6a, 0x7f, + 0xde, 0x0b, 0xc3, 0xb6, 0x19, 0x14, 0x05, 0xca, 0xea, 0xc3, 0xa8, 0x04, + 0x94, 0x3b, 0x46, 0x7c, 0x32, 0x0d, 0xf3, 0x00, 0x66, 0x22, 0xc8, 0x8d, + 0x69, 0x6d, 0x36, 0x8c, 0x11, 0x18, 0xb7, 0xd3, 0xb2, 0x1c, 0x60, 0xb4, + 0x38, 0xfa, 0x02, 0x8c, 0xce, 0xd3, 0xdd, 0x46, 0x07, 0xde, 0x0a, 0x3e, + 0xeb, 0x5d, 0x7c, 0xc8, 0x7c, 0xfb, 0xb0, 0x2b, 0x53, 0xa4, 0x92, 0x62, + 0x69, 0x51, 0x25, 0x05, 0x61, 0x1a, 0x44, 0x81, 0x8c, 0x2c, 0xa9, 0x43, + 0x96, 0x23, 0xdf, 0xac, 0x3a, 0x81, 0x9a, 0x0e, 0x29, 0xc5, 0x1c, 0xa9, + 0xe9, 0x5d, 0x1e, 0xb6, 0x9e, 0x9e, 0x30, 0x0a, 0x39, 0xce, 0xf1, 0x88, + 0x80, 0xfb, 0x4b, 0x5d, 0xcc, 0x32, 0xec, 0x85, 0x62, 0x43, 0x25, 0x34, + 0x02, 0x56, 0x27, 0x01, 0x91, 0xb4, 0x3b, 0x70, 0x2a, 0x3f, 0x6e, 0xb1, + 0xe8, 0x9c, 0x88, 0x01, 0x7d, 0x9f, 0xd4, 0xf9, 0xdb, 0x53, 0x6d, 0x60, + 0x9d, 0xbf, 0x2c, 0xe7, 0x58, 0xab, 0xb8, 0x5f, 0x46, 0xfc, 0xce, 0xc4, + 0x1b, 0x03, 0x3c, 0x09, 0xeb, 0x49, 0x31, 0x5c, 0x69, 0x46, 0xb3, 0xe0, + 0x47, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x17, 0x30, 0x82, + 0x01, 0x13, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3a, 0x9a, + 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef, 0xf6, 0xbd, 0x05, 0x41, 0x6e, + 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd2, 0xc4, 0xb0, 0xd2, 0x91, + 0xd4, 0x4c, 0x11, 0x71, 0xb3, 0x61, 0xcb, 0x3d, 0xa1, 0xfe, 0xdd, 0xa8, + 0x6a, 0xd4, 0xe3, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x32, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, + 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, + 0x6c, 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, + 0x30, 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, + 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0x0b, 0x53, + 0xbd, 0x92, 0x86, 0x11, 0xa7, 0x24, 0x7b, 0xed, 0x5b, 0x31, 0xcf, 0x1d, + 0x1f, 0x6c, 0x70, 0xc5, 0xb8, 0x6e, 0xbe, 0x4e, 0xbb, 0xf6, 0xbe, 0x97, + 0x50, 0xe1, 0x30, 0x7f, 0xba, 0x28, 0x5c, 0x62, 0x94, 0xc2, 0xe3, 0x7e, + 0x33, 0xf7, 0xfb, 0x42, 0x76, 0x85, 0xdb, 0x95, 0x1c, 0x8c, 0x22, 0x58, + 0x75, 0x09, 0x0c, 0x88, 0x65, 0x67, 0x39, 0x0a, 0x16, 0x09, 0xc5, 0xa0, + 0x38, 0x97, 0xa4, 0xc5, 0x23, 0x93, 0x3f, 0xb4, 0x18, 0xa6, 0x01, 0x06, + 0x44, 0x91, 0xe3, 0xa7, 0x69, 0x27, 0xb4, 0x5a, 0x25, 0x7f, 0x3a, 0xb7, + 0x32, 0xcd, 0xdd, 0x84, 0xff, 0x2a, 0x38, 0x29, 0x33, 0xa4, 0xdd, 0x67, + 0xb2, 0x85, 0xfe, 0xa1, 0x88, 0x20, 0x1c, 0x50, 0x89, 0xc8, 0xdc, 0x2a, + 0xf6, 0x42, 0x03, 0x37, 0x4c, 0xe6, 0x88, 0xdf, 0xd5, 0xaf, 0x24, 0xf2, + 0xb1, 0xc3, 0xdf, 0xcc, 0xb5, 0xec, 0xe0, 0x99, 0x5e, 0xb7, 0x49, 0x54, + 0x20, 0x3c, 0x94, 0x18, 0x0c, 0xc7, 0x1c, 0x52, 0x18, 0x49, 0xa4, 0x6d, + 0xe1, 0xb3, 0x58, 0x0b, 0xc9, 0xd8, 0xec, 0xd9, 0xae, 0x1c, 0x32, 0x8e, + 0x28, 0x70, 0x0d, 0xe2, 0xfe, 0xa6, 0x17, 0x9e, 0x84, 0x0f, 0xbd, 0x57, + 0x70, 0xb3, 0x5a, 0xe9, 0x1f, 0xa0, 0x86, 0x53, 0xbb, 0xef, 0x7c, 0xff, + 0x69, 0x0b, 0xe0, 0x48, 0xc3, 0xb7, 0x93, 0x0b, 0xc8, 0x0a, 0x54, 0xc4, + 0xac, 0x5d, 0x14, 0x67, 0x37, 0x6c, 0xca, 0xa5, 0x2f, 0x31, 0x08, 0x37, + 0xaa, 0x6e, 0x6f, 0x8c, 0xbc, 0x9b, 0xe2, 0x57, 0x5d, 0x24, 0x81, 0xaf, + 0x97, 0x97, 0x9c, 0x84, 0xad, 0x6c, 0xac, 0x37, 0x4c, 0x66, 0xf3, 0x61, + 0x91, 0x11, 0x20, 0xe4, 0xbe, 0x30, 0x9f, 0x7a, 0xa4, 0x29, 0x09, 0xb0, + 0xe1, 0x34, 0x5f, 0x64, 0x77, 0x18, 0x40, 0x51, 0xdf, 0x8c, 0x30, 0xa6, + 0xaf, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 46:f0:8c:db:cf:2c:54:66:ef:33:01:dd:5f:34 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA + Validity + Not Before: Aug 19 00:00:00 2015 GMT + Not After : Aug 19 00:00:00 2025 GMT + Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign CloudSSL CA - SHA256 - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a3:c0:75:e1:32:98:e5:d9:ae:84:7c:8d:e8:23: + 5f:46:95:5b:4c:a2:25:70:d7:90:04:85:80:c9:b5: + f4:8a:65:4d:92:cb:a5:c4:42:a0:b6:79:25:31:ed: + f1:85:20:cd:13:51:3d:67:ac:97:4d:68:9b:33:86: + 5c:b3:7b:2d:aa:df:77:a0:61:d1:f5:3c:fb:9a:fc: + d3:d5:94:ca:c9:1e:80:1b:90:90:c8:ac:8d:f6:60: + 17:9c:31:b8:c5:61:a2:e2:6e:57:25:08:6f:24:99: + 99:cf:94:bf:c7:8b:6b:b0:1f:ca:14:fa:18:9b:6c: + 10:7c:99:2b:da:4a:63:e5:b2:4e:c2:fd:3e:10:0b: + 48:f4:77:0b:2f:f0:96:4b:3a:ee:bd:35:de:85:8d: + da:13:0e:ce:01:c4:71:d3:d3:77:c5:08:a6:60:39: + 25:a7:27:69:5c:83:d1:6f:76:78:ee:c5:44:5b:45: + bd:29:3b:e2:c6:09:0f:a2:be:2b:dc:e3:5c:da:5a: + 6f:8e:e7:c9:07:6b:7e:a1:c0:53:95:82:89:e0:78: + 5c:72:a8:6c:be:67:6b:ab:e7:33:d9:87:f2:f8:5c: + 27:f4:f6:2a:3b:87:ef:da:c2:47:da:bf:ac:eb:27: + 64:7b:4c:53:eb:34:e1:2f:9b:20:4d:54:12:6b:7d: + 28:bd + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + A9:2B:87:E1:CE:24:47:3B:1B:BF:CF:85:37:02:55:9D:0D:94:58:E6 + X509v3 Authority Key Identifier: + keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + + Authority Information Access: + OCSP - URI:http://ocsp.globalsign.com/rootr1 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.globalsign.com/root.crl + + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.4146.1.20 + Policy: 2.23.140.1.2.2 + CPS: https://www.globalsign.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + a2:1d:69:8a:0a:8e:c4:14:83:2a:2a:12:4d:39:27:90:4e:f0: + 8d:ac:d2:96:62:47:36:5e:92:d1:fa:c5:93:b5:37:07:65:29: + d2:f4:53:50:6b:c9:f4:fe:34:f5:dd:b8:1d:fa:fc:dc:14:ac: + 56:94:27:9c:42:aa:04:4d:b7:ed:58:d9:99:d2:49:e6:20:2f: + d3:a7:77:b8:2a:89:1a:ef:a7:cf:86:2d:d6:53:e9:0b:93:9c: + 4e:ab:d9:45:ee:a4:84:85:ff:34:e4:0e:c0:bb:a5:ce:5f:95: + 89:85:70:aa:c1:5d:ec:cf:2b:d3:d9:83:df:03:ca:81:a7:02: + 32:b7:77:61:10:25:4e:d9:74:f3:d9:79:82:b5:26:70:b4:52: + bc:8f:33:d7:8a:ae:19:d0:fc:92:ad:2f:ba:3c:a0:48:58:47: + 5e:fd:20:56:95:20:c1:72:1d:ab:66:99:a4:d5:78:37:48:1b: + 9f:b2:4c:37:67:7a:fd:42:d2:d3:56:9e:d3:1d:8e:c4:0c:68: + 96:b6:47:51:10:f7:7b:eb:15:09:64:f5:f9:f0:63:16:2d:3d: + df:23:42:3a:93:63:cc:ab:af:4f:57:06:c7:fe:14:55:62:ce: + 27:11:19:e1:f4:42:ed:22:30:6b:35:1a:4a:05:80:a4:65:df: + cc:cb:6f:d0 +-----BEGIN CERTIFICATE----- +MIIEizCCA3OgAwIBAgIORvCM288sVGbvMwHdXzQwDQYJKoZIhvcNAQELBQAwVzEL +MAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsT +B1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNTA4MTkw +MDAwMDBaFw0yNTA4MTkwMDAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBH +bG9iYWxTaWduIG52LXNhMS0wKwYDVQQDEyRHbG9iYWxTaWduIENsb3VkU1NMIENB +IC0gU0hBMjU2IC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj +wHXhMpjl2a6EfI3oI19GlVtMoiVw15AEhYDJtfSKZU2Sy6XEQqC2eSUx7fGFIM0T +UT1nrJdNaJszhlyzey2q33egYdH1PPua/NPVlMrJHoAbkJDIrI32YBecMbjFYaLi +blclCG8kmZnPlL/Hi2uwH8oU+hibbBB8mSvaSmPlsk7C/T4QC0j0dwsv8JZLOu69 +Nd6FjdoTDs4BxHHT03fFCKZgOSWnJ2lcg9FvdnjuxURbRb0pO+LGCQ+ivivc41za +Wm+O58kHa36hwFOVgongeFxyqGy+Z2ur5zPZh/L4XCf09io7h+/awkfav6zrJ2R7 +TFPrNOEvmyBNVBJrfSi9AgMBAAGjggFTMIIBTzAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAw +HQYDVR0OBBYEFKkrh+HOJEc7G7/PhTcCVZ0NlFjmMB8GA1UdIwQYMBaAFGB7ZhpF +DZfKiVAvfQTNNKj//P1LMD0GCCsGAQUFBwEBBDEwLzAtBggrBgEFBQcwAYYhaHR0 +cDovL29jc3AuZ2xvYmFsc2lnbi5jb20vcm9vdHIxMDMGA1UdHwQsMCowKKAmoCSG +Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC5jcmwwVgYDVR0gBE8wTTAL +BgkrBgEEAaAyARQwPgYGZ4EMAQICMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3 +Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQCi +HWmKCo7EFIMqKhJNOSeQTvCNrNKWYkc2XpLR+sWTtTcHZSnS9FNQa8n0/jT13bgd ++vzcFKxWlCecQqoETbftWNmZ0knmIC/Tp3e4Koka76fPhi3WU+kLk5xOq9lF7qSE +hf805A7Au6XOX5WJhXCqwV3szyvT2YPfA8qBpwIyt3dhECVO2XTz2XmCtSZwtFK8 +jzPXiq4Z0PySrS+6PKBIWEde/SBWlSDBch2rZpmk1Xg3SBufskw3Z3r9QtLTVp7T +HY7EDGiWtkdREPd76xUJZPX58GMWLT3fI0I6k2PMq69PVwbH/hRVYs4nERnh9ELt +IjBrNRpKBYCkZd/My2/Q +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert20[] = { + 0x30, 0x82, 0x04, 0x8b, 0x30, 0x82, 0x03, 0x73, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0e, 0x46, 0xf0, 0x8c, 0xdb, 0xcf, 0x2c, 0x54, 0x66, 0xef, + 0x33, 0x01, 0xdd, 0x5f, 0x34, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, + 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, + 0x73, 0x61, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x07, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x38, 0x31, 0x39, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x38, + 0x31, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x57, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, + 0x2d, 0x73, 0x61, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x24, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, + 0x20, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, + 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, + 0xc0, 0x75, 0xe1, 0x32, 0x98, 0xe5, 0xd9, 0xae, 0x84, 0x7c, 0x8d, 0xe8, + 0x23, 0x5f, 0x46, 0x95, 0x5b, 0x4c, 0xa2, 0x25, 0x70, 0xd7, 0x90, 0x04, + 0x85, 0x80, 0xc9, 0xb5, 0xf4, 0x8a, 0x65, 0x4d, 0x92, 0xcb, 0xa5, 0xc4, + 0x42, 0xa0, 0xb6, 0x79, 0x25, 0x31, 0xed, 0xf1, 0x85, 0x20, 0xcd, 0x13, + 0x51, 0x3d, 0x67, 0xac, 0x97, 0x4d, 0x68, 0x9b, 0x33, 0x86, 0x5c, 0xb3, + 0x7b, 0x2d, 0xaa, 0xdf, 0x77, 0xa0, 0x61, 0xd1, 0xf5, 0x3c, 0xfb, 0x9a, + 0xfc, 0xd3, 0xd5, 0x94, 0xca, 0xc9, 0x1e, 0x80, 0x1b, 0x90, 0x90, 0xc8, + 0xac, 0x8d, 0xf6, 0x60, 0x17, 0x9c, 0x31, 0xb8, 0xc5, 0x61, 0xa2, 0xe2, + 0x6e, 0x57, 0x25, 0x08, 0x6f, 0x24, 0x99, 0x99, 0xcf, 0x94, 0xbf, 0xc7, + 0x8b, 0x6b, 0xb0, 0x1f, 0xca, 0x14, 0xfa, 0x18, 0x9b, 0x6c, 0x10, 0x7c, + 0x99, 0x2b, 0xda, 0x4a, 0x63, 0xe5, 0xb2, 0x4e, 0xc2, 0xfd, 0x3e, 0x10, + 0x0b, 0x48, 0xf4, 0x77, 0x0b, 0x2f, 0xf0, 0x96, 0x4b, 0x3a, 0xee, 0xbd, + 0x35, 0xde, 0x85, 0x8d, 0xda, 0x13, 0x0e, 0xce, 0x01, 0xc4, 0x71, 0xd3, + 0xd3, 0x77, 0xc5, 0x08, 0xa6, 0x60, 0x39, 0x25, 0xa7, 0x27, 0x69, 0x5c, + 0x83, 0xd1, 0x6f, 0x76, 0x78, 0xee, 0xc5, 0x44, 0x5b, 0x45, 0xbd, 0x29, + 0x3b, 0xe2, 0xc6, 0x09, 0x0f, 0xa2, 0xbe, 0x2b, 0xdc, 0xe3, 0x5c, 0xda, + 0x5a, 0x6f, 0x8e, 0xe7, 0xc9, 0x07, 0x6b, 0x7e, 0xa1, 0xc0, 0x53, 0x95, + 0x82, 0x89, 0xe0, 0x78, 0x5c, 0x72, 0xa8, 0x6c, 0xbe, 0x67, 0x6b, 0xab, + 0xe7, 0x33, 0xd9, 0x87, 0xf2, 0xf8, 0x5c, 0x27, 0xf4, 0xf6, 0x2a, 0x3b, + 0x87, 0xef, 0xda, 0xc2, 0x47, 0xda, 0xbf, 0xac, 0xeb, 0x27, 0x64, 0x7b, + 0x4c, 0x53, 0xeb, 0x34, 0xe1, 0x2f, 0x9b, 0x20, 0x4d, 0x54, 0x12, 0x6b, + 0x7d, 0x28, 0xbd, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x53, + 0x30, 0x82, 0x01, 0x4f, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x03, 0x02, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa9, 0x2b, + 0x87, 0xe1, 0xce, 0x24, 0x47, 0x3b, 0x1b, 0xbf, 0xcf, 0x85, 0x37, 0x02, + 0x55, 0x9d, 0x0d, 0x94, 0x58, 0xe6, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, + 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, + 0xfc, 0xfd, 0x4b, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, + 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x56, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4f, 0x30, 0x4d, 0x30, 0x0b, + 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0, 0x32, 0x01, 0x14, 0x30, + 0x3e, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, 0x34, 0x30, + 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, + 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa2, + 0x1d, 0x69, 0x8a, 0x0a, 0x8e, 0xc4, 0x14, 0x83, 0x2a, 0x2a, 0x12, 0x4d, + 0x39, 0x27, 0x90, 0x4e, 0xf0, 0x8d, 0xac, 0xd2, 0x96, 0x62, 0x47, 0x36, + 0x5e, 0x92, 0xd1, 0xfa, 0xc5, 0x93, 0xb5, 0x37, 0x07, 0x65, 0x29, 0xd2, + 0xf4, 0x53, 0x50, 0x6b, 0xc9, 0xf4, 0xfe, 0x34, 0xf5, 0xdd, 0xb8, 0x1d, + 0xfa, 0xfc, 0xdc, 0x14, 0xac, 0x56, 0x94, 0x27, 0x9c, 0x42, 0xaa, 0x04, + 0x4d, 0xb7, 0xed, 0x58, 0xd9, 0x99, 0xd2, 0x49, 0xe6, 0x20, 0x2f, 0xd3, + 0xa7, 0x77, 0xb8, 0x2a, 0x89, 0x1a, 0xef, 0xa7, 0xcf, 0x86, 0x2d, 0xd6, + 0x53, 0xe9, 0x0b, 0x93, 0x9c, 0x4e, 0xab, 0xd9, 0x45, 0xee, 0xa4, 0x84, + 0x85, 0xff, 0x34, 0xe4, 0x0e, 0xc0, 0xbb, 0xa5, 0xce, 0x5f, 0x95, 0x89, + 0x85, 0x70, 0xaa, 0xc1, 0x5d, 0xec, 0xcf, 0x2b, 0xd3, 0xd9, 0x83, 0xdf, + 0x03, 0xca, 0x81, 0xa7, 0x02, 0x32, 0xb7, 0x77, 0x61, 0x10, 0x25, 0x4e, + 0xd9, 0x74, 0xf3, 0xd9, 0x79, 0x82, 0xb5, 0x26, 0x70, 0xb4, 0x52, 0xbc, + 0x8f, 0x33, 0xd7, 0x8a, 0xae, 0x19, 0xd0, 0xfc, 0x92, 0xad, 0x2f, 0xba, + 0x3c, 0xa0, 0x48, 0x58, 0x47, 0x5e, 0xfd, 0x20, 0x56, 0x95, 0x20, 0xc1, + 0x72, 0x1d, 0xab, 0x66, 0x99, 0xa4, 0xd5, 0x78, 0x37, 0x48, 0x1b, 0x9f, + 0xb2, 0x4c, 0x37, 0x67, 0x7a, 0xfd, 0x42, 0xd2, 0xd3, 0x56, 0x9e, 0xd3, + 0x1d, 0x8e, 0xc4, 0x0c, 0x68, 0x96, 0xb6, 0x47, 0x51, 0x10, 0xf7, 0x7b, + 0xeb, 0x15, 0x09, 0x64, 0xf5, 0xf9, 0xf0, 0x63, 0x16, 0x2d, 0x3d, 0xdf, + 0x23, 0x42, 0x3a, 0x93, 0x63, 0xcc, 0xab, 0xaf, 0x4f, 0x57, 0x06, 0xc7, + 0xfe, 0x14, 0x55, 0x62, 0xce, 0x27, 0x11, 0x19, 0xe1, 0xf4, 0x42, 0xed, + 0x22, 0x30, 0x6b, 0x35, 0x1a, 0x4a, 0x05, 0x80, 0xa4, 0x65, 0xdf, 0xcc, + 0xcb, 0x6f, 0xd0, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 1b:09:3b:78:60:96:da:37:bb:a4:51:94:46:c8:96:78 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority + Validity + Not Before: Nov 8 00:00:00 2006 GMT + Not After : Nov 7 23:59:59 2021 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b: + 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57: + 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8: + 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe: + 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d: + a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59: + 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49: + d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69: + 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96: + bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5: + f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02: + ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6: + f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19: + 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d: + 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95: + ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f: + 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8: + 25:15 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.verisign.com/pca3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.verisign.com/cps + + X509v3 Subject Key Identifier: + 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + Authority Information Access: + OCSP - URI:http://ocsp.verisign.com + + Signature Algorithm: sha1WithRSAEncryption + a3:cd:7d:1e:f7:c7:75:8d:48:e7:56:34:4c:00:90:75:a9:51: + a5:56:c1:6d:bc:fe:f5:53:22:e9:98:a2:ac:9a:7e:70:1e:b3: + 8e:3b:45:e3:86:95:31:da:6d:4c:fb:34:50:80:96:cd:24:f2: + 40:df:04:3f:e2:65:ce:34:22:61:15:ea:66:70:64:d2:f1:6e: + f3:ca:18:59:6a:41:46:7e:82:de:19:b0:70:31:56:69:0d:0c: + e6:1d:9d:71:58:dc:cc:de:62:f5:e1:7a:10:02:d8:7a:dc:3b: + fa:57:bd:c9:e9:8f:46:21:39:9f:51:65:4c:8e:3a:be:28:41: + 70:1d +-----BEGIN CERTIFICATE----- +MIIEkDCCA/mgAwIBAgIQGwk7eGCW2je7pFGURsiWeDANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT +LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv +ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8 +RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb +ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR +TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH +iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB +AAGjggFbMIIBVzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0 +dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9 +BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy +aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI +KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU +j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t +L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEAo819HvfHdY1I51Y0 +TACQdalRpVbBbbz+9VMi6ZiirJp+cB6zjjtF44aVMdptTPs0UICWzSTyQN8EP+Jl +zjQiYRXqZnBk0vFu88oYWWpBRn6C3hmwcDFWaQ0M5h2dcVjczN5i9eF6EALYetw7 ++le9yemPRiE5n1FlTI46vihBcB0= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert21[] = { + 0x30, 0x82, 0x04, 0x90, 0x30, 0x82, 0x03, 0xf9, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x1b, 0x09, 0x3b, 0x78, 0x60, 0x96, 0xda, 0x37, 0xbb, + 0xa4, 0x51, 0x94, 0x46, 0xc8, 0x96, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, + 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30, + 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, + 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, + 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, + 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30, + 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35, + 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c, + 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3, + 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22, + 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1, + 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb, + 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0, + 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85, + 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33, + 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51, + 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74, + 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0, + 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06, + 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff, + 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4, + 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19, + 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe, + 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47, + 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5, + 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14, + 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f, + 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5b, 0x30, 0x82, 0x01, 0x57, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, + 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, + 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, + 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, + 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, + 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, + 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, + 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, + 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0xa3, 0xcd, 0x7d, 0x1e, 0xf7, 0xc7, 0x75, 0x8d, 0x48, 0xe7, 0x56, 0x34, + 0x4c, 0x00, 0x90, 0x75, 0xa9, 0x51, 0xa5, 0x56, 0xc1, 0x6d, 0xbc, 0xfe, + 0xf5, 0x53, 0x22, 0xe9, 0x98, 0xa2, 0xac, 0x9a, 0x7e, 0x70, 0x1e, 0xb3, + 0x8e, 0x3b, 0x45, 0xe3, 0x86, 0x95, 0x31, 0xda, 0x6d, 0x4c, 0xfb, 0x34, + 0x50, 0x80, 0x96, 0xcd, 0x24, 0xf2, 0x40, 0xdf, 0x04, 0x3f, 0xe2, 0x65, + 0xce, 0x34, 0x22, 0x61, 0x15, 0xea, 0x66, 0x70, 0x64, 0xd2, 0xf1, 0x6e, + 0xf3, 0xca, 0x18, 0x59, 0x6a, 0x41, 0x46, 0x7e, 0x82, 0xde, 0x19, 0xb0, + 0x70, 0x31, 0x56, 0x69, 0x0d, 0x0c, 0xe6, 0x1d, 0x9d, 0x71, 0x58, 0xdc, + 0xcc, 0xde, 0x62, 0xf5, 0xe1, 0x7a, 0x10, 0x02, 0xd8, 0x7a, 0xdc, 0x3b, + 0xfa, 0x57, 0xbd, 0xc9, 0xe9, 0x8f, 0x46, 0x21, 0x39, 0x9f, 0x51, 0x65, + 0x4c, 0x8e, 0x3a, 0xbe, 0x28, 0x41, 0x70, 0x1d, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 0a:01:41:42:00:00:01:53:85:73:6a:0b:85:ec:a7:08 + Signature Algorithm: sha256WithRSAEncryption + Issuer: O=Digital Signature Trust Co., CN=DST Root CA X3 + Validity + Not Before: Mar 17 16:40:46 2016 GMT + Not After : Mar 17 16:40:46 2021 GMT + Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9c:d3:0c:f0:5a:e5:2e:47:b7:72:5d:37:83:b3: + 68:63:30:ea:d7:35:26:19:25:e1:bd:be:35:f1:70: + 92:2f:b7:b8:4b:41:05:ab:a9:9e:35:08:58:ec:b1: + 2a:c4:68:87:0b:a3:e3:75:e4:e6:f3:a7:62:71:ba: + 79:81:60:1f:d7:91:9a:9f:f3:d0:78:67:71:c8:69: + 0e:95:91:cf:fe:e6:99:e9:60:3c:48:cc:7e:ca:4d: + 77:12:24:9d:47:1b:5a:eb:b9:ec:1e:37:00:1c:9c: + ac:7b:a7:05:ea:ce:4a:eb:bd:41:e5:36:98:b9:cb: + fd:6d:3c:96:68:df:23:2a:42:90:0c:86:74:67:c8: + 7f:a5:9a:b8:52:61:14:13:3f:65:e9:82:87:cb:db: + fa:0e:56:f6:86:89:f3:85:3f:97:86:af:b0:dc:1a: + ef:6b:0d:95:16:7d:c4:2b:a0:65:b2:99:04:36:75: + 80:6b:ac:4a:f3:1b:90:49:78:2f:a2:96:4f:2a:20: + 25:29:04:c6:74:c0:d0:31:cd:8f:31:38:95:16:ba: + a8:33:b8:43:f1:b1:1f:c3:30:7f:a2:79:31:13:3d: + 2d:36:f8:e3:fc:f2:33:6a:b9:39:31:c5:af:c4:8d: + 0d:1d:64:16:33:aa:fa:84:29:b6:d4:0b:c0:d8:7d: + c3:93 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://isrg.trustid.ocsp.identrust.com + CA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c + + X509v3 Authority Key Identifier: + keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10 + + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 + Policy: 1.3.6.1.4.1.44947.1.1.1 + CPS: http://cps.root-x1.letsencrypt.org + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl + + X509v3 Subject Key Identifier: + A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1 + Signature Algorithm: sha256WithRSAEncryption + dd:33:d7:11:f3:63:58:38:dd:18:15:fb:09:55:be:76:56:b9: + 70:48:a5:69:47:27:7b:c2:24:08:92:f1:5a:1f:4a:12:29:37: + 24:74:51:1c:62:68:b8:cd:95:70:67:e5:f7:a4:bc:4e:28:51: + cd:9b:e8:ae:87:9d:ea:d8:ba:5a:a1:01:9a:dc:f0:dd:6a:1d: + 6a:d8:3e:57:23:9e:a6:1e:04:62:9a:ff:d7:05:ca:b7:1f:3f: + c0:0a:48:bc:94:b0:b6:65:62:e0:c1:54:e5:a3:2a:ad:20:c4: + e9:e6:bb:dc:c8:f6:b5:c3:32:a3:98:cc:77:a8:e6:79:65:07: + 2b:cb:28:fe:3a:16:52:81:ce:52:0c:2e:5f:83:e8:d5:06:33: + fb:77:6c:ce:40:ea:32:9e:1f:92:5c:41:c1:74:6c:5b:5d:0a: + 5f:33:cc:4d:9f:ac:38:f0:2f:7b:2c:62:9d:d9:a3:91:6f:25: + 1b:2f:90:b1:19:46:3d:f6:7e:1b:a6:7a:87:b9:a3:7a:6d:18: + fa:25:a5:91:87:15:e0:f2:16:2f:58:b0:06:2f:2c:68:26:c6: + 4b:98:cd:da:9f:0c:f9:7f:90:ed:43:4a:12:44:4e:6f:73:7a: + 28:ea:a4:aa:6e:7b:4c:7d:87:dd:e0:c9:02:44:a7:87:af:c3: + 34:5b:b4:42 +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert22[] = { + 0x30, 0x82, 0x04, 0x92, 0x30, 0x82, 0x03, 0x7a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x0a, 0x01, 0x41, 0x42, 0x00, 0x00, 0x01, 0x53, 0x85, + 0x73, 0x6a, 0x0b, 0x85, 0xec, 0xa7, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x3f, + 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x44, + 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, + 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x20, 0x58, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x33, 0x31, + 0x37, 0x31, 0x36, 0x34, 0x30, 0x34, 0x36, 0x5a, 0x17, 0x0d, 0x32, 0x31, + 0x30, 0x33, 0x31, 0x37, 0x31, 0x36, 0x34, 0x30, 0x34, 0x36, 0x5a, 0x30, + 0x4a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x1a, 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x20, 0x58, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0x9c, 0xd3, 0x0c, 0xf0, 0x5a, 0xe5, 0x2e, 0x47, 0xb7, 0x72, 0x5d, 0x37, + 0x83, 0xb3, 0x68, 0x63, 0x30, 0xea, 0xd7, 0x35, 0x26, 0x19, 0x25, 0xe1, + 0xbd, 0xbe, 0x35, 0xf1, 0x70, 0x92, 0x2f, 0xb7, 0xb8, 0x4b, 0x41, 0x05, + 0xab, 0xa9, 0x9e, 0x35, 0x08, 0x58, 0xec, 0xb1, 0x2a, 0xc4, 0x68, 0x87, + 0x0b, 0xa3, 0xe3, 0x75, 0xe4, 0xe6, 0xf3, 0xa7, 0x62, 0x71, 0xba, 0x79, + 0x81, 0x60, 0x1f, 0xd7, 0x91, 0x9a, 0x9f, 0xf3, 0xd0, 0x78, 0x67, 0x71, + 0xc8, 0x69, 0x0e, 0x95, 0x91, 0xcf, 0xfe, 0xe6, 0x99, 0xe9, 0x60, 0x3c, + 0x48, 0xcc, 0x7e, 0xca, 0x4d, 0x77, 0x12, 0x24, 0x9d, 0x47, 0x1b, 0x5a, + 0xeb, 0xb9, 0xec, 0x1e, 0x37, 0x00, 0x1c, 0x9c, 0xac, 0x7b, 0xa7, 0x05, + 0xea, 0xce, 0x4a, 0xeb, 0xbd, 0x41, 0xe5, 0x36, 0x98, 0xb9, 0xcb, 0xfd, + 0x6d, 0x3c, 0x96, 0x68, 0xdf, 0x23, 0x2a, 0x42, 0x90, 0x0c, 0x86, 0x74, + 0x67, 0xc8, 0x7f, 0xa5, 0x9a, 0xb8, 0x52, 0x61, 0x14, 0x13, 0x3f, 0x65, + 0xe9, 0x82, 0x87, 0xcb, 0xdb, 0xfa, 0x0e, 0x56, 0xf6, 0x86, 0x89, 0xf3, + 0x85, 0x3f, 0x97, 0x86, 0xaf, 0xb0, 0xdc, 0x1a, 0xef, 0x6b, 0x0d, 0x95, + 0x16, 0x7d, 0xc4, 0x2b, 0xa0, 0x65, 0xb2, 0x99, 0x04, 0x36, 0x75, 0x80, + 0x6b, 0xac, 0x4a, 0xf3, 0x1b, 0x90, 0x49, 0x78, 0x2f, 0xa2, 0x96, 0x4f, + 0x2a, 0x20, 0x25, 0x29, 0x04, 0xc6, 0x74, 0xc0, 0xd0, 0x31, 0xcd, 0x8f, + 0x31, 0x38, 0x95, 0x16, 0xba, 0xa8, 0x33, 0xb8, 0x43, 0xf1, 0xb1, 0x1f, + 0xc3, 0x30, 0x7f, 0xa2, 0x79, 0x31, 0x13, 0x3d, 0x2d, 0x36, 0xf8, 0xe3, + 0xfc, 0xf2, 0x33, 0x6a, 0xb9, 0x39, 0x31, 0xc5, 0xaf, 0xc4, 0x8d, 0x0d, + 0x1d, 0x64, 0x16, 0x33, 0xaa, 0xfa, 0x84, 0x29, 0xb6, 0xd4, 0x0b, 0xc0, + 0xd8, 0x7d, 0xc3, 0x93, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, + 0x7d, 0x30, 0x82, 0x01, 0x79, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x32, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x26, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x73, 0x72, 0x67, 0x2e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x69, 0x64, 0x2e, 0x6f, 0x63, 0x73, 0x70, 0x2e, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x02, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x70, + 0x70, 0x73, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x2f, 0x64, + 0x73, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x78, 0x33, 0x2e, 0x70, + 0x37, 0x63, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0xc4, 0xa7, 0xb1, 0xa4, 0x7b, 0x2c, 0x71, 0xfa, 0xdb, + 0xe1, 0x4b, 0x90, 0x75, 0xff, 0xc4, 0x15, 0x60, 0x85, 0x89, 0x10, 0x30, + 0x54, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4d, 0x30, 0x4b, 0x30, 0x08, + 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x01, 0x30, 0x3f, 0x06, 0x0b, + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xdf, 0x13, 0x01, 0x01, 0x01, 0x30, + 0x30, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x70, + 0x73, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x78, 0x31, 0x2e, 0x6c, 0x65, + 0x74, 0x73, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x30, 0x3c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x35, 0x30, 0x33, + 0x30, 0x31, 0xa0, 0x2f, 0xa0, 0x2d, 0x86, 0x2b, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x53, 0x54, + 0x52, 0x4f, 0x4f, 0x54, 0x43, 0x41, 0x58, 0x33, 0x43, 0x52, 0x4c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0xa8, 0x4a, 0x6a, 0x63, 0x04, 0x7d, 0xdd, 0xba, 0xe6, 0xd1, + 0x39, 0xb7, 0xa6, 0x45, 0x65, 0xef, 0xf3, 0xa8, 0xec, 0xa1, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xdd, 0x33, 0xd7, 0x11, 0xf3, 0x63, + 0x58, 0x38, 0xdd, 0x18, 0x15, 0xfb, 0x09, 0x55, 0xbe, 0x76, 0x56, 0xb9, + 0x70, 0x48, 0xa5, 0x69, 0x47, 0x27, 0x7b, 0xc2, 0x24, 0x08, 0x92, 0xf1, + 0x5a, 0x1f, 0x4a, 0x12, 0x29, 0x37, 0x24, 0x74, 0x51, 0x1c, 0x62, 0x68, + 0xb8, 0xcd, 0x95, 0x70, 0x67, 0xe5, 0xf7, 0xa4, 0xbc, 0x4e, 0x28, 0x51, + 0xcd, 0x9b, 0xe8, 0xae, 0x87, 0x9d, 0xea, 0xd8, 0xba, 0x5a, 0xa1, 0x01, + 0x9a, 0xdc, 0xf0, 0xdd, 0x6a, 0x1d, 0x6a, 0xd8, 0x3e, 0x57, 0x23, 0x9e, + 0xa6, 0x1e, 0x04, 0x62, 0x9a, 0xff, 0xd7, 0x05, 0xca, 0xb7, 0x1f, 0x3f, + 0xc0, 0x0a, 0x48, 0xbc, 0x94, 0xb0, 0xb6, 0x65, 0x62, 0xe0, 0xc1, 0x54, + 0xe5, 0xa3, 0x2a, 0xad, 0x20, 0xc4, 0xe9, 0xe6, 0xbb, 0xdc, 0xc8, 0xf6, + 0xb5, 0xc3, 0x32, 0xa3, 0x98, 0xcc, 0x77, 0xa8, 0xe6, 0x79, 0x65, 0x07, + 0x2b, 0xcb, 0x28, 0xfe, 0x3a, 0x16, 0x52, 0x81, 0xce, 0x52, 0x0c, 0x2e, + 0x5f, 0x83, 0xe8, 0xd5, 0x06, 0x33, 0xfb, 0x77, 0x6c, 0xce, 0x40, 0xea, + 0x32, 0x9e, 0x1f, 0x92, 0x5c, 0x41, 0xc1, 0x74, 0x6c, 0x5b, 0x5d, 0x0a, + 0x5f, 0x33, 0xcc, 0x4d, 0x9f, 0xac, 0x38, 0xf0, 0x2f, 0x7b, 0x2c, 0x62, + 0x9d, 0xd9, 0xa3, 0x91, 0x6f, 0x25, 0x1b, 0x2f, 0x90, 0xb1, 0x19, 0x46, + 0x3d, 0xf6, 0x7e, 0x1b, 0xa6, 0x7a, 0x87, 0xb9, 0xa3, 0x7a, 0x6d, 0x18, + 0xfa, 0x25, 0xa5, 0x91, 0x87, 0x15, 0xe0, 0xf2, 0x16, 0x2f, 0x58, 0xb0, + 0x06, 0x2f, 0x2c, 0x68, 0x26, 0xc6, 0x4b, 0x98, 0xcd, 0xda, 0x9f, 0x0c, + 0xf9, 0x7f, 0x90, 0xed, 0x43, 0x4a, 0x12, 0x44, 0x4e, 0x6f, 0x73, 0x7a, + 0x28, 0xea, 0xa4, 0xaa, 0x6e, 0x7b, 0x4c, 0x7d, 0x87, 0xdd, 0xe0, 0xc9, + 0x02, 0x44, 0xa7, 0x87, 0xaf, 0xc3, 0x34, 0x5b, 0xb4, 0x42, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 06:7f:94:4a:2a:27:cd:f3:fa:c2:ae:2b:01:f9:08:ee:b9:c4:c6 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Services Root Certificate Authority - G2 + Validity + Not Before: May 25 12:00:00 2015 GMT + Not After : Dec 31 01:00:00 2037 GMT + Subject: C=US, O=Amazon, CN=Amazon Root CA 1 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b2:78:80:71:ca:78:d5:e3:71:af:47:80:50:74: + 7d:6e:d8:d7:88:76:f4:99:68:f7:58:21:60:f9:74: + 84:01:2f:ac:02:2d:86:d3:a0:43:7a:4e:b2:a4:d0: + 36:ba:01:be:8d:db:48:c8:07:17:36:4c:f4:ee:88: + 23:c7:3e:eb:37:f5:b5:19:f8:49:68:b0:de:d7:b9: + 76:38:1d:61:9e:a4:fe:82:36:a5:e5:4a:56:e4:45: + e1:f9:fd:b4:16:fa:74:da:9c:9b:35:39:2f:fa:b0: + 20:50:06:6c:7a:d0:80:b2:a6:f9:af:ec:47:19:8f: + 50:38:07:dc:a2:87:39:58:f8:ba:d5:a9:f9:48:67: + 30:96:ee:94:78:5e:6f:89:a3:51:c0:30:86:66:a1: + 45:66:ba:54:eb:a3:c3:91:f9:48:dc:ff:d1:e8:30: + 2d:7d:2d:74:70:35:d7:88:24:f7:9e:c4:59:6e:bb: + 73:87:17:f2:32:46:28:b8:43:fa:b7:1d:aa:ca:b4: + f2:9f:24:0e:2d:4b:f7:71:5c:5e:69:ff:ea:95:02: + cb:38:8a:ae:50:38:6f:db:fb:2d:62:1b:c5:c7:1e: + 54:e1:77:e0:67:c8:0f:9c:87:23:d6:3f:40:20:7f: + 20:80:c4:80:4c:3e:3b:24:26:8e:04:ae:6c:9a:c8: + aa:0d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 84:18:CC:85:34:EC:BC:0C:94:94:2E:08:59:9C:C7:B2:10:4E:0A:08 + X509v3 Authority Key Identifier: + keyid:9C:5F:00:DF:AA:01:D7:30:2B:38:88:A2:B8:6D:4A:9C:F2:11:91:83 + + Authority Information Access: + OCSP - URI:http://ocsp.rootg2.amazontrust.com + CA Issuers - URI:http://crt.rootg2.amazontrust.com/rootg2.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.rootg2.amazontrust.com/rootg2.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + + Signature Algorithm: sha256WithRSAEncryption + 62:37:42:5c:bc:10:b5:3e:8b:2c:e9:0c:9b:6c:45:e2:07:00: + 7a:f9:c5:58:0b:b9:08:8c:3e:ed:b3:25:3c:b5:6f:50:e4:cd: + 35:6a:a7:93:34:96:32:21:a9:48:44:ab:9c:ed:3d:b4:aa:73: + 6d:e4:7f:16:80:89:6c:cf:28:03:18:83:47:79:a3:10:7e:30: + 5b:ac:3b:b0:60:e0:77:d4:08:a6:e1:1d:7c:5e:c0:bb:f9:9a: + 7b:22:9d:a7:00:09:7e:ac:46:17:83:dc:9c:26:57:99:30:39: + 62:96:8f:ed:da:de:aa:c5:cc:1b:3e:ca:43:68:6c:57:16:bc: + d5:0e:20:2e:fe:ff:c2:6a:5d:2e:a0:4a:6d:14:58:87:94:e6: + 39:31:5f:7c:73:cb:90:88:6a:84:11:96:27:a6:ed:d9:81:46: + a6:7e:a3:72:00:0a:52:3e:83:88:07:63:77:89:69:17:0f:39: + 85:d2:ab:08:45:4d:d0:51:3a:fd:5d:5d:37:64:4c:7e:30:b2: + 55:24:42:9d:36:b0:5d:9c:17:81:61:f1:ca:f9:10:02:24:ab: + eb:0d:74:91:8d:7b:45:29:50:39:88:b2:a6:89:35:25:1e:14: + 6a:47:23:31:2f:5c:9a:fa:ad:9a:0e:62:51:a4:2a:a9:c4:f9: + 34:9d:21:18 +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgITBn+USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF +ADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNj +b3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4x +OzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1 +dGhvcml0eSAtIEcyMB4XDTE1MDUyNTEyMDAwMFoXDTM3MTIzMTAxMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaOCATEwggEtMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBSEGMyFNOy8DJSULghZnMeyEE4KCDAfBgNVHSMEGDAW +gBScXwDfqgHXMCs4iKK4bUqc8hGRgzB4BggrBgEFBQcBAQRsMGowLgYIKwYBBQUH +MAGGImh0dHA6Ly9vY3NwLnJvb3RnMi5hbWF6b250cnVzdC5jb20wOAYIKwYBBQUH +MAKGLGh0dHA6Ly9jcnQucm9vdGcyLmFtYXpvbnRydXN0LmNvbS9yb290ZzIuY2Vy +MD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwucm9vdGcyLmFtYXpvbnRydXN0 +LmNvbS9yb290ZzIuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsF +AAOCAQEAYjdCXLwQtT6LLOkMm2xF4gcAevnFWAu5CIw+7bMlPLVvUOTNNWqnkzSW +MiGpSESrnO09tKpzbeR/FoCJbM8oAxiDR3mjEH4wW6w7sGDgd9QIpuEdfF7Au/ma +eyKdpwAJfqxGF4PcnCZXmTA5YpaP7dreqsXMGz7KQ2hsVxa81Q4gLv7/wmpdLqBK +bRRYh5TmOTFffHPLkIhqhBGWJ6bt2YFGpn6jcgAKUj6DiAdjd4lpFw85hdKrCEVN +0FE6/V1dN2RMfjCyVSRCnTawXZwXgWHxyvkQAiSr6w10kY17RSlQOYiypok1JR4U +akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert23[] = { + 0x30, 0x82, 0x04, 0x92, 0x30, 0x82, 0x03, 0x7a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x13, 0x06, 0x7f, 0x94, 0x4a, 0x2a, 0x27, 0xcd, 0xf3, 0xfa, + 0xc2, 0xae, 0x2b, 0x01, 0xf9, 0x08, 0xee, 0xb9, 0xc4, 0xc6, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x81, 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, + 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, + 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x53, 0x74, + 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x35, 0x32, 0x35, 0x31, 0x32, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x32, 0x33, + 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x39, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d, + 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x10, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x20, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xb2, 0x78, 0x80, 0x71, 0xca, 0x78, 0xd5, 0xe3, + 0x71, 0xaf, 0x47, 0x80, 0x50, 0x74, 0x7d, 0x6e, 0xd8, 0xd7, 0x88, 0x76, + 0xf4, 0x99, 0x68, 0xf7, 0x58, 0x21, 0x60, 0xf9, 0x74, 0x84, 0x01, 0x2f, + 0xac, 0x02, 0x2d, 0x86, 0xd3, 0xa0, 0x43, 0x7a, 0x4e, 0xb2, 0xa4, 0xd0, + 0x36, 0xba, 0x01, 0xbe, 0x8d, 0xdb, 0x48, 0xc8, 0x07, 0x17, 0x36, 0x4c, + 0xf4, 0xee, 0x88, 0x23, 0xc7, 0x3e, 0xeb, 0x37, 0xf5, 0xb5, 0x19, 0xf8, + 0x49, 0x68, 0xb0, 0xde, 0xd7, 0xb9, 0x76, 0x38, 0x1d, 0x61, 0x9e, 0xa4, + 0xfe, 0x82, 0x36, 0xa5, 0xe5, 0x4a, 0x56, 0xe4, 0x45, 0xe1, 0xf9, 0xfd, + 0xb4, 0x16, 0xfa, 0x74, 0xda, 0x9c, 0x9b, 0x35, 0x39, 0x2f, 0xfa, 0xb0, + 0x20, 0x50, 0x06, 0x6c, 0x7a, 0xd0, 0x80, 0xb2, 0xa6, 0xf9, 0xaf, 0xec, + 0x47, 0x19, 0x8f, 0x50, 0x38, 0x07, 0xdc, 0xa2, 0x87, 0x39, 0x58, 0xf8, + 0xba, 0xd5, 0xa9, 0xf9, 0x48, 0x67, 0x30, 0x96, 0xee, 0x94, 0x78, 0x5e, + 0x6f, 0x89, 0xa3, 0x51, 0xc0, 0x30, 0x86, 0x66, 0xa1, 0x45, 0x66, 0xba, + 0x54, 0xeb, 0xa3, 0xc3, 0x91, 0xf9, 0x48, 0xdc, 0xff, 0xd1, 0xe8, 0x30, + 0x2d, 0x7d, 0x2d, 0x74, 0x70, 0x35, 0xd7, 0x88, 0x24, 0xf7, 0x9e, 0xc4, + 0x59, 0x6e, 0xbb, 0x73, 0x87, 0x17, 0xf2, 0x32, 0x46, 0x28, 0xb8, 0x43, + 0xfa, 0xb7, 0x1d, 0xaa, 0xca, 0xb4, 0xf2, 0x9f, 0x24, 0x0e, 0x2d, 0x4b, + 0xf7, 0x71, 0x5c, 0x5e, 0x69, 0xff, 0xea, 0x95, 0x02, 0xcb, 0x38, 0x8a, + 0xae, 0x50, 0x38, 0x6f, 0xdb, 0xfb, 0x2d, 0x62, 0x1b, 0xc5, 0xc7, 0x1e, + 0x54, 0xe1, 0x77, 0xe0, 0x67, 0xc8, 0x0f, 0x9c, 0x87, 0x23, 0xd6, 0x3f, + 0x40, 0x20, 0x7f, 0x20, 0x80, 0xc4, 0x80, 0x4c, 0x3e, 0x3b, 0x24, 0x26, + 0x8e, 0x04, 0xae, 0x6c, 0x9a, 0xc8, 0xaa, 0x0d, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x01, 0x31, 0x30, 0x82, 0x01, 0x2d, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, + 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x84, 0x18, 0xcc, 0x85, 0x34, 0xec, 0xbc, + 0x0c, 0x94, 0x94, 0x2e, 0x08, 0x59, 0x9c, 0xc7, 0xb2, 0x10, 0x4e, 0x0a, + 0x08, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x9c, 0x5f, 0x00, 0xdf, 0xaa, 0x01, 0xd7, 0x30, 0x2b, 0x38, + 0x88, 0xa2, 0xb8, 0x6d, 0x4a, 0x9c, 0xf2, 0x11, 0x91, 0x83, 0x30, 0x78, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x6c, + 0x30, 0x6a, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, + 0x63, 0x73, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x61, + 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x38, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x02, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x74, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x61, 0x6d, + 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x63, 0x65, 0x72, + 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x36, 0x30, 0x34, 0x30, + 0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, + 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, + 0x30, 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x62, 0x37, 0x42, 0x5c, 0xbc, 0x10, + 0xb5, 0x3e, 0x8b, 0x2c, 0xe9, 0x0c, 0x9b, 0x6c, 0x45, 0xe2, 0x07, 0x00, + 0x7a, 0xf9, 0xc5, 0x58, 0x0b, 0xb9, 0x08, 0x8c, 0x3e, 0xed, 0xb3, 0x25, + 0x3c, 0xb5, 0x6f, 0x50, 0xe4, 0xcd, 0x35, 0x6a, 0xa7, 0x93, 0x34, 0x96, + 0x32, 0x21, 0xa9, 0x48, 0x44, 0xab, 0x9c, 0xed, 0x3d, 0xb4, 0xaa, 0x73, + 0x6d, 0xe4, 0x7f, 0x16, 0x80, 0x89, 0x6c, 0xcf, 0x28, 0x03, 0x18, 0x83, + 0x47, 0x79, 0xa3, 0x10, 0x7e, 0x30, 0x5b, 0xac, 0x3b, 0xb0, 0x60, 0xe0, + 0x77, 0xd4, 0x08, 0xa6, 0xe1, 0x1d, 0x7c, 0x5e, 0xc0, 0xbb, 0xf9, 0x9a, + 0x7b, 0x22, 0x9d, 0xa7, 0x00, 0x09, 0x7e, 0xac, 0x46, 0x17, 0x83, 0xdc, + 0x9c, 0x26, 0x57, 0x99, 0x30, 0x39, 0x62, 0x96, 0x8f, 0xed, 0xda, 0xde, + 0xaa, 0xc5, 0xcc, 0x1b, 0x3e, 0xca, 0x43, 0x68, 0x6c, 0x57, 0x16, 0xbc, + 0xd5, 0x0e, 0x20, 0x2e, 0xfe, 0xff, 0xc2, 0x6a, 0x5d, 0x2e, 0xa0, 0x4a, + 0x6d, 0x14, 0x58, 0x87, 0x94, 0xe6, 0x39, 0x31, 0x5f, 0x7c, 0x73, 0xcb, + 0x90, 0x88, 0x6a, 0x84, 0x11, 0x96, 0x27, 0xa6, 0xed, 0xd9, 0x81, 0x46, + 0xa6, 0x7e, 0xa3, 0x72, 0x00, 0x0a, 0x52, 0x3e, 0x83, 0x88, 0x07, 0x63, + 0x77, 0x89, 0x69, 0x17, 0x0f, 0x39, 0x85, 0xd2, 0xab, 0x08, 0x45, 0x4d, + 0xd0, 0x51, 0x3a, 0xfd, 0x5d, 0x5d, 0x37, 0x64, 0x4c, 0x7e, 0x30, 0xb2, + 0x55, 0x24, 0x42, 0x9d, 0x36, 0xb0, 0x5d, 0x9c, 0x17, 0x81, 0x61, 0xf1, + 0xca, 0xf9, 0x10, 0x02, 0x24, 0xab, 0xeb, 0x0d, 0x74, 0x91, 0x8d, 0x7b, + 0x45, 0x29, 0x50, 0x39, 0x88, 0xb2, 0xa6, 0x89, 0x35, 0x25, 0x1e, 0x14, + 0x6a, 0x47, 0x23, 0x31, 0x2f, 0x5c, 0x9a, 0xfa, 0xad, 0x9a, 0x0e, 0x62, + 0x51, 0xa4, 0x2a, 0xa9, 0xc4, 0xf9, 0x34, 0x9d, 0x21, 0x18, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 01:fd:a3:eb:6e:ca:75:c8:88:43:8b:72:4b:cf:bc:91 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA + Validity + Not Before: Mar 8 12:00:00 2013 GMT + Not After : Mar 8 12:00:00 2023 GMT + Subject: C=US, O=DigiCert Inc, CN=DigiCert SHA2 Secure Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:dc:ae:58:90:4d:c1:c4:30:15:90:35:5b:6e:3c: + 82:15:f5:2c:5c:bd:e3:db:ff:71:43:fa:64:25:80: + d4:ee:18:a2:4d:f0:66:d0:0a:73:6e:11:98:36:17: + 64:af:37:9d:fd:fa:41:84:af:c7:af:8c:fe:1a:73: + 4d:cf:33:97:90:a2:96:87:53:83:2b:b9:a6:75:48: + 2d:1d:56:37:7b:da:31:32:1a:d7:ac:ab:06:f4:aa: + 5d:4b:b7:47:46:dd:2a:93:c3:90:2e:79:80:80:ef: + 13:04:6a:14:3b:b5:9b:92:be:c2:07:65:4e:fc:da: + fc:ff:7a:ae:dc:5c:7e:55:31:0c:e8:39:07:a4:d7: + be:2f:d3:0b:6a:d2:b1:df:5f:fe:57:74:53:3b:35: + 80:dd:ae:8e:44:98:b3:9f:0e:d3:da:e0:d7:f4:6b: + 29:ab:44:a7:4b:58:84:6d:92:4b:81:c3:da:73:8b: + 12:97:48:90:04:45:75:1a:dd:37:31:97:92:e8:cd: + 54:0d:3b:e4:c1:3f:39:5e:2e:b8:f3:5c:7e:10:8e: + 86:41:00:8d:45:66:47:b0:a1:65:ce:a0:aa:29:09: + 4e:f3:97:eb:e8:2e:ab:0f:72:a7:30:0e:fa:c7:f4: + fd:14:77:c3:a4:5b:28:57:c2:b3:f9:82:fd:b7:45: + 58:9b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl + + Full Name: + URI:http://crl4.digicert.com/DigiCertGlobalRootCA.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.digicert.com/CPS + + X509v3 Subject Key Identifier: + 0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2 + X509v3 Authority Key Identifier: + keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55 + + Signature Algorithm: sha256WithRSAEncryption + 23:3e:df:4b:d2:31:42:a5:b6:7e:42:5c:1a:44:cc:69:d1:68: + b4:5d:4b:e0:04:21:6c:4b:e2:6d:cc:b1:e0:97:8f:a6:53:09: + cd:aa:2a:65:e5:39:4f:1e:83:a5:6e:5c:98:a2:24:26:e6:fb: + a1:ed:93:c7:2e:02:c6:4d:4a:bf:b0:42:df:78:da:b3:a8:f9: + 6d:ff:21:85:53:36:60:4c:76:ce:ec:38:dc:d6:51:80:f0:c5: + d6:e5:d4:4d:27:64:ab:9b:c7:3e:71:fb:48:97:b8:33:6d:c9: + 13:07:ee:96:a2:1b:18:15:f6:5c:4c:40:ed:b3:c2:ec:ff:71: + c1:e3:47:ff:d4:b9:00:b4:37:42:da:20:c9:ea:6e:8a:ee:14: + 06:ae:7d:a2:59:98:88:a8:1b:6f:2d:f4:f2:c9:14:5f:26:cf: + 2c:8d:7e:ed:37:c0:a9:d5:39:b9:82:bf:19:0c:ea:34:af:00: + 21:68:f8:ad:73:e2:c9:32:da:38:25:0b:55:d3:9a:1d:f0:68: + 86:ed:2e:41:34:ef:7c:a5:50:1d:bf:3a:f9:d3:c1:08:0c:e6: + ed:1e:8a:58:25:e4:b8:77:ad:2d:6e:f5:52:dd:b4:74:8f:ab: + 49:2e:9d:3b:93:34:28:1f:78:ce:94:ea:c7:bd:d3:c9:6d:1c: + de:5c:32:f3 +-----BEGIN CERTIFICATE----- +MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg +U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83 +nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd +KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f +/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX +kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0 +/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C +AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY +aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6 +Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1 +oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD +QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v +d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh +xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB +CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl +5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA +8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC +2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit +c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0 +j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert24[] = { + 0x30, 0x82, 0x04, 0x94, 0x30, 0x82, 0x03, 0x7c, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x01, 0xfd, 0xa3, 0xeb, 0x6e, 0xca, 0x75, 0xc8, 0x88, + 0x43, 0x8b, 0x72, 0x4b, 0xcf, 0xbc, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x33, 0x30, 0x38, 0x31, + 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x33, + 0x30, 0x38, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4d, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, + 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, 0x32, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xdc, 0xae, 0x58, 0x90, 0x4d, 0xc1, 0xc4, 0x30, 0x15, 0x90, 0x35, + 0x5b, 0x6e, 0x3c, 0x82, 0x15, 0xf5, 0x2c, 0x5c, 0xbd, 0xe3, 0xdb, 0xff, + 0x71, 0x43, 0xfa, 0x64, 0x25, 0x80, 0xd4, 0xee, 0x18, 0xa2, 0x4d, 0xf0, + 0x66, 0xd0, 0x0a, 0x73, 0x6e, 0x11, 0x98, 0x36, 0x17, 0x64, 0xaf, 0x37, + 0x9d, 0xfd, 0xfa, 0x41, 0x84, 0xaf, 0xc7, 0xaf, 0x8c, 0xfe, 0x1a, 0x73, + 0x4d, 0xcf, 0x33, 0x97, 0x90, 0xa2, 0x96, 0x87, 0x53, 0x83, 0x2b, 0xb9, + 0xa6, 0x75, 0x48, 0x2d, 0x1d, 0x56, 0x37, 0x7b, 0xda, 0x31, 0x32, 0x1a, + 0xd7, 0xac, 0xab, 0x06, 0xf4, 0xaa, 0x5d, 0x4b, 0xb7, 0x47, 0x46, 0xdd, + 0x2a, 0x93, 0xc3, 0x90, 0x2e, 0x79, 0x80, 0x80, 0xef, 0x13, 0x04, 0x6a, + 0x14, 0x3b, 0xb5, 0x9b, 0x92, 0xbe, 0xc2, 0x07, 0x65, 0x4e, 0xfc, 0xda, + 0xfc, 0xff, 0x7a, 0xae, 0xdc, 0x5c, 0x7e, 0x55, 0x31, 0x0c, 0xe8, 0x39, + 0x07, 0xa4, 0xd7, 0xbe, 0x2f, 0xd3, 0x0b, 0x6a, 0xd2, 0xb1, 0xdf, 0x5f, + 0xfe, 0x57, 0x74, 0x53, 0x3b, 0x35, 0x80, 0xdd, 0xae, 0x8e, 0x44, 0x98, + 0xb3, 0x9f, 0x0e, 0xd3, 0xda, 0xe0, 0xd7, 0xf4, 0x6b, 0x29, 0xab, 0x44, + 0xa7, 0x4b, 0x58, 0x84, 0x6d, 0x92, 0x4b, 0x81, 0xc3, 0xda, 0x73, 0x8b, + 0x12, 0x97, 0x48, 0x90, 0x04, 0x45, 0x75, 0x1a, 0xdd, 0x37, 0x31, 0x97, + 0x92, 0xe8, 0xcd, 0x54, 0x0d, 0x3b, 0xe4, 0xc1, 0x3f, 0x39, 0x5e, 0x2e, + 0xb8, 0xf3, 0x5c, 0x7e, 0x10, 0x8e, 0x86, 0x41, 0x00, 0x8d, 0x45, 0x66, + 0x47, 0xb0, 0xa1, 0x65, 0xce, 0xa0, 0xaa, 0x29, 0x09, 0x4e, 0xf3, 0x97, + 0xeb, 0xe8, 0x2e, 0xab, 0x0f, 0x72, 0xa7, 0x30, 0x0e, 0xfa, 0xc7, 0xf4, + 0xfd, 0x14, 0x77, 0xc3, 0xa4, 0x5b, 0x28, 0x57, 0xc2, 0xb3, 0xf9, 0x82, + 0xfd, 0xb7, 0x45, 0x58, 0x9b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x5a, 0x30, 0x82, 0x01, 0x56, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, + 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30, + 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, + 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, + 0x43, 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, + 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x37, 0xa0, 0x35, + 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, + 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, + 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, + 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, + 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0f, 0x80, 0x61, 0x1c, 0x82, + 0x31, 0x61, 0xd5, 0x2f, 0x28, 0xe7, 0x8d, 0x46, 0x38, 0xb4, 0x2c, 0xe1, + 0xc6, 0xd9, 0xe2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, + 0x66, 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x23, 0x3e, 0xdf, 0x4b, + 0xd2, 0x31, 0x42, 0xa5, 0xb6, 0x7e, 0x42, 0x5c, 0x1a, 0x44, 0xcc, 0x69, + 0xd1, 0x68, 0xb4, 0x5d, 0x4b, 0xe0, 0x04, 0x21, 0x6c, 0x4b, 0xe2, 0x6d, + 0xcc, 0xb1, 0xe0, 0x97, 0x8f, 0xa6, 0x53, 0x09, 0xcd, 0xaa, 0x2a, 0x65, + 0xe5, 0x39, 0x4f, 0x1e, 0x83, 0xa5, 0x6e, 0x5c, 0x98, 0xa2, 0x24, 0x26, + 0xe6, 0xfb, 0xa1, 0xed, 0x93, 0xc7, 0x2e, 0x02, 0xc6, 0x4d, 0x4a, 0xbf, + 0xb0, 0x42, 0xdf, 0x78, 0xda, 0xb3, 0xa8, 0xf9, 0x6d, 0xff, 0x21, 0x85, + 0x53, 0x36, 0x60, 0x4c, 0x76, 0xce, 0xec, 0x38, 0xdc, 0xd6, 0x51, 0x80, + 0xf0, 0xc5, 0xd6, 0xe5, 0xd4, 0x4d, 0x27, 0x64, 0xab, 0x9b, 0xc7, 0x3e, + 0x71, 0xfb, 0x48, 0x97, 0xb8, 0x33, 0x6d, 0xc9, 0x13, 0x07, 0xee, 0x96, + 0xa2, 0x1b, 0x18, 0x15, 0xf6, 0x5c, 0x4c, 0x40, 0xed, 0xb3, 0xc2, 0xec, + 0xff, 0x71, 0xc1, 0xe3, 0x47, 0xff, 0xd4, 0xb9, 0x00, 0xb4, 0x37, 0x42, + 0xda, 0x20, 0xc9, 0xea, 0x6e, 0x8a, 0xee, 0x14, 0x06, 0xae, 0x7d, 0xa2, + 0x59, 0x98, 0x88, 0xa8, 0x1b, 0x6f, 0x2d, 0xf4, 0xf2, 0xc9, 0x14, 0x5f, + 0x26, 0xcf, 0x2c, 0x8d, 0x7e, 0xed, 0x37, 0xc0, 0xa9, 0xd5, 0x39, 0xb9, + 0x82, 0xbf, 0x19, 0x0c, 0xea, 0x34, 0xaf, 0x00, 0x21, 0x68, 0xf8, 0xad, + 0x73, 0xe2, 0xc9, 0x32, 0xda, 0x38, 0x25, 0x0b, 0x55, 0xd3, 0x9a, 0x1d, + 0xf0, 0x68, 0x86, 0xed, 0x2e, 0x41, 0x34, 0xef, 0x7c, 0xa5, 0x50, 0x1d, + 0xbf, 0x3a, 0xf9, 0xd3, 0xc1, 0x08, 0x0c, 0xe6, 0xed, 0x1e, 0x8a, 0x58, + 0x25, 0xe4, 0xb8, 0x77, 0xad, 0x2d, 0x6e, 0xf5, 0x52, 0xdd, 0xb4, 0x74, + 0x8f, 0xab, 0x49, 0x2e, 0x9d, 0x3b, 0x93, 0x34, 0x28, 0x1f, 0x78, 0xce, + 0x94, 0xea, 0xc7, 0xbd, 0xd3, 0xc9, 0x6d, 0x1c, 0xde, 0x5c, 0x32, 0xf3, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3740804 (0x391484) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority + Validity + Not Before: Jan 1 07:00:00 2014 GMT + Not After : May 30 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bd:ed:c1:03:fc:f6:8f:fc:02:b1:6f:5b:9f:48: + d9:9d:79:e2:a2:b7:03:61:56:18:c3:47:b6:d7:ca: + 3d:35:2e:89:43:f7:a1:69:9b:de:8a:1a:fd:13:20: + 9c:b4:49:77:32:29:56:fd:b9:ec:8c:dd:22:fa:72: + dc:27:61:97:ee:f6:5a:84:ec:6e:19:b9:89:2c:dc: + 84:5b:d5:74:fb:6b:5f:c5:89:a5:10:52:89:46:55: + f4:b8:75:1c:e6:7f:e4:54:ae:4b:f8:55:72:57:02: + 19:f8:17:71:59:eb:1e:28:07:74:c5:9d:48:be:6c: + b4:f4:a4:b0:f3:64:37:79:92:c0:ec:46:5e:7f:e1: + 6d:53:4c:62:af:cd:1f:0b:63:bb:3a:9d:fb:fc:79: + 00:98:61:74:cf:26:82:40:63:f3:b2:72:6a:19:0d: + 99:ca:d4:0e:75:cc:37:fb:8b:89:c1:59:f1:62:7f: + 5f:b3:5f:65:30:f8:a7:b7:4d:76:5a:1e:76:5e:34: + c0:e8:96:56:99:8a:b3:f0:7f:a4:cd:bd:dc:32:31: + 7c:91:cf:e0:5f:11:f8:6b:aa:49:5c:d1:99:94:d1: + a2:e3:63:5b:09:76:b5:56:62:e1:4b:74:1d:96:d4: + 26:d4:08:04:59:d0:98:0e:0e:e6:de:fc:c3:ec:1f: + 90:f1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27 + X509v3 Authority Key Identifier: + keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7 + + Authority Information Access: + OCSP - URI:http://ocsp.starfieldtech.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.starfieldtech.com/sfroot.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.starfieldtech.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 85:63:c1:d9:dd:b9:ff:a9:bd:a6:19:dc:bf:13:3a:11:38:22: + 54:b1:ac:05:10:fb:7c:b3:96:3f:31:8b:66:ff:88:f3:e1:bf: + fb:c7:1f:00:ff:46:6a:8b:61:32:c9:01:51:76:fb:9a:c6:fa: + 20:51:c8:46:c4:98:d7:79:a3:e3:04:72:3f:8b:4d:34:53:67: + ec:33:2c:7b:e8:94:01:28:7c:3a:34:5b:02:77:16:8d:40:25: + 33:b0:bc:6c:97:d7:05:7a:ff:8c:85:ce:6f:a0:53:00:17:6e: + 1e:6c:bd:22:d7:0a:88:37:f6:7d:eb:99:41:ef:27:cb:8c:60: + 6b:4c:01:7e:65:50:0b:4f:b8:95:9a:9a:6e:34:fd:73:3a:33: + f1:91:d5:f3:4e:2d:74:e8:ef:d3:90:35:f1:06:68:64:d4:d0: + 13:fd:52:d3:c6:6d:c1:3a:8a:31:dd:05:26:35:4a:8c:65:b8: + 52:6b:81:ec:d2:9c:b5:34:10:97:9c:3e:c6:2f:ed:8e:42:42: + 24:2e:e9:73:9a:25:f9:11:f1:f2:23:69:cb:e5:94:69:a0:d2: + dc:b0:fc:44:89:ac:17:a8:cc:d5:37:77:16:c5:80:b9:0c:8f: + 57:02:55:99:85:7b:49:f0:2e:5b:a0:c2:57:53:5d:a2:e8:a6: + 37:c3:01:fa +-----BEGIN CERTIFICATE----- +MIIEoDCCA4igAwIBAgIDORSEMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAlVT +MSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQL +EylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NDAxMDEwNzAwMDBaFw0zMTA1MzAwNzAwMDBaMIGPMQswCQYDVQQGEwJVUzEQMA4G +A1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UEChMcU3Rh +cmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UEAxMpU3RhcmZpZWxkIFJv +b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC97cED/PaP/AKxb1ufSNmdeeKitwNhVhjDR7bXyj01LolD +96Fpm96KGv0TIJy0SXcyKVb9ueyM3SL6ctwnYZfu9lqE7G4ZuYks3IRb1XT7a1/F +iaUQUolGVfS4dRzmf+RUrkv4VXJXAhn4F3FZ6x4oB3TFnUi+bLT0pLDzZDd5ksDs +Rl5/4W1TTGKvzR8LY7s6nfv8eQCYYXTPJoJAY/OycmoZDZnK1A51zDf7i4nBWfFi +f1+zX2Uw+Ke3TXZaHnZeNMDollaZirPwf6TNvdwyMXyRz+BfEfhrqklc0ZmU0aLj +Y1sJdrVWYuFLdB2W1CbUCARZ0JgODube/MPsH5DxAgMBAAGjggEpMIIBJTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfAwyH6fZMH/E +fWijYqihzqsHWycwHwYDVR0jBBgwFoAUv1+30c7dH4b0W1Ws3NcQwg6piOcwOgYI +KwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0 +ZWNoLmNvbS8wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5zdGFyZmllbGR0 +ZWNoLmNvbS9zZnJvb3QuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF +BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQCFY8HZ3bn/qb2mGdy/EzoROCJUsawFEPt8s5Y/ +MYtm/4jz4b/7xx8A/0Zqi2EyyQFRdvuaxvogUchGxJjXeaPjBHI/i000U2fsMyx7 +6JQBKHw6NFsCdxaNQCUzsLxsl9cFev+Mhc5voFMAF24ebL0i1wqIN/Z965lB7yfL +jGBrTAF+ZVALT7iVmppuNP1zOjPxkdXzTi106O/TkDXxBmhk1NAT/VLTxm3BOoox +3QUmNUqMZbhSa4Hs0py1NBCXnD7GL+2OQkIkLulzmiX5EfHyI2nL5ZRpoNLcsPxE +iawXqMzVN3cWxYC5DI9XAlWZhXtJ8C5boMJXU12i6KY3wwH6 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert25[] = { + 0x30, 0x82, 0x04, 0xa0, 0x30, 0x82, 0x03, 0x88, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x39, 0x14, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x68, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, + 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x34, 0x30, 0x31, 0x30, 0x31, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, + 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x30, 0x81, 0x8f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, + 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, + 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, + 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, + 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, + 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, + 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xbd, 0xed, 0xc1, 0x03, 0xfc, 0xf6, 0x8f, 0xfc, 0x02, 0xb1, + 0x6f, 0x5b, 0x9f, 0x48, 0xd9, 0x9d, 0x79, 0xe2, 0xa2, 0xb7, 0x03, 0x61, + 0x56, 0x18, 0xc3, 0x47, 0xb6, 0xd7, 0xca, 0x3d, 0x35, 0x2e, 0x89, 0x43, + 0xf7, 0xa1, 0x69, 0x9b, 0xde, 0x8a, 0x1a, 0xfd, 0x13, 0x20, 0x9c, 0xb4, + 0x49, 0x77, 0x32, 0x29, 0x56, 0xfd, 0xb9, 0xec, 0x8c, 0xdd, 0x22, 0xfa, + 0x72, 0xdc, 0x27, 0x61, 0x97, 0xee, 0xf6, 0x5a, 0x84, 0xec, 0x6e, 0x19, + 0xb9, 0x89, 0x2c, 0xdc, 0x84, 0x5b, 0xd5, 0x74, 0xfb, 0x6b, 0x5f, 0xc5, + 0x89, 0xa5, 0x10, 0x52, 0x89, 0x46, 0x55, 0xf4, 0xb8, 0x75, 0x1c, 0xe6, + 0x7f, 0xe4, 0x54, 0xae, 0x4b, 0xf8, 0x55, 0x72, 0x57, 0x02, 0x19, 0xf8, + 0x17, 0x71, 0x59, 0xeb, 0x1e, 0x28, 0x07, 0x74, 0xc5, 0x9d, 0x48, 0xbe, + 0x6c, 0xb4, 0xf4, 0xa4, 0xb0, 0xf3, 0x64, 0x37, 0x79, 0x92, 0xc0, 0xec, + 0x46, 0x5e, 0x7f, 0xe1, 0x6d, 0x53, 0x4c, 0x62, 0xaf, 0xcd, 0x1f, 0x0b, + 0x63, 0xbb, 0x3a, 0x9d, 0xfb, 0xfc, 0x79, 0x00, 0x98, 0x61, 0x74, 0xcf, + 0x26, 0x82, 0x40, 0x63, 0xf3, 0xb2, 0x72, 0x6a, 0x19, 0x0d, 0x99, 0xca, + 0xd4, 0x0e, 0x75, 0xcc, 0x37, 0xfb, 0x8b, 0x89, 0xc1, 0x59, 0xf1, 0x62, + 0x7f, 0x5f, 0xb3, 0x5f, 0x65, 0x30, 0xf8, 0xa7, 0xb7, 0x4d, 0x76, 0x5a, + 0x1e, 0x76, 0x5e, 0x34, 0xc0, 0xe8, 0x96, 0x56, 0x99, 0x8a, 0xb3, 0xf0, + 0x7f, 0xa4, 0xcd, 0xbd, 0xdc, 0x32, 0x31, 0x7c, 0x91, 0xcf, 0xe0, 0x5f, + 0x11, 0xf8, 0x6b, 0xaa, 0x49, 0x5c, 0xd1, 0x99, 0x94, 0xd1, 0xa2, 0xe3, + 0x63, 0x5b, 0x09, 0x76, 0xb5, 0x56, 0x62, 0xe1, 0x4b, 0x74, 0x1d, 0x96, + 0xd4, 0x26, 0xd4, 0x08, 0x04, 0x59, 0xd0, 0x98, 0x0e, 0x0e, 0xe6, 0xde, + 0xfc, 0xc3, 0xec, 0x1f, 0x90, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x29, 0x30, 0x82, 0x01, 0x25, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x7c, 0x0c, 0x32, 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4, + 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1, 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0xbf, 0x5f, 0xb7, 0xd1, 0xce, 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55, 0xac, + 0xdc, 0xd7, 0x10, 0xc2, 0x0e, 0xa9, 0x88, 0xe7, 0x30, 0x3a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c, + 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, + 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x38, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0xa0, 0x2b, 0xa0, + 0x29, 0x86, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, + 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f, + 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20, + 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x85, 0x63, 0xc1, 0xd9, + 0xdd, 0xb9, 0xff, 0xa9, 0xbd, 0xa6, 0x19, 0xdc, 0xbf, 0x13, 0x3a, 0x11, + 0x38, 0x22, 0x54, 0xb1, 0xac, 0x05, 0x10, 0xfb, 0x7c, 0xb3, 0x96, 0x3f, + 0x31, 0x8b, 0x66, 0xff, 0x88, 0xf3, 0xe1, 0xbf, 0xfb, 0xc7, 0x1f, 0x00, + 0xff, 0x46, 0x6a, 0x8b, 0x61, 0x32, 0xc9, 0x01, 0x51, 0x76, 0xfb, 0x9a, + 0xc6, 0xfa, 0x20, 0x51, 0xc8, 0x46, 0xc4, 0x98, 0xd7, 0x79, 0xa3, 0xe3, + 0x04, 0x72, 0x3f, 0x8b, 0x4d, 0x34, 0x53, 0x67, 0xec, 0x33, 0x2c, 0x7b, + 0xe8, 0x94, 0x01, 0x28, 0x7c, 0x3a, 0x34, 0x5b, 0x02, 0x77, 0x16, 0x8d, + 0x40, 0x25, 0x33, 0xb0, 0xbc, 0x6c, 0x97, 0xd7, 0x05, 0x7a, 0xff, 0x8c, + 0x85, 0xce, 0x6f, 0xa0, 0x53, 0x00, 0x17, 0x6e, 0x1e, 0x6c, 0xbd, 0x22, + 0xd7, 0x0a, 0x88, 0x37, 0xf6, 0x7d, 0xeb, 0x99, 0x41, 0xef, 0x27, 0xcb, + 0x8c, 0x60, 0x6b, 0x4c, 0x01, 0x7e, 0x65, 0x50, 0x0b, 0x4f, 0xb8, 0x95, + 0x9a, 0x9a, 0x6e, 0x34, 0xfd, 0x73, 0x3a, 0x33, 0xf1, 0x91, 0xd5, 0xf3, + 0x4e, 0x2d, 0x74, 0xe8, 0xef, 0xd3, 0x90, 0x35, 0xf1, 0x06, 0x68, 0x64, + 0xd4, 0xd0, 0x13, 0xfd, 0x52, 0xd3, 0xc6, 0x6d, 0xc1, 0x3a, 0x8a, 0x31, + 0xdd, 0x05, 0x26, 0x35, 0x4a, 0x8c, 0x65, 0xb8, 0x52, 0x6b, 0x81, 0xec, + 0xd2, 0x9c, 0xb5, 0x34, 0x10, 0x97, 0x9c, 0x3e, 0xc6, 0x2f, 0xed, 0x8e, + 0x42, 0x42, 0x24, 0x2e, 0xe9, 0x73, 0x9a, 0x25, 0xf9, 0x11, 0xf1, 0xf2, + 0x23, 0x69, 0xcb, 0xe5, 0x94, 0x69, 0xa0, 0xd2, 0xdc, 0xb0, 0xfc, 0x44, + 0x89, 0xac, 0x17, 0xa8, 0xcc, 0xd5, 0x37, 0x77, 0x16, 0xc5, 0x80, 0xb9, + 0x0c, 0x8f, 0x57, 0x02, 0x55, 0x99, 0x85, 0x7b, 0x49, 0xf0, 0x2e, 0x5b, + 0xa0, 0xc2, 0x57, 0x53, 0x5d, 0xa2, 0xe8, 0xa6, 0x37, 0xc3, 0x01, 0xfa, +}; +
diff --git a/quic/core/crypto/common_cert_set_3b.inc b/quic/core/crypto/common_cert_set_3b.inc new file mode 100644 index 0000000..f175f24 --- /dev/null +++ b/quic/core/crypto/common_cert_set_3b.inc
@@ -0,0 +1,5717 @@ +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 28:1c:89:29:66:14:43:80:42:63:55:3a:32:40:ae:b3 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3 + Validity + Not Before: Jun 30 00:00:00 2015 GMT + Not After : Jun 29 23:59:59 2025 GMT + Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c0:9e:3a:0f:9a:b2:ba:d3:d2:dc:15:ec:d0:30: + 54:59:30:4d:40:51:ae:42:71:71:d2:8d:53:73:81: + fe:b8:e0:c4:96:c5:8e:7e:c2:f1:b7:63:4a:cf:a7: + 1e:3f:a8:e7:ce:53:a0:fa:2d:f7:d6:e6:ce:70:11: + a6:ee:e1:03:52:d2:68:de:3d:08:0d:87:fd:1c:d7: + 0b:97:62:6d:82:30:76:1b:47:3a:c4:f7:ce:ed:1d: + 7c:8c:b7:17:8e:53:80:1e:1d:0f:5d:8c:f9:90:e4: + 04:1e:02:7e:cb:b0:49:ef:da:52:25:fb:fb:67:ed: + dd:84:74:59:84:0e:f3:de:70:66:8d:e4:52:38:f7: + 53:5a:37:13:67:0b:3e:bb:a8:58:b7:2e:ed:ff:b7: + 5e:11:73:b9:77:45:52:67:46:ae:c4:dc:24:81:89: + 76:0a:ca:a1:6c:66:73:04:82:aa:f5:70:6c:5f:1b: + 9a:00:79:46:d6:7f:7a:26:17:30:cf:39:4b:2c:74: + d9:89:44:76:10:d0:ed:f7:8b:bb:89:05:75:4d:0b: + 0d:b3:da:e9:bf:f1:6a:7d:2a:11:db:1e:9f:8c:e3: + c4:06:69:e1:1d:88:45:39:d1:6e:55:d8:aa:b7:9b: + 6f:ea:f4:de:ac:17:11:92:5d:40:9b:83:7b:9a:e2: + f7:a9 + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 + CPS: https://www.geotrust.com/resources/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/GeoTrustPCA-G3.crl + + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + F3:B5:56:0C:C4:09:B0:B4:CF:1F:AA:F9:DD:23:56:F0:77:E8:A1:F9 + X509v3 Authority Key Identifier: + keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D + + Signature Algorithm: sha256WithRSAEncryption + c3:7e:d8:83:4b:04:4c:55:29:2a:4f:14:9d:9a:6e:de:90:70: + c1:a4:26:4c:88:8e:78:48:ef:bd:9c:b0:a0:f5:f0:66:fc:fe: + 59:26:e1:79:ef:c8:b7:60:64:a8:8b:47:ea:2f:e0:83:99:da: + 41:19:d7:c5:be:05:fa:f2:90:11:f0:0a:ff:6c:dc:05:b4:d8: + 06:6f:a4:6f:8d:be:20:2b:54:db:f9:a2:45:83:9a:1e:a5:21: + 89:35:1d:7c:20:5c:17:fd:04:2e:45:d8:b2:c6:f8:42:99:fc: + 54:08:4e:4b:80:5f:39:37:ba:95:4e:a6:37:0a:9e:93:5e:87: + 5b:e9:90:d6:a8:b6:65:08:8d:61:49:eb:83:20:a9:5d:1b:16: + 60:62:6b:2f:54:fb:5a:02:0d:7a:27:e2:4b:e1:05:14:c2:e4: + e9:f9:70:c0:d9:f7:34:65:0e:a2:91:4b:ac:28:f2:b7:08:0f: + 98:ca:d7:3e:70:b6:c8:0b:f1:8b:9c:51:f8:c6:10:6c:d2:53: + 4f:62:8c:11:00:3e:88:df:bf:e6:d2:cc:70:bd:ed:25:9c:fb: + dd:24:0a:bd:59:91:4a:42:03:38:12:71:32:88:76:a0:8e:7c: + bb:32:ef:88:2a:1b:d4:6a:6f:50:b9:52:67:8b:ab:30:fa:1f: + fd:e3:24:9a +-----BEGIN CERTIFICATE----- +MIIEpjCCA46gAwIBAgIQKByJKWYUQ4BCY1U6MkCuszANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTE1MDYzMDAwMDAwMFoXDTI1MDYyOTIzNTk1OVowRzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlk +U1NMIFNIQTI1NiBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAwJ46D5qyutPS3BXs0DBUWTBNQFGuQnFx0o1Tc4H+uODElsWOfsLxt2NKz6ce +P6jnzlOg+i331ubOcBGm7uEDUtJo3j0IDYf9HNcLl2JtgjB2G0c6xPfO7R18jLcX +jlOAHh0PXYz5kOQEHgJ+y7BJ79pSJfv7Z+3dhHRZhA7z3nBmjeRSOPdTWjcTZws+ +u6hYty7t/7deEXO5d0VSZ0auxNwkgYl2CsqhbGZzBIKq9XBsXxuaAHlG1n96Jhcw +zzlLLHTZiUR2ENDt94u7iQV1TQsNs9rpv/FqfSoR2x6fjOPEBmnhHYhFOdFuVdiq +t5tv6vTerBcRkl1Am4N7muL3qQIDAQABo4IBOjCCATYwLgYIKwYBBQUHAQEEIjAg +MB4GCCsGAQUFBzABhhJodHRwOi8vZy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB +/wIBADBJBgNVHSAEQjBAMD4GBmeBDAECATA0MDIGCCsGAQUFBwIBFiZodHRwczov +L3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA2BgNVHR8ELzAtMCugKaAn +hiVodHRwOi8vZy5zeW1jYi5jb20vR2VvVHJ1c3RQQ0EtRzMuY3JsMB0GA1UdJQQW +MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPO1VgzECbC0zx+q+d0jVvB36KH5MB8GA1UdIwQYMBaAFMR5yo6hTgMdHNxr2zFb +lD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQDDftiDSwRMVSkqTxSdmm7ekHDBpCZM +iI54SO+9nLCg9fBm/P5ZJuF578i3YGSoi0fqL+CDmdpBGdfFvgX68pAR8Ar/bNwF +tNgGb6Rvjb4gK1Tb+aJFg5oepSGJNR18IFwX/QQuRdiyxvhCmfxUCE5LgF85N7qV +TqY3Cp6TXodb6ZDWqLZlCI1hSeuDIKldGxZgYmsvVPtaAg16J+JL4QUUwuTp+XDA +2fc0ZQ6ikUusKPK3CA+Yytc+cLbIC/GLnFH4xhBs0lNPYowRAD6I37/m0sxwve0l +nPvdJAq9WZFKQgM4EnEyiHagjny7Mu+IKhvUam9QuVJni6sw+h/94ySa +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert26[] = { + 0x30, 0x82, 0x04, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x28, 0x1c, 0x89, 0x29, 0x66, 0x14, 0x43, 0x80, 0x42, + 0x63, 0x55, 0x3a, 0x32, 0x40, 0xae, 0xb3, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65, + 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, + 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, + 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x35, 0x30, 0x36, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x36, 0x32, 0x39, 0x32, 0x33, + 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, + 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, + 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xc0, 0x9e, 0x3a, 0x0f, 0x9a, 0xb2, 0xba, 0xd3, 0xd2, + 0xdc, 0x15, 0xec, 0xd0, 0x30, 0x54, 0x59, 0x30, 0x4d, 0x40, 0x51, 0xae, + 0x42, 0x71, 0x71, 0xd2, 0x8d, 0x53, 0x73, 0x81, 0xfe, 0xb8, 0xe0, 0xc4, + 0x96, 0xc5, 0x8e, 0x7e, 0xc2, 0xf1, 0xb7, 0x63, 0x4a, 0xcf, 0xa7, 0x1e, + 0x3f, 0xa8, 0xe7, 0xce, 0x53, 0xa0, 0xfa, 0x2d, 0xf7, 0xd6, 0xe6, 0xce, + 0x70, 0x11, 0xa6, 0xee, 0xe1, 0x03, 0x52, 0xd2, 0x68, 0xde, 0x3d, 0x08, + 0x0d, 0x87, 0xfd, 0x1c, 0xd7, 0x0b, 0x97, 0x62, 0x6d, 0x82, 0x30, 0x76, + 0x1b, 0x47, 0x3a, 0xc4, 0xf7, 0xce, 0xed, 0x1d, 0x7c, 0x8c, 0xb7, 0x17, + 0x8e, 0x53, 0x80, 0x1e, 0x1d, 0x0f, 0x5d, 0x8c, 0xf9, 0x90, 0xe4, 0x04, + 0x1e, 0x02, 0x7e, 0xcb, 0xb0, 0x49, 0xef, 0xda, 0x52, 0x25, 0xfb, 0xfb, + 0x67, 0xed, 0xdd, 0x84, 0x74, 0x59, 0x84, 0x0e, 0xf3, 0xde, 0x70, 0x66, + 0x8d, 0xe4, 0x52, 0x38, 0xf7, 0x53, 0x5a, 0x37, 0x13, 0x67, 0x0b, 0x3e, + 0xbb, 0xa8, 0x58, 0xb7, 0x2e, 0xed, 0xff, 0xb7, 0x5e, 0x11, 0x73, 0xb9, + 0x77, 0x45, 0x52, 0x67, 0x46, 0xae, 0xc4, 0xdc, 0x24, 0x81, 0x89, 0x76, + 0x0a, 0xca, 0xa1, 0x6c, 0x66, 0x73, 0x04, 0x82, 0xaa, 0xf5, 0x70, 0x6c, + 0x5f, 0x1b, 0x9a, 0x00, 0x79, 0x46, 0xd6, 0x7f, 0x7a, 0x26, 0x17, 0x30, + 0xcf, 0x39, 0x4b, 0x2c, 0x74, 0xd9, 0x89, 0x44, 0x76, 0x10, 0xd0, 0xed, + 0xf7, 0x8b, 0xbb, 0x89, 0x05, 0x75, 0x4d, 0x0b, 0x0d, 0xb3, 0xda, 0xe9, + 0xbf, 0xf1, 0x6a, 0x7d, 0x2a, 0x11, 0xdb, 0x1e, 0x9f, 0x8c, 0xe3, 0xc4, + 0x06, 0x69, 0xe1, 0x1d, 0x88, 0x45, 0x39, 0xd1, 0x6e, 0x55, 0xd8, 0xaa, + 0xb7, 0x9b, 0x6f, 0xea, 0xf4, 0xde, 0xac, 0x17, 0x11, 0x92, 0x5d, 0x40, + 0x9b, 0x83, 0x7b, 0x9a, 0xe2, 0xf7, 0xa9, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x3a, 0x30, 0x82, 0x01, 0x36, 0x30, 0x2e, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, + 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x49, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x42, 0x30, 0x40, 0x30, 0x3e, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, + 0x01, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x36, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, + 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, + 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xf3, 0xb5, 0x56, 0x0c, 0xc4, 0x09, 0xb0, 0xb4, 0xcf, 0x1f, 0xaa, + 0xf9, 0xdd, 0x23, 0x56, 0xf0, 0x77, 0xe8, 0xa1, 0xf9, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc4, 0x79, + 0xca, 0x8e, 0xa1, 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b, 0xdb, 0x31, 0x5b, + 0x94, 0x3e, 0x3f, 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0xc3, 0x7e, 0xd8, 0x83, 0x4b, 0x04, 0x4c, 0x55, 0x29, 0x2a, + 0x4f, 0x14, 0x9d, 0x9a, 0x6e, 0xde, 0x90, 0x70, 0xc1, 0xa4, 0x26, 0x4c, + 0x88, 0x8e, 0x78, 0x48, 0xef, 0xbd, 0x9c, 0xb0, 0xa0, 0xf5, 0xf0, 0x66, + 0xfc, 0xfe, 0x59, 0x26, 0xe1, 0x79, 0xef, 0xc8, 0xb7, 0x60, 0x64, 0xa8, + 0x8b, 0x47, 0xea, 0x2f, 0xe0, 0x83, 0x99, 0xda, 0x41, 0x19, 0xd7, 0xc5, + 0xbe, 0x05, 0xfa, 0xf2, 0x90, 0x11, 0xf0, 0x0a, 0xff, 0x6c, 0xdc, 0x05, + 0xb4, 0xd8, 0x06, 0x6f, 0xa4, 0x6f, 0x8d, 0xbe, 0x20, 0x2b, 0x54, 0xdb, + 0xf9, 0xa2, 0x45, 0x83, 0x9a, 0x1e, 0xa5, 0x21, 0x89, 0x35, 0x1d, 0x7c, + 0x20, 0x5c, 0x17, 0xfd, 0x04, 0x2e, 0x45, 0xd8, 0xb2, 0xc6, 0xf8, 0x42, + 0x99, 0xfc, 0x54, 0x08, 0x4e, 0x4b, 0x80, 0x5f, 0x39, 0x37, 0xba, 0x95, + 0x4e, 0xa6, 0x37, 0x0a, 0x9e, 0x93, 0x5e, 0x87, 0x5b, 0xe9, 0x90, 0xd6, + 0xa8, 0xb6, 0x65, 0x08, 0x8d, 0x61, 0x49, 0xeb, 0x83, 0x20, 0xa9, 0x5d, + 0x1b, 0x16, 0x60, 0x62, 0x6b, 0x2f, 0x54, 0xfb, 0x5a, 0x02, 0x0d, 0x7a, + 0x27, 0xe2, 0x4b, 0xe1, 0x05, 0x14, 0xc2, 0xe4, 0xe9, 0xf9, 0x70, 0xc0, + 0xd9, 0xf7, 0x34, 0x65, 0x0e, 0xa2, 0x91, 0x4b, 0xac, 0x28, 0xf2, 0xb7, + 0x08, 0x0f, 0x98, 0xca, 0xd7, 0x3e, 0x70, 0xb6, 0xc8, 0x0b, 0xf1, 0x8b, + 0x9c, 0x51, 0xf8, 0xc6, 0x10, 0x6c, 0xd2, 0x53, 0x4f, 0x62, 0x8c, 0x11, + 0x00, 0x3e, 0x88, 0xdf, 0xbf, 0xe6, 0xd2, 0xcc, 0x70, 0xbd, 0xed, 0x25, + 0x9c, 0xfb, 0xdd, 0x24, 0x0a, 0xbd, 0x59, 0x91, 0x4a, 0x42, 0x03, 0x38, + 0x12, 0x71, 0x32, 0x88, 0x76, 0xa0, 0x8e, 0x7c, 0xbb, 0x32, 0xef, 0x88, + 0x2a, 0x1b, 0xd4, 0x6a, 0x6f, 0x50, 0xb9, 0x52, 0x67, 0x8b, 0xab, 0x30, + 0xfa, 0x1f, 0xfd, 0xe3, 0x24, 0x9a, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + e4:05:47:83:0e:0c:64:52:97:6f:7a:35:49:c0:dd:48 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=PL, O=Unizeto Technologies S.A., OU=Certum Certification Authority, CN=Certum Trusted Network CA + Validity + Not Before: Jan 21 12:00:00 2015 GMT + Not After : Jan 18 12:00:00 2025 GMT + Subject: C=RU, O=Yandex LLC, OU=Yandex Certification Authority, CN=Yandex CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a6:05:24:76:61:b9:9e:42:60:22:63:85:59:e5: + 9d:88:0d:df:ef:21:64:5a:26:94:71:3a:a4:7f:2b: + 53:c3:ac:7b:ba:95:42:6d:6a:5b:d6:7e:78:0c:67: + 40:98:2f:6a:2d:d0:b7:18:3a:7e:99:60:01:e5:27: + bf:ff:49:f5:cd:c4:58:c3:4c:e1:70:d5:fd:08:a8: + 79:95:76:1c:0e:05:41:fa:bd:80:38:2a:87:4f:c1: + 67:42:aa:17:a6:ee:a7:8c:8e:ef:2d:7f:7a:1d:05: + 17:8f:7e:3b:92:35:f5:68:ed:93:03:55:23:4f:4b: + a2:00:86:65:91:0f:eb:f6:3c:d5:db:6d:0e:ed:e8: + 7c:3a:c8:ba:b7:53:c1:a4:d8:40:02:e5:b5:a2:ca: + bf:da:9c:94:0d:fc:c5:1c:2a:59:88:62:57:93:2e: + 11:f0:38:2c:7a:81:2a:f2:25:15:17:35:70:2c:4b: + f7:23:4c:82:ef:33:9f:c2:9a:0b:a3:e2:5d:6b:38: + 77:f9:60:33:cf:2e:7b:56:b7:13:93:1f:34:97:71: + 99:76:02:46:35:14:7c:dc:ca:48:8a:0a:72:4b:78: + 6d:82:34:96:13:45:cf:02:2f:50:13:39:43:89:c0: + e1:74:d7:28:71:21:e5:aa:97:0e:ee:46:ec:93:f7: + 23:7d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Subject Key Identifier: + 37:5C:E3:19:E0:B2:8E:A1:A8:4E:D2:CF:AB:D0:DC:E3:0B:5C:35:4D + X509v3 Authority Key Identifier: + keyid:08:76:CD:CB:07:FF:24:F6:C5:CD:ED:BB:90:BC:E2:84:37:46:75:F7 + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.certum.pl/ctnca.crl + + Authority Information Access: + OCSP - URI:http://subca.ocsp-certum.com + CA Issuers - URI:http://repository.certum.pl/ctnca.cer + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.certum.pl/CPS + + Signature Algorithm: sha256WithRSAEncryption + 02:5e:8e:7b:e0:66:a1:c6:ab:8b:18:1f:0e:b9:c4:cd:71:db: + 44:5c:03:7d:65:ea:b8:47:b5:1e:ce:24:70:a0:7f:d3:df:66: + 4b:8c:90:e2:a5:ed:9b:94:36:b4:a8:be:f0:74:8c:26:92:75: + 9d:56:50:9e:ad:d0:1a:a0:df:a4:14:56:10:75:93:7a:c1:f4: + 53:a0:76:74:2c:72:ba:b5:d1:c9:e2:dc:46:86:3f:1d:f6:33: + 87:59:ec:9c:dc:2d:1e:4d:43:1a:ce:ba:d9:87:7e:e2:47:45: + 72:3d:28:03:c9:0a:4d:e0:57:a3:5e:6e:7e:cc:5a:c8:c4:78: + 01:57:68:7a:38:3b:53:36:e7:92:6d:8a:2c:2f:d7:8b:b6:34: + a8:d1:b6:f8:5e:3b:ab:ed:a5:8f:39:6f:45:ad:cb:63:ed:6a: + 64:c9:10:a7:03:08:12:53:b1:1c:af:ca:f7:53:fc:d8:29:4b: + 1b:fb:38:cd:c0:63:ff:5f:e4:b9:8d:5e:aa:2b:d2:c3:22:35: + 31:f6:30:0e:53:32:f4:93:c5:43:cb:c8:f0:15:56:8f:00:19: + 87:ca:78:22:8d:a0:2e:db:2f:a0:c3:7e:29:5d:91:25:84:1d: + 1d:39:ab:1b:c5:d6:91:fe:69:0e:46:80:bc:45:7b:35:53:2a: + df:00:b6:77 +-----BEGIN CERTIFICATE----- +MIIEqDCCA5CgAwIBAgIRAOQFR4MODGRSl296NUnA3UgwDQYJKoZIhvcNAQELBQAw +fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG +A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTAeFw0xNTAxMjExMjAwMDBa +Fw0yNTAxMTgxMjAwMDBaMF8xCzAJBgNVBAYTAlJVMRMwEQYDVQQKEwpZYW5kZXgg +TExDMScwJQYDVQQLEx5ZYW5kZXggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEjAQ +BgNVBAMTCVlhbmRleCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKYFJHZhuZ5CYCJjhVnlnYgN3+8hZFomlHE6pH8rU8Ose7qVQm1qW9Z+eAxnQJgv +ai3Qtxg6fplgAeUnv/9J9c3EWMNM4XDV/QioeZV2HA4FQfq9gDgqh0/BZ0KqF6bu +p4yO7y1/eh0FF49+O5I19WjtkwNVI09LogCGZZEP6/Y81dttDu3ofDrIurdTwaTY +QALltaLKv9qclA38xRwqWYhiV5MuEfA4LHqBKvIlFRc1cCxL9yNMgu8zn8KaC6Pi +XWs4d/lgM88ue1a3E5MfNJdxmXYCRjUUfNzKSIoKckt4bYI0lhNFzwIvUBM5Q4nA +4XTXKHEh5aqXDu5G7JP3I30CAwEAAaOCAT4wggE6MA8GA1UdEwEB/wQFMAMBAf8w +HQYDVR0OBBYEFDdc4xngso6hqE7Sz6vQ3OMLXDVNMB8GA1UdIwQYMBaAFAh2zcsH +/yT2xc3tu5C84oQ3RnX3MA4GA1UdDwEB/wQEAwIBBjAvBgNVHR8EKDAmMCSgIqAg +hh5odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYS5jcmwwawYIKwYBBQUHAQEEXzBd +MCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0uY29tMDEGCCsG +AQUFBzAChiVodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY3RuY2EuY2VyMDkG +A1UdIAQyMDAwLgYEVR0gADAmMCQGCCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1 +bS5wbC9DUFMwDQYJKoZIhvcNAQELBQADggEBAAJejnvgZqHGq4sYHw65xM1x20Rc +A31l6rhHtR7OJHCgf9PfZkuMkOKl7ZuUNrSovvB0jCaSdZ1WUJ6t0Bqg36QUVhB1 +k3rB9FOgdnQscrq10cni3EaGPx32M4dZ7JzcLR5NQxrOutmHfuJHRXI9KAPJCk3g +V6Nebn7MWsjEeAFXaHo4O1M255Jtiiwv14u2NKjRtvheO6vtpY85b0Wty2PtamTJ +EKcDCBJTsRyvyvdT/NgpSxv7OM3AY/9f5LmNXqor0sMiNTH2MA5TMvSTxUPLyPAV +Vo8AGYfKeCKNoC7bL6DDfildkSWEHR05qxvF1pH+aQ5GgLxFezVTKt8Atnc= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert27[] = { + 0x30, 0x82, 0x04, 0xa8, 0x30, 0x82, 0x03, 0x90, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x11, 0x00, 0xe4, 0x05, 0x47, 0x83, 0x0e, 0x0c, 0x64, 0x52, + 0x97, 0x6f, 0x7a, 0x35, 0x49, 0xc0, 0xdd, 0x48, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, + 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x50, 0x4c, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x19, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x54, 0x65, 0x63, + 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x20, 0x53, 0x2e, + 0x41, 0x2e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x1e, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x22, 0x30, 0x20, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x35, 0x30, 0x31, 0x32, 0x31, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, + 0x17, 0x0d, 0x32, 0x35, 0x30, 0x31, 0x31, 0x38, 0x31, 0x32, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x52, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x59, 0x61, 0x6e, 0x64, 0x65, 0x78, 0x20, + 0x4c, 0x4c, 0x43, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x1e, 0x59, 0x61, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x59, 0x61, 0x6e, 0x64, 0x65, + 0x78, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa6, 0x05, 0x24, 0x76, 0x61, 0xb9, 0x9e, 0x42, 0x60, 0x22, 0x63, + 0x85, 0x59, 0xe5, 0x9d, 0x88, 0x0d, 0xdf, 0xef, 0x21, 0x64, 0x5a, 0x26, + 0x94, 0x71, 0x3a, 0xa4, 0x7f, 0x2b, 0x53, 0xc3, 0xac, 0x7b, 0xba, 0x95, + 0x42, 0x6d, 0x6a, 0x5b, 0xd6, 0x7e, 0x78, 0x0c, 0x67, 0x40, 0x98, 0x2f, + 0x6a, 0x2d, 0xd0, 0xb7, 0x18, 0x3a, 0x7e, 0x99, 0x60, 0x01, 0xe5, 0x27, + 0xbf, 0xff, 0x49, 0xf5, 0xcd, 0xc4, 0x58, 0xc3, 0x4c, 0xe1, 0x70, 0xd5, + 0xfd, 0x08, 0xa8, 0x79, 0x95, 0x76, 0x1c, 0x0e, 0x05, 0x41, 0xfa, 0xbd, + 0x80, 0x38, 0x2a, 0x87, 0x4f, 0xc1, 0x67, 0x42, 0xaa, 0x17, 0xa6, 0xee, + 0xa7, 0x8c, 0x8e, 0xef, 0x2d, 0x7f, 0x7a, 0x1d, 0x05, 0x17, 0x8f, 0x7e, + 0x3b, 0x92, 0x35, 0xf5, 0x68, 0xed, 0x93, 0x03, 0x55, 0x23, 0x4f, 0x4b, + 0xa2, 0x00, 0x86, 0x65, 0x91, 0x0f, 0xeb, 0xf6, 0x3c, 0xd5, 0xdb, 0x6d, + 0x0e, 0xed, 0xe8, 0x7c, 0x3a, 0xc8, 0xba, 0xb7, 0x53, 0xc1, 0xa4, 0xd8, + 0x40, 0x02, 0xe5, 0xb5, 0xa2, 0xca, 0xbf, 0xda, 0x9c, 0x94, 0x0d, 0xfc, + 0xc5, 0x1c, 0x2a, 0x59, 0x88, 0x62, 0x57, 0x93, 0x2e, 0x11, 0xf0, 0x38, + 0x2c, 0x7a, 0x81, 0x2a, 0xf2, 0x25, 0x15, 0x17, 0x35, 0x70, 0x2c, 0x4b, + 0xf7, 0x23, 0x4c, 0x82, 0xef, 0x33, 0x9f, 0xc2, 0x9a, 0x0b, 0xa3, 0xe2, + 0x5d, 0x6b, 0x38, 0x77, 0xf9, 0x60, 0x33, 0xcf, 0x2e, 0x7b, 0x56, 0xb7, + 0x13, 0x93, 0x1f, 0x34, 0x97, 0x71, 0x99, 0x76, 0x02, 0x46, 0x35, 0x14, + 0x7c, 0xdc, 0xca, 0x48, 0x8a, 0x0a, 0x72, 0x4b, 0x78, 0x6d, 0x82, 0x34, + 0x96, 0x13, 0x45, 0xcf, 0x02, 0x2f, 0x50, 0x13, 0x39, 0x43, 0x89, 0xc0, + 0xe1, 0x74, 0xd7, 0x28, 0x71, 0x21, 0xe5, 0xaa, 0x97, 0x0e, 0xee, 0x46, + 0xec, 0x93, 0xf7, 0x23, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x3e, 0x30, 0x82, 0x01, 0x3a, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x37, 0x5c, + 0xe3, 0x19, 0xe0, 0xb2, 0x8e, 0xa1, 0xa8, 0x4e, 0xd2, 0xcf, 0xab, 0xd0, + 0xdc, 0xe3, 0x0b, 0x5c, 0x35, 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x08, 0x76, 0xcd, 0xcb, 0x07, + 0xff, 0x24, 0xf6, 0xc5, 0xcd, 0xed, 0xbb, 0x90, 0xbc, 0xe2, 0x84, 0x37, + 0x46, 0x75, 0xf7, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0xa0, 0x22, 0xa0, 0x20, + 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x63, + 0x74, 0x6e, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x6b, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x5f, 0x30, 0x5d, + 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x75, 0x62, + 0x63, 0x61, 0x2e, 0x6f, 0x63, 0x73, 0x70, 0x2d, 0x63, 0x65, 0x72, 0x74, + 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e, 0x70, 0x6c, 0x2f, + 0x63, 0x74, 0x6e, 0x63, 0x61, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x39, 0x06, + 0x03, 0x55, 0x1d, 0x20, 0x04, 0x32, 0x30, 0x30, 0x30, 0x2e, 0x06, 0x04, + 0x55, 0x1d, 0x20, 0x00, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x18, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, + 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x02, 0x5e, 0x8e, 0x7b, 0xe0, 0x66, 0xa1, 0xc6, + 0xab, 0x8b, 0x18, 0x1f, 0x0e, 0xb9, 0xc4, 0xcd, 0x71, 0xdb, 0x44, 0x5c, + 0x03, 0x7d, 0x65, 0xea, 0xb8, 0x47, 0xb5, 0x1e, 0xce, 0x24, 0x70, 0xa0, + 0x7f, 0xd3, 0xdf, 0x66, 0x4b, 0x8c, 0x90, 0xe2, 0xa5, 0xed, 0x9b, 0x94, + 0x36, 0xb4, 0xa8, 0xbe, 0xf0, 0x74, 0x8c, 0x26, 0x92, 0x75, 0x9d, 0x56, + 0x50, 0x9e, 0xad, 0xd0, 0x1a, 0xa0, 0xdf, 0xa4, 0x14, 0x56, 0x10, 0x75, + 0x93, 0x7a, 0xc1, 0xf4, 0x53, 0xa0, 0x76, 0x74, 0x2c, 0x72, 0xba, 0xb5, + 0xd1, 0xc9, 0xe2, 0xdc, 0x46, 0x86, 0x3f, 0x1d, 0xf6, 0x33, 0x87, 0x59, + 0xec, 0x9c, 0xdc, 0x2d, 0x1e, 0x4d, 0x43, 0x1a, 0xce, 0xba, 0xd9, 0x87, + 0x7e, 0xe2, 0x47, 0x45, 0x72, 0x3d, 0x28, 0x03, 0xc9, 0x0a, 0x4d, 0xe0, + 0x57, 0xa3, 0x5e, 0x6e, 0x7e, 0xcc, 0x5a, 0xc8, 0xc4, 0x78, 0x01, 0x57, + 0x68, 0x7a, 0x38, 0x3b, 0x53, 0x36, 0xe7, 0x92, 0x6d, 0x8a, 0x2c, 0x2f, + 0xd7, 0x8b, 0xb6, 0x34, 0xa8, 0xd1, 0xb6, 0xf8, 0x5e, 0x3b, 0xab, 0xed, + 0xa5, 0x8f, 0x39, 0x6f, 0x45, 0xad, 0xcb, 0x63, 0xed, 0x6a, 0x64, 0xc9, + 0x10, 0xa7, 0x03, 0x08, 0x12, 0x53, 0xb1, 0x1c, 0xaf, 0xca, 0xf7, 0x53, + 0xfc, 0xd8, 0x29, 0x4b, 0x1b, 0xfb, 0x38, 0xcd, 0xc0, 0x63, 0xff, 0x5f, + 0xe4, 0xb9, 0x8d, 0x5e, 0xaa, 0x2b, 0xd2, 0xc3, 0x22, 0x35, 0x31, 0xf6, + 0x30, 0x0e, 0x53, 0x32, 0xf4, 0x93, 0xc5, 0x43, 0xcb, 0xc8, 0xf0, 0x15, + 0x56, 0x8f, 0x00, 0x19, 0x87, 0xca, 0x78, 0x22, 0x8d, 0xa0, 0x2e, 0xdb, + 0x2f, 0xa0, 0xc3, 0x7e, 0x29, 0x5d, 0x91, 0x25, 0x84, 0x1d, 0x1d, 0x39, + 0xab, 0x1b, 0xc5, 0xd6, 0x91, 0xfe, 0x69, 0x0e, 0x46, 0x80, 0xbc, 0x45, + 0x7b, 0x35, 0x53, 0x2a, 0xdf, 0x00, 0xb6, 0x77, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 5d:72:fb:33:76:20:f6:4c:72:80:db:e9:12:81:ff:6a + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=thawte, Inc., CN=thawte EV SSL CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c4:dd:da:94:1e:32:b2:2e:a0:83:c0:a6:7d:5f: + 65:2d:fd:27:b8:73:0e:f8:0b:a9:d4:56:26:69:98: + 67:35:39:64:58:ce:82:6f:98:94:d1:8f:e0:90:d6: + ed:55:4b:98:4b:d7:10:59:34:02:1b:e7:51:31:51: + c4:38:c2:bc:db:03:5c:ca:e1:7c:dc:4f:59:97:ea: + 07:7f:0f:85:3e:92:ea:aa:a7:d9:be:01:41:e4:62: + 56:47:36:bd:57:91:e6:21:d3:f8:41:0b:d8:ba:e8: + ed:81:ad:70:c0:8b:6e:f3:89:6e:27:9e:a6:a6:73: + 59:bb:71:00:d4:4f:4b:48:e9:d5:c9:27:36:9c:7c: + 1c:02:aa:ac:bd:3b:d1:53:83:6a:1f:e6:08:47:33: + a7:b1:9f:02:be:9b:47:ed:33:04:dc:1c:80:27:d1: + 4a:33:a0:8c:eb:01:47:a1:32:90:64:7b:c4:e0:84: + c9:32:e9:dd:34:1f:8a:68:67:f3:ad:10:63:eb:ee: + 8a:9a:b1:2a:1b:26:74:a1:2a:b0:8f:fe:52:98:46: + 97:cf:a3:56:1c:6f:6e:99:97:8d:26:0e:a9:ec:c2: + 53:70:fc:7a:a5:19:49:bd:b5:17:82:55:de:97:e0: + 5d:62:84:81:f0:70:a8:34:53:4f:14:fd:3d:5d:3d: + 6f:b9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://t2.symcb.com + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.thawte.com/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://t1.symcb.com/ThawtePCA.crl + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-536 + X509v3 Subject Key Identifier: + F0:70:51:DA:D3:2A:91:4F:52:77:D7:86:77:74:0F:CE:71:1A:6C:22 + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha256WithRSAEncryption + a1:2e:94:3e:9b:16:f4:58:1a:6f:c1:fa:c1:7e:43:93:b2:c3: + f7:89:eb:13:62:5d:dd:cc:61:13:2b:1d:4e:88:79:11:62:14: + 37:30:46:ff:89:62:10:85:2a:87:1e:f8:e2:af:fe:93:02:93: + ca:f2:e9:46:03:6b:a1:1a:ac:d5:f0:80:1b:98:6f:b8:3a:50: + f8:54:71:06:03:e7:84:cc:8e:61:d2:5f:4d:0c:97:02:65:b5: + 8c:26:bc:05:98:f4:dc:c6:af:e4:57:7f:e3:dc:a1:d7:27:47: + 2a:e0:2c:3f:09:74:dc:5a:e5:b5:7c:fa:82:9a:15:fa:74:2b: + 84:2e:6b:ac:ef:35:a6:30:fa:47:4a:aa:36:44:f6:5a:91:07: + d3:e4:4e:97:3f:a6:53:d8:29:33:32:6f:8b:3d:b5:a5:0d:e5: + e4:8a:e8:f5:c0:fa:af:d8:37:28:27:c3:ed:34:31:d9:7c:a6: + af:4d:12:4f:d0:2b:92:9c:69:95:f2:28:a6:fe:a8:c6:e0:2c: + 4d:36:eb:11:34:d6:e1:81:99:9d:41:f2:e7:c5:57:05:0e:19: + ca:af:42:39:1f:a7:27:5e:e0:0a:17:b8:ae:47:ab:92:f1:8a: + 04:df:30:e0:bb:4f:8a:f9:1b:88:4f:03:b4:25:7a:78:de:2e: + 7d:29:d1:31 +-----BEGIN CERTIFICATE----- +MIIErzCCA5egAwIBAgIQXXL7M3Yg9kxygNvpEoH/ajANBgkqhkiG9w0BAQsFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx +MDMwMjM1OTU5WjBEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu +MR4wHAYDVQQDExV0aGF3dGUgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDE3dqUHjKyLqCDwKZ9X2Ut/Se4cw74C6nUViZpmGc1 +OWRYzoJvmJTRj+CQ1u1VS5hL1xBZNAIb51ExUcQ4wrzbA1zK4XzcT1mX6gd/D4U+ +kuqqp9m+AUHkYlZHNr1XkeYh0/hBC9i66O2BrXDAi27ziW4nnqamc1m7cQDUT0tI +6dXJJzacfBwCqqy9O9FTg2of5ghHM6exnwK+m0ftMwTcHIAn0UozoIzrAUehMpBk +e8TghMky6d00H4poZ/OtEGPr7oqasSobJnShKrCP/lKYRpfPo1Ycb26Zl40mDqns +wlNw/HqlGUm9tReCVd6X4F1ihIHwcKg0U08U/T1dPW+5AgMBAAGjggE1MIIBMTAS +BgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQj +MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly90Mi5zeW1jYi5jb20wOwYDVR0gBDQwMjAw +BgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3Bz +MDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly90MS5zeW1jYi5jb20vVGhhd3RlUENB +LmNybDApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01MzYw +HQYDVR0OBBYEFPBwUdrTKpFPUnfXhnd0D85xGmwiMB8GA1UdIwQYMBaAFHtbRc+v +zst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQChLpQ+mxb0WBpvwfrB +fkOTssP3iesTYl3dzGETKx1OiHkRYhQ3MEb/iWIQhSqHHvjir/6TApPK8ulGA2uh +GqzV8IAbmG+4OlD4VHEGA+eEzI5h0l9NDJcCZbWMJrwFmPTcxq/kV3/j3KHXJ0cq +4Cw/CXTcWuW1fPqCmhX6dCuELmus7zWmMPpHSqo2RPZakQfT5E6XP6ZT2CkzMm+L +PbWlDeXkiuj1wPqv2DcoJ8PtNDHZfKavTRJP0CuSnGmV8iim/qjG4CxNNusRNNbh +gZmdQfLnxVcFDhnKr0I5H6cnXuAKF7iuR6uS8YoE3zDgu0+K+RuITwO0JXp43i59 +KdEx +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert28[] = { + 0x30, 0x82, 0x04, 0xaf, 0x30, 0x82, 0x03, 0x97, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x5d, 0x72, 0xfb, 0x33, 0x76, 0x20, 0xf6, 0x4c, 0x72, + 0x80, 0xdb, 0xe9, 0x12, 0x81, 0xff, 0x6a, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, + 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x44, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c, + 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xc4, 0xdd, 0xda, 0x94, 0x1e, 0x32, 0xb2, + 0x2e, 0xa0, 0x83, 0xc0, 0xa6, 0x7d, 0x5f, 0x65, 0x2d, 0xfd, 0x27, 0xb8, + 0x73, 0x0e, 0xf8, 0x0b, 0xa9, 0xd4, 0x56, 0x26, 0x69, 0x98, 0x67, 0x35, + 0x39, 0x64, 0x58, 0xce, 0x82, 0x6f, 0x98, 0x94, 0xd1, 0x8f, 0xe0, 0x90, + 0xd6, 0xed, 0x55, 0x4b, 0x98, 0x4b, 0xd7, 0x10, 0x59, 0x34, 0x02, 0x1b, + 0xe7, 0x51, 0x31, 0x51, 0xc4, 0x38, 0xc2, 0xbc, 0xdb, 0x03, 0x5c, 0xca, + 0xe1, 0x7c, 0xdc, 0x4f, 0x59, 0x97, 0xea, 0x07, 0x7f, 0x0f, 0x85, 0x3e, + 0x92, 0xea, 0xaa, 0xa7, 0xd9, 0xbe, 0x01, 0x41, 0xe4, 0x62, 0x56, 0x47, + 0x36, 0xbd, 0x57, 0x91, 0xe6, 0x21, 0xd3, 0xf8, 0x41, 0x0b, 0xd8, 0xba, + 0xe8, 0xed, 0x81, 0xad, 0x70, 0xc0, 0x8b, 0x6e, 0xf3, 0x89, 0x6e, 0x27, + 0x9e, 0xa6, 0xa6, 0x73, 0x59, 0xbb, 0x71, 0x00, 0xd4, 0x4f, 0x4b, 0x48, + 0xe9, 0xd5, 0xc9, 0x27, 0x36, 0x9c, 0x7c, 0x1c, 0x02, 0xaa, 0xac, 0xbd, + 0x3b, 0xd1, 0x53, 0x83, 0x6a, 0x1f, 0xe6, 0x08, 0x47, 0x33, 0xa7, 0xb1, + 0x9f, 0x02, 0xbe, 0x9b, 0x47, 0xed, 0x33, 0x04, 0xdc, 0x1c, 0x80, 0x27, + 0xd1, 0x4a, 0x33, 0xa0, 0x8c, 0xeb, 0x01, 0x47, 0xa1, 0x32, 0x90, 0x64, + 0x7b, 0xc4, 0xe0, 0x84, 0xc9, 0x32, 0xe9, 0xdd, 0x34, 0x1f, 0x8a, 0x68, + 0x67, 0xf3, 0xad, 0x10, 0x63, 0xeb, 0xee, 0x8a, 0x9a, 0xb1, 0x2a, 0x1b, + 0x26, 0x74, 0xa1, 0x2a, 0xb0, 0x8f, 0xfe, 0x52, 0x98, 0x46, 0x97, 0xcf, + 0xa3, 0x56, 0x1c, 0x6f, 0x6e, 0x99, 0x97, 0x8d, 0x26, 0x0e, 0xa9, 0xec, + 0xc2, 0x53, 0x70, 0xfc, 0x7a, 0xa5, 0x19, 0x49, 0xbd, 0xb5, 0x17, 0x82, + 0x55, 0xde, 0x97, 0xe0, 0x5d, 0x62, 0x84, 0x81, 0xf0, 0x70, 0xa8, 0x34, + 0x53, 0x4f, 0x14, 0xfd, 0x3d, 0x5d, 0x3d, 0x6f, 0xb9, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x35, 0x30, 0x82, 0x01, 0x31, 0x30, 0x12, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, + 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, + 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, + 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, + 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, + 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, + 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x74, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, + 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, + 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x36, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf0, 0x70, + 0x51, 0xda, 0xd3, 0x2a, 0x91, 0x4f, 0x52, 0x77, 0xd7, 0x86, 0x77, 0x74, + 0x0f, 0xce, 0x71, 0x1a, 0x6c, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, + 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, + 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa1, + 0x2e, 0x94, 0x3e, 0x9b, 0x16, 0xf4, 0x58, 0x1a, 0x6f, 0xc1, 0xfa, 0xc1, + 0x7e, 0x43, 0x93, 0xb2, 0xc3, 0xf7, 0x89, 0xeb, 0x13, 0x62, 0x5d, 0xdd, + 0xcc, 0x61, 0x13, 0x2b, 0x1d, 0x4e, 0x88, 0x79, 0x11, 0x62, 0x14, 0x37, + 0x30, 0x46, 0xff, 0x89, 0x62, 0x10, 0x85, 0x2a, 0x87, 0x1e, 0xf8, 0xe2, + 0xaf, 0xfe, 0x93, 0x02, 0x93, 0xca, 0xf2, 0xe9, 0x46, 0x03, 0x6b, 0xa1, + 0x1a, 0xac, 0xd5, 0xf0, 0x80, 0x1b, 0x98, 0x6f, 0xb8, 0x3a, 0x50, 0xf8, + 0x54, 0x71, 0x06, 0x03, 0xe7, 0x84, 0xcc, 0x8e, 0x61, 0xd2, 0x5f, 0x4d, + 0x0c, 0x97, 0x02, 0x65, 0xb5, 0x8c, 0x26, 0xbc, 0x05, 0x98, 0xf4, 0xdc, + 0xc6, 0xaf, 0xe4, 0x57, 0x7f, 0xe3, 0xdc, 0xa1, 0xd7, 0x27, 0x47, 0x2a, + 0xe0, 0x2c, 0x3f, 0x09, 0x74, 0xdc, 0x5a, 0xe5, 0xb5, 0x7c, 0xfa, 0x82, + 0x9a, 0x15, 0xfa, 0x74, 0x2b, 0x84, 0x2e, 0x6b, 0xac, 0xef, 0x35, 0xa6, + 0x30, 0xfa, 0x47, 0x4a, 0xaa, 0x36, 0x44, 0xf6, 0x5a, 0x91, 0x07, 0xd3, + 0xe4, 0x4e, 0x97, 0x3f, 0xa6, 0x53, 0xd8, 0x29, 0x33, 0x32, 0x6f, 0x8b, + 0x3d, 0xb5, 0xa5, 0x0d, 0xe5, 0xe4, 0x8a, 0xe8, 0xf5, 0xc0, 0xfa, 0xaf, + 0xd8, 0x37, 0x28, 0x27, 0xc3, 0xed, 0x34, 0x31, 0xd9, 0x7c, 0xa6, 0xaf, + 0x4d, 0x12, 0x4f, 0xd0, 0x2b, 0x92, 0x9c, 0x69, 0x95, 0xf2, 0x28, 0xa6, + 0xfe, 0xa8, 0xc6, 0xe0, 0x2c, 0x4d, 0x36, 0xeb, 0x11, 0x34, 0xd6, 0xe1, + 0x81, 0x99, 0x9d, 0x41, 0xf2, 0xe7, 0xc5, 0x57, 0x05, 0x0e, 0x19, 0xca, + 0xaf, 0x42, 0x39, 0x1f, 0xa7, 0x27, 0x5e, 0xe0, 0x0a, 0x17, 0xb8, 0xae, + 0x47, 0xab, 0x92, 0xf1, 0x8a, 0x04, 0xdf, 0x30, 0xe0, 0xbb, 0x4f, 0x8a, + 0xf9, 0x1b, 0x88, 0x4f, 0x03, 0xb4, 0x25, 0x7a, 0x78, 0xde, 0x2e, 0x7d, + 0x29, 0xd1, 0x31, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 04:e1:e7:a4:dc:5c:f2:f3:6d:c0:2b:42:b8:5d:15:9f + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA + Validity + Not Before: Oct 22 12:00:00 2013 GMT + Not After : Oct 22 12:00:00 2028 GMT + Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b6:e0:2f:c2:24:06:c8:6d:04:5f:d7:ef:0a:64: + 06:b2:7d:22:26:65:16:ae:42:40:9b:ce:dc:9f:9f: + 76:07:3e:c3:30:55:87:19:b9:4f:94:0e:5a:94:1f: + 55:56:b4:c2:02:2a:af:d0:98:ee:0b:40:d7:c4:d0: + 3b:72:c8:14:9e:ef:90:b1:11:a9:ae:d2:c8:b8:43: + 3a:d9:0b:0b:d5:d5:95:f5:40:af:c8:1d:ed:4d:9c: + 5f:57:b7:86:50:68:99:f5:8a:da:d2:c7:05:1f:a8: + 97:c9:dc:a4:b1:82:84:2d:c6:ad:a5:9c:c7:19:82: + a6:85:0f:5e:44:58:2a:37:8f:fd:35:f1:0b:08:27: + 32:5a:f5:bb:8b:9e:a4:bd:51:d0:27:e2:dd:3b:42: + 33:a3:05:28:c4:bb:28:cc:9a:ac:2b:23:0d:78:c6: + 7b:e6:5e:71:b7:4a:3e:08:fb:81:b7:16:16:a1:9d: + 23:12:4d:e5:d7:92:08:ac:75:a4:9c:ba:cd:17:b2: + 1e:44:35:65:7f:53:25:39:d1:1c:0a:9a:63:1b:19: + 92:74:68:0a:37:c2:c2:52:48:cb:39:5a:a2:b6:e1: + 5d:c1:dd:a0:20:b8:21:a2:93:26:6f:14:4a:21:41: + c7:ed:6d:9b:f2:48:2f:f3:03:f5:a2:68:92:53:2f: + 5e:e3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.digicert.com/CPS + + X509v3 Subject Key Identifier: + 51:68:FF:90:AF:02:07:75:3C:CC:D9:65:64:62:A2:12:B8:59:72:3B + X509v3 Authority Key Identifier: + keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 + + Signature Algorithm: sha256WithRSAEncryption + 18:8a:95:89:03:e6:6d:df:5c:fc:1d:68:ea:4a:8f:83:d6:51: + 2f:8d:6b:44:16:9e:ac:63:f5:d2:6e:6c:84:99:8b:aa:81:71: + 84:5b:ed:34:4e:b0:b7:79:92:29:cc:2d:80:6a:f0:8e:20:e1: + 79:a4:fe:03:47:13:ea:f5:86:ca:59:71:7d:f4:04:96:6b:d3: + 59:58:3d:fe:d3:31:25:5c:18:38:84:a3:e6:9f:82:fd:8c:5b: + 98:31:4e:cd:78:9e:1a:fd:85:cb:49:aa:f2:27:8b:99:72:fc: + 3e:aa:d5:41:0b:da:d5:36:a1:bf:1c:6e:47:49:7f:5e:d9:48: + 7c:03:d9:fd:8b:49:a0:98:26:42:40:eb:d6:92:11:a4:64:0a: + 57:54:c4:f5:1d:d6:02:5e:6b:ac:ee:c4:80:9a:12:72:fa:56: + 93:d7:ff:bf:30:85:06:30:bf:0b:7f:4e:ff:57:05:9d:24:ed: + 85:c3:2b:fb:a6:75:a8:ac:2d:16:ef:7d:79:27:b2:eb:c2:9d: + 0b:07:ea:aa:85:d3:01:a3:20:28:41:59:43:28:d2:81:e3:aa: + f6:ec:7b:3b:77:b6:40:62:80:05:41:45:01:ef:17:06:3e:de: + c0:33:9b:67:d3:61:2e:72:87:e4:69:fc:12:00:57:40:1e:70: + f5:1e:c9:b4 +-----BEGIN CERTIFICATE----- +MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy +YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2 +4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC +Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1 +itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn +4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X +sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft +bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy +dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t +L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG +BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ +UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D +aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd +aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH +E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly +/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu +xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF +0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae +cPUeybQ= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert29[] = { + 0x30, 0x82, 0x04, 0xb1, 0x30, 0x82, 0x03, 0x99, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x04, 0xe1, 0xe7, 0xa4, 0xdc, 0x5c, 0xf2, 0xf3, 0x6d, + 0xc0, 0x2b, 0x42, 0xb8, 0x5d, 0x15, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32, + 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x70, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, + 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, + 0x32, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, + 0x61, 0x6e, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6, + 0xe0, 0x2f, 0xc2, 0x24, 0x06, 0xc8, 0x6d, 0x04, 0x5f, 0xd7, 0xef, 0x0a, + 0x64, 0x06, 0xb2, 0x7d, 0x22, 0x26, 0x65, 0x16, 0xae, 0x42, 0x40, 0x9b, + 0xce, 0xdc, 0x9f, 0x9f, 0x76, 0x07, 0x3e, 0xc3, 0x30, 0x55, 0x87, 0x19, + 0xb9, 0x4f, 0x94, 0x0e, 0x5a, 0x94, 0x1f, 0x55, 0x56, 0xb4, 0xc2, 0x02, + 0x2a, 0xaf, 0xd0, 0x98, 0xee, 0x0b, 0x40, 0xd7, 0xc4, 0xd0, 0x3b, 0x72, + 0xc8, 0x14, 0x9e, 0xef, 0x90, 0xb1, 0x11, 0xa9, 0xae, 0xd2, 0xc8, 0xb8, + 0x43, 0x3a, 0xd9, 0x0b, 0x0b, 0xd5, 0xd5, 0x95, 0xf5, 0x40, 0xaf, 0xc8, + 0x1d, 0xed, 0x4d, 0x9c, 0x5f, 0x57, 0xb7, 0x86, 0x50, 0x68, 0x99, 0xf5, + 0x8a, 0xda, 0xd2, 0xc7, 0x05, 0x1f, 0xa8, 0x97, 0xc9, 0xdc, 0xa4, 0xb1, + 0x82, 0x84, 0x2d, 0xc6, 0xad, 0xa5, 0x9c, 0xc7, 0x19, 0x82, 0xa6, 0x85, + 0x0f, 0x5e, 0x44, 0x58, 0x2a, 0x37, 0x8f, 0xfd, 0x35, 0xf1, 0x0b, 0x08, + 0x27, 0x32, 0x5a, 0xf5, 0xbb, 0x8b, 0x9e, 0xa4, 0xbd, 0x51, 0xd0, 0x27, + 0xe2, 0xdd, 0x3b, 0x42, 0x33, 0xa3, 0x05, 0x28, 0xc4, 0xbb, 0x28, 0xcc, + 0x9a, 0xac, 0x2b, 0x23, 0x0d, 0x78, 0xc6, 0x7b, 0xe6, 0x5e, 0x71, 0xb7, + 0x4a, 0x3e, 0x08, 0xfb, 0x81, 0xb7, 0x16, 0x16, 0xa1, 0x9d, 0x23, 0x12, + 0x4d, 0xe5, 0xd7, 0x92, 0x08, 0xac, 0x75, 0xa4, 0x9c, 0xba, 0xcd, 0x17, + 0xb2, 0x1e, 0x44, 0x35, 0x65, 0x7f, 0x53, 0x25, 0x39, 0xd1, 0x1c, 0x0a, + 0x9a, 0x63, 0x1b, 0x19, 0x92, 0x74, 0x68, 0x0a, 0x37, 0xc2, 0xc2, 0x52, + 0x48, 0xcb, 0x39, 0x5a, 0xa2, 0xb6, 0xe1, 0x5d, 0xc1, 0xdd, 0xa0, 0x20, + 0xb8, 0x21, 0xa2, 0x93, 0x26, 0x6f, 0x14, 0x4a, 0x21, 0x41, 0xc7, 0xed, + 0x6d, 0x9b, 0xf2, 0x48, 0x2f, 0xf3, 0x03, 0xf5, 0xa2, 0x68, 0x92, 0x53, + 0x2f, 0x5e, 0xe3, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x49, + 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, + 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, + 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, + 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, + 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, + 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x51, 0x68, 0xff, 0x90, 0xaf, 0x02, 0x07, 0x75, 0x3c, 0xcc, 0xd9, 0x65, + 0x64, 0x62, 0xa2, 0x12, 0xb8, 0x59, 0x72, 0x3b, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3, + 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02, + 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0x18, 0x8a, 0x95, 0x89, 0x03, 0xe6, 0x6d, 0xdf, 0x5c, 0xfc, 0x1d, + 0x68, 0xea, 0x4a, 0x8f, 0x83, 0xd6, 0x51, 0x2f, 0x8d, 0x6b, 0x44, 0x16, + 0x9e, 0xac, 0x63, 0xf5, 0xd2, 0x6e, 0x6c, 0x84, 0x99, 0x8b, 0xaa, 0x81, + 0x71, 0x84, 0x5b, 0xed, 0x34, 0x4e, 0xb0, 0xb7, 0x79, 0x92, 0x29, 0xcc, + 0x2d, 0x80, 0x6a, 0xf0, 0x8e, 0x20, 0xe1, 0x79, 0xa4, 0xfe, 0x03, 0x47, + 0x13, 0xea, 0xf5, 0x86, 0xca, 0x59, 0x71, 0x7d, 0xf4, 0x04, 0x96, 0x6b, + 0xd3, 0x59, 0x58, 0x3d, 0xfe, 0xd3, 0x31, 0x25, 0x5c, 0x18, 0x38, 0x84, + 0xa3, 0xe6, 0x9f, 0x82, 0xfd, 0x8c, 0x5b, 0x98, 0x31, 0x4e, 0xcd, 0x78, + 0x9e, 0x1a, 0xfd, 0x85, 0xcb, 0x49, 0xaa, 0xf2, 0x27, 0x8b, 0x99, 0x72, + 0xfc, 0x3e, 0xaa, 0xd5, 0x41, 0x0b, 0xda, 0xd5, 0x36, 0xa1, 0xbf, 0x1c, + 0x6e, 0x47, 0x49, 0x7f, 0x5e, 0xd9, 0x48, 0x7c, 0x03, 0xd9, 0xfd, 0x8b, + 0x49, 0xa0, 0x98, 0x26, 0x42, 0x40, 0xeb, 0xd6, 0x92, 0x11, 0xa4, 0x64, + 0x0a, 0x57, 0x54, 0xc4, 0xf5, 0x1d, 0xd6, 0x02, 0x5e, 0x6b, 0xac, 0xee, + 0xc4, 0x80, 0x9a, 0x12, 0x72, 0xfa, 0x56, 0x93, 0xd7, 0xff, 0xbf, 0x30, + 0x85, 0x06, 0x30, 0xbf, 0x0b, 0x7f, 0x4e, 0xff, 0x57, 0x05, 0x9d, 0x24, + 0xed, 0x85, 0xc3, 0x2b, 0xfb, 0xa6, 0x75, 0xa8, 0xac, 0x2d, 0x16, 0xef, + 0x7d, 0x79, 0x27, 0xb2, 0xeb, 0xc2, 0x9d, 0x0b, 0x07, 0xea, 0xaa, 0x85, + 0xd3, 0x01, 0xa3, 0x20, 0x28, 0x41, 0x59, 0x43, 0x28, 0xd2, 0x81, 0xe3, + 0xaa, 0xf6, 0xec, 0x7b, 0x3b, 0x77, 0xb6, 0x40, 0x62, 0x80, 0x05, 0x41, + 0x45, 0x01, 0xef, 0x17, 0x06, 0x3e, 0xde, 0xc0, 0x33, 0x9b, 0x67, 0xd3, + 0x61, 0x2e, 0x72, 0x87, 0xe4, 0x69, 0xfc, 0x12, 0x00, 0x57, 0x40, 0x1e, + 0x70, 0xf5, 0x1e, 0xc9, 0xb4, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 16:87:d6:88:6d:e2:30:06:85:23:3d:bf:11:bf:65:97 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=thawte, Inc., CN=thawte SSL CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b2:fc:06:fb:04:93:d2:ea:59:20:3b:44:85:97: + 52:39:e7:10:f0:7a:e0:b0:94:40:da:46:f8:0c:28: + bb:b9:ce:60:38:3f:d2:d8:11:42:1b:91:ad:49:ee: + 8f:c7:de:6c:de:37:6f:fd:8b:20:3c:6d:e7:74:d3: + dc:d5:24:88:41:80:89:ee:36:be:c4:d5:be:8d:53: + 13:aa:e4:a5:b8:93:0a:be:ec:da:cd:3c:d4:32:56: + ef:d0:4e:a0:b8:97:bb:39:50:1e:6e:65:c3:fd:b2: + ce:e0:59:a9:48:09:c6:fe:be:ae:fc:3e:3b:81:20: + 97:8b:8f:46:df:60:64:07:75:bb:1b:86:38:9f:47: + 7b:34:ce:a1:d1:97:ad:76:d8:9f:b7:26:db:79:80: + 36:48:f2:c5:37:f8:d9:32:ae:7c:a4:53:81:c7:99: + a1:54:38:2f:4f:75:a0:bb:5a:a5:bb:cd:ac:02:5b: + 19:02:d5:13:18:a7:ce:ac:74:55:12:05:8b:9b:a2: + 95:46:64:72:38:cd:5a:1b:3a:16:a7:be:71:99:8c: + 54:03:b8:96:6c:01:d3:3e:06:98:3f:21:81:3b:02: + 7e:00:47:53:01:1e:0e:46:43:fb:4b:2d:dc:0b:1a: + e8:2f:98:f8:7e:d1:99:ab:13:6c:a4:17:de:6f:f6: + 15:f5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://t1.symcb.com/ThawtePCA.crl + + Authority Information Access: + OCSP - URI:http://t2.symcb.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: https://www.thawte.com/cps + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-537 + X509v3 Subject Key Identifier: + C2:4F:48:57:FC:D1:4F:9A:C0:5D:38:7D:0E:05:DB:D9:2E:B5:52:60 + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha256WithRSAEncryption + 8d:06:de:43:c9:76:02:ca:d9:23:97:5e:f3:63:d7:7d:44:c2: + 0f:6b:0a:f5:07:e5:8b:b8:fa:e0:a3:fa:6b:80:92:b5:03:2c: + c5:37:e0:c2:e5:95:b5:92:70:18:28:42:94:ee:4b:77:6a:01: + 0f:8b:23:ec:56:4d:f4:00:69:e5:84:c8:e2:ea:de:5b:3e:f6: + 3c:07:3a:94:ca:6c:27:b1:cc:83:1a:60:71:27:d2:bf:02:f5: + 1e:44:d3:48:d5:a6:d3:76:21:00:9c:fa:98:64:eb:17:36:3f: + eb:1b:3c:3e:a6:b1:d9:58:06:0e:72:d9:68:be:f1:a7:20:d7: + 52:e4:a4:77:1f:71:70:9d:55:35:85:37:e1:1d:4d:94:c2:70: + 7f:95:40:6e:4b:7d:b2:b4:29:2a:03:79:c8:b9:4c:67:61:04: + a0:8b:27:ff:59:00:eb:55:7f:c6:b7:33:35:2d:5e:4e:ac:b8: + ea:12:c5:e8:f7:b9:ab:be:74:92:2c:b7:d9:4d:ca:84:2f:1c: + c2:f0:72:7c:b2:31:6e:cf:80:e5:88:07:36:51:7b:ba:61:af: + 6d:8d:23:5b:34:a3:95:bc:a2:31:7f:f2:f5:e7:b7:e8:ef:c4: + b5:27:32:e9:f7:9e:69:c7:2b:e8:be:bb:0c:aa:e7:ea:60:12: + ea:26:8a:78 +-----BEGIN CERTIFICATE----- +MIIEsjCCA5qgAwIBAgIQFofWiG3iMAaFIz2/Eb9llzANBgkqhkiG9w0BAQsFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx +MDMwMjM1OTU5WjBBMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu +MRswGQYDVQQDExJ0aGF3dGUgU1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCy/Ab7BJPS6lkgO0SFl1I55xDweuCwlEDaRvgMKLu5zmA4 +P9LYEUIbka1J7o/H3mzeN2/9iyA8bed009zVJIhBgInuNr7E1b6NUxOq5KW4kwq+ +7NrNPNQyVu/QTqC4l7s5UB5uZcP9ss7gWalICcb+vq78PjuBIJeLj0bfYGQHdbsb +hjifR3s0zqHRl6122J+3Jtt5gDZI8sU3+NkyrnykU4HHmaFUOC9PdaC7WqW7zawC +WxkC1RMYp86sdFUSBYubopVGZHI4zVobOhanvnGZjFQDuJZsAdM+Bpg/IYE7An4A +R1MBHg5GQ/tLLdwLGugvmPh+0ZmrE2ykF95v9hX1AgMBAAGjggE7MIIBNzASBgNV +HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAyBgNVHR8EKzApMCegJaAj +hiFodHRwOi8vdDEuc3ltY2IuY29tL1RoYXd0ZVBDQS5jcmwwLwYIKwYBBQUHAQEE +IzAhMB8GCCsGAQUFBzABhhNodHRwOi8vdDIuc3ltY2IuY29tMEEGA1UdIAQ6MDgw +NgYKYIZIAYb4RQEHNjAoMCYGCCsGAQUFBwIBFhpodHRwczovL3d3dy50aGF3dGUu +Y29tL2NwczApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01 +MzcwHQYDVR0OBBYEFMJPSFf80U+awF04fQ4F29kutVJgMB8GA1UdIwQYMBaAFHtb +Rc+vzst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQCNBt5DyXYCytkj +l17zY9d9RMIPawr1B+WLuPrgo/prgJK1AyzFN+DC5ZW1knAYKEKU7kt3agEPiyPs +Vk30AGnlhMji6t5bPvY8BzqUymwnscyDGmBxJ9K/AvUeRNNI1abTdiEAnPqYZOsX +Nj/rGzw+prHZWAYOctlovvGnINdS5KR3H3FwnVU1hTfhHU2UwnB/lUBuS32ytCkq +A3nIuUxnYQSgiyf/WQDrVX/GtzM1LV5OrLjqEsXo97mrvnSSLLfZTcqELxzC8HJ8 +sjFuz4DliAc2UXu6Ya9tjSNbNKOVvKIxf/L157fo78S1JzLp955pxyvovrsMqufq +YBLqJop4 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert30[] = { + 0x30, 0x82, 0x04, 0xb2, 0x30, 0x82, 0x03, 0x9a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x16, 0x87, 0xd6, 0x88, 0x6d, 0xe2, 0x30, 0x06, 0x85, + 0x23, 0x3d, 0xbf, 0x11, 0xbf, 0x65, 0x97, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, + 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x41, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x74, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xb2, 0xfc, 0x06, 0xfb, 0x04, 0x93, 0xd2, 0xea, 0x59, 0x20, + 0x3b, 0x44, 0x85, 0x97, 0x52, 0x39, 0xe7, 0x10, 0xf0, 0x7a, 0xe0, 0xb0, + 0x94, 0x40, 0xda, 0x46, 0xf8, 0x0c, 0x28, 0xbb, 0xb9, 0xce, 0x60, 0x38, + 0x3f, 0xd2, 0xd8, 0x11, 0x42, 0x1b, 0x91, 0xad, 0x49, 0xee, 0x8f, 0xc7, + 0xde, 0x6c, 0xde, 0x37, 0x6f, 0xfd, 0x8b, 0x20, 0x3c, 0x6d, 0xe7, 0x74, + 0xd3, 0xdc, 0xd5, 0x24, 0x88, 0x41, 0x80, 0x89, 0xee, 0x36, 0xbe, 0xc4, + 0xd5, 0xbe, 0x8d, 0x53, 0x13, 0xaa, 0xe4, 0xa5, 0xb8, 0x93, 0x0a, 0xbe, + 0xec, 0xda, 0xcd, 0x3c, 0xd4, 0x32, 0x56, 0xef, 0xd0, 0x4e, 0xa0, 0xb8, + 0x97, 0xbb, 0x39, 0x50, 0x1e, 0x6e, 0x65, 0xc3, 0xfd, 0xb2, 0xce, 0xe0, + 0x59, 0xa9, 0x48, 0x09, 0xc6, 0xfe, 0xbe, 0xae, 0xfc, 0x3e, 0x3b, 0x81, + 0x20, 0x97, 0x8b, 0x8f, 0x46, 0xdf, 0x60, 0x64, 0x07, 0x75, 0xbb, 0x1b, + 0x86, 0x38, 0x9f, 0x47, 0x7b, 0x34, 0xce, 0xa1, 0xd1, 0x97, 0xad, 0x76, + 0xd8, 0x9f, 0xb7, 0x26, 0xdb, 0x79, 0x80, 0x36, 0x48, 0xf2, 0xc5, 0x37, + 0xf8, 0xd9, 0x32, 0xae, 0x7c, 0xa4, 0x53, 0x81, 0xc7, 0x99, 0xa1, 0x54, + 0x38, 0x2f, 0x4f, 0x75, 0xa0, 0xbb, 0x5a, 0xa5, 0xbb, 0xcd, 0xac, 0x02, + 0x5b, 0x19, 0x02, 0xd5, 0x13, 0x18, 0xa7, 0xce, 0xac, 0x74, 0x55, 0x12, + 0x05, 0x8b, 0x9b, 0xa2, 0x95, 0x46, 0x64, 0x72, 0x38, 0xcd, 0x5a, 0x1b, + 0x3a, 0x16, 0xa7, 0xbe, 0x71, 0x99, 0x8c, 0x54, 0x03, 0xb8, 0x96, 0x6c, + 0x01, 0xd3, 0x3e, 0x06, 0x98, 0x3f, 0x21, 0x81, 0x3b, 0x02, 0x7e, 0x00, + 0x47, 0x53, 0x01, 0x1e, 0x0e, 0x46, 0x43, 0xfb, 0x4b, 0x2d, 0xdc, 0x0b, + 0x1a, 0xe8, 0x2f, 0x98, 0xf8, 0x7e, 0xd1, 0x99, 0xab, 0x13, 0x6c, 0xa4, + 0x17, 0xde, 0x6f, 0xf6, 0x15, 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x3b, 0x30, 0x82, 0x01, 0x37, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, + 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x32, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, + 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, 0x31, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, + 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x74, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38, 0x30, + 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, + 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55, + 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, + 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, + 0x33, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xc2, 0x4f, 0x48, 0x57, 0xfc, 0xd1, 0x4f, 0x9a, 0xc0, 0x5d, 0x38, + 0x7d, 0x0e, 0x05, 0xdb, 0xd9, 0x2e, 0xb5, 0x52, 0x60, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, + 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, + 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x8d, 0x06, 0xde, 0x43, 0xc9, 0x76, 0x02, 0xca, 0xd9, 0x23, + 0x97, 0x5e, 0xf3, 0x63, 0xd7, 0x7d, 0x44, 0xc2, 0x0f, 0x6b, 0x0a, 0xf5, + 0x07, 0xe5, 0x8b, 0xb8, 0xfa, 0xe0, 0xa3, 0xfa, 0x6b, 0x80, 0x92, 0xb5, + 0x03, 0x2c, 0xc5, 0x37, 0xe0, 0xc2, 0xe5, 0x95, 0xb5, 0x92, 0x70, 0x18, + 0x28, 0x42, 0x94, 0xee, 0x4b, 0x77, 0x6a, 0x01, 0x0f, 0x8b, 0x23, 0xec, + 0x56, 0x4d, 0xf4, 0x00, 0x69, 0xe5, 0x84, 0xc8, 0xe2, 0xea, 0xde, 0x5b, + 0x3e, 0xf6, 0x3c, 0x07, 0x3a, 0x94, 0xca, 0x6c, 0x27, 0xb1, 0xcc, 0x83, + 0x1a, 0x60, 0x71, 0x27, 0xd2, 0xbf, 0x02, 0xf5, 0x1e, 0x44, 0xd3, 0x48, + 0xd5, 0xa6, 0xd3, 0x76, 0x21, 0x00, 0x9c, 0xfa, 0x98, 0x64, 0xeb, 0x17, + 0x36, 0x3f, 0xeb, 0x1b, 0x3c, 0x3e, 0xa6, 0xb1, 0xd9, 0x58, 0x06, 0x0e, + 0x72, 0xd9, 0x68, 0xbe, 0xf1, 0xa7, 0x20, 0xd7, 0x52, 0xe4, 0xa4, 0x77, + 0x1f, 0x71, 0x70, 0x9d, 0x55, 0x35, 0x85, 0x37, 0xe1, 0x1d, 0x4d, 0x94, + 0xc2, 0x70, 0x7f, 0x95, 0x40, 0x6e, 0x4b, 0x7d, 0xb2, 0xb4, 0x29, 0x2a, + 0x03, 0x79, 0xc8, 0xb9, 0x4c, 0x67, 0x61, 0x04, 0xa0, 0x8b, 0x27, 0xff, + 0x59, 0x00, 0xeb, 0x55, 0x7f, 0xc6, 0xb7, 0x33, 0x35, 0x2d, 0x5e, 0x4e, + 0xac, 0xb8, 0xea, 0x12, 0xc5, 0xe8, 0xf7, 0xb9, 0xab, 0xbe, 0x74, 0x92, + 0x2c, 0xb7, 0xd9, 0x4d, 0xca, 0x84, 0x2f, 0x1c, 0xc2, 0xf0, 0x72, 0x7c, + 0xb2, 0x31, 0x6e, 0xcf, 0x80, 0xe5, 0x88, 0x07, 0x36, 0x51, 0x7b, 0xba, + 0x61, 0xaf, 0x6d, 0x8d, 0x23, 0x5b, 0x34, 0xa3, 0x95, 0xbc, 0xa2, 0x31, + 0x7f, 0xf2, 0xf5, 0xe7, 0xb7, 0xe8, 0xef, 0xc4, 0xb5, 0x27, 0x32, 0xe9, + 0xf7, 0x9e, 0x69, 0xc7, 0x2b, 0xe8, 0xbe, 0xbb, 0x0c, 0xaa, 0xe7, 0xea, + 0x60, 0x12, 0xea, 0x26, 0x8a, 0x78, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 93:92:85:40:01:65:71:5f:94:7f:28:8f:ef:c9:9b:28 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=PL, O=Unizeto Sp. z o.o., CN=Certum CA + Validity + Not Before: Oct 22 12:07:37 2008 GMT + Not After : Jun 10 10:46:39 2027 GMT + Subject: C=PL, O=Unizeto Technologies S.A., OU=Certum Certification Authority, CN=Certum Trusted Network CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e3:fb:7d:a3:72:ba:c2:f0:c9:14:87:f5:6b:01: + 4e:e1:6e:40:07:ba:6d:27:5d:7f:f7:5b:2d:b3:5a: + c7:51:5f:ab:a4:32:a6:61:87:b6:6e:0f:86:d2:30: + 02:97:f8:d7:69:57:a1:18:39:5d:6a:64:79:c6:01: + 59:ac:3c:31:4a:38:7c:d2:04:d2:4b:28:e8:20:5f: + 3b:07:a2:cc:4d:73:db:f3:ae:4f:c7:56:d5:5a:a7: + 96:89:fa:f3:ab:68:d4:23:86:59:27:cf:09:27:bc: + ac:6e:72:83:1c:30:72:df:e0:a2:e9:d2:e1:74:75: + 19:bd:2a:9e:7b:15:54:04:1b:d7:43:39:ad:55:28: + c5:e2:1a:bb:f4:c0:e4:ae:38:49:33:cc:76:85:9f: + 39:45:d2:a4:9e:f2:12:8c:51:f8:7c:e4:2d:7f:f5: + ac:5f:eb:16:9f:b1:2d:d1:ba:cc:91:42:77:4c:25: + c9:90:38:6f:db:f0:cc:fb:8e:1e:97:59:3e:d5:60: + 4e:e6:05:28:ed:49:79:13:4b:ba:48:db:2f:f9:72: + d3:39:ca:fe:1f:d8:34:72:f5:b4:40:cf:31:01:c3: + ec:de:11:2d:17:5d:1f:b8:50:d1:5e:19:a7:69:de: + 07:33:28:ca:50:95:f9:a7:54:cb:54:86:50:45:a9: + f9:49 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Subject Key Identifier: + 08:76:CD:CB:07:FF:24:F6:C5:CD:ED:BB:90:BC:E2:84:37:46:75:F7 + X509v3 Authority Key Identifier: + DirName:/C=PL/O=Unizeto Sp. z o.o./CN=Certum CA + serial:01:00:20 + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.certum.pl/ca.crl + + Authority Information Access: + OCSP - URI:http://subca.ocsp-certum.com + CA Issuers - URI:http://repository.certum.pl/ca.cer + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.certum.pl/CPS + + Signature Algorithm: sha256WithRSAEncryption + 8d:e6:fd:40:66:a3:4c:9c:a7:ab:a1:da:84:dd:1c:30:07:e6: + db:c7:2d:ec:83:a1:56:e4:1d:3c:26:a1:a5:09:2b:e8:7d:62: + be:b2:75:94:dd:08:f2:7f:28:41:e4:80:67:02:4e:8a:8f:c3: + 35:d0:d5:a9:27:28:ea:d2:f4:ab:06:86:43:ae:8c:e3:f9:88: + 7d:e0:db:bd:42:81:80:02:12:75:b2:e8:17:71:ab:21:95:31: + 46:42:0d:88:10:39:d3:6f:ec:2f:42:ea:40:53:62:bf:eb:ca: + 78:9e:ab:a2:d5:2e:05:ea:33:ab:e9:d6:97:94:42:5e:04:ed: + 2c:ed:6a:9c:7a:95:7d:05:2a:05:7f:08:5d:66:ad:61:d4:76: + ac:75:96:97:73:63:bd:1a:41:59:29:a5:5e:22:83:c3:8b:59: + fa:9a:a2:f6:bd:30:bf:72:1d:1c:99:86:9c:f2:85:3c:1d:f7: + 26:96:2f:2e:f9:02:b1:b5:a9:50:e8:38:fa:9b:0a:5e:b4:04: + c0:ce:4e:39:2c:ca:0b:5b:62:f0:4d:58:50:34:99:e6:9a:2c: + d2:90:d7:09:81:d6:c0:aa:5e:ce:fe:d2:f7:a1:ba:4b:d9:d6: + 86:8e:19:1f:a6:06:47:42:72:e0:56:0a:00:1c:78:b9:8d:cc: + 99:04:37:49 +-----BEGIN CERTIFICATE----- +MIIEtDCCA5ygAwIBAgIRAJOShUABZXFflH8oj+/JmygwDQYJKoZIhvcNAQELBQAw +PjELMAkGA1UEBhMCUEwxGzAZBgNVBAoTElVuaXpldG8gU3AuIHogby5vLjESMBAG +A1UEAxMJQ2VydHVtIENBMB4XDTA4MTAyMjEyMDczN1oXDTI3MDYxMDEwNDYzOVow +fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG +A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAOP7faNyusLwyRSH9WsBTuFuQAe6bSddf/dbLbNax1Ff +q6QypmGHtm4PhtIwApf412lXoRg5XWpkecYBWaw8MUo4fNIE0kso6CBfOweizE1z +2/OuT8dW1Vqnlon686to1COGWSfPCSe8rG5ygxwwct/gounS4XR1Gb0qnnsVVAQb +10M5rVUoxeIau/TA5K44STPMdoWfOUXSpJ7yEoxR+HzkLX/1rF/rFp+xLdG6zJFC +d0wlyZA4b9vwzPuOHpdZPtVgTuYFKO1JeRNLukjbL/ly0znK/h/YNHL1tEDPMQHD +7N4RLRddH7hQ0V4Zp2neBzMoylCV+adUy1SGUEWp+UkCAwEAAaOCAWswggFnMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAh2zcsH/yT2xc3tu5C84oQ3RnX3MFIG +A1UdIwRLMEmhQqRAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNw +LiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQYIDAQAgMA4GA1UdDwEB/wQEAwIB +BjAsBgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vY3JsLmNlcnR1bS5wbC9jYS5jcmww +aAYIKwYBBQUHAQEEXDBaMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1j +ZXJ0dW0uY29tMC4GCCsGAQUFBzAChiJodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0u +cGwvY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQGCCsGAQUFBwIBFhhodHRw +Oi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQELBQADggEBAI3m/UBmo0yc +p6uh2oTdHDAH5tvHLeyDoVbkHTwmoaUJK+h9Yr6ydZTdCPJ/KEHkgGcCToqPwzXQ +1aknKOrS9KsGhkOujOP5iH3g271CgYACEnWy6BdxqyGVMUZCDYgQOdNv7C9C6kBT +Yr/rynieq6LVLgXqM6vp1peUQl4E7Sztapx6lX0FKgV/CF1mrWHUdqx1lpdzY70a +QVkppV4ig8OLWfqaova9ML9yHRyZhpzyhTwd9yaWLy75ArG1qVDoOPqbCl60BMDO +TjksygtbYvBNWFA0meaaLNKQ1wmB1sCqXs7+0vehukvZ1oaOGR+mBkdCcuBWCgAc +eLmNzJkEN0k= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert31[] = { + 0x30, 0x82, 0x04, 0xb4, 0x30, 0x82, 0x03, 0x9c, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x11, 0x00, 0x93, 0x92, 0x85, 0x40, 0x01, 0x65, 0x71, 0x5f, + 0x94, 0x7f, 0x28, 0x8f, 0xef, 0xc9, 0x9b, 0x28, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, + 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x50, 0x4c, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x12, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x53, 0x70, 0x2e, + 0x20, 0x7a, 0x20, 0x6f, 0x2e, 0x6f, 0x2e, 0x31, 0x12, 0x30, 0x10, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d, + 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x31, 0x30, 0x32, + 0x32, 0x31, 0x32, 0x30, 0x37, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x37, + 0x30, 0x36, 0x31, 0x30, 0x31, 0x30, 0x34, 0x36, 0x33, 0x39, 0x5a, 0x30, + 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x50, 0x4c, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x19, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x54, 0x65, 0x63, + 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x20, 0x53, 0x2e, + 0x41, 0x2e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x1e, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x22, 0x30, 0x20, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xe3, 0xfb, 0x7d, 0xa3, 0x72, 0xba, 0xc2, 0xf0, + 0xc9, 0x14, 0x87, 0xf5, 0x6b, 0x01, 0x4e, 0xe1, 0x6e, 0x40, 0x07, 0xba, + 0x6d, 0x27, 0x5d, 0x7f, 0xf7, 0x5b, 0x2d, 0xb3, 0x5a, 0xc7, 0x51, 0x5f, + 0xab, 0xa4, 0x32, 0xa6, 0x61, 0x87, 0xb6, 0x6e, 0x0f, 0x86, 0xd2, 0x30, + 0x02, 0x97, 0xf8, 0xd7, 0x69, 0x57, 0xa1, 0x18, 0x39, 0x5d, 0x6a, 0x64, + 0x79, 0xc6, 0x01, 0x59, 0xac, 0x3c, 0x31, 0x4a, 0x38, 0x7c, 0xd2, 0x04, + 0xd2, 0x4b, 0x28, 0xe8, 0x20, 0x5f, 0x3b, 0x07, 0xa2, 0xcc, 0x4d, 0x73, + 0xdb, 0xf3, 0xae, 0x4f, 0xc7, 0x56, 0xd5, 0x5a, 0xa7, 0x96, 0x89, 0xfa, + 0xf3, 0xab, 0x68, 0xd4, 0x23, 0x86, 0x59, 0x27, 0xcf, 0x09, 0x27, 0xbc, + 0xac, 0x6e, 0x72, 0x83, 0x1c, 0x30, 0x72, 0xdf, 0xe0, 0xa2, 0xe9, 0xd2, + 0xe1, 0x74, 0x75, 0x19, 0xbd, 0x2a, 0x9e, 0x7b, 0x15, 0x54, 0x04, 0x1b, + 0xd7, 0x43, 0x39, 0xad, 0x55, 0x28, 0xc5, 0xe2, 0x1a, 0xbb, 0xf4, 0xc0, + 0xe4, 0xae, 0x38, 0x49, 0x33, 0xcc, 0x76, 0x85, 0x9f, 0x39, 0x45, 0xd2, + 0xa4, 0x9e, 0xf2, 0x12, 0x8c, 0x51, 0xf8, 0x7c, 0xe4, 0x2d, 0x7f, 0xf5, + 0xac, 0x5f, 0xeb, 0x16, 0x9f, 0xb1, 0x2d, 0xd1, 0xba, 0xcc, 0x91, 0x42, + 0x77, 0x4c, 0x25, 0xc9, 0x90, 0x38, 0x6f, 0xdb, 0xf0, 0xcc, 0xfb, 0x8e, + 0x1e, 0x97, 0x59, 0x3e, 0xd5, 0x60, 0x4e, 0xe6, 0x05, 0x28, 0xed, 0x49, + 0x79, 0x13, 0x4b, 0xba, 0x48, 0xdb, 0x2f, 0xf9, 0x72, 0xd3, 0x39, 0xca, + 0xfe, 0x1f, 0xd8, 0x34, 0x72, 0xf5, 0xb4, 0x40, 0xcf, 0x31, 0x01, 0xc3, + 0xec, 0xde, 0x11, 0x2d, 0x17, 0x5d, 0x1f, 0xb8, 0x50, 0xd1, 0x5e, 0x19, + 0xa7, 0x69, 0xde, 0x07, 0x33, 0x28, 0xca, 0x50, 0x95, 0xf9, 0xa7, 0x54, + 0xcb, 0x54, 0x86, 0x50, 0x45, 0xa9, 0xf9, 0x49, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x01, 0x6b, 0x30, 0x82, 0x01, 0x67, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, + 0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x08, 0x76, 0xcd, 0xcb, 0x07, 0xff, 0x24, 0xf6, 0xc5, 0xcd, 0xed, + 0xbb, 0x90, 0xbc, 0xe2, 0x84, 0x37, 0x46, 0x75, 0xf7, 0x30, 0x52, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x4b, 0x30, 0x49, 0xa1, 0x42, 0xa4, 0x40, + 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x50, 0x4c, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x12, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x53, 0x70, + 0x2e, 0x20, 0x7a, 0x20, 0x6f, 0x2e, 0x6f, 0x2e, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x43, 0x65, 0x72, 0x74, 0x75, + 0x6d, 0x20, 0x43, 0x41, 0x82, 0x03, 0x01, 0x00, 0x20, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x06, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x25, 0x30, 0x23, + 0x30, 0x21, 0xa0, 0x1f, 0xa0, 0x1d, 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, + 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x68, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x5c, 0x30, 0x5a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x73, 0x75, 0x62, 0x63, 0x61, 0x2e, 0x6f, 0x63, 0x73, 0x70, 0x2d, 0x63, + 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x2e, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x22, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e, + 0x70, 0x6c, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x39, 0x06, + 0x03, 0x55, 0x1d, 0x20, 0x04, 0x32, 0x30, 0x30, 0x30, 0x2e, 0x06, 0x04, + 0x55, 0x1d, 0x20, 0x00, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x18, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, + 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x8d, 0xe6, 0xfd, 0x40, 0x66, 0xa3, 0x4c, 0x9c, + 0xa7, 0xab, 0xa1, 0xda, 0x84, 0xdd, 0x1c, 0x30, 0x07, 0xe6, 0xdb, 0xc7, + 0x2d, 0xec, 0x83, 0xa1, 0x56, 0xe4, 0x1d, 0x3c, 0x26, 0xa1, 0xa5, 0x09, + 0x2b, 0xe8, 0x7d, 0x62, 0xbe, 0xb2, 0x75, 0x94, 0xdd, 0x08, 0xf2, 0x7f, + 0x28, 0x41, 0xe4, 0x80, 0x67, 0x02, 0x4e, 0x8a, 0x8f, 0xc3, 0x35, 0xd0, + 0xd5, 0xa9, 0x27, 0x28, 0xea, 0xd2, 0xf4, 0xab, 0x06, 0x86, 0x43, 0xae, + 0x8c, 0xe3, 0xf9, 0x88, 0x7d, 0xe0, 0xdb, 0xbd, 0x42, 0x81, 0x80, 0x02, + 0x12, 0x75, 0xb2, 0xe8, 0x17, 0x71, 0xab, 0x21, 0x95, 0x31, 0x46, 0x42, + 0x0d, 0x88, 0x10, 0x39, 0xd3, 0x6f, 0xec, 0x2f, 0x42, 0xea, 0x40, 0x53, + 0x62, 0xbf, 0xeb, 0xca, 0x78, 0x9e, 0xab, 0xa2, 0xd5, 0x2e, 0x05, 0xea, + 0x33, 0xab, 0xe9, 0xd6, 0x97, 0x94, 0x42, 0x5e, 0x04, 0xed, 0x2c, 0xed, + 0x6a, 0x9c, 0x7a, 0x95, 0x7d, 0x05, 0x2a, 0x05, 0x7f, 0x08, 0x5d, 0x66, + 0xad, 0x61, 0xd4, 0x76, 0xac, 0x75, 0x96, 0x97, 0x73, 0x63, 0xbd, 0x1a, + 0x41, 0x59, 0x29, 0xa5, 0x5e, 0x22, 0x83, 0xc3, 0x8b, 0x59, 0xfa, 0x9a, + 0xa2, 0xf6, 0xbd, 0x30, 0xbf, 0x72, 0x1d, 0x1c, 0x99, 0x86, 0x9c, 0xf2, + 0x85, 0x3c, 0x1d, 0xf7, 0x26, 0x96, 0x2f, 0x2e, 0xf9, 0x02, 0xb1, 0xb5, + 0xa9, 0x50, 0xe8, 0x38, 0xfa, 0x9b, 0x0a, 0x5e, 0xb4, 0x04, 0xc0, 0xce, + 0x4e, 0x39, 0x2c, 0xca, 0x0b, 0x5b, 0x62, 0xf0, 0x4d, 0x58, 0x50, 0x34, + 0x99, 0xe6, 0x9a, 0x2c, 0xd2, 0x90, 0xd7, 0x09, 0x81, 0xd6, 0xc0, 0xaa, + 0x5e, 0xce, 0xfe, 0xd2, 0xf7, 0xa1, 0xba, 0x4b, 0xd9, 0xd6, 0x86, 0x8e, + 0x19, 0x1f, 0xa6, 0x06, 0x47, 0x42, 0x72, 0xe0, 0x56, 0x0a, 0x00, 0x1c, + 0x78, 0xb9, 0x8d, 0xcc, 0x99, 0x04, 0x37, 0x49, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 48:e9:94:40:d4:36:49:1c:b8:b8:82:3d:09:43:94:c7 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3 + Validity + Not Before: Jun 10 00:00:00 2014 GMT + Not After : Jun 9 23:59:59 2024 GMT + Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c4:95:63:28:d0:4e:30:45:af:8b:97:34:14:45: + f8:5c:58:4a:fa:33:8e:6e:9c:60:ab:f3:86:ff:34: + 74:b2:2b:be:a1:8c:d5:a2:a3:60:7a:40:b9:e1:fc: + 22:ca:67:ba:60:aa:c7:9a:f9:06:7f:ee:f7:ba:85: + 05:b0:03:ff:72:ae:15:41:4a:98:64:d7:17:4b:54: + ef:05:c6:98:07:93:27:3e:4f:dc:0f:c6:7b:8b:e7: + f3:06:5e:8d:e8:b4:ae:29:b4:1e:1e:2d:16:90:d3: + ea:aa:e7:8c:3b:6d:af:36:59:ff:c5:0a:fa:c7:4c: + bd:36:8b:64:c4:4a:f5:ce:33:f9:07:be:7f:45:90: + a8:08:14:b0:d0:a5:4f:df:82:80:da:1b:ee:c3:13: + b0:98:f5:0f:f9:7e:76:b5:e6:b9:5d:68:b9:5c:50: + 90:89:a4:36:b1:70:16:ea:b1:10:b5:6a:76:df:e1: + bb:fc:78:f2:72:99:cf:c9:a2:d4:73:54:77:bf:c0: + 39:77:e5:ae:12:c5:78:5a:19:45:d4:41:19:d3:7c: + f5:6f:99:6b:d7:8b:bc:2d:09:9d:4b:10:61:c0:da: + 52:c3:af:22:43:c6:eb:37:7e:63:74:30:0d:6a:71: + 8e:de:5d:5b:8a:c8:c5:d7:9b:29:e8:ae:b6:25:61: + 81:eb + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://g.symcd.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://g.symcb.com/GeoTrustPCA-G3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-697 + X509v3 Subject Key Identifier: + 4C:F4:BF:E8:3B:BE:C2:24:F3:1B:47:3B:B5:6E:48:8E:16:AB:AF:12 + X509v3 Authority Key Identifier: + keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D + + Signature Algorithm: sha256WithRSAEncryption + 7a:53:b5:de:b6:ef:52:a3:5f:8a:f5:89:f1:42:cc:5e:46:88: + ae:a5:08:87:51:de:0f:0f:02:eb:0c:82:78:e3:73:7d:71:bd: + 43:e9:ca:8a:3f:e0:25:92:9b:33:33:74:49:5e:00:d9:73:14: + 1c:0b:46:76:1c:8a:0d:4d:8c:6c:7e:4b:f7:60:d8:81:78:a0: + 78:d0:25:62:ab:10:ca:22:e8:1c:19:dd:52:83:64:05:e5:87: + 66:ae:e7:7a:a4:3b:3e:d8:70:7a:76:a2:67:39:d4:c9:fa:e5: + b7:1e:41:e2:09:39:88:1c:18:55:0a:c4:41:af:b2:f3:f3:0f: + 42:14:61:74:81:e3:da:87:5a:9a:4d:8b:d3:c9:8f:89:66:13: + 29:11:e4:ff:e2:df:8e:96:0c:5a:a1:aa:6b:9b:fd:fc:03:3b: + 55:0d:a6:a2:25:48:17:1f:42:a8:da:6c:7e:69:6e:a0:df:67: + d2:6d:f4:0e:6a:12:79:f5:7c:c8:a5:32:1c:c4:31:b2:e6:bb: + a8:6b:6a:a2:8a:60:69:c0:57:7d:b2:f2:31:0c:98:65:32:ec: + 08:5a:ce:c6:98:e9:21:97:3f:2c:79:29:03:f5:f6:94:2b:53: + 31:f3:93:68:57:e1:d7:4f:3a:d1:61:a1:60:ce:b9:ab:98:ae: + 35:54:63:8b +-----BEGIN CERTIFICATE----- +MIIEtTCCA52gAwIBAgIQSOmUQNQ2SRy4uII9CUOUxzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTE0MDYxMDAwMDAwMFoXDTI0MDYwOTIzNTk1OVowRzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlk +U1NMIFNIQTI1NiBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAxJVjKNBOMEWvi5c0FEX4XFhK+jOObpxgq/OG/zR0siu+oYzVoqNgekC54fwi +yme6YKrHmvkGf+73uoUFsAP/cq4VQUqYZNcXS1TvBcaYB5MnPk/cD8Z7i+fzBl6N +6LSuKbQeHi0WkNPqqueMO22vNln/xQr6x0y9NotkxEr1zjP5B75/RZCoCBSw0KVP +34KA2hvuwxOwmPUP+X52tea5XWi5XFCQiaQ2sXAW6rEQtWp23+G7/HjycpnPyaLU +c1R3v8A5d+WuEsV4WhlF1EEZ03z1b5lr14u8LQmdSxBhwNpSw68iQ8brN35jdDAN +anGO3l1bisjF15sp6K62JWGB6wIDAQABo4IBSTCCAUUwLgYIKwYBBQUHAQEEIjAg +MB4GCCsGAQUFBzABhhJodHRwOi8vZy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB +/wIBADBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYwMzAxBggrBgEFBQcCARYlaHR0 +cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vZy5zeW1jYi5jb20vR2VvVHJ1c3RQQ0EtRzMuY3JsMA4GA1Ud +DwEB/wQEAwIBBjApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0kt +MS02OTcwHQYDVR0OBBYEFEz0v+g7vsIk8xtHO7VuSI4Wq68SMB8GA1UdIwQYMBaA +FMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQB6U7Xetu9S +o1+K9YnxQsxeRoiupQiHUd4PDwLrDIJ443N9cb1D6cqKP+AlkpszM3RJXgDZcxQc +C0Z2HIoNTYxsfkv3YNiBeKB40CViqxDKIugcGd1Sg2QF5Ydmrud6pDs+2HB6dqJn +OdTJ+uW3HkHiCTmIHBhVCsRBr7Lz8w9CFGF0gePah1qaTYvTyY+JZhMpEeT/4t+O +lgxaoaprm/38AztVDaaiJUgXH0Ko2mx+aW6g32fSbfQOahJ59XzIpTIcxDGy5ruo +a2qiimBpwFd9svIxDJhlMuwIWs7GmOkhlz8seSkD9faUK1Mx85NoV+HXTzrRYaFg +zrmrmK41VGOL +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert32[] = { + 0x30, 0x82, 0x04, 0xb5, 0x30, 0x82, 0x03, 0x9d, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x48, 0xe9, 0x94, 0x40, 0xd4, 0x36, 0x49, 0x1c, 0xb8, + 0xb8, 0x82, 0x3d, 0x09, 0x43, 0x94, 0xc7, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65, + 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, + 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, + 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x36, 0x30, 0x39, 0x32, 0x33, + 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, + 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, + 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xc4, 0x95, 0x63, 0x28, 0xd0, 0x4e, 0x30, 0x45, 0xaf, + 0x8b, 0x97, 0x34, 0x14, 0x45, 0xf8, 0x5c, 0x58, 0x4a, 0xfa, 0x33, 0x8e, + 0x6e, 0x9c, 0x60, 0xab, 0xf3, 0x86, 0xff, 0x34, 0x74, 0xb2, 0x2b, 0xbe, + 0xa1, 0x8c, 0xd5, 0xa2, 0xa3, 0x60, 0x7a, 0x40, 0xb9, 0xe1, 0xfc, 0x22, + 0xca, 0x67, 0xba, 0x60, 0xaa, 0xc7, 0x9a, 0xf9, 0x06, 0x7f, 0xee, 0xf7, + 0xba, 0x85, 0x05, 0xb0, 0x03, 0xff, 0x72, 0xae, 0x15, 0x41, 0x4a, 0x98, + 0x64, 0xd7, 0x17, 0x4b, 0x54, 0xef, 0x05, 0xc6, 0x98, 0x07, 0x93, 0x27, + 0x3e, 0x4f, 0xdc, 0x0f, 0xc6, 0x7b, 0x8b, 0xe7, 0xf3, 0x06, 0x5e, 0x8d, + 0xe8, 0xb4, 0xae, 0x29, 0xb4, 0x1e, 0x1e, 0x2d, 0x16, 0x90, 0xd3, 0xea, + 0xaa, 0xe7, 0x8c, 0x3b, 0x6d, 0xaf, 0x36, 0x59, 0xff, 0xc5, 0x0a, 0xfa, + 0xc7, 0x4c, 0xbd, 0x36, 0x8b, 0x64, 0xc4, 0x4a, 0xf5, 0xce, 0x33, 0xf9, + 0x07, 0xbe, 0x7f, 0x45, 0x90, 0xa8, 0x08, 0x14, 0xb0, 0xd0, 0xa5, 0x4f, + 0xdf, 0x82, 0x80, 0xda, 0x1b, 0xee, 0xc3, 0x13, 0xb0, 0x98, 0xf5, 0x0f, + 0xf9, 0x7e, 0x76, 0xb5, 0xe6, 0xb9, 0x5d, 0x68, 0xb9, 0x5c, 0x50, 0x90, + 0x89, 0xa4, 0x36, 0xb1, 0x70, 0x16, 0xea, 0xb1, 0x10, 0xb5, 0x6a, 0x76, + 0xdf, 0xe1, 0xbb, 0xfc, 0x78, 0xf2, 0x72, 0x99, 0xcf, 0xc9, 0xa2, 0xd4, + 0x73, 0x54, 0x77, 0xbf, 0xc0, 0x39, 0x77, 0xe5, 0xae, 0x12, 0xc5, 0x78, + 0x5a, 0x19, 0x45, 0xd4, 0x41, 0x19, 0xd3, 0x7c, 0xf5, 0x6f, 0x99, 0x6b, + 0xd7, 0x8b, 0xbc, 0x2d, 0x09, 0x9d, 0x4b, 0x10, 0x61, 0xc0, 0xda, 0x52, + 0xc3, 0xaf, 0x22, 0x43, 0xc6, 0xeb, 0x37, 0x7e, 0x63, 0x74, 0x30, 0x0d, + 0x6a, 0x71, 0x8e, 0xde, 0x5d, 0x5b, 0x8a, 0xc8, 0xc5, 0xd7, 0x9b, 0x29, + 0xe8, 0xae, 0xb6, 0x25, 0x61, 0x81, 0xeb, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x49, 0x30, 0x82, 0x01, 0x45, 0x30, 0x2e, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, + 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, + 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x36, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, + 0x29, 0xa0, 0x27, 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2d, + 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29, + 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, + 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, + 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, + 0x31, 0x2d, 0x36, 0x39, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x4c, 0xf4, 0xbf, 0xe8, 0x3b, 0xbe, 0xc2, 0x24, + 0xf3, 0x1b, 0x47, 0x3b, 0xb5, 0x6e, 0x48, 0x8e, 0x16, 0xab, 0xaf, 0x12, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xc4, 0x79, 0xca, 0x8e, 0xa1, 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b, + 0xdb, 0x31, 0x5b, 0x94, 0x3e, 0x3f, 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x7a, 0x53, 0xb5, 0xde, 0xb6, 0xef, 0x52, + 0xa3, 0x5f, 0x8a, 0xf5, 0x89, 0xf1, 0x42, 0xcc, 0x5e, 0x46, 0x88, 0xae, + 0xa5, 0x08, 0x87, 0x51, 0xde, 0x0f, 0x0f, 0x02, 0xeb, 0x0c, 0x82, 0x78, + 0xe3, 0x73, 0x7d, 0x71, 0xbd, 0x43, 0xe9, 0xca, 0x8a, 0x3f, 0xe0, 0x25, + 0x92, 0x9b, 0x33, 0x33, 0x74, 0x49, 0x5e, 0x00, 0xd9, 0x73, 0x14, 0x1c, + 0x0b, 0x46, 0x76, 0x1c, 0x8a, 0x0d, 0x4d, 0x8c, 0x6c, 0x7e, 0x4b, 0xf7, + 0x60, 0xd8, 0x81, 0x78, 0xa0, 0x78, 0xd0, 0x25, 0x62, 0xab, 0x10, 0xca, + 0x22, 0xe8, 0x1c, 0x19, 0xdd, 0x52, 0x83, 0x64, 0x05, 0xe5, 0x87, 0x66, + 0xae, 0xe7, 0x7a, 0xa4, 0x3b, 0x3e, 0xd8, 0x70, 0x7a, 0x76, 0xa2, 0x67, + 0x39, 0xd4, 0xc9, 0xfa, 0xe5, 0xb7, 0x1e, 0x41, 0xe2, 0x09, 0x39, 0x88, + 0x1c, 0x18, 0x55, 0x0a, 0xc4, 0x41, 0xaf, 0xb2, 0xf3, 0xf3, 0x0f, 0x42, + 0x14, 0x61, 0x74, 0x81, 0xe3, 0xda, 0x87, 0x5a, 0x9a, 0x4d, 0x8b, 0xd3, + 0xc9, 0x8f, 0x89, 0x66, 0x13, 0x29, 0x11, 0xe4, 0xff, 0xe2, 0xdf, 0x8e, + 0x96, 0x0c, 0x5a, 0xa1, 0xaa, 0x6b, 0x9b, 0xfd, 0xfc, 0x03, 0x3b, 0x55, + 0x0d, 0xa6, 0xa2, 0x25, 0x48, 0x17, 0x1f, 0x42, 0xa8, 0xda, 0x6c, 0x7e, + 0x69, 0x6e, 0xa0, 0xdf, 0x67, 0xd2, 0x6d, 0xf4, 0x0e, 0x6a, 0x12, 0x79, + 0xf5, 0x7c, 0xc8, 0xa5, 0x32, 0x1c, 0xc4, 0x31, 0xb2, 0xe6, 0xbb, 0xa8, + 0x6b, 0x6a, 0xa2, 0x8a, 0x60, 0x69, 0xc0, 0x57, 0x7d, 0xb2, 0xf2, 0x31, + 0x0c, 0x98, 0x65, 0x32, 0xec, 0x08, 0x5a, 0xce, 0xc6, 0x98, 0xe9, 0x21, + 0x97, 0x3f, 0x2c, 0x79, 0x29, 0x03, 0xf5, 0xf6, 0x94, 0x2b, 0x53, 0x31, + 0xf3, 0x93, 0x68, 0x57, 0xe1, 0xd7, 0x4f, 0x3a, 0xd1, 0x61, 0xa1, 0x60, + 0xce, 0xb9, 0xab, 0x98, 0xae, 0x35, 0x54, 0x63, 0x8b, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 0c:79:a9:44:b0:8c:11:95:20:92:61:5f:e2:6b:1d:83 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA + Validity + Not Before: Oct 22 12:00:00 2013 GMT + Not After : Oct 22 12:00:00 2028 GMT + Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d7:53:a4:04:51:f8:99:a6:16:48:4b:67:27:aa: + 93:49:d0:39:ed:0c:b0:b0:00:87:f1:67:28:86:85: + 8c:8e:63:da:bc:b1:40:38:e2:d3:f5:ec:a5:05:18: + b8:3d:3e:c5:99:17:32:ec:18:8c:fa:f1:0c:a6:64: + 21:85:cb:07:10:34:b0:52:88:2b:1f:68:9b:d2:b1: + 8f:12:b0:b3:d2:e7:88:1f:1f:ef:38:77:54:53:5f: + 80:79:3f:2e:1a:aa:a8:1e:4b:2b:0d:ab:b7:63:b9: + 35:b7:7d:14:bc:59:4b:df:51:4a:d2:a1:e2:0c:e2: + 90:82:87:6a:ae:ea:d7:64:d6:98:55:e8:fd:af:1a: + 50:6c:54:bc:11:f2:fd:4a:f2:9d:bb:7f:0e:f4:d5: + be:8e:16:89:12:55:d8:c0:71:34:ee:f6:dc:2d:ec: + c4:87:25:86:8d:d8:21:e4:b0:4d:0c:89:dc:39:26: + 17:dd:f6:d7:94:85:d8:04:21:70:9d:6f:6f:ff:5c: + ba:19:e1:45:cb:56:57:28:7e:1c:0d:41:57:aa:b7: + b8:27:bb:b1:e4:fa:2a:ef:21:23:75:1a:ad:2d:9b: + 86:35:8c:9c:77:b5:73:ad:d8:94:2d:e4:f3:0c:9d: + ee:c1:4e:62:7e:17:c0:71:9e:2c:de:f1:f9:10:28: + 19:33 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.digicert.com/CPS + + X509v3 Subject Key Identifier: + 3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F + X509v3 Authority Key Identifier: + keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 + + Signature Algorithm: sha256WithRSAEncryption + 9d:b6:d0:90:86:e1:86:02:ed:c5:a0:f0:34:1c:74:c1:8d:76: + cc:86:0a:a8:f0:4a:8a:42:d6:3f:c8:a9:4d:ad:7c:08:ad:e6: + b6:50:b8:a2:1a:4d:88:07:b1:29:21:dc:e7:da:c6:3c:21:e0: + e3:11:49:70:ac:7a:1d:01:a4:ca:11:3a:57:ab:7d:57:2a:40: + 74:fd:d3:1d:85:18:50:df:57:47:75:a1:7d:55:20:2e:47:37: + 50:72:8c:7f:82:1b:d2:62:8f:2d:03:5a:da:c3:c8:a1:ce:2c: + 52:a2:00:63:eb:73:ba:71:c8:49:27:23:97:64:85:9e:38:0e: + ad:63:68:3c:ba:52:81:58:79:a3:2c:0c:df:de:6d:eb:31:f2: + ba:a0:7c:6c:f1:2c:d4:e1:bd:77:84:37:03:ce:32:b5:c8:9a: + 81:1a:4a:92:4e:3b:46:9a:85:fe:83:a2:f9:9e:8c:a3:cc:0d: + 5e:b3:3d:cf:04:78:8f:14:14:7b:32:9c:c7:00:a6:5c:c4:b5: + a1:55:8d:5a:56:68:a4:22:70:aa:3c:81:71:d9:9d:a8:45:3b: + f4:e5:f6:a2:51:dd:c7:7b:62:e8:6f:0c:74:eb:b8:da:f8:bf: + 87:0d:79:50:91:90:9b:18:3b:91:59:27:f1:35:28:13:ab:26: + 7e:d5:f7:7a +-----BEGIN CERTIFICATE----- +MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW +YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY +uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/ +LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy +/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh +cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k +8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB +Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp +Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy +dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2 +MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j +b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW +gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh +hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg +4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa +2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs +1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1 +oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn +8TUoE6smftX3eg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert33[] = { + 0x30, 0x82, 0x04, 0xb6, 0x30, 0x82, 0x03, 0x9e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x0c, 0x79, 0xa9, 0x44, 0xb0, 0x8c, 0x11, 0x95, 0x20, + 0x92, 0x61, 0x5f, 0xe2, 0x6b, 0x1d, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32, + 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x75, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, + 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, + 0x32, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xd7, 0x53, 0xa4, 0x04, 0x51, 0xf8, 0x99, 0xa6, + 0x16, 0x48, 0x4b, 0x67, 0x27, 0xaa, 0x93, 0x49, 0xd0, 0x39, 0xed, 0x0c, + 0xb0, 0xb0, 0x00, 0x87, 0xf1, 0x67, 0x28, 0x86, 0x85, 0x8c, 0x8e, 0x63, + 0xda, 0xbc, 0xb1, 0x40, 0x38, 0xe2, 0xd3, 0xf5, 0xec, 0xa5, 0x05, 0x18, + 0xb8, 0x3d, 0x3e, 0xc5, 0x99, 0x17, 0x32, 0xec, 0x18, 0x8c, 0xfa, 0xf1, + 0x0c, 0xa6, 0x64, 0x21, 0x85, 0xcb, 0x07, 0x10, 0x34, 0xb0, 0x52, 0x88, + 0x2b, 0x1f, 0x68, 0x9b, 0xd2, 0xb1, 0x8f, 0x12, 0xb0, 0xb3, 0xd2, 0xe7, + 0x88, 0x1f, 0x1f, 0xef, 0x38, 0x77, 0x54, 0x53, 0x5f, 0x80, 0x79, 0x3f, + 0x2e, 0x1a, 0xaa, 0xa8, 0x1e, 0x4b, 0x2b, 0x0d, 0xab, 0xb7, 0x63, 0xb9, + 0x35, 0xb7, 0x7d, 0x14, 0xbc, 0x59, 0x4b, 0xdf, 0x51, 0x4a, 0xd2, 0xa1, + 0xe2, 0x0c, 0xe2, 0x90, 0x82, 0x87, 0x6a, 0xae, 0xea, 0xd7, 0x64, 0xd6, + 0x98, 0x55, 0xe8, 0xfd, 0xaf, 0x1a, 0x50, 0x6c, 0x54, 0xbc, 0x11, 0xf2, + 0xfd, 0x4a, 0xf2, 0x9d, 0xbb, 0x7f, 0x0e, 0xf4, 0xd5, 0xbe, 0x8e, 0x16, + 0x89, 0x12, 0x55, 0xd8, 0xc0, 0x71, 0x34, 0xee, 0xf6, 0xdc, 0x2d, 0xec, + 0xc4, 0x87, 0x25, 0x86, 0x8d, 0xd8, 0x21, 0xe4, 0xb0, 0x4d, 0x0c, 0x89, + 0xdc, 0x39, 0x26, 0x17, 0xdd, 0xf6, 0xd7, 0x94, 0x85, 0xd8, 0x04, 0x21, + 0x70, 0x9d, 0x6f, 0x6f, 0xff, 0x5c, 0xba, 0x19, 0xe1, 0x45, 0xcb, 0x56, + 0x57, 0x28, 0x7e, 0x1c, 0x0d, 0x41, 0x57, 0xaa, 0xb7, 0xb8, 0x27, 0xbb, + 0xb1, 0xe4, 0xfa, 0x2a, 0xef, 0x21, 0x23, 0x75, 0x1a, 0xad, 0x2d, 0x9b, + 0x86, 0x35, 0x8c, 0x9c, 0x77, 0xb5, 0x73, 0xad, 0xd8, 0x94, 0x2d, 0xe4, + 0xf3, 0x0c, 0x9d, 0xee, 0xc1, 0x4e, 0x62, 0x7e, 0x17, 0xc0, 0x71, 0x9e, + 0x2c, 0xde, 0xf1, 0xf9, 0x10, 0x28, 0x19, 0x33, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x01, 0x49, 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, + 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, + 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x02, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, + 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0, + 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, + 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, + 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, + 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, + 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, + 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3d, 0xd3, 0x50, 0xa5, 0xd6, 0xa0, 0xad, + 0xee, 0xf3, 0x4a, 0x60, 0x0a, 0x65, 0xd3, 0x21, 0xd4, 0xf8, 0xf8, 0xd6, + 0x0f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, + 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9d, 0xb6, 0xd0, 0x90, 0x86, 0xe1, + 0x86, 0x02, 0xed, 0xc5, 0xa0, 0xf0, 0x34, 0x1c, 0x74, 0xc1, 0x8d, 0x76, + 0xcc, 0x86, 0x0a, 0xa8, 0xf0, 0x4a, 0x8a, 0x42, 0xd6, 0x3f, 0xc8, 0xa9, + 0x4d, 0xad, 0x7c, 0x08, 0xad, 0xe6, 0xb6, 0x50, 0xb8, 0xa2, 0x1a, 0x4d, + 0x88, 0x07, 0xb1, 0x29, 0x21, 0xdc, 0xe7, 0xda, 0xc6, 0x3c, 0x21, 0xe0, + 0xe3, 0x11, 0x49, 0x70, 0xac, 0x7a, 0x1d, 0x01, 0xa4, 0xca, 0x11, 0x3a, + 0x57, 0xab, 0x7d, 0x57, 0x2a, 0x40, 0x74, 0xfd, 0xd3, 0x1d, 0x85, 0x18, + 0x50, 0xdf, 0x57, 0x47, 0x75, 0xa1, 0x7d, 0x55, 0x20, 0x2e, 0x47, 0x37, + 0x50, 0x72, 0x8c, 0x7f, 0x82, 0x1b, 0xd2, 0x62, 0x8f, 0x2d, 0x03, 0x5a, + 0xda, 0xc3, 0xc8, 0xa1, 0xce, 0x2c, 0x52, 0xa2, 0x00, 0x63, 0xeb, 0x73, + 0xba, 0x71, 0xc8, 0x49, 0x27, 0x23, 0x97, 0x64, 0x85, 0x9e, 0x38, 0x0e, + 0xad, 0x63, 0x68, 0x3c, 0xba, 0x52, 0x81, 0x58, 0x79, 0xa3, 0x2c, 0x0c, + 0xdf, 0xde, 0x6d, 0xeb, 0x31, 0xf2, 0xba, 0xa0, 0x7c, 0x6c, 0xf1, 0x2c, + 0xd4, 0xe1, 0xbd, 0x77, 0x84, 0x37, 0x03, 0xce, 0x32, 0xb5, 0xc8, 0x9a, + 0x81, 0x1a, 0x4a, 0x92, 0x4e, 0x3b, 0x46, 0x9a, 0x85, 0xfe, 0x83, 0xa2, + 0xf9, 0x9e, 0x8c, 0xa3, 0xcc, 0x0d, 0x5e, 0xb3, 0x3d, 0xcf, 0x04, 0x78, + 0x8f, 0x14, 0x14, 0x7b, 0x32, 0x9c, 0xc7, 0x00, 0xa6, 0x5c, 0xc4, 0xb5, + 0xa1, 0x55, 0x8d, 0x5a, 0x56, 0x68, 0xa4, 0x22, 0x70, 0xaa, 0x3c, 0x81, + 0x71, 0xd9, 0x9d, 0xa8, 0x45, 0x3b, 0xf4, 0xe5, 0xf6, 0xa2, 0x51, 0xdd, + 0xc7, 0x7b, 0x62, 0xe8, 0x6f, 0x0c, 0x74, 0xeb, 0xb8, 0xda, 0xf8, 0xbf, + 0x87, 0x0d, 0x79, 0x50, 0x91, 0x90, 0x9b, 0x18, 0x3b, 0x91, 0x59, 0x27, + 0xf1, 0x35, 0x28, 0x13, 0xab, 0x26, 0x7e, 0xd5, 0xf7, 0x7a, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 36:34:9e:18:c9:9c:26:69:b6:56:2e:6c:e5:ad:71:32 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2008 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA - G3 + Validity + Not Before: May 23 00:00:00 2013 GMT + Not After : May 22 23:59:59 2023 GMT + Subject: C=US, O=thawte, Inc., CN=thawte SHA256 SSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a3:63:2b:d4:ba:5d:38:ae:b0:cf:b9:4c:38:df: + 20:7d:f1:2b:47:71:1d:8b:68:f3:56:f9:9c:da:aa: + e5:84:26:de:a5:71:30:bc:f3:31:23:9d:e8:3b:80: + c8:66:57:75:b6:57:0e:db:93:f5:26:8e:70:ba:64: + 52:66:8a:2a:88:5c:44:18:4d:a8:a2:7c:bd:56:61: + 32:90:12:f9:35:87:48:60:b0:6e:90:67:44:01:8d: + e7:c9:0d:63:68:72:72:ab:63:3c:86:b8:1f:7d:ad: + 88:25:a7:6a:88:29:fb:59:c6:78:71:5f:2c:ba:89: + e6:d3:80:fd:57:ec:b9:51:5f:43:33:2e:7e:25:3b: + a4:04:d1:60:8c:b3:44:33:93:0c:ad:2a:b6:44:a2: + 19:3b:af:c4:90:6f:7b:05:87:86:9b:2c:6a:9d:2b: + 6c:77:c9:00:9f:c9:cf:ac:ed:3e:1b:f7:c3:f3:d9: + f8:6c:d4:a0:57:c4:fb:28:32:aa:33:f0:e6:ba:98: + df:e5:c2:4e:9c:74:bf:8a:48:c2:f2:1b:f0:77:40: + 41:07:04:b2:3a:d5:4c:c4:29:a9:11:40:3f:02:46: + f0:91:d5:d2:81:83:86:13:b3:31:ed:46:ab:a8:87: + 76:a9:99:7d:bc:cd:31:50:f4:a5:b5:dc:a5:32:b3: + 8b:8b + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.thawte.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: https://www.thawte.com/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.thawte.com/ThawtePCA-G3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-415 + X509v3 Subject Key Identifier: + 2B:9A:35:AE:01:18:38:30:E1:70:7A:05:E0:11:76:A3:CE:BD:90:14 + X509v3 Authority Key Identifier: + keyid:AD:6C:AA:94:60:9C:ED:E4:FF:FA:3E:0A:74:2B:63:03:F7:B6:59:BF + + Signature Algorithm: sha256WithRSAEncryption + 74:a6:56:e8:af:93:96:19:fb:26:f9:0d:b0:44:a5:cd:e9:7a: + 48:03:74:01:6c:13:71:b7:e0:82:90:99:62:23:e3:d6:99:af: + f0:c7:1e:9e:a8:18:21:db:b4:94:3f:34:56:1b:99:55:2f:8e: + f0:45:33:32:b7:72:c1:13:5b:34:d3:f5:60:e5:2e:18:d1:5c: + c5:6a:c1:aa:87:50:0c:1c:9d:64:2b:ff:1b:dc:d5:2e:61:0b: + e7:b9:b6:91:53:86:d9:03:2a:d1:3d:7b:4a:da:2b:07:be:29: + f2:60:42:a9:91:1a:0e:2e:3c:d1:7d:a5:13:14:02:fa:ee:8b: + 8d:b6:c8:b8:3e:56:81:57:21:24:3f:65:c3:b4:c9:ce:5c:8d: + 46:ac:53:f3:f9:55:74:c8:2b:fd:d2:78:70:f5:f8:11:e5:f4: + a7:ad:20:f5:9d:f1:ec:70:f6:13:ac:e6:8c:8d:db:3f:c6:f2: + 79:0e:ab:52:f2:cc:1b:79:27:cf:16:b3:d6:f3:c6:36:80:43: + ec:c5:94:f0:dd:90:8d:f8:c6:52:46:56:eb:74:47:be:a6:f3: + 19:ae:71:4c:c0:e1:e7:d4:cf:ed:d4:06:28:2a:11:3c:ba:d9: + 41:6e:00:e7:81:37:93:e4:da:62:c6:1d:67:6f:63:b4:14:86: + d9:a6:62:f0 +-----BEGIN CERTIFICATE----- +MIIEwjCCA6qgAwIBAgIQNjSeGMmcJmm2Vi5s5a1xMjANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0xMzA1MjMwMDAwMDBa +Fw0yMzA1MjIyMzU5NTlaMEMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUs +IEluYy4xHTAbBgNVBAMTFHRoYXd0ZSBTSEEyNTYgU1NMIENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2Mr1LpdOK6wz7lMON8gffErR3Edi2jzVvmc +2qrlhCbepXEwvPMxI53oO4DIZld1tlcO25P1Jo5wumRSZooqiFxEGE2oony9VmEy +kBL5NYdIYLBukGdEAY3nyQ1jaHJyq2M8hrgffa2IJadqiCn7WcZ4cV8suonm04D9 +V+y5UV9DMy5+JTukBNFgjLNEM5MMrSq2RKIZO6/EkG97BYeGmyxqnStsd8kAn8nP +rO0+G/fD89n4bNSgV8T7KDKqM/Dmupjf5cJOnHS/ikjC8hvwd0BBBwSyOtVMxCmp +EUA/AkbwkdXSgYOGE7Mx7UarqId2qZl9vM0xUPSltdylMrOLiwIDAQABo4IBRDCC +AUAwMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3 +dGUuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwQQYDVR0gBDowODA2BgpghkgBhvhF +AQc2MCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3BzMDcG +A1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVQQ0Et +RzMuY3JsMA4GA1UdDwEB/wQEAwIBBjAqBgNVHREEIzAhpB8wHTEbMBkGA1UEAxMS +VmVyaVNpZ25NUEtJLTItNDE1MB0GA1UdDgQWBBQrmjWuARg4MOFwegXgEXajzr2Q +FDAfBgNVHSMEGDAWgBStbKqUYJzt5P/6Pgp0K2MD97ZZvzANBgkqhkiG9w0BAQsF +AAOCAQEAdKZW6K+Tlhn7JvkNsESlzel6SAN0AWwTcbfggpCZYiPj1pmv8McenqgY +Idu0lD80VhuZVS+O8EUzMrdywRNbNNP1YOUuGNFcxWrBqodQDBydZCv/G9zVLmEL +57m2kVOG2QMq0T17StorB74p8mBCqZEaDi480X2lExQC+u6LjbbIuD5WgVchJD9l +w7TJzlyNRqxT8/lVdMgr/dJ4cPX4EeX0p60g9Z3x7HD2E6zmjI3bP8byeQ6rUvLM +G3knzxaz1vPGNoBD7MWU8N2QjfjGUkZW63RHvqbzGa5xTMDh59TP7dQGKCoRPLrZ +QW4A54E3k+TaYsYdZ29jtBSG2aZi8A== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert34[] = { + 0x30, 0x82, 0x04, 0xc2, 0x30, 0x82, 0x03, 0xaa, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x36, 0x34, 0x9e, 0x18, 0xc9, 0x9c, 0x26, 0x69, 0xb6, + 0x56, 0x2e, 0x6c, 0xe5, 0xad, 0x71, 0x32, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x38, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x1b, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x35, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, + 0x17, 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x32, 0x32, 0x33, 0x35, 0x39, + 0x35, 0x39, 0x5a, 0x30, 0x43, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x14, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, + 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x63, 0x2b, + 0xd4, 0xba, 0x5d, 0x38, 0xae, 0xb0, 0xcf, 0xb9, 0x4c, 0x38, 0xdf, 0x20, + 0x7d, 0xf1, 0x2b, 0x47, 0x71, 0x1d, 0x8b, 0x68, 0xf3, 0x56, 0xf9, 0x9c, + 0xda, 0xaa, 0xe5, 0x84, 0x26, 0xde, 0xa5, 0x71, 0x30, 0xbc, 0xf3, 0x31, + 0x23, 0x9d, 0xe8, 0x3b, 0x80, 0xc8, 0x66, 0x57, 0x75, 0xb6, 0x57, 0x0e, + 0xdb, 0x93, 0xf5, 0x26, 0x8e, 0x70, 0xba, 0x64, 0x52, 0x66, 0x8a, 0x2a, + 0x88, 0x5c, 0x44, 0x18, 0x4d, 0xa8, 0xa2, 0x7c, 0xbd, 0x56, 0x61, 0x32, + 0x90, 0x12, 0xf9, 0x35, 0x87, 0x48, 0x60, 0xb0, 0x6e, 0x90, 0x67, 0x44, + 0x01, 0x8d, 0xe7, 0xc9, 0x0d, 0x63, 0x68, 0x72, 0x72, 0xab, 0x63, 0x3c, + 0x86, 0xb8, 0x1f, 0x7d, 0xad, 0x88, 0x25, 0xa7, 0x6a, 0x88, 0x29, 0xfb, + 0x59, 0xc6, 0x78, 0x71, 0x5f, 0x2c, 0xba, 0x89, 0xe6, 0xd3, 0x80, 0xfd, + 0x57, 0xec, 0xb9, 0x51, 0x5f, 0x43, 0x33, 0x2e, 0x7e, 0x25, 0x3b, 0xa4, + 0x04, 0xd1, 0x60, 0x8c, 0xb3, 0x44, 0x33, 0x93, 0x0c, 0xad, 0x2a, 0xb6, + 0x44, 0xa2, 0x19, 0x3b, 0xaf, 0xc4, 0x90, 0x6f, 0x7b, 0x05, 0x87, 0x86, + 0x9b, 0x2c, 0x6a, 0x9d, 0x2b, 0x6c, 0x77, 0xc9, 0x00, 0x9f, 0xc9, 0xcf, + 0xac, 0xed, 0x3e, 0x1b, 0xf7, 0xc3, 0xf3, 0xd9, 0xf8, 0x6c, 0xd4, 0xa0, + 0x57, 0xc4, 0xfb, 0x28, 0x32, 0xaa, 0x33, 0xf0, 0xe6, 0xba, 0x98, 0xdf, + 0xe5, 0xc2, 0x4e, 0x9c, 0x74, 0xbf, 0x8a, 0x48, 0xc2, 0xf2, 0x1b, 0xf0, + 0x77, 0x40, 0x41, 0x07, 0x04, 0xb2, 0x3a, 0xd5, 0x4c, 0xc4, 0x29, 0xa9, + 0x11, 0x40, 0x3f, 0x02, 0x46, 0xf0, 0x91, 0xd5, 0xd2, 0x81, 0x83, 0x86, + 0x13, 0xb3, 0x31, 0xed, 0x46, 0xab, 0xa8, 0x87, 0x76, 0xa9, 0x99, 0x7d, + 0xbc, 0xcd, 0x31, 0x50, 0xf4, 0xa5, 0xb5, 0xdc, 0xa5, 0x32, 0xb3, 0x8b, + 0x8b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x44, 0x30, 0x82, + 0x01, 0x40, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, + 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, + 0x38, 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, + 0x01, 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x37, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, 0x30, 0x2e, 0x30, 0x2c, 0xa0, 0x2a, + 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, + 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2d, + 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2a, + 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, + 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, + 0x2d, 0x32, 0x2d, 0x34, 0x31, 0x35, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2b, 0x9a, 0x35, 0xae, 0x01, 0x18, 0x38, + 0x30, 0xe1, 0x70, 0x7a, 0x05, 0xe0, 0x11, 0x76, 0xa3, 0xce, 0xbd, 0x90, + 0x14, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0xad, 0x6c, 0xaa, 0x94, 0x60, 0x9c, 0xed, 0xe4, 0xff, 0xfa, + 0x3e, 0x0a, 0x74, 0x2b, 0x63, 0x03, 0xf7, 0xb6, 0x59, 0xbf, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x74, 0xa6, 0x56, 0xe8, 0xaf, 0x93, + 0x96, 0x19, 0xfb, 0x26, 0xf9, 0x0d, 0xb0, 0x44, 0xa5, 0xcd, 0xe9, 0x7a, + 0x48, 0x03, 0x74, 0x01, 0x6c, 0x13, 0x71, 0xb7, 0xe0, 0x82, 0x90, 0x99, + 0x62, 0x23, 0xe3, 0xd6, 0x99, 0xaf, 0xf0, 0xc7, 0x1e, 0x9e, 0xa8, 0x18, + 0x21, 0xdb, 0xb4, 0x94, 0x3f, 0x34, 0x56, 0x1b, 0x99, 0x55, 0x2f, 0x8e, + 0xf0, 0x45, 0x33, 0x32, 0xb7, 0x72, 0xc1, 0x13, 0x5b, 0x34, 0xd3, 0xf5, + 0x60, 0xe5, 0x2e, 0x18, 0xd1, 0x5c, 0xc5, 0x6a, 0xc1, 0xaa, 0x87, 0x50, + 0x0c, 0x1c, 0x9d, 0x64, 0x2b, 0xff, 0x1b, 0xdc, 0xd5, 0x2e, 0x61, 0x0b, + 0xe7, 0xb9, 0xb6, 0x91, 0x53, 0x86, 0xd9, 0x03, 0x2a, 0xd1, 0x3d, 0x7b, + 0x4a, 0xda, 0x2b, 0x07, 0xbe, 0x29, 0xf2, 0x60, 0x42, 0xa9, 0x91, 0x1a, + 0x0e, 0x2e, 0x3c, 0xd1, 0x7d, 0xa5, 0x13, 0x14, 0x02, 0xfa, 0xee, 0x8b, + 0x8d, 0xb6, 0xc8, 0xb8, 0x3e, 0x56, 0x81, 0x57, 0x21, 0x24, 0x3f, 0x65, + 0xc3, 0xb4, 0xc9, 0xce, 0x5c, 0x8d, 0x46, 0xac, 0x53, 0xf3, 0xf9, 0x55, + 0x74, 0xc8, 0x2b, 0xfd, 0xd2, 0x78, 0x70, 0xf5, 0xf8, 0x11, 0xe5, 0xf4, + 0xa7, 0xad, 0x20, 0xf5, 0x9d, 0xf1, 0xec, 0x70, 0xf6, 0x13, 0xac, 0xe6, + 0x8c, 0x8d, 0xdb, 0x3f, 0xc6, 0xf2, 0x79, 0x0e, 0xab, 0x52, 0xf2, 0xcc, + 0x1b, 0x79, 0x27, 0xcf, 0x16, 0xb3, 0xd6, 0xf3, 0xc6, 0x36, 0x80, 0x43, + 0xec, 0xc5, 0x94, 0xf0, 0xdd, 0x90, 0x8d, 0xf8, 0xc6, 0x52, 0x46, 0x56, + 0xeb, 0x74, 0x47, 0xbe, 0xa6, 0xf3, 0x19, 0xae, 0x71, 0x4c, 0xc0, 0xe1, + 0xe7, 0xd4, 0xcf, 0xed, 0xd4, 0x06, 0x28, 0x2a, 0x11, 0x3c, 0xba, 0xd9, + 0x41, 0x6e, 0x00, 0xe7, 0x81, 0x37, 0x93, 0xe4, 0xda, 0x62, 0xc6, 0x1d, + 0x67, 0x6f, 0x63, 0xb4, 0x14, 0x86, 0xd9, 0xa6, 0x62, 0xf0, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 41:82:12:7d:12:d9:c6:b3:21:39:43:12:56:64:00:b8 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3 + Validity + Not Before: May 23 00:00:00 2013 GMT + Not After : May 22 23:59:59 2023 GMT + Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SHA256 SSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c6:a9:0b:5d:17:a5:7d:c6:cf:2a:ef:c6:66:d1: + 42:1e:5f:83:78:68:91:af:e6:a7:8b:f0:1d:44:01: + 0a:19:ca:9c:d4:8b:1d:e1:a1:90:a3:c1:5b:b4:d7: + 5b:6a:8b:fc:0e:49:1e:c2:62:29:fe:80:15:39:8b: + 81:2a:27:b5:fb:12:a8:05:22:0b:c5:2c:f5:d9:98: + dd:16:2f:3b:66:e7:62:a2:43:32:ac:8f:b5:85:c8: + 52:06:2c:5c:c0:77:fa:67:f7:83:e8:5e:05:8d:c8: + ab:a1:16:32:8a:d2:40:ec:86:3a:1c:23:a9:8d:b5: + 00:de:72:bd:85:55:fe:06:01:60:5d:ad:b3:e0:65: + 73:a5:92:14:9e:94:56:6f:93:ee:af:a9:3a:30:25: + 4a:8e:09:84:ef:b7:d2:d5:d7:9b:49:cd:e9:c0:5e: + 67:71:22:ac:50:90:43:20:5d:a1:a3:15:83:fd:fc: + a7:39:bc:6b:65:48:12:60:ff:dd:23:b3:3a:aa:f4: + 9f:9c:37:53:41:a2:47:93:81:33:09:e5:22:c6:c8: + 1c:49:a1:6e:8d:cc:83:b3:9a:cd:ea:43:f2:19:d3: + 24:cb:a8:29:ae:52:cc:f4:08:27:b0:84:ea:ce:27: + b5:e1:34:13:73:92:5c:87:86:2a:c6:b0:68:36:ad: + cb:09 + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://pca-g3-ocsp.geotrust.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.geotrust.com/resources/cps + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.geotrust.com/GeoTrustPCA-G3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-416 + X509v3 Subject Key Identifier: + 14:67:8E:ED:83:4F:D6:1E:9D:40:04:0C:04:46:A1:70:34:B2:0F:72 + X509v3 Authority Key Identifier: + keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D + + Signature Algorithm: sha256WithRSAEncryption + 10:10:ea:f2:10:d6:08:46:e2:c1:8f:3e:36:59:c8:2b:0f:fe: + 4d:ec:e3:f8:b6:56:31:78:25:d4:76:f2:08:dd:ef:3f:cd:8b: + 1c:7e:aa:7f:fc:0b:a8:23:64:51:b3:87:d6:09:fa:22:fa:c7: + 0a:51:e8:ce:b8:f6:03:70:e0:1b:5a:b9:b1:b2:93:11:10:f9: + 97:05:07:29:6c:6d:57:25:54:e8:f9:66:9b:0e:fb:db:9f:ee: + 96:6f:65:cb:1f:d8:55:ce:31:fa:cf:02:f4:d0:7f:50:66:ff: + 2f:79:9b:a5:c2:df:d6:cf:c8:15:83:96:84:98:b2:46:d4:5f: + 13:a8:3e:a7:34:9c:05:38:da:cf:d6:69:95:a9:26:87:76:01: + d7:b2:51:0f:81:69:46:26:1c:99:b6:83:58:e3:3b:58:8f:dc: + b4:71:c0:b9:bf:42:9c:1c:03:9e:e4:46:a8:ea:b9:c1:cd:f6: + 5b:a9:3c:96:fb:79:a4:33:73:a7:9e:78:b9:70:dc:72:74:c4: + 32:c8:00:1b:c9:ef:48:d3:fb:3a:9b:fa:fe:7a:9a:40:69:1c: + c8:da:28:37:0b:d3:a3:b9:7e:96:cc:2b:28:c3:56:6c:6f:e9: + db:52:b1:fa:9a:fb:e7:af:b5:97:a6:22:c3:c5:a8:93:b1:00: + c9:07:b2:7d +-----BEGIN CERTIFICATE----- +MIIExzCCA6+gAwIBAgIQQYISfRLZxrMhOUMSVmQAuDANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTEzMDUyMzAwMDAwMFoXDTIzMDUyMjIzNTk1OVowRjELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHzAdBgNVBAMTFkdlb1Ry +dXN0IFNIQTI1NiBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDGqQtdF6V9xs8q78Zm0UIeX4N4aJGv5qeL8B1EAQoZypzUix3hoZCjwVu011tq +i/wOSR7CYin+gBU5i4EqJ7X7EqgFIgvFLPXZmN0WLztm52KiQzKsj7WFyFIGLFzA +d/pn94PoXgWNyKuhFjKK0kDshjocI6mNtQDecr2FVf4GAWBdrbPgZXOlkhSelFZv +k+6vqTowJUqOCYTvt9LV15tJzenAXmdxIqxQkEMgXaGjFYP9/Kc5vGtlSBJg/90j +szqq9J+cN1NBokeTgTMJ5SLGyBxJoW6NzIOzms3qQ/IZ0yTLqCmuUsz0CCewhOrO +J7XhNBNzklyHhirGsGg2rcsJAgMBAAGjggFcMIIBWDA7BggrBgEFBQcBAQQvMC0w +KwYIKwYBBQUHMAGGH2h0dHA6Ly9wY2EtZzMtb2NzcC5nZW90cnVzdC5jb20wEgYD +VR0TAQH/BAgwBgEB/wIBADBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYwMzAxBggr +BgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA7 +BgNVHR8ENDAyMDCgLqAshipodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9HZW9UcnVz +dFBDQS1HMy5jcmwwDgYDVR0PAQH/BAQDAgEGMCoGA1UdEQQjMCGkHzAdMRswGQYD +VQQDExJWZXJpU2lnbk1QS0ktMi00MTYwHQYDVR0OBBYEFBRnju2DT9YenUAEDARG +oXA0sg9yMB8GA1UdIwQYMBaAFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3 +DQEBCwUAA4IBAQAQEOryENYIRuLBjz42WcgrD/5N7OP4tlYxeCXUdvII3e8/zYsc +fqp//AuoI2RRs4fWCfoi+scKUejOuPYDcOAbWrmxspMREPmXBQcpbG1XJVTo+Wab +Dvvbn+6Wb2XLH9hVzjH6zwL00H9QZv8veZulwt/Wz8gVg5aEmLJG1F8TqD6nNJwF +ONrP1mmVqSaHdgHXslEPgWlGJhyZtoNY4ztYj9y0ccC5v0KcHAOe5Eao6rnBzfZb +qTyW+3mkM3Onnni5cNxydMQyyAAbye9I0/s6m/r+eppAaRzI2ig3C9OjuX6WzCso +w1Zsb+nbUrH6mvvnr7WXpiLDxaiTsQDJB7J9 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert35[] = { + 0x30, 0x82, 0x04, 0xc7, 0x30, 0x82, 0x03, 0xaf, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x41, 0x82, 0x12, 0x7d, 0x12, 0xd9, 0xc6, 0xb3, 0x21, + 0x39, 0x43, 0x12, 0x56, 0x64, 0x00, 0xb8, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65, + 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, + 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, + 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, + 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x33, 0x30, 0x35, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x32, 0x32, 0x33, + 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x46, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x47, 0x65, 0x6f, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x53, + 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xc6, 0xa9, 0x0b, 0x5d, 0x17, 0xa5, 0x7d, 0xc6, 0xcf, 0x2a, + 0xef, 0xc6, 0x66, 0xd1, 0x42, 0x1e, 0x5f, 0x83, 0x78, 0x68, 0x91, 0xaf, + 0xe6, 0xa7, 0x8b, 0xf0, 0x1d, 0x44, 0x01, 0x0a, 0x19, 0xca, 0x9c, 0xd4, + 0x8b, 0x1d, 0xe1, 0xa1, 0x90, 0xa3, 0xc1, 0x5b, 0xb4, 0xd7, 0x5b, 0x6a, + 0x8b, 0xfc, 0x0e, 0x49, 0x1e, 0xc2, 0x62, 0x29, 0xfe, 0x80, 0x15, 0x39, + 0x8b, 0x81, 0x2a, 0x27, 0xb5, 0xfb, 0x12, 0xa8, 0x05, 0x22, 0x0b, 0xc5, + 0x2c, 0xf5, 0xd9, 0x98, 0xdd, 0x16, 0x2f, 0x3b, 0x66, 0xe7, 0x62, 0xa2, + 0x43, 0x32, 0xac, 0x8f, 0xb5, 0x85, 0xc8, 0x52, 0x06, 0x2c, 0x5c, 0xc0, + 0x77, 0xfa, 0x67, 0xf7, 0x83, 0xe8, 0x5e, 0x05, 0x8d, 0xc8, 0xab, 0xa1, + 0x16, 0x32, 0x8a, 0xd2, 0x40, 0xec, 0x86, 0x3a, 0x1c, 0x23, 0xa9, 0x8d, + 0xb5, 0x00, 0xde, 0x72, 0xbd, 0x85, 0x55, 0xfe, 0x06, 0x01, 0x60, 0x5d, + 0xad, 0xb3, 0xe0, 0x65, 0x73, 0xa5, 0x92, 0x14, 0x9e, 0x94, 0x56, 0x6f, + 0x93, 0xee, 0xaf, 0xa9, 0x3a, 0x30, 0x25, 0x4a, 0x8e, 0x09, 0x84, 0xef, + 0xb7, 0xd2, 0xd5, 0xd7, 0x9b, 0x49, 0xcd, 0xe9, 0xc0, 0x5e, 0x67, 0x71, + 0x22, 0xac, 0x50, 0x90, 0x43, 0x20, 0x5d, 0xa1, 0xa3, 0x15, 0x83, 0xfd, + 0xfc, 0xa7, 0x39, 0xbc, 0x6b, 0x65, 0x48, 0x12, 0x60, 0xff, 0xdd, 0x23, + 0xb3, 0x3a, 0xaa, 0xf4, 0x9f, 0x9c, 0x37, 0x53, 0x41, 0xa2, 0x47, 0x93, + 0x81, 0x33, 0x09, 0xe5, 0x22, 0xc6, 0xc8, 0x1c, 0x49, 0xa1, 0x6e, 0x8d, + 0xcc, 0x83, 0xb3, 0x9a, 0xcd, 0xea, 0x43, 0xf2, 0x19, 0xd3, 0x24, 0xcb, + 0xa8, 0x29, 0xae, 0x52, 0xcc, 0xf4, 0x08, 0x27, 0xb0, 0x84, 0xea, 0xce, + 0x27, 0xb5, 0xe1, 0x34, 0x13, 0x73, 0x92, 0x5c, 0x87, 0x86, 0x2a, 0xc6, + 0xb0, 0x68, 0x36, 0xad, 0xcb, 0x09, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x82, 0x01, 0x5c, 0x30, 0x82, 0x01, 0x58, 0x30, 0x3b, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2f, 0x30, 0x2d, 0x30, + 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, + 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x63, 0x61, 0x2d, + 0x67, 0x33, 0x2d, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x65, 0x6f, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, + 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x3b, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0xa0, + 0x2e, 0xa0, 0x2c, 0x86, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, + 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, + 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x34, 0x31, 0x36, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x14, 0x67, + 0x8e, 0xed, 0x83, 0x4f, 0xd6, 0x1e, 0x9d, 0x40, 0x04, 0x0c, 0x04, 0x46, + 0xa1, 0x70, 0x34, 0xb2, 0x0f, 0x72, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc4, 0x79, 0xca, 0x8e, 0xa1, + 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b, 0xdb, 0x31, 0x5b, 0x94, 0x3e, 0x3f, + 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x10, + 0x10, 0xea, 0xf2, 0x10, 0xd6, 0x08, 0x46, 0xe2, 0xc1, 0x8f, 0x3e, 0x36, + 0x59, 0xc8, 0x2b, 0x0f, 0xfe, 0x4d, 0xec, 0xe3, 0xf8, 0xb6, 0x56, 0x31, + 0x78, 0x25, 0xd4, 0x76, 0xf2, 0x08, 0xdd, 0xef, 0x3f, 0xcd, 0x8b, 0x1c, + 0x7e, 0xaa, 0x7f, 0xfc, 0x0b, 0xa8, 0x23, 0x64, 0x51, 0xb3, 0x87, 0xd6, + 0x09, 0xfa, 0x22, 0xfa, 0xc7, 0x0a, 0x51, 0xe8, 0xce, 0xb8, 0xf6, 0x03, + 0x70, 0xe0, 0x1b, 0x5a, 0xb9, 0xb1, 0xb2, 0x93, 0x11, 0x10, 0xf9, 0x97, + 0x05, 0x07, 0x29, 0x6c, 0x6d, 0x57, 0x25, 0x54, 0xe8, 0xf9, 0x66, 0x9b, + 0x0e, 0xfb, 0xdb, 0x9f, 0xee, 0x96, 0x6f, 0x65, 0xcb, 0x1f, 0xd8, 0x55, + 0xce, 0x31, 0xfa, 0xcf, 0x02, 0xf4, 0xd0, 0x7f, 0x50, 0x66, 0xff, 0x2f, + 0x79, 0x9b, 0xa5, 0xc2, 0xdf, 0xd6, 0xcf, 0xc8, 0x15, 0x83, 0x96, 0x84, + 0x98, 0xb2, 0x46, 0xd4, 0x5f, 0x13, 0xa8, 0x3e, 0xa7, 0x34, 0x9c, 0x05, + 0x38, 0xda, 0xcf, 0xd6, 0x69, 0x95, 0xa9, 0x26, 0x87, 0x76, 0x01, 0xd7, + 0xb2, 0x51, 0x0f, 0x81, 0x69, 0x46, 0x26, 0x1c, 0x99, 0xb6, 0x83, 0x58, + 0xe3, 0x3b, 0x58, 0x8f, 0xdc, 0xb4, 0x71, 0xc0, 0xb9, 0xbf, 0x42, 0x9c, + 0x1c, 0x03, 0x9e, 0xe4, 0x46, 0xa8, 0xea, 0xb9, 0xc1, 0xcd, 0xf6, 0x5b, + 0xa9, 0x3c, 0x96, 0xfb, 0x79, 0xa4, 0x33, 0x73, 0xa7, 0x9e, 0x78, 0xb9, + 0x70, 0xdc, 0x72, 0x74, 0xc4, 0x32, 0xc8, 0x00, 0x1b, 0xc9, 0xef, 0x48, + 0xd3, 0xfb, 0x3a, 0x9b, 0xfa, 0xfe, 0x7a, 0x9a, 0x40, 0x69, 0x1c, 0xc8, + 0xda, 0x28, 0x37, 0x0b, 0xd3, 0xa3, 0xb9, 0x7e, 0x96, 0xcc, 0x2b, 0x28, + 0xc3, 0x56, 0x6c, 0x6f, 0xe9, 0xdb, 0x52, 0xb1, 0xfa, 0x9a, 0xfb, 0xe7, + 0xaf, 0xb5, 0x97, 0xa6, 0x22, 0xc3, 0xc5, 0xa8, 0x93, 0xb1, 0x00, 0xc9, + 0x07, 0xb2, 0x7d, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 7 (0x7) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2 + Validity + Not Before: May 3 07:00:00 2011 GMT + Not After : May 3 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certs.godaddy.com/repository/, CN=Go Daddy Secure Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b9:e0:cb:10:d4:af:76:bd:d4:93:62:eb:30:64: + b8:81:08:6c:c3:04:d9:62:17:8e:2f:ff:3e:65:cf: + 8f:ce:62:e6:3c:52:1c:da:16:45:4b:55:ab:78:6b: + 63:83:62:90:ce:0f:69:6c:99:c8:1a:14:8b:4c:cc: + 45:33:ea:88:dc:9e:a3:af:2b:fe:80:61:9d:79:57: + c4:cf:2e:f4:3f:30:3c:5d:47:fc:9a:16:bc:c3:37: + 96:41:51:8e:11:4b:54:f8:28:be:d0:8c:be:f0:30: + 38:1e:f3:b0:26:f8:66:47:63:6d:de:71:26:47:8f: + 38:47:53:d1:46:1d:b4:e3:dc:00:ea:45:ac:bd:bc: + 71:d9:aa:6f:00:db:db:cd:30:3a:79:4f:5f:4c:47: + f8:1d:ef:5b:c2:c4:9d:60:3b:b1:b2:43:91:d8:a4: + 33:4e:ea:b3:d6:27:4f:ad:25:8a:a5:c6:f4:d5:d0: + a6:ae:74:05:64:57:88:b5:44:55:d4:2d:2a:3a:3e: + f8:b8:bd:e9:32:0a:02:94:64:c4:16:3a:50:f1:4a: + ae:e7:79:33:af:0c:20:07:7f:e8:df:04:39:c2:69: + 02:6c:63:52:fa:77:c1:1b:c8:74:87:c8:b9:93:18: + 50:54:35:4b:69:4e:bc:3b:d3:49:2e:1f:dc:c1:d2: + 52:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 40:C2:BD:27:8E:CC:34:83:30:A2:33:D7:FB:6C:B3:F0:B4:2C:80:CE + X509v3 Authority Key Identifier: + keyid:3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE + + Authority Information Access: + OCSP - URI:http://ocsp.godaddy.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.godaddy.com/gdroot-g2.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.godaddy.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 08:7e:6c:93:10:c8:38:b8:96:a9:90:4b:ff:a1:5f:4f:04:ef: + 6c:3e:9c:88:06:c9:50:8f:a6:73:f7:57:31:1b:be:bc:e4:2f: + db:f8:ba:d3:5b:e0:b4:e7:e6:79:62:0e:0c:a2:d7:6a:63:73: + 31:b5:f5:a8:48:a4:3b:08:2d:a2:5d:90:d7:b4:7c:25:4f:11: + 56:30:c4:b6:44:9d:7b:2c:9d:e5:5e:e6:ef:0c:61:aa:bf:e4: + 2a:1b:ee:84:9e:b8:83:7d:c1:43:ce:44:a7:13:70:0d:91:1f: + f4:c8:13:ad:83:60:d9:d8:72:a8:73:24:1e:b5:ac:22:0e:ca: + 17:89:62:58:44:1b:ab:89:25:01:00:0f:cd:c4:1b:62:db:51: + b4:d3:0f:51:2a:9b:f4:bc:73:fc:76:ce:36:a4:cd:d9:d8:2c: + ea:ae:9b:f5:2a:b2:90:d1:4d:75:18:8a:3f:8a:41:90:23:7d: + 5b:4b:fe:a4:03:58:9b:46:b2:c3:60:60:83:f8:7d:50:41:ce: + c2:a1:90:c3:bb:ef:02:2f:d2:15:54:ee:44:15:d9:0a:ae:a7: + 8a:33:ed:b1:2d:76:36:26:dc:04:eb:9f:f7:61:1f:15:dc:87: + 6f:ee:46:96:28:ad:a1:26:7d:0a:09:a7:2e:04:a3:8d:bc:f8: + bc:04:30:01 +-----BEGIN CERTIFICATE----- +MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3 +MDAwMFowgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UE +CxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQD +EypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzD +BNliF44v/z5lz4/OYuY8UhzaFkVLVat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOv +K/6AYZ15V8TPLvQ/MDxdR/yaFrzDN5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23e +cSZHjzhHU9FGHbTj3ADqRay9vHHZqm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HY +pDNO6rPWJ0+tJYqlxvTV0KaudAVkV4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7n +eTOvDCAHf+jfBDnCaQJsY1L6d8EbyHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMB +AAGjggEaMIIBFjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUQMK9J47MNIMwojPX+2yz8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv +9r0FQW4gwZTaD94wNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC5nb2RhZGR5LmNvbS8wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5n +b2RhZGR5LmNvbS9nZHJvb3QtZzIuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEG +CCsGAQUFBwIBFiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQAIfmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz +91cxG7685C/b+LrTW+C05+Z5Yg4MotdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2 +RJ17LJ3lXubvDGGqv+QqG+6EnriDfcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawi +DsoXiWJYRBuriSUBAA/NxBti21G00w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11 +GIo/ikGQI31bS/6kA1ibRrLDYGCD+H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2x +LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert36[] = { + 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x03, 0xb8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x83, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, + 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, + 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, + 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xb4, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, + 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, + 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, + 0x72, 0x74, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x2f, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x2a, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0xe0, 0xcb, 0x10, 0xd4, 0xaf, 0x76, + 0xbd, 0xd4, 0x93, 0x62, 0xeb, 0x30, 0x64, 0xb8, 0x81, 0x08, 0x6c, 0xc3, + 0x04, 0xd9, 0x62, 0x17, 0x8e, 0x2f, 0xff, 0x3e, 0x65, 0xcf, 0x8f, 0xce, + 0x62, 0xe6, 0x3c, 0x52, 0x1c, 0xda, 0x16, 0x45, 0x4b, 0x55, 0xab, 0x78, + 0x6b, 0x63, 0x83, 0x62, 0x90, 0xce, 0x0f, 0x69, 0x6c, 0x99, 0xc8, 0x1a, + 0x14, 0x8b, 0x4c, 0xcc, 0x45, 0x33, 0xea, 0x88, 0xdc, 0x9e, 0xa3, 0xaf, + 0x2b, 0xfe, 0x80, 0x61, 0x9d, 0x79, 0x57, 0xc4, 0xcf, 0x2e, 0xf4, 0x3f, + 0x30, 0x3c, 0x5d, 0x47, 0xfc, 0x9a, 0x16, 0xbc, 0xc3, 0x37, 0x96, 0x41, + 0x51, 0x8e, 0x11, 0x4b, 0x54, 0xf8, 0x28, 0xbe, 0xd0, 0x8c, 0xbe, 0xf0, + 0x30, 0x38, 0x1e, 0xf3, 0xb0, 0x26, 0xf8, 0x66, 0x47, 0x63, 0x6d, 0xde, + 0x71, 0x26, 0x47, 0x8f, 0x38, 0x47, 0x53, 0xd1, 0x46, 0x1d, 0xb4, 0xe3, + 0xdc, 0x00, 0xea, 0x45, 0xac, 0xbd, 0xbc, 0x71, 0xd9, 0xaa, 0x6f, 0x00, + 0xdb, 0xdb, 0xcd, 0x30, 0x3a, 0x79, 0x4f, 0x5f, 0x4c, 0x47, 0xf8, 0x1d, + 0xef, 0x5b, 0xc2, 0xc4, 0x9d, 0x60, 0x3b, 0xb1, 0xb2, 0x43, 0x91, 0xd8, + 0xa4, 0x33, 0x4e, 0xea, 0xb3, 0xd6, 0x27, 0x4f, 0xad, 0x25, 0x8a, 0xa5, + 0xc6, 0xf4, 0xd5, 0xd0, 0xa6, 0xae, 0x74, 0x05, 0x64, 0x57, 0x88, 0xb5, + 0x44, 0x55, 0xd4, 0x2d, 0x2a, 0x3a, 0x3e, 0xf8, 0xb8, 0xbd, 0xe9, 0x32, + 0x0a, 0x02, 0x94, 0x64, 0xc4, 0x16, 0x3a, 0x50, 0xf1, 0x4a, 0xae, 0xe7, + 0x79, 0x33, 0xaf, 0x0c, 0x20, 0x07, 0x7f, 0xe8, 0xdf, 0x04, 0x39, 0xc2, + 0x69, 0x02, 0x6c, 0x63, 0x52, 0xfa, 0x77, 0xc1, 0x1b, 0xc8, 0x74, 0x87, + 0xc8, 0xb9, 0x93, 0x18, 0x50, 0x54, 0x35, 0x4b, 0x69, 0x4e, 0xbc, 0x3b, + 0xd3, 0x49, 0x2e, 0x1f, 0xdc, 0xc1, 0xd2, 0x52, 0xfb, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1a, 0x30, 0x82, 0x01, 0x16, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x40, 0xc2, 0xbd, 0x27, 0x8e, 0xcc, + 0x34, 0x83, 0x30, 0xa2, 0x33, 0xd7, 0xfb, 0x6c, 0xb3, 0xf0, 0xb4, 0x2c, + 0x80, 0xce, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x3a, 0x9a, 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef, + 0xf6, 0xbd, 0x05, 0x41, 0x6e, 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, + 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, + 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x08, 0x7e, 0x6c, 0x93, + 0x10, 0xc8, 0x38, 0xb8, 0x96, 0xa9, 0x90, 0x4b, 0xff, 0xa1, 0x5f, 0x4f, + 0x04, 0xef, 0x6c, 0x3e, 0x9c, 0x88, 0x06, 0xc9, 0x50, 0x8f, 0xa6, 0x73, + 0xf7, 0x57, 0x31, 0x1b, 0xbe, 0xbc, 0xe4, 0x2f, 0xdb, 0xf8, 0xba, 0xd3, + 0x5b, 0xe0, 0xb4, 0xe7, 0xe6, 0x79, 0x62, 0x0e, 0x0c, 0xa2, 0xd7, 0x6a, + 0x63, 0x73, 0x31, 0xb5, 0xf5, 0xa8, 0x48, 0xa4, 0x3b, 0x08, 0x2d, 0xa2, + 0x5d, 0x90, 0xd7, 0xb4, 0x7c, 0x25, 0x4f, 0x11, 0x56, 0x30, 0xc4, 0xb6, + 0x44, 0x9d, 0x7b, 0x2c, 0x9d, 0xe5, 0x5e, 0xe6, 0xef, 0x0c, 0x61, 0xaa, + 0xbf, 0xe4, 0x2a, 0x1b, 0xee, 0x84, 0x9e, 0xb8, 0x83, 0x7d, 0xc1, 0x43, + 0xce, 0x44, 0xa7, 0x13, 0x70, 0x0d, 0x91, 0x1f, 0xf4, 0xc8, 0x13, 0xad, + 0x83, 0x60, 0xd9, 0xd8, 0x72, 0xa8, 0x73, 0x24, 0x1e, 0xb5, 0xac, 0x22, + 0x0e, 0xca, 0x17, 0x89, 0x62, 0x58, 0x44, 0x1b, 0xab, 0x89, 0x25, 0x01, + 0x00, 0x0f, 0xcd, 0xc4, 0x1b, 0x62, 0xdb, 0x51, 0xb4, 0xd3, 0x0f, 0x51, + 0x2a, 0x9b, 0xf4, 0xbc, 0x73, 0xfc, 0x76, 0xce, 0x36, 0xa4, 0xcd, 0xd9, + 0xd8, 0x2c, 0xea, 0xae, 0x9b, 0xf5, 0x2a, 0xb2, 0x90, 0xd1, 0x4d, 0x75, + 0x18, 0x8a, 0x3f, 0x8a, 0x41, 0x90, 0x23, 0x7d, 0x5b, 0x4b, 0xfe, 0xa4, + 0x03, 0x58, 0x9b, 0x46, 0xb2, 0xc3, 0x60, 0x60, 0x83, 0xf8, 0x7d, 0x50, + 0x41, 0xce, 0xc2, 0xa1, 0x90, 0xc3, 0xbb, 0xef, 0x02, 0x2f, 0xd2, 0x15, + 0x54, 0xee, 0x44, 0x15, 0xd9, 0x0a, 0xae, 0xa7, 0x8a, 0x33, 0xed, 0xb1, + 0x2d, 0x76, 0x36, 0x26, 0xdc, 0x04, 0xeb, 0x9f, 0xf7, 0x61, 0x1f, 0x15, + 0xdc, 0x87, 0x6f, 0xee, 0x46, 0x96, 0x28, 0xad, 0xa1, 0x26, 0x7d, 0x0a, + 0x09, 0xa7, 0x2e, 0x04, 0xa3, 0x8d, 0xbc, 0xf8, 0xbc, 0x04, 0x30, 0x01, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 25:0c:e8:e0:30:61:2e:9f:2b:89:f7:05:4d:7c:f8:fd + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority + Validity + Not Before: Nov 8 00:00:00 2006 GMT + Not After : Nov 7 23:59:59 2021 GMT + Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b: + 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57: + 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8: + 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe: + 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d: + a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59: + 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49: + d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69: + 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96: + bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5: + f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02: + ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6: + f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19: + 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d: + 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95: + ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f: + 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8: + 25:15 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.verisign.com/pca3.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://www.verisign.com/cps + + X509v3 Subject Key Identifier: + 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + 1.3.6.1.5.5.7.1.12: + 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif + Authority Information Access: + OCSP - URI:http://ocsp.verisign.com + + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1 + Signature Algorithm: sha1WithRSAEncryption + 13:02:dd:f8:e8:86:00:f2:5a:f8:f8:20:0c:59:88:62:07:ce: + ce:f7:4e:f9:bb:59:a1:98:e5:e1:38:dd:4e:bc:66:18:d3:ad: + eb:18:f2:0d:c9:6d:3e:4a:94:20:c3:3c:ba:bd:65:54:c6:af: + 44:b3:10:ad:2c:6b:3e:ab:d7:07:b6:b8:81:63:c5:f9:5e:2e: + e5:2a:67:ce:cd:33:0c:2a:d7:89:56:03:23:1f:b3:be:e8:3a: + 08:59:b4:ec:45:35:f7:8a:5b:ff:66:cf:50:af:c6:6d:57:8d: + 19:78:b7:b9:a2:d1:57:ea:1f:9a:4b:af:ba:c9:8e:12:7e:c6: + bd:ff +-----BEGIN CERTIFICATE----- +MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT +LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv +ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8 +RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb +ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR +TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH +iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB +AAGjggGbMIIBlzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0 +dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9 +BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy +aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI +KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU +j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t +L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC52ZXJpc2lnbi5jb20wPgYDVR0lBDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMC +BggrBgEFBQcDAwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUA +A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K +lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ +tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/ +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert37[] = { + 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x04, 0x39, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x25, 0x0c, 0xe8, 0xe0, 0x30, 0x61, 0x2e, 0x9f, 0x2b, + 0x89, 0xf7, 0x05, 0x4d, 0x7c, 0xf8, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, + 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, + 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30, + 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, + 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, + 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, + 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30, + 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35, + 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c, + 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3, + 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22, + 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1, + 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb, + 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0, + 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85, + 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33, + 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51, + 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74, + 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0, + 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06, + 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff, + 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4, + 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19, + 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe, + 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47, + 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5, + 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14, + 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f, + 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x9b, 0x30, 0x82, 0x01, 0x97, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, + 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, + 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, + 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, + 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, + 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, + 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, + 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, + 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, + 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d, 0x25, + 0x04, 0x37, 0x30, 0x35, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60, + 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x81, 0x81, 0x00, 0x13, 0x02, 0xdd, 0xf8, 0xe8, 0x86, 0x00, 0xf2, + 0x5a, 0xf8, 0xf8, 0x20, 0x0c, 0x59, 0x88, 0x62, 0x07, 0xce, 0xce, 0xf7, + 0x4e, 0xf9, 0xbb, 0x59, 0xa1, 0x98, 0xe5, 0xe1, 0x38, 0xdd, 0x4e, 0xbc, + 0x66, 0x18, 0xd3, 0xad, 0xeb, 0x18, 0xf2, 0x0d, 0xc9, 0x6d, 0x3e, 0x4a, + 0x94, 0x20, 0xc3, 0x3c, 0xba, 0xbd, 0x65, 0x54, 0xc6, 0xaf, 0x44, 0xb3, + 0x10, 0xad, 0x2c, 0x6b, 0x3e, 0xab, 0xd7, 0x07, 0xb6, 0xb8, 0x81, 0x63, + 0xc5, 0xf9, 0x5e, 0x2e, 0xe5, 0x2a, 0x67, 0xce, 0xcd, 0x33, 0x0c, 0x2a, + 0xd7, 0x89, 0x56, 0x03, 0x23, 0x1f, 0xb3, 0xbe, 0xe8, 0x3a, 0x08, 0x59, + 0xb4, 0xec, 0x45, 0x35, 0xf7, 0x8a, 0x5b, 0xff, 0x66, 0xcf, 0x50, 0xaf, + 0xc6, 0x6d, 0x57, 0x8d, 0x19, 0x78, 0xb7, 0xb9, 0xa2, 0xd1, 0x57, 0xea, + 0x1f, 0x9a, 0x4b, 0xaf, 0xba, 0xc9, 0x8e, 0x12, 0x7e, 0xc6, 0xbd, 0xff, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 2c:69:e1:2f:6a:67:0b:d9:9d:d2:0f:91:9e:f0:9e:51 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA + Validity + Not Before: Jun 10 00:00:00 2014 GMT + Not After : Jun 9 23:59:59 2024 GMT + Subject: C=US, O=thawte, Inc., OU=Domain Validated SSL, CN=thawte DV SSL CA - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ea:94:07:85:c8:41:2c:f6:83:12:6c:92:5f:ab: + 1f:00:d4:96:6f:74:cd:2e:11:e9:6c:0f:39:01:b9: + 48:90:40:39:4d:c4:a2:c8:79:6a:a5:9a:bd:91:44: + 65:77:54:ad:ff:25:5f:ee:42:fb:b3:02:0f:ea:5d: + 7a:dd:1a:54:9e:d7:73:42:9b:cc:79:5f:c5:4d:f4: + b7:0b:18:39:20:7a:dd:50:01:5d:34:45:5f:4c:11: + 0e:f5:87:26:26:b4:b0:f3:7e:71:a0:31:71:50:89: + 68:5a:63:8a:14:62:e5:8c:3a:16:55:0d:3e:eb:aa: + 80:1d:71:7a:e3:87:07:ab:bd:a2:74:cd:da:08:01: + 9d:1b:cc:27:88:8c:47:d4:69:25:42:d6:bb:50:6d: + 85:50:d0:48:82:0d:08:9f:e9:23:e3:42:c6:3c:98: + b8:bb:6e:c5:70:13:df:19:1d:01:fd:d2:b5:4e:e6: + 62:f4:07:fa:6b:7d:11:77:c4:62:4f:40:4e:a5:78: + 97:ab:2c:4d:0c:a7:7c:c3:c4:50:32:9f:d0:70:9b: + 0f:ff:ff:75:59:34:85:ad:49:d5:35:ee:4f:5b:d4: + d4:36:95:a0:7e:e8:c5:a1:1c:bd:13:4e:7d:ee:63: + 6a:96:19:99:c8:a7:2a:00:e6:51:8d:46:eb:30:58: + e8:2d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: https://www.thawte.com/cps + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://t.symcd.com + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://t.symcb.com/ThawtePCA.crl + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-698 + X509v3 Subject Key Identifier: + 9F:B8:C1:A9:6C:F2:F5:C0:22:2A:94:ED:5C:99:AC:D4:EC:D7:C6:07 + X509v3 Authority Key Identifier: + keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50 + + Signature Algorithm: sha256WithRSAEncryption + 53:54:f2:47:a8:02:d7:ef:aa:35:78:be:4a:08:0d:90:18:4b: + 6d:9e:2a:53:2b:e9:54:17:77:74:29:7e:d0:37:07:05:b8:e4: + fa:b8:b4:63:98:44:dc:c6:4f:81:06:8c:3a:be:c7:30:57:c6: + 70:fc:d6:93:19:9f:c3:55:d7:3e:1f:72:8a:9d:30:5a:35:97: + 32:cb:63:e4:c6:72:df:fb:68:ca:69:2f:db:cd:50:38:3e:2b: + bb:ab:3b:82:c7:fd:4b:9b:bd:7c:41:98:ef:01:53:d8:35:8f: + 25:c9:03:06:e6:9c:57:c1:51:0f:9e:f6:7d:93:4d:f8:76:c8: + 3a:6b:f4:c4:8f:33:32:7f:9d:21:84:34:d9:a7:f9:92:fa:41: + 91:61:84:05:9d:a3:79:46:ce:67:e7:81:f2:5e:ac:4c:bc:a8: + ab:6a:6d:15:e2:9c:4e:5a:d9:63:80:bc:f7:42:eb:9a:44:c6: + 8c:6b:06:36:b4:8b:32:89:de:c2:f1:a8:26:aa:a9:ac:ff:ea: + 71:a6:e7:8c:41:fa:17:35:bb:b3:87:31:a9:93:c2:c8:58:e1: + 0a:4e:95:83:9c:b9:ed:3b:a5:ef:08:e0:74:f9:c3:1b:e6:07: + a3:ee:07:d7:42:22:79:21:a0:a1:d4:1d:26:d3:d0:d6:a6:5d: + 2b:41:c0:79 +-----BEGIN CERTIFICATE----- +MIIE0jCCA7qgAwIBAgIQLGnhL2pnC9md0g+RnvCeUTANBgkqhkiG9w0BAQsFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTQwNjEwMDAwMDAwWhcNMjQw +NjA5MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu +MR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEeMBwGA1UEAxMVdGhhd3Rl +IERWIFNTTCBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +6pQHhchBLPaDEmySX6sfANSWb3TNLhHpbA85AblIkEA5TcSiyHlqpZq9kURld1St +/yVf7kL7swIP6l163RpUntdzQpvMeV/FTfS3Cxg5IHrdUAFdNEVfTBEO9YcmJrSw +835xoDFxUIloWmOKFGLljDoWVQ0+66qAHXF644cHq72idM3aCAGdG8wniIxH1Gkl +Qta7UG2FUNBIgg0In+kj40LGPJi4u27FcBPfGR0B/dK1TuZi9Af6a30Rd8RiT0BO +pXiXqyxNDKd8w8RQMp/QcJsP//91WTSFrUnVNe5PW9TUNpWgfujFoRy9E0597mNq +lhmZyKcqAOZRjUbrMFjoLQIDAQABo4IBOTCCATUwEgYDVR0TAQH/BAgwBgEB/wIB +ADBBBgNVHSAEOjA4MDYGCmCGSAGG+EUBBzYwKDAmBggrBgEFBQcCARYaaHR0cHM6 +Ly93d3cudGhhd3RlLmNvbS9jcHMwDgYDVR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEB +BCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL3Quc3ltY2QuY29tMDEGA1UdHwQqMCgw +JqAkoCKGIGh0dHA6Ly90LnN5bWNiLmNvbS9UaGF3dGVQQ0EuY3JsMCkGA1UdEQQi +MCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTY5ODAdBgNVHQ4EFgQUn7jB +qWzy9cAiKpTtXJms1OzXxgcwHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutX +SFAwDQYJKoZIhvcNAQELBQADggEBAFNU8keoAtfvqjV4vkoIDZAYS22eKlMr6VQX +d3QpftA3BwW45Pq4tGOYRNzGT4EGjDq+xzBXxnD81pMZn8NV1z4fcoqdMFo1lzLL +Y+TGct/7aMppL9vNUDg+K7urO4LH/UubvXxBmO8BU9g1jyXJAwbmnFfBUQ+e9n2T +Tfh2yDpr9MSPMzJ/nSGENNmn+ZL6QZFhhAWdo3lGzmfngfJerEy8qKtqbRXinE5a +2WOAvPdC65pExoxrBja0izKJ3sLxqCaqqaz/6nGm54xB+hc1u7OHMamTwshY4QpO +lYOcue07pe8I4HT5wxvmB6PuB9dCInkhoKHUHSbT0NamXStBwHk= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert38[] = { + 0x30, 0x82, 0x04, 0xd2, 0x30, 0x82, 0x03, 0xba, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x2c, 0x69, 0xe1, 0x2f, 0x6a, 0x67, 0x0b, 0xd9, 0x9d, + 0xd2, 0x0f, 0x91, 0x9e, 0xf0, 0x9e, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, + 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, + 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, + 0x36, 0x30, 0x39, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x63, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x14, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, + 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xea, 0x94, 0x07, 0x85, 0xc8, 0x41, 0x2c, 0xf6, 0x83, 0x12, 0x6c, 0x92, + 0x5f, 0xab, 0x1f, 0x00, 0xd4, 0x96, 0x6f, 0x74, 0xcd, 0x2e, 0x11, 0xe9, + 0x6c, 0x0f, 0x39, 0x01, 0xb9, 0x48, 0x90, 0x40, 0x39, 0x4d, 0xc4, 0xa2, + 0xc8, 0x79, 0x6a, 0xa5, 0x9a, 0xbd, 0x91, 0x44, 0x65, 0x77, 0x54, 0xad, + 0xff, 0x25, 0x5f, 0xee, 0x42, 0xfb, 0xb3, 0x02, 0x0f, 0xea, 0x5d, 0x7a, + 0xdd, 0x1a, 0x54, 0x9e, 0xd7, 0x73, 0x42, 0x9b, 0xcc, 0x79, 0x5f, 0xc5, + 0x4d, 0xf4, 0xb7, 0x0b, 0x18, 0x39, 0x20, 0x7a, 0xdd, 0x50, 0x01, 0x5d, + 0x34, 0x45, 0x5f, 0x4c, 0x11, 0x0e, 0xf5, 0x87, 0x26, 0x26, 0xb4, 0xb0, + 0xf3, 0x7e, 0x71, 0xa0, 0x31, 0x71, 0x50, 0x89, 0x68, 0x5a, 0x63, 0x8a, + 0x14, 0x62, 0xe5, 0x8c, 0x3a, 0x16, 0x55, 0x0d, 0x3e, 0xeb, 0xaa, 0x80, + 0x1d, 0x71, 0x7a, 0xe3, 0x87, 0x07, 0xab, 0xbd, 0xa2, 0x74, 0xcd, 0xda, + 0x08, 0x01, 0x9d, 0x1b, 0xcc, 0x27, 0x88, 0x8c, 0x47, 0xd4, 0x69, 0x25, + 0x42, 0xd6, 0xbb, 0x50, 0x6d, 0x85, 0x50, 0xd0, 0x48, 0x82, 0x0d, 0x08, + 0x9f, 0xe9, 0x23, 0xe3, 0x42, 0xc6, 0x3c, 0x98, 0xb8, 0xbb, 0x6e, 0xc5, + 0x70, 0x13, 0xdf, 0x19, 0x1d, 0x01, 0xfd, 0xd2, 0xb5, 0x4e, 0xe6, 0x62, + 0xf4, 0x07, 0xfa, 0x6b, 0x7d, 0x11, 0x77, 0xc4, 0x62, 0x4f, 0x40, 0x4e, + 0xa5, 0x78, 0x97, 0xab, 0x2c, 0x4d, 0x0c, 0xa7, 0x7c, 0xc3, 0xc4, 0x50, + 0x32, 0x9f, 0xd0, 0x70, 0x9b, 0x0f, 0xff, 0xff, 0x75, 0x59, 0x34, 0x85, + 0xad, 0x49, 0xd5, 0x35, 0xee, 0x4f, 0x5b, 0xd4, 0xd4, 0x36, 0x95, 0xa0, + 0x7e, 0xe8, 0xc5, 0xa1, 0x1c, 0xbd, 0x13, 0x4e, 0x7d, 0xee, 0x63, 0x6a, + 0x96, 0x19, 0x99, 0xc8, 0xa7, 0x2a, 0x00, 0xe6, 0x51, 0x8d, 0x46, 0xeb, + 0x30, 0x58, 0xe8, 0x2d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, + 0x39, 0x30, 0x82, 0x01, 0x35, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38, + 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, + 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, 0x30, 0x28, 0x30, + 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, + 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, + 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x36, 0x39, 0x38, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9f, 0xb8, 0xc1, + 0xa9, 0x6c, 0xf2, 0xf5, 0xc0, 0x22, 0x2a, 0x94, 0xed, 0x5c, 0x99, 0xac, + 0xd4, 0xec, 0xd7, 0xc6, 0x07, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, + 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, + 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, + 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x53, 0x54, + 0xf2, 0x47, 0xa8, 0x02, 0xd7, 0xef, 0xaa, 0x35, 0x78, 0xbe, 0x4a, 0x08, + 0x0d, 0x90, 0x18, 0x4b, 0x6d, 0x9e, 0x2a, 0x53, 0x2b, 0xe9, 0x54, 0x17, + 0x77, 0x74, 0x29, 0x7e, 0xd0, 0x37, 0x07, 0x05, 0xb8, 0xe4, 0xfa, 0xb8, + 0xb4, 0x63, 0x98, 0x44, 0xdc, 0xc6, 0x4f, 0x81, 0x06, 0x8c, 0x3a, 0xbe, + 0xc7, 0x30, 0x57, 0xc6, 0x70, 0xfc, 0xd6, 0x93, 0x19, 0x9f, 0xc3, 0x55, + 0xd7, 0x3e, 0x1f, 0x72, 0x8a, 0x9d, 0x30, 0x5a, 0x35, 0x97, 0x32, 0xcb, + 0x63, 0xe4, 0xc6, 0x72, 0xdf, 0xfb, 0x68, 0xca, 0x69, 0x2f, 0xdb, 0xcd, + 0x50, 0x38, 0x3e, 0x2b, 0xbb, 0xab, 0x3b, 0x82, 0xc7, 0xfd, 0x4b, 0x9b, + 0xbd, 0x7c, 0x41, 0x98, 0xef, 0x01, 0x53, 0xd8, 0x35, 0x8f, 0x25, 0xc9, + 0x03, 0x06, 0xe6, 0x9c, 0x57, 0xc1, 0x51, 0x0f, 0x9e, 0xf6, 0x7d, 0x93, + 0x4d, 0xf8, 0x76, 0xc8, 0x3a, 0x6b, 0xf4, 0xc4, 0x8f, 0x33, 0x32, 0x7f, + 0x9d, 0x21, 0x84, 0x34, 0xd9, 0xa7, 0xf9, 0x92, 0xfa, 0x41, 0x91, 0x61, + 0x84, 0x05, 0x9d, 0xa3, 0x79, 0x46, 0xce, 0x67, 0xe7, 0x81, 0xf2, 0x5e, + 0xac, 0x4c, 0xbc, 0xa8, 0xab, 0x6a, 0x6d, 0x15, 0xe2, 0x9c, 0x4e, 0x5a, + 0xd9, 0x63, 0x80, 0xbc, 0xf7, 0x42, 0xeb, 0x9a, 0x44, 0xc6, 0x8c, 0x6b, + 0x06, 0x36, 0xb4, 0x8b, 0x32, 0x89, 0xde, 0xc2, 0xf1, 0xa8, 0x26, 0xaa, + 0xa9, 0xac, 0xff, 0xea, 0x71, 0xa6, 0xe7, 0x8c, 0x41, 0xfa, 0x17, 0x35, + 0xbb, 0xb3, 0x87, 0x31, 0xa9, 0x93, 0xc2, 0xc8, 0x58, 0xe1, 0x0a, 0x4e, + 0x95, 0x83, 0x9c, 0xb9, 0xed, 0x3b, 0xa5, 0xef, 0x08, 0xe0, 0x74, 0xf9, + 0xc3, 0x1b, 0xe6, 0x07, 0xa3, 0xee, 0x07, 0xd7, 0x42, 0x22, 0x79, 0x21, + 0xa0, 0xa1, 0xd4, 0x1d, 0x26, 0xd3, 0xd0, 0xd6, 0xa6, 0x5d, 0x2b, 0x41, + 0xc0, 0x79, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1372799044 (0x51d34044) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority + Validity + Not Before: Sep 22 17:14:57 2014 GMT + Not After : Sep 23 01:31:53 2024 GMT + Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ba:84:b6:72:db:9e:0c:6b:e2:99:e9:30:01:a7: + 76:ea:32:b8:95:41:1a:c9:da:61:4e:58:72:cf:fe: + f6:82:79:bf:73:61:06:0a:a5:27:d8:b3:5f:d3:45: + 4e:1c:72:d6:4e:32:f2:72:8a:0f:f7:83:19:d0:6a: + 80:80:00:45:1e:b0:c7:e7:9a:bf:12:57:27:1c:a3: + 68:2f:0a:87:bd:6a:6b:0e:5e:65:f3:1c:77:d5:d4: + 85:8d:70:21:b4:b3:32:e7:8b:a2:d5:86:39:02:b1: + b8:d2:47:ce:e4:c9:49:c4:3b:a7:de:fb:54:7d:57: + be:f0:e8:6e:c2:79:b2:3a:0b:55:e2:50:98:16:32: + 13:5c:2f:78:56:c1:c2:94:b3:f2:5a:e4:27:9a:9f: + 24:d7:c6:ec:d0:9b:25:82:e3:cc:c2:c4:45:c5:8c: + 97:7a:06:6b:2a:11:9f:a9:0a:6e:48:3b:6f:db:d4: + 11:19:42:f7:8f:07:bf:f5:53:5f:9c:3e:f4:17:2c: + e6:69:ac:4e:32:4c:62:77:ea:b7:e8:e5:bb:34:bc: + 19:8b:ae:9c:51:e7:b7:7e:b5:53:b1:33:22:e5:6d: + cf:70:3c:1a:fa:e2:9b:67:b6:83:f4:8d:a5:af:62: + 4c:4d:e0:58:ac:64:34:12:03:f8:b6:8d:94:63:24: + a4:71 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:1 + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.entrust.net/rootca1.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/CPS + + X509v3 Subject Key Identifier: + 6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB + X509v3 Authority Key Identifier: + keyid:68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D + + Signature Algorithm: sha256WithRSAEncryption + 69:33:83:fc:28:7a:6f:7d:ef:9d:55:eb:c5:3e:7a:9d:75:b3: + cc:c3:38:36:d9:34:a2:28:68:18:ea:1e:69:d3:bd:e7:d0:77: + da:b8:00:83:4e:4a:cf:6f:d1:f1:c1:22:3f:74:e4:f7:98:49: + 9e:9b:b6:9e:e1:db:98:77:2d:56:34:b1:a8:3c:d9:fd:c0:cd: + c7:bf:05:03:d4:02:c5:f1:e5:c6:da:08:a5:13:c7:62:23:11: + d1:61:30:1d:60:84:45:ef:79:a8:c6:26:93:a4:b7:cd:34:b8: + 69:c5:13:f6:91:b3:c9:45:73:76:b6:92:f6:76:0a:5b:e1:03: + 47:b7:e9:29:4c:91:32:23:37:4a:9c:35:d8:78:fd:1d:1f:e4: + 83:89:24:80:ad:b7:f9:cf:e4:5d:a5:d4:71:c4:85:5b:70:1f: + db:3f:1c:01:eb:1a:45:26:31:14:cc:65:bf:67:de:ca:cc:33: + 65:e5:41:91:d7:37:be:41:1a:96:9d:e6:8a:97:9d:a7:ce:ac: + 4e:9a:3d:bd:01:a0:6a:d9:4f:22:00:8b:44:d5:69:62:7b:2e: + eb:cc:ba:e7:92:7d:69:67:3d:fc:b8:7c:de:41:87:d0:69:ea: + ba:0a:18:7a:1a:95:43:b3:79:71:28:76:6d:a1:fb:57:4a:ec: + 4d:c8:0e:10 +-----BEGIN CERTIFICATE----- +MIIE/zCCA+egAwIBAgIEUdNARDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDkyMjE3MTQ1N1oXDTI0MDkyMzAx +MzE1M1owgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgw +JgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQL +EzAoYykgMjAwOSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9u +bHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoS2ctueDGvi +mekwAad26jK4lUEaydphTlhyz/72gnm/c2EGCqUn2LNf00VOHHLWTjLycooP94MZ +0GqAgABFHrDH55q/ElcnHKNoLwqHvWprDl5l8xx31dSFjXAhtLMy54ui1YY5ArG4 +0kfO5MlJxDun3vtUfVe+8OhuwnmyOgtV4lCYFjITXC94VsHClLPyWuQnmp8k18bs +0JslguPMwsRFxYyXegZrKhGfqQpuSDtv29QRGUL3jwe/9VNfnD70FyzmaaxOMkxi +d+q36OW7NLwZi66cUee3frVTsTMi5W3PcDwa+uKbZ7aD9I2lr2JMTeBYrGQ0EgP4 +to2UYySkcQIDAQABo4IBDzCCAQswDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQEwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz +cC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1 +c3QubmV0L3Jvb3RjYTEuY3JsMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYGCCsGAQUF +BwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NQUzAdBgNVHQ4EFgQUanImetAe +733nO2lR1GyNn5ASZqswHwYDVR0jBBgwFoAUaJDkZ6SmU4DHhmak8fdLQ/uEvW0w +DQYJKoZIhvcNAQELBQADggEBAGkzg/woem99751V68U+ep11s8zDODbZNKIoaBjq +HmnTvefQd9q4AINOSs9v0fHBIj905PeYSZ6btp7h25h3LVY0sag82f3Azce/BQPU +AsXx5cbaCKUTx2IjEdFhMB1ghEXveajGJpOkt800uGnFE/aRs8lFc3a2kvZ2Clvh +A0e36SlMkTIjN0qcNdh4/R0f5IOJJICtt/nP5F2l1HHEhVtwH9s/HAHrGkUmMRTM +Zb9n3srMM2XlQZHXN75BGpad5oqXnafOrE6aPb0BoGrZTyIAi0TVaWJ7LuvMuueS +fWlnPfy4fN5Bh9Bp6roKGHoalUOzeXEodm2h+1dK7E3IDhA= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert39[] = { + 0x30, 0x82, 0x04, 0xff, 0x30, 0x82, 0x03, 0xe7, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x51, 0xd3, 0x40, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xb0, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x30, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x73, 0x20, + 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, + 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x45, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d, + 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x45, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x34, 0x30, 0x39, 0x32, 0x32, 0x31, 0x37, 0x31, 0x34, 0x35, + 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x39, 0x32, 0x33, 0x30, 0x31, + 0x33, 0x31, 0x35, 0x33, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, + 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28, 0x30, + 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65, 0x20, + 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74, 0x65, + 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45, + 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, + 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xba, 0x84, 0xb6, 0x72, 0xdb, 0x9e, 0x0c, 0x6b, 0xe2, + 0x99, 0xe9, 0x30, 0x01, 0xa7, 0x76, 0xea, 0x32, 0xb8, 0x95, 0x41, 0x1a, + 0xc9, 0xda, 0x61, 0x4e, 0x58, 0x72, 0xcf, 0xfe, 0xf6, 0x82, 0x79, 0xbf, + 0x73, 0x61, 0x06, 0x0a, 0xa5, 0x27, 0xd8, 0xb3, 0x5f, 0xd3, 0x45, 0x4e, + 0x1c, 0x72, 0xd6, 0x4e, 0x32, 0xf2, 0x72, 0x8a, 0x0f, 0xf7, 0x83, 0x19, + 0xd0, 0x6a, 0x80, 0x80, 0x00, 0x45, 0x1e, 0xb0, 0xc7, 0xe7, 0x9a, 0xbf, + 0x12, 0x57, 0x27, 0x1c, 0xa3, 0x68, 0x2f, 0x0a, 0x87, 0xbd, 0x6a, 0x6b, + 0x0e, 0x5e, 0x65, 0xf3, 0x1c, 0x77, 0xd5, 0xd4, 0x85, 0x8d, 0x70, 0x21, + 0xb4, 0xb3, 0x32, 0xe7, 0x8b, 0xa2, 0xd5, 0x86, 0x39, 0x02, 0xb1, 0xb8, + 0xd2, 0x47, 0xce, 0xe4, 0xc9, 0x49, 0xc4, 0x3b, 0xa7, 0xde, 0xfb, 0x54, + 0x7d, 0x57, 0xbe, 0xf0, 0xe8, 0x6e, 0xc2, 0x79, 0xb2, 0x3a, 0x0b, 0x55, + 0xe2, 0x50, 0x98, 0x16, 0x32, 0x13, 0x5c, 0x2f, 0x78, 0x56, 0xc1, 0xc2, + 0x94, 0xb3, 0xf2, 0x5a, 0xe4, 0x27, 0x9a, 0x9f, 0x24, 0xd7, 0xc6, 0xec, + 0xd0, 0x9b, 0x25, 0x82, 0xe3, 0xcc, 0xc2, 0xc4, 0x45, 0xc5, 0x8c, 0x97, + 0x7a, 0x06, 0x6b, 0x2a, 0x11, 0x9f, 0xa9, 0x0a, 0x6e, 0x48, 0x3b, 0x6f, + 0xdb, 0xd4, 0x11, 0x19, 0x42, 0xf7, 0x8f, 0x07, 0xbf, 0xf5, 0x53, 0x5f, + 0x9c, 0x3e, 0xf4, 0x17, 0x2c, 0xe6, 0x69, 0xac, 0x4e, 0x32, 0x4c, 0x62, + 0x77, 0xea, 0xb7, 0xe8, 0xe5, 0xbb, 0x34, 0xbc, 0x19, 0x8b, 0xae, 0x9c, + 0x51, 0xe7, 0xb7, 0x7e, 0xb5, 0x53, 0xb1, 0x33, 0x22, 0xe5, 0x6d, 0xcf, + 0x70, 0x3c, 0x1a, 0xfa, 0xe2, 0x9b, 0x67, 0xb6, 0x83, 0xf4, 0x8d, 0xa5, + 0xaf, 0x62, 0x4c, 0x4d, 0xe0, 0x58, 0xac, 0x64, 0x34, 0x12, 0x03, 0xf8, + 0xb6, 0x8d, 0x94, 0x63, 0x24, 0xa4, 0x71, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x0f, 0x30, 0x82, 0x01, 0x0b, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x33, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, + 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, + 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63, + 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, + 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, 0x1e, + 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, 0x12, + 0x66, 0xab, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x68, 0x90, 0xe4, 0x67, 0xa4, 0xa6, 0x53, 0x80, 0xc7, + 0x86, 0x66, 0xa4, 0xf1, 0xf7, 0x4b, 0x43, 0xfb, 0x84, 0xbd, 0x6d, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x33, 0x83, 0xfc, 0x28, + 0x7a, 0x6f, 0x7d, 0xef, 0x9d, 0x55, 0xeb, 0xc5, 0x3e, 0x7a, 0x9d, 0x75, + 0xb3, 0xcc, 0xc3, 0x38, 0x36, 0xd9, 0x34, 0xa2, 0x28, 0x68, 0x18, 0xea, + 0x1e, 0x69, 0xd3, 0xbd, 0xe7, 0xd0, 0x77, 0xda, 0xb8, 0x00, 0x83, 0x4e, + 0x4a, 0xcf, 0x6f, 0xd1, 0xf1, 0xc1, 0x22, 0x3f, 0x74, 0xe4, 0xf7, 0x98, + 0x49, 0x9e, 0x9b, 0xb6, 0x9e, 0xe1, 0xdb, 0x98, 0x77, 0x2d, 0x56, 0x34, + 0xb1, 0xa8, 0x3c, 0xd9, 0xfd, 0xc0, 0xcd, 0xc7, 0xbf, 0x05, 0x03, 0xd4, + 0x02, 0xc5, 0xf1, 0xe5, 0xc6, 0xda, 0x08, 0xa5, 0x13, 0xc7, 0x62, 0x23, + 0x11, 0xd1, 0x61, 0x30, 0x1d, 0x60, 0x84, 0x45, 0xef, 0x79, 0xa8, 0xc6, + 0x26, 0x93, 0xa4, 0xb7, 0xcd, 0x34, 0xb8, 0x69, 0xc5, 0x13, 0xf6, 0x91, + 0xb3, 0xc9, 0x45, 0x73, 0x76, 0xb6, 0x92, 0xf6, 0x76, 0x0a, 0x5b, 0xe1, + 0x03, 0x47, 0xb7, 0xe9, 0x29, 0x4c, 0x91, 0x32, 0x23, 0x37, 0x4a, 0x9c, + 0x35, 0xd8, 0x78, 0xfd, 0x1d, 0x1f, 0xe4, 0x83, 0x89, 0x24, 0x80, 0xad, + 0xb7, 0xf9, 0xcf, 0xe4, 0x5d, 0xa5, 0xd4, 0x71, 0xc4, 0x85, 0x5b, 0x70, + 0x1f, 0xdb, 0x3f, 0x1c, 0x01, 0xeb, 0x1a, 0x45, 0x26, 0x31, 0x14, 0xcc, + 0x65, 0xbf, 0x67, 0xde, 0xca, 0xcc, 0x33, 0x65, 0xe5, 0x41, 0x91, 0xd7, + 0x37, 0xbe, 0x41, 0x1a, 0x96, 0x9d, 0xe6, 0x8a, 0x97, 0x9d, 0xa7, 0xce, + 0xac, 0x4e, 0x9a, 0x3d, 0xbd, 0x01, 0xa0, 0x6a, 0xd9, 0x4f, 0x22, 0x00, + 0x8b, 0x44, 0xd5, 0x69, 0x62, 0x7b, 0x2e, 0xeb, 0xcc, 0xba, 0xe7, 0x92, + 0x7d, 0x69, 0x67, 0x3d, 0xfc, 0xb8, 0x7c, 0xde, 0x41, 0x87, 0xd0, 0x69, + 0xea, 0xba, 0x0a, 0x18, 0x7a, 0x1a, 0x95, 0x43, 0xb3, 0x79, 0x71, 0x28, + 0x76, 0x6d, 0xa1, 0xfb, 0x57, 0x4a, 0xec, 0x4d, 0xc8, 0x0e, 0x10, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 7 (0x7) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2 + Validity + Not Before: May 3 07:00:00 2011 GMT + Not After : May 3 07:00:00 2031 GMT + Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certs.starfieldtech.com/repository/, CN=Starfield Secure Certificate Authority - G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e5:90:66:4b:ec:f9:46:71:a9:20:83:be:e9:6c: + bf:4a:c9:48:69:81:75:4e:6d:24:f6:cb:17:13:f8: + b0:71:59:84:7a:6b:2b:85:a4:34:b5:16:e5:cb:cc: + e9:41:70:2c:a4:2e:d6:fa:32:7d:e1:a8:de:94:10: + ac:31:c1:c0:d8:6a:ff:59:27:ab:76:d6:fc:0b:74: + 6b:b8:a7:ae:3f:c4:54:f4:b4:31:44:dd:93:56:8c: + a4:4c:5e:9b:89:cb:24:83:9b:e2:57:7d:b7:d8:12: + 1f:c9:85:6d:f4:d1:80:f1:50:9b:87:ae:d4:0b:10: + 05:fb:27:ba:28:6d:17:e9:0e:d6:4d:b9:39:55:06: + ff:0a:24:05:7e:2f:c6:1d:72:6c:d4:8b:29:8c:57: + 7d:da:d9:eb:66:1a:d3:4f:a7:df:7f:52:c4:30:c5: + a5:c9:0e:02:c5:53:bf:77:38:68:06:24:c3:66:c8: + 37:7e:30:1e:45:71:23:35:ff:90:d8:2a:9d:8d:e7: + b0:92:4d:3c:7f:2a:0a:93:dc:cd:16:46:65:f7:60: + 84:8b:76:4b:91:27:73:14:92:e0:ea:ee:8f:16:ea: + 8d:0e:3e:76:17:bf:7d:89:80:80:44:43:e7:2d:e0: + 43:09:75:da:36:e8:ad:db:89:3a:f5:5d:12:8e:23: + 04:83 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 25:45:81:68:50:26:38:3D:3B:2D:2C:BE:CD:6A:D9:B6:3D:B3:66:63 + X509v3 Authority Key Identifier: + keyid:7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27 + + Authority Information Access: + OCSP - URI:http://ocsp.starfieldtech.com/ + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.starfieldtech.com/sfroot-g2.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: https://certs.starfieldtech.com/repository/ + + Signature Algorithm: sha256WithRSAEncryption + 56:65:ca:fe:f3:3f:0a:a8:93:8b:18:c7:de:43:69:13:34:20: + be:4e:5f:78:a8:6b:9c:db:6a:4d:41:db:c1:13:ec:dc:31:00: + 22:5e:f7:00:9e:0c:e0:34:65:34:f9:b1:3a:4e:48:c8:12:81: + 88:5c:5b:3e:08:53:7a:f7:1a:64:df:b8:50:61:cc:53:51:40: + 29:4b:c2:f4:ae:3a:5f:e4:ca:ad:26:cc:4e:61:43:e5:fd:57: + a6:37:70:ce:43:2b:b0:94:c3:92:e9:e1:5f:aa:10:49:b7:69: + e4:e0:d0:1f:64:a4:2b:cd:1f:6f:a0:f8:84:24:18:ce:79:3d: + a9:91:bf:54:18:13:89:99:54:11:0d:55:c5:26:0b:79:4f:5a: + 1c:6e:f9:63:db:14:80:a4:07:ab:fa:b2:a5:b9:88:dd:91:fe: + 65:3b:a4:a3:79:be:89:4d:e1:d0:b0:f4:c8:17:0c:0a:96:14: + 7c:09:b7:6c:e1:c2:d8:55:d4:18:a0:aa:41:69:70:24:a3:b9: + ef:e9:5a:dc:3e:eb:94:4a:f0:b7:de:5f:0e:76:fa:fb:fb:69: + 03:45:40:50:ee:72:0c:a4:12:86:81:cd:13:d1:4e:c4:3c:ca: + 4e:0d:d2:26:f1:00:b7:b4:a6:a2:e1:6e:7a:81:fd:30:ac:7a: + 1f:c7:59:7b +-----BEGIN CERTIFICATE----- +MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw +MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk +dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg +Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF +pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE +3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV +Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+ +MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX +v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+ +zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB +BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo +LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo +LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF +BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN +QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0 +rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO +eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ +sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ +7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7 +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert40[] = { + 0x30, 0x82, 0x05, 0x00, 0x30, 0x82, 0x03, 0xe8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x8f, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, + 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, + 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, + 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, + 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xc6, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, + 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, + 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, + 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, + 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x31, 0x34, 0x30, 0x32, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b, 0x53, 0x74, 0x61, 0x72, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, + 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, + 0x90, 0x66, 0x4b, 0xec, 0xf9, 0x46, 0x71, 0xa9, 0x20, 0x83, 0xbe, 0xe9, + 0x6c, 0xbf, 0x4a, 0xc9, 0x48, 0x69, 0x81, 0x75, 0x4e, 0x6d, 0x24, 0xf6, + 0xcb, 0x17, 0x13, 0xf8, 0xb0, 0x71, 0x59, 0x84, 0x7a, 0x6b, 0x2b, 0x85, + 0xa4, 0x34, 0xb5, 0x16, 0xe5, 0xcb, 0xcc, 0xe9, 0x41, 0x70, 0x2c, 0xa4, + 0x2e, 0xd6, 0xfa, 0x32, 0x7d, 0xe1, 0xa8, 0xde, 0x94, 0x10, 0xac, 0x31, + 0xc1, 0xc0, 0xd8, 0x6a, 0xff, 0x59, 0x27, 0xab, 0x76, 0xd6, 0xfc, 0x0b, + 0x74, 0x6b, 0xb8, 0xa7, 0xae, 0x3f, 0xc4, 0x54, 0xf4, 0xb4, 0x31, 0x44, + 0xdd, 0x93, 0x56, 0x8c, 0xa4, 0x4c, 0x5e, 0x9b, 0x89, 0xcb, 0x24, 0x83, + 0x9b, 0xe2, 0x57, 0x7d, 0xb7, 0xd8, 0x12, 0x1f, 0xc9, 0x85, 0x6d, 0xf4, + 0xd1, 0x80, 0xf1, 0x50, 0x9b, 0x87, 0xae, 0xd4, 0x0b, 0x10, 0x05, 0xfb, + 0x27, 0xba, 0x28, 0x6d, 0x17, 0xe9, 0x0e, 0xd6, 0x4d, 0xb9, 0x39, 0x55, + 0x06, 0xff, 0x0a, 0x24, 0x05, 0x7e, 0x2f, 0xc6, 0x1d, 0x72, 0x6c, 0xd4, + 0x8b, 0x29, 0x8c, 0x57, 0x7d, 0xda, 0xd9, 0xeb, 0x66, 0x1a, 0xd3, 0x4f, + 0xa7, 0xdf, 0x7f, 0x52, 0xc4, 0x30, 0xc5, 0xa5, 0xc9, 0x0e, 0x02, 0xc5, + 0x53, 0xbf, 0x77, 0x38, 0x68, 0x06, 0x24, 0xc3, 0x66, 0xc8, 0x37, 0x7e, + 0x30, 0x1e, 0x45, 0x71, 0x23, 0x35, 0xff, 0x90, 0xd8, 0x2a, 0x9d, 0x8d, + 0xe7, 0xb0, 0x92, 0x4d, 0x3c, 0x7f, 0x2a, 0x0a, 0x93, 0xdc, 0xcd, 0x16, + 0x46, 0x65, 0xf7, 0x60, 0x84, 0x8b, 0x76, 0x4b, 0x91, 0x27, 0x73, 0x14, + 0x92, 0xe0, 0xea, 0xee, 0x8f, 0x16, 0xea, 0x8d, 0x0e, 0x3e, 0x76, 0x17, + 0xbf, 0x7d, 0x89, 0x80, 0x80, 0x44, 0x43, 0xe7, 0x2d, 0xe0, 0x43, 0x09, + 0x75, 0xda, 0x36, 0xe8, 0xad, 0xdb, 0x89, 0x3a, 0xf5, 0x5d, 0x12, 0x8e, + 0x23, 0x04, 0x83, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x2c, + 0x30, 0x82, 0x01, 0x28, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x25, 0x45, 0x81, 0x68, 0x50, 0x26, 0x38, 0x3d, 0x3b, 0x2d, 0x2c, 0xbe, + 0xcd, 0x6a, 0xd9, 0xb6, 0x3d, 0xb3, 0x66, 0x63, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7c, 0x0c, 0x32, + 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4, 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1, + 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1e, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0xa0, 0x2e, 0xa0, 0x2c, 0x86, 0x2a, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f, 0x6f, 0x74, 0x2d, + 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, + 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20, + 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x56, 0x65, 0xca, 0xfe, + 0xf3, 0x3f, 0x0a, 0xa8, 0x93, 0x8b, 0x18, 0xc7, 0xde, 0x43, 0x69, 0x13, + 0x34, 0x20, 0xbe, 0x4e, 0x5f, 0x78, 0xa8, 0x6b, 0x9c, 0xdb, 0x6a, 0x4d, + 0x41, 0xdb, 0xc1, 0x13, 0xec, 0xdc, 0x31, 0x00, 0x22, 0x5e, 0xf7, 0x00, + 0x9e, 0x0c, 0xe0, 0x34, 0x65, 0x34, 0xf9, 0xb1, 0x3a, 0x4e, 0x48, 0xc8, + 0x12, 0x81, 0x88, 0x5c, 0x5b, 0x3e, 0x08, 0x53, 0x7a, 0xf7, 0x1a, 0x64, + 0xdf, 0xb8, 0x50, 0x61, 0xcc, 0x53, 0x51, 0x40, 0x29, 0x4b, 0xc2, 0xf4, + 0xae, 0x3a, 0x5f, 0xe4, 0xca, 0xad, 0x26, 0xcc, 0x4e, 0x61, 0x43, 0xe5, + 0xfd, 0x57, 0xa6, 0x37, 0x70, 0xce, 0x43, 0x2b, 0xb0, 0x94, 0xc3, 0x92, + 0xe9, 0xe1, 0x5f, 0xaa, 0x10, 0x49, 0xb7, 0x69, 0xe4, 0xe0, 0xd0, 0x1f, + 0x64, 0xa4, 0x2b, 0xcd, 0x1f, 0x6f, 0xa0, 0xf8, 0x84, 0x24, 0x18, 0xce, + 0x79, 0x3d, 0xa9, 0x91, 0xbf, 0x54, 0x18, 0x13, 0x89, 0x99, 0x54, 0x11, + 0x0d, 0x55, 0xc5, 0x26, 0x0b, 0x79, 0x4f, 0x5a, 0x1c, 0x6e, 0xf9, 0x63, + 0xdb, 0x14, 0x80, 0xa4, 0x07, 0xab, 0xfa, 0xb2, 0xa5, 0xb9, 0x88, 0xdd, + 0x91, 0xfe, 0x65, 0x3b, 0xa4, 0xa3, 0x79, 0xbe, 0x89, 0x4d, 0xe1, 0xd0, + 0xb0, 0xf4, 0xc8, 0x17, 0x0c, 0x0a, 0x96, 0x14, 0x7c, 0x09, 0xb7, 0x6c, + 0xe1, 0xc2, 0xd8, 0x55, 0xd4, 0x18, 0xa0, 0xaa, 0x41, 0x69, 0x70, 0x24, + 0xa3, 0xb9, 0xef, 0xe9, 0x5a, 0xdc, 0x3e, 0xeb, 0x94, 0x4a, 0xf0, 0xb7, + 0xde, 0x5f, 0x0e, 0x76, 0xfa, 0xfb, 0xfb, 0x69, 0x03, 0x45, 0x40, 0x50, + 0xee, 0x72, 0x0c, 0xa4, 0x12, 0x86, 0x81, 0xcd, 0x13, 0xd1, 0x4e, 0xc4, + 0x3c, 0xca, 0x4e, 0x0d, 0xd2, 0x26, 0xf1, 0x00, 0xb7, 0xb4, 0xa6, 0xa2, + 0xe1, 0x6e, 0x7a, 0x81, 0xfd, 0x30, 0xac, 0x7a, 0x1f, 0xc7, 0x59, 0x7b, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1372807406 (0x51d360ee) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2 + Validity + Not Before: Oct 22 17:05:14 2014 GMT + Not After : Oct 23 07:33:22 2024 GMT + Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Certification Authority - L1K + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:3f:96:d0:4d:b9:2f:44:e7:db:39:5e:9b:50: + ee:5c:a5:61:da:41:67:53:09:aa:00:9a:8e:57:7f: + 29:6b:db:c7:e1:21:24:aa:3a:d0:8d:47:23:d2:ed: + 72:16:f0:91:21:d2:5d:b7:b8:4b:a8:83:8f:b7:91: + 32:68:cf:ce:25:93:2c:b2:7d:97:c8:fe:c1:b4:17: + ba:09:9e:03:90:93:7b:7c:49:83:22:68:8a:9b:de: + 47:c3:31:98:7a:2e:7d:40:0b:d2:ef:3e:d3:b2:8c: + aa:8f:48:a9:ff:00:e8:29:58:06:f7:b6:93:5a:94: + 73:26:26:ad:58:0e:e5:42:b8:d5:ea:73:79:64:68: + 53:25:b8:84:cf:94:7a:ae:06:45:0c:a3:6b:4d:d0: + c6:be:ea:18:a4:36:f0:92:b2:ba:1c:88:8f:3a:52: + 7f:f7:5e:6d:83:1c:9d:f0:1f:e5:c3:d6:dd:a5:78: + 92:3d:b0:6d:2c:ea:c9:cf:94:41:19:71:44:68:ba: + 47:3c:04:e9:5d:ba:3e:f0:35:f7:15:b6:9e:f2:2e: + 15:1e:3f:47:c8:c8:38:a7:73:45:5d:4d:b0:3b:b1: + 8e:17:29:37:ea:dd:05:01:22:bb:94:36:2a:8d:5b: + 35:fe:53:19:2f:08:46:c1:2a:b3:1a:62:1d:4e:2b: + d9:1b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: + CA:TRUE, pathlen:0 + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.entrust.net/g2ca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/rpa + + X509v3 Subject Key Identifier: + 82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF + X509v3 Authority Key Identifier: + keyid:6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB + + Signature Algorithm: sha256WithRSAEncryption + 3f:1c:1a:5b:ff:40:22:1d:8f:35:0c:2d:aa:99:27:ab:c0:11: + 32:70:d7:36:28:69:a5:8d:b1:27:99:42:be:c4:93:eb:48:57: + 43:71:23:c4:e5:4e:ad:ae:43:6f:92:76:c5:19:ef:ca:bc:6f: + 42:4c:16:9a:86:a9:04:38:c7:65:f0:f5:0c:e0:4a:df:a2:fa: + ce:1a:11:a8:9c:69:2f:1b:df:ea:e2:32:f3:ce:4c:bc:46:0c: + c0:89:80:d1:87:6b:a2:cf:6b:d4:7f:fd:f5:60:52:67:57:a0: + 6d:d1:64:41:14:6d:34:62:ed:06:6c:24:f2:06:bc:28:02:af: + 03:2d:c2:33:05:fb:cb:aa:16:e8:65:10:43:f5:69:5c:e3:81: + 58:99:cd:6b:d3:b8:c7:7b:19:55:c9:40:ce:79:55:b8:73:89: + e9:5c:40:66:43:12:7f:07:b8:65:56:d5:8d:c3:a7:f5:b1:b6: + 65:9e:c0:83:36:7f:16:45:3c:74:4b:93:8a:3c:f1:2b:f5:35: + 70:73:7b:e7:82:04:b1:18:98:0e:d4:9c:6f:1a:fc:fc:a7:33: + a5:bb:bb:18:f3:6b:7a:5d:32:87:f7:6d:25:e4:e2:76:86:21: + 1e:11:46:cd:76:0e:6f:4f:a4:21:71:0a:84:a7:2d:36:a9:48: + 22:51:7e:82 +-----BEGIN CERTIFICATE----- +MIIFAzCCA+ugAwIBAgIEUdNg7jANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMTQxMDIyMTcw +NTE0WhcNMjQxMDIzMDczMzIyWjCBujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eSAtIEwxSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANo/ltBNuS9E59s5XptQ7lylYdpBZ1MJqgCajld/KWvbx+EhJKo60I1HI9Ltchbw +kSHSXbe4S6iDj7eRMmjPziWTLLJ9l8j+wbQXugmeA5CTe3xJgyJoipveR8MxmHou +fUAL0u8+07KMqo9Iqf8A6ClYBve2k1qUcyYmrVgO5UK41epzeWRoUyW4hM+Ueq4G +RQyja03Qxr7qGKQ28JKyuhyIjzpSf/debYMcnfAf5cPW3aV4kj2wbSzqyc+UQRlx +RGi6RzwE6V26PvA19xW2nvIuFR4/R8jIOKdzRV1NsDuxjhcpN+rdBQEiu5Q2Ko1b +Nf5TGS8IRsEqsxpiHU4r2RsCAwEAAaOCAQkwggEFMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMECDAGAQH/AgEAMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0 +cDovL29jc3AuZW50cnVzdC5uZXQwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2Ny +bC5lbnRydXN0Lm5ldC9nMmNhLmNybDA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggr +BgEFBQcCARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwHQYDVR0OBBYEFIKi +cHTdvFM/z3vU981/p2DGCky/MB8GA1UdIwQYMBaAFGpyJnrQHu995ztpUdRsjZ+Q +EmarMA0GCSqGSIb3DQEBCwUAA4IBAQA/HBpb/0AiHY81DC2qmSerwBEycNc2KGml +jbEnmUK+xJPrSFdDcSPE5U6trkNvknbFGe/KvG9CTBaahqkEOMdl8PUM4ErfovrO +GhGonGkvG9/q4jLzzky8RgzAiYDRh2uiz2vUf/31YFJnV6Bt0WRBFG00Yu0GbCTy +BrwoAq8DLcIzBfvLqhboZRBD9Wlc44FYmc1r07jHexlVyUDOeVW4c4npXEBmQxJ/ +B7hlVtWNw6f1sbZlnsCDNn8WRTx0S5OKPPEr9TVwc3vnggSxGJgO1JxvGvz8pzOl +u7sY82t6XTKH920l5OJ2hiEeEUbNdg5vT6QhcQqEpy02qUgiUX6C +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert41[] = { + 0x30, 0x82, 0x05, 0x03, 0x30, 0x82, 0x03, 0xeb, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x51, 0xd3, 0x60, 0xee, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xbe, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x1f, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, + 0x61, 0x6c, 0x2d, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, + 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, + 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, + 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x32, 0x32, 0x31, 0x37, 0x30, + 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x31, 0x30, 0x32, 0x33, + 0x30, 0x37, 0x33, 0x33, 0x32, 0x32, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, + 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, + 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x32, + 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, + 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, + 0x20, 0x4c, 0x31, 0x4b, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xda, 0x3f, 0x96, 0xd0, 0x4d, 0xb9, 0x2f, 0x44, 0xe7, 0xdb, 0x39, + 0x5e, 0x9b, 0x50, 0xee, 0x5c, 0xa5, 0x61, 0xda, 0x41, 0x67, 0x53, 0x09, + 0xaa, 0x00, 0x9a, 0x8e, 0x57, 0x7f, 0x29, 0x6b, 0xdb, 0xc7, 0xe1, 0x21, + 0x24, 0xaa, 0x3a, 0xd0, 0x8d, 0x47, 0x23, 0xd2, 0xed, 0x72, 0x16, 0xf0, + 0x91, 0x21, 0xd2, 0x5d, 0xb7, 0xb8, 0x4b, 0xa8, 0x83, 0x8f, 0xb7, 0x91, + 0x32, 0x68, 0xcf, 0xce, 0x25, 0x93, 0x2c, 0xb2, 0x7d, 0x97, 0xc8, 0xfe, + 0xc1, 0xb4, 0x17, 0xba, 0x09, 0x9e, 0x03, 0x90, 0x93, 0x7b, 0x7c, 0x49, + 0x83, 0x22, 0x68, 0x8a, 0x9b, 0xde, 0x47, 0xc3, 0x31, 0x98, 0x7a, 0x2e, + 0x7d, 0x40, 0x0b, 0xd2, 0xef, 0x3e, 0xd3, 0xb2, 0x8c, 0xaa, 0x8f, 0x48, + 0xa9, 0xff, 0x00, 0xe8, 0x29, 0x58, 0x06, 0xf7, 0xb6, 0x93, 0x5a, 0x94, + 0x73, 0x26, 0x26, 0xad, 0x58, 0x0e, 0xe5, 0x42, 0xb8, 0xd5, 0xea, 0x73, + 0x79, 0x64, 0x68, 0x53, 0x25, 0xb8, 0x84, 0xcf, 0x94, 0x7a, 0xae, 0x06, + 0x45, 0x0c, 0xa3, 0x6b, 0x4d, 0xd0, 0xc6, 0xbe, 0xea, 0x18, 0xa4, 0x36, + 0xf0, 0x92, 0xb2, 0xba, 0x1c, 0x88, 0x8f, 0x3a, 0x52, 0x7f, 0xf7, 0x5e, + 0x6d, 0x83, 0x1c, 0x9d, 0xf0, 0x1f, 0xe5, 0xc3, 0xd6, 0xdd, 0xa5, 0x78, + 0x92, 0x3d, 0xb0, 0x6d, 0x2c, 0xea, 0xc9, 0xcf, 0x94, 0x41, 0x19, 0x71, + 0x44, 0x68, 0xba, 0x47, 0x3c, 0x04, 0xe9, 0x5d, 0xba, 0x3e, 0xf0, 0x35, + 0xf7, 0x15, 0xb6, 0x9e, 0xf2, 0x2e, 0x15, 0x1e, 0x3f, 0x47, 0xc8, 0xc8, + 0x38, 0xa7, 0x73, 0x45, 0x5d, 0x4d, 0xb0, 0x3b, 0xb1, 0x8e, 0x17, 0x29, + 0x37, 0xea, 0xdd, 0x05, 0x01, 0x22, 0xbb, 0x94, 0x36, 0x2a, 0x8d, 0x5b, + 0x35, 0xfe, 0x53, 0x19, 0x2f, 0x08, 0x46, 0xc1, 0x2a, 0xb3, 0x1a, 0x62, + 0x1d, 0x4e, 0x2b, 0xd9, 0x1b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x09, 0x30, 0x82, 0x01, 0x05, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, + 0x02, 0x01, 0x00, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x30, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, + 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x2f, 0x67, 0x32, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, + 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x82, 0xa2, + 0x70, 0x74, 0xdd, 0xbc, 0x53, 0x3f, 0xcf, 0x7b, 0xd4, 0xf7, 0xcd, 0x7f, + 0xa7, 0x60, 0xc6, 0x0a, 0x4c, 0xbf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, + 0x1e, 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, + 0x12, 0x66, 0xab, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3f, + 0x1c, 0x1a, 0x5b, 0xff, 0x40, 0x22, 0x1d, 0x8f, 0x35, 0x0c, 0x2d, 0xaa, + 0x99, 0x27, 0xab, 0xc0, 0x11, 0x32, 0x70, 0xd7, 0x36, 0x28, 0x69, 0xa5, + 0x8d, 0xb1, 0x27, 0x99, 0x42, 0xbe, 0xc4, 0x93, 0xeb, 0x48, 0x57, 0x43, + 0x71, 0x23, 0xc4, 0xe5, 0x4e, 0xad, 0xae, 0x43, 0x6f, 0x92, 0x76, 0xc5, + 0x19, 0xef, 0xca, 0xbc, 0x6f, 0x42, 0x4c, 0x16, 0x9a, 0x86, 0xa9, 0x04, + 0x38, 0xc7, 0x65, 0xf0, 0xf5, 0x0c, 0xe0, 0x4a, 0xdf, 0xa2, 0xfa, 0xce, + 0x1a, 0x11, 0xa8, 0x9c, 0x69, 0x2f, 0x1b, 0xdf, 0xea, 0xe2, 0x32, 0xf3, + 0xce, 0x4c, 0xbc, 0x46, 0x0c, 0xc0, 0x89, 0x80, 0xd1, 0x87, 0x6b, 0xa2, + 0xcf, 0x6b, 0xd4, 0x7f, 0xfd, 0xf5, 0x60, 0x52, 0x67, 0x57, 0xa0, 0x6d, + 0xd1, 0x64, 0x41, 0x14, 0x6d, 0x34, 0x62, 0xed, 0x06, 0x6c, 0x24, 0xf2, + 0x06, 0xbc, 0x28, 0x02, 0xaf, 0x03, 0x2d, 0xc2, 0x33, 0x05, 0xfb, 0xcb, + 0xaa, 0x16, 0xe8, 0x65, 0x10, 0x43, 0xf5, 0x69, 0x5c, 0xe3, 0x81, 0x58, + 0x99, 0xcd, 0x6b, 0xd3, 0xb8, 0xc7, 0x7b, 0x19, 0x55, 0xc9, 0x40, 0xce, + 0x79, 0x55, 0xb8, 0x73, 0x89, 0xe9, 0x5c, 0x40, 0x66, 0x43, 0x12, 0x7f, + 0x07, 0xb8, 0x65, 0x56, 0xd5, 0x8d, 0xc3, 0xa7, 0xf5, 0xb1, 0xb6, 0x65, + 0x9e, 0xc0, 0x83, 0x36, 0x7f, 0x16, 0x45, 0x3c, 0x74, 0x4b, 0x93, 0x8a, + 0x3c, 0xf1, 0x2b, 0xf5, 0x35, 0x70, 0x73, 0x7b, 0xe7, 0x82, 0x04, 0xb1, + 0x18, 0x98, 0x0e, 0xd4, 0x9c, 0x6f, 0x1a, 0xfc, 0xfc, 0xa7, 0x33, 0xa5, + 0xbb, 0xbb, 0x18, 0xf3, 0x6b, 0x7a, 0x5d, 0x32, 0x87, 0xf7, 0x6d, 0x25, + 0xe4, 0xe2, 0x76, 0x86, 0x21, 0x1e, 0x11, 0x46, 0xcd, 0x76, 0x0e, 0x6f, + 0x4f, 0xa4, 0x21, 0x71, 0x0a, 0x84, 0xa7, 0x2d, 0x36, 0xa9, 0x48, 0x22, + 0x51, 0x7e, 0x82, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 0e:e9:4c:c3:00:00:00:00:51:d3:77:85 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2 + Validity + Not Before: Oct 5 19:13:56 2015 GMT + Not After : Dec 5 19:43:56 2030 GMT + Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Certification Authority - L1K + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:3f:96:d0:4d:b9:2f:44:e7:db:39:5e:9b:50: + ee:5c:a5:61:da:41:67:53:09:aa:00:9a:8e:57:7f: + 29:6b:db:c7:e1:21:24:aa:3a:d0:8d:47:23:d2:ed: + 72:16:f0:91:21:d2:5d:b7:b8:4b:a8:83:8f:b7:91: + 32:68:cf:ce:25:93:2c:b2:7d:97:c8:fe:c1:b4:17: + ba:09:9e:03:90:93:7b:7c:49:83:22:68:8a:9b:de: + 47:c3:31:98:7a:2e:7d:40:0b:d2:ef:3e:d3:b2:8c: + aa:8f:48:a9:ff:00:e8:29:58:06:f7:b6:93:5a:94: + 73:26:26:ad:58:0e:e5:42:b8:d5:ea:73:79:64:68: + 53:25:b8:84:cf:94:7a:ae:06:45:0c:a3:6b:4d:d0: + c6:be:ea:18:a4:36:f0:92:b2:ba:1c:88:8f:3a:52: + 7f:f7:5e:6d:83:1c:9d:f0:1f:e5:c3:d6:dd:a5:78: + 92:3d:b0:6d:2c:ea:c9:cf:94:41:19:71:44:68:ba: + 47:3c:04:e9:5d:ba:3e:f0:35:f7:15:b6:9e:f2:2e: + 15:1e:3f:47:c8:c8:38:a7:73:45:5d:4d:b0:3b:b1: + 8e:17:29:37:ea:dd:05:01:22:bb:94:36:2a:8d:5b: + 35:fe:53:19:2f:08:46:c1:2a:b3:1a:62:1d:4e:2b: + d9:1b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.entrust.net/g2ca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/rpa + + X509v3 Subject Key Identifier: + 82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF + X509v3 Authority Key Identifier: + keyid:6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB + + Signature Algorithm: sha256WithRSAEncryption + 39:d5:8e:98:83:61:c8:2c:63:d3:70:1d:19:30:cb:f6:09:ac: + cc:69:d5:c9:dc:37:41:f2:32:0f:ef:74:c3:58:f6:78:27:09: + 34:08:95:92:2f:d7:df:b8:a3:fd:0e:81:e9:a4:9c:d3:3f:4d: + 68:2b:15:31:0a:15:cc:52:04:93:e8:93:50:c3:d9:b1:e2:e1: + 68:b7:3a:09:74:f1:34:58:0a:3f:77:98:40:b8:e6:68:ff:5d: + e4:c8:46:c5:ec:81:d7:c9:82:18:5c:83:ce:71:d8:bc:bf:ac: + 99:02:93:db:94:98:84:d2:9c:a6:b5:fe:5c:bb:f0:4a:af:21: + ac:c2:3f:49:24:67:d6:2e:8e:cf:ac:cc:64:15:18:72:e5:6c: + 77:d3:52:a8:b9:dd:8d:ac:00:4a:35:19:d4:6f:73:a3:75:ef: + 6b:64:c3:e0:8d:83:12:a1:8a:e7:0e:86:4d:d8:b4:20:1b:be: + 6a:a5:8c:4b:68:66:e3:2b:c7:58:0b:fb:56:10:d4:91:fb:1d: + d3:31:58:10:8c:44:e3:75:7b:10:9d:b5:38:b1:f6:aa:ca:81: + 64:6c:e8:f2:e2:81:55:97:51:7f:e1:c2:27:50:a2:c9:3c:5b: + 00:43:f6:5b:b9:d5:a5:fc:ff:07:50:40:67:07:b0:55:f0:b7: + 7e:6e:2d:cc +-----BEGIN CERTIFICATE----- +MIIFDjCCA/agAwIBAgIMDulMwwAAAABR03eFMA0GCSqGSIb3DQEBCwUAMIG+MQsw +CQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2Vl +IHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMDkg +RW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIwMAYDVQQD +EylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjAeFw0x +NTEwMDUxOTEzNTZaFw0zMDEyMDUxOTQzNTZaMIG6MQswCQYDVQQGEwJVUzEWMBQG +A1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5l +dC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMTIgRW50cnVzdCwgSW5jLiAt +IGZvciBhdXRob3JpemVkIHVzZSBvbmx5MS4wLAYDVQQDEyVFbnRydXN0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gTDFLMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA2j+W0E25L0Tn2zlem1DuXKVh2kFnUwmqAJqOV38pa9vH4SEkqjrQ +jUcj0u1yFvCRIdJdt7hLqIOPt5EyaM/OJZMssn2XyP7BtBe6CZ4DkJN7fEmDImiK +m95HwzGYei59QAvS7z7Tsoyqj0ip/wDoKVgG97aTWpRzJiatWA7lQrjV6nN5ZGhT +JbiEz5R6rgZFDKNrTdDGvuoYpDbwkrK6HIiPOlJ/915tgxyd8B/lw9bdpXiSPbBt +LOrJz5RBGXFEaLpHPATpXbo+8DX3Fbae8i4VHj9HyMg4p3NFXU2wO7GOFyk36t0F +ASK7lDYqjVs1/lMZLwhGwSqzGmIdTivZGwIDAQABo4IBDDCCAQgwDgYDVR0PAQH/ +BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwMwYIKwYBBQUHAQEEJzAlMCMGCCsG +AQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAwBgNVHR8EKTAnMCWgI6Ah +hh9odHRwOi8vY3JsLmVudHJ1c3QubmV0L2cyY2EuY3JsMDsGA1UdIAQ0MDIwMAYE +VR0gADAoMCYGCCsGAQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAd +BgNVHQ4EFgQUgqJwdN28Uz/Pe9T3zX+nYMYKTL8wHwYDVR0jBBgwFoAUanImetAe +733nO2lR1GyNn5ASZqswDQYJKoZIhvcNAQELBQADggEBADnVjpiDYcgsY9NwHRkw +y/YJrMxp1cncN0HyMg/vdMNY9ngnCTQIlZIv19+4o/0OgemknNM/TWgrFTEKFcxS +BJPok1DD2bHi4Wi3Ogl08TRYCj93mEC45mj/XeTIRsXsgdfJghhcg85x2Ly/rJkC +k9uUmITSnKa1/ly78EqvIazCP0kkZ9Yujs+szGQVGHLlbHfTUqi53Y2sAEo1GdRv +c6N172tkw+CNgxKhiucOhk3YtCAbvmqljEtoZuMrx1gL+1YQ1JH7HdMxWBCMRON1 +exCdtTix9qrKgWRs6PLigVWXUX/hwidQosk8WwBD9lu51aX8/wdQQGcHsFXwt35u +Lcw= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert42[] = { + 0x30, 0x82, 0x05, 0x0e, 0x30, 0x82, 0x03, 0xf6, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x0c, 0x0e, 0xe9, 0x4c, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x51, + 0xd3, 0x77, 0x85, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0xbe, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28, + 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65, + 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74, + 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, + 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, + 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x35, 0x31, 0x30, 0x30, 0x35, 0x31, 0x39, 0x31, 0x33, 0x35, 0x36, 0x5a, + 0x17, 0x0d, 0x33, 0x30, 0x31, 0x32, 0x30, 0x35, 0x31, 0x39, 0x34, 0x33, + 0x35, 0x36, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77, + 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74, 0x65, 0x72, 0x6d, + 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, + 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x32, 0x20, 0x45, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x45, + 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x4c, 0x31, 0x4b, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0x3f, 0x96, + 0xd0, 0x4d, 0xb9, 0x2f, 0x44, 0xe7, 0xdb, 0x39, 0x5e, 0x9b, 0x50, 0xee, + 0x5c, 0xa5, 0x61, 0xda, 0x41, 0x67, 0x53, 0x09, 0xaa, 0x00, 0x9a, 0x8e, + 0x57, 0x7f, 0x29, 0x6b, 0xdb, 0xc7, 0xe1, 0x21, 0x24, 0xaa, 0x3a, 0xd0, + 0x8d, 0x47, 0x23, 0xd2, 0xed, 0x72, 0x16, 0xf0, 0x91, 0x21, 0xd2, 0x5d, + 0xb7, 0xb8, 0x4b, 0xa8, 0x83, 0x8f, 0xb7, 0x91, 0x32, 0x68, 0xcf, 0xce, + 0x25, 0x93, 0x2c, 0xb2, 0x7d, 0x97, 0xc8, 0xfe, 0xc1, 0xb4, 0x17, 0xba, + 0x09, 0x9e, 0x03, 0x90, 0x93, 0x7b, 0x7c, 0x49, 0x83, 0x22, 0x68, 0x8a, + 0x9b, 0xde, 0x47, 0xc3, 0x31, 0x98, 0x7a, 0x2e, 0x7d, 0x40, 0x0b, 0xd2, + 0xef, 0x3e, 0xd3, 0xb2, 0x8c, 0xaa, 0x8f, 0x48, 0xa9, 0xff, 0x00, 0xe8, + 0x29, 0x58, 0x06, 0xf7, 0xb6, 0x93, 0x5a, 0x94, 0x73, 0x26, 0x26, 0xad, + 0x58, 0x0e, 0xe5, 0x42, 0xb8, 0xd5, 0xea, 0x73, 0x79, 0x64, 0x68, 0x53, + 0x25, 0xb8, 0x84, 0xcf, 0x94, 0x7a, 0xae, 0x06, 0x45, 0x0c, 0xa3, 0x6b, + 0x4d, 0xd0, 0xc6, 0xbe, 0xea, 0x18, 0xa4, 0x36, 0xf0, 0x92, 0xb2, 0xba, + 0x1c, 0x88, 0x8f, 0x3a, 0x52, 0x7f, 0xf7, 0x5e, 0x6d, 0x83, 0x1c, 0x9d, + 0xf0, 0x1f, 0xe5, 0xc3, 0xd6, 0xdd, 0xa5, 0x78, 0x92, 0x3d, 0xb0, 0x6d, + 0x2c, 0xea, 0xc9, 0xcf, 0x94, 0x41, 0x19, 0x71, 0x44, 0x68, 0xba, 0x47, + 0x3c, 0x04, 0xe9, 0x5d, 0xba, 0x3e, 0xf0, 0x35, 0xf7, 0x15, 0xb6, 0x9e, + 0xf2, 0x2e, 0x15, 0x1e, 0x3f, 0x47, 0xc8, 0xc8, 0x38, 0xa7, 0x73, 0x45, + 0x5d, 0x4d, 0xb0, 0x3b, 0xb1, 0x8e, 0x17, 0x29, 0x37, 0xea, 0xdd, 0x05, + 0x01, 0x22, 0xbb, 0x94, 0x36, 0x2a, 0x8d, 0x5b, 0x35, 0xfe, 0x53, 0x19, + 0x2f, 0x08, 0x46, 0xc1, 0x2a, 0xb3, 0x1a, 0x62, 0x1d, 0x4e, 0x2b, 0xd9, + 0x1b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x0c, 0x30, 0x82, + 0x01, 0x08, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x30, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, + 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, + 0x2f, 0x67, 0x32, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, + 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, + 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x82, 0xa2, 0x70, + 0x74, 0xdd, 0xbc, 0x53, 0x3f, 0xcf, 0x7b, 0xd4, 0xf7, 0xcd, 0x7f, 0xa7, + 0x60, 0xc6, 0x0a, 0x4c, 0xbf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, + 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, 0x1e, + 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, 0x12, + 0x66, 0xab, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x39, 0xd5, + 0x8e, 0x98, 0x83, 0x61, 0xc8, 0x2c, 0x63, 0xd3, 0x70, 0x1d, 0x19, 0x30, + 0xcb, 0xf6, 0x09, 0xac, 0xcc, 0x69, 0xd5, 0xc9, 0xdc, 0x37, 0x41, 0xf2, + 0x32, 0x0f, 0xef, 0x74, 0xc3, 0x58, 0xf6, 0x78, 0x27, 0x09, 0x34, 0x08, + 0x95, 0x92, 0x2f, 0xd7, 0xdf, 0xb8, 0xa3, 0xfd, 0x0e, 0x81, 0xe9, 0xa4, + 0x9c, 0xd3, 0x3f, 0x4d, 0x68, 0x2b, 0x15, 0x31, 0x0a, 0x15, 0xcc, 0x52, + 0x04, 0x93, 0xe8, 0x93, 0x50, 0xc3, 0xd9, 0xb1, 0xe2, 0xe1, 0x68, 0xb7, + 0x3a, 0x09, 0x74, 0xf1, 0x34, 0x58, 0x0a, 0x3f, 0x77, 0x98, 0x40, 0xb8, + 0xe6, 0x68, 0xff, 0x5d, 0xe4, 0xc8, 0x46, 0xc5, 0xec, 0x81, 0xd7, 0xc9, + 0x82, 0x18, 0x5c, 0x83, 0xce, 0x71, 0xd8, 0xbc, 0xbf, 0xac, 0x99, 0x02, + 0x93, 0xdb, 0x94, 0x98, 0x84, 0xd2, 0x9c, 0xa6, 0xb5, 0xfe, 0x5c, 0xbb, + 0xf0, 0x4a, 0xaf, 0x21, 0xac, 0xc2, 0x3f, 0x49, 0x24, 0x67, 0xd6, 0x2e, + 0x8e, 0xcf, 0xac, 0xcc, 0x64, 0x15, 0x18, 0x72, 0xe5, 0x6c, 0x77, 0xd3, + 0x52, 0xa8, 0xb9, 0xdd, 0x8d, 0xac, 0x00, 0x4a, 0x35, 0x19, 0xd4, 0x6f, + 0x73, 0xa3, 0x75, 0xef, 0x6b, 0x64, 0xc3, 0xe0, 0x8d, 0x83, 0x12, 0xa1, + 0x8a, 0xe7, 0x0e, 0x86, 0x4d, 0xd8, 0xb4, 0x20, 0x1b, 0xbe, 0x6a, 0xa5, + 0x8c, 0x4b, 0x68, 0x66, 0xe3, 0x2b, 0xc7, 0x58, 0x0b, 0xfb, 0x56, 0x10, + 0xd4, 0x91, 0xfb, 0x1d, 0xd3, 0x31, 0x58, 0x10, 0x8c, 0x44, 0xe3, 0x75, + 0x7b, 0x10, 0x9d, 0xb5, 0x38, 0xb1, 0xf6, 0xaa, 0xca, 0x81, 0x64, 0x6c, + 0xe8, 0xf2, 0xe2, 0x81, 0x55, 0x97, 0x51, 0x7f, 0xe1, 0xc2, 0x27, 0x50, + 0xa2, 0xc9, 0x3c, 0x5b, 0x00, 0x43, 0xf6, 0x5b, 0xb9, 0xd5, 0xa5, 0xfc, + 0xff, 0x07, 0x50, 0x40, 0x67, 0x07, 0xb0, 0x55, 0xf0, 0xb7, 0x7e, 0x6e, + 0x2d, 0xcc, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120038507 (0x727a46b) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Validity + Not Before: Apr 2 14:36:10 2014 GMT + Not After : Apr 2 14:35:52 2021 GMT + Subject: C=NL, L=Amsterdam, O=Verizon Enterprise Solutions, OU=Cybertrust, CN=Verizon Akamai SureServer CA G14-SHA2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:dd:6e:9e:02:69:02:b5:a3:99:2e:08:64:32:6a: + 59:f3:c6:9e:a6:20:07:d2:48:d1:a8:93:c7:ea:47: + 8f:83:39:40:d7:20:5d:8d:9a:ba:ab:d8:70:ec:9d: + 88:d1:bd:62:f6:db:ec:9d:5e:35:01:76:03:23:e5: + 6f:d2:af:46:35:59:5a:5c:d1:a8:23:c1:eb:e9:20: + d4:49:d6:3f:00:d8:a8:22:de:43:79:81:ac:e9:a4: + 92:f5:77:70:05:1e:5c:b6:a0:f7:90:a4:cd:ab:28: + 2c:90:c2:e7:0f:c3:af:1c:47:59:d5:84:2e:df:26: + 07:45:23:5a:c6:e8:90:c8:85:4b:8c:16:1e:60:f9: + 01:13:f1:14:1f:e6:e8:14:ed:c5:d2:6f:63:28:6e: + 72:8c:49:ae:08:72:c7:93:95:b4:0b:0c:ae:8f:9a: + 67:84:f5:57:1b:db:81:d7:17:9d:41:11:43:19:bd: + 6d:4a:85:ed:8f:70:25:ab:66:ab:f6:fa:6d:1c:3c: + ab:ed:17:bd:56:84:e1:db:75:33:b2:28:4b:99:8e: + f9:4b:82:33:50:9f:92:53:ed:fa:ad:0f:95:9c:a3: + f2:cb:60:f0:77:1d:c9:01:8b:5f:2d:86:be:bf:36: + b8:24:96:13:7c:c1:86:5a:6c:c1:48:2a:7f:3e:93: + 60:c5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:2 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6334.1.50 + CPS: https://secure.omniroot.com/repository + + Authority Information Access: + OCSP - URI:http://ocsp.omniroot.com/baltimoreroot + CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.crt + CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.der + + X509v3 Key Usage: critical + Digital Signature, Non Repudiation, Certificate Sign, CRL Sign + X509v3 Authority Key Identifier: + keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl + + X509v3 Subject Key Identifier: + F8:BD:FA:AF:73:77:C6:C7:1B:F9:4B:4D:11:A7:D1:33:AF:AF:72:11 + Signature Algorithm: sha256WithRSAEncryption + 80:d9:7a:ed:72:05:37:8f:61:aa:73:7c:9a:6a:fc:fe:01:e2: + 19:81:70:07:25:32:b0:f0:6f:3b:c7:6a:28:3d:e4:51:87:e6: + 7e:82:ec:ae:48:a7:b1:77:38:c2:d6:56:af:8f:f2:01:fc:65: + 65:10:09:f7:74:29:b5:0e:92:ee:90:98:d1:88:a2:65:b7:cd: + 9c:0e:a7:86:98:28:bc:ae:15:83:b6:1a:d7:1d:ec:19:da:7a: + 8e:40:f9:99:15:d5:7d:a5:ba:ab:fd:26:98:6e:9c:41:3b:b6: + 81:18:ec:70:48:d7:6e:7f:a6:e1:77:25:d6:dd:62:e8:52:f3: + 8c:16:39:67:e2:22:0d:77:2e:fb:11:6c:e4:dd:38:b4:27:5f: + 03:a8:3d:44:e2:f2:84:4b:84:fd:56:a6:9e:4d:7b:a2:16:4f: + 07:f5:34:24:72:a5:a2:fa:16:66:2a:a4:4a:0e:c8:0d:27:44: + 9c:77:d4:12:10:87:d2:00:2c:7a:bb:8e:88:22:91:15:be:a2: + 59:ca:34:e0:1c:61:94:86:20:33:cd:e7:4c:5d:3b:92:3e:cb: + d6:2d:ea:54:fa:fb:af:54:f5:a8:c5:0b:ca:8b:87:00:e6:9f: + e6:95:bf:b7:c4:a3:59:f5:16:6c:5f:3e:69:55:80:39:f6:75: + 50:14:3e:32 +-----BEGIN CERTIFICATE----- +MIIFHzCCBAegAwIBAgIEByekazANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDQwMjE0MzYxMFoX +DTIxMDQwMjE0MzU1MlowgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJk +YW0xJTAjBgNVBAoTHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNV +BAsTCkN5YmVydHJ1c3QxLjAsBgNVBAMTJVZlcml6b24gQWthbWFpIFN1cmVTZXJ2 +ZXIgQ0EgRzE0LVNIQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDd +bp4CaQK1o5kuCGQyalnzxp6mIAfSSNGok8fqR4+DOUDXIF2Nmrqr2HDsnYjRvWL2 +2+ydXjUBdgMj5W/Sr0Y1WVpc0agjwevpINRJ1j8A2Kgi3kN5gazppJL1d3AFHly2 +oPeQpM2rKCyQwucPw68cR1nVhC7fJgdFI1rG6JDIhUuMFh5g+QET8RQf5ugU7cXS +b2MobnKMSa4IcseTlbQLDK6PmmeE9Vcb24HXF51BEUMZvW1Khe2PcCWrZqv2+m0c +PKvtF71WhOHbdTOyKEuZjvlLgjNQn5JT7fqtD5Wco/LLYPB3HckBi18thr6/Nrgk +lhN8wYZabMFIKn8+k2DFAgMBAAGjggG3MIIBszASBgNVHRMBAf8ECDAGAQH/AgEC +MEwGA1UdIARFMEMwQQYJKwYBBAGxPgEyMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v +c2VjdXJlLm9tbmlyb290LmNvbS9yZXBvc2l0b3J5MIG6BggrBgEFBQcBAQSBrTCB +qjAyBggrBgEFBQcwAYYmaHR0cDovL29jc3Aub21uaXJvb3QuY29tL2JhbHRpbW9y +ZXJvb3QwOQYIKwYBBQUHMAKGLWh0dHBzOi8vY2FjZXJ0Lm9tbmlyb290LmNvbS9i +YWx0aW1vcmVyb290LmNydDA5BggrBgEFBQcwAoYtaHR0cHM6Ly9jYWNlcnQub21u +aXJvb3QuY29tL2JhbHRpbW9yZXJvb3QuZGVyMA4GA1UdDwEB/wQEAwIBxjAfBgNV +HSMEGDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DBCBgNVHR8EOzA5MDegNaAzhjFo +dHRwOi8vY2RwMS5wdWJsaWMtdHJ1c3QuY29tL0NSTC9PbW5pcm9vdDIwMjUuY3Js +MB0GA1UdDgQWBBT4vfqvc3fGxxv5S00Rp9Ezr69yETANBgkqhkiG9w0BAQsFAAOC +AQEAgNl67XIFN49hqnN8mmr8/gHiGYFwByUysPBvO8dqKD3kUYfmfoLsrkinsXc4 +wtZWr4/yAfxlZRAJ93QptQ6S7pCY0YiiZbfNnA6nhpgovK4Vg7Ya1x3sGdp6jkD5 +mRXVfaW6q/0mmG6cQTu2gRjscEjXbn+m4Xcl1t1i6FLzjBY5Z+IiDXcu+xFs5N04 +tCdfA6g9ROLyhEuE/Vamnk17ohZPB/U0JHKlovoWZiqkSg7IDSdEnHfUEhCH0gAs +eruOiCKRFb6iWco04BxhlIYgM83nTF07kj7L1i3qVPr7r1T1qMULyouHAOaf5pW/ +t8SjWfUWbF8+aVWAOfZ1UBQ+Mg== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert43[] = { + 0x30, 0x82, 0x05, 0x1f, 0x30, 0x82, 0x04, 0x07, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0xa4, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, + 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x36, 0x31, 0x30, 0x5a, 0x17, + 0x0d, 0x32, 0x31, 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x35, 0x35, + 0x32, 0x5a, 0x30, 0x81, 0x8d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x13, 0x09, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64, + 0x61, 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x1c, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x45, 0x6e, 0x74, + 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x20, 0x53, 0x6f, 0x6c, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x25, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x41, 0x6b, 0x61, + 0x6d, 0x61, 0x69, 0x20, 0x53, 0x75, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x47, 0x31, 0x34, 0x2d, 0x53, 0x48, + 0x41, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdd, + 0x6e, 0x9e, 0x02, 0x69, 0x02, 0xb5, 0xa3, 0x99, 0x2e, 0x08, 0x64, 0x32, + 0x6a, 0x59, 0xf3, 0xc6, 0x9e, 0xa6, 0x20, 0x07, 0xd2, 0x48, 0xd1, 0xa8, + 0x93, 0xc7, 0xea, 0x47, 0x8f, 0x83, 0x39, 0x40, 0xd7, 0x20, 0x5d, 0x8d, + 0x9a, 0xba, 0xab, 0xd8, 0x70, 0xec, 0x9d, 0x88, 0xd1, 0xbd, 0x62, 0xf6, + 0xdb, 0xec, 0x9d, 0x5e, 0x35, 0x01, 0x76, 0x03, 0x23, 0xe5, 0x6f, 0xd2, + 0xaf, 0x46, 0x35, 0x59, 0x5a, 0x5c, 0xd1, 0xa8, 0x23, 0xc1, 0xeb, 0xe9, + 0x20, 0xd4, 0x49, 0xd6, 0x3f, 0x00, 0xd8, 0xa8, 0x22, 0xde, 0x43, 0x79, + 0x81, 0xac, 0xe9, 0xa4, 0x92, 0xf5, 0x77, 0x70, 0x05, 0x1e, 0x5c, 0xb6, + 0xa0, 0xf7, 0x90, 0xa4, 0xcd, 0xab, 0x28, 0x2c, 0x90, 0xc2, 0xe7, 0x0f, + 0xc3, 0xaf, 0x1c, 0x47, 0x59, 0xd5, 0x84, 0x2e, 0xdf, 0x26, 0x07, 0x45, + 0x23, 0x5a, 0xc6, 0xe8, 0x90, 0xc8, 0x85, 0x4b, 0x8c, 0x16, 0x1e, 0x60, + 0xf9, 0x01, 0x13, 0xf1, 0x14, 0x1f, 0xe6, 0xe8, 0x14, 0xed, 0xc5, 0xd2, + 0x6f, 0x63, 0x28, 0x6e, 0x72, 0x8c, 0x49, 0xae, 0x08, 0x72, 0xc7, 0x93, + 0x95, 0xb4, 0x0b, 0x0c, 0xae, 0x8f, 0x9a, 0x67, 0x84, 0xf5, 0x57, 0x1b, + 0xdb, 0x81, 0xd7, 0x17, 0x9d, 0x41, 0x11, 0x43, 0x19, 0xbd, 0x6d, 0x4a, + 0x85, 0xed, 0x8f, 0x70, 0x25, 0xab, 0x66, 0xab, 0xf6, 0xfa, 0x6d, 0x1c, + 0x3c, 0xab, 0xed, 0x17, 0xbd, 0x56, 0x84, 0xe1, 0xdb, 0x75, 0x33, 0xb2, + 0x28, 0x4b, 0x99, 0x8e, 0xf9, 0x4b, 0x82, 0x33, 0x50, 0x9f, 0x92, 0x53, + 0xed, 0xfa, 0xad, 0x0f, 0x95, 0x9c, 0xa3, 0xf2, 0xcb, 0x60, 0xf0, 0x77, + 0x1d, 0xc9, 0x01, 0x8b, 0x5f, 0x2d, 0x86, 0xbe, 0xbf, 0x36, 0xb8, 0x24, + 0x96, 0x13, 0x7c, 0xc1, 0x86, 0x5a, 0x6c, 0xc1, 0x48, 0x2a, 0x7f, 0x3e, + 0x93, 0x60, 0xc5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xb7, + 0x30, 0x82, 0x01, 0xb3, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02, + 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, + 0x41, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x32, + 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, + 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x81, 0xba, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xad, 0x30, 0x81, + 0xaa, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x01, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, + 0x73, 0x70, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, + 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d, + 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, + 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, + 0x2e, 0x63, 0x72, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, + 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, + 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x2e, + 0x64, 0x65, 0x72, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0xc6, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, + 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, + 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, + 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf8, + 0xbd, 0xfa, 0xaf, 0x73, 0x77, 0xc6, 0xc7, 0x1b, 0xf9, 0x4b, 0x4d, 0x11, + 0xa7, 0xd1, 0x33, 0xaf, 0xaf, 0x72, 0x11, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x01, 0x00, 0x80, 0xd9, 0x7a, 0xed, 0x72, 0x05, 0x37, 0x8f, 0x61, + 0xaa, 0x73, 0x7c, 0x9a, 0x6a, 0xfc, 0xfe, 0x01, 0xe2, 0x19, 0x81, 0x70, + 0x07, 0x25, 0x32, 0xb0, 0xf0, 0x6f, 0x3b, 0xc7, 0x6a, 0x28, 0x3d, 0xe4, + 0x51, 0x87, 0xe6, 0x7e, 0x82, 0xec, 0xae, 0x48, 0xa7, 0xb1, 0x77, 0x38, + 0xc2, 0xd6, 0x56, 0xaf, 0x8f, 0xf2, 0x01, 0xfc, 0x65, 0x65, 0x10, 0x09, + 0xf7, 0x74, 0x29, 0xb5, 0x0e, 0x92, 0xee, 0x90, 0x98, 0xd1, 0x88, 0xa2, + 0x65, 0xb7, 0xcd, 0x9c, 0x0e, 0xa7, 0x86, 0x98, 0x28, 0xbc, 0xae, 0x15, + 0x83, 0xb6, 0x1a, 0xd7, 0x1d, 0xec, 0x19, 0xda, 0x7a, 0x8e, 0x40, 0xf9, + 0x99, 0x15, 0xd5, 0x7d, 0xa5, 0xba, 0xab, 0xfd, 0x26, 0x98, 0x6e, 0x9c, + 0x41, 0x3b, 0xb6, 0x81, 0x18, 0xec, 0x70, 0x48, 0xd7, 0x6e, 0x7f, 0xa6, + 0xe1, 0x77, 0x25, 0xd6, 0xdd, 0x62, 0xe8, 0x52, 0xf3, 0x8c, 0x16, 0x39, + 0x67, 0xe2, 0x22, 0x0d, 0x77, 0x2e, 0xfb, 0x11, 0x6c, 0xe4, 0xdd, 0x38, + 0xb4, 0x27, 0x5f, 0x03, 0xa8, 0x3d, 0x44, 0xe2, 0xf2, 0x84, 0x4b, 0x84, + 0xfd, 0x56, 0xa6, 0x9e, 0x4d, 0x7b, 0xa2, 0x16, 0x4f, 0x07, 0xf5, 0x34, + 0x24, 0x72, 0xa5, 0xa2, 0xfa, 0x16, 0x66, 0x2a, 0xa4, 0x4a, 0x0e, 0xc8, + 0x0d, 0x27, 0x44, 0x9c, 0x77, 0xd4, 0x12, 0x10, 0x87, 0xd2, 0x00, 0x2c, + 0x7a, 0xbb, 0x8e, 0x88, 0x22, 0x91, 0x15, 0xbe, 0xa2, 0x59, 0xca, 0x34, + 0xe0, 0x1c, 0x61, 0x94, 0x86, 0x20, 0x33, 0xcd, 0xe7, 0x4c, 0x5d, 0x3b, + 0x92, 0x3e, 0xcb, 0xd6, 0x2d, 0xea, 0x54, 0xfa, 0xfb, 0xaf, 0x54, 0xf5, + 0xa8, 0xc5, 0x0b, 0xca, 0x8b, 0x87, 0x00, 0xe6, 0x9f, 0xe6, 0x95, 0xbf, + 0xb7, 0xc4, 0xa3, 0x59, 0xf5, 0x16, 0x6c, 0x5f, 0x3e, 0x69, 0x55, 0x80, + 0x39, 0xf6, 0x75, 0x50, 0x14, 0x3e, 0x32, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 7e:e1:4a:6f:6f:ef:f2:d3:7f:3f:ad:65:4d:3a:da:b4 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 EV SSL CA - G3 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d8:a1:65:74:23:e8:2b:64:e2:32:d7:33:37:3d: + 8e:f5:34:16:48:dd:4f:7f:87:1c:f8:44:23:13:8e: + fb:11:d8:44:5a:18:71:8e:60:16:26:92:9b:fd:17: + 0b:e1:71:70:42:fe:bf:fa:1c:c0:aa:a3:a7:b5:71: + e8:ff:18:83:f6:df:10:0a:13:62:c8:3d:9c:a7:de: + 2e:3f:0c:d9:1d:e7:2e:fb:2a:ce:c8:9a:7f:87:bf: + d8:4c:04:15:32:c9:d1:cc:95:71:a0:4e:28:4f:84: + d9:35:fb:e3:86:6f:94:53:e6:72:8a:63:67:2e:be: + 69:f6:f7:6e:8e:9c:60:04:eb:29:fa:c4:47:42:d2: + 78:98:e3:ec:0b:a5:92:dc:b7:9a:bd:80:64:2b:38: + 7c:38:09:5b:66:f6:2d:95:7a:86:b2:34:2e:85:9e: + 90:0e:5f:b7:5d:a4:51:72:46:70:13:bf:67:f2:b6: + a7:4d:14:1e:6c:b9:53:ee:23:1a:4e:8d:48:55:43: + 41:b1:89:75:6a:40:28:c5:7d:dd:d2:6e:d2:02:19: + 2f:7b:24:94:4b:eb:f1:1a:a9:9b:e3:23:9a:ea:fa: + 33:ab:0a:2c:b7:f4:60:08:dd:9f:1c:cd:dd:2d:01: + 66:80:af:b3:2f:29:1d:23:b8:8a:e1:a1:70:07:0c: + 34:0f + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://s2.symcb.com + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.symauth.com/cps + User Notice: + Explicit Text: http://www.symauth.com/rpa + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://s1.symcb.com/pca3-g5.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-533 + X509v3 Subject Key Identifier: + 01:59:AB:E7:DD:3A:0B:59:A6:64:63:D6:CF:20:07:57:D5:91:E7:6A + X509v3 Authority Key Identifier: + keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + + Signature Algorithm: sha256WithRSAEncryption + 42:01:55:7b:d0:16:1a:5d:58:e8:bb:9b:a8:4d:d7:f3:d7:eb: + 13:94:86:d6:7f:21:0b:47:bc:57:9b:92:5d:4f:05:9f:38:a4: + 10:7c:cf:83:be:06:43:46:8d:08:bc:6a:d7:10:a6:fa:ab:af: + 2f:61:a8:63:f2:65:df:7f:4c:88:12:88:4f:b3:69:d9:ff:27: + c0:0a:97:91:8f:56:fb:89:c4:a8:bb:92:2d:1b:73:b0:c6:ab: + 36:f4:96:6c:20:08:ef:0a:1e:66:24:45:4f:67:00:40:c8:07: + 54:74:33:3b:a6:ad:bb:23:9f:66:ed:a2:44:70:34:fb:0e:ea: + 01:fd:cf:78:74:df:a7:ad:55:b7:5f:4d:f6:d6:3f:e0:86:ce: + 24:c7:42:a9:13:14:44:35:4b:b6:df:c9:60:ac:0c:7f:d9:93: + 21:4b:ee:9c:e4:49:02:98:d3:60:7b:5c:bc:d5:30:2f:07:ce: + 44:42:c4:0b:99:fe:e6:9f:fc:b0:78:86:51:6d:d1:2c:9d:c6: + 96:fb:85:82:bb:04:2f:f7:62:80:ef:62:da:7f:f6:0e:ac:90: + b8:56:bd:79:3f:f2:80:6e:a3:d9:b9:0f:5d:3a:07:1d:91:93: + 86:4b:29:4c:e1:dc:b5:e1:e0:33:9d:b3:cb:36:91:4b:fe:a1: + b4:ee:f0:f9 +-----BEGIN CERTIFICATE----- +MIIFKzCCBBOgAwIBAgIQfuFKb2/v8tN/P61lTTratDANBgkqhkiG9w0BAQsFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB3MQsw +CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV +BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIENs +YXNzIDMgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDYoWV0I+grZOIy1zM3PY71NBZI3U9/hxz4RCMTjvsR2ERaGHGOYBYmkpv9 +FwvhcXBC/r/6HMCqo6e1cej/GIP23xAKE2LIPZyn3i4/DNkd5y77Ks7Imn+Hv9hM +BBUyydHMlXGgTihPhNk1++OGb5RT5nKKY2cuvmn2926OnGAE6yn6xEdC0niY4+wL +pZLct5q9gGQrOHw4CVtm9i2VeoayNC6FnpAOX7ddpFFyRnATv2fytqdNFB5suVPu +IxpOjUhVQ0GxiXVqQCjFfd3SbtICGS97JJRL6/EaqZvjI5rq+jOrCiy39GAI3Z8c +zd0tAWaAr7MvKR0juIrhoXAHDDQPAgMBAAGjggFdMIIBWTAvBggrBgEFBQcBAQQj +MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wEgYDVR0TAQH/BAgw +BgEB/wIBADBlBgNVHSAEXjBcMFoGBFUdIAAwUjAmBggrBgEFBQcCARYaaHR0cDov +L3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIwHBoaaHR0cDovL3d3dy5z +eW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3MxLnN5bWNi +LmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx +GjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTMzMB0GA1UdDgQWBBQBWavn3ToLWaZk +Y9bPIAdX1ZHnajAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq +hkiG9w0BAQsFAAOCAQEAQgFVe9AWGl1Y6LubqE3X89frE5SG1n8hC0e8V5uSXU8F +nzikEHzPg74GQ0aNCLxq1xCm+quvL2GoY/Jl339MiBKIT7Np2f8nwAqXkY9W+4nE +qLuSLRtzsMarNvSWbCAI7woeZiRFT2cAQMgHVHQzO6atuyOfZu2iRHA0+w7qAf3P +eHTfp61Vt19N9tY/4IbOJMdCqRMURDVLtt/JYKwMf9mTIUvunORJApjTYHtcvNUw +LwfORELEC5n+5p/8sHiGUW3RLJ3GlvuFgrsEL/digO9i2n/2DqyQuFa9eT/ygG6j +2bkPXToHHZGThkspTOHcteHgM52zyzaRS/6htO7w+Q== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert44[] = { + 0x30, 0x82, 0x05, 0x2b, 0x30, 0x82, 0x04, 0x13, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x7e, 0xe1, 0x4a, 0x6f, 0x6f, 0xef, 0xf2, 0xd3, 0x7f, + 0x3f, 0xad, 0x65, 0x4d, 0x3a, 0xda, 0xb4, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x77, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d, + 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x1f, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c, + 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xd8, 0xa1, 0x65, 0x74, 0x23, 0xe8, 0x2b, + 0x64, 0xe2, 0x32, 0xd7, 0x33, 0x37, 0x3d, 0x8e, 0xf5, 0x34, 0x16, 0x48, + 0xdd, 0x4f, 0x7f, 0x87, 0x1c, 0xf8, 0x44, 0x23, 0x13, 0x8e, 0xfb, 0x11, + 0xd8, 0x44, 0x5a, 0x18, 0x71, 0x8e, 0x60, 0x16, 0x26, 0x92, 0x9b, 0xfd, + 0x17, 0x0b, 0xe1, 0x71, 0x70, 0x42, 0xfe, 0xbf, 0xfa, 0x1c, 0xc0, 0xaa, + 0xa3, 0xa7, 0xb5, 0x71, 0xe8, 0xff, 0x18, 0x83, 0xf6, 0xdf, 0x10, 0x0a, + 0x13, 0x62, 0xc8, 0x3d, 0x9c, 0xa7, 0xde, 0x2e, 0x3f, 0x0c, 0xd9, 0x1d, + 0xe7, 0x2e, 0xfb, 0x2a, 0xce, 0xc8, 0x9a, 0x7f, 0x87, 0xbf, 0xd8, 0x4c, + 0x04, 0x15, 0x32, 0xc9, 0xd1, 0xcc, 0x95, 0x71, 0xa0, 0x4e, 0x28, 0x4f, + 0x84, 0xd9, 0x35, 0xfb, 0xe3, 0x86, 0x6f, 0x94, 0x53, 0xe6, 0x72, 0x8a, + 0x63, 0x67, 0x2e, 0xbe, 0x69, 0xf6, 0xf7, 0x6e, 0x8e, 0x9c, 0x60, 0x04, + 0xeb, 0x29, 0xfa, 0xc4, 0x47, 0x42, 0xd2, 0x78, 0x98, 0xe3, 0xec, 0x0b, + 0xa5, 0x92, 0xdc, 0xb7, 0x9a, 0xbd, 0x80, 0x64, 0x2b, 0x38, 0x7c, 0x38, + 0x09, 0x5b, 0x66, 0xf6, 0x2d, 0x95, 0x7a, 0x86, 0xb2, 0x34, 0x2e, 0x85, + 0x9e, 0x90, 0x0e, 0x5f, 0xb7, 0x5d, 0xa4, 0x51, 0x72, 0x46, 0x70, 0x13, + 0xbf, 0x67, 0xf2, 0xb6, 0xa7, 0x4d, 0x14, 0x1e, 0x6c, 0xb9, 0x53, 0xee, + 0x23, 0x1a, 0x4e, 0x8d, 0x48, 0x55, 0x43, 0x41, 0xb1, 0x89, 0x75, 0x6a, + 0x40, 0x28, 0xc5, 0x7d, 0xdd, 0xd2, 0x6e, 0xd2, 0x02, 0x19, 0x2f, 0x7b, + 0x24, 0x94, 0x4b, 0xeb, 0xf1, 0x1a, 0xa9, 0x9b, 0xe3, 0x23, 0x9a, 0xea, + 0xfa, 0x33, 0xab, 0x0a, 0x2c, 0xb7, 0xf4, 0x60, 0x08, 0xdd, 0x9f, 0x1c, + 0xcd, 0xdd, 0x2d, 0x01, 0x66, 0x80, 0xaf, 0xb3, 0x2f, 0x29, 0x1d, 0x23, + 0xb8, 0x8a, 0xe1, 0xa1, 0x70, 0x07, 0x0c, 0x34, 0x0f, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5d, 0x30, 0x82, 0x01, 0x59, 0x30, 0x2f, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, + 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, + 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, + 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x65, 0x06, 0x03, 0x55, + 0x1d, 0x20, 0x04, 0x5e, 0x30, 0x5c, 0x30, 0x5a, 0x06, 0x04, 0x55, 0x1d, + 0x20, 0x00, 0x30, 0x52, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, + 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, + 0x70, 0x61, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, + 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, + 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29, 0x06, 0x03, + 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, + 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, + 0x35, 0x33, 0x33, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x01, 0x59, 0xab, 0xe7, 0xdd, 0x3a, 0x0b, 0x59, 0xa6, 0x64, + 0x63, 0xd6, 0xcf, 0x20, 0x07, 0x57, 0xd5, 0x91, 0xe7, 0x6a, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f, + 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43, + 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x01, 0x00, 0x42, 0x01, 0x55, 0x7b, 0xd0, 0x16, 0x1a, 0x5d, 0x58, + 0xe8, 0xbb, 0x9b, 0xa8, 0x4d, 0xd7, 0xf3, 0xd7, 0xeb, 0x13, 0x94, 0x86, + 0xd6, 0x7f, 0x21, 0x0b, 0x47, 0xbc, 0x57, 0x9b, 0x92, 0x5d, 0x4f, 0x05, + 0x9f, 0x38, 0xa4, 0x10, 0x7c, 0xcf, 0x83, 0xbe, 0x06, 0x43, 0x46, 0x8d, + 0x08, 0xbc, 0x6a, 0xd7, 0x10, 0xa6, 0xfa, 0xab, 0xaf, 0x2f, 0x61, 0xa8, + 0x63, 0xf2, 0x65, 0xdf, 0x7f, 0x4c, 0x88, 0x12, 0x88, 0x4f, 0xb3, 0x69, + 0xd9, 0xff, 0x27, 0xc0, 0x0a, 0x97, 0x91, 0x8f, 0x56, 0xfb, 0x89, 0xc4, + 0xa8, 0xbb, 0x92, 0x2d, 0x1b, 0x73, 0xb0, 0xc6, 0xab, 0x36, 0xf4, 0x96, + 0x6c, 0x20, 0x08, 0xef, 0x0a, 0x1e, 0x66, 0x24, 0x45, 0x4f, 0x67, 0x00, + 0x40, 0xc8, 0x07, 0x54, 0x74, 0x33, 0x3b, 0xa6, 0xad, 0xbb, 0x23, 0x9f, + 0x66, 0xed, 0xa2, 0x44, 0x70, 0x34, 0xfb, 0x0e, 0xea, 0x01, 0xfd, 0xcf, + 0x78, 0x74, 0xdf, 0xa7, 0xad, 0x55, 0xb7, 0x5f, 0x4d, 0xf6, 0xd6, 0x3f, + 0xe0, 0x86, 0xce, 0x24, 0xc7, 0x42, 0xa9, 0x13, 0x14, 0x44, 0x35, 0x4b, + 0xb6, 0xdf, 0xc9, 0x60, 0xac, 0x0c, 0x7f, 0xd9, 0x93, 0x21, 0x4b, 0xee, + 0x9c, 0xe4, 0x49, 0x02, 0x98, 0xd3, 0x60, 0x7b, 0x5c, 0xbc, 0xd5, 0x30, + 0x2f, 0x07, 0xce, 0x44, 0x42, 0xc4, 0x0b, 0x99, 0xfe, 0xe6, 0x9f, 0xfc, + 0xb0, 0x78, 0x86, 0x51, 0x6d, 0xd1, 0x2c, 0x9d, 0xc6, 0x96, 0xfb, 0x85, + 0x82, 0xbb, 0x04, 0x2f, 0xf7, 0x62, 0x80, 0xef, 0x62, 0xda, 0x7f, 0xf6, + 0x0e, 0xac, 0x90, 0xb8, 0x56, 0xbd, 0x79, 0x3f, 0xf2, 0x80, 0x6e, 0xa3, + 0xd9, 0xb9, 0x0f, 0x5d, 0x3a, 0x07, 0x1d, 0x91, 0x93, 0x86, 0x4b, 0x29, + 0x4c, 0xe1, 0xdc, 0xb5, 0xe1, 0xe0, 0x33, 0x9d, 0xb3, 0xcb, 0x36, 0x91, + 0x4b, 0xfe, 0xa1, 0xb4, 0xee, 0xf0, 0xf9, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 51:3f:b9:74:38:70:b7:34:40:41:8d:30:93:06:99:ff + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5 + Validity + Not Before: Oct 31 00:00:00 2013 GMT + Not After : Oct 30 23:59:59 2023 GMT + Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 Secure Server CA - G4 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b2:d8:05:ca:1c:74:2d:b5:17:56:39:c5:4a:52: + 09:96:e8:4b:d8:0c:f1:68:9f:9a:42:28:62:c3:a5: + 30:53:7e:55:11:82:5b:03:7a:0d:2f:e1:79:04:c9: + b4:96:77:19:81:01:94:59:f9:bc:f7:7a:99:27:82: + 2d:b7:83:dd:5a:27:7f:b2:03:7a:9c:53:25:e9:48: + 1f:46:4f:c8:9d:29:f8:be:79:56:f6:f7:fd:d9:3a: + 68:da:8b:4b:82:33:41:12:c3:c8:3c:cc:d6:96:7a: + 84:21:1a:22:04:03:27:17:8b:1c:68:61:93:0f:0e: + 51:80:33:1d:b4:b5:ce:eb:7e:d0:62:ac:ee:b3:7b: + 01:74:ef:69:35:eb:ca:d5:3d:a9:ee:97:98:ca:8d: + aa:44:0e:25:99:4a:15:96:a4:ce:6d:02:54:1f:2a: + 6a:26:e2:06:3a:63:48:ac:b4:4c:d1:75:93:50:ff: + 13:2f:d6:da:e1:c6:18:f5:9f:c9:25:5d:f3:00:3a: + de:26:4d:b4:29:09:cd:0f:3d:23:6f:16:4a:81:16: + fb:f2:83:10:c3:b8:d6:d8:55:32:3d:f1:bd:0f:bd: + 8c:52:95:4a:16:97:7a:52:21:63:75:2f:16:f9:c4: + 66:be:f5:b5:09:d8:ff:27:00:cd:44:7c:6f:4b:3f: + b0:f7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://s1.symcb.com/pca3-g5.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://s2.symcb.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.symauth.com/cps + User Notice: + Explicit Text: http://www.symauth.com/rpa + + X509v3 Subject Alternative Name: + DirName:/CN=SymantecPKI-1-534 + X509v3 Subject Key Identifier: + 5F:60:CF:61:90:55:DF:84:43:14:8A:60:2A:B2:F5:7A:F4:43:18:EF + X509v3 Authority Key Identifier: + keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33 + + Signature Algorithm: sha256WithRSAEncryption + 5e:94:56:49:dd:8e:2d:65:f5:c1:36:51:b6:03:e3:da:9e:73: + 19:f2:1f:59:ab:58:7e:6c:26:05:2c:fa:81:d7:5c:23:17:22: + 2c:37:93:f7:86:ec:85:e6:b0:a3:fd:1f:e2:32:a8:45:6f:e1: + d9:fb:b9:af:d2:70:a0:32:42:65:bf:84:fe:16:2a:8f:3f:c5: + a6:d6:a3:93:7d:43:e9:74:21:91:35:28:f4:63:e9:2e:ed:f7: + f5:5c:7f:4b:9a:b5:20:e9:0a:bd:e0:45:10:0c:14:94:9a:5d: + a5:e3:4b:91:e8:24:9b:46:40:65:f4:22:72:cd:99:f8:88:11: + f5:f3:7f:e6:33:82:e6:a8:c5:7e:fe:d0:08:e2:25:58:08:71: + 68:e6:cd:a2:e6:14:de:4e:52:24:2d:fd:e5:79:13:53:e7:5e: + 2f:2d:4d:1b:6d:40:15:52:2b:f7:87:89:78:12:81:6e:d9:4d: + aa:2d:78:d4:c2:2c:3d:08:5f:87:91:9e:1f:0e:b0:de:30:52: + 64:86:89:aa:9d:66:9c:0e:76:0c:80:f2:74:d8:2a:f8:b8:3a: + ce:d7:d6:0f:11:be:6b:ab:14:f5:bd:41:a0:22:63:89:f1:ba: + 0f:6f:29:63:66:2d:3f:ac:8c:72:c5:fb:c7:e4:d4:0f:f2:3b: + 4f:8c:29:c7 +-----BEGIN CERTIFICATE----- +MIIFODCCBCCgAwIBAgIQUT+5dDhwtzRAQY0wkwaZ/zANBgkqhkiG9w0BAQsFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB+MQsw +CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV +BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVjIENs +YXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAstgFyhx0LbUXVjnFSlIJluhL2AzxaJ+aQihiw6UwU35VEYJb +A3oNL+F5BMm0lncZgQGUWfm893qZJ4Itt4PdWid/sgN6nFMl6UgfRk/InSn4vnlW +9vf92Tpo2otLgjNBEsPIPMzWlnqEIRoiBAMnF4scaGGTDw5RgDMdtLXO637QYqzu +s3sBdO9pNevK1T2p7peYyo2qRA4lmUoVlqTObQJUHypqJuIGOmNIrLRM0XWTUP8T +L9ba4cYY9Z/JJV3zADreJk20KQnNDz0jbxZKgRb78oMQw7jW2FUyPfG9D72MUpVK +Fpd6UiFjdS8W+cRmvvW1Cdj/JwDNRHxvSz+w9wIDAQABo4IBYzCCAV8wEgYDVR0T +AQH/BAgwBgEB/wIBADAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vczEuc3ltY2Iu +Y29tL3BjYTMtZzUuY3JsMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQjMCEw +HwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wawYDVR0gBGQwYjBgBgpg +hkgBhvhFAQc2MFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20v +Y3BzMCgGCCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20vcnBhMCkG +A1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTUzNDAdBgNVHQ4E +FgQUX2DPYZBV34RDFIpgKrL1evRDGO8wHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnz +Qzn6Aq8zMTMwDQYJKoZIhvcNAQELBQADggEBAF6UVkndji1l9cE2UbYD49qecxny +H1mrWH5sJgUs+oHXXCMXIiw3k/eG7IXmsKP9H+IyqEVv4dn7ua/ScKAyQmW/hP4W +Ko8/xabWo5N9Q+l0IZE1KPRj6S7t9/Vcf0uatSDpCr3gRRAMFJSaXaXjS5HoJJtG +QGX0InLNmfiIEfXzf+YzguaoxX7+0AjiJVgIcWjmzaLmFN5OUiQt/eV5E1PnXi8t +TRttQBVSK/eHiXgSgW7ZTaoteNTCLD0IX4eRnh8OsN4wUmSGiaqdZpwOdgyA8nTY +Kvi4Os7X1g8RvmurFPW9QaAiY4nxug9vKWNmLT+sjHLF+8fk1A/yO0+MKcc= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert45[] = { + 0x30, 0x82, 0x05, 0x38, 0x30, 0x82, 0x04, 0x20, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x51, 0x3f, 0xb9, 0x74, 0x38, 0x70, 0xb7, 0x34, 0x40, + 0x41, 0x8d, 0x30, 0x93, 0x06, 0x99, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x7e, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d, + 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x26, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, + 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d, + 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xb2, 0xd8, 0x05, 0xca, 0x1c, 0x74, 0x2d, 0xb5, 0x17, 0x56, 0x39, 0xc5, + 0x4a, 0x52, 0x09, 0x96, 0xe8, 0x4b, 0xd8, 0x0c, 0xf1, 0x68, 0x9f, 0x9a, + 0x42, 0x28, 0x62, 0xc3, 0xa5, 0x30, 0x53, 0x7e, 0x55, 0x11, 0x82, 0x5b, + 0x03, 0x7a, 0x0d, 0x2f, 0xe1, 0x79, 0x04, 0xc9, 0xb4, 0x96, 0x77, 0x19, + 0x81, 0x01, 0x94, 0x59, 0xf9, 0xbc, 0xf7, 0x7a, 0x99, 0x27, 0x82, 0x2d, + 0xb7, 0x83, 0xdd, 0x5a, 0x27, 0x7f, 0xb2, 0x03, 0x7a, 0x9c, 0x53, 0x25, + 0xe9, 0x48, 0x1f, 0x46, 0x4f, 0xc8, 0x9d, 0x29, 0xf8, 0xbe, 0x79, 0x56, + 0xf6, 0xf7, 0xfd, 0xd9, 0x3a, 0x68, 0xda, 0x8b, 0x4b, 0x82, 0x33, 0x41, + 0x12, 0xc3, 0xc8, 0x3c, 0xcc, 0xd6, 0x96, 0x7a, 0x84, 0x21, 0x1a, 0x22, + 0x04, 0x03, 0x27, 0x17, 0x8b, 0x1c, 0x68, 0x61, 0x93, 0x0f, 0x0e, 0x51, + 0x80, 0x33, 0x1d, 0xb4, 0xb5, 0xce, 0xeb, 0x7e, 0xd0, 0x62, 0xac, 0xee, + 0xb3, 0x7b, 0x01, 0x74, 0xef, 0x69, 0x35, 0xeb, 0xca, 0xd5, 0x3d, 0xa9, + 0xee, 0x97, 0x98, 0xca, 0x8d, 0xaa, 0x44, 0x0e, 0x25, 0x99, 0x4a, 0x15, + 0x96, 0xa4, 0xce, 0x6d, 0x02, 0x54, 0x1f, 0x2a, 0x6a, 0x26, 0xe2, 0x06, + 0x3a, 0x63, 0x48, 0xac, 0xb4, 0x4c, 0xd1, 0x75, 0x93, 0x50, 0xff, 0x13, + 0x2f, 0xd6, 0xda, 0xe1, 0xc6, 0x18, 0xf5, 0x9f, 0xc9, 0x25, 0x5d, 0xf3, + 0x00, 0x3a, 0xde, 0x26, 0x4d, 0xb4, 0x29, 0x09, 0xcd, 0x0f, 0x3d, 0x23, + 0x6f, 0x16, 0x4a, 0x81, 0x16, 0xfb, 0xf2, 0x83, 0x10, 0xc3, 0xb8, 0xd6, + 0xd8, 0x55, 0x32, 0x3d, 0xf1, 0xbd, 0x0f, 0xbd, 0x8c, 0x52, 0x95, 0x4a, + 0x16, 0x97, 0x7a, 0x52, 0x21, 0x63, 0x75, 0x2f, 0x16, 0xf9, 0xc4, 0x66, + 0xbe, 0xf5, 0xb5, 0x09, 0xd8, 0xff, 0x27, 0x00, 0xcd, 0x44, 0x7c, 0x6f, + 0x4b, 0x3f, 0xb0, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, + 0x63, 0x30, 0x82, 0x01, 0x5f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, + 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, + 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, + 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x32, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x6b, 0x06, 0x03, + 0x55, 0x1d, 0x20, 0x04, 0x64, 0x30, 0x62, 0x30, 0x60, 0x06, 0x0a, 0x60, + 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x52, 0x30, + 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, + 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, + 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x29, 0x06, + 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, + 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, + 0x2d, 0x35, 0x33, 0x34, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x5f, 0x60, 0xcf, 0x61, 0x90, 0x55, 0xdf, 0x84, 0x43, + 0x14, 0x8a, 0x60, 0x2a, 0xb2, 0xf5, 0x7a, 0xf4, 0x43, 0x18, 0xef, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, + 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x5e, 0x94, 0x56, 0x49, 0xdd, 0x8e, 0x2d, 0x65, + 0xf5, 0xc1, 0x36, 0x51, 0xb6, 0x03, 0xe3, 0xda, 0x9e, 0x73, 0x19, 0xf2, + 0x1f, 0x59, 0xab, 0x58, 0x7e, 0x6c, 0x26, 0x05, 0x2c, 0xfa, 0x81, 0xd7, + 0x5c, 0x23, 0x17, 0x22, 0x2c, 0x37, 0x93, 0xf7, 0x86, 0xec, 0x85, 0xe6, + 0xb0, 0xa3, 0xfd, 0x1f, 0xe2, 0x32, 0xa8, 0x45, 0x6f, 0xe1, 0xd9, 0xfb, + 0xb9, 0xaf, 0xd2, 0x70, 0xa0, 0x32, 0x42, 0x65, 0xbf, 0x84, 0xfe, 0x16, + 0x2a, 0x8f, 0x3f, 0xc5, 0xa6, 0xd6, 0xa3, 0x93, 0x7d, 0x43, 0xe9, 0x74, + 0x21, 0x91, 0x35, 0x28, 0xf4, 0x63, 0xe9, 0x2e, 0xed, 0xf7, 0xf5, 0x5c, + 0x7f, 0x4b, 0x9a, 0xb5, 0x20, 0xe9, 0x0a, 0xbd, 0xe0, 0x45, 0x10, 0x0c, + 0x14, 0x94, 0x9a, 0x5d, 0xa5, 0xe3, 0x4b, 0x91, 0xe8, 0x24, 0x9b, 0x46, + 0x40, 0x65, 0xf4, 0x22, 0x72, 0xcd, 0x99, 0xf8, 0x88, 0x11, 0xf5, 0xf3, + 0x7f, 0xe6, 0x33, 0x82, 0xe6, 0xa8, 0xc5, 0x7e, 0xfe, 0xd0, 0x08, 0xe2, + 0x25, 0x58, 0x08, 0x71, 0x68, 0xe6, 0xcd, 0xa2, 0xe6, 0x14, 0xde, 0x4e, + 0x52, 0x24, 0x2d, 0xfd, 0xe5, 0x79, 0x13, 0x53, 0xe7, 0x5e, 0x2f, 0x2d, + 0x4d, 0x1b, 0x6d, 0x40, 0x15, 0x52, 0x2b, 0xf7, 0x87, 0x89, 0x78, 0x12, + 0x81, 0x6e, 0xd9, 0x4d, 0xaa, 0x2d, 0x78, 0xd4, 0xc2, 0x2c, 0x3d, 0x08, + 0x5f, 0x87, 0x91, 0x9e, 0x1f, 0x0e, 0xb0, 0xde, 0x30, 0x52, 0x64, 0x86, + 0x89, 0xaa, 0x9d, 0x66, 0x9c, 0x0e, 0x76, 0x0c, 0x80, 0xf2, 0x74, 0xd8, + 0x2a, 0xf8, 0xb8, 0x3a, 0xce, 0xd7, 0xd6, 0x0f, 0x11, 0xbe, 0x6b, 0xab, + 0x14, 0xf5, 0xbd, 0x41, 0xa0, 0x22, 0x63, 0x89, 0xf1, 0xba, 0x0f, 0x6f, + 0x29, 0x63, 0x66, 0x2d, 0x3f, 0xac, 0x8c, 0x72, 0xc5, 0xfb, 0xc7, 0xe4, + 0xd4, 0x0f, 0xf2, 0x3b, 0x4f, 0x8c, 0x29, 0xc7, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 69:87:94:19:d9:e3:62:70:74:9d:bb:e5:9d:c6:68:5e + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2008 VeriSign, Inc. - For authorized use only, CN=VeriSign Universal Root Certification Authority + Validity + Not Before: Apr 9 00:00:00 2013 GMT + Not After : Apr 8 23:59:59 2023 GMT + Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 Secure Server SHA256 SSL CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:be:38:16:51:8b:80:db:ea:0e:4d:ec:e8:3f:5c: + c4:7c:a2:5d:ed:3b:af:a5:d6:9e:10:35:2c:e3:c5: + e5:a8:de:8c:86:17:26:e6:de:0b:51:4a:2c:d0:fb: + d1:14:5a:72:f7:c9:dd:b8:83:1c:c6:46:8c:31:25: + 91:0e:59:17:a3:d0:13:8c:92:c1:af:81:54:4e:bc: + 62:02:9e:aa:a7:1a:57:d8:ca:a6:99:7a:70:56:4f: + 98:07:2e:4b:96:d0:4c:39:53:b9:61:2f:3b:76:7c: + 8e:05:9e:99:44:d1:03:54:77:29:2b:56:2a:aa:61: + e4:84:2f:12:15:3c:bd:d7:8a:e8:09:1e:56:f1:b5: + 14:ac:8a:84:ce:ae:78:a2:60:0a:53:7e:13:4c:1a: + 40:70:0e:52:59:ff:5a:68:2e:4c:46:13:3b:39:09: + 82:78:02:35:49:20:08:82:b3:b1:6c:89:0f:6e:1e: + 35:25:b0:2c:24:83:e3:c5:50:2c:ba:46:90:45:87: + 0d:72:ff:5d:11:38:c5:91:76:c5:2c:fb:05:2a:82: + 95:a1:59:63:e3:d0:26:58:cd:67:56:3a:ba:df:7c: + d2:d2:3b:d8:de:1a:7a:77:e4:0c:8c:0b:eb:2b:c2: + 22:b0:bd:55:ba:d9:b9:55:d1:22:7a:c6:02:4e:3f: + c3:35 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.ws.symantec.com/universal-root.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Authority Information Access: + OCSP - URI:http://ocsp.ws.symantec.com + + X509v3 Certificate Policies: + Policy: 2.16.840.1.113733.1.7.54 + CPS: http://www.symauth.com/cps + User Notice: + Explicit Text: http://www.symauth.com/rpa + + X509v3 Subject Alternative Name: + DirName:/CN=VeriSignMPKI-2-373 + X509v3 Subject Key Identifier: + DB:62:20:FB:7D:02:89:7C:D2:3B:6F:C7:E4:32:6C:05:52:1D:AD:B1 + X509v3 Authority Key Identifier: + keyid:B6:77:FA:69:48:47:9F:53:12:D5:C2:EA:07:32:76:07:D1:97:07:19 + + Signature Algorithm: sha256WithRSAEncryption + 19:cc:95:e2:2f:7b:49:d0:48:90:53:f4:07:b1:20:44:35:70: + 14:d5:44:37:31:ef:ef:70:d1:2d:4c:e9:2d:b0:53:91:01:4c: + 54:e7:7d:9b:da:3a:ff:b7:cb:14:ad:30:0f:69:1a:2a:f0:bc: + cd:35:eb:48:dc:b9:87:fd:cf:b1:5a:f6:05:da:3c:64:e6:2b: + e6:dc:73:5e:9a:d8:0c:9b:d2:97:b3:e8:fa:87:95:53:e1:99: + ad:88:e8:fa:bc:09:4d:a2:c4:6a:1b:28:3b:2d:c3:21:15:ee: + 14:fa:9d:98:10:eb:9f:3e:e6:24:24:5f:7a:1c:05:bb:9a:31: + 23:58:79:4c:ec:6d:18:19:4d:51:1f:08:61:bd:91:05:0c:5a: + 9c:26:fc:0b:a5:20:25:bf:6a:1b:2b:f7:02:09:72:69:83:32: + 14:c3:60:5b:7e:fd:9a:32:fa:b4:95:0e:1a:f9:3b:09:a4:54: + 47:9a:0c:ce:32:af:d1:21:cc:7f:d2:06:ef:60:0e:62:6f:6f: + 81:1a:17:9d:c8:cb:28:cc:e2:5f:6e:2c:7a:b4:cb:47:7c:74: + 68:7b:48:71:02:9c:23:09:f3:5a:ae:5f:42:2e:5f:2b:59:2d: + 52:88:e5:8d:0b:b3:a8:61:f9:4b:9b:55:d6:da:b1:92:3b:bf: + c3:9b:f9:2c +-----BEGIN CERTIFICATE----- +MIIFSTCCBDGgAwIBAgIQaYeUGdnjYnB0nbvlncZoXjANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0xMzA0MDkwMDAwMDBaFw0yMzA0MDgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEd +MBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVj +IFRydXN0IE5ldHdvcmsxNTAzBgNVBAMTLFN5bWFudGVjIENsYXNzIDMgU2VjdXJl +IFNlcnZlciBTSEEyNTYgU1NMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvjgWUYuA2+oOTezoP1zEfKJd7TuvpdaeEDUs48XlqN6Mhhcm5t4LUUos +0PvRFFpy98nduIMcxkaMMSWRDlkXo9ATjJLBr4FUTrxiAp6qpxpX2MqmmXpwVk+Y +By5LltBMOVO5YS87dnyOBZ6ZRNEDVHcpK1YqqmHkhC8SFTy914roCR5W8bUUrIqE +zq54omAKU34TTBpAcA5SWf9aaC5MRhM7OQmCeAI1SSAIgrOxbIkPbh41JbAsJIPj +xVAsukaQRYcNcv9dETjFkXbFLPsFKoKVoVlj49AmWM1nVjq633zS0jvY3hp6d+QM +jAvrK8IisL1Vutm5VdEiesYCTj/DNQIDAQABo4IBejCCAXYwEgYDVR0TAQH/BAgw +BgEB/wIBADA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vY3JsLndzLnN5bWFudGVj +LmNvbS91bml2ZXJzYWwtcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUF +BwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3Aud3Muc3ltYW50ZWMuY29t +MGsGA1UdIARkMGIwYAYKYIZIAYb4RQEHNjBSMCYGCCsGAQUFBwIBFhpodHRwOi8v +d3d3LnN5bWF1dGguY29tL2NwczAoBggrBgEFBQcCAjAcGhpodHRwOi8vd3d3LnN5 +bWF1dGguY29tL3JwYTAqBgNVHREEIzAhpB8wHTEbMBkGA1UEAxMSVmVyaVNpZ25N +UEtJLTItMzczMB0GA1UdDgQWBBTbYiD7fQKJfNI7b8fkMmwFUh2tsTAfBgNVHSME +GDAWgBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEAGcyV +4i97SdBIkFP0B7EgRDVwFNVENzHv73DRLUzpLbBTkQFMVOd9m9o6/7fLFK0wD2ka +KvC8zTXrSNy5h/3PsVr2Bdo8ZOYr5txzXprYDJvSl7Po+oeVU+GZrYjo+rwJTaLE +ahsoOy3DIRXuFPqdmBDrnz7mJCRfehwFu5oxI1h5TOxtGBlNUR8IYb2RBQxanCb8 +C6UgJb9qGyv3AglyaYMyFMNgW379mjL6tJUOGvk7CaRUR5oMzjKv0SHMf9IG72AO +Ym9vgRoXncjLKMziX24serTLR3x0aHtIcQKcIwnzWq5fQi5fK1ktUojljQuzqGH5 +S5tV1tqxkju/w5v5LA== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert46[] = { + 0x30, 0x82, 0x05, 0x49, 0x30, 0x82, 0x04, 0x31, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x69, 0x87, 0x94, 0x19, 0xd9, 0xe3, 0x62, 0x70, 0x74, + 0x9d, 0xbb, 0xe5, 0x9d, 0xc6, 0x68, 0x5e, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, + 0xbd, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, + 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, + 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x56, + 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x55, 0x6e, 0x69, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x33, 0x30, 0x34, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x34, 0x30, 0x38, 0x32, + 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0x84, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d, + 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x31, 0x35, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x2c, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, + 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x53, 0x48, 0x41, 0x32, + 0x35, 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbe, 0x38, 0x16, 0x51, 0x8b, 0x80, + 0xdb, 0xea, 0x0e, 0x4d, 0xec, 0xe8, 0x3f, 0x5c, 0xc4, 0x7c, 0xa2, 0x5d, + 0xed, 0x3b, 0xaf, 0xa5, 0xd6, 0x9e, 0x10, 0x35, 0x2c, 0xe3, 0xc5, 0xe5, + 0xa8, 0xde, 0x8c, 0x86, 0x17, 0x26, 0xe6, 0xde, 0x0b, 0x51, 0x4a, 0x2c, + 0xd0, 0xfb, 0xd1, 0x14, 0x5a, 0x72, 0xf7, 0xc9, 0xdd, 0xb8, 0x83, 0x1c, + 0xc6, 0x46, 0x8c, 0x31, 0x25, 0x91, 0x0e, 0x59, 0x17, 0xa3, 0xd0, 0x13, + 0x8c, 0x92, 0xc1, 0xaf, 0x81, 0x54, 0x4e, 0xbc, 0x62, 0x02, 0x9e, 0xaa, + 0xa7, 0x1a, 0x57, 0xd8, 0xca, 0xa6, 0x99, 0x7a, 0x70, 0x56, 0x4f, 0x98, + 0x07, 0x2e, 0x4b, 0x96, 0xd0, 0x4c, 0x39, 0x53, 0xb9, 0x61, 0x2f, 0x3b, + 0x76, 0x7c, 0x8e, 0x05, 0x9e, 0x99, 0x44, 0xd1, 0x03, 0x54, 0x77, 0x29, + 0x2b, 0x56, 0x2a, 0xaa, 0x61, 0xe4, 0x84, 0x2f, 0x12, 0x15, 0x3c, 0xbd, + 0xd7, 0x8a, 0xe8, 0x09, 0x1e, 0x56, 0xf1, 0xb5, 0x14, 0xac, 0x8a, 0x84, + 0xce, 0xae, 0x78, 0xa2, 0x60, 0x0a, 0x53, 0x7e, 0x13, 0x4c, 0x1a, 0x40, + 0x70, 0x0e, 0x52, 0x59, 0xff, 0x5a, 0x68, 0x2e, 0x4c, 0x46, 0x13, 0x3b, + 0x39, 0x09, 0x82, 0x78, 0x02, 0x35, 0x49, 0x20, 0x08, 0x82, 0xb3, 0xb1, + 0x6c, 0x89, 0x0f, 0x6e, 0x1e, 0x35, 0x25, 0xb0, 0x2c, 0x24, 0x83, 0xe3, + 0xc5, 0x50, 0x2c, 0xba, 0x46, 0x90, 0x45, 0x87, 0x0d, 0x72, 0xff, 0x5d, + 0x11, 0x38, 0xc5, 0x91, 0x76, 0xc5, 0x2c, 0xfb, 0x05, 0x2a, 0x82, 0x95, + 0xa1, 0x59, 0x63, 0xe3, 0xd0, 0x26, 0x58, 0xcd, 0x67, 0x56, 0x3a, 0xba, + 0xdf, 0x7c, 0xd2, 0xd2, 0x3b, 0xd8, 0xde, 0x1a, 0x7a, 0x77, 0xe4, 0x0c, + 0x8c, 0x0b, 0xeb, 0x2b, 0xc2, 0x22, 0xb0, 0xbd, 0x55, 0xba, 0xd9, 0xb9, + 0x55, 0xd1, 0x22, 0x7a, 0xc6, 0x02, 0x4e, 0x3f, 0xc3, 0x35, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x7a, 0x30, 0x82, 0x01, 0x76, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, + 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3e, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x37, 0x30, 0x35, 0x30, 0x33, 0xa0, 0x31, 0xa0, 0x2f, + 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x2e, 0x77, 0x73, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x6c, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x77, 0x73, 0x2e, + 0x73, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x6b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x64, 0x30, 0x62, 0x30, + 0x60, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, + 0x36, 0x30, 0x52, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, + 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, + 0x61, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21, + 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, + 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x33, 0x37, 0x33, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xdb, 0x62, 0x20, 0xfb, + 0x7d, 0x02, 0x89, 0x7c, 0xd2, 0x3b, 0x6f, 0xc7, 0xe4, 0x32, 0x6c, 0x05, + 0x52, 0x1d, 0xad, 0xb1, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xb6, 0x77, 0xfa, 0x69, 0x48, 0x47, 0x9f, + 0x53, 0x12, 0xd5, 0xc2, 0xea, 0x07, 0x32, 0x76, 0x07, 0xd1, 0x97, 0x07, + 0x19, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x19, 0xcc, 0x95, + 0xe2, 0x2f, 0x7b, 0x49, 0xd0, 0x48, 0x90, 0x53, 0xf4, 0x07, 0xb1, 0x20, + 0x44, 0x35, 0x70, 0x14, 0xd5, 0x44, 0x37, 0x31, 0xef, 0xef, 0x70, 0xd1, + 0x2d, 0x4c, 0xe9, 0x2d, 0xb0, 0x53, 0x91, 0x01, 0x4c, 0x54, 0xe7, 0x7d, + 0x9b, 0xda, 0x3a, 0xff, 0xb7, 0xcb, 0x14, 0xad, 0x30, 0x0f, 0x69, 0x1a, + 0x2a, 0xf0, 0xbc, 0xcd, 0x35, 0xeb, 0x48, 0xdc, 0xb9, 0x87, 0xfd, 0xcf, + 0xb1, 0x5a, 0xf6, 0x05, 0xda, 0x3c, 0x64, 0xe6, 0x2b, 0xe6, 0xdc, 0x73, + 0x5e, 0x9a, 0xd8, 0x0c, 0x9b, 0xd2, 0x97, 0xb3, 0xe8, 0xfa, 0x87, 0x95, + 0x53, 0xe1, 0x99, 0xad, 0x88, 0xe8, 0xfa, 0xbc, 0x09, 0x4d, 0xa2, 0xc4, + 0x6a, 0x1b, 0x28, 0x3b, 0x2d, 0xc3, 0x21, 0x15, 0xee, 0x14, 0xfa, 0x9d, + 0x98, 0x10, 0xeb, 0x9f, 0x3e, 0xe6, 0x24, 0x24, 0x5f, 0x7a, 0x1c, 0x05, + 0xbb, 0x9a, 0x31, 0x23, 0x58, 0x79, 0x4c, 0xec, 0x6d, 0x18, 0x19, 0x4d, + 0x51, 0x1f, 0x08, 0x61, 0xbd, 0x91, 0x05, 0x0c, 0x5a, 0x9c, 0x26, 0xfc, + 0x0b, 0xa5, 0x20, 0x25, 0xbf, 0x6a, 0x1b, 0x2b, 0xf7, 0x02, 0x09, 0x72, + 0x69, 0x83, 0x32, 0x14, 0xc3, 0x60, 0x5b, 0x7e, 0xfd, 0x9a, 0x32, 0xfa, + 0xb4, 0x95, 0x0e, 0x1a, 0xf9, 0x3b, 0x09, 0xa4, 0x54, 0x47, 0x9a, 0x0c, + 0xce, 0x32, 0xaf, 0xd1, 0x21, 0xcc, 0x7f, 0xd2, 0x06, 0xef, 0x60, 0x0e, + 0x62, 0x6f, 0x6f, 0x81, 0x1a, 0x17, 0x9d, 0xc8, 0xcb, 0x28, 0xcc, 0xe2, + 0x5f, 0x6e, 0x2c, 0x7a, 0xb4, 0xcb, 0x47, 0x7c, 0x74, 0x68, 0x7b, 0x48, + 0x71, 0x02, 0x9c, 0x23, 0x09, 0xf3, 0x5a, 0xae, 0x5f, 0x42, 0x2e, 0x5f, + 0x2b, 0x59, 0x2d, 0x52, 0x88, 0xe5, 0x8d, 0x0b, 0xb3, 0xa8, 0x61, 0xf9, + 0x4b, 0x9b, 0x55, 0xd6, 0xda, 0xb1, 0x92, 0x3b, 0xbf, 0xc3, 0x9b, 0xf9, + 0x2c, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120036009 (0x7279aa9) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Validity + Not Before: Dec 19 20:07:32 2013 GMT + Not After : Dec 19 20:06:55 2017 GMT + Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24: + 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72: + 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88: + 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1: + d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03: + e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42: + f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe: + 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29: + e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18: + 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51: + af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41: + 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a: + 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a: + 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94: + 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67: + 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae: + ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be: + ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34: + 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f: + e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29: + 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d: + 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08: + 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf: + ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2: + 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08: + 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0: + 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70: + 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7: + d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09: + c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18: + 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33: + a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13: + 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01: + a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41: + 4a:a8:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6334.1.0 + CPS: http://cybertrust.omniroot.com/repository.cfm + + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Authority Key Identifier: + keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl + + X509v3 Subject Key Identifier: + 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5 + Signature Algorithm: sha256WithRSAEncryption + 76:85:c5:23:31:1f:b4:73:ea:a0:bc:a5:ed:df:45:43:6a:7f: + 69:20:1b:80:b2:fb:1c:dd:aa:7f:88:d3:31:41:36:f7:fb:fb: + 6b:ad:98:8c:78:1f:9d:11:67:3a:cd:4b:ec:a8:bc:9d:15:19: + c4:3b:0b:a7:93:ce:e8:fc:9d:5b:e8:1f:cb:56:ae:76:43:2b: + c7:13:51:77:41:a8:66:4c:5f:a7:d1:d7:aa:75:c5:1b:29:4c: + c9:f4:6d:a1:5e:a1:85:93:16:c2:cb:3b:ab:14:7d:44:fd:da: + 25:29:86:2a:fe:63:20:ca:d2:0b:c2:34:15:bb:af:5b:7f:8a: + e0:aa:ed:45:a6:ea:79:db:d8:35:66:54:43:de:37:33:d1:e4: + e0:cd:57:ca:71:b0:7d:e9:16:77:64:e8:59:97:b9:d5:2e:d1: + b4:91:da:77:71:f3:4a:0f:48:d2:34:99:60:95:37:ac:1f:01: + cd:10:9d:e8:2a:a5:20:c7:50:9b:b3:6c:49:78:2b:58:92:64: + 89:b8:95:36:a8:34:aa:f0:41:d2:95:5a:24:54:97:4d:6e:05: + c4:95:ad:c4:7a:a3:39:fb:79:06:8a:9b:a6:4f:d9:22:fa:44: + 4e:36:f3:c9:0f:a6:39:e7:80:b2:5e:bf:bd:39:d1:46:e5:55: + 47:db:bc:6e +-----BEGIN CERTIFICATE----- +MIIFhjCCBG6gAwIBAgIEByeaqTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEzMTIxOTIwMDczMloX +DTE3MTIxOTIwMDY1NVowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n +dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y +YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0 +IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3 +p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu +e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA +iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R +PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg +lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU +mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R +DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV +S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq +qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE +OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o +/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCASAwggEcMBIGA1Ud +EwEB/wQIMAYBAf8CAQAwUwYDVR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEF +BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku +Y2ZtMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH +AwIwHwYDVR0jBBgwFoAU5Z1ZMIJHWMys+ghUNoZ7OrUETfAwQgYDVR0fBDswOTA3 +oDWgM4YxaHR0cDovL2NkcDEucHVibGljLXRydXN0LmNvbS9DUkwvT21uaXJvb3Qy +MDI1LmNybDAdBgNVHQ4EFgQUUa8kJpz0aCJXgCYrO0ZiFXsezKUwDQYJKoZIhvcN +AQELBQADggEBAHaFxSMxH7Rz6qC8pe3fRUNqf2kgG4Cy+xzdqn+I0zFBNvf7+2ut +mIx4H50RZzrNS+yovJ0VGcQ7C6eTzuj8nVvoH8tWrnZDK8cTUXdBqGZMX6fR16p1 +xRspTMn0baFeoYWTFsLLO6sUfUT92iUphir+YyDK0gvCNBW7r1t/iuCq7UWm6nnb +2DVmVEPeNzPR5ODNV8pxsH3pFndk6FmXudUu0bSR2ndx80oPSNI0mWCVN6wfAc0Q +negqpSDHUJuzbEl4K1iSZIm4lTaoNKrwQdKVWiRUl01uBcSVrcR6ozn7eQaKm6ZP +2SL6RE4288kPpjnngLJev7050UblVUfbvG4= +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert47[] = { + 0x30, 0x82, 0x05, 0x86, 0x30, 0x82, 0x04, 0x6e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0x9a, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, + 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x37, 0x33, 0x32, 0x5a, 0x17, + 0x0d, 0x31, 0x37, 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x36, 0x35, + 0x35, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, + 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, + 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, + 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, + 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37, + 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37, + 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d, + 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10, + 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae, + 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30, + 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2, + 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77, + 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0, + 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9, + 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09, + 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57, + 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1, + 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb, + 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8, + 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21, + 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60, + 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb, + 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4, + 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a, + 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14, + 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae, + 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca, + 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99, + 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11, + 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b, + 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57, + 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02, + 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5, + 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c, + 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb, + 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90, + 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa, + 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d, + 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae, + 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86, + 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4, + 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a, + 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6, + 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8, + 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68, + 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0, + 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10, + 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, + 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, + 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, + 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x02, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, + 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, + 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, + 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, + 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, + 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4, + 0x68, 0x22, 0x57, 0x80, 0x26, 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e, + 0xcc, 0xa5, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x76, 0x85, + 0xc5, 0x23, 0x31, 0x1f, 0xb4, 0x73, 0xea, 0xa0, 0xbc, 0xa5, 0xed, 0xdf, + 0x45, 0x43, 0x6a, 0x7f, 0x69, 0x20, 0x1b, 0x80, 0xb2, 0xfb, 0x1c, 0xdd, + 0xaa, 0x7f, 0x88, 0xd3, 0x31, 0x41, 0x36, 0xf7, 0xfb, 0xfb, 0x6b, 0xad, + 0x98, 0x8c, 0x78, 0x1f, 0x9d, 0x11, 0x67, 0x3a, 0xcd, 0x4b, 0xec, 0xa8, + 0xbc, 0x9d, 0x15, 0x19, 0xc4, 0x3b, 0x0b, 0xa7, 0x93, 0xce, 0xe8, 0xfc, + 0x9d, 0x5b, 0xe8, 0x1f, 0xcb, 0x56, 0xae, 0x76, 0x43, 0x2b, 0xc7, 0x13, + 0x51, 0x77, 0x41, 0xa8, 0x66, 0x4c, 0x5f, 0xa7, 0xd1, 0xd7, 0xaa, 0x75, + 0xc5, 0x1b, 0x29, 0x4c, 0xc9, 0xf4, 0x6d, 0xa1, 0x5e, 0xa1, 0x85, 0x93, + 0x16, 0xc2, 0xcb, 0x3b, 0xab, 0x14, 0x7d, 0x44, 0xfd, 0xda, 0x25, 0x29, + 0x86, 0x2a, 0xfe, 0x63, 0x20, 0xca, 0xd2, 0x0b, 0xc2, 0x34, 0x15, 0xbb, + 0xaf, 0x5b, 0x7f, 0x8a, 0xe0, 0xaa, 0xed, 0x45, 0xa6, 0xea, 0x79, 0xdb, + 0xd8, 0x35, 0x66, 0x54, 0x43, 0xde, 0x37, 0x33, 0xd1, 0xe4, 0xe0, 0xcd, + 0x57, 0xca, 0x71, 0xb0, 0x7d, 0xe9, 0x16, 0x77, 0x64, 0xe8, 0x59, 0x97, + 0xb9, 0xd5, 0x2e, 0xd1, 0xb4, 0x91, 0xda, 0x77, 0x71, 0xf3, 0x4a, 0x0f, + 0x48, 0xd2, 0x34, 0x99, 0x60, 0x95, 0x37, 0xac, 0x1f, 0x01, 0xcd, 0x10, + 0x9d, 0xe8, 0x2a, 0xa5, 0x20, 0xc7, 0x50, 0x9b, 0xb3, 0x6c, 0x49, 0x78, + 0x2b, 0x58, 0x92, 0x64, 0x89, 0xb8, 0x95, 0x36, 0xa8, 0x34, 0xaa, 0xf0, + 0x41, 0xd2, 0x95, 0x5a, 0x24, 0x54, 0x97, 0x4d, 0x6e, 0x05, 0xc4, 0x95, + 0xad, 0xc4, 0x7a, 0xa3, 0x39, 0xfb, 0x79, 0x06, 0x8a, 0x9b, 0xa6, 0x4f, + 0xd9, 0x22, 0xfa, 0x44, 0x4e, 0x36, 0xf3, 0xc9, 0x0f, 0xa6, 0x39, 0xe7, + 0x80, 0xb2, 0x5e, 0xbf, 0xbd, 0x39, 0xd1, 0x46, 0xe5, 0x55, 0x47, 0xdb, + 0xbc, 0x6e, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 75:96:c2:3e:fa:89:59:45:6e:79:f7:17:ba:cf:64:f3 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign + Validity + Not Before: Nov 8 00:58:58 2014 GMT + Not After : Nov 8 00:58:58 2029 GMT + Subject: C=CN, O=WoSign CA Limited, CN=WoSign Class 3 OV Server CA G2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d6:74:87:af:99:c0:57:96:99:c2:89:74:3c:92: + 55:99:bf:1f:07:00:35:05:26:96:16:5b:03:c1:42: + 37:33:be:3f:0d:4f:ff:bb:94:26:91:d7:14:16:78: + 1b:f7:13:a2:4b:4c:e5:5c:a7:10:40:35:59:30:d1: + 77:99:e3:9d:29:c2:be:31:95:bd:92:61:5b:b0:23: + fb:67:58:d5:52:e4:7b:2f:f0:73:1c:73:94:55:ba: + c8:68:59:02:10:10:e4:f7:11:f0:c3:b6:d7:ae:56: + 80:00:9e:65:64:a6:83:91:41:e6:ed:a7:7a:65:a5: + 1f:30:2e:13:3c:bf:df:63:97:f3:96:f0:52:32:b4: + f4:7b:98:57:ed:36:4f:f7:21:4a:28:9d:dd:1c:92: + b3:4d:8d:9c:58:8b:17:21:d8:dc:a1:b7:ae:73:78: + 8a:c4:b6:e9:7f:28:8e:9a:d5:2e:9e:39:e9:da:59: + 74:e3:c8:97:10:32:94:19:59:d4:0f:89:57:44:e6: + e5:2b:17:30:62:52:98:7f:ab:0d:a5:01:ea:04:41: + ca:fa:13:0e:3b:87:06:ba:bd:47:31:d7:63:03:01: + f4:be:a1:37:11:9f:1e:01:95:4e:0f:3f:54:1e:92: + a6:9f:30:8c:fe:98:e8:56:96:66:04:e1:35:fe:59: + ac:57 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Client Authentication, TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crls1.wosign.com/ca1.crl + + Authority Information Access: + OCSP - URI:http://ocsp1.wosign.com/ca1 + CA Issuers - URI:http://aia1.wosign.com/ca1g2-server3.cer + + X509v3 Subject Key Identifier: + F9:8B:EC:04:38:6A:3F:AA:06:C6:94:AD:73:95:2A:B0:C8:E6:B8:FB + X509v3 Authority Key Identifier: + keyid:E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E + + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.36305.6.3.2.1 + CPS: http://www.wosign.com/policy/ + + Signature Algorithm: sha256WithRSAEncryption + 5e:67:ba:78:32:05:b6:b7:af:e7:de:6a:7a:82:64:0e:a0:0b: + f2:9e:9a:ba:c6:2b:6f:56:3a:b4:62:57:ab:7c:ad:60:50:96: + 34:9c:a3:88:cf:d9:8f:50:af:f6:f0:00:36:1b:1f:1f:87:55: + 3c:60:9a:f0:b0:0d:9a:80:2d:8a:3b:be:05:b3:d7:a0:80:b6: + b8:19:eb:51:db:ec:64:54:f1:1a:89:4a:48:a1:4d:3f:31:7d: + c4:79:94:4b:f1:de:ab:83:af:5f:86:be:96:1c:b3:3e:1c:e7: + bc:96:b2:e8:5a:ac:b5:58:cb:3c:56:6f:0a:a7:a5:d0:36:89: + 82:26:8c:b9:1f:b6:eb:8f:7e:78:fc:5b:8b:79:1c:d6:df:47: + a7:56:f4:98:4e:c7:a9:d5:0e:75:56:06:7f:b4:37:46:08:c6: + e9:4f:8b:5b:43:1c:e0:45:3e:95:20:71:c0:1c:98:16:ef:f2: + 78:df:ac:4d:bb:bf:56:0e:cf:85:af:cf:bf:04:ed:72:6b:fd: + 1f:57:0e:58:91:44:11:58:3b:62:3b:09:78:b3:a4:75:6a:ec: + b3:c2:2b:32:cc:b3:8d:c3:a3:6e:dc:8a:d5:e8:4a:c4:0b:7b: + db:30:5d:95:33:c3:d1:a3:69:64:5b:a8:aa:96:48:73:73:e3: + c9:b9:24:df:17:75:aa:af:07:3a:cf:be:9b:8a:80:a7:bf:7c: + e2:e9:2a:e6:fd:b0:2c:e7:e6:e6:7e:b3:35:15:65:00:f4:e1: + 39:73:0e:28:4b:f0:0c:98:9e:3a:eb:ce:7b:7a:9e:40:c1:50: + 65:96:9a:e7:4b:77:cd:dd:cb:7d:97:b4:ea:09:b2:e9:49:28: + c3:30:e0:87:15:f0:26:ea:d8:03:fd:ec:da:08:83:65:dc:77: + c5:6e:3d:34:f7:87:c3:1c:1d:26:33:ec:33:ac:c6:99:53:ab: + 60:f4:b0:d9:ee:64:5a:33:07:70:13:74:88:07:f5:86:f9:18: + d3:b2:47:c8:ae:03:4a:53:de:1c:65:d6:0a:2e:3a:51:93:ee: + b7:e3:6f:0a:fb:e9:fe:4e:e8:bb:1d:c2:97:ab:0a:b9:ed:36: + 32:1b:4d:a1:cc:03:a6:9d:b3:d9:1c:d5:67:e2:8f:74:3c:92: + 2a:74:b1:56:50:df:53:15:d7:21:d6:eb:f3:fb:63:e3:20:2c: + 0a:74:37:0b:c1:a1:35:6a:84:70:f4:45:f8:b2:b6:81:49:aa: + fd:54:45:90:4d:e7:04:07:5f:78:14:dd:3a:bb:2b:f9:72:50: + ec:68:ea:3c:a8:d1:80:bb:be:35:43:97:c3:32:b2:f5:aa:ad: + c9:7f:83:9f:7d:69:1e:15 +-----BEGIN CERTIFICATE----- +MIIFozCCA4ugAwIBAgIQdZbCPvqJWUVuefcXus9k8zANBgkqhkiG9w0BAQsFADBV +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV +BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0xNDExMDgw +MDU4NThaFw0yOTExMDgwMDU4NThaMFIxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX +b1NpZ24gQ0EgTGltaXRlZDEnMCUGA1UEAxMeV29TaWduIENsYXNzIDMgT1YgU2Vy +dmVyIENBIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1nSHr5nA +V5aZwol0PJJVmb8fBwA1BSaWFlsDwUI3M74/DU//u5QmkdcUFngb9xOiS0zlXKcQ +QDVZMNF3meOdKcK+MZW9kmFbsCP7Z1jVUuR7L/BzHHOUVbrIaFkCEBDk9xHww7bX +rlaAAJ5lZKaDkUHm7ad6ZaUfMC4TPL/fY5fzlvBSMrT0e5hX7TZP9yFKKJ3dHJKz +TY2cWIsXIdjcobeuc3iKxLbpfyiOmtUunjnp2ll048iXEDKUGVnUD4lXROblKxcw +YlKYf6sNpQHqBEHK+hMOO4cGur1HMddjAwH0vqE3EZ8eAZVODz9UHpKmnzCM/pjo +VpZmBOE1/lmsVwIDAQABo4IBcDCCAWwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdJQQW +MBQGCCsGAQUFBwMCBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMDAGA1Ud +HwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmxzMS53b3NpZ24uY29tL2NhMS5jcmwwbQYI +KwYBBQUHAQEEYTBfMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcDEud29zaWduLmNv +bS9jYTEwNAYIKwYBBQUHMAKGKGh0dHA6Ly9haWExLndvc2lnbi5jb20vY2ExZzIt +c2VydmVyMy5jZXIwHQYDVR0OBBYEFPmL7AQ4aj+qBsaUrXOVKrDI5rj7MB8GA1Ud +IwQYMBaAFOFmzw7R8bNLtwYgFP6HEtX2/vs+MEYGA1UdIAQ/MD0wOwYMKwYBBAGC +m1EGAwIBMCswKQYIKwYBBQUHAgEWHWh0dHA6Ly93d3cud29zaWduLmNvbS9wb2xp +Y3kvMA0GCSqGSIb3DQEBCwUAA4ICAQBeZ7p4MgW2t6/n3mp6gmQOoAvynpq6xitv +Vjq0YlerfK1gUJY0nKOIz9mPUK/28AA2Gx8fh1U8YJrwsA2agC2KO74Fs9eggLa4 +GetR2+xkVPEaiUpIoU0/MX3EeZRL8d6rg69fhr6WHLM+HOe8lrLoWqy1WMs8Vm8K +p6XQNomCJoy5H7brj354/FuLeRzW30enVvSYTsep1Q51VgZ/tDdGCMbpT4tbQxzg +RT6VIHHAHJgW7/J436xNu79WDs+Fr8+/BO1ya/0fVw5YkUQRWDtiOwl4s6R1auyz +wisyzLONw6Nu3IrV6ErEC3vbMF2VM8PRo2lkW6iqlkhzc+PJuSTfF3Wqrwc6z76b +ioCnv3zi6Srm/bAs5+bmfrM1FWUA9OE5cw4oS/AMmJ466857ep5AwVBllprnS3fN +3ct9l7TqCbLpSSjDMOCHFfAm6tgD/ezaCINl3HfFbj0094fDHB0mM+wzrMaZU6tg +9LDZ7mRaMwdwE3SIB/WG+RjTskfIrgNKU94cZdYKLjpRk+63428K++n+Tui7HcKX +qwq57TYyG02hzAOmnbPZHNVn4o90PJIqdLFWUN9TFdch1uvz+2PjICwKdDcLwaE1 +aoRw9EX4sraBSar9VEWQTecEB194FN06uyv5clDsaOo8qNGAu741Q5fDMrL1qq3J +f4OffWkeFQ== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert48[] = { + 0x30, 0x82, 0x05, 0xa3, 0x30, 0x82, 0x03, 0x8b, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x75, 0x96, 0xc2, 0x3e, 0xfa, 0x89, 0x59, 0x45, 0x6e, + 0x79, 0xf7, 0x17, 0xba, 0xcf, 0x64, 0xf3, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x55, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, + 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, + 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67, + 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x31, 0x30, 0x38, 0x30, + 0x30, 0x35, 0x38, 0x35, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x39, 0x31, 0x31, + 0x30, 0x38, 0x30, 0x30, 0x35, 0x38, 0x35, 0x38, 0x5a, 0x30, 0x52, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57, + 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x1e, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x4f, 0x56, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd6, 0x74, 0x87, 0xaf, 0x99, 0xc0, + 0x57, 0x96, 0x99, 0xc2, 0x89, 0x74, 0x3c, 0x92, 0x55, 0x99, 0xbf, 0x1f, + 0x07, 0x00, 0x35, 0x05, 0x26, 0x96, 0x16, 0x5b, 0x03, 0xc1, 0x42, 0x37, + 0x33, 0xbe, 0x3f, 0x0d, 0x4f, 0xff, 0xbb, 0x94, 0x26, 0x91, 0xd7, 0x14, + 0x16, 0x78, 0x1b, 0xf7, 0x13, 0xa2, 0x4b, 0x4c, 0xe5, 0x5c, 0xa7, 0x10, + 0x40, 0x35, 0x59, 0x30, 0xd1, 0x77, 0x99, 0xe3, 0x9d, 0x29, 0xc2, 0xbe, + 0x31, 0x95, 0xbd, 0x92, 0x61, 0x5b, 0xb0, 0x23, 0xfb, 0x67, 0x58, 0xd5, + 0x52, 0xe4, 0x7b, 0x2f, 0xf0, 0x73, 0x1c, 0x73, 0x94, 0x55, 0xba, 0xc8, + 0x68, 0x59, 0x02, 0x10, 0x10, 0xe4, 0xf7, 0x11, 0xf0, 0xc3, 0xb6, 0xd7, + 0xae, 0x56, 0x80, 0x00, 0x9e, 0x65, 0x64, 0xa6, 0x83, 0x91, 0x41, 0xe6, + 0xed, 0xa7, 0x7a, 0x65, 0xa5, 0x1f, 0x30, 0x2e, 0x13, 0x3c, 0xbf, 0xdf, + 0x63, 0x97, 0xf3, 0x96, 0xf0, 0x52, 0x32, 0xb4, 0xf4, 0x7b, 0x98, 0x57, + 0xed, 0x36, 0x4f, 0xf7, 0x21, 0x4a, 0x28, 0x9d, 0xdd, 0x1c, 0x92, 0xb3, + 0x4d, 0x8d, 0x9c, 0x58, 0x8b, 0x17, 0x21, 0xd8, 0xdc, 0xa1, 0xb7, 0xae, + 0x73, 0x78, 0x8a, 0xc4, 0xb6, 0xe9, 0x7f, 0x28, 0x8e, 0x9a, 0xd5, 0x2e, + 0x9e, 0x39, 0xe9, 0xda, 0x59, 0x74, 0xe3, 0xc8, 0x97, 0x10, 0x32, 0x94, + 0x19, 0x59, 0xd4, 0x0f, 0x89, 0x57, 0x44, 0xe6, 0xe5, 0x2b, 0x17, 0x30, + 0x62, 0x52, 0x98, 0x7f, 0xab, 0x0d, 0xa5, 0x01, 0xea, 0x04, 0x41, 0xca, + 0xfa, 0x13, 0x0e, 0x3b, 0x87, 0x06, 0xba, 0xbd, 0x47, 0x31, 0xd7, 0x63, + 0x03, 0x01, 0xf4, 0xbe, 0xa1, 0x37, 0x11, 0x9f, 0x1e, 0x01, 0x95, 0x4e, + 0x0f, 0x3f, 0x54, 0x1e, 0x92, 0xa6, 0x9f, 0x30, 0x8c, 0xfe, 0x98, 0xe8, + 0x56, 0x96, 0x66, 0x04, 0xe1, 0x35, 0xfe, 0x59, 0xac, 0x57, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x70, 0x30, 0x82, 0x01, 0x6c, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x12, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, + 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x73, + 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x6d, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x61, 0x30, 0x5f, + 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, + 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x61, 0x31, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x61, 0x69, 0x61, 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67, + 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x31, 0x67, 0x32, 0x2d, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x33, 0x2e, 0x63, 0x65, 0x72, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf9, 0x8b, + 0xec, 0x04, 0x38, 0x6a, 0x3f, 0xaa, 0x06, 0xc6, 0x94, 0xad, 0x73, 0x95, + 0x2a, 0xb0, 0xc8, 0xe6, 0xb8, 0xfb, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe1, 0x66, 0xcf, 0x0e, 0xd1, + 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14, 0xfe, 0x87, 0x12, 0xd5, 0xf6, + 0xfe, 0xfb, 0x3e, 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, + 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x0c, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x9b, 0x51, 0x06, 0x03, 0x02, 0x01, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1d, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x6f, 0x73, + 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x5e, + 0x67, 0xba, 0x78, 0x32, 0x05, 0xb6, 0xb7, 0xaf, 0xe7, 0xde, 0x6a, 0x7a, + 0x82, 0x64, 0x0e, 0xa0, 0x0b, 0xf2, 0x9e, 0x9a, 0xba, 0xc6, 0x2b, 0x6f, + 0x56, 0x3a, 0xb4, 0x62, 0x57, 0xab, 0x7c, 0xad, 0x60, 0x50, 0x96, 0x34, + 0x9c, 0xa3, 0x88, 0xcf, 0xd9, 0x8f, 0x50, 0xaf, 0xf6, 0xf0, 0x00, 0x36, + 0x1b, 0x1f, 0x1f, 0x87, 0x55, 0x3c, 0x60, 0x9a, 0xf0, 0xb0, 0x0d, 0x9a, + 0x80, 0x2d, 0x8a, 0x3b, 0xbe, 0x05, 0xb3, 0xd7, 0xa0, 0x80, 0xb6, 0xb8, + 0x19, 0xeb, 0x51, 0xdb, 0xec, 0x64, 0x54, 0xf1, 0x1a, 0x89, 0x4a, 0x48, + 0xa1, 0x4d, 0x3f, 0x31, 0x7d, 0xc4, 0x79, 0x94, 0x4b, 0xf1, 0xde, 0xab, + 0x83, 0xaf, 0x5f, 0x86, 0xbe, 0x96, 0x1c, 0xb3, 0x3e, 0x1c, 0xe7, 0xbc, + 0x96, 0xb2, 0xe8, 0x5a, 0xac, 0xb5, 0x58, 0xcb, 0x3c, 0x56, 0x6f, 0x0a, + 0xa7, 0xa5, 0xd0, 0x36, 0x89, 0x82, 0x26, 0x8c, 0xb9, 0x1f, 0xb6, 0xeb, + 0x8f, 0x7e, 0x78, 0xfc, 0x5b, 0x8b, 0x79, 0x1c, 0xd6, 0xdf, 0x47, 0xa7, + 0x56, 0xf4, 0x98, 0x4e, 0xc7, 0xa9, 0xd5, 0x0e, 0x75, 0x56, 0x06, 0x7f, + 0xb4, 0x37, 0x46, 0x08, 0xc6, 0xe9, 0x4f, 0x8b, 0x5b, 0x43, 0x1c, 0xe0, + 0x45, 0x3e, 0x95, 0x20, 0x71, 0xc0, 0x1c, 0x98, 0x16, 0xef, 0xf2, 0x78, + 0xdf, 0xac, 0x4d, 0xbb, 0xbf, 0x56, 0x0e, 0xcf, 0x85, 0xaf, 0xcf, 0xbf, + 0x04, 0xed, 0x72, 0x6b, 0xfd, 0x1f, 0x57, 0x0e, 0x58, 0x91, 0x44, 0x11, + 0x58, 0x3b, 0x62, 0x3b, 0x09, 0x78, 0xb3, 0xa4, 0x75, 0x6a, 0xec, 0xb3, + 0xc2, 0x2b, 0x32, 0xcc, 0xb3, 0x8d, 0xc3, 0xa3, 0x6e, 0xdc, 0x8a, 0xd5, + 0xe8, 0x4a, 0xc4, 0x0b, 0x7b, 0xdb, 0x30, 0x5d, 0x95, 0x33, 0xc3, 0xd1, + 0xa3, 0x69, 0x64, 0x5b, 0xa8, 0xaa, 0x96, 0x48, 0x73, 0x73, 0xe3, 0xc9, + 0xb9, 0x24, 0xdf, 0x17, 0x75, 0xaa, 0xaf, 0x07, 0x3a, 0xcf, 0xbe, 0x9b, + 0x8a, 0x80, 0xa7, 0xbf, 0x7c, 0xe2, 0xe9, 0x2a, 0xe6, 0xfd, 0xb0, 0x2c, + 0xe7, 0xe6, 0xe6, 0x7e, 0xb3, 0x35, 0x15, 0x65, 0x00, 0xf4, 0xe1, 0x39, + 0x73, 0x0e, 0x28, 0x4b, 0xf0, 0x0c, 0x98, 0x9e, 0x3a, 0xeb, 0xce, 0x7b, + 0x7a, 0x9e, 0x40, 0xc1, 0x50, 0x65, 0x96, 0x9a, 0xe7, 0x4b, 0x77, 0xcd, + 0xdd, 0xcb, 0x7d, 0x97, 0xb4, 0xea, 0x09, 0xb2, 0xe9, 0x49, 0x28, 0xc3, + 0x30, 0xe0, 0x87, 0x15, 0xf0, 0x26, 0xea, 0xd8, 0x03, 0xfd, 0xec, 0xda, + 0x08, 0x83, 0x65, 0xdc, 0x77, 0xc5, 0x6e, 0x3d, 0x34, 0xf7, 0x87, 0xc3, + 0x1c, 0x1d, 0x26, 0x33, 0xec, 0x33, 0xac, 0xc6, 0x99, 0x53, 0xab, 0x60, + 0xf4, 0xb0, 0xd9, 0xee, 0x64, 0x5a, 0x33, 0x07, 0x70, 0x13, 0x74, 0x88, + 0x07, 0xf5, 0x86, 0xf9, 0x18, 0xd3, 0xb2, 0x47, 0xc8, 0xae, 0x03, 0x4a, + 0x53, 0xde, 0x1c, 0x65, 0xd6, 0x0a, 0x2e, 0x3a, 0x51, 0x93, 0xee, 0xb7, + 0xe3, 0x6f, 0x0a, 0xfb, 0xe9, 0xfe, 0x4e, 0xe8, 0xbb, 0x1d, 0xc2, 0x97, + 0xab, 0x0a, 0xb9, 0xed, 0x36, 0x32, 0x1b, 0x4d, 0xa1, 0xcc, 0x03, 0xa6, + 0x9d, 0xb3, 0xd9, 0x1c, 0xd5, 0x67, 0xe2, 0x8f, 0x74, 0x3c, 0x92, 0x2a, + 0x74, 0xb1, 0x56, 0x50, 0xdf, 0x53, 0x15, 0xd7, 0x21, 0xd6, 0xeb, 0xf3, + 0xfb, 0x63, 0xe3, 0x20, 0x2c, 0x0a, 0x74, 0x37, 0x0b, 0xc1, 0xa1, 0x35, + 0x6a, 0x84, 0x70, 0xf4, 0x45, 0xf8, 0xb2, 0xb6, 0x81, 0x49, 0xaa, 0xfd, + 0x54, 0x45, 0x90, 0x4d, 0xe7, 0x04, 0x07, 0x5f, 0x78, 0x14, 0xdd, 0x3a, + 0xbb, 0x2b, 0xf9, 0x72, 0x50, 0xec, 0x68, 0xea, 0x3c, 0xa8, 0xd1, 0x80, + 0xbb, 0xbe, 0x35, 0x43, 0x97, 0xc3, 0x32, 0xb2, 0xf5, 0xaa, 0xad, 0xc9, + 0x7f, 0x83, 0x9f, 0x7d, 0x69, 0x1e, 0x15, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 120040007 (0x727aa47) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root + Validity + Not Before: May 7 17:04:09 2014 GMT + Not After : May 7 17:03:30 2018 GMT + Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2 + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24: + 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72: + 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88: + 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1: + d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03: + e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42: + f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe: + 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29: + e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18: + 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51: + af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41: + 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a: + 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a: + 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94: + 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67: + 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae: + ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be: + ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34: + 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f: + e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29: + 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d: + 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08: + 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf: + ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2: + 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08: + 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0: + 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70: + 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7: + d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09: + c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18: + 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33: + a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13: + 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01: + a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41: + 4a:a8:fb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Certificate Policies: + Policy: 1.3.6.1.4.1.6334.1.0 + CPS: http://cybertrust.omniroot.com/repository.cfm + Policy: 1.3.6.1.4.1.311.42.1 + + Authority Information Access: + OCSP - URI:http://ocsp.omniroot.com/baltimoreroot + + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication, OCSP Signing + X509v3 Authority Key Identifier: + keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl + + X509v3 Subject Key Identifier: + 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5 + Signature Algorithm: sha256WithRSAEncryption + 69:62:f6:84:91:00:c4:6f:82:7b:24:e1:42:a2:a5:8b:82:5c: + a7:c5:44:cb:e7:52:76:63:d3:76:9e:78:e2:69:35:b1:38:ba: + b0:96:c6:1f:ac:7b:c6:b2:65:77:8b:7d:8d:ae:64:b9:a5:8c: + 17:ca:58:65:c3:ad:82:f5:c5:a2:f5:01:13:93:c6:7e:44:e5: + c4:61:fa:03:b6:56:c1:72:e1:c8:28:c5:69:21:8f:ac:6e:fd: + 7f:43:83:36:b8:c0:d6:a0:28:fe:1a:45:be:fd:93:8c:8d:a4: + 64:79:1f:14:db:a1:9f:21:dc:c0:4e:7b:17:22:17:b1:b6:3c: + d3:9b:e2:0a:a3:7e:99:b0:c1:ac:d8:f4:86:df:3c:da:7d:14: + 9c:40:c1:7c:d2:18:6f:f1:4f:26:45:09:95:94:5c:da:d0:98: + f8:f4:4c:82:96:10:de:ac:30:cb:2b:ae:f9:92:ea:bf:79:03: + fc:1e:3f:ac:09:a4:3f:65:fd:91:4f:96:24:a7:ce:b4:4e:6a: + 96:29:17:ae:c0:a8:df:17:22:f4:17:e3:dc:1c:39:06:56:10: + ea:ea:b5:74:17:3c:4e:dd:7e:91:0a:a8:0b:78:07:a7:31:44: + 08:31:ab:18:84:0f:12:9c:e7:de:84:2c:e9:6d:93:45:bf:a8: + c1:3f:34:dc +-----BEGIN CERTIFICATE----- +MIIF4TCCBMmgAwIBAgIEByeqRzANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDUwNzE3MDQwOVoX +DTE4MDUwNzE3MDMzMFowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n +dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y +YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0 +IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3 +p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu +e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA +iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R +PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg +lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU +mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R +DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV +S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq +qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE +OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o +/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCAXswggF3MBIGA1Ud +EwEB/wQIMAYBAf8CAQAwYAYDVR0gBFkwVzBIBgkrBgEEAbE+AQAwOzA5BggrBgEF +BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku +Y2ZtMAsGCSsGAQQBgjcqATBCBggrBgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGGJmh0 +dHA6Ly9vY3NwLm9tbmlyb290LmNvbS9iYWx0aW1vcmVyb290MA4GA1UdDwEB/wQE +AwIBhjAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMJMB8G +A1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaGezq1BE3wMEIGA1UdHwQ7MDkwN6A1oDOG +MWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVzdC5jb20vQ1JML09tbmlyb290MjAyNS5j +cmwwHQYDVR0OBBYEFFGvJCac9GgiV4AmKztGYhV7HsylMA0GCSqGSIb3DQEBCwUA +A4IBAQBpYvaEkQDEb4J7JOFCoqWLglynxUTL51J2Y9N2nnjiaTWxOLqwlsYfrHvG +smV3i32NrmS5pYwXylhlw62C9cWi9QETk8Z+ROXEYfoDtlbBcuHIKMVpIY+sbv1/ +Q4M2uMDWoCj+GkW+/ZOMjaRkeR8U26GfIdzATnsXIhextjzTm+IKo36ZsMGs2PSG +3zzafRScQMF80hhv8U8mRQmVlFza0Jj49EyClhDerDDLK675kuq/eQP8Hj+sCaQ/ +Zf2RT5Ykp860TmqWKReuwKjfFyL0F+PcHDkGVhDq6rV0FzxO3X6RCqgLeAenMUQI +MasYhA8SnOfehCzpbZNFv6jBPzTc +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert49[] = { + 0x30, 0x82, 0x05, 0xe1, 0x30, 0x82, 0x04, 0xc9, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x04, 0x07, 0x27, 0xaa, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, + 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, + 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x34, 0x30, 0x39, 0x5a, 0x17, + 0x0d, 0x31, 0x38, 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x33, 0x33, + 0x30, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, + 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, + 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, + 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, + 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, + 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37, + 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37, + 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d, + 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10, + 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae, + 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30, + 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2, + 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77, + 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0, + 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9, + 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09, + 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57, + 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1, + 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb, + 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8, + 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21, + 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60, + 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb, + 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4, + 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a, + 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14, + 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae, + 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca, + 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99, + 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11, + 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b, + 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57, + 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02, + 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5, + 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c, + 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb, + 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90, + 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa, + 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d, + 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae, + 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86, + 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4, + 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a, + 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6, + 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8, + 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68, + 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0, + 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10, + 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x7b, 0x30, 0x82, 0x01, 0x77, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x60, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x59, 0x30, + 0x57, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, + 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, + 0x63, 0x66, 0x6d, 0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x2a, 0x01, 0x30, 0x42, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x01, 0x01, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x26, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x6f, 0x6d, + 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, + 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x86, 0x30, 0x27, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, + 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, + 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, + 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, + 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, + 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4, 0x68, 0x22, 0x57, 0x80, 0x26, + 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e, 0xcc, 0xa5, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x62, 0xf6, 0x84, 0x91, 0x00, 0xc4, + 0x6f, 0x82, 0x7b, 0x24, 0xe1, 0x42, 0xa2, 0xa5, 0x8b, 0x82, 0x5c, 0xa7, + 0xc5, 0x44, 0xcb, 0xe7, 0x52, 0x76, 0x63, 0xd3, 0x76, 0x9e, 0x78, 0xe2, + 0x69, 0x35, 0xb1, 0x38, 0xba, 0xb0, 0x96, 0xc6, 0x1f, 0xac, 0x7b, 0xc6, + 0xb2, 0x65, 0x77, 0x8b, 0x7d, 0x8d, 0xae, 0x64, 0xb9, 0xa5, 0x8c, 0x17, + 0xca, 0x58, 0x65, 0xc3, 0xad, 0x82, 0xf5, 0xc5, 0xa2, 0xf5, 0x01, 0x13, + 0x93, 0xc6, 0x7e, 0x44, 0xe5, 0xc4, 0x61, 0xfa, 0x03, 0xb6, 0x56, 0xc1, + 0x72, 0xe1, 0xc8, 0x28, 0xc5, 0x69, 0x21, 0x8f, 0xac, 0x6e, 0xfd, 0x7f, + 0x43, 0x83, 0x36, 0xb8, 0xc0, 0xd6, 0xa0, 0x28, 0xfe, 0x1a, 0x45, 0xbe, + 0xfd, 0x93, 0x8c, 0x8d, 0xa4, 0x64, 0x79, 0x1f, 0x14, 0xdb, 0xa1, 0x9f, + 0x21, 0xdc, 0xc0, 0x4e, 0x7b, 0x17, 0x22, 0x17, 0xb1, 0xb6, 0x3c, 0xd3, + 0x9b, 0xe2, 0x0a, 0xa3, 0x7e, 0x99, 0xb0, 0xc1, 0xac, 0xd8, 0xf4, 0x86, + 0xdf, 0x3c, 0xda, 0x7d, 0x14, 0x9c, 0x40, 0xc1, 0x7c, 0xd2, 0x18, 0x6f, + 0xf1, 0x4f, 0x26, 0x45, 0x09, 0x95, 0x94, 0x5c, 0xda, 0xd0, 0x98, 0xf8, + 0xf4, 0x4c, 0x82, 0x96, 0x10, 0xde, 0xac, 0x30, 0xcb, 0x2b, 0xae, 0xf9, + 0x92, 0xea, 0xbf, 0x79, 0x03, 0xfc, 0x1e, 0x3f, 0xac, 0x09, 0xa4, 0x3f, + 0x65, 0xfd, 0x91, 0x4f, 0x96, 0x24, 0xa7, 0xce, 0xb4, 0x4e, 0x6a, 0x96, + 0x29, 0x17, 0xae, 0xc0, 0xa8, 0xdf, 0x17, 0x22, 0xf4, 0x17, 0xe3, 0xdc, + 0x1c, 0x39, 0x06, 0x56, 0x10, 0xea, 0xea, 0xb5, 0x74, 0x17, 0x3c, 0x4e, + 0xdd, 0x7e, 0x91, 0x0a, 0xa8, 0x0b, 0x78, 0x07, 0xa7, 0x31, 0x44, 0x08, + 0x31, 0xab, 0x18, 0x84, 0x0f, 0x12, 0x9c, 0xe7, 0xde, 0x84, 0x2c, 0xe9, + 0x6d, 0x93, 0x45, 0xbf, 0xa8, 0xc1, 0x3f, 0x34, 0xdc, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 13:8b:fe:f3:32:94:f9:d8:16:f9:45:c2:71:95:29:98 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority + Validity + Not Before: Dec 16 01:00:05 2015 GMT + Not After : Dec 16 01:00:05 2030 GMT + Subject: C=IL, O=StartCom Ltd., OU=StartCom Certification Authority, CN=StartCom Class 3 OV Server CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:af:67:1c:6f:e5:45:e0:d7:46:4b:75:2c:b6:80: + f2:9a:17:4d:2d:ff:de:ae:d2:d4:00:8a:3a:b8:31: + fe:8e:37:9e:fa:aa:d5:a3:5b:16:12:c1:19:3e:34: + 85:96:c3:be:d3:b3:43:f4:8d:6f:16:bd:30:ba:07: + fc:d8:9a:c1:79:89:80:6d:a0:8c:be:dd:37:f7:eb: + 05:d3:53:7f:57:58:76:55:b6:a8:a8:86:44:b8:bb: + d0:13:da:fd:8f:e1:f2:cd:a0:15:38:55:56:ce:26: + cf:7c:93:75:29:7a:0a:ab:fb:ba:09:38:20:11:57: + 07:5d:7f:49:9f:2a:4a:67:1e:9e:58:e9:c7:7f:f9: + c3:ed:fe:5f:4d:af:b8:4f:9d:df:69:2d:69:1b:3a: + 58:81:69:63:30:ea:87:8d:0f:52:9d:5a:da:39:44: + ba:9f:89:9f:36:b6:c2:19:5c:d9:26:78:d9:ae:5e: + fc:95:90:bf:e8:11:c0:47:0f:77:89:dd:6a:28:4f: + 0a:bc:32:64:57:43:3d:08:65:93:e5:45:ae:dd:28: + 0c:27:2c:8e:a6:2b:09:03:5d:a1:78:d2:8c:ab:b6: + 6b:b9:46:c9:19:00:39:b9:bf:c6:13:2b:73:72:1f: + f2:3e:37:b8:e8:b9:14:65:88:4d:e2:f1:1b:d8:a5: + 1d:3b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Extended Key Usage: + TLS Web Client Authentication, TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.startssl.com/sfsca.crl + + Authority Information Access: + OCSP - URI:http://ocsp.startssl.com + CA Issuers - URI:http://aia.startssl.com/certs/ca.crt + + X509v3 Subject Key Identifier: + B1:3F:1C:92:7B:92:B0:5A:25:B3:38:FB:9C:07:A4:26:50:32:E3:51 + X509v3 Authority Key Identifier: + keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2 + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.startssl.com/policy + + Signature Algorithm: sha256WithRSAEncryption + 85:f2:e8:14:d3:1b:c1:a1:16:1d:a4:f4:4d:ba:51:8b:5c:52: + b1:54:54:12:16:17:9c:96:78:6f:d3:bf:df:43:36:f5:12:89: + 61:72:44:df:1c:9b:09:4f:60:26:68:c1:e6:66:50:70:b3:6a: + f1:a8:6a:0c:1e:2e:93:f1:ee:07:3e:09:dd:30:45:b2:56:8e: + dc:2c:5c:ab:49:fa:b9:04:03:40:15:7a:b5:30:e0:1d:91:8f: + a6:d6:6f:1f:99:a0:84:95:39:bd:ac:77:7f:72:4b:dd:2d:ae: + ff:a8:58:1d:46:27:d4:83:c7:69:64:9f:19:bb:10:f8:04:42: + 87:59:5d:02:b1:d6:e5:c8:da:43:30:a3:e8:37:a5:d2:48:0b: + a2:83:4e:9d:4f:83:58:9d:d7:47:22:b1:89:f0:89:3b:3d:28: + 43:2c:9b:17:7c:03:ee:9d:26:25:e0:04:b8:1d:04:57:42:47: + da:58:69:f0:d3:29:ab:12:02:99:2b:2a:d8:9d:a0:1f:54:5e: + 23:9a:0c:d2:99:58:c4:a1:e5:49:c2:25:a7:64:20:52:2e:e7: + 89:f5:19:c0:8b:d0:63:b1:78:1e:be:01:47:be:76:81:46:f1: + 99:1f:94:9a:be:fa:82:15:b5:84:84:79:75:93:ba:9f:b5:e4: + 9b:c2:cb:69:5c:bd:1f:55:0a:a7:26:30:05:51:be:65:ee:57: + a9:6a:df:bd:f9:36:2f:ad:1e:46:41:2b:b1:88:d0:88:25:85: + 40:17:79:bf:3d:8d:e2:f4:2d:ea:30:31:df:a1:40:cb:35:ff: + 82:9f:f5:99:3c:4a:fd:9d:a1:d1:55:cc:20:a8:1c:d8:20:05: + ab:b3:14:65:95:53:d8:e8:8e:57:c5:77:6b:2d:4d:88:e9:5d: + 62:d5:a2:f8:70:e1:70:eb:45:23:0e:f0:00:46:c2:48:31:e8: + e7:36:80:36:2d:22:f2:01:27:53:eb:ce:a7:69:49:82:bf:e7: + 0f:9c:f3:20:2e:f5:fa:5d:ce:ea:58:3a:8f:d8:aa:7d:30:b7: + 74:96:7c:3d:6e:b4:ec:4a:3b:59:b6:a9:50:0d:0f:05:06:70: + 26:b9:95:91:d1:5e:24:8c:8f:ca:74:57:97:90:8b:5a:b7:fe: + 8d:ad:d8:e8:c2:06:bc:08:56:21:02:12:53:c6:9f:86:04:58: + ca:2d:f8:03:0d:57:0b:1c:37:bd:f0:5a:35:f2:fe:3b:d6:a4: + 37:15:e9:f8:08:92:96:3d:74:c8:b5:5c:6e:65:08:e7:df:69: + 73:9c:ec:e3:30:5a:a6:df:5c:be:da:7f:00:ee:a5:da:2b:5c: + 1e:2a:6a:c0:a3:ae:1e:f1 +-----BEGIN CERTIFICATE----- +MIIF5TCCA82gAwIBAgIQE4v+8zKU+dgW+UXCcZUpmDANBgkqhkiG9w0BAQsFADB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTUxMjE2MDEwMDA1WhcN +MzAxMjE2MDEwMDA1WjB4MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20g +THRkLjEpMCcGA1UECxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +JjAkBgNVBAMTHVN0YXJ0Q29tIENsYXNzIDMgT1YgU2VydmVyIENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr2ccb+VF4NdGS3UstoDymhdNLf/ertLU +AIo6uDH+jjee+qrVo1sWEsEZPjSFlsO+07ND9I1vFr0wugf82JrBeYmAbaCMvt03 +9+sF01N/V1h2VbaoqIZEuLvQE9r9j+HyzaAVOFVWzibPfJN1KXoKq/u6CTggEVcH +XX9JnypKZx6eWOnHf/nD7f5fTa+4T53faS1pGzpYgWljMOqHjQ9SnVraOUS6n4mf +NrbCGVzZJnjZrl78lZC/6BHARw93id1qKE8KvDJkV0M9CGWT5UWu3SgMJyyOpisJ +A12heNKMq7ZruUbJGQA5ub/GEytzch/yPje46LkUZYhN4vEb2KUdOwIDAQABo4IB +ZDCCAWAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEF +BQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6 +Ly9jcmwuc3RhcnRzc2wuY29tL3Nmc2NhLmNybDBmBggrBgEFBQcBAQRaMFgwJAYI +KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbTAwBggrBgEFBQcwAoYk +aHR0cDovL2FpYS5zdGFydHNzbC5jb20vY2VydHMvY2EuY3J0MB0GA1UdDgQWBBSx +PxySe5KwWiWzOPucB6QmUDLjUTAfBgNVHSMEGDAWgBROC+8apEBbpRdphzDKNGhD +0EGu8jA/BgNVHSAEODA2MDQGBFUdIAAwLDAqBggrBgEFBQcCARYeaHR0cDovL3d3 +dy5zdGFydHNzbC5jb20vcG9saWN5MA0GCSqGSIb3DQEBCwUAA4ICAQCF8ugU0xvB +oRYdpPRNulGLXFKxVFQSFheclnhv07/fQzb1EolhckTfHJsJT2AmaMHmZlBws2rx +qGoMHi6T8e4HPgndMEWyVo7cLFyrSfq5BANAFXq1MOAdkY+m1m8fmaCElTm9rHd/ +ckvdLa7/qFgdRifUg8dpZJ8ZuxD4BEKHWV0CsdblyNpDMKPoN6XSSAuig06dT4NY +nddHIrGJ8Ik7PShDLJsXfAPunSYl4AS4HQRXQkfaWGnw0ymrEgKZKyrYnaAfVF4j +mgzSmVjEoeVJwiWnZCBSLueJ9RnAi9BjsXgevgFHvnaBRvGZH5SavvqCFbWEhHl1 +k7qfteSbwstpXL0fVQqnJjAFUb5l7lepat+9+TYvrR5GQSuxiNCIJYVAF3m/PY3i +9C3qMDHfoUDLNf+Cn/WZPEr9naHRVcwgqBzYIAWrsxRllVPY6I5XxXdrLU2I6V1i +1aL4cOFw60UjDvAARsJIMejnNoA2LSLyASdT686naUmCv+cPnPMgLvX6Xc7qWDqP +2Kp9MLd0lnw9brTsSjtZtqlQDQ8FBnAmuZWR0V4kjI/KdFeXkItat/6Nrdjowga8 +CFYhAhJTxp+GBFjKLfgDDVcLHDe98Fo18v471qQ3Fen4CJKWPXTItVxuZQjn32lz +nOzjMFqm31y+2n8A7qXaK1weKmrAo64e8Q== +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert50[] = { + 0x30, 0x82, 0x05, 0xe5, 0x30, 0x82, 0x03, 0xcd, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x13, 0x8b, 0xfe, 0xf3, 0x32, 0x94, 0xf9, 0xd8, 0x16, + 0xf9, 0x45, 0xc2, 0x71, 0x95, 0x29, 0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7d, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, + 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, + 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, + 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, + 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x31, + 0x32, 0x31, 0x36, 0x30, 0x31, 0x30, 0x30, 0x30, 0x35, 0x5a, 0x17, 0x0d, + 0x33, 0x30, 0x31, 0x32, 0x31, 0x36, 0x30, 0x31, 0x30, 0x30, 0x30, 0x35, + 0x5a, 0x30, 0x78, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, + 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, + 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1d, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x20, 0x33, 0x20, 0x4f, 0x56, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xaf, 0x67, 0x1c, 0x6f, 0xe5, 0x45, 0xe0, 0xd7, 0x46, 0x4b, 0x75, 0x2c, + 0xb6, 0x80, 0xf2, 0x9a, 0x17, 0x4d, 0x2d, 0xff, 0xde, 0xae, 0xd2, 0xd4, + 0x00, 0x8a, 0x3a, 0xb8, 0x31, 0xfe, 0x8e, 0x37, 0x9e, 0xfa, 0xaa, 0xd5, + 0xa3, 0x5b, 0x16, 0x12, 0xc1, 0x19, 0x3e, 0x34, 0x85, 0x96, 0xc3, 0xbe, + 0xd3, 0xb3, 0x43, 0xf4, 0x8d, 0x6f, 0x16, 0xbd, 0x30, 0xba, 0x07, 0xfc, + 0xd8, 0x9a, 0xc1, 0x79, 0x89, 0x80, 0x6d, 0xa0, 0x8c, 0xbe, 0xdd, 0x37, + 0xf7, 0xeb, 0x05, 0xd3, 0x53, 0x7f, 0x57, 0x58, 0x76, 0x55, 0xb6, 0xa8, + 0xa8, 0x86, 0x44, 0xb8, 0xbb, 0xd0, 0x13, 0xda, 0xfd, 0x8f, 0xe1, 0xf2, + 0xcd, 0xa0, 0x15, 0x38, 0x55, 0x56, 0xce, 0x26, 0xcf, 0x7c, 0x93, 0x75, + 0x29, 0x7a, 0x0a, 0xab, 0xfb, 0xba, 0x09, 0x38, 0x20, 0x11, 0x57, 0x07, + 0x5d, 0x7f, 0x49, 0x9f, 0x2a, 0x4a, 0x67, 0x1e, 0x9e, 0x58, 0xe9, 0xc7, + 0x7f, 0xf9, 0xc3, 0xed, 0xfe, 0x5f, 0x4d, 0xaf, 0xb8, 0x4f, 0x9d, 0xdf, + 0x69, 0x2d, 0x69, 0x1b, 0x3a, 0x58, 0x81, 0x69, 0x63, 0x30, 0xea, 0x87, + 0x8d, 0x0f, 0x52, 0x9d, 0x5a, 0xda, 0x39, 0x44, 0xba, 0x9f, 0x89, 0x9f, + 0x36, 0xb6, 0xc2, 0x19, 0x5c, 0xd9, 0x26, 0x78, 0xd9, 0xae, 0x5e, 0xfc, + 0x95, 0x90, 0xbf, 0xe8, 0x11, 0xc0, 0x47, 0x0f, 0x77, 0x89, 0xdd, 0x6a, + 0x28, 0x4f, 0x0a, 0xbc, 0x32, 0x64, 0x57, 0x43, 0x3d, 0x08, 0x65, 0x93, + 0xe5, 0x45, 0xae, 0xdd, 0x28, 0x0c, 0x27, 0x2c, 0x8e, 0xa6, 0x2b, 0x09, + 0x03, 0x5d, 0xa1, 0x78, 0xd2, 0x8c, 0xab, 0xb6, 0x6b, 0xb9, 0x46, 0xc9, + 0x19, 0x00, 0x39, 0xb9, 0xbf, 0xc6, 0x13, 0x2b, 0x73, 0x72, 0x1f, 0xf2, + 0x3e, 0x37, 0xb8, 0xe8, 0xb9, 0x14, 0x65, 0x88, 0x4d, 0xe2, 0xf1, 0x1b, + 0xd8, 0xa5, 0x1d, 0x3b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, + 0x64, 0x30, 0x82, 0x01, 0x60, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, + 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x01, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, + 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, + 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, + 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63, 0x61, + 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x66, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x01, 0x01, 0x04, 0x5a, 0x30, 0x58, 0x30, 0x24, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x30, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x24, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x69, 0x61, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, + 0x3f, 0x1c, 0x92, 0x7b, 0x92, 0xb0, 0x5a, 0x25, 0xb3, 0x38, 0xfb, 0x9c, + 0x07, 0xa4, 0x26, 0x50, 0x32, 0xe3, 0x51, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, 0x1a, + 0xa4, 0x40, 0x5b, 0xa5, 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, 0x43, + 0xd0, 0x41, 0xae, 0xf2, 0x30, 0x3f, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x38, 0x30, 0x36, 0x30, 0x34, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, + 0x2c, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x02, 0x01, 0x00, 0x85, 0xf2, 0xe8, 0x14, 0xd3, 0x1b, 0xc1, + 0xa1, 0x16, 0x1d, 0xa4, 0xf4, 0x4d, 0xba, 0x51, 0x8b, 0x5c, 0x52, 0xb1, + 0x54, 0x54, 0x12, 0x16, 0x17, 0x9c, 0x96, 0x78, 0x6f, 0xd3, 0xbf, 0xdf, + 0x43, 0x36, 0xf5, 0x12, 0x89, 0x61, 0x72, 0x44, 0xdf, 0x1c, 0x9b, 0x09, + 0x4f, 0x60, 0x26, 0x68, 0xc1, 0xe6, 0x66, 0x50, 0x70, 0xb3, 0x6a, 0xf1, + 0xa8, 0x6a, 0x0c, 0x1e, 0x2e, 0x93, 0xf1, 0xee, 0x07, 0x3e, 0x09, 0xdd, + 0x30, 0x45, 0xb2, 0x56, 0x8e, 0xdc, 0x2c, 0x5c, 0xab, 0x49, 0xfa, 0xb9, + 0x04, 0x03, 0x40, 0x15, 0x7a, 0xb5, 0x30, 0xe0, 0x1d, 0x91, 0x8f, 0xa6, + 0xd6, 0x6f, 0x1f, 0x99, 0xa0, 0x84, 0x95, 0x39, 0xbd, 0xac, 0x77, 0x7f, + 0x72, 0x4b, 0xdd, 0x2d, 0xae, 0xff, 0xa8, 0x58, 0x1d, 0x46, 0x27, 0xd4, + 0x83, 0xc7, 0x69, 0x64, 0x9f, 0x19, 0xbb, 0x10, 0xf8, 0x04, 0x42, 0x87, + 0x59, 0x5d, 0x02, 0xb1, 0xd6, 0xe5, 0xc8, 0xda, 0x43, 0x30, 0xa3, 0xe8, + 0x37, 0xa5, 0xd2, 0x48, 0x0b, 0xa2, 0x83, 0x4e, 0x9d, 0x4f, 0x83, 0x58, + 0x9d, 0xd7, 0x47, 0x22, 0xb1, 0x89, 0xf0, 0x89, 0x3b, 0x3d, 0x28, 0x43, + 0x2c, 0x9b, 0x17, 0x7c, 0x03, 0xee, 0x9d, 0x26, 0x25, 0xe0, 0x04, 0xb8, + 0x1d, 0x04, 0x57, 0x42, 0x47, 0xda, 0x58, 0x69, 0xf0, 0xd3, 0x29, 0xab, + 0x12, 0x02, 0x99, 0x2b, 0x2a, 0xd8, 0x9d, 0xa0, 0x1f, 0x54, 0x5e, 0x23, + 0x9a, 0x0c, 0xd2, 0x99, 0x58, 0xc4, 0xa1, 0xe5, 0x49, 0xc2, 0x25, 0xa7, + 0x64, 0x20, 0x52, 0x2e, 0xe7, 0x89, 0xf5, 0x19, 0xc0, 0x8b, 0xd0, 0x63, + 0xb1, 0x78, 0x1e, 0xbe, 0x01, 0x47, 0xbe, 0x76, 0x81, 0x46, 0xf1, 0x99, + 0x1f, 0x94, 0x9a, 0xbe, 0xfa, 0x82, 0x15, 0xb5, 0x84, 0x84, 0x79, 0x75, + 0x93, 0xba, 0x9f, 0xb5, 0xe4, 0x9b, 0xc2, 0xcb, 0x69, 0x5c, 0xbd, 0x1f, + 0x55, 0x0a, 0xa7, 0x26, 0x30, 0x05, 0x51, 0xbe, 0x65, 0xee, 0x57, 0xa9, + 0x6a, 0xdf, 0xbd, 0xf9, 0x36, 0x2f, 0xad, 0x1e, 0x46, 0x41, 0x2b, 0xb1, + 0x88, 0xd0, 0x88, 0x25, 0x85, 0x40, 0x17, 0x79, 0xbf, 0x3d, 0x8d, 0xe2, + 0xf4, 0x2d, 0xea, 0x30, 0x31, 0xdf, 0xa1, 0x40, 0xcb, 0x35, 0xff, 0x82, + 0x9f, 0xf5, 0x99, 0x3c, 0x4a, 0xfd, 0x9d, 0xa1, 0xd1, 0x55, 0xcc, 0x20, + 0xa8, 0x1c, 0xd8, 0x20, 0x05, 0xab, 0xb3, 0x14, 0x65, 0x95, 0x53, 0xd8, + 0xe8, 0x8e, 0x57, 0xc5, 0x77, 0x6b, 0x2d, 0x4d, 0x88, 0xe9, 0x5d, 0x62, + 0xd5, 0xa2, 0xf8, 0x70, 0xe1, 0x70, 0xeb, 0x45, 0x23, 0x0e, 0xf0, 0x00, + 0x46, 0xc2, 0x48, 0x31, 0xe8, 0xe7, 0x36, 0x80, 0x36, 0x2d, 0x22, 0xf2, + 0x01, 0x27, 0x53, 0xeb, 0xce, 0xa7, 0x69, 0x49, 0x82, 0xbf, 0xe7, 0x0f, + 0x9c, 0xf3, 0x20, 0x2e, 0xf5, 0xfa, 0x5d, 0xce, 0xea, 0x58, 0x3a, 0x8f, + 0xd8, 0xaa, 0x7d, 0x30, 0xb7, 0x74, 0x96, 0x7c, 0x3d, 0x6e, 0xb4, 0xec, + 0x4a, 0x3b, 0x59, 0xb6, 0xa9, 0x50, 0x0d, 0x0f, 0x05, 0x06, 0x70, 0x26, + 0xb9, 0x95, 0x91, 0xd1, 0x5e, 0x24, 0x8c, 0x8f, 0xca, 0x74, 0x57, 0x97, + 0x90, 0x8b, 0x5a, 0xb7, 0xfe, 0x8d, 0xad, 0xd8, 0xe8, 0xc2, 0x06, 0xbc, + 0x08, 0x56, 0x21, 0x02, 0x12, 0x53, 0xc6, 0x9f, 0x86, 0x04, 0x58, 0xca, + 0x2d, 0xf8, 0x03, 0x0d, 0x57, 0x0b, 0x1c, 0x37, 0xbd, 0xf0, 0x5a, 0x35, + 0xf2, 0xfe, 0x3b, 0xd6, 0xa4, 0x37, 0x15, 0xe9, 0xf8, 0x08, 0x92, 0x96, + 0x3d, 0x74, 0xc8, 0xb5, 0x5c, 0x6e, 0x65, 0x08, 0xe7, 0xdf, 0x69, 0x73, + 0x9c, 0xec, 0xe3, 0x30, 0x5a, 0xa6, 0xdf, 0x5c, 0xbe, 0xda, 0x7f, 0x00, + 0xee, 0xa5, 0xda, 0x2b, 0x5c, 0x1e, 0x2a, 0x6a, 0xc0, 0xa3, 0xae, 0x1e, + 0xf1, +}; + +#if 0 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 7250751724796726 (0x19c28530e93b36) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority + Validity + Not Before: Sep 17 22:46:36 2006 GMT + Not After : Dec 31 23:59:59 2019 GMT + Subject: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:bd:ca:8d:ac:b8:91:15:56:97:7b:6b:5c:7a:c2: + de:6b:d9:a1:b0:c3:10:23:fa:a7:a1:b2:cc:31:fa: + 3e:d9:a6:29:6f:16:3d:e0:6b:f8:b8:40:5f:db:39: + a8:00:7a:8b:a0:4d:54:7d:c2:22:78:fc:8e:09:b8: + a8:85:d7:cc:95:97:4b:74:d8:9e:7e:f0:00:e4:0e: + 89:ae:49:28:44:1a:10:99:32:0f:25:88:53:a4:0d: + b3:0f:12:08:16:0b:03:71:27:1c:7f:e1:db:d2:fd: + 67:68:c4:05:5d:0a:0e:5d:70:d7:d8:97:a0:bc:53: + 41:9a:91:8d:f4:9e:36:66:7a:7e:56:c1:90:5f:e6: + b1:68:20:36:a4:8c:24:2c:2c:47:0b:59:76:66:30: + b5:be:de:ed:8f:f8:9d:d3:bb:01:30:e6:f2:f3:0e: + e0:2c:92:80:f3:85:f9:28:8a:b4:54:2e:9a:ed:f7: + 76:fc:15:68:16:eb:4a:6c:eb:2e:12:8f:d4:cf:fe: + 0c:c7:5c:1d:0b:7e:05:32:be:5e:b0:09:2a:42:d5: + c9:4e:90:b3:59:0d:bb:7a:7e:cd:d5:08:5a:b4:7f: + d8:1c:69:11:f9:27:0f:7b:06:af:54:83:18:7b:e1: + dd:54:7a:51:68:6e:77:fc:c6:bf:52:4a:66:46:a1: + b2:67:1a:bb:a3:4f:77:a0:be:5d:ff:fc:56:0b:43: + 72:77:90:ca:9e:f9:f2:39:f5:0d:a9:f4:ea:d7:e7: + b3:10:2f:30:42:37:21:cc:30:70:c9:86:98:0f:cc: + 58:4d:83:bb:7d:e5:1a:a5:37:8d:b6:ac:32:97:00: + 3a:63:71:24:1e:9e:37:c4:ff:74:d4:37:c0:e2:fe: + 88:46:60:11:dd:08:3f:50:36:ab:b8:7a:a4:95:62: + 6a:6e:b0:ca:6a:21:5a:69:f3:f3:fb:1d:70:39:95: + f3:a7:6e:a6:81:89:a1:88:c5:3b:71:ca:a3:52:ee: + 83:bb:fd:a0:77:f4:e4:6f:e7:42:db:6d:4a:99:8a: + 34:48:bc:17:dc:e4:80:08:22:b6:f2:31:c0:3f:04: + 3e:eb:9f:20:79:d6:b8:06:64:64:02:31:d7:a9:cd: + 52:fb:84:45:69:09:00:2a:dc:55:8b:c4:06:46:4b: + c0:4a:1d:09:5b:39:28:fd:a9:ab:ce:00:f9:2e:48: + 4b:26:e6:30:4c:a5:58:ca:b4:44:82:4f:e7:91:1e: + 33:c3:b0:93:ff:11:fc:81:d2:ca:1f:71:29:dd:76: + 4f:92:25:af:1d:81:b7:0f:2f:8c:c3:06:cc:2f:27: + a3:4a:e4:0e:99:ba:7c:1e:45:1f:7f:aa:19:45:96: + fd:fc:3d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:2 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E + X509v3 Authority Key Identifier: + keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2 + + Authority Information Access: + OCSP - URI:http://ocsp.startssl.com/ca + CA Issuers - URI:http://aia.startssl.com/certs/ca.crt + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl.startssl.com/sfsca.crl + + Signature Algorithm: sha256WithRSAEncryption + b6:6d:f8:70:fb:e2:0d:4c:98:b3:07:49:15:f5:04:c4:6c:ca: + ca:f5:68:a0:08:fe:12:6d:9c:04:06:c9:ad:9a:91:52:3e:78: + c4:5c:ee:9f:54:1d:ee:e3:f1:5e:30:c9:49:e1:39:e0:a6:9d: + 36:6c:57:fa:e6:34:4f:55:e8:87:a8:2c:dd:05:f1:58:12:91: + e8:ca:ce:28:78:8f:df:07:85:01:a5:dc:45:96:05:d4:80:b2: + 2b:05:9a:cb:9a:a5:8b:e0:3a:67:e6:73:47:be:4a:fd:27:b1: + 88:ef:e6:ca:cf:8d:0e:26:9f:fa:5f:57:78:ad:6d:fe:ae:9b: + 35:08:b1:c3:ba:c1:00:4a:4b:7d:14:bd:f7:f1:d3:55:18:ac: + d0:33:70:88:6d:c4:09:71:14:a6:2b:4f:88:81:e7:0b:00:37: + a9:15:7d:7e:d7:01:96:3f:2f:af:7b:62:ae:0a:4a:bf:4b:39: + 2e:35:10:8b:fe:04:39:e4:3c:3a:0c:09:56:40:3a:b5:f4:c2: + 68:0c:b5:f9:52:cd:ee:9d:f8:98:fc:78:e7:58:47:8f:1c:73: + 58:69:33:ab:ff:dd:df:8e:24:01:77:98:19:3a:b0:66:79:bc: + e1:08:a3:0e:4f:c1:04:b3:f3:01:c8:eb:d3:59:1c:35:d2:93: + 1e:70:65:82:7f:db:cf:fb:c8:99:12:60:c3:44:6f:3a:80:4b: + d7:be:21:aa:14:7a:64:cb:dd:37:43:45:5b:32:2e:45:f0:d9: + 59:1f:6b:18:f0:7c:e9:55:36:19:61:5f:b5:7d:f1:8d:bd:88: + e4:75:4b:98:dd:27:b0:e4:84:44:2a:61:84:57:05:82:11:1f: + aa:35:58:f3:20:0e:af:59:ef:fa:55:72:72:0d:26:d0:9b:53: + 49:ac:ce:37:2e:65:61:ff:f6:ec:1b:ea:f6:f1:a6:d3:d1:b5: + 7b:be:35:f4:22:c1:bc:8d:01:bd:68:5e:83:0d:2f:ec:d6:da: + 63:0c:27:d1:54:3e:e4:a8:d3:ce:4b:32:b8:91:94:ff:fb:5b: + 49:2d:75:18:a8:ba:71:9a:3b:ae:d9:c0:a9:4f:87:91:ed:8b: + 7b:6b:20:98:89:39:83:4f:80:c4:69:cc:17:c9:c8:4e:be:e4: + a9:a5:81:76:70:06:04:32:cd:83:65:f4:bc:7d:3e:13:bc:d2: + e8:6f:63:aa:b5:3b:da:8d:86:32:82:78:9d:d9:cc:ff:bf:57: + 64:74:ed:28:3d:44:62:15:61:4b:f7:94:b0:0d:2a:67:1c:f0: + cb:9b:a5:92:bf:f8:41:5a:c1:3d:60:ed:9f:bb:b8:6d:9b:ce: + a9:6a:16:3f:7e:ea:06:f1 +-----BEGIN CERTIFICATE----- +MIIGXDCCBESgAwIBAgIHGcKFMOk7NjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQG +EwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERp +Z2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MjI0NjM2WhcNMTkxMjMxMjM1 +OTU5WjBVMQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQx +KjAoBgNVBAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL3Kjay4kRVWl3trXHrC3mvZobDD +ECP6p6GyzDH6PtmmKW8WPeBr+LhAX9s5qAB6i6BNVH3CInj8jgm4qIXXzJWXS3TY +nn7wAOQOia5JKEQaEJkyDyWIU6QNsw8SCBYLA3EnHH/h29L9Z2jEBV0KDl1w19iX +oLxTQZqRjfSeNmZ6flbBkF/msWggNqSMJCwsRwtZdmYwtb7e7Y/4ndO7ATDm8vMO +4CySgPOF+SiKtFQumu33dvwVaBbrSmzrLhKP1M/+DMdcHQt+BTK+XrAJKkLVyU6Q +s1kNu3p+zdUIWrR/2BxpEfknD3sGr1SDGHvh3VR6UWhud/zGv1JKZkahsmcau6NP +d6C+Xf/8VgtDcneQyp758jn1Dan06tfnsxAvMEI3IcwwcMmGmA/MWE2Du33lGqU3 +jbasMpcAOmNxJB6eN8T/dNQ3wOL+iEZgEd0IP1A2q7h6pJViam6wymohWmnz8/sd +cDmV86dupoGJoYjFO3HKo1Lug7v9oHf05G/nQtttSpmKNEi8F9zkgAgitvIxwD8E +PuufIHnWuAZkZAIx16nNUvuERWkJACrcVYvEBkZLwEodCVs5KP2pq84A+S5ISybm +MEylWMq0RIJP55EeM8Owk/8R/IHSyh9xKd12T5Ilrx2Btw8vjMMGzC8no0rkDpm6 +fB5FH3+qGUWW/fw9AgMBAAGjggEHMIIBAzASBgNVHRMBAf8ECDAGAQH/AgECMA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU4WbPDtHxs0u3BiAU/ocS1fb++z4wHwYD +VR0jBBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIwaQYIKwYBBQUHAQEEXTBbMCcG +CCsGAQUFBzABhhtodHRwOi8vb2NzcC5zdGFydHNzbC5jb20vY2EwMAYIKwYBBQUH +MAKGJGh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2NlcnRzL2NhLmNydDAyBgNVHR8E +KzApMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwDQYJ +KoZIhvcNAQELBQADggIBALZt+HD74g1MmLMHSRX1BMRsysr1aKAI/hJtnAQGya2a +kVI+eMRc7p9UHe7j8V4wyUnhOeCmnTZsV/rmNE9V6IeoLN0F8VgSkejKzih4j98H +hQGl3EWWBdSAsisFmsuapYvgOmfmc0e+Sv0nsYjv5srPjQ4mn/pfV3itbf6umzUI +scO6wQBKS30Uvffx01UYrNAzcIhtxAlxFKYrT4iB5wsAN6kVfX7XAZY/L697Yq4K +Sr9LOS41EIv+BDnkPDoMCVZAOrX0wmgMtflSze6d+Jj8eOdYR48cc1hpM6v/3d+O +JAF3mBk6sGZ5vOEIow5PwQSz8wHI69NZHDXSkx5wZYJ/28/7yJkSYMNEbzqAS9e+ +IaoUemTL3TdDRVsyLkXw2VkfaxjwfOlVNhlhX7V98Y29iOR1S5jdJ7DkhEQqYYRX +BYIRH6o1WPMgDq9Z7/pVcnINJtCbU0mszjcuZWH/9uwb6vbxptPRtXu+NfQiwbyN +Ab1oXoMNL+zW2mMMJ9FUPuSo085LMriRlP/7W0ktdRiounGaO67ZwKlPh5Hti3tr +IJiJOYNPgMRpzBfJyE6+5KmlgXZwBgQyzYNl9Lx9PhO80uhvY6q1O9qNhjKCeJ3Z +zP+/V2R07Sg9RGIVYUv3lLANKmcc8MubpZK/+EFawT1g7Z+7uG2bzqlqFj9+6gbx +-----END CERTIFICATE----- +#endif +static const unsigned char kDERCert51[] = { + 0x30, 0x82, 0x06, 0x5c, 0x30, 0x82, 0x04, 0x44, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x07, 0x19, 0xc2, 0x85, 0x30, 0xe9, 0x3b, 0x36, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, + 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, + 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, + 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, + 0x30, 0x36, 0x30, 0x39, 0x31, 0x37, 0x32, 0x32, 0x34, 0x36, 0x33, 0x36, + 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, + 0x39, 0x35, 0x39, 0x5a, 0x30, 0x55, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, + 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, + 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66, + 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x30, 0x82, 0x02, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, + 0x82, 0x02, 0x01, 0x00, 0xbd, 0xca, 0x8d, 0xac, 0xb8, 0x91, 0x15, 0x56, + 0x97, 0x7b, 0x6b, 0x5c, 0x7a, 0xc2, 0xde, 0x6b, 0xd9, 0xa1, 0xb0, 0xc3, + 0x10, 0x23, 0xfa, 0xa7, 0xa1, 0xb2, 0xcc, 0x31, 0xfa, 0x3e, 0xd9, 0xa6, + 0x29, 0x6f, 0x16, 0x3d, 0xe0, 0x6b, 0xf8, 0xb8, 0x40, 0x5f, 0xdb, 0x39, + 0xa8, 0x00, 0x7a, 0x8b, 0xa0, 0x4d, 0x54, 0x7d, 0xc2, 0x22, 0x78, 0xfc, + 0x8e, 0x09, 0xb8, 0xa8, 0x85, 0xd7, 0xcc, 0x95, 0x97, 0x4b, 0x74, 0xd8, + 0x9e, 0x7e, 0xf0, 0x00, 0xe4, 0x0e, 0x89, 0xae, 0x49, 0x28, 0x44, 0x1a, + 0x10, 0x99, 0x32, 0x0f, 0x25, 0x88, 0x53, 0xa4, 0x0d, 0xb3, 0x0f, 0x12, + 0x08, 0x16, 0x0b, 0x03, 0x71, 0x27, 0x1c, 0x7f, 0xe1, 0xdb, 0xd2, 0xfd, + 0x67, 0x68, 0xc4, 0x05, 0x5d, 0x0a, 0x0e, 0x5d, 0x70, 0xd7, 0xd8, 0x97, + 0xa0, 0xbc, 0x53, 0x41, 0x9a, 0x91, 0x8d, 0xf4, 0x9e, 0x36, 0x66, 0x7a, + 0x7e, 0x56, 0xc1, 0x90, 0x5f, 0xe6, 0xb1, 0x68, 0x20, 0x36, 0xa4, 0x8c, + 0x24, 0x2c, 0x2c, 0x47, 0x0b, 0x59, 0x76, 0x66, 0x30, 0xb5, 0xbe, 0xde, + 0xed, 0x8f, 0xf8, 0x9d, 0xd3, 0xbb, 0x01, 0x30, 0xe6, 0xf2, 0xf3, 0x0e, + 0xe0, 0x2c, 0x92, 0x80, 0xf3, 0x85, 0xf9, 0x28, 0x8a, 0xb4, 0x54, 0x2e, + 0x9a, 0xed, 0xf7, 0x76, 0xfc, 0x15, 0x68, 0x16, 0xeb, 0x4a, 0x6c, 0xeb, + 0x2e, 0x12, 0x8f, 0xd4, 0xcf, 0xfe, 0x0c, 0xc7, 0x5c, 0x1d, 0x0b, 0x7e, + 0x05, 0x32, 0xbe, 0x5e, 0xb0, 0x09, 0x2a, 0x42, 0xd5, 0xc9, 0x4e, 0x90, + 0xb3, 0x59, 0x0d, 0xbb, 0x7a, 0x7e, 0xcd, 0xd5, 0x08, 0x5a, 0xb4, 0x7f, + 0xd8, 0x1c, 0x69, 0x11, 0xf9, 0x27, 0x0f, 0x7b, 0x06, 0xaf, 0x54, 0x83, + 0x18, 0x7b, 0xe1, 0xdd, 0x54, 0x7a, 0x51, 0x68, 0x6e, 0x77, 0xfc, 0xc6, + 0xbf, 0x52, 0x4a, 0x66, 0x46, 0xa1, 0xb2, 0x67, 0x1a, 0xbb, 0xa3, 0x4f, + 0x77, 0xa0, 0xbe, 0x5d, 0xff, 0xfc, 0x56, 0x0b, 0x43, 0x72, 0x77, 0x90, + 0xca, 0x9e, 0xf9, 0xf2, 0x39, 0xf5, 0x0d, 0xa9, 0xf4, 0xea, 0xd7, 0xe7, + 0xb3, 0x10, 0x2f, 0x30, 0x42, 0x37, 0x21, 0xcc, 0x30, 0x70, 0xc9, 0x86, + 0x98, 0x0f, 0xcc, 0x58, 0x4d, 0x83, 0xbb, 0x7d, 0xe5, 0x1a, 0xa5, 0x37, + 0x8d, 0xb6, 0xac, 0x32, 0x97, 0x00, 0x3a, 0x63, 0x71, 0x24, 0x1e, 0x9e, + 0x37, 0xc4, 0xff, 0x74, 0xd4, 0x37, 0xc0, 0xe2, 0xfe, 0x88, 0x46, 0x60, + 0x11, 0xdd, 0x08, 0x3f, 0x50, 0x36, 0xab, 0xb8, 0x7a, 0xa4, 0x95, 0x62, + 0x6a, 0x6e, 0xb0, 0xca, 0x6a, 0x21, 0x5a, 0x69, 0xf3, 0xf3, 0xfb, 0x1d, + 0x70, 0x39, 0x95, 0xf3, 0xa7, 0x6e, 0xa6, 0x81, 0x89, 0xa1, 0x88, 0xc5, + 0x3b, 0x71, 0xca, 0xa3, 0x52, 0xee, 0x83, 0xbb, 0xfd, 0xa0, 0x77, 0xf4, + 0xe4, 0x6f, 0xe7, 0x42, 0xdb, 0x6d, 0x4a, 0x99, 0x8a, 0x34, 0x48, 0xbc, + 0x17, 0xdc, 0xe4, 0x80, 0x08, 0x22, 0xb6, 0xf2, 0x31, 0xc0, 0x3f, 0x04, + 0x3e, 0xeb, 0x9f, 0x20, 0x79, 0xd6, 0xb8, 0x06, 0x64, 0x64, 0x02, 0x31, + 0xd7, 0xa9, 0xcd, 0x52, 0xfb, 0x84, 0x45, 0x69, 0x09, 0x00, 0x2a, 0xdc, + 0x55, 0x8b, 0xc4, 0x06, 0x46, 0x4b, 0xc0, 0x4a, 0x1d, 0x09, 0x5b, 0x39, + 0x28, 0xfd, 0xa9, 0xab, 0xce, 0x00, 0xf9, 0x2e, 0x48, 0x4b, 0x26, 0xe6, + 0x30, 0x4c, 0xa5, 0x58, 0xca, 0xb4, 0x44, 0x82, 0x4f, 0xe7, 0x91, 0x1e, + 0x33, 0xc3, 0xb0, 0x93, 0xff, 0x11, 0xfc, 0x81, 0xd2, 0xca, 0x1f, 0x71, + 0x29, 0xdd, 0x76, 0x4f, 0x92, 0x25, 0xaf, 0x1d, 0x81, 0xb7, 0x0f, 0x2f, + 0x8c, 0xc3, 0x06, 0xcc, 0x2f, 0x27, 0xa3, 0x4a, 0xe4, 0x0e, 0x99, 0xba, + 0x7c, 0x1e, 0x45, 0x1f, 0x7f, 0xaa, 0x19, 0x45, 0x96, 0xfd, 0xfc, 0x3d, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x07, 0x30, 0x82, 0x01, + 0x03, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, + 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xe1, 0x66, 0xcf, 0x0e, 0xd1, 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14, + 0xfe, 0x87, 0x12, 0xd5, 0xf6, 0xfe, 0xfb, 0x3e, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, + 0x1a, 0xa4, 0x40, 0x5b, 0xa5, 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, + 0x43, 0xd0, 0x41, 0xae, 0xf2, 0x30, 0x69, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x5d, 0x30, 0x5b, 0x30, 0x27, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x61, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x02, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, + 0x69, 0x61, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x63, 0x61, + 0x2e, 0x63, 0x72, 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, + 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x02, 0x01, 0x00, 0xb6, 0x6d, 0xf8, 0x70, 0xfb, 0xe2, 0x0d, 0x4c, + 0x98, 0xb3, 0x07, 0x49, 0x15, 0xf5, 0x04, 0xc4, 0x6c, 0xca, 0xca, 0xf5, + 0x68, 0xa0, 0x08, 0xfe, 0x12, 0x6d, 0x9c, 0x04, 0x06, 0xc9, 0xad, 0x9a, + 0x91, 0x52, 0x3e, 0x78, 0xc4, 0x5c, 0xee, 0x9f, 0x54, 0x1d, 0xee, 0xe3, + 0xf1, 0x5e, 0x30, 0xc9, 0x49, 0xe1, 0x39, 0xe0, 0xa6, 0x9d, 0x36, 0x6c, + 0x57, 0xfa, 0xe6, 0x34, 0x4f, 0x55, 0xe8, 0x87, 0xa8, 0x2c, 0xdd, 0x05, + 0xf1, 0x58, 0x12, 0x91, 0xe8, 0xca, 0xce, 0x28, 0x78, 0x8f, 0xdf, 0x07, + 0x85, 0x01, 0xa5, 0xdc, 0x45, 0x96, 0x05, 0xd4, 0x80, 0xb2, 0x2b, 0x05, + 0x9a, 0xcb, 0x9a, 0xa5, 0x8b, 0xe0, 0x3a, 0x67, 0xe6, 0x73, 0x47, 0xbe, + 0x4a, 0xfd, 0x27, 0xb1, 0x88, 0xef, 0xe6, 0xca, 0xcf, 0x8d, 0x0e, 0x26, + 0x9f, 0xfa, 0x5f, 0x57, 0x78, 0xad, 0x6d, 0xfe, 0xae, 0x9b, 0x35, 0x08, + 0xb1, 0xc3, 0xba, 0xc1, 0x00, 0x4a, 0x4b, 0x7d, 0x14, 0xbd, 0xf7, 0xf1, + 0xd3, 0x55, 0x18, 0xac, 0xd0, 0x33, 0x70, 0x88, 0x6d, 0xc4, 0x09, 0x71, + 0x14, 0xa6, 0x2b, 0x4f, 0x88, 0x81, 0xe7, 0x0b, 0x00, 0x37, 0xa9, 0x15, + 0x7d, 0x7e, 0xd7, 0x01, 0x96, 0x3f, 0x2f, 0xaf, 0x7b, 0x62, 0xae, 0x0a, + 0x4a, 0xbf, 0x4b, 0x39, 0x2e, 0x35, 0x10, 0x8b, 0xfe, 0x04, 0x39, 0xe4, + 0x3c, 0x3a, 0x0c, 0x09, 0x56, 0x40, 0x3a, 0xb5, 0xf4, 0xc2, 0x68, 0x0c, + 0xb5, 0xf9, 0x52, 0xcd, 0xee, 0x9d, 0xf8, 0x98, 0xfc, 0x78, 0xe7, 0x58, + 0x47, 0x8f, 0x1c, 0x73, 0x58, 0x69, 0x33, 0xab, 0xff, 0xdd, 0xdf, 0x8e, + 0x24, 0x01, 0x77, 0x98, 0x19, 0x3a, 0xb0, 0x66, 0x79, 0xbc, 0xe1, 0x08, + 0xa3, 0x0e, 0x4f, 0xc1, 0x04, 0xb3, 0xf3, 0x01, 0xc8, 0xeb, 0xd3, 0x59, + 0x1c, 0x35, 0xd2, 0x93, 0x1e, 0x70, 0x65, 0x82, 0x7f, 0xdb, 0xcf, 0xfb, + 0xc8, 0x99, 0x12, 0x60, 0xc3, 0x44, 0x6f, 0x3a, 0x80, 0x4b, 0xd7, 0xbe, + 0x21, 0xaa, 0x14, 0x7a, 0x64, 0xcb, 0xdd, 0x37, 0x43, 0x45, 0x5b, 0x32, + 0x2e, 0x45, 0xf0, 0xd9, 0x59, 0x1f, 0x6b, 0x18, 0xf0, 0x7c, 0xe9, 0x55, + 0x36, 0x19, 0x61, 0x5f, 0xb5, 0x7d, 0xf1, 0x8d, 0xbd, 0x88, 0xe4, 0x75, + 0x4b, 0x98, 0xdd, 0x27, 0xb0, 0xe4, 0x84, 0x44, 0x2a, 0x61, 0x84, 0x57, + 0x05, 0x82, 0x11, 0x1f, 0xaa, 0x35, 0x58, 0xf3, 0x20, 0x0e, 0xaf, 0x59, + 0xef, 0xfa, 0x55, 0x72, 0x72, 0x0d, 0x26, 0xd0, 0x9b, 0x53, 0x49, 0xac, + 0xce, 0x37, 0x2e, 0x65, 0x61, 0xff, 0xf6, 0xec, 0x1b, 0xea, 0xf6, 0xf1, + 0xa6, 0xd3, 0xd1, 0xb5, 0x7b, 0xbe, 0x35, 0xf4, 0x22, 0xc1, 0xbc, 0x8d, + 0x01, 0xbd, 0x68, 0x5e, 0x83, 0x0d, 0x2f, 0xec, 0xd6, 0xda, 0x63, 0x0c, + 0x27, 0xd1, 0x54, 0x3e, 0xe4, 0xa8, 0xd3, 0xce, 0x4b, 0x32, 0xb8, 0x91, + 0x94, 0xff, 0xfb, 0x5b, 0x49, 0x2d, 0x75, 0x18, 0xa8, 0xba, 0x71, 0x9a, + 0x3b, 0xae, 0xd9, 0xc0, 0xa9, 0x4f, 0x87, 0x91, 0xed, 0x8b, 0x7b, 0x6b, + 0x20, 0x98, 0x89, 0x39, 0x83, 0x4f, 0x80, 0xc4, 0x69, 0xcc, 0x17, 0xc9, + 0xc8, 0x4e, 0xbe, 0xe4, 0xa9, 0xa5, 0x81, 0x76, 0x70, 0x06, 0x04, 0x32, + 0xcd, 0x83, 0x65, 0xf4, 0xbc, 0x7d, 0x3e, 0x13, 0xbc, 0xd2, 0xe8, 0x6f, + 0x63, 0xaa, 0xb5, 0x3b, 0xda, 0x8d, 0x86, 0x32, 0x82, 0x78, 0x9d, 0xd9, + 0xcc, 0xff, 0xbf, 0x57, 0x64, 0x74, 0xed, 0x28, 0x3d, 0x44, 0x62, 0x15, + 0x61, 0x4b, 0xf7, 0x94, 0xb0, 0x0d, 0x2a, 0x67, 0x1c, 0xf0, 0xcb, 0x9b, + 0xa5, 0x92, 0xbf, 0xf8, 0x41, 0x5a, 0xc1, 0x3d, 0x60, 0xed, 0x9f, 0xbb, + 0xb8, 0x6d, 0x9b, 0xce, 0xa9, 0x6a, 0x16, 0x3f, 0x7e, 0xea, 0x06, 0xf1, +};
diff --git a/quic/core/crypto/common_cert_set_test.cc b/quic/core/crypto/common_cert_set_test.cc new file mode 100644 index 0000000..04720e1 --- /dev/null +++ b/quic/core/crypto/common_cert_set_test.cc
@@ -0,0 +1,249 @@ +// Copyright (c) 2013 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/crypto/common_cert_set.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +// Google Internet Authority cert from v2 of the cert set. +static const unsigned char kGIACertificate2[] = { + 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, + 0x34, 0x30, 0x35, 0x31, 0x35, 0x31, 0x35, 0x35, 0x36, 0x5a, 0x17, 0x0d, + 0x31, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, + 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, + 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3, + 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a, + 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a, + 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f, + 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1, + 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4, + 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53, + 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f, + 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73, + 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b, + 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb, + 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4, + 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19, + 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65, + 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80, + 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99, + 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75, + 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e, + 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf, + 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2, + 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37, + 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, + 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81, + 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, + 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, + 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10, + 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, + 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0xaa, 0xfa, 0xa9, 0x20, 0xcd, 0x6a, 0x67, 0x83, 0xed, 0x5e, 0xd4, 0x7e, + 0xde, 0x1d, 0xc4, 0x7f, 0xe0, 0x25, 0x06, 0x00, 0xc5, 0x24, 0xfb, 0xa9, + 0xc8, 0x2d, 0x6d, 0x7e, 0xde, 0x9d, 0x82, 0x65, 0x2c, 0x81, 0x63, 0x34, + 0x66, 0x3e, 0xe9, 0x52, 0xc2, 0x08, 0xb4, 0xcb, 0x2f, 0xf7, 0x5f, 0x99, + 0x3a, 0x6a, 0x9c, 0x50, 0x7a, 0x85, 0x05, 0x8c, 0x7d, 0xd1, 0x2a, 0x48, + 0x84, 0xd3, 0x09, 0x6c, 0x7c, 0xc2, 0xcd, 0x35, 0x9f, 0xf3, 0x82, 0xee, + 0x52, 0xde, 0x68, 0x5f, 0xe4, 0x00, 0x8a, 0x17, 0x20, 0x96, 0xf7, 0x29, + 0x8d, 0x9a, 0x4d, 0xcb, 0xa8, 0xde, 0x86, 0xc8, 0x0d, 0x6f, 0x56, 0x87, + 0x03, 0x7d, 0x03, 0x3f, 0xdc, 0xfa, 0x79, 0x7d, 0x21, 0x19, 0xf9, 0xc8, + 0x3a, 0x2f, 0x51, 0x76, 0x8c, 0xc7, 0x41, 0x92, 0x71, 0x8f, 0x25, 0xce, + 0x37, 0xf8, 0x4a, 0x4c, 0x00, 0x23, 0xef, 0xc4, 0x35, 0x10, 0xae, 0xe0, + 0x23, 0x80, 0x73, 0x7c, 0x4d, 0x34, 0x2e, 0xc8, 0x6e, 0x90, 0xd6, 0x10, + 0x1e, 0x99, 0x84, 0x73, 0x1a, 0x70, 0xf2, 0xed, 0x55, 0x0e, 0xee, 0x17, + 0x06, 0xea, 0x67, 0xee, 0x32, 0xeb, 0x2c, 0xdd, 0x67, 0x07, 0x3f, 0xf6, + 0x8b, 0xc2, 0x70, 0xde, 0x5b, 0x00, 0xe6, 0xbb, 0x1b, 0xd3, 0x36, 0x1a, + 0x22, 0x6c, 0x6c, 0xb0, 0x35, 0x42, 0x6c, 0x90, 0x09, 0x3d, 0x93, 0xe9, + 0x64, 0x09, 0x22, 0x0e, 0x85, 0x06, 0x9f, 0xc2, 0x73, 0x21, 0xd3, 0xe6, + 0x5f, 0x80, 0xe4, 0x8d, 0x85, 0x22, 0x3a, 0x73, 0x03, 0xb1, 0x60, 0x8e, + 0xae, 0x68, 0xe2, 0xf4, 0x3e, 0x97, 0xe7, 0x60, 0x12, 0x09, 0x68, 0x36, + 0xde, 0x3a, 0xd6, 0xe2, 0x43, 0x95, 0x5b, 0x37, 0x81, 0x92, 0x81, 0x1f, + 0xbb, 0x8d, 0xd7, 0xad, 0x52, 0x64, 0x16, 0x57, 0x96, 0xd9, 0x5e, 0x34, + 0x7e, 0xc8, 0x35, 0xd8, +}; + +// Google Internet Authority cert from v3 of the cert set. +static const unsigned char kGIACertificate3[] = { + 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x02, 0x3a, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, + 0x34, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, + 0x31, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, + 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, + 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3, + 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a, + 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a, + 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f, + 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1, + 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4, + 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53, + 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f, + 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73, + 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b, + 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb, + 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4, + 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19, + 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65, + 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80, + 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99, + 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75, + 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e, + 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf, + 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2, + 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37, + 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, + 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, + 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81, + 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03, + 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, + 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, + 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, + 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10, + 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, + 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x08, 0x4e, 0x04, 0xa7, 0x80, 0x7f, 0x10, 0x16, 0x43, 0x5e, 0x02, 0xad, + 0xd7, 0x42, 0x80, 0xf4, 0xb0, 0x8e, 0xd2, 0xae, 0xb3, 0xeb, 0x11, 0x7d, + 0x90, 0x84, 0x18, 0x7d, 0xe7, 0x90, 0x15, 0xfb, 0x49, 0x7f, 0xa8, 0x99, + 0x05, 0x91, 0xbb, 0x7a, 0xc9, 0xd6, 0x3c, 0x37, 0x18, 0x09, 0x9a, 0xb6, + 0xc7, 0x92, 0x20, 0x07, 0x35, 0x33, 0x09, 0xe4, 0x28, 0x63, 0x72, 0x0d, + 0xb4, 0xe0, 0x32, 0x9c, 0x87, 0x98, 0xc4, 0x1b, 0x76, 0x89, 0x67, 0xc1, + 0x50, 0x58, 0xb0, 0x13, 0xaa, 0x13, 0x1a, 0x1b, 0x32, 0xa5, 0xbe, 0xea, + 0x11, 0x95, 0x4c, 0x48, 0x63, 0x49, 0xe9, 0x99, 0x5d, 0x20, 0x37, 0xcc, + 0xfe, 0x2a, 0x69, 0x51, 0x16, 0x95, 0x4b, 0xa9, 0xde, 0x49, 0x82, 0xc0, + 0x10, 0x70, 0xf4, 0x2c, 0xf3, 0xec, 0xbc, 0x24, 0x24, 0xd0, 0x4e, 0xac, + 0xa5, 0xd9, 0x5e, 0x1e, 0x6d, 0x92, 0xc1, 0xa7, 0xac, 0x48, 0x35, 0x81, + 0xf9, 0xe5, 0xe4, 0x9c, 0x65, 0x69, 0xcd, 0x87, 0xa4, 0x41, 0x50, 0x3f, + 0x2e, 0x57, 0xa5, 0x91, 0x51, 0x12, 0x58, 0x0e, 0x8c, 0x09, 0xa1, 0xac, + 0x7a, 0xa4, 0x12, 0xa5, 0x27, 0xf3, 0x9a, 0x10, 0x97, 0x7d, 0x55, 0x03, + 0x06, 0xf7, 0x66, 0x58, 0x5f, 0x5f, 0x64, 0xe1, 0xab, 0x5d, 0x6d, 0xa5, + 0x39, 0x48, 0x75, 0x98, 0x4c, 0x29, 0x5a, 0x3a, 0x8d, 0xd3, 0x2b, 0xca, + 0x9c, 0x55, 0x04, 0xbf, 0xf4, 0xe6, 0x14, 0xd5, 0x80, 0xac, 0x26, 0xed, + 0x17, 0x89, 0xa6, 0x93, 0x6c, 0x5c, 0xa4, 0xcc, 0xb8, 0xf0, 0x66, 0x8e, + 0x64, 0xe3, 0x7d, 0x9a, 0xe2, 0x00, 0xb3, 0x49, 0xc7, 0xe4, 0x0a, 0xaa, + 0xdd, 0x5b, 0x83, 0xc7, 0x70, 0x90, 0x46, 0x4e, 0xbe, 0xd0, 0xdb, 0x59, + 0x96, 0x6c, 0x2e, 0xf5, 0x16, 0x36, 0xde, 0x71, 0xcc, 0x01, 0xc2, 0x12, + 0xc1, 0x21, 0xc6, 0x16, +}; + +class CommonCertSetsTest : public QuicTest {}; + +TEST_F(CommonCertSetsTest, FindGIA_2) { + QuicStringPiece gia(reinterpret_cast<const char*>(kGIACertificate2), + sizeof(kGIACertificate2)); + + const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC()); + // Common Cert Set 2's hash. + const uint64_t in_hash = UINT64_C(0xe81a92926081e801); + uint64_t hash; + uint32_t index; + ASSERT_TRUE(sets->MatchCert( + gia, + QuicStringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)), + &hash, &index)); + EXPECT_EQ(in_hash, hash); + + QuicStringPiece gia_copy = sets->GetCert(hash, index); + EXPECT_FALSE(gia_copy.empty()); + ASSERT_EQ(gia.size(), gia_copy.size()); + EXPECT_EQ(0, memcmp(gia.data(), gia_copy.data(), gia.size())); +} + +TEST_F(CommonCertSetsTest, FindGIA_3) { + QuicStringPiece gia(reinterpret_cast<const char*>(kGIACertificate3), + sizeof(kGIACertificate3)); + + const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC()); + // Common Cert Set 3's hash. + const uint64_t in_hash = UINT64_C(0x918215a28680ed7e); + uint64_t hash; + uint32_t index; + ASSERT_TRUE(sets->MatchCert( + gia, + QuicStringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)), + &hash, &index)); + EXPECT_EQ(in_hash, hash); + + QuicStringPiece gia_copy = sets->GetCert(hash, index); + EXPECT_FALSE(gia_copy.empty()); + ASSERT_EQ(gia.size(), gia_copy.size()); + EXPECT_EQ(0, memcmp(gia.data(), gia_copy.data(), gia.size())); +} + +TEST_F(CommonCertSetsTest, NonMatch) { + const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC()); + QuicStringPiece not_a_cert("hello"); + const uint64_t in_hash = UINT64_C(0xc9fef74053f99f39); + uint64_t hash; + uint32_t index; + EXPECT_FALSE(sets->MatchCert( + not_a_cert, + QuicStringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)), + &hash, &index)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/crypto_framer.cc b/quic/core/crypto/crypto_framer.cc new file mode 100644 index 0000000..dfd9eab --- /dev/null +++ b/quic/core/crypto/crypto_framer.cc
@@ -0,0 +1,352 @@ +// Copyright (c) 2012 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/crypto/crypto_framer.h" + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +namespace { + +const size_t kQuicTagSize = sizeof(QuicTag); +const size_t kCryptoEndOffsetSize = sizeof(uint32_t); +const size_t kNumEntriesSize = sizeof(uint16_t); + +// OneShotVisitor is a framer visitor that records a single handshake message. +class OneShotVisitor : public CryptoFramerVisitorInterface { + public: + OneShotVisitor() : error_(false) {} + + void OnError(CryptoFramer* framer) override { error_ = true; } + + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override { + out_ = QuicMakeUnique<CryptoHandshakeMessage>(message); + } + + bool error() const { return error_; } + + std::unique_ptr<CryptoHandshakeMessage> release() { return std::move(out_); } + + private: + std::unique_ptr<CryptoHandshakeMessage> out_; + bool error_; +}; + +} // namespace + +CryptoFramer::CryptoFramer() + : visitor_(nullptr), + error_detail_(""), + num_entries_(0), + values_len_(0), + process_truncated_messages_(false) { + Clear(); +} + +CryptoFramer::~CryptoFramer() {} + +// static +std::unique_ptr<CryptoHandshakeMessage> CryptoFramer::ParseMessage( + QuicStringPiece in) { + OneShotVisitor visitor; + CryptoFramer framer; + + framer.set_visitor(&visitor); + if (!framer.ProcessInput(in) || visitor.error() || + framer.InputBytesRemaining()) { + return nullptr; + } + + return visitor.release(); +} + +QuicErrorCode CryptoFramer::error() const { + return error_; +} + +const QuicString& CryptoFramer::error_detail() const { + return error_detail_; +} + +bool CryptoFramer::ProcessInput(QuicStringPiece input, EncryptionLevel level) { + return ProcessInput(input); +} + +bool CryptoFramer::ProcessInput(QuicStringPiece input) { + DCHECK_EQ(QUIC_NO_ERROR, error_); + if (error_ != QUIC_NO_ERROR) { + return false; + } + error_ = Process(input); + if (error_ != QUIC_NO_ERROR) { + DCHECK(!error_detail_.empty()); + visitor_->OnError(this); + return false; + } + + return true; +} + +size_t CryptoFramer::InputBytesRemaining() const { + return buffer_.length(); +} + +bool CryptoFramer::HasTag(QuicTag tag) const { + if (state_ != STATE_READING_VALUES) { + return false; + } + for (const auto& it : tags_and_lengths_) { + if (it.first == tag) { + return true; + } + } + return false; +} + +void CryptoFramer::ForceHandshake() { + QuicDataReader reader(buffer_.data(), buffer_.length(), HOST_BYTE_ORDER); + for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) { + QuicStringPiece value; + if (reader.BytesRemaining() < item.second) { + break; + } + reader.ReadStringPiece(&value, item.second); + message_.SetStringPiece(item.first, value); + } + visitor_->OnHandshakeMessage(message_); +} + +// static +QuicData* CryptoFramer::ConstructHandshakeMessage( + const CryptoHandshakeMessage& message) { + size_t num_entries = message.tag_value_map().size(); + size_t pad_length = 0; + bool need_pad_tag = false; + bool need_pad_value = false; + + size_t len = message.size(); + if (len < message.minimum_size()) { + need_pad_tag = true; + need_pad_value = true; + num_entries++; + + size_t delta = message.minimum_size() - len; + const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize; + if (delta > overhead) { + pad_length = delta - overhead; + } + len += overhead + pad_length; + } + + if (num_entries > kMaxEntries) { + return nullptr; + } + + std::unique_ptr<char[]> buffer(new char[len]); + QuicDataWriter writer(len, buffer.get(), HOST_BYTE_ORDER); + if (!writer.WriteTag(message.tag())) { + DCHECK(false) << "Failed to write message tag."; + return nullptr; + } + if (!writer.WriteUInt16(static_cast<uint16_t>(num_entries))) { + DCHECK(false) << "Failed to write size."; + return nullptr; + } + if (!writer.WriteUInt16(0)) { + DCHECK(false) << "Failed to write padding."; + return nullptr; + } + + uint32_t end_offset = 0; + // Tags and offsets + for (auto it = message.tag_value_map().begin(); + it != message.tag_value_map().end(); ++it) { + if (it->first == kPAD && need_pad_tag) { + // Existing PAD tags are only checked when padding needs to be added + // because parts of the code may need to reserialize received messages + // and those messages may, legitimately include padding. + DCHECK(false) << "Message needed padding but already contained a PAD tag"; + return nullptr; + } + + if (it->first > kPAD && need_pad_tag) { + need_pad_tag = false; + if (!WritePadTag(&writer, pad_length, &end_offset)) { + return nullptr; + } + } + + if (!writer.WriteTag(it->first)) { + DCHECK(false) << "Failed to write tag."; + return nullptr; + } + end_offset += it->second.length(); + if (!writer.WriteUInt32(end_offset)) { + DCHECK(false) << "Failed to write end offset."; + return nullptr; + } + } + + if (need_pad_tag) { + if (!WritePadTag(&writer, pad_length, &end_offset)) { + return nullptr; + } + } + + // Values + for (auto it = message.tag_value_map().begin(); + it != message.tag_value_map().end(); ++it) { + if (it->first > kPAD && need_pad_value) { + need_pad_value = false; + if (!writer.WriteRepeatedByte('-', pad_length)) { + DCHECK(false) << "Failed to write padding."; + return nullptr; + } + } + + if (!writer.WriteBytes(it->second.data(), it->second.length())) { + DCHECK(false) << "Failed to write value."; + return nullptr; + } + } + + if (need_pad_value) { + if (!writer.WriteRepeatedByte('-', pad_length)) { + DCHECK(false) << "Failed to write padding."; + return nullptr; + } + } + + return new QuicData(buffer.release(), len, true); +} + +void CryptoFramer::Clear() { + message_.Clear(); + tags_and_lengths_.clear(); + error_ = QUIC_NO_ERROR; + error_detail_ = ""; + state_ = STATE_READING_TAG; +} + +QuicErrorCode CryptoFramer::Process(QuicStringPiece input) { + // Add this data to the buffer. + buffer_.append(input.data(), input.length()); + QuicDataReader reader(buffer_.data(), buffer_.length(), HOST_BYTE_ORDER); + + switch (state_) { + case STATE_READING_TAG: + if (reader.BytesRemaining() < kQuicTagSize) { + break; + } + QuicTag message_tag; + reader.ReadTag(&message_tag); + message_.set_tag(message_tag); + state_ = STATE_READING_NUM_ENTRIES; + QUIC_FALLTHROUGH_INTENDED; + case STATE_READING_NUM_ENTRIES: + if (reader.BytesRemaining() < kNumEntriesSize + sizeof(uint16_t)) { + break; + } + reader.ReadUInt16(&num_entries_); + if (num_entries_ > kMaxEntries) { + error_detail_ = QuicStrCat(num_entries_, " entries"); + return QUIC_CRYPTO_TOO_MANY_ENTRIES; + } + uint16_t padding; + reader.ReadUInt16(&padding); + + tags_and_lengths_.reserve(num_entries_); + state_ = STATE_READING_TAGS_AND_LENGTHS; + values_len_ = 0; + QUIC_FALLTHROUGH_INTENDED; + case STATE_READING_TAGS_AND_LENGTHS: { + if (reader.BytesRemaining() < + num_entries_ * (kQuicTagSize + kCryptoEndOffsetSize)) { + break; + } + + uint32_t last_end_offset = 0; + for (unsigned i = 0; i < num_entries_; ++i) { + QuicTag tag; + reader.ReadTag(&tag); + if (i > 0 && tag <= tags_and_lengths_[i - 1].first) { + if (tag == tags_and_lengths_[i - 1].first) { + error_detail_ = QuicStrCat("Duplicate tag:", tag); + return QUIC_CRYPTO_DUPLICATE_TAG; + } + error_detail_ = QuicStrCat("Tag ", tag, " out of order"); + return QUIC_CRYPTO_TAGS_OUT_OF_ORDER; + } + + uint32_t end_offset; + reader.ReadUInt32(&end_offset); + + if (end_offset < last_end_offset) { + error_detail_ = + QuicStrCat("End offset: ", end_offset, " vs ", last_end_offset); + return QUIC_CRYPTO_TAGS_OUT_OF_ORDER; + } + tags_and_lengths_.push_back(std::make_pair( + tag, static_cast<size_t>(end_offset - last_end_offset))); + last_end_offset = end_offset; + } + values_len_ = last_end_offset; + state_ = STATE_READING_VALUES; + QUIC_FALLTHROUGH_INTENDED; + } + case STATE_READING_VALUES: + if (reader.BytesRemaining() < values_len_) { + if (!process_truncated_messages_) { + break; + } + QUIC_LOG(ERROR) << "Trunacted message. Missing " + << values_len_ - reader.BytesRemaining() << " bytes."; + } + for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) { + QuicStringPiece value; + if (!reader.ReadStringPiece(&value, item.second)) { + DCHECK(process_truncated_messages_); + // Store an empty value. + message_.SetStringPiece(item.first, ""); + continue; + } + message_.SetStringPiece(item.first, value); + } + visitor_->OnHandshakeMessage(message_); + Clear(); + state_ = STATE_READING_TAG; + break; + } + // Save any remaining data. + buffer_ = QuicString(reader.PeekRemainingPayload()); + return QUIC_NO_ERROR; +} + +// static +bool CryptoFramer::WritePadTag(QuicDataWriter* writer, + size_t pad_length, + uint32_t* end_offset) { + if (!writer->WriteTag(kPAD)) { + DCHECK(false) << "Failed to write tag."; + return false; + } + *end_offset += pad_length; + if (!writer->WriteUInt32(*end_offset)) { + DCHECK(false) << "Failed to write end offset."; + return false; + } + return true; +} + +} // namespace quic
diff --git a/quic/core/crypto/crypto_framer.h b/quic/core/crypto/crypto_framer.h new file mode 100644 index 0000000..e83e6a6 --- /dev/null +++ b/quic/core/crypto/crypto_framer.h
@@ -0,0 +1,138 @@ +// Copyright (c) 2012 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_CRYPTO_CRYPTO_FRAMER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_ + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <utility> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class CryptoFramer; +class QuicData; +class QuicDataWriter; + +class QUIC_EXPORT_PRIVATE CryptoFramerVisitorInterface { + public: + virtual ~CryptoFramerVisitorInterface() {} + + // Called if an error is detected. + virtual void OnError(CryptoFramer* framer) = 0; + + // Called when a complete handshake message has been parsed. + virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) = 0; +}; + +// A class for framing the crypto messages that are exchanged in a QUIC +// session. +class QUIC_EXPORT_PRIVATE CryptoFramer : public CryptoMessageParser { + public: + CryptoFramer(); + + ~CryptoFramer() override; + + // ParseMessage parses exactly one message from the given QuicStringPiece. If + // there is an error, the message is truncated, or the message has trailing + // garbage then nullptr will be returned. + static std::unique_ptr<CryptoHandshakeMessage> ParseMessage( + QuicStringPiece in); + + // Set callbacks to be called from the framer. A visitor must be set, or + // else the framer will crash. It is acceptable for the visitor to do + // nothing. If this is called multiple times, only the last visitor + // will be used. |visitor| will be owned by the framer. + void set_visitor(CryptoFramerVisitorInterface* visitor) { + visitor_ = visitor; + } + + QuicErrorCode error() const override; + const QuicString& error_detail() const override; + + // Processes input data, which must be delivered in order. Returns + // false if there was an error, and true otherwise. ProcessInput optionally + // takes an EncryptionLevel, but it is ignored. The variant with the + // EncryptionLevel is provided to match the CryptoMessageParser interface. + bool ProcessInput(QuicStringPiece input, EncryptionLevel level) override; + bool ProcessInput(QuicStringPiece input); + + // Returns the number of bytes of buffered input data remaining to be + // parsed. + size_t InputBytesRemaining() const override; + + // Checks if the specified tag has been seen. Returns |true| if it + // has, and |false| if it has not or a CHLO has not been seen. + bool HasTag(QuicTag tag) const; + + // Even if the CHLO has not been fully received, force processing of + // the handshake message. This is dangerous and should not be used + // except as a mechanism of last resort. + void ForceHandshake(); + + // Returns a new QuicData owned by the caller that contains a serialized + // |message|, or nullptr if there was an error. + static QuicData* ConstructHandshakeMessage( + const CryptoHandshakeMessage& message); + + // Debug only method which permits processing truncated messages. + void set_process_truncated_messages(bool process_truncated_messages) { + process_truncated_messages_ = process_truncated_messages; + } + + private: + // Clears per-message state. Does not clear the visitor. + void Clear(); + + // Process does does the work of |ProcessInput|, but returns an error code, + // doesn't set error_ and doesn't call |visitor_->OnError()|. + QuicErrorCode Process(QuicStringPiece input); + + static bool WritePadTag(QuicDataWriter* writer, + size_t pad_length, + uint32_t* end_offset); + + // Represents the current state of the parsing state machine. + enum CryptoFramerState { + STATE_READING_TAG, + STATE_READING_NUM_ENTRIES, + STATE_READING_TAGS_AND_LENGTHS, + STATE_READING_VALUES + }; + + // Visitor to invoke when messages are parsed. + CryptoFramerVisitorInterface* visitor_; + // Last error. + QuicErrorCode error_; + // Remaining unparsed data. + QuicString buffer_; + // Current state of the parsing. + CryptoFramerState state_; + // The message currently being parsed. + CryptoHandshakeMessage message_; + // The issue which caused |error_| + QuicString error_detail_; + // Number of entires in the message currently being parsed. + uint16_t num_entries_; + // tags_and_lengths_ contains the tags that are currently being parsed and + // their lengths. + std::vector<std::pair<QuicTag, size_t>> tags_and_lengths_; + // Cumulative length of all values in the message currently being parsed. + size_t values_len_; + // Set to true to allow of processing of truncated messages for debugging. + bool process_truncated_messages_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_
diff --git a/quic/core/crypto/crypto_framer_test.cc b/quic/core/crypto/crypto_framer_test.cc new file mode 100644 index 0000000..d72355c --- /dev/null +++ b/quic/core/crypto/crypto_framer_test.cc
@@ -0,0 +1,464 @@ +// Copyright (c) 2012 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/crypto/crypto_framer.h" + +#include <map> +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +char* AsChars(unsigned char* data) { + return reinterpret_cast<char*>(data); +} + +class TestCryptoVisitor : public CryptoFramerVisitorInterface { + public: + TestCryptoVisitor() : error_count_(0) {} + + void OnError(CryptoFramer* framer) override { + QUIC_DLOG(ERROR) << "CryptoFramer Error: " << framer->error(); + ++error_count_; + } + + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override { + messages_.push_back(message); + } + + // Counters from the visitor callbacks. + int error_count_; + + std::vector<CryptoHandshakeMessage> messages_; +}; + +TEST(CryptoFramerTest, ConstructHandshakeMessage) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x12345678, "abcdef"); + message.SetStringPiece(0x12345679, "ghijk"); + message.SetStringPiece(0x1234567A, "lmnopqr"); + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x03, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, + // tag 3 + 0x7A, 0x56, 0x34, 0x12, + // end offset 3 + 0x12, 0x00, 0x00, 0x00, + // value 1 + 'a', 'b', 'c', 'd', 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', 'k', + // value 3 + 'l', 'm', 'n', 'o', 'p', 'q', 'r', + }; + + CryptoFramer framer; + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + ASSERT_TRUE(data != nullptr); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x12345678, "abcdef"); + message.SetStringPiece(0x12345679, "ghijk"); + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, + // value 1 + 'a', 'b', 'c', 'd', 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', 'k', + }; + + CryptoFramer framer; + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageZeroLength) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x12345678, ""); + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x01, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x00, 0x00, 0x00, 0x00, + }; + + CryptoFramer framer; + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + for (uint32_t key = 1; key <= kMaxEntries + 1; ++key) { + message.SetStringPiece(key, "abcdef"); + } + + CryptoFramer framer; + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + EXPECT_TRUE(data == nullptr); +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSize) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x01020304, "test"); + message.set_minimum_size(64); + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 'P', 'A', 'D', 0, + // end offset 1 + 0x24, 0x00, 0x00, 0x00, + // tag 2 + 0x04, 0x03, 0x02, 0x01, + // end offset 2 + 0x28, 0x00, 0x00, 0x00, + // 36 bytes of padding. + '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', + // value 2 + 't', 'e', 's', 't', + }; + + CryptoFramer framer; + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSizePadLast) { + CryptoHandshakeMessage message; + message.set_tag(0xFFAA7733); + message.SetStringPiece(1, ""); + message.set_minimum_size(64); + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x01, 0x00, 0x00, 0x00, + // end offset 1 + 0x00, 0x00, 0x00, 0x00, + // tag 2 + 'P', 'A', 'D', 0, + // end offset 2 + 0x28, 0x00, 0x00, 0x00, + // 40 bytes of padding. + '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', + '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', + }; + + CryptoFramer framer; + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST(CryptoFramerTest, ProcessInput) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, + // value 1 + 'a', 'b', 'c', 'd', 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', 'k', + }; + + EXPECT_TRUE(framer.ProcessInput( + QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input)))); + EXPECT_EQ(0u, framer.InputBytesRemaining()); + EXPECT_EQ(0, visitor.error_count_); + ASSERT_EQ(1u, visitor.messages_.size()); + const CryptoHandshakeMessage& message = visitor.messages_[0]; + EXPECT_EQ(0xFFAA7733, message.tag()); + EXPECT_EQ(2u, message.tag_value_map().size()); + EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678)); + EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679)); +} + +TEST(CryptoFramerTest, ProcessInputWithThreeKeys) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x03, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, + // tag 3 + 0x7A, 0x56, 0x34, 0x12, + // end offset 3 + 0x12, 0x00, 0x00, 0x00, + // value 1 + 'a', 'b', 'c', 'd', 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', 'k', + // value 3 + 'l', 'm', 'n', 'o', 'p', 'q', 'r', + }; + + EXPECT_TRUE(framer.ProcessInput( + QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input)))); + EXPECT_EQ(0u, framer.InputBytesRemaining()); + EXPECT_EQ(0, visitor.error_count_); + ASSERT_EQ(1u, visitor.messages_.size()); + const CryptoHandshakeMessage& message = visitor.messages_[0]; + EXPECT_EQ(0xFFAA7733, message.tag()); + EXPECT_EQ(3u, message.tag_value_map().size()); + EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678)); + EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679)); + EXPECT_EQ("lmnopqr", crypto_test_utils::GetValueForTag(message, 0x1234567A)); +} + +TEST(CryptoFramerTest, ProcessInputIncrementally) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x06, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x0b, 0x00, 0x00, 0x00, + // value 1 + 'a', 'b', 'c', 'd', 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', 'k', + }; + + for (size_t i = 0; i < QUIC_ARRAYSIZE(input); i++) { + EXPECT_TRUE(framer.ProcessInput(QuicStringPiece(AsChars(input) + i, 1))); + } + EXPECT_EQ(0u, framer.InputBytesRemaining()); + ASSERT_EQ(1u, visitor.messages_.size()); + const CryptoHandshakeMessage& message = visitor.messages_[0]; + EXPECT_EQ(0xFFAA7733, message.tag()); + EXPECT_EQ(2u, message.tag_value_map().size()); + EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678)); + EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679)); +} + +TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x13, + // end offset 1 + 0x01, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x02, 0x00, 0x00, 0x00, + }; + + EXPECT_FALSE(framer.ProcessInput( + QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input)))); + EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error()); + EXPECT_EQ(1, visitor.error_count_); +} + +TEST(CryptoFramerTest, ProcessEndOffsetsOutOfOrder) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x79, 0x56, 0x34, 0x12, + // end offset 1 + 0x01, 0x00, 0x00, 0x00, + // tag 2 + 0x78, 0x56, 0x34, 0x13, + // end offset 2 + 0x00, 0x00, 0x00, 0x00, + }; + + EXPECT_FALSE(framer.ProcessInput( + QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input)))); + EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error()); + EXPECT_EQ(1, visitor.error_count_); +} + +TEST(CryptoFramerTest, ProcessInputTooManyEntries) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0xA0, 0x00, + // padding + 0x00, 0x00, + }; + + EXPECT_FALSE(framer.ProcessInput( + QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input)))); + EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error()); + EXPECT_EQ(1, visitor.error_count_); +} + +TEST(CryptoFramerTest, ProcessInputZeroLength) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entries + 0x02, 0x00, + // padding + 0x00, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // end offset 1 + 0x00, 0x00, 0x00, 0x00, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // end offset 2 + 0x05, 0x00, 0x00, 0x00, + }; + + EXPECT_TRUE(framer.ProcessInput( + QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input)))); + EXPECT_EQ(0, visitor.error_count_); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/crypto_handshake.cc b/quic/core/crypto/crypto_handshake.cc new file mode 100644 index 0000000..3d6baac --- /dev/null +++ b/quic/core/crypto/crypto_handshake.cc
@@ -0,0 +1,41 @@ +// Copyright (c) 2013 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/crypto/crypto_handshake.h" + +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h" +#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.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" + +namespace quic { + +QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() + : key_exchange(0), + aead(0), + token_binding_key_param(0), + sct_supported_by_client(false) {} + +QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {} + +CrypterPair::CrypterPair() {} + +CrypterPair::~CrypterPair() {} + +// static +const char QuicCryptoConfig::kInitialLabel[] = "QUIC key expansion"; + +// static +const char QuicCryptoConfig::kCETVLabel[] = "QUIC CETV block"; + +// static +const char QuicCryptoConfig::kForwardSecureLabel[] = + "QUIC forward secure key expansion"; + +QuicCryptoConfig::QuicCryptoConfig() + : common_cert_sets(CommonCertSets::GetInstanceQUIC()) {} + +QuicCryptoConfig::~QuicCryptoConfig() {} + +} // namespace quic
diff --git a/quic/core/crypto/crypto_handshake.h b/quic/core/crypto/crypto_handshake.h new file mode 100644 index 0000000..9b148c8 --- /dev/null +++ b/quic/core/crypto/crypto_handshake.h
@@ -0,0 +1,190 @@ +// Copyright (c) 2013 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_CRYPTO_CRYPTO_HANDSHAKE_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_ + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class CommonCertSets; +class KeyExchange; +class QuicDecrypter; +class QuicEncrypter; + +// HandshakeFailureReason enum values are uploaded to UMA, they cannot be +// changed. +enum HandshakeFailureReason { + HANDSHAKE_OK = 0, + + // Failure reasons for an invalid client nonce in CHLO. + // + // The default error value for nonce verification failures from strike + // register (covers old strike registers and unknown failures). + CLIENT_NONCE_UNKNOWN_FAILURE = 1, + // Client nonce had incorrect length. + CLIENT_NONCE_INVALID_FAILURE = 2, + // Client nonce is not unique. + CLIENT_NONCE_NOT_UNIQUE_FAILURE = 3, + // Client orbit is invalid or incorrect. + CLIENT_NONCE_INVALID_ORBIT_FAILURE = 4, + // Client nonce's timestamp is not in the strike register's valid time range. + CLIENT_NONCE_INVALID_TIME_FAILURE = 5, + // Strike register's RPC call timed out, client nonce couldn't be verified. + CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT = 6, + // Strike register is down, client nonce couldn't be verified. + CLIENT_NONCE_STRIKE_REGISTER_FAILURE = 7, + + // Failure reasons for an invalid server nonce in CHLO. + // + // Unbox of server nonce failed. + SERVER_NONCE_DECRYPTION_FAILURE = 8, + // Decrypted server nonce had incorrect length. + SERVER_NONCE_INVALID_FAILURE = 9, + // Server nonce is not unique. + SERVER_NONCE_NOT_UNIQUE_FAILURE = 10, + // Server nonce's timestamp is not in the strike register's valid time range. + SERVER_NONCE_INVALID_TIME_FAILURE = 11, + // The server requires handshake confirmation. + SERVER_NONCE_REQUIRED_FAILURE = 20, + + // Failure reasons for an invalid server config in CHLO. + // + // Missing Server config id (kSCID) tag. + SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 12, + // Couldn't find the Server config id (kSCID). + SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE = 13, + + // Failure reasons for an invalid source-address token. + // + // Missing Source-address token (kSourceAddressTokenTag) tag. + SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 14, + // Unbox of Source-address token failed. + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE = 15, + // Couldn't parse the unbox'ed Source-address token. + SOURCE_ADDRESS_TOKEN_PARSE_FAILURE = 16, + // Source-address token is for a different IP address. + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE = 17, + // The source-address token has a timestamp in the future. + SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE = 18, + // The source-address token has expired. + SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE = 19, + + // The expected leaf certificate hash could not be validated. + INVALID_EXPECTED_LEAF_CERTIFICATE = 21, + + MAX_FAILURE_REASON = 22, +}; + +// These errors will be packed into an uint32_t and we don't want to set the +// most significant bit, which may be misinterpreted as the sign bit. +static_assert(MAX_FAILURE_REASON <= 32, "failure reason out of sync"); + +// A CrypterPair contains the encrypter and decrypter for an encryption level. +struct QUIC_EXPORT_PRIVATE CrypterPair { + CrypterPair(); + ~CrypterPair(); + std::unique_ptr<QuicEncrypter> encrypter; + std::unique_ptr<QuicDecrypter> decrypter; +}; + +// Parameters negotiated by the crypto handshake. +struct QUIC_EXPORT_PRIVATE QuicCryptoNegotiatedParameters + : public QuicReferenceCounted { + // Initializes the members to 0 or empty values. + QuicCryptoNegotiatedParameters(); + + QuicTag key_exchange; + QuicTag aead; + QuicString initial_premaster_secret; + QuicString forward_secure_premaster_secret; + // initial_subkey_secret is used as the PRK input to the HKDF used when + // performing key extraction that needs to happen before forward-secure keys + // are available. + QuicString initial_subkey_secret; + // subkey_secret is used as the PRK input to the HKDF used for key extraction. + QuicString subkey_secret; + CrypterPair initial_crypters; + CrypterPair forward_secure_crypters; + // Normalized SNI: converted to lower case and trailing '.' removed. + QuicString sni; + QuicString client_nonce; + QuicString server_nonce; + // hkdf_input_suffix contains the HKDF input following the label: the + // ConnectionId, client hello and server config. This is only populated in the + // client because only the client needs to derive the forward secure keys at a + // later time from the initial keys. + QuicString hkdf_input_suffix; + // cached_certs contains the cached certificates that a client used when + // sending a client hello. + std::vector<QuicString> cached_certs; + // client_key_exchange is used by clients to store the ephemeral KeyExchange + // for the connection. + std::unique_ptr<KeyExchange> client_key_exchange; + // channel_id is set by servers to a ChannelID key when the client correctly + // proves possession of the corresponding private key. It consists of 32 + // bytes of x coordinate, followed by 32 bytes of y coordinate. Both values + // are big-endian and the pair is a P-256 public key. + QuicString channel_id; + QuicTag token_binding_key_param; + + // Used when generating proof signature when sending server config updates. + + // Used to generate cert chain when sending server config updates. + QuicString client_common_set_hashes; + QuicString client_cached_cert_hashes; + + // Default to false; set to true if the client indicates that it supports sct + // by sending CSCT tag with an empty value in client hello. + bool sct_supported_by_client; + + protected: + ~QuicCryptoNegotiatedParameters() override; +}; + +// QuicCryptoConfig contains common configuration between clients and servers. +class QUIC_EXPORT_PRIVATE QuicCryptoConfig { + public: + // kInitialLabel is a constant that is used when deriving the initial + // (non-forward secure) keys for the connection in order to tie the resulting + // key to this protocol. + static const char kInitialLabel[]; + + // kCETVLabel is a constant that is used when deriving the keys for the + // encrypted tag/value block in the client hello. + static const char kCETVLabel[]; + + // kForwardSecureLabel is a constant that is used when deriving the forward + // secure keys for the connection in order to tie the resulting key to this + // protocol. + static const char kForwardSecureLabel[]; + + QuicCryptoConfig(); + QuicCryptoConfig(const QuicCryptoConfig&) = delete; + QuicCryptoConfig& operator=(const QuicCryptoConfig&) = delete; + ~QuicCryptoConfig(); + + // Key exchange methods. The following two members' values correspond by + // index. + QuicTagVector kexs; + // Authenticated encryption with associated data (AEAD) algorithms. + QuicTagVector aead; + + // Supported Token Binding key parameters that can be negotiated in the client + // hello. + QuicTagVector tb_key_params; + + const CommonCertSets* common_cert_sets; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_
diff --git a/quic/core/crypto/crypto_handshake_message.cc b/quic/core/crypto/crypto_handshake_message.cc new file mode 100644 index 0000000..a057254 --- /dev/null +++ b/quic/core/crypto/crypto_handshake_message.cc
@@ -0,0 +1,378 @@ +// Copyright (c) 2013 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/crypto/crypto_handshake_message.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.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/quic_socket_address_coder.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +CryptoHandshakeMessage::CryptoHandshakeMessage() : tag_(0), minimum_size_(0) {} + +CryptoHandshakeMessage::CryptoHandshakeMessage( + const CryptoHandshakeMessage& other) + : tag_(other.tag_), + tag_value_map_(other.tag_value_map_), + minimum_size_(other.minimum_size_) { + // Don't copy serialized_. unique_ptr doesn't have a copy constructor. + // The new object can lazily reconstruct serialized_. +} + +CryptoHandshakeMessage::CryptoHandshakeMessage(CryptoHandshakeMessage&& other) = + default; + +CryptoHandshakeMessage::~CryptoHandshakeMessage() {} + +CryptoHandshakeMessage& CryptoHandshakeMessage::operator=( + const CryptoHandshakeMessage& other) { + tag_ = other.tag_; + tag_value_map_ = other.tag_value_map_; + // Don't copy serialized_. unique_ptr doesn't have an assignment operator. + // However, invalidate serialized_. + serialized_.reset(); + minimum_size_ = other.minimum_size_; + return *this; +} + +CryptoHandshakeMessage& CryptoHandshakeMessage::operator=( + CryptoHandshakeMessage&& other) = default; + +void CryptoHandshakeMessage::Clear() { + tag_ = 0; + tag_value_map_.clear(); + minimum_size_ = 0; + serialized_.reset(); +} + +const QuicData& CryptoHandshakeMessage::GetSerialized() const { + if (!serialized_.get()) { + serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this)); + } + return *serialized_; +} + +void CryptoHandshakeMessage::MarkDirty() { + serialized_.reset(); +} + +void CryptoHandshakeMessage::SetVersionVector( + QuicTag tag, + ParsedQuicVersionVector versions) { + QuicVersionLabelVector version_labels; + for (ParsedQuicVersion version : versions) { + version_labels.push_back( + QuicEndian::HostToNet32(CreateQuicVersionLabel(version))); + } + SetVector(tag, version_labels); +} + +void CryptoHandshakeMessage::SetVersion(QuicTag tag, + ParsedQuicVersion version) { + SetValue(tag, QuicEndian::HostToNet32(CreateQuicVersionLabel(version))); +} + +void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, + QuicStringPiece value) { + tag_value_map_[tag] = QuicString(value); +} + +void CryptoHandshakeMessage::Erase(QuicTag tag) { + tag_value_map_.erase(tag); +} + +QuicErrorCode CryptoHandshakeMessage::GetTaglist( + QuicTag tag, + QuicTagVector* out_tags) const { + auto it = tag_value_map_.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; + + if (it == tag_value_map_.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() % sizeof(QuicTag) != 0) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + out_tags->clear(); + return ret; + } + + size_t num_tags = it->second.size() / sizeof(QuicTag); + out_tags->resize(num_tags); + for (size_t i = 0; i < num_tags; ++i) { + QuicTag tag; + memcpy(&tag, it->second.data() + i * sizeof(tag), sizeof(tag)); + (*out_tags)[i] = tag; + } + return ret; +} + +QuicErrorCode CryptoHandshakeMessage::GetVersionLabelList( + QuicTag tag, + QuicVersionLabelVector* out) const { + QuicErrorCode error = GetTaglist(tag, out); + if (error != QUIC_NO_ERROR) { + return error; + } + + for (size_t i = 0; i < out->size(); ++i) { + (*out)[i] = QuicEndian::HostToNet32((*out)[i]); + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode CryptoHandshakeMessage::GetVersionLabel( + QuicTag tag, + QuicVersionLabel* out) const { + QuicErrorCode error = GetUint32(tag, out); + if (error != QUIC_NO_ERROR) { + return error; + } + + *out = QuicEndian::HostToNet32(*out); + return QUIC_NO_ERROR; +} + +bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag, + QuicStringPiece* out) const { + auto it = tag_value_map_.find(tag); + if (it == tag_value_map_.end()) { + return false; + } + *out = it->second; + return true; +} + +bool CryptoHandshakeMessage::HasStringPiece(QuicTag tag) const { + return QuicContainsKey(tag_value_map_, tag); +} + +QuicErrorCode CryptoHandshakeMessage::GetNthValue24( + QuicTag tag, + unsigned index, + QuicStringPiece* out) const { + QuicStringPiece value; + if (!GetStringPiece(tag, &value)) { + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + for (unsigned i = 0;; i++) { + if (value.empty()) { + return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND; + } + if (value.size() < 3) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + const unsigned char* data = + reinterpret_cast<const unsigned char*>(value.data()); + size_t size = static_cast<size_t>(data[0]) | + (static_cast<size_t>(data[1]) << 8) | + (static_cast<size_t>(data[2]) << 16); + value.remove_prefix(3); + + if (value.size() < size) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (i == index) { + *out = QuicStringPiece(value.data(), size); + return QUIC_NO_ERROR; + } + + value.remove_prefix(size); + } +} + +QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag, + uint32_t* out) const { + return GetPOD(tag, out, sizeof(uint32_t)); +} + +QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag, + uint64_t* out) const { + return GetPOD(tag, out, sizeof(uint64_t)); +} + +QuicErrorCode CryptoHandshakeMessage::GetUint128(QuicTag tag, + QuicUint128* out) const { + return GetPOD(tag, out, sizeof(QuicUint128)); +} + +size_t CryptoHandshakeMessage::size() const { + size_t ret = sizeof(QuicTag) + sizeof(uint16_t) /* number of entries */ + + sizeof(uint16_t) /* padding */; + ret += (sizeof(QuicTag) + sizeof(uint32_t) /* end offset */) * + tag_value_map_.size(); + for (auto i = tag_value_map_.begin(); i != tag_value_map_.end(); ++i) { + ret += i->second.size(); + } + + return ret; +} + +void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) { + if (min_bytes == minimum_size_) { + return; + } + serialized_.reset(); + minimum_size_ = min_bytes; +} + +size_t CryptoHandshakeMessage::minimum_size() const { + return minimum_size_; +} + +QuicString CryptoHandshakeMessage::DebugString() const { + return DebugStringInternal(0); +} + +QuicErrorCode CryptoHandshakeMessage::GetPOD(QuicTag tag, + void* out, + size_t len) const { + auto it = tag_value_map_.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; + + if (it == tag_value_map_.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() != len) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + memset(out, 0, len); + return ret; + } + + memcpy(out, it->second.data(), len); + return ret; +} + +QuicString CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { + QuicString ret = QuicString(2 * indent, ' ') + QuicTagToString(tag_) + "<\n"; + ++indent; + for (auto it = tag_value_map_.begin(); it != tag_value_map_.end(); ++it) { + ret += QuicString(2 * indent, ' ') + QuicTagToString(it->first) + ": "; + + bool done = false; + switch (it->first) { + case kICSL: + case kCFCW: + case kSFCW: + case kIRTT: + case kMIDS: + case kSCLS: + case kTCID: + // uint32_t value + if (it->second.size() == 4) { + uint32_t value; + memcpy(&value, it->second.data(), sizeof(value)); + ret += QuicTextUtils::Uint64ToString(value); + done = true; + } + break; + case kRCID: + // uint64_t value + if (it->second.size() == 8) { + uint64_t value; + memcpy(&value, it->second.data(), sizeof(value)); + value = QuicEndian::NetToHost64(value); + ret += QuicTextUtils::Uint64ToString(value); + done = true; + } + break; + case kTBKP: + case kKEXS: + case kAEAD: + case kCOPT: + case kPDMD: + case kVER: + // tag lists + if (it->second.size() % sizeof(QuicTag) == 0) { + for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) { + QuicTag tag; + memcpy(&tag, it->second.data() + j, sizeof(tag)); + if (j > 0) { + ret += ","; + } + ret += "'" + QuicTagToString(tag) + "'"; + } + done = true; + } + break; + case kRREJ: + // uint32_t lists + if (it->second.size() % sizeof(uint32_t) == 0) { + for (size_t j = 0; j < it->second.size(); j += sizeof(uint32_t)) { + uint32_t value; + memcpy(&value, it->second.data() + j, sizeof(value)); + if (j > 0) { + ret += ","; + } + ret += CryptoUtils::HandshakeFailureReasonToString( + static_cast<HandshakeFailureReason>(value)); + } + done = true; + } + break; + case kCADR: + // IP address and port + if (!it->second.empty()) { + QuicSocketAddressCoder decoder; + if (decoder.Decode(it->second.data(), it->second.size())) { + ret += QuicSocketAddress(decoder.ip(), decoder.port()).ToString(); + done = true; + } + } + break; + case kSCFG: + // nested messages. + if (!it->second.empty()) { + std::unique_ptr<CryptoHandshakeMessage> msg( + CryptoFramer::ParseMessage(it->second)); + if (msg) { + ret += "\n"; + ret += msg->DebugStringInternal(indent + 1); + + done = true; + } + } + break; + case kPAD: + ret += QuicStringPrintf("(%d bytes of padding)", + static_cast<int>(it->second.size())); + done = true; + break; + case kSNI: + case kUAID: + ret += "\"" + it->second + "\""; + done = true; + break; + } + + if (!done) { + // If there's no specific format for this tag, or the value is invalid, + // then just use hex. + ret += "0x" + QuicTextUtils::HexEncode(it->second); + } + ret += "\n"; + } + --indent; + ret += QuicString(2 * indent, ' ') + ">"; + return ret; +} + +} // namespace quic
diff --git a/quic/core/crypto/crypto_handshake_message.h b/quic/core/crypto/crypto_handshake_message.h new file mode 100644 index 0000000..1f6e8b6 --- /dev/null +++ b/quic/core/crypto/crypto_handshake_message.h
@@ -0,0 +1,155 @@ +// Copyright (c) 2013 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_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +// An intermediate format of a handshake message that's convenient for a +// CryptoFramer to serialize from or parse into. +class QUIC_EXPORT_PRIVATE CryptoHandshakeMessage { + public: + CryptoHandshakeMessage(); + CryptoHandshakeMessage(const CryptoHandshakeMessage& other); + CryptoHandshakeMessage(CryptoHandshakeMessage&& other); + ~CryptoHandshakeMessage(); + + CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other); + CryptoHandshakeMessage& operator=(CryptoHandshakeMessage&& other); + + // Clears state. + void Clear(); + + // GetSerialized returns the serialized form of this message and caches the + // result. Subsequently altering the message does not invalidate the cache. + const QuicData& GetSerialized() const; + + // MarkDirty invalidates the cache created by |GetSerialized|. + void MarkDirty(); + + // SetValue sets an element with the given tag to the raw, memory contents of + // |v|. + template <class T> + void SetValue(QuicTag tag, const T& v) { + tag_value_map_[tag] = + QuicString(reinterpret_cast<const char*>(&v), sizeof(v)); + } + + // SetVector sets an element with the given tag to the raw contents of an + // array of elements in |v|. + template <class T> + void SetVector(QuicTag tag, const std::vector<T>& v) { + if (v.empty()) { + tag_value_map_[tag] = QuicString(); + } else { + tag_value_map_[tag] = QuicString(reinterpret_cast<const char*>(&v[0]), + v.size() * sizeof(T)); + } + } + + // Sets an element with the given tag to the on-the-wire representation of + // |version|. + void SetVersion(QuicTag tag, ParsedQuicVersion version); + + // Sets an element with the given tag to the on-the-wire representation of + // the elements in |versions|. + void SetVersionVector(QuicTag tag, ParsedQuicVersionVector versions); + + // Returns the message tag. + QuicTag tag() const { return tag_; } + // Sets the message tag. + void set_tag(QuicTag tag) { tag_ = tag; } + + const QuicTagValueMap& tag_value_map() const { return tag_value_map_; } + + void SetStringPiece(QuicTag tag, QuicStringPiece value); + + // Erase removes a tag/value, if present, from the message. + void Erase(QuicTag tag); + + // GetTaglist finds an element with the given tag containing zero or more + // tags. If such a tag doesn't exist, it returns an error code. Otherwise it + // populates |out_tags| with the tags and returns QUIC_NO_ERROR. + QuicErrorCode GetTaglist(QuicTag tag, QuicTagVector* out_tags) const; + + // GetVersionLabelList finds an element with the given tag containing zero or + // more version labels. If such a tag doesn't exist, it returns an error code. + // Otherwise it populates |out| with the labels and returns QUIC_NO_ERROR. + QuicErrorCode GetVersionLabelList(QuicTag tag, + QuicVersionLabelVector* out) const; + + // GetVersionLabel finds an element with the given tag containing a single + // version label. If such a tag doesn't exist, it returns an error code. + // Otherwise it populates |out| with the label and returns QUIC_NO_ERROR. + QuicErrorCode GetVersionLabel(QuicTag tag, QuicVersionLabel* out) const; + + bool GetStringPiece(QuicTag tag, QuicStringPiece* out) const; + bool HasStringPiece(QuicTag tag) const; + + // GetNthValue24 interprets the value with the given tag to be a series of + // 24-bit, length prefixed values and it returns the subvalue with the given + // index. + QuicErrorCode GetNthValue24(QuicTag tag, + unsigned index, + QuicStringPiece* out) const; + QuicErrorCode GetUint32(QuicTag tag, uint32_t* out) const; + QuicErrorCode GetUint64(QuicTag tag, uint64_t* out) const; + QuicErrorCode GetUint128(QuicTag tag, QuicUint128* out) const; + + // size returns 4 (message tag) + 2 (uint16_t, number of entries) + + // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes. + size_t size() const; + + // set_minimum_size sets the minimum number of bytes that the message should + // consume. The CryptoFramer will add a PAD tag as needed when serializing in + // order to ensure this. Setting a value of 0 disables padding. + // + // Padding is useful in order to ensure that messages are a minimum size. A + // QUIC server can require a minimum size in order to reduce the + // amplification factor of any mirror DoS attack. + void set_minimum_size(size_t min_bytes); + + size_t minimum_size() const; + + // DebugString returns a multi-line, string representation of the message + // suitable for including in debug output. + QuicString DebugString() const; + + private: + // GetPOD is a utility function for extracting a plain-old-data value. If + // |tag| exists in the message, and has a value of exactly |len| bytes then + // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out| + // are zeroed out. + // + // If used to copy integers then this assumes that the machine is + // little-endian. + QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const; + + QuicString DebugStringInternal(size_t indent) const; + + QuicTag tag_; + QuicTagValueMap tag_value_map_; + + size_t minimum_size_; + + // The serialized form of the handshake message. This member is constructed + // lazily. + mutable std::unique_ptr<QuicData> serialized_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
diff --git a/quic/core/crypto/crypto_handshake_message_test.cc b/quic/core/crypto/crypto_handshake_message_test.cc new file mode 100644 index 0000000..fc52980 --- /dev/null +++ b/quic/core/crypto/crypto_handshake_message_test.cc
@@ -0,0 +1,131 @@ +// Copyright (c) 2015 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/crypto/crypto_handshake_message.h" + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +TEST(CryptoHandshakeMessageTest, DebugString) { + const char* str = "SHLO<\n>"; + + CryptoHandshakeMessage message; + message.set_tag(kSHLO); + EXPECT_EQ(str, message.DebugString()); + + // Test copy + CryptoHandshakeMessage message2(message); + EXPECT_EQ(str, message2.DebugString()); + + // Test move + CryptoHandshakeMessage message3(std::move(message)); + EXPECT_EQ(str, message3.DebugString()); + + // Test assign + CryptoHandshakeMessage message4 = message3; + EXPECT_EQ(str, message4.DebugString()); + + // Test move-assign + CryptoHandshakeMessage message5 = std::move(message3); + EXPECT_EQ(str, message5.DebugString()); +} + +TEST(CryptoHandshakeMessageTest, DebugStringWithUintVector) { + const char* str = + "REJ <\n RREJ: " + "SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE," + "CLIENT_NONCE_NOT_UNIQUE_FAILURE\n>"; + + CryptoHandshakeMessage message; + message.set_tag(kREJ); + std::vector<uint32_t> reasons = { + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + CLIENT_NONCE_NOT_UNIQUE_FAILURE}; + message.SetVector(kRREJ, reasons); + EXPECT_EQ(str, message.DebugString()); + + // Test copy + CryptoHandshakeMessage message2(message); + EXPECT_EQ(str, message2.DebugString()); + + // Test move + CryptoHandshakeMessage message3(std::move(message)); + EXPECT_EQ(str, message3.DebugString()); + + // Test assign + CryptoHandshakeMessage message4 = message3; + EXPECT_EQ(str, message4.DebugString()); + + // Test move-assign + CryptoHandshakeMessage message5 = std::move(message3); + EXPECT_EQ(str, message5.DebugString()); +} + +TEST(CryptoHandshakeMessageTest, DebugStringWithTagVector) { + const char* str = "CHLO<\n COPT: 'TBBR','PAD ','BYTE'\n>"; + + CryptoHandshakeMessage message; + message.set_tag(kCHLO); + message.SetVector(kCOPT, QuicTagVector{kTBBR, kPAD, kBYTE}); + EXPECT_EQ(str, message.DebugString()); + + // Test copy + CryptoHandshakeMessage message2(message); + EXPECT_EQ(str, message2.DebugString()); + + // Test move + CryptoHandshakeMessage message3(std::move(message)); + EXPECT_EQ(str, message3.DebugString()); + + // Test assign + CryptoHandshakeMessage message4 = message3; + EXPECT_EQ(str, message4.DebugString()); + + // Test move-assign + CryptoHandshakeMessage message5 = std::move(message3); + EXPECT_EQ(str, message5.DebugString()); +} + +TEST(CryptoHandshakeMessageTest, ServerDesignatedConnectionId) { + const char* str = "SREJ<\n RCID: 18364758544493064720\n>"; + + CryptoHandshakeMessage message; + message.set_tag(kSREJ); + message.SetValue(kRCID, + QuicEndian::NetToHost64(UINT64_C(18364758544493064720))); + EXPECT_EQ(str, message.DebugString()); + + // Test copy + CryptoHandshakeMessage message2(message); + EXPECT_EQ(str, message2.DebugString()); + + // Test move + CryptoHandshakeMessage message3(std::move(message)); + EXPECT_EQ(str, message3.DebugString()); + + // Test assign + CryptoHandshakeMessage message4 = message3; + EXPECT_EQ(str, message4.DebugString()); + + // Test move-assign + CryptoHandshakeMessage message5 = std::move(message3); + EXPECT_EQ(str, message5.DebugString()); +} + +TEST(CryptoHandshakeMessageTest, HasStringPiece) { + CryptoHandshakeMessage message; + EXPECT_FALSE(message.HasStringPiece(kRCID)); + message.SetStringPiece(kRCID, "foo"); + EXPECT_TRUE(message.HasStringPiece(kRCID)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/crypto_message_parser.h b/quic/core/crypto/crypto_message_parser.h new file mode 100644 index 0000000..8b9fa4a --- /dev/null +++ b/quic/core/crypto/crypto_message_parser.h
@@ -0,0 +1,34 @@ +// Copyright (c) 2012 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_CRYPTO_CRYPTO_MESSAGE_PARSER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE CryptoMessageParser { + public: + virtual ~CryptoMessageParser() {} + + virtual QuicErrorCode error() const = 0; + virtual const QuicString& error_detail() const = 0; + + // Processes input data, which must be delivered in order. The input data + // being processed was received at encryption level |level|. Returns + // false if there was an error, and true otherwise. + virtual bool ProcessInput(QuicStringPiece input, EncryptionLevel level) = 0; + + // Returns the number of bytes of buffered input data remaining to be + // parsed. + virtual size_t InputBytesRemaining() const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_
diff --git a/quic/core/crypto/crypto_message_printer_bin.cc b/quic/core/crypto/crypto_message_printer_bin.cc new file mode 100644 index 0000000..f14abb0 --- /dev/null +++ b/quic/core/crypto/crypto_message_printer_bin.cc
@@ -0,0 +1,57 @@ +// Copyright (c) 2019 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. + +// Dumps the contents of a QUIC crypto handshake message in a human readable +// format. +// +// Usage: crypto_message_printer_bin <hex of message> + +#include <iostream> + +#include "base/init_google.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using quic::Perspective; +using quic::QuicString; +using std::cerr; +using std::cout; +using std::endl; + +namespace quic { + +class CryptoMessagePrinter : public ::quic::CryptoFramerVisitorInterface { + public: + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override { + cout << message.DebugString() << endl; + } + + void OnError(CryptoFramer* framer) override { + cerr << "Error code: " << framer->error() << endl; + cerr << "Error details: " << framer->error_detail() << endl; + } +}; + +} // namespace quic + +int main(int argc, char* argv[]) { + InitGoogle(argv[0], &argc, &argv, true); + + quic::CryptoMessagePrinter printer; + quic::CryptoFramer framer; + framer.set_visitor(&printer); + framer.set_process_truncated_messages(true); + QuicString input = quic::QuicTextUtils::HexDecode(argv[1]); + if (!framer.ProcessInput(input)) { + return 1; + } + if (framer.InputBytesRemaining() != 0) { + cerr << "Input partially consumed. " << framer.InputBytesRemaining() + << " bytes remaining." << endl; + return 2; + } + return 0; +}
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h new file mode 100644 index 0000000..e5a4048 --- /dev/null +++ b/quic/core/crypto/crypto_protocol.h
@@ -0,0 +1,313 @@ +// Copyright (c) 2012 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_CRYPTO_CRYPTO_PROTOCOL_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_ + +#include <cstddef> + +#include "net/third_party/quiche/src/quic/core/quic_tag.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +// Version and Crypto tags are written to the wire with a big-endian +// representation of the name of the tag. For example +// the client hello tag (CHLO) will be written as the +// following 4 bytes: 'C' 'H' 'L' 'O'. Since it is +// stored in memory as a little endian uint32_t, we need +// to reverse the order of the bytes. +// +// We use a macro to ensure that no static initialisers are created. Use the +// MakeQuicTag function in normal code. +#define TAG(a, b, c, d) \ + static_cast<QuicTag>((d << 24) + (c << 16) + (b << 8) + a) + +namespace quic { + +typedef QuicString ServerConfigID; + +// clang-format off +const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello +const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello +const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config +const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject +const QuicTag kSREJ = TAG('S', 'R', 'E', 'J'); // Stateless reject +const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value + // pairs +const QuicTag kPRST = TAG('P', 'R', 'S', 'T'); // Public reset +const QuicTag kSCUP = TAG('S', 'C', 'U', 'P'); // Server config update +const QuicTag kALPN = TAG('A', 'L', 'P', 'N'); // Application-layer protocol + +// Key exchange methods +const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256 +const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519 + +// AEAD algorithms +const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12 +const QuicTag kCC20 = TAG('C', 'C', '2', '0'); // ChaCha20 + Poly1305 RFC7539 + +// Congestion control feedback types +const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic + +// Connection options (COPT) values +const QuicTag kAFCW = TAG('A', 'F', 'C', 'W'); // Auto-tune flow control + // receive windows. +const QuicTag kIFW5 = TAG('I', 'F', 'W', '5'); // Set initial size + // of stream flow control + // receive window to + // 32KB. (2^5 KB). +const QuicTag kIFW6 = TAG('I', 'F', 'W', '6'); // Set initial size + // of stream flow control + // receive window to + // 64KB. (2^6 KB). +const QuicTag kIFW7 = TAG('I', 'F', 'W', '7'); // Set initial size + // of stream flow control + // receive window to + // 128KB. (2^7 KB). +const QuicTag kIFW8 = TAG('I', 'F', 'W', '8'); // Set initial size + // of stream flow control + // receive window to + // 256KB. (2^8 KB). +const QuicTag kIFW9 = TAG('I', 'F', 'W', '9'); // Set initial size + // of stream flow control + // receive window to + // 512KB. (2^9 KB). +const QuicTag kIFWA = TAG('I', 'F', 'W', 'a'); // Set initial size + // of stream flow control + // receive window to + // 1MB. (2^0xa KB). +const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP +const QuicTag k1RTT = TAG('1', 'R', 'T', 'T'); // STARTUP in BBR for 1 RTT +const QuicTag k2RTT = TAG('2', 'R', 'T', 'T'); // STARTUP in BBR for 2 RTTs +const QuicTag kLRTT = TAG('L', 'R', 'T', 'T'); // Exit STARTUP in BBR on loss +const QuicTag kBBS1 = TAG('B', 'B', 'S', '1'); // Rate-based recovery in + // BBR STARTUP +const QuicTag kBBS2 = TAG('B', 'B', 'S', '2'); // More aggressive packet + // conservation in BBR STARTUP +const QuicTag kBBS3 = TAG('B', 'B', 'S', '3'); // Slowstart packet + // conservation in BBR STARTUP +const QuicTag kBBS4 = TAG('B', 'B', 'S', '4'); // Reduce rate in STARTUP by + // bytes_lost / CWND. +const QuicTag kBBS5 = TAG('B', 'B', 'S', '5'); // Reduce rate in STARTUP by + // 2 * bytes_lost / CWND. +const QuicTag kBBRR = TAG('B', 'B', 'R', 'R'); // Rate-based recovery in BBR +const QuicTag kBBR1 = TAG('B', 'B', 'R', '1'); // DEPRECATED +const QuicTag kBBR2 = TAG('B', 'B', 'R', '2'); // DEPRECATED +const QuicTag kBBR3 = TAG('B', 'B', 'R', '3'); // Fully drain the queue once + // per cycle +const QuicTag kBBR4 = TAG('B', 'B', 'R', '4'); // 20 RTT ack aggregation +const QuicTag kBBR5 = TAG('B', 'B', 'R', '5'); // 40 RTT ack aggregation +const QuicTag kBBR6 = TAG('B', 'B', 'R', '6'); // PROBE_RTT with 0.75 * BDP +const QuicTag kBBR7 = TAG('B', 'B', 'R', '7'); // Skip PROBE_RTT if rtt has + // not changed 12.5% +const QuicTag kBBR8 = TAG('B', 'B', 'R', '8'); // Disable PROBE_RTT when + // recently app-limited +const QuicTag kBBR9 = TAG('B', 'B', 'R', '9'); // Ignore app-limited calls in + // BBR if enough inflight. +const QuicTag kBBRS = TAG('B', 'B', 'R', 'S'); // Use 1.5x pacing in startup + // after a loss has occurred. +const QuicTag kBBQ1 = TAG('B', 'B', 'Q', '1'); // BBR with lower 2.77 STARTUP + // pacing and CWND gain. +const QuicTag kBBQ2 = TAG('B', 'B', 'Q', '2'); // BBR with lower 2.0 STARTUP + // CWND gain. +const QuicTag kBBQ3 = TAG('B', 'B', 'Q', '3'); // BBR with ack aggregation + // compensation in STARTUP. +const QuicTag kBBQ4 = TAG('B', 'B', 'Q', '4'); // Drain gain of 0.75. +const QuicTag kBBQ5 = TAG('B', 'B', 'Q', '5'); // Expire ack aggregation upon + // bandwidth increase in + // STARTUP. +const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control +const QuicTag kTPCC = TAG('P', 'C', 'C', '\0'); // Performance-Oriented + // Congestion Control +const QuicTag kBYTE = TAG('B', 'Y', 'T', 'E'); // TCP cubic or reno in bytes +const QuicTag kIW03 = TAG('I', 'W', '0', '3'); // Force ICWND to 3 +const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10 +const QuicTag kIW20 = TAG('I', 'W', '2', '0'); // Force ICWND to 20 +const QuicTag kIW50 = TAG('I', 'W', '5', '0'); // Force ICWND to 50 +const QuicTag k1CON = TAG('1', 'C', 'O', 'N'); // Emulate a single connection +const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe +const QuicTag k1TLP = TAG('1', 'T', 'L', 'P'); // 1 tail loss probe +const QuicTag k1RTO = TAG('1', 'R', 'T', 'O'); // Send 1 packet upon RTO +const QuicTag kNRTO = TAG('N', 'R', 'T', 'O'); // CWND reduction on loss +const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based loss detection +const QuicTag kATIM = TAG('A', 'T', 'I', 'M'); // Adaptive time loss detection +const QuicTag kMIN1 = TAG('M', 'I', 'N', '1'); // Min CWND of 1 packet +const QuicTag kMIN4 = TAG('M', 'I', 'N', '4'); // Min CWND of 4 packets, + // with a min rate of 1 BDP. +const QuicTag kTLPR = TAG('T', 'L', 'P', 'R'); // Tail loss probe delay of + // 0.5RTT. +const QuicTag kMAD0 = TAG('M', 'A', 'D', '0'); // Ignore ack delay +const QuicTag kMAD1 = TAG('M', 'A', 'D', '1'); // 25ms initial max ack delay +const QuicTag kMAD2 = TAG('M', 'A', 'D', '2'); // No min TLP +const QuicTag kMAD3 = TAG('M', 'A', 'D', '3'); // No min RTO +const QuicTag kMAD4 = TAG('M', 'A', 'D', '4'); // IETF style TLP +const QuicTag kMAD5 = TAG('M', 'A', 'D', '5'); // IETF style TLP with 2x mult +const QuicTag kACD0 = TAG('A', 'D', 'D', '0'); // Disable ack decimation +const QuicTag kACKD = TAG('A', 'C', 'K', 'D'); // Ack decimation style acking. +const QuicTag kAKD2 = TAG('A', 'K', 'D', '2'); // Ack decimation tolerating + // out of order packets. +const QuicTag kAKD3 = TAG('A', 'K', 'D', '3'); // Ack decimation style acking + // with 1/8 RTT acks. +const QuicTag kAKD4 = TAG('A', 'K', 'D', '4'); // Ack decimation with 1/8 RTT + // tolerating out of order. +const QuicTag kAKDU = TAG('A', 'K', 'D', 'U'); // Unlimited number of packets + // received before acking +const QuicTag kACKQ = TAG('A', 'C', 'K', 'Q'); // Send an immediate ack after + // 1 RTT of not receiving. +const QuicTag kSSLR = TAG('S', 'S', 'L', 'R'); // Slow Start Large Reduction. +const QuicTag kNPRR = TAG('N', 'P', 'R', 'R'); // Pace at unity instead of PRR +const QuicTag k5RTO = TAG('5', 'R', 'T', 'O'); // Close connection on 5 RTOs +const QuicTag kCONH = TAG('C', 'O', 'N', 'H'); // Conservative Handshake + // Retransmissions. +const QuicTag kLFAK = TAG('L', 'F', 'A', 'K'); // Don't invoke FACK on the + // first ack. +const QuicTag kSTMP = TAG('S', 'T', 'M', 'P'); // Send and process timestamps +// TODO(fayang): Remove this connection option when QUIC_VERSION_35, is removed +// Since MAX_HEADER_LIST_SIZE settings frame is supported instead. +const QuicTag kSMHL = TAG('S', 'M', 'H', 'L'); // Support MAX_HEADER_LIST_SIZE + // settings frame. +const QuicTag kNSTP = TAG('N', 'S', 'T', 'P'); // No stop waiting frames. +const QuicTag kNRTT = TAG('N', 'R', 'T', 'T'); // Ignore initial RTT + +// Optional support of truncated Connection IDs. If sent by a peer, the value +// is the minimum number of bytes allowed for the connection ID sent to the +// peer. +const QuicTag kTCID = TAG('T', 'C', 'I', 'D'); // Connection ID truncation. + +// Multipath option. +const QuicTag kMPTH = TAG('M', 'P', 'T', 'H'); // Enable multipath. + +const QuicTag kNCMR = TAG('N', 'C', 'M', 'R'); // Do not attempt connection + // migration. + +// Disable Pacing offload option. +const QuicTag kNPCO = TAG('N', 'P', 'C', 'O'); // No pacing offload. + +// Enable bandwidth resumption experiment. +const QuicTag kBWRE = TAG('B', 'W', 'R', 'E'); // Bandwidth resumption. +const QuicTag kBWMX = TAG('B', 'W', 'M', 'X'); // Max bandwidth resumption. +const QuicTag kBWRS = TAG('B', 'W', 'R', 'S'); // Server bandwidth resumption. +const QuicTag kBWS2 = TAG('B', 'W', 'S', '2'); // Server bw resumption v2. + +// Enable path MTU discovery experiment. +const QuicTag kMTUH = TAG('M', 'T', 'U', 'H'); // High-target MTU discovery. +const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery. + +// Proof types (i.e. certificate types) +// NOTE: although it would be silly to do so, specifying both kX509 and kX59R +// is allowed and is equivalent to specifying only kX509. +const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key + // types +const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys + // only +const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID. + +// Client hello tags +const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version +const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce +const QuicTag kNONP = TAG('N', 'O', 'N', 'P'); // The client's proof nonce +const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods +const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated + // encryption algorithms +const QuicTag kCOPT = TAG('C', 'O', 'P', 'T'); // Connection options +const QuicTag kCLOP = TAG('C', 'L', 'O', 'P'); // Client connection options +const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle network timeout +const QuicTag kSCLS = TAG('S', 'C', 'L', 'S'); // Silently close on timeout +const QuicTag kMIDS = TAG('M', 'I', 'D', 'S'); // Max incoming dynamic streams +const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us. +const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name + // indication +const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values +const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id +const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit. +const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand. +const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature). +const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set +const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate +const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry +const QuicTag kSTTL = TAG('S', 'T', 'T', 'L'); // Server Config TTL +const QuicTag kSFCW = TAG('S', 'F', 'C', 'W'); // Initial stream flow control + // receive window. +const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection + // flow control receive window. +const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID. +const QuicTag kXLCT = TAG('X', 'L', 'C', 'T'); // Expected leaf certificate. +const QuicTag kTBKP = TAG('T', 'B', 'K', 'P'); // Token Binding key params. + +// Token Binding tags +const QuicTag kTB10 = TAG('T', 'B', '1', '0'); // TB draft 10 with P256. + +// Rejection tags +const QuicTag kRREJ = TAG('R', 'R', 'E', 'J'); // Reasons for server sending +// Stateless Reject tags +const QuicTag kRCID = TAG('R', 'C', 'I', 'D'); // Server-designated + // connection ID +// Server hello tags +const QuicTag kCADR = TAG('C', 'A', 'D', 'R'); // Client IP address and port +const QuicTag kASAD = TAG('A', 'S', 'A', 'D'); // Alternate Server IP address + // and port. +const QuicTag kSRST = TAG('S', 'R', 'S', 'T'); // Stateless reset token used + // in IETF public reset packet + +// CETV tags +const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key +const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature + +// Public reset tags +const QuicTag kRNON = TAG('R', 'N', 'O', 'N'); // Public reset nonce proof +const QuicTag kRSEQ = TAG('R', 'S', 'E', 'Q'); // Rejected packet number + +// Universal tags +const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding + +// Stats collection tags +const QuicTag kEPID = TAG('E', 'P', 'I', 'D'); // Endpoint identifier. + +// clang-format on + +// These tags have a special form so that they appear either at the beginning +// or the end of a handshake message. Since handshake messages are sorted by +// tag value, the tags with 0 at the end will sort first and those with 255 at +// the end will sort last. +// +// The certificate chain should have a tag that will cause it to be sorted at +// the end of any handshake messages because it's likely to be large and the +// client might be able to get everything that it needs from the small values at +// the beginning. +// +// Likewise tags with random values should be towards the beginning of the +// message because the server mightn't hold state for a rejected client hello +// and therefore the client may have issues reassembling the rejection message +// in the event that it sent two client hellos. +const QuicTag kServerNonceTag = TAG('S', 'N', 'O', 0); // The server's nonce +const QuicTag kSourceAddressTokenTag = + TAG('S', 'T', 'K', 0); // Source-address token +const QuicTag kCertificateTag = TAG('C', 'R', 'T', 255); // Certificate chain +const QuicTag kCertificateSCTTag = + TAG('C', 'S', 'C', 'T'); // Signed cert timestamp (RFC6962) of leaf cert. + +#undef TAG + +const size_t kMaxEntries = 128; // Max number of entries in a message. + +const size_t kNonceSize = 32; // Size in bytes of the connection nonce. + +const size_t kOrbitSize = 8; // Number of bytes in an orbit value. + +// kProofSignatureLabel is prepended to the CHLO hash and server configs before +// signing to avoid any cross-protocol attacks on the signature. +const char kProofSignatureLabel[] = "QUIC CHLO and server config signature"; + +// kClientHelloMinimumSize is the minimum size of a client hello. Client hellos +// will have PAD tags added in order to ensure this minimum is met and client +// hellos smaller than this will be an error. This minimum size reduces the +// amplification factor of any mirror DoS attack. +// +// A client may pad an inchoate client hello to a size larger than +// kClientHelloMinimumSize to make it more likely to receive a complete +// rejection message. +const size_t kClientHelloMinimumSize = 1024; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_
diff --git a/quic/core/crypto/crypto_secret_boxer.cc b/quic/core/crypto/crypto_secret_boxer.cc new file mode 100644 index 0000000..ada12ad --- /dev/null +++ b/quic/core/crypto/crypto_secret_boxer.cc
@@ -0,0 +1,143 @@ +// Copyright (c) 2013 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 <cstdint> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h" + +#include "third_party/boringssl/src/include/openssl/aead.h" +#include "third_party/boringssl/src/include/openssl/err.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// kSIVNonceSize contains the number of bytes of nonce in each AES-GCM-SIV box. +// AES-GCM-SIV takes a 12-byte nonce and, since the messages are so small, each +// key is good for more than 2^64 source-address tokens. See table 1 of +// https://eprint.iacr.org/2017/168.pdf +static const size_t kSIVNonceSize = 12; + +// AES-GCM-SIV comes in AES-128 and AES-256 flavours. The AES-256 version is +// used here so that the key size matches the 256-bit XSalsa20 keys that we +// used to use. +static const size_t kBoxKeySize = 32; + +struct CryptoSecretBoxer::State { + // ctxs are the initialised AEAD contexts. These objects contain the + // scheduled AES state for each of the keys. + std::vector<bssl::UniquePtr<EVP_AEAD_CTX>> ctxs; +}; + +CryptoSecretBoxer::CryptoSecretBoxer() {} + +CryptoSecretBoxer::~CryptoSecretBoxer() {} + +// static +size_t CryptoSecretBoxer::GetKeySize() { + return kBoxKeySize; +} + +// kAEAD is the AEAD used for boxing: AES-256-GCM-SIV. +static const EVP_AEAD* (*const kAEAD)() = EVP_aead_aes_256_gcm_siv; + +void CryptoSecretBoxer::SetKeys(const std::vector<QuicString>& keys) { + DCHECK(!keys.empty()); + const EVP_AEAD* const aead = kAEAD(); + std::unique_ptr<State> new_state(new State); + + for (const QuicString& key : keys) { + DCHECK_EQ(kBoxKeySize, key.size()); + bssl::UniquePtr<EVP_AEAD_CTX> ctx( + EVP_AEAD_CTX_new(aead, reinterpret_cast<const uint8_t*>(key.data()), + key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH)); + if (!ctx) { + ERR_clear_error(); + LOG(DFATAL) << "EVP_AEAD_CTX_init failed"; + return; + } + + new_state->ctxs.push_back(std::move(ctx)); + } + + QuicWriterMutexLock l(&lock_); + state_ = std::move(new_state); +} + +QuicString CryptoSecretBoxer::Box(QuicRandom* rand, + QuicStringPiece plaintext) const { + // The box is formatted as: + // 12 bytes of random nonce + // n bytes of ciphertext + // 16 bytes of authenticator + size_t out_len = + kSIVNonceSize + plaintext.size() + EVP_AEAD_max_overhead(kAEAD()); + + QuicString ret; + ret.resize(out_len); + uint8_t* out = reinterpret_cast<uint8_t*>(const_cast<char*>(ret.data())); + + // Write kSIVNonceSize bytes of random nonce to the beginning of the output + // buffer. + rand->RandBytes(out, kSIVNonceSize); + const uint8_t* const nonce = out; + out += kSIVNonceSize; + out_len -= kSIVNonceSize; + + size_t bytes_written; + { + QuicReaderMutexLock l(&lock_); + if (!EVP_AEAD_CTX_seal(state_->ctxs[0].get(), out, &bytes_written, out_len, + nonce, kSIVNonceSize, + reinterpret_cast<const uint8_t*>(plaintext.data()), + plaintext.size(), nullptr, 0)) { + ERR_clear_error(); + LOG(DFATAL) << "EVP_AEAD_CTX_seal failed"; + return ""; + } + } + + DCHECK_EQ(out_len, bytes_written); + return ret; +} + +bool CryptoSecretBoxer::Unbox(QuicStringPiece in_ciphertext, + QuicString* out_storage, + QuicStringPiece* out) const { + if (in_ciphertext.size() < kSIVNonceSize) { + return false; + } + + const uint8_t* const nonce = + reinterpret_cast<const uint8_t*>(in_ciphertext.data()); + const uint8_t* const ciphertext = nonce + kSIVNonceSize; + const size_t ciphertext_len = in_ciphertext.size() - kSIVNonceSize; + + out_storage->resize(ciphertext_len); + + bool ok = false; + { + QuicReaderMutexLock l(&lock_); + for (const bssl::UniquePtr<EVP_AEAD_CTX>& ctx : state_->ctxs) { + size_t bytes_written; + if (EVP_AEAD_CTX_open(ctx.get(), + reinterpret_cast<uint8_t*>( + const_cast<char*>(out_storage->data())), + &bytes_written, ciphertext_len, nonce, + kSIVNonceSize, ciphertext, ciphertext_len, nullptr, + 0)) { + ok = true; + *out = QuicStringPiece(out_storage->data(), bytes_written); + break; + } + + ERR_clear_error(); + } + } + + return ok; +} + +} // namespace quic
diff --git a/quic/core/crypto/crypto_secret_boxer.h b/quic/core/crypto/crypto_secret_boxer.h new file mode 100644 index 0000000..de470b1 --- /dev/null +++ b/quic/core/crypto/crypto_secret_boxer.h
@@ -0,0 +1,68 @@ +// Copyright (c) 2013 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_CRYPTO_CRYPTO_SECRET_BOXER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_ + +#include <cstddef> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicRandom; + +// CryptoSecretBoxer encrypts small chunks of plaintext (called 'boxing') and +// then, later, can authenticate+decrypt the resulting boxes. This object is +// thread-safe. +class QUIC_EXPORT_PRIVATE CryptoSecretBoxer { + public: + CryptoSecretBoxer(); + CryptoSecretBoxer(const CryptoSecretBoxer&) = delete; + CryptoSecretBoxer& operator=(const CryptoSecretBoxer&) = delete; + ~CryptoSecretBoxer(); + + // GetKeySize returns the number of bytes in a key. + static size_t GetKeySize(); + + // SetKeys sets a list of encryption keys. The first key in the list will be + // used by |Box|, but all supplied keys will be tried by |Unbox|, to handle + // key skew across the fleet. This must be called before |Box| or |Unbox|. + // Keys must be |GetKeySize()| bytes long. + void SetKeys(const std::vector<QuicString>& keys); + + // Box encrypts |plaintext| using a random nonce generated from |rand| and + // returns the resulting ciphertext. Since an authenticator and nonce are + // included, the result will be slightly larger than |plaintext|. The first + // key in the vector supplied to |SetKeys| will be used. + QuicString Box(QuicRandom* rand, QuicStringPiece plaintext) const; + + // Unbox takes the result of a previous call to |Box| in |ciphertext| and + // authenticates+decrypts it. If |ciphertext| cannot be decrypted with any of + // the supplied keys, the function returns false. Otherwise, |out_storage| is + // used to store the result and |out| is set to point into |out_storage| and + // contains the original plaintext. + bool Unbox(QuicStringPiece ciphertext, + QuicString* out_storage, + QuicStringPiece* out) const; + + private: + struct State; + + mutable QuicMutex lock_; + + // state_ is an opaque pointer to whatever additional state the concrete + // implementation of CryptoSecretBoxer requires. + std::unique_ptr<State> state_ GUARDED_BY(lock_); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_
diff --git a/quic/core/crypto/crypto_secret_boxer_test.cc b/quic/core/crypto/crypto_secret_boxer_test.cc new file mode 100644 index 0000000..d060051 --- /dev/null +++ b/quic/core/crypto/crypto_secret_boxer_test.cc
@@ -0,0 +1,79 @@ +// Copyright (c) 2013 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/crypto/crypto_secret_boxer.h" + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class CryptoSecretBoxerTest : public QuicTest {}; + +TEST_F(CryptoSecretBoxerTest, BoxAndUnbox) { + QuicStringPiece message("hello world"); + + CryptoSecretBoxer boxer; + boxer.SetKeys({QuicString(CryptoSecretBoxer::GetKeySize(), 0x11)}); + + const QuicString box = boxer.Box(QuicRandom::GetInstance(), message); + + QuicString storage; + QuicStringPiece result; + EXPECT_TRUE(boxer.Unbox(box, &storage, &result)); + EXPECT_EQ(result, message); + + EXPECT_FALSE(boxer.Unbox(QuicString(1, 'X') + box, &storage, &result)); + EXPECT_FALSE(boxer.Unbox(box.substr(1, QuicString::npos), &storage, &result)); + EXPECT_FALSE(boxer.Unbox(QuicString(), &storage, &result)); + EXPECT_FALSE(boxer.Unbox( + QuicString(1, box[0] ^ 0x80) + box.substr(1, QuicString::npos), &storage, + &result)); +} + +// Helper function to test whether one boxer can decode the output of another. +static bool CanDecode(const CryptoSecretBoxer& decoder, + const CryptoSecretBoxer& encoder) { + QuicStringPiece message("hello world"); + const QuicString boxed = encoder.Box(QuicRandom::GetInstance(), message); + QuicString storage; + QuicStringPiece result; + bool ok = decoder.Unbox(boxed, &storage, &result); + if (ok) { + EXPECT_EQ(result, message); + } + return ok; +} + +TEST_F(CryptoSecretBoxerTest, MultipleKeys) { + QuicString key_11(CryptoSecretBoxer::GetKeySize(), 0x11); + QuicString key_12(CryptoSecretBoxer::GetKeySize(), 0x12); + + CryptoSecretBoxer boxer_11, boxer_12, boxer; + boxer_11.SetKeys({key_11}); + boxer_12.SetKeys({key_12}); + boxer.SetKeys({key_12, key_11}); + + // Neither single-key boxer can decode the other's tokens. + EXPECT_FALSE(CanDecode(boxer_11, boxer_12)); + EXPECT_FALSE(CanDecode(boxer_12, boxer_11)); + + // |boxer| encodes with the first key, which is key_12. + EXPECT_TRUE(CanDecode(boxer_12, boxer)); + EXPECT_FALSE(CanDecode(boxer_11, boxer)); + + // The boxer with both keys can decode tokens from either single-key boxer. + EXPECT_TRUE(CanDecode(boxer, boxer_11)); + EXPECT_TRUE(CanDecode(boxer, boxer_12)); + + // After we flush key_11 from |boxer|, it can no longer decode tokens from + // |boxer_11|. + boxer.SetKeys({key_12}); + EXPECT_FALSE(CanDecode(boxer, boxer_11)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/crypto_server_test.cc b/quic/core/crypto/crypto_server_test.cc new file mode 100644 index 0000000..11615c2 --- /dev/null +++ b/quic/core/crypto/crypto_server_test.cc
@@ -0,0 +1,1174 @@ +// Copyright (c) 2013 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 <algorithm> +#include <cstdint> +#include <memory> +#include <ostream> +#include <vector> + +#include "third_party/boringssl/src/include/openssl/sha.h" +#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h" +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/failing_proof_source.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { + +namespace { + +class DummyProofVerifierCallback : public ProofVerifierCallback { + public: + DummyProofVerifierCallback() {} + ~DummyProofVerifierCallback() override {} + + void Run(bool ok, + const QuicString& error_details, + std::unique_ptr<ProofVerifyDetails>* details) override { + DCHECK(false); + } +}; + +const char kOldConfigId[] = "old-config-id"; + +} // namespace + +struct TestParams { + TestParams(bool enable_stateless_rejects, + bool use_stateless_rejects, + ParsedQuicVersionVector supported_versions) + : enable_stateless_rejects(enable_stateless_rejects), + use_stateless_rejects(use_stateless_rejects), + supported_versions(std::move(supported_versions)) {} + + friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { + os << " enable_stateless_rejects: " << p.enable_stateless_rejects + << std::endl; + os << " use_stateless_rejects: " << p.use_stateless_rejects << std::endl; + os << " versions: " + << ParsedQuicVersionVectorToString(p.supported_versions) << " }"; + return os; + } + + // This only enables the stateless reject feature via the feature-flag. + // It does not force the crypto server to emit stateless rejects. + bool enable_stateless_rejects; + // If true, this forces the server to send a stateless reject when + // rejecting messages. This should be a no-op if + // enable_stateless_rejects is false. + bool use_stateless_rejects; + // Versions supported by client and server. + ParsedQuicVersionVector supported_versions; +}; + +// Constructs various test permutations. +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + static const bool kTrueFalse[] = {true, false}; + for (bool enable_stateless_rejects : kTrueFalse) { + for (bool use_stateless_rejects : kTrueFalse) { + // Start with all versions, remove highest on each iteration. + ParsedQuicVersionVector supported_versions = AllSupportedVersions(); + while (!supported_versions.empty()) { + params.push_back(TestParams(enable_stateless_rejects, + use_stateless_rejects, supported_versions)); + supported_versions.erase(supported_versions.begin()); + } + } + } + return params; +} + +class CryptoServerTest : public QuicTestWithParam<TestParams> { + public: + CryptoServerTest() + : rand_(QuicRandom::GetInstance()), + client_address_(QuicIpAddress::Loopback4(), 1234), + client_version_(UnsupportedQuicVersion()), + config_(QuicCryptoServerConfig::TESTING, + rand_, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + peer_(&config_), + compressed_certs_cache_( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), + params_(new QuicCryptoNegotiatedParameters), + signed_config_(new QuicSignedServerConfig), + chlo_packet_size_(kDefaultMaxPacketSize) { + supported_versions_ = GetParam().supported_versions; + config_.set_enable_serving_sct(true); + + client_version_ = supported_versions_.front(); + client_version_string_ = ParsedQuicVersionToString(client_version_); + + SetQuicReloadableFlag(enable_quic_stateless_reject_support, + GetParam().enable_stateless_rejects); + use_stateless_rejects_ = GetParam().use_stateless_rejects; + } + + void SetUp() override { + QuicCryptoServerConfig::ConfigOptions old_config_options; + old_config_options.id = kOldConfigId; + delete config_.AddDefaultConfig(rand_, &clock_, old_config_options); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); + std::unique_ptr<QuicServerConfigProtobuf> primary_config( + config_.GenerateConfig(rand_, &clock_, config_options_)); + primary_config->set_primary_time(clock_.WallNow().ToUNIXSeconds()); + std::unique_ptr<CryptoHandshakeMessage> msg( + config_.AddConfig(std::move(primary_config), clock_.WallNow())); + + QuicStringPiece orbit; + CHECK(msg->GetStringPiece(kORBT, &orbit)); + CHECK_EQ(sizeof(orbit_), orbit.size()); + memcpy(orbit_, orbit.data(), orbit.size()); + + char public_value[32]; + memset(public_value, 42, sizeof(public_value)); + + nonce_hex_ = "#" + QuicTextUtils::HexEncode(GenerateNonce()); + pub_hex_ = + "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value)); + + CryptoHandshakeMessage client_hello = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"CSCT", ""}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + ShouldSucceed(client_hello); + // The message should be rejected because the source-address token is + // missing. + CheckRejectTag(); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); + CheckForServerDesignatedConnectionId(); + + QuicStringPiece srct; + ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct)); + srct_hex_ = "#" + QuicTextUtils::HexEncode(srct); + + QuicStringPiece scfg; + ASSERT_TRUE(out_.GetStringPiece(kSCFG, &scfg)); + server_config_ = CryptoFramer::ParseMessage(scfg); + + QuicStringPiece scid; + ASSERT_TRUE(server_config_->GetStringPiece(kSCID, &scid)); + scid_hex_ = "#" + QuicTextUtils::HexEncode(scid); + + signed_config_ = QuicReferenceCountedPointer<QuicSignedServerConfig>( + new QuicSignedServerConfig()); + DCHECK(signed_config_->chain.get() == nullptr); + } + + // Helper used to accept the result of ValidateClientHello and pass + // it on to ProcessClientHello. + class ValidateCallback : public ValidateClientHelloResultCallback { + public: + ValidateCallback(CryptoServerTest* test, + bool should_succeed, + const char* error_substr, + bool* called) + : test_(test), + should_succeed_(should_succeed), + error_substr_(error_substr), + called_(called) { + *called_ = false; + } + + void Run(QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> /* details */) override { + ASSERT_FALSE(*called_); + test_->ProcessValidationResult(std::move(result), should_succeed_, + error_substr_); + *called_ = true; + } + + private: + CryptoServerTest* test_; + const bool should_succeed_; + const char* const error_substr_; + bool* called_; + }; + + void CheckServerHello(const CryptoHandshakeMessage& server_hello) { + QuicVersionLabelVector versions; + server_hello.GetVersionLabelList(kVER, &versions); + ASSERT_EQ(supported_versions_.size(), versions.size()); + for (size_t i = 0; i < versions.size(); ++i) { + EXPECT_EQ(CreateQuicVersionLabel(supported_versions_[i]), versions[i]); + } + + QuicStringPiece address; + ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address)); + QuicSocketAddressCoder decoder; + ASSERT_TRUE(decoder.Decode(address.data(), address.size())); + EXPECT_EQ(client_address_.host(), decoder.ip()); + EXPECT_EQ(client_address_.port(), decoder.port()); + } + + void ShouldSucceed(const CryptoHandshakeMessage& message) { + bool called = false; + QuicSocketAddress server_address(QuicIpAddress::Any4(), 5); + config_.ValidateClientHello( + message, client_address_.host(), server_address, + supported_versions_.front().transport_version, &clock_, signed_config_, + QuicMakeUnique<ValidateCallback>(this, true, "", &called)); + EXPECT_TRUE(called); + } + + void ShouldFailMentioning(const char* error_substr, + const CryptoHandshakeMessage& message) { + bool called = false; + ShouldFailMentioning(error_substr, message, &called); + EXPECT_TRUE(called); + } + + void ShouldFailMentioning(const char* error_substr, + const CryptoHandshakeMessage& message, + bool* called) { + QuicSocketAddress server_address(QuicIpAddress::Any4(), 5); + config_.ValidateClientHello( + message, client_address_.host(), server_address, + supported_versions_.front().transport_version, &clock_, signed_config_, + QuicMakeUnique<ValidateCallback>(this, false, error_substr, called)); + } + + class ProcessCallback : public ProcessClientHelloResultCallback { + public: + ProcessCallback( + QuicReferenceCountedPointer<ValidateCallback::Result> result, + bool should_succeed, + const char* error_substr, + bool* called, + CryptoHandshakeMessage* out) + : result_(std::move(result)), + should_succeed_(should_succeed), + error_substr_(error_substr), + called_(called), + out_(out) { + *called_ = false; + } + + void Run( + QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> proof_source_details) override { + if (should_succeed_) { + ASSERT_EQ(error, QUIC_NO_ERROR) + << "Message failed with error " << error_details << ": " + << result_->client_hello.DebugString(); + } else { + ASSERT_NE(error, QUIC_NO_ERROR) + << "Message didn't fail: " << result_->client_hello.DebugString(); + + EXPECT_TRUE(error_details.find(error_substr_) != QuicString::npos) + << error_substr_ << " not in " << error_details; + } + if (message != nullptr) { + *out_ = *message; + } + *called_ = true; + } + + private: + const QuicReferenceCountedPointer<ValidateCallback::Result> result_; + const bool should_succeed_; + const char* const error_substr_; + bool* called_; + CryptoHandshakeMessage* out_; + }; + + void ProcessValidationResult( + QuicReferenceCountedPointer<ValidateCallback::Result> result, + bool should_succeed, + const char* error_substr) { + QuicSocketAddress server_address(QuicIpAddress::Any4(), 5); + QuicConnectionId server_designated_connection_id = + TestConnectionId(rand_for_id_generation_.RandUint64()); + bool called; + config_.ProcessClientHello( + result, /*reject_only=*/false, + /*connection_id=*/TestConnectionId(1), server_address, client_address_, + supported_versions_.front(), supported_versions_, + use_stateless_rejects_, server_designated_connection_id, &clock_, rand_, + &compressed_certs_cache_, params_, signed_config_, + /*total_framing_overhead=*/50, chlo_packet_size_, + QuicMakeUnique<ProcessCallback>(result, should_succeed, error_substr, + &called, &out_)); + EXPECT_TRUE(called); + } + + QuicString GenerateNonce() { + QuicString nonce; + CryptoUtils::GenerateNonce( + clock_.WallNow(), rand_, + QuicStringPiece(reinterpret_cast<const char*>(orbit_), sizeof(orbit_)), + &nonce); + return nonce; + } + + void CheckRejectReasons( + const HandshakeFailureReason* expected_handshake_failures, + size_t expected_count) { + QuicTagVector reject_reasons; + static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync"); + QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reasons); + ASSERT_EQ(QUIC_NO_ERROR, error_code); + + EXPECT_EQ(expected_count, reject_reasons.size()); + for (size_t i = 0; i < reject_reasons.size(); ++i) { + EXPECT_EQ(static_cast<QuicTag>(expected_handshake_failures[i]), + reject_reasons[i]); + } + } + + // If the server is rejecting statelessly, make sure it contains a + // server-designated connection id. Once the check is complete, + // allow the random id-generator to move to the next value. + void CheckForServerDesignatedConnectionId() { + uint64_t server_designated_connection_id; + if (!RejectsAreStateless()) { + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, + out_.GetUint64(kRCID, &server_designated_connection_id)); + } else { + ASSERT_EQ(QUIC_NO_ERROR, + out_.GetUint64(kRCID, &server_designated_connection_id)); + server_designated_connection_id = + QuicEndian::NetToHost64(server_designated_connection_id); + EXPECT_EQ(rand_for_id_generation_.RandUint64(), + server_designated_connection_id); + } + rand_for_id_generation_.ChangeValue(); + } + + void CheckRejectTag() { + if (RejectsAreStateless()) { + ASSERT_EQ(kSREJ, out_.tag()) << QuicTagToString(out_.tag()); + } else { + ASSERT_EQ(kREJ, out_.tag()) << QuicTagToString(out_.tag()); + } + } + + bool RejectsAreStateless() { + return GetParam().enable_stateless_rejects && + GetParam().use_stateless_rejects; + } + + QuicString XlctHexString() { + uint64_t xlct = crypto_test_utils::LeafCertHashForTesting(); + return "#" + QuicTextUtils::HexEncode(reinterpret_cast<char*>(&xlct), + sizeof(xlct)); + } + + protected: + QuicRandom* const rand_; + MockRandom rand_for_id_generation_; + MockClock clock_; + QuicSocketAddress client_address_; + ParsedQuicVersionVector supported_versions_; + ParsedQuicVersion client_version_; + QuicString client_version_string_; + QuicCryptoServerConfig config_; + QuicCryptoServerConfigPeer peer_; + QuicCompressedCertsCache compressed_certs_cache_; + QuicCryptoServerConfig::ConfigOptions config_options_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + CryptoHandshakeMessage out_; + uint8_t orbit_[kOrbitSize]; + bool use_stateless_rejects_; + size_t chlo_packet_size_; + + // These strings contain hex escaped values from the server suitable for using + // when constructing client hello messages. + QuicString nonce_hex_, pub_hex_, srct_hex_, scid_hex_; + std::unique_ptr<CryptoHandshakeMessage> server_config_; +}; + +INSTANTIATE_TEST_SUITE_P(CryptoServerTests, CryptoServerTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(CryptoServerTest, BadSNI) { + // clang-format off + static const char* const kBadSNIs[] = { + "", + "foo", + "#00", + "#ff00", + "127.0.0.1", + "ffee::1", + }; + // clang-format on + + for (size_t i = 0; i < QUIC_ARRAYSIZE(kBadSNIs); i++) { + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"SNI", kBadSNIs[i]}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + ShouldFailMentioning("SNI", msg); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); + } +} + +TEST_P(CryptoServerTest, DefaultCert) { + // Check that the server replies with a default certificate when no SNI is + // specified. The CHLO is constructed to generate a REJ with certs, so must + // not contain a valid STK, and must include PDMD. + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"PDMD", "X509"}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + QuicStringPiece cert, proof, cert_sct; + EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert)); + EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof)); + EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct)); + EXPECT_NE(0u, cert.size()); + EXPECT_NE(0u, proof.size()); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); + EXPECT_LT(0u, cert_sct.size()); +} + +TEST_P(CryptoServerTest, RejectTooLarge) { + // Check that the server replies with no certificate when a CHLO is + // constructed with a PDMD but no SKT when the REJ would be too large. + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"PDMD", "X509"}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + // The REJ will be larger than the CHLO so no PROF or CRT will be sent. + config_.set_chlo_multiplier(1); + + ShouldSucceed(msg); + QuicStringPiece cert, proof, cert_sct; + EXPECT_FALSE(out_.GetStringPiece(kCertificateTag, &cert)); + EXPECT_FALSE(out_.GetStringPiece(kPROF, &proof)); + EXPECT_FALSE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct)); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, RejectNotTooLarge) { + // When the CHLO packet is large enough, ensure that a full REJ is sent. + chlo_packet_size_ *= 2; + + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"PDMD", "X509"}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + // The REJ will be larger than the CHLO so no PROF or CRT will be sent. + config_.set_chlo_multiplier(1); + + ShouldSucceed(msg); + QuicStringPiece cert, proof, cert_sct; + EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert)); + EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof)); + EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct)); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, RejectTooLargeButValidSTK) { + // Check that the server replies with no certificate when a CHLO is + // constructed with a PDMD but no SKT when the REJ would be too large. + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"#004b5453", srct_hex_}, + {"PDMD", "X509"}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + // The REJ will be larger than the CHLO so no PROF or CRT will be sent. + config_.set_chlo_multiplier(1); + + ShouldSucceed(msg); + QuicStringPiece cert, proof, cert_sct; + EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert)); + EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof)); + EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct)); + EXPECT_NE(0u, cert.size()); + EXPECT_NE(0u, proof.size()); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, TooSmall) { + ShouldFailMentioning( + "too small", + crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"VER\0", client_version_string_.c_str()}})); + + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, BadSourceAddressToken) { + // Invalid source-address tokens should be ignored. + // clang-format off + static const char* const kBadSourceAddressTokens[] = { + "", + "foo", + "#0000", + "#0000000000000000000000000000000000000000", + }; + // clang-format on + + for (size_t i = 0; i < QUIC_ARRAYSIZE(kBadSourceAddressTokens); i++) { + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"STK", kBadSourceAddressTokens[i]}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + ShouldSucceed(msg); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); + } +} + +TEST_P(CryptoServerTest, BadClientNonce) { + // clang-format off + static const char* const kBadNonces[] = { + "", + "#0000", + "#0000000000000000000000000000000000000000", + }; + // clang-format on + + for (size_t i = 0; i < QUIC_ARRAYSIZE(kBadNonces); i++) { + // Invalid nonces should be ignored, in an inchoate CHLO. + + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"NONC", kBadNonces[i]}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); + + // Invalid nonces should result in CLIENT_NONCE_INVALID_FAILURE. + CryptoHandshakeMessage msg1 = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", kBadNonces[i]}, + {"NONP", kBadNonces[i]}, + {"XLCT", XlctHexString()}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg1); + + CheckRejectTag(); + const HandshakeFailureReason kRejectReasons1[] = { + CLIENT_NONCE_INVALID_FAILURE}; + CheckRejectReasons(kRejectReasons1, QUIC_ARRAYSIZE(kRejectReasons1)); + } +} + +TEST_P(CryptoServerTest, NoClientNonce) { + // No client nonces should result in INCHOATE_HELLO_FAILURE. + + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); + + CryptoHandshakeMessage msg1 = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"XLCT", XlctHexString()}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg1); + CheckRejectTag(); + const HandshakeFailureReason kRejectReasons1[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons1, QUIC_ARRAYSIZE(kRejectReasons1)); +} + +TEST_P(CryptoServerTest, DowngradeAttack) { + if (supported_versions_.size() == 1) { + // No downgrade attack is possible if the server only supports one version. + return; + } + // Set the client's preferred version to a supported version that + // is not the "current" version (supported_versions_.front()). + QuicString bad_version = + ParsedQuicVersionToString(supported_versions_.back()); + + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"VER\0", bad_version}}, kClientHelloMinimumSize); + + ShouldFailMentioning("Downgrade", msg); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, CorruptServerConfig) { + // This tests corrupted server config. + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", (QuicString(1, 'X') + scid_hex_)}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + CheckRejectTag(); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, CorruptSourceAddressToken) { + // This tests corrupted source address token. + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", (QuicString(1, 'X') + srct_hex_)}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"XLCT", XlctHexString()}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + CheckRejectTag(); + const HandshakeFailureReason kRejectReasons[] = { + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, CorruptSourceAddressTokenIsStillAccepted) { + // This tests corrupted source address token. + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", (QuicString(1, 'X') + srct_hex_)}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"XLCT", XlctHexString()}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + config_.set_validate_source_address_token(false); + + ShouldSucceed(msg); + EXPECT_EQ(kSHLO, out_.tag()); +} + +TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) { + // This test corrupts client nonce and source address token. + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", (QuicString(1, 'X') + srct_hex_)}, + {"PUBS", pub_hex_}, + {"NONC", (QuicString(1, 'X') + nonce_hex_)}, + {"XLCT", XlctHexString()}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + CheckRejectTag(); + const HandshakeFailureReason kRejectReasons[] = { + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, CorruptMultipleTags) { + // This test corrupts client nonce, server nonce and source address token. + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", (QuicString(1, 'X') + srct_hex_)}, + {"PUBS", pub_hex_}, + {"NONC", (QuicString(1, 'X') + nonce_hex_)}, + {"NONP", (QuicString(1, 'X') + nonce_hex_)}, + {"SNO\0", (QuicString(1, 'X') + nonce_hex_)}, + {"XLCT", XlctHexString()}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + CheckRejectTag(); + + const HandshakeFailureReason kRejectReasons[] = { + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, NoServerNonce) { + // When no server nonce is present and no strike register is configured, + // the CHLO should be rejected. + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"NONP", nonce_hex_}, + {"XLCT", XlctHexString()}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + + // Even without a server nonce, this ClientHello should be accepted in + // version 33. + ASSERT_EQ(kSHLO, out_.tag()); + CheckServerHello(out_); +} + +TEST_P(CryptoServerTest, ProofForSuppliedServerConfig) { + client_address_ = QuicSocketAddress(QuicIpAddress::Loopback6(), 1234); + + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"PDMD", "X509"}, + {"SCID", kOldConfigId}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"NONP", "123456789012345678901234567890"}, + {"VER\0", client_version_string_}, + {"XLCT", XlctHexString()}}, + kClientHelloMinimumSize); + + ShouldSucceed(msg); + // The message should be rejected because the source-address token is no + // longer valid. + CheckRejectTag(); + const HandshakeFailureReason kRejectReasons[] = { + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); + + QuicStringPiece cert, proof, scfg_str; + EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert)); + EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof)); + EXPECT_TRUE(out_.GetStringPiece(kSCFG, &scfg_str)); + std::unique_ptr<CryptoHandshakeMessage> scfg( + CryptoFramer::ParseMessage(scfg_str)); + QuicStringPiece scid; + EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid)); + EXPECT_NE(scid, kOldConfigId); + + // Get certs from compressed certs. + const CommonCertSets* common_cert_sets(CommonCertSets::GetInstanceQUIC()); + std::vector<QuicString> cached_certs; + + std::vector<QuicString> certs; + ASSERT_TRUE(CertCompressor::DecompressChain(cert, cached_certs, + common_cert_sets, &certs)); + + // Check that the proof in the REJ message is valid. + std::unique_ptr<ProofVerifier> proof_verifier( + crypto_test_utils::ProofVerifierForTesting()); + std::unique_ptr<ProofVerifyContext> verify_context( + crypto_test_utils::ProofVerifyContextForTesting()); + std::unique_ptr<ProofVerifyDetails> details; + QuicString error_details; + std::unique_ptr<ProofVerifierCallback> callback( + new DummyProofVerifierCallback()); + QuicString chlo_hash; + CryptoUtils::HashHandshakeMessage(msg, &chlo_hash, Perspective::IS_SERVER); + EXPECT_EQ(QUIC_SUCCESS, + proof_verifier->VerifyProof( + "test.example.com", 443, (QuicString(scfg_str)), + client_version_.transport_version, chlo_hash, certs, "", + (QuicString(proof)), verify_context.get(), &error_details, + &details, std::move(callback))); +} + +TEST_P(CryptoServerTest, RejectInvalidXlct) { + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"VER\0", client_version_string_}, + {"XLCT", "#0102030405060708"}}, + kClientHelloMinimumSize); + + // If replay protection isn't disabled, then + // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false + // and cause ProcessClientHello to exit early (and generate a REJ message). + config_.set_replay_protection(false); + + ShouldSucceed(msg); + + const HandshakeFailureReason kRejectReasons[] = { + INVALID_EXPECTED_LEAF_CERTIFICATE}; + + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +TEST_P(CryptoServerTest, ValidXlct) { + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"VER\0", client_version_string_}, + {"XLCT", XlctHexString()}}, + kClientHelloMinimumSize); + + // If replay protection isn't disabled, then + // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false + // and cause ProcessClientHello to exit early (and generate a REJ message). + config_.set_replay_protection(false); + + ShouldSucceed(msg); + EXPECT_EQ(kSHLO, out_.tag()); +} + +TEST_P(CryptoServerTest, NonceInSHLO) { + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"VER\0", client_version_string_}, + {"XLCT", XlctHexString()}}, + kClientHelloMinimumSize); + + // If replay protection isn't disabled, then + // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false + // and cause ProcessClientHello to exit early (and generate a REJ message). + config_.set_replay_protection(false); + + ShouldSucceed(msg); + EXPECT_EQ(kSHLO, out_.tag()); + + QuicStringPiece nonce; + EXPECT_TRUE(out_.GetStringPiece(kServerNonceTag, &nonce)); +} + +TEST_P(CryptoServerTest, ProofSourceFailure) { + // Install a ProofSource which will unconditionally fail + peer_.ResetProofSource(std::unique_ptr<ProofSource>(new FailingProofSource)); + + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"PDMD", "X509"}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + // Just ensure that we don't crash as occurred in b/33916924. + ShouldFailMentioning("", msg); +} + +// Regression test for crbug.com/723604 +// For 2RTT, if the first CHLO from the client contains hashes of cached +// certs (stored in CCRT tag) but the second CHLO does not, then the second REJ +// from the server should not contain hashes of cached certs. +TEST_P(CryptoServerTest, TwoRttServerDropCachedCerts) { + // Send inchoate CHLO to get cert chain from server. This CHLO is only for + // the purpose of getting the server's certs; it is not part of the 2RTT + // handshake. + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + ShouldSucceed(msg); + + // Decompress cert chain from server to individual certs. + QuicStringPiece certs_compressed; + ASSERT_TRUE(out_.GetStringPiece(kCertificateTag, &certs_compressed)); + ASSERT_NE(0u, certs_compressed.size()); + std::vector<QuicString> certs; + ASSERT_TRUE(CertCompressor::DecompressChain( + certs_compressed, /*cached_certs=*/{}, /*common_sets=*/nullptr, &certs)); + + // Start 2-RTT. Client sends CHLO with bad source-address token and hashes of + // the certs, which tells the server that the client has cached those certs. + config_.set_chlo_multiplier(1); + const char kBadSourceAddressToken[] = ""; + msg.SetStringPiece(kSourceAddressTokenTag, kBadSourceAddressToken); + std::vector<uint64_t> hashes(certs.size()); + for (size_t i = 0; i < certs.size(); ++i) { + hashes[i] = QuicUtils::QuicUtils::FNV1a_64_Hash(certs[i]); + } + msg.SetVector(kCCRT, hashes); + ShouldSucceed(msg); + + // Server responds with inchoate REJ containing valid source-address token. + QuicStringPiece srct; + ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct)); + + // Client now drops cached certs; sends CHLO with updated source-address + // token but no hashes of certs. + msg.SetStringPiece(kSourceAddressTokenTag, srct); + msg.Erase(kCCRT); + ShouldSucceed(msg); + + // Server response's cert chain should not contain hashes of + // previously-cached certs. + ASSERT_TRUE(out_.GetStringPiece(kCertificateTag, &certs_compressed)); + ASSERT_NE(0u, certs_compressed.size()); + ASSERT_TRUE(CertCompressor::DecompressChain( + certs_compressed, /*cached_certs=*/{}, /*common_sets=*/nullptr, &certs)); +} + +class CryptoServerConfigGenerationTest : public QuicTest {}; + +TEST_F(CryptoServerConfigGenerationTest, Determinism) { + // Test that using a deterministic PRNG causes the server-config to be + // deterministic. + + MockRandom rand_a, rand_b; + const QuicCryptoServerConfig::ConfigOptions options; + MockClock clock; + + QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + std::unique_ptr<CryptoHandshakeMessage> scfg_a( + a.AddDefaultConfig(&rand_a, &clock, options)); + std::unique_ptr<CryptoHandshakeMessage> scfg_b( + b.AddDefaultConfig(&rand_b, &clock, options)); + + ASSERT_EQ(scfg_a->DebugString(), scfg_b->DebugString()); +} + +TEST_F(CryptoServerConfigGenerationTest, SCIDVaries) { + // This test ensures that the server config ID varies for different server + // configs. + + MockRandom rand_a, rand_b; + const QuicCryptoServerConfig::ConfigOptions options; + MockClock clock; + + QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + rand_b.ChangeValue(); + QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + std::unique_ptr<CryptoHandshakeMessage> scfg_a( + a.AddDefaultConfig(&rand_a, &clock, options)); + std::unique_ptr<CryptoHandshakeMessage> scfg_b( + b.AddDefaultConfig(&rand_b, &clock, options)); + + QuicStringPiece scid_a, scid_b; + EXPECT_TRUE(scfg_a->GetStringPiece(kSCID, &scid_a)); + EXPECT_TRUE(scfg_b->GetStringPiece(kSCID, &scid_b)); + + EXPECT_NE(scid_a, scid_b); +} + +TEST_F(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) { + MockRandom rand_a; + const QuicCryptoServerConfig::ConfigOptions options; + MockClock clock; + + QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + std::unique_ptr<CryptoHandshakeMessage> scfg( + a.AddDefaultConfig(&rand_a, &clock, options)); + + QuicStringPiece scid; + EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid)); + // Need to take a copy of |scid| has we're about to call |Erase|. + const QuicString scid_str(scid); + + scfg->Erase(kSCID); + scfg->MarkDirty(); + const QuicData& serialized(scfg->GetSerialized()); + + uint8_t digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast<const uint8_t*>(serialized.data()), + serialized.length(), digest); + + // scid is a SHA-256 hash, truncated to 16 bytes. + ASSERT_EQ(scid.size(), 16u); + EXPECT_EQ(0, memcmp(digest, scid_str.c_str(), scid.size())); +} + +class CryptoServerTestNoConfig : public CryptoServerTest { + public: + void SetUp() override { + // Deliberately don't add a config so that we can test this situation. + } +}; + +TEST_P(CryptoServerTestNoConfig, DontCrash) { + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + ShouldFailMentioning("No config", msg); + + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; + CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons)); +} + +class CryptoServerTestOldVersion : public CryptoServerTest { + public: + void SetUp() override { + client_version_ = supported_versions_.back(); + client_version_string_ = ParsedQuicVersionToString(client_version_); + CryptoServerTest::SetUp(); + } +}; + +TEST_P(CryptoServerTestOldVersion, ServerIgnoresXlct) { + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"VER\0", client_version_string_}, + {"XLCT", "#0100000000000000"}}, + kClientHelloMinimumSize); + + // If replay protection isn't disabled, then + // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false + // and cause ProcessClientHello to exit early (and generate a REJ message). + config_.set_replay_protection(false); + + ShouldSucceed(msg); + EXPECT_EQ(kSHLO, out_.tag()); +} + +TEST_P(CryptoServerTestOldVersion, XlctNotRequired) { + CryptoHandshakeMessage msg = + crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"SCID", scid_hex_}, + {"#004b5453", srct_hex_}, + {"PUBS", pub_hex_}, + {"NONC", nonce_hex_}, + {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + + // If replay protection isn't disabled, then + // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false + // and cause ProcessClientHello to exit early (and generate a REJ message). + config_.set_replay_protection(false); + + ShouldSucceed(msg); + EXPECT_EQ(kSHLO, out_.tag()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/crypto_utils.cc b/quic/core/crypto/crypto_utils.cc new file mode 100644 index 0000000..4f6a61d --- /dev/null +++ b/quic/core/crypto/crypto_utils.cc
@@ -0,0 +1,469 @@ +// Copyright (c) 2013 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/crypto/crypto_utils.h" + +#include <memory> + +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/hkdf.h" +#include "third_party/boringssl/src/include/openssl/sha.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.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/crypto/quic_hkdf.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// TODO(nharper): HkdfExpandLabel and SetKeyAndIV (below) implement what is +// specified in draft-ietf-quic-tls-16. The latest editors' draft has changed +// derivation again, and this will need to be updated to reflect those (and any +// other future) changes. +// static +std::vector<uint8_t> CryptoUtils::HkdfExpandLabel( + const EVP_MD* prf, + const std::vector<uint8_t>& secret, + const QuicString& label, + size_t out_len) { + bssl::ScopedCBB quic_hkdf_label; + CBB inner_label; + const char label_prefix[] = "quic "; + // The minimum possible length for the QuicHkdfLabel is 9 bytes - 2 bytes for + // Length, plus 1 byte for the length of the inner label, plus the length of + // that label (which is at least 5), plus 1 byte at the end. + if (!CBB_init(quic_hkdf_label.get(), 9) || + !CBB_add_u16(quic_hkdf_label.get(), out_len) || + !CBB_add_u8_length_prefixed(quic_hkdf_label.get(), &inner_label) || + !CBB_add_bytes(&inner_label, + reinterpret_cast<const uint8_t*>(label_prefix), + QUIC_ARRAYSIZE(label_prefix) - 1) || + !CBB_add_bytes(&inner_label, + reinterpret_cast<const uint8_t*>(label.data()), + label.size()) || + !CBB_add_u8(quic_hkdf_label.get(), 0) || + !CBB_flush(quic_hkdf_label.get())) { + QUIC_LOG(ERROR) << "Building HKDF label failed"; + return std::vector<uint8_t>(); + } + std::vector<uint8_t> out; + out.resize(out_len); + if (!HKDF_expand(out.data(), out_len, prf, secret.data(), secret.size(), + CBB_data(quic_hkdf_label.get()), + CBB_len(quic_hkdf_label.get()))) { + QUIC_LOG(ERROR) << "Running HKDF-Expand-Label failed"; + return std::vector<uint8_t>(); + } + return out; +} + +void CryptoUtils::SetKeyAndIV(const EVP_MD* prf, + const std::vector<uint8_t>& pp_secret, + QuicCrypter* crypter) { + std::vector<uint8_t> key = CryptoUtils::HkdfExpandLabel( + prf, pp_secret, "key", crypter->GetKeySize()); + std::vector<uint8_t> iv = + CryptoUtils::HkdfExpandLabel(prf, pp_secret, "iv", crypter->GetIVSize()); + crypter->SetKey( + QuicStringPiece(reinterpret_cast<char*>(key.data()), key.size())); + crypter->SetIV( + QuicStringPiece(reinterpret_cast<char*>(iv.data()), iv.size())); +} + +namespace { + +const uint8_t kInitialSalt[] = {0x9c, 0x10, 0x8f, 0x98, 0x52, 0x0a, 0x5c, + 0x5c, 0x32, 0x96, 0x8e, 0x95, 0x0e, 0x8a, + 0x2c, 0x5f, 0xe0, 0x6d, 0x6c, 0x38}; + +const char kPreSharedKeyLabel[] = "QUIC PSK"; + +} // namespace + +// static +void CryptoUtils::CreateTlsInitialCrypters(Perspective perspective, + QuicTransportVersion version, + QuicConnectionId connection_id, + CrypterPair* crypters) { + QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(connection_id, version)) + << "CreateTlsInitialCrypters: attempted to use connection ID " + << connection_id << " which is invalid with version " + << QuicVersionToString(version); + const EVP_MD* hash = EVP_sha256(); + + std::vector<uint8_t> handshake_secret; + handshake_secret.resize(EVP_MAX_MD_SIZE); + size_t handshake_secret_len; + const bool hkdf_extract_success = HKDF_extract( + handshake_secret.data(), &handshake_secret_len, hash, + reinterpret_cast<const uint8_t*>(connection_id.data()), + connection_id.length(), kInitialSalt, QUIC_ARRAYSIZE(kInitialSalt)); + QUIC_BUG_IF(!hkdf_extract_success) + << "HKDF_extract failed when creating initial crypters"; + handshake_secret.resize(handshake_secret_len); + + const QuicString client_label = "client in"; + const QuicString server_label = "server in"; + QuicString encryption_label, decryption_label; + if (perspective == Perspective::IS_CLIENT) { + encryption_label = client_label; + decryption_label = server_label; + } else { + encryption_label = server_label; + decryption_label = client_label; + } + crypters->encrypter = QuicMakeUnique<Aes128GcmEncrypter>(); + std::vector<uint8_t> encryption_secret = HkdfExpandLabel( + hash, handshake_secret, encryption_label, EVP_MD_size(hash)); + SetKeyAndIV(hash, encryption_secret, crypters->encrypter.get()); + + crypters->decrypter = QuicMakeUnique<Aes128GcmDecrypter>(); + std::vector<uint8_t> decryption_secret = HkdfExpandLabel( + hash, handshake_secret, decryption_label, EVP_MD_size(hash)); + SetKeyAndIV(hash, decryption_secret, crypters->decrypter.get()); +} + +// static +void CryptoUtils::GenerateNonce(QuicWallTime now, + QuicRandom* random_generator, + QuicStringPiece orbit, + QuicString* nonce) { + // a 4-byte timestamp + 28 random bytes. + nonce->reserve(kNonceSize); + nonce->resize(kNonceSize); + + uint32_t gmt_unix_time = static_cast<uint32_t>(now.ToUNIXSeconds()); + // The time in the nonce must be encoded in big-endian because the + // strike-register depends on the nonces being ordered by time. + (*nonce)[0] = static_cast<char>(gmt_unix_time >> 24); + (*nonce)[1] = static_cast<char>(gmt_unix_time >> 16); + (*nonce)[2] = static_cast<char>(gmt_unix_time >> 8); + (*nonce)[3] = static_cast<char>(gmt_unix_time); + size_t bytes_written = 4; + + if (orbit.size() == 8) { + memcpy(&(*nonce)[bytes_written], orbit.data(), orbit.size()); + bytes_written += orbit.size(); + } + + random_generator->RandBytes(&(*nonce)[bytes_written], + kNonceSize - bytes_written); +} + +// static +bool CryptoUtils::DeriveKeys(QuicStringPiece premaster_secret, + QuicTag aead, + QuicStringPiece client_nonce, + QuicStringPiece server_nonce, + QuicStringPiece pre_shared_key, + const QuicString& hkdf_input, + Perspective perspective, + Diversification diversification, + CrypterPair* crypters, + QuicString* subkey_secret) { + // If the connection is using PSK, concatenate it with the pre-master secret. + std::unique_ptr<char[]> psk_premaster_secret; + if (!pre_shared_key.empty()) { + const QuicStringPiece label(kPreSharedKeyLabel); + const size_t psk_premaster_secret_size = label.size() + 1 + + pre_shared_key.size() + 8 + + premaster_secret.size() + 8; + + psk_premaster_secret = QuicMakeUnique<char[]>(psk_premaster_secret_size); + QuicDataWriter writer(psk_premaster_secret_size, psk_premaster_secret.get(), + HOST_BYTE_ORDER); + + if (!writer.WriteStringPiece(label) || !writer.WriteUInt8(0) || + !writer.WriteStringPiece(pre_shared_key) || + !writer.WriteUInt64(pre_shared_key.size()) || + !writer.WriteStringPiece(premaster_secret) || + !writer.WriteUInt64(premaster_secret.size()) || + writer.remaining() != 0) { + return false; + } + + premaster_secret = + QuicStringPiece(psk_premaster_secret.get(), psk_premaster_secret_size); + } + + crypters->encrypter = QuicEncrypter::Create(aead); + crypters->decrypter = QuicDecrypter::Create(aead); + size_t key_bytes = crypters->encrypter->GetKeySize(); + size_t nonce_prefix_bytes = crypters->encrypter->GetNoncePrefixSize(); + size_t subkey_secret_bytes = + subkey_secret == nullptr ? 0 : premaster_secret.length(); + + QuicStringPiece nonce = client_nonce; + QuicString nonce_storage; + if (!server_nonce.empty()) { + nonce_storage = QuicString(client_nonce) + QuicString(server_nonce); + nonce = nonce_storage; + } + + QuicHKDF hkdf(premaster_secret, nonce, hkdf_input, key_bytes, + nonce_prefix_bytes, subkey_secret_bytes); + + // Key derivation depends on the key diversification method being employed. + // both the client and the server support never doing key diversification. + // The server also supports immediate diversification, and the client + // supports pending diversification. + switch (diversification.mode()) { + case Diversification::NEVER: { + if (perspective == Perspective::IS_SERVER) { + if (!crypters->encrypter->SetKey(hkdf.server_write_key()) || + !crypters->encrypter->SetNoncePrefix(hkdf.server_write_iv()) || + !crypters->decrypter->SetKey(hkdf.client_write_key()) || + !crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv())) { + return false; + } + } else { + if (!crypters->encrypter->SetKey(hkdf.client_write_key()) || + !crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) || + !crypters->decrypter->SetKey(hkdf.server_write_key()) || + !crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) { + return false; + } + } + break; + } + case Diversification::PENDING: { + if (perspective == Perspective::IS_SERVER) { + QUIC_BUG << "Pending diversification is only for clients."; + return false; + } + + if (!crypters->encrypter->SetKey(hkdf.client_write_key()) || + !crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) || + !crypters->decrypter->SetPreliminaryKey(hkdf.server_write_key()) || + !crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) { + return false; + } + break; + } + case Diversification::NOW: { + if (perspective == Perspective::IS_CLIENT) { + QUIC_BUG << "Immediate diversification is only for servers."; + return false; + } + + QuicString key, nonce_prefix; + QuicDecrypter::DiversifyPreliminaryKey( + hkdf.server_write_key(), hkdf.server_write_iv(), + *diversification.nonce(), key_bytes, nonce_prefix_bytes, &key, + &nonce_prefix); + if (!crypters->decrypter->SetKey(hkdf.client_write_key()) || + !crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv()) || + !crypters->encrypter->SetKey(key) || + !crypters->encrypter->SetNoncePrefix(nonce_prefix)) { + return false; + } + break; + } + default: + DCHECK(false); + } + + if (subkey_secret != nullptr) { + *subkey_secret = QuicString(hkdf.subkey_secret()); + } + + return true; +} + +// static +bool CryptoUtils::ExportKeyingMaterial(QuicStringPiece subkey_secret, + QuicStringPiece label, + QuicStringPiece context, + size_t result_len, + QuicString* result) { + for (size_t i = 0; i < label.length(); i++) { + if (label[i] == '\0') { + QUIC_LOG(ERROR) << "ExportKeyingMaterial label may not contain NULs"; + return false; + } + } + // Create HKDF info input: null-terminated label + length-prefixed context + if (context.length() >= std::numeric_limits<uint32_t>::max()) { + QUIC_LOG(ERROR) << "Context value longer than 2^32"; + return false; + } + uint32_t context_length = static_cast<uint32_t>(context.length()); + QuicString info = QuicString(label); + info.push_back('\0'); + info.append(reinterpret_cast<char*>(&context_length), sizeof(context_length)); + info.append(context.data(), context.length()); + + QuicHKDF hkdf(subkey_secret, QuicStringPiece() /* no salt */, info, + result_len, 0 /* no fixed IV */, 0 /* no subkey secret */); + *result = QuicString(hkdf.client_write_key()); + return true; +} + +// static +uint64_t CryptoUtils::ComputeLeafCertHash(QuicStringPiece cert) { + return QuicUtils::FNV1a_64_Hash(cert); +} + +QuicErrorCode CryptoUtils::ValidateServerHello( + const CryptoHandshakeMessage& server_hello, + const ParsedQuicVersionVector& negotiated_versions, + QuicString* error_details) { + DCHECK(error_details != nullptr); + + if (server_hello.tag() != kSHLO) { + *error_details = "Bad tag"; + return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; + } + + QuicVersionLabelVector supported_version_labels; + if (server_hello.GetVersionLabelList(kVER, &supported_version_labels) != + QUIC_NO_ERROR) { + *error_details = "server hello missing version list"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + return ValidateServerHelloVersions(supported_version_labels, + negotiated_versions, error_details); +} + +QuicErrorCode CryptoUtils::ValidateServerHelloVersions( + const QuicVersionLabelVector& server_versions, + const ParsedQuicVersionVector& negotiated_versions, + QuicString* error_details) { + if (!negotiated_versions.empty()) { + bool mismatch = server_versions.size() != negotiated_versions.size(); + for (size_t i = 0; i < server_versions.size() && !mismatch; ++i) { + mismatch = + server_versions[i] != CreateQuicVersionLabel(negotiated_versions[i]); + } + // The server sent a list of supported versions, and the connection + // reports that there was a version negotiation during the handshake. + // Ensure that these two lists are identical. + if (mismatch) { + *error_details = QuicStrCat( + "Downgrade attack detected: ServerVersions(", server_versions.size(), + ")[", QuicVersionLabelVectorToString(server_versions, ",", 30), + "] NegotiatedVersions(", negotiated_versions.size(), ")[", + ParsedQuicVersionVectorToString(negotiated_versions, ",", 30), "]"); + return QUIC_VERSION_NEGOTIATION_MISMATCH; + } + } + return QUIC_NO_ERROR; +} + +QuicErrorCode CryptoUtils::ValidateClientHello( + const CryptoHandshakeMessage& client_hello, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + QuicString* error_details) { + if (client_hello.tag() != kCHLO) { + *error_details = "Bad tag"; + return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; + } + + // If the client's preferred version is not the version we are currently + // speaking, then the client went through a version negotiation. In this + // case, we need to make sure that we actually do not support this version + // and that it wasn't a downgrade attack. + QuicVersionLabel client_version_label; + if (client_hello.GetVersionLabel(kVER, &client_version_label) != + QUIC_NO_ERROR) { + *error_details = "client hello missing version list"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + return ValidateClientHelloVersion(client_version_label, version, + supported_versions, error_details); +} + +QuicErrorCode CryptoUtils::ValidateClientHelloVersion( + QuicVersionLabel client_version, + ParsedQuicVersion connection_version, + const ParsedQuicVersionVector& supported_versions, + QuicString* error_details) { + if (client_version != CreateQuicVersionLabel(connection_version)) { + // Check to see if |client_version| is actually on the supported versions + // list. If not, the server doesn't support that version and it's not a + // downgrade attack. + for (size_t i = 0; i < supported_versions.size(); ++i) { + if (client_version == CreateQuicVersionLabel(supported_versions[i])) { + *error_details = QuicStrCat( + "Downgrade attack detected: ClientVersion[", + QuicVersionLabelToString(client_version), "] SupportedVersions(", + supported_versions.size(), ")[", + ParsedQuicVersionVectorToString(supported_versions, ",", 30), "]"); + return QUIC_VERSION_NEGOTIATION_MISMATCH; + } + } + } + return QUIC_NO_ERROR; +} + +#define RETURN_STRING_LITERAL(x) \ + case x: \ + return #x + +// Returns the name of the HandshakeFailureReason as a char* +// static +const char* CryptoUtils::HandshakeFailureReasonToString( + HandshakeFailureReason reason) { + switch (reason) { + RETURN_STRING_LITERAL(HANDSHAKE_OK); + RETURN_STRING_LITERAL(CLIENT_NONCE_UNKNOWN_FAILURE); + RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_FAILURE); + RETURN_STRING_LITERAL(CLIENT_NONCE_NOT_UNIQUE_FAILURE); + RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_ORBIT_FAILURE); + RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_TIME_FAILURE); + RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT); + RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_FAILURE); + + RETURN_STRING_LITERAL(SERVER_NONCE_DECRYPTION_FAILURE); + RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_FAILURE); + RETURN_STRING_LITERAL(SERVER_NONCE_NOT_UNIQUE_FAILURE); + RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_TIME_FAILURE); + RETURN_STRING_LITERAL(SERVER_NONCE_REQUIRED_FAILURE); + + RETURN_STRING_LITERAL(SERVER_CONFIG_INCHOATE_HELLO_FAILURE); + RETURN_STRING_LITERAL(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); + + RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_INVALID_FAILURE); + RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE); + RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_PARSE_FAILURE); + RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE); + RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE); + RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE); + + RETURN_STRING_LITERAL(INVALID_EXPECTED_LEAF_CERTIFICATE); + RETURN_STRING_LITERAL(MAX_FAILURE_REASON); + } + // Return a default value so that we return this when |reason| doesn't match + // any HandshakeFailureReason.. This can happen when the message by the peer + // (attacker) has invalid reason. + return "INVALID_HANDSHAKE_FAILURE_REASON"; +} + +// static +void CryptoUtils::HashHandshakeMessage(const CryptoHandshakeMessage& message, + QuicString* output, + Perspective perspective) { + const QuicData& serialized = message.GetSerialized(); + uint8_t digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast<const uint8_t*>(serialized.data()), + serialized.length(), digest); + output->assign(reinterpret_cast<const char*>(digest), sizeof(digest)); +} + +#undef RETURN_STRING_LITERAL // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/crypto/crypto_utils.h b/quic/core/crypto/crypto_utils.h new file mode 100644 index 0000000..511bae9 --- /dev/null +++ b/quic/core/crypto/crypto_utils.h
@@ -0,0 +1,230 @@ +// Copyright (c) 2013 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. + +// Some helpers for quic crypto + +#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_ + +#include <cstddef> +#include <cstdint> + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/evp.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/quic_crypter.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicRandom; + +class QUIC_EXPORT_PRIVATE CryptoUtils { + public: + CryptoUtils() = delete; + + // Diversification is a utility class that's used to act like a union type. + // Values can be created by calling the functions like |NoDiversification|, + // below. + class Diversification { + public: + enum Mode { + NEVER, // Key diversification will never be used. Forward secure + // crypters will always use this mode. + + PENDING, // Key diversification will happen when a nonce is later + // received. This should only be used by clients initial + // decrypters which are waiting on the divesification nonce + // from the server. + + NOW, // Key diversification will happen immediate based on the nonce. + // This should only be used by servers initial encrypters. + }; + + Diversification(const Diversification& diversification) = default; + + static Diversification Never() { return Diversification(NEVER, nullptr); } + static Diversification Pending() { + return Diversification(PENDING, nullptr); + } + static Diversification Now(DiversificationNonce* nonce) { + return Diversification(NOW, nonce); + } + + Mode mode() const { return mode_; } + DiversificationNonce* nonce() const { + DCHECK_EQ(mode_, NOW); + return nonce_; + } + + private: + Diversification(Mode mode, DiversificationNonce* nonce) + : mode_(mode), nonce_(nonce) {} + + Mode mode_; + DiversificationNonce* nonce_; + }; + + // SetKeyAndIV derives the key and IV from the given packet protection secret + // |pp_secret| and sets those fields on the given QuicCrypter |*crypter|. + // This follows the derivation described in section 7.3 of RFC 8446, except + // with the label prefix in HKDF-Expand-Label changed from "tls13 " to "quic " + // as described in draft-ietf-quic-tls-14, section 5.1. + static void SetKeyAndIV(const EVP_MD* prf, + const std::vector<uint8_t>& pp_secret, + QuicCrypter* crypter); + + // QUIC encrypts TLS handshake messages with a version-specific key (to + // prevent network observers that are not aware of that QUIC version from + // making decisions based on the TLS handshake). This packet protection secret + // is derived from the connection ID in the client's Initial packet. + // + // This function takes that |connection_id| and creates the encrypter and + // decrypter (put in |*crypters|) to use for this packet protection, as well + // as setting the key and IV on those crypters. + static void CreateTlsInitialCrypters(Perspective perspective, + QuicTransportVersion version, + QuicConnectionId connection_id, + CrypterPair* crypters); + + // Generates the connection nonce. The nonce is formed as: + // <4 bytes> current time + // <8 bytes> |orbit| (or random if |orbit| is empty) + // <20 bytes> random + static void GenerateNonce(QuicWallTime now, + QuicRandom* random_generator, + QuicStringPiece orbit, + QuicString* nonce); + + // DeriveKeys populates |crypters->encrypter|, |crypters->decrypter|, and + // |subkey_secret| (optional -- may be null) given the contents of + // |premaster_secret|, |client_nonce|, |server_nonce| and |hkdf_input|. |aead| + // determines which cipher will be used. |perspective| controls whether the + // server's keys are assigned to |encrypter| or |decrypter|. |server_nonce| is + // optional and, if non-empty, is mixed into the key derivation. + // |subkey_secret| will have the same length as |premaster_secret|. + // + // If |pre_shared_key| is non-empty, it is incorporated into the key + // derivation parameters. If it is empty, the key derivation is unaltered. + // + // If the mode of |diversification| is NEVER, the the crypters will be + // configured to never perform key diversification. If the mode is + // NOW (which is only for servers, then the encrypter will be keyed via a + // two-step process that uses the nonce from |diversification|. + // If the mode is PENDING (which is only for servres), then the + // decrypter will only be keyed to a preliminary state: a call to + // |SetDiversificationNonce| with a diversification nonce will be needed to + // complete keying. + static bool DeriveKeys(QuicStringPiece premaster_secret, + QuicTag aead, + QuicStringPiece client_nonce, + QuicStringPiece server_nonce, + QuicStringPiece pre_shared_key, + const QuicString& hkdf_input, + Perspective perspective, + Diversification diversification, + CrypterPair* crypters, + QuicString* subkey_secret); + + // Performs key extraction to derive a new secret of |result_len| bytes + // dependent on |subkey_secret|, |label|, and |context|. Returns false if the + // parameters are invalid (e.g. |label| contains null bytes); returns true on + // success. + static bool ExportKeyingMaterial(QuicStringPiece subkey_secret, + QuicStringPiece label, + QuicStringPiece context, + size_t result_len, + QuicString* result); + + // Computes the FNV-1a hash of the provided DER-encoded cert for use in the + // XLCT tag. + static uint64_t ComputeLeafCertHash(QuicStringPiece cert); + + // Validates that |server_hello| is actually an SHLO message and that it is + // not part of a downgrade attack. + // + // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error + // code and sets |error_details|. + static QuicErrorCode ValidateServerHello( + const CryptoHandshakeMessage& server_hello, + const ParsedQuicVersionVector& negotiated_versions, + QuicString* error_details); + + // Validates that the |server_versions| received do not indicate that the + // ServerHello is part of a downgrade attack. |negotiated_versions| must + // contain the list of versions received in the server's version negotiation + // packet (or be empty if no such packet was received). + // + // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error + // code and sets |error_details|. + static QuicErrorCode ValidateServerHelloVersions( + const QuicVersionLabelVector& server_versions, + const ParsedQuicVersionVector& negotiated_versions, + QuicString* error_details); + + // Validates that |client_hello| is actually a CHLO and that this is not part + // of a downgrade attack. + // This includes verifiying versions and detecting downgrade attacks. + // + // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error + // code and sets |error_details|. + static QuicErrorCode ValidateClientHello( + const CryptoHandshakeMessage& client_hello, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + QuicString* error_details); + + // Validates that the |client_version| received does not indicate that a + // downgrade attack has occurred. |connection_version| is the version of the + // QuicConnection, and |supported_versions| is all versions that that + // QuicConnection supports. + // + // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error + // code and sets |error_details|. + static QuicErrorCode ValidateClientHelloVersion( + QuicVersionLabel client_version, + ParsedQuicVersion connection_version, + const ParsedQuicVersionVector& supported_versions, + QuicString* error_details); + + // Returns the name of the HandshakeFailureReason as a char* + static const char* HandshakeFailureReasonToString( + HandshakeFailureReason reason); + + // Writes a hash of the serialized |message| into |output|. + static void HashHandshakeMessage(const CryptoHandshakeMessage& message, + QuicString* output, + Perspective perspective); + + private: + // Implements the HKDF-Expand-Label function as defined in section 7.1 of RFC + // 8446, except that it uses "quic " as the prefix instead of "tls13 ", as + // specified by draft-ietf-quic-tls-14. The HKDF-Expand-Label function takes 4 + // explicit arguments (Secret, Label, Context, and Length), as well as + // implicit PRF which is the hash function negotiated by TLS. Its use in QUIC + // (as needed by the QUIC stack, instead of as used internally by the TLS + // stack) is only for deriving initial secrets for obfuscation and for + // calculating packet protection keys and IVs from the corresponding packet + // protection secret. Neither of these uses need a Context (a zero-length + // context is provided), so this argument is omitted here. + // + // The implicit PRF is explicitly passed into HkdfExpandLabel as |prf|; the + // Secret, Label, and Length are passed in as |secret|, |label|, and + // |out_len|, respectively. The resulting expanded secret is returned. + static std::vector<uint8_t> HkdfExpandLabel( + const EVP_MD* prf, + const std::vector<uint8_t>& secret, + const QuicString& label, + size_t out_len); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_
diff --git a/quic/core/crypto/crypto_utils_test.cc b/quic/core/crypto/crypto_utils_test.cc new file mode 100644 index 0000000..90a37af --- /dev/null +++ b/quic/core/crypto/crypto_utils_test.cc
@@ -0,0 +1,153 @@ +// Copyright (c) 2013 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/crypto/crypto_utils.h" + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +class CryptoUtilsTest : public QuicTest {}; + +TEST_F(CryptoUtilsTest, TestExportKeyingMaterial) { + const struct TestVector { + // Input (strings of hexadecimal digits): + const char* subkey_secret; + const char* label; + const char* context; + size_t result_len; + + // Expected output (string of hexadecimal digits): + const char* expected; // Null if it should fail. + } test_vector[] = { + // Try a typical input + {"4823c1189ecc40fce888fbb4cf9ae6254f19ba12e6d9af54788f195a6f509ca3", + "e934f78d7a71dd85420fceeb8cea0317", + "b8d766b5d3c8aba0009c7ed3de553eba53b4de1030ea91383dcdf724cd8b7217", 32, + "a9979da0d5f1c1387d7cbe68f5c4163ddb445a03c4ad6ee72cb49d56726d679e"}, + // Don't let the label contain nulls + {"14fe51e082ffee7d1b4d8d4ab41f8c55", "3132333435363700", + "58585858585858585858585858585858", 16, nullptr}, + // Make sure nulls in the context are fine + {"d862c2e36b0a42f7827c67ebc8d44df7", "7a5b95e4e8378123", + "4142434445464700", 16, "12d418c6d0738a2e4d85b2d0170f76e1"}, + // ... and give a different result than without + {"d862c2e36b0a42f7827c67ebc8d44df7", "7a5b95e4e8378123", "41424344454647", + 16, "abfa1c479a6e3ffb98a11dee7d196408"}, + // Try weird lengths + {"d0ec8a34f6cc9a8c96", "49711798cc6251", + "933d4a2f30d22f089cfba842791116adc121e0", 23, + "c9a46ed0757bd1812f1f21b4d41e62125fec8364a21db7"}, + }; + + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_vector); i++) { + // Decode the test vector. + QuicString subkey_secret = + QuicTextUtils::HexDecode(test_vector[i].subkey_secret); + QuicString label = QuicTextUtils::HexDecode(test_vector[i].label); + QuicString context = QuicTextUtils::HexDecode(test_vector[i].context); + size_t result_len = test_vector[i].result_len; + bool expect_ok = test_vector[i].expected != nullptr; + QuicString expected; + if (expect_ok) { + expected = QuicTextUtils::HexDecode(test_vector[i].expected); + } + + QuicString result; + bool ok = CryptoUtils::ExportKeyingMaterial(subkey_secret, label, context, + result_len, &result); + EXPECT_EQ(expect_ok, ok); + if (expect_ok) { + EXPECT_EQ(result_len, result.length()); + test::CompareCharArraysWithHexError("HKDF output", result.data(), + result.length(), expected.data(), + expected.length()); + } + } +} + +TEST_F(CryptoUtilsTest, HandshakeFailureReasonToString) { + EXPECT_STREQ("HANDSHAKE_OK", + CryptoUtils::HandshakeFailureReasonToString(HANDSHAKE_OK)); + EXPECT_STREQ("CLIENT_NONCE_UNKNOWN_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + CLIENT_NONCE_UNKNOWN_FAILURE)); + EXPECT_STREQ("CLIENT_NONCE_INVALID_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + CLIENT_NONCE_INVALID_FAILURE)); + EXPECT_STREQ("CLIENT_NONCE_NOT_UNIQUE_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + CLIENT_NONCE_NOT_UNIQUE_FAILURE)); + EXPECT_STREQ("CLIENT_NONCE_INVALID_ORBIT_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + CLIENT_NONCE_INVALID_ORBIT_FAILURE)); + EXPECT_STREQ("CLIENT_NONCE_INVALID_TIME_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + CLIENT_NONCE_INVALID_TIME_FAILURE)); + EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT", + CryptoUtils::HandshakeFailureReasonToString( + CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT)); + EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + CLIENT_NONCE_STRIKE_REGISTER_FAILURE)); + EXPECT_STREQ("SERVER_NONCE_DECRYPTION_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SERVER_NONCE_DECRYPTION_FAILURE)); + EXPECT_STREQ("SERVER_NONCE_INVALID_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SERVER_NONCE_INVALID_FAILURE)); + EXPECT_STREQ("SERVER_NONCE_NOT_UNIQUE_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SERVER_NONCE_NOT_UNIQUE_FAILURE)); + EXPECT_STREQ("SERVER_NONCE_INVALID_TIME_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SERVER_NONCE_INVALID_TIME_FAILURE)); + EXPECT_STREQ("SERVER_NONCE_REQUIRED_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SERVER_NONCE_REQUIRED_FAILURE)); + EXPECT_STREQ("SERVER_CONFIG_INCHOATE_HELLO_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SERVER_CONFIG_INCHOATE_HELLO_FAILURE)); + EXPECT_STREQ("SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE)); + EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_INVALID_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SOURCE_ADDRESS_TOKEN_INVALID_FAILURE)); + EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE)); + EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_PARSE_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SOURCE_ADDRESS_TOKEN_PARSE_FAILURE)); + EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE)); + EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE)); + EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE", + CryptoUtils::HandshakeFailureReasonToString( + SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE)); + EXPECT_STREQ("INVALID_EXPECTED_LEAF_CERTIFICATE", + CryptoUtils::HandshakeFailureReasonToString( + INVALID_EXPECTED_LEAF_CERTIFICATE)); + EXPECT_STREQ("MAX_FAILURE_REASON", + CryptoUtils::HandshakeFailureReasonToString(MAX_FAILURE_REASON)); + EXPECT_STREQ( + "INVALID_HANDSHAKE_FAILURE_REASON", + CryptoUtils::HandshakeFailureReasonToString( + static_cast<HandshakeFailureReason>(MAX_FAILURE_REASON + 1))); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/curve25519_key_exchange.cc b/quic/core/crypto/curve25519_key_exchange.cc new file mode 100644 index 0000000..368b453 --- /dev/null +++ b/quic/core/crypto/curve25519_key_exchange.cc
@@ -0,0 +1,102 @@ +// Copyright (c) 2013 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/crypto/curve25519_key_exchange.h" + +#include <cstdint> + +#include "third_party/boringssl/src/include/openssl/curve25519.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { +namespace { + +class Curve25519KeyExchangeFactory : public KeyExchange::Factory { + public: + Curve25519KeyExchangeFactory() = default; + ~Curve25519KeyExchangeFactory() override = default; + + std::unique_ptr<KeyExchange> Create(QuicRandom* rand) const override { + const QuicString private_value = Curve25519KeyExchange::NewPrivateKey(rand); + return Curve25519KeyExchange::New(private_value); + } + + QuicTag tag() const override { return kC255; } +}; + +} // namespace + +Curve25519KeyExchange::Curve25519KeyExchange() {} + +Curve25519KeyExchange::~Curve25519KeyExchange() {} + +// static +std::unique_ptr<Curve25519KeyExchange> Curve25519KeyExchange::New( + QuicStringPiece private_key) { + // We don't want to #include the BoringSSL headers in the public header file, + // so we use literals for the sizes of private_key_ and public_key_. Here we + // assert that those values are equal to the values from the BoringSSL + // header. + static_assert( + sizeof(Curve25519KeyExchange::private_key_) == X25519_PRIVATE_KEY_LEN, + "header out of sync"); + static_assert( + sizeof(Curve25519KeyExchange::public_key_) == X25519_PUBLIC_VALUE_LEN, + "header out of sync"); + + if (private_key.size() != X25519_PRIVATE_KEY_LEN) { + return nullptr; + } + + auto ka = QuicWrapUnique(new Curve25519KeyExchange); + memcpy(ka->private_key_, private_key.data(), X25519_PRIVATE_KEY_LEN); + X25519_public_from_private(ka->public_key_, ka->private_key_); + return ka; +} + +// static +QuicString Curve25519KeyExchange::NewPrivateKey(QuicRandom* rand) { + uint8_t private_key[X25519_PRIVATE_KEY_LEN]; + rand->RandBytes(private_key, sizeof(private_key)); + return QuicString(reinterpret_cast<char*>(private_key), sizeof(private_key)); +} + +const Curve25519KeyExchange::Factory& Curve25519KeyExchange::GetFactory() + const { + static const Factory* factory = new Curve25519KeyExchangeFactory; + return *factory; +} + +bool Curve25519KeyExchange::CalculateSharedKey( + QuicStringPiece peer_public_value, + QuicString* out_result) const { + if (peer_public_value.size() != X25519_PUBLIC_VALUE_LEN) { + return false; + } + + uint8_t result[X25519_PUBLIC_VALUE_LEN]; + if (!X25519(result, private_key_, + reinterpret_cast<const uint8_t*>(peer_public_value.data()))) { + return false; + } + + out_result->assign(reinterpret_cast<char*>(result), sizeof(result)); + return true; +} + +void Curve25519KeyExchange::CalculateSharedKey( + QuicStringPiece peer_public_value, + QuicString* shared_key, + std::unique_ptr<Callback> callback) const { + callback->Run(CalculateSharedKey(peer_public_value, shared_key)); +} + +QuicStringPiece Curve25519KeyExchange::public_value() const { + return QuicStringPiece(reinterpret_cast<const char*>(public_key_), + sizeof(public_key_)); +} + +} // namespace quic
diff --git a/quic/core/crypto/curve25519_key_exchange.h b/quic/core/crypto/curve25519_key_exchange.h new file mode 100644 index 0000000..a715e3e --- /dev/null +++ b/quic/core/crypto/curve25519_key_exchange.h
@@ -0,0 +1,52 @@ +// Copyright (c) 2013 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_CRYPTO_CURVE25519_KEY_EXCHANGE_H_ +#define QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicRandom; + +// Curve25519KeyExchange implements a KeyExchange using elliptic-curve +// Diffie-Hellman on curve25519. See http://cr.yp.to/ecdh.html +class QUIC_EXPORT_PRIVATE Curve25519KeyExchange : public KeyExchange { + public: + ~Curve25519KeyExchange() override; + + // New creates a new object from a private key. If the private key is + // invalid, nullptr is returned. + static std::unique_ptr<Curve25519KeyExchange> New( + QuicStringPiece private_key); + + // NewPrivateKey returns a private key, generated from |rand|, suitable for + // passing to |New|. + static QuicString NewPrivateKey(QuicRandom* rand); + + // KeyExchange interface. + const Factory& GetFactory() const override; + bool CalculateSharedKey(QuicStringPiece peer_public_value, + QuicString* shared_key) const override; + void CalculateSharedKey(QuicStringPiece peer_public_value, + QuicString* shared_key, + std::unique_ptr<Callback> callback) const override; + QuicStringPiece public_value() const override; + + private: + Curve25519KeyExchange(); + + uint8_t private_key_[32]; + uint8_t public_key_[32]; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
diff --git a/quic/core/crypto/curve25519_key_exchange_test.cc b/quic/core/crypto/curve25519_key_exchange_test.cc new file mode 100644 index 0000000..e5ed6e0 --- /dev/null +++ b/quic/core/crypto/curve25519_key_exchange_test.cc
@@ -0,0 +1,102 @@ +// Copyright (c) 2013 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/crypto/curve25519_key_exchange.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class Curve25519KeyExchangeTest : public QuicTest { + public: + // Holds the result of a key exchange callback. + class TestCallbackResult { + public: + void set_ok(bool ok) { ok_ = ok; } + bool ok() { return ok_; } + + private: + bool ok_ = false; + }; + + // Key exchange callback which sets the result into the specified + // TestCallbackResult. + class TestCallback : public KeyExchange::Callback { + public: + TestCallback(TestCallbackResult* result) : result_(result) {} + virtual ~TestCallback() = default; + + void Run(bool ok) { result_->set_ok(ok); } + + private: + TestCallbackResult* result_; + }; +}; + +// SharedKey just tests that the basic key exchange identity holds: that both +// parties end up with the same key. +TEST_F(Curve25519KeyExchangeTest, SharedKey) { + QuicRandom* const rand = QuicRandom::GetInstance(); + + for (int i = 0; i < 5; i++) { + const QuicString alice_key(Curve25519KeyExchange::NewPrivateKey(rand)); + const QuicString bob_key(Curve25519KeyExchange::NewPrivateKey(rand)); + + std::unique_ptr<Curve25519KeyExchange> alice( + Curve25519KeyExchange::New(alice_key)); + std::unique_ptr<Curve25519KeyExchange> bob( + Curve25519KeyExchange::New(bob_key)); + + const QuicStringPiece alice_public(alice->public_value()); + const QuicStringPiece bob_public(bob->public_value()); + + QuicString alice_shared, bob_shared; + ASSERT_TRUE(alice->CalculateSharedKey(bob_public, &alice_shared)); + ASSERT_TRUE(bob->CalculateSharedKey(alice_public, &bob_shared)); + ASSERT_EQ(alice_shared, bob_shared); + } +} + +// SharedKeyAsync just tests that the basic asynchronouse key exchange identity +// holds: that both parties end up with the same key. +TEST_F(Curve25519KeyExchangeTest, SharedKeyAsync) { + QuicRandom* const rand = QuicRandom::GetInstance(); + + for (int i = 0; i < 5; i++) { + const QuicString alice_key(Curve25519KeyExchange::NewPrivateKey(rand)); + const QuicString bob_key(Curve25519KeyExchange::NewPrivateKey(rand)); + + std::unique_ptr<Curve25519KeyExchange> alice( + Curve25519KeyExchange::New(alice_key)); + std::unique_ptr<Curve25519KeyExchange> bob( + Curve25519KeyExchange::New(bob_key)); + + const QuicStringPiece alice_public(alice->public_value()); + const QuicStringPiece bob_public(bob->public_value()); + + QuicString alice_shared, bob_shared; + TestCallbackResult alice_result; + ASSERT_FALSE(alice_result.ok()); + alice->CalculateSharedKey(bob_public, &alice_shared, + QuicMakeUnique<TestCallback>(&alice_result)); + ASSERT_TRUE(alice_result.ok()); + TestCallbackResult bob_result; + ASSERT_FALSE(bob_result.ok()); + bob->CalculateSharedKey(alice_public, &bob_shared, + QuicMakeUnique<TestCallback>(&bob_result)); + ASSERT_TRUE(bob_result.ok()); + ASSERT_EQ(alice_shared, bob_shared); + ASSERT_NE(0u, alice_shared.length()); + ASSERT_NE(0u, bob_shared.length()); + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/key_exchange.h b/quic/core/crypto/key_exchange.h new file mode 100644 index 0000000..84ca626 --- /dev/null +++ b/quic/core/crypto/key_exchange.h
@@ -0,0 +1,87 @@ +// Copyright (c) 2013 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_CRYPTO_KEY_EXCHANGE_H_ +#define QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_ + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicRandom; + +// KeyExchange is an abstract class that provides an interface to a +// key-exchange primitive. +class QUIC_EXPORT_PRIVATE KeyExchange { + public: + virtual ~KeyExchange() {} + + class Factory { + public: + virtual ~Factory() = default; + Factory(const Factory&) = delete; + Factory& operator=(const Factory&) = delete; + + // Generates a new public, private key pair. (This is intended for + // servers that need to generate forward-secure keys.) + virtual std::unique_ptr<KeyExchange> Create(QuicRandom* rand) const = 0; + + // Returns the tag value that identifies this key exchange function. + virtual QuicTag tag() const = 0; + + protected: + Factory() = default; + }; + + // Callback base class for receiving the results of an async call to + // CalculateSharedKeys. + class Callback { + public: + Callback() = default; + virtual ~Callback() = default; + + // Invoked upon completion of CalculateSharedKeys. + // + // |ok| indicates whether the operation completed successfully. If false, + // then the value of |shared_key| passed in to CalculateSharedKey is + // undefined. + virtual void Run(bool ok) = 0; + + private: + Callback(const Callback&) = delete; + Callback& operator=(const Callback&) = delete; + }; + + // Get a reference to the singleton Factory object for this KeyExchange type. + virtual const Factory& GetFactory() const = 0; + + // CalculateSharedKey computes the shared key between the local private key + // (which is implicitly known by a KeyExchange object) and a public value + // from the peer. + virtual bool CalculateSharedKey(QuicStringPiece peer_public_value, + QuicString* shared_key) const = 0; + + // CalculateSharedKey computes the shared key between the local private key + // (which is may not be locally known to a KeyExchange object) and a public + // value from the peer. + // Callers should expect that |callback| might be invoked synchronously. + virtual void CalculateSharedKey(QuicStringPiece peer_public_value, + QuicString* shared_key, + std::unique_ptr<Callback> callback) const = 0; + + // public_value returns the local public key which can be sent to a peer in + // order to complete a key exchange. The returned QuicStringPiece is a + // reference to a member of the KeyExchange and is only valid for as long as + // the KeyExchange exists. + virtual QuicStringPiece public_value() const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_
diff --git a/quic/core/crypto/null_decrypter.cc b/quic/core/crypto/null_decrypter.cc new file mode 100644 index 0000000..288d3c4 --- /dev/null +++ b/quic/core/crypto/null_decrypter.cc
@@ -0,0 +1,115 @@ +// Copyright (c) 2012 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/crypto/null_decrypter.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +NullDecrypter::NullDecrypter(Perspective perspective) + : perspective_(perspective) {} + +bool NullDecrypter::SetKey(QuicStringPiece key) { + return key.empty(); +} + +bool NullDecrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) { + return nonce_prefix.empty(); +} + +bool NullDecrypter::SetIV(QuicStringPiece iv) { + return iv.empty(); +} + +bool NullDecrypter::SetPreliminaryKey(QuicStringPiece key) { + QUIC_BUG << "Should not be called"; + return false; +} + +bool NullDecrypter::SetDiversificationNonce(const DiversificationNonce& nonce) { + QUIC_BUG << "Should not be called"; + return true; +} + +bool NullDecrypter::DecryptPacket(uint64_t /*packet_number*/, + QuicStringPiece associated_data, + QuicStringPiece ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) { + QuicDataReader reader(ciphertext.data(), ciphertext.length(), + HOST_BYTE_ORDER); + QuicUint128 hash; + + if (!ReadHash(&reader, &hash)) { + return false; + } + + QuicStringPiece plaintext = reader.ReadRemainingPayload(); + if (plaintext.length() > max_output_length) { + QUIC_BUG << "Output buffer must be larger than the plaintext."; + return false; + } + if (hash != ComputeHash(associated_data, plaintext)) { + return false; + } + // Copy the plaintext to output. + memcpy(output, plaintext.data(), plaintext.length()); + *output_length = plaintext.length(); + return true; +} + +size_t NullDecrypter::GetKeySize() const { + return 0; +} + +size_t NullDecrypter::GetIVSize() const { + return 0; +} + +QuicStringPiece NullDecrypter::GetKey() const { + return QuicStringPiece(); +} + +QuicStringPiece NullDecrypter::GetNoncePrefix() const { + return QuicStringPiece(); +} + +uint32_t NullDecrypter::cipher_id() const { + return 0; +} + +bool NullDecrypter::ReadHash(QuicDataReader* reader, QuicUint128* hash) { + uint64_t lo; + uint32_t hi; + if (!reader->ReadUInt64(&lo) || !reader->ReadUInt32(&hi)) { + return false; + } + *hash = MakeQuicUint128(hi, lo); + return true; +} + +QuicUint128 NullDecrypter::ComputeHash(const QuicStringPiece data1, + const QuicStringPiece data2) const { + QuicUint128 correct_hash; + if (perspective_ == Perspective::IS_CLIENT) { + // Peer is a server. + correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Server"); + } else { + // Peer is a client. + correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Client"); + } + QuicUint128 mask = MakeQuicUint128(UINT64_C(0x0), UINT64_C(0xffffffff)); + mask <<= 96; + correct_hash &= ~mask; + return correct_hash; +} + +} // namespace quic
diff --git a/quic/core/crypto/null_decrypter.h b/quic/core/crypto/null_decrypter.h new file mode 100644 index 0000000..8381987 --- /dev/null +++ b/quic/core/crypto/null_decrypter.h
@@ -0,0 +1,60 @@ +// Copyright (c) 2012 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_CRYPTO_NULL_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_ + +#include <cstddef> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +class QuicDataReader; + +// A NullDecrypter is a QuicDecrypter used before a crypto negotiation +// has occurred. It does not actually decrypt the payload, but does +// verify a hash (fnv128) over both the payload and associated data. +class QUIC_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { + public: + explicit NullDecrypter(Perspective perspective); + NullDecrypter(const NullDecrypter&) = delete; + NullDecrypter& operator=(const NullDecrypter&) = delete; + ~NullDecrypter() override {} + + // QuicDecrypter implementation + bool SetKey(QuicStringPiece key) override; + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override; + bool SetIV(QuicStringPiece iv) override; + bool SetPreliminaryKey(QuicStringPiece key) override; + bool SetDiversificationNonce(const DiversificationNonce& nonce) override; + bool DecryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) override; + size_t GetKeySize() const override; + size_t GetIVSize() const override; + QuicStringPiece GetKey() const override; + QuicStringPiece GetNoncePrefix() const override; + + uint32_t cipher_id() const override; + + private: + bool ReadHash(QuicDataReader* reader, QuicUint128* hash); + QuicUint128 ComputeHash(QuicStringPiece data1, QuicStringPiece data2) const; + + Perspective perspective_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_
diff --git a/quic/core/crypto/null_decrypter_test.cc b/quic/core/crypto/null_decrypter_test.cc new file mode 100644 index 0000000..09b1aaf --- /dev/null +++ b/quic/core/crypto/null_decrypter_test.cc
@@ -0,0 +1,136 @@ +// Copyright (c) 2012 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/crypto/null_decrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { + +class NullDecrypterTest : public QuicTestWithParam<bool> {}; + +TEST_F(NullDecrypterTest, DecryptClient) { + unsigned char expected[] = { + // fnv hash + 0x97, + 0xdc, + 0x27, + 0x2f, + 0x18, + 0xa8, + 0x56, + 0x73, + 0xdf, + 0x8d, + 0x1d, + 0xd0, + // payload + 'g', + 'o', + 'o', + 'd', + 'b', + 'y', + 'e', + '!', + }; + const char* data = reinterpret_cast<const char*>(expected); + size_t len = QUIC_ARRAYSIZE(expected); + NullDecrypter decrypter(Perspective::IS_SERVER); + char buffer[256]; + size_t length = 0; + ASSERT_TRUE(decrypter.DecryptPacket( + 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256)); + EXPECT_LT(0u, length); + EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length)); +} + +TEST_F(NullDecrypterTest, DecryptServer) { + unsigned char expected[] = { + // fnv hash + 0x63, + 0x5e, + 0x08, + 0x03, + 0x32, + 0x80, + 0x8f, + 0x73, + 0xdf, + 0x8d, + 0x1d, + 0x1a, + // payload + 'g', + 'o', + 'o', + 'd', + 'b', + 'y', + 'e', + '!', + }; + const char* data = reinterpret_cast<const char*>(expected); + size_t len = QUIC_ARRAYSIZE(expected); + NullDecrypter decrypter(Perspective::IS_CLIENT); + char buffer[256]; + size_t length = 0; + ASSERT_TRUE(decrypter.DecryptPacket( + 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256)); + EXPECT_LT(0u, length); + EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length)); +} + +TEST_F(NullDecrypterTest, BadHash) { + unsigned char expected[] = { + // fnv hash + 0x46, + 0x11, + 0xea, + 0x5f, + 0xcf, + 0x1d, + 0x66, + 0x5b, + 0xba, + 0xf0, + 0xbc, + 0xfd, + // payload + 'g', + 'o', + 'o', + 'd', + 'b', + 'y', + 'e', + '!', + }; + const char* data = reinterpret_cast<const char*>(expected); + size_t len = QUIC_ARRAYSIZE(expected); + NullDecrypter decrypter(Perspective::IS_CLIENT); + char buffer[256]; + size_t length = 0; + ASSERT_FALSE(decrypter.DecryptPacket( + 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256)); +} + +TEST_F(NullDecrypterTest, ShortInput) { + unsigned char expected[] = { + // fnv hash (truncated) + 0x46, 0x11, 0xea, 0x5f, 0xcf, 0x1d, 0x66, 0x5b, 0xba, 0xf0, 0xbc, + }; + const char* data = reinterpret_cast<const char*>(expected); + size_t len = QUIC_ARRAYSIZE(expected); + NullDecrypter decrypter(Perspective::IS_CLIENT); + char buffer[256]; + size_t length = 0; + ASSERT_FALSE(decrypter.DecryptPacket( + 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/null_encrypter.cc b/quic/core/crypto/null_encrypter.cc new file mode 100644 index 0000000..9819a31 --- /dev/null +++ b/quic/core/crypto/null_encrypter.cc
@@ -0,0 +1,88 @@ +// Copyright (c) 2012 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/crypto/null_encrypter.h" + +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" + +namespace quic { + +const size_t kHashSizeShort = 12; // size of uint128 serialized short + +NullEncrypter::NullEncrypter(Perspective perspective) + : perspective_(perspective) {} + +bool NullEncrypter::SetKey(QuicStringPiece key) { + return key.empty(); +} + +bool NullEncrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) { + return nonce_prefix.empty(); +} + +bool NullEncrypter::SetIV(QuicStringPiece iv) { + return iv.empty(); +} + +bool NullEncrypter::EncryptPacket(uint64_t /*packet_number*/, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + char* output, + size_t* output_length, + size_t max_output_length) { + const size_t len = plaintext.size() + GetHashLength(); + if (max_output_length < len) { + return false; + } + QuicUint128 hash; + if (perspective_ == Perspective::IS_SERVER) { + hash = + QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Server"); + } else { + hash = + QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Client"); + } + // TODO(ianswett): memmove required for in place encryption. Placing the + // hash at the end would allow use of memcpy, doing nothing for in place. + memmove(output + GetHashLength(), plaintext.data(), plaintext.length()); + QuicUtils::SerializeUint128Short(hash, + reinterpret_cast<unsigned char*>(output)); + *output_length = len; + return true; +} + +size_t NullEncrypter::GetKeySize() const { + return 0; +} + +size_t NullEncrypter::GetNoncePrefixSize() const { + return 0; +} + +size_t NullEncrypter::GetIVSize() const { + return 0; +} + +size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - GetHashLength(); +} + +size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + GetHashLength(); +} + +QuicStringPiece NullEncrypter::GetKey() const { + return QuicStringPiece(); +} + +QuicStringPiece NullEncrypter::GetNoncePrefix() const { + return QuicStringPiece(); +} + +size_t NullEncrypter::GetHashLength() const { + return kHashSizeShort; +} + +} // namespace quic
diff --git a/quic/core/crypto/null_encrypter.h b/quic/core/crypto/null_encrypter.h new file mode 100644 index 0000000..fe4487d --- /dev/null +++ b/quic/core/crypto/null_encrypter.h
@@ -0,0 +1,54 @@ +// Copyright (c) 2012 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_CRYPTO_NULL_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_ + +#include <cstddef> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// A NullEncrypter is a QuicEncrypter used before a crypto negotiation +// has occurred. It does not actually encrypt the payload, but does +// generate a MAC (fnv128) over both the payload and associated data. +class QUIC_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { + public: + explicit NullEncrypter(Perspective perspective); + NullEncrypter(const NullEncrypter&) = delete; + NullEncrypter& operator=(const NullEncrypter&) = delete; + ~NullEncrypter() override {} + + // QuicEncrypter implementation + bool SetKey(QuicStringPiece key) override; + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override; + bool SetIV(QuicStringPiece iv) override; + bool EncryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + char* output, + size_t* output_length, + size_t max_output_length) override; + size_t GetKeySize() const override; + size_t GetNoncePrefixSize() const override; + size_t GetIVSize() const override; + size_t GetMaxPlaintextSize(size_t ciphertext_size) const override; + size_t GetCiphertextSize(size_t plaintext_size) const override; + QuicStringPiece GetKey() const override; + QuicStringPiece GetNoncePrefix() const override; + + private: + size_t GetHashLength() const; + + Perspective perspective_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_
diff --git a/quic/core/crypto/null_encrypter_test.cc b/quic/core/crypto/null_encrypter_test.cc new file mode 100644 index 0000000..fd95cc6 --- /dev/null +++ b/quic/core/crypto/null_encrypter_test.cc
@@ -0,0 +1,100 @@ +// Copyright (c) 2012 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/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { + +class NullEncrypterTest : public QuicTestWithParam<bool> {}; + +TEST_F(NullEncrypterTest, EncryptClient) { + unsigned char expected[] = { + // fnv hash + 0x97, + 0xdc, + 0x27, + 0x2f, + 0x18, + 0xa8, + 0x56, + 0x73, + 0xdf, + 0x8d, + 0x1d, + 0xd0, + // payload + 'g', + 'o', + 'o', + 'd', + 'b', + 'y', + 'e', + '!', + }; + char encrypted[256]; + size_t encrypted_len = 0; + NullEncrypter encrypter(Perspective::IS_CLIENT); + ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted, + &encrypted_len, 256)); + test::CompareCharArraysWithHexError( + "encrypted data", encrypted, encrypted_len, + reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected)); +} + +TEST_F(NullEncrypterTest, EncryptServer) { + unsigned char expected[] = { + // fnv hash + 0x63, + 0x5e, + 0x08, + 0x03, + 0x32, + 0x80, + 0x8f, + 0x73, + 0xdf, + 0x8d, + 0x1d, + 0x1a, + // payload + 'g', + 'o', + 'o', + 'd', + 'b', + 'y', + 'e', + '!', + }; + char encrypted[256]; + size_t encrypted_len = 0; + NullEncrypter encrypter(Perspective::IS_SERVER); + ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted, + &encrypted_len, 256)); + test::CompareCharArraysWithHexError( + "encrypted data", encrypted, encrypted_len, + reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected)); +} + +TEST_F(NullEncrypterTest, GetMaxPlaintextSize) { + NullEncrypter encrypter(Perspective::IS_CLIENT); + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22)); +} + +TEST_F(NullEncrypterTest, GetCiphertextSize) { + NullEncrypter encrypter(Perspective::IS_CLIENT); + EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(112u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(22u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/p256_key_exchange.cc b/quic/core/crypto/p256_key_exchange.cc new file mode 100644 index 0000000..2bfbd09 --- /dev/null +++ b/quic/core/crypto/p256_key_exchange.cc
@@ -0,0 +1,143 @@ +// Copyright (c) 2013 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/crypto/p256_key_exchange.h" + +#include <cstdint> +#include <memory> +#include <utility> + +#include "third_party/boringssl/src/include/openssl/ec.h" +#include "third_party/boringssl/src/include/openssl/ecdh.h" +#include "third_party/boringssl/src/include/openssl/err.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { +namespace { + +class P256KeyExchangeFactory : public KeyExchange::Factory { + public: + P256KeyExchangeFactory() = default; + ~P256KeyExchangeFactory() override = default; + + std::unique_ptr<KeyExchange> Create(QuicRandom* /* rand */) const override { + // TODO(agl): avoid the serialisation/deserialisation in this function. + const QuicString private_value = P256KeyExchange::NewPrivateKey(); + return P256KeyExchange::New(private_value); + } + + QuicTag tag() const override { return kP256; } +}; + +} // namespace + +P256KeyExchange::P256KeyExchange(bssl::UniquePtr<EC_KEY> private_key, + const uint8_t* public_key) + : private_key_(std::move(private_key)) { + memcpy(public_key_, public_key, sizeof(public_key_)); +} + +P256KeyExchange::~P256KeyExchange() {} + +// static +std::unique_ptr<P256KeyExchange> P256KeyExchange::New(QuicStringPiece key) { + if (key.empty()) { + QUIC_DLOG(INFO) << "Private key is empty"; + return nullptr; + } + + const uint8_t* keyp = reinterpret_cast<const uint8_t*>(key.data()); + bssl::UniquePtr<EC_KEY> private_key( + d2i_ECPrivateKey(nullptr, &keyp, key.size())); + if (!private_key.get() || !EC_KEY_check_key(private_key.get())) { + QUIC_DLOG(INFO) << "Private key is invalid."; + return nullptr; + } + + uint8_t public_key[kUncompressedP256PointBytes]; + if (EC_POINT_point2oct(EC_KEY_get0_group(private_key.get()), + EC_KEY_get0_public_key(private_key.get()), + POINT_CONVERSION_UNCOMPRESSED, public_key, + sizeof(public_key), nullptr) != sizeof(public_key)) { + QUIC_DLOG(INFO) << "Can't get public key."; + return nullptr; + } + + return QuicWrapUnique( + new P256KeyExchange(std::move(private_key), public_key)); +} + +// static +QuicString P256KeyExchange::NewPrivateKey() { + bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (!key.get() || !EC_KEY_generate_key(key.get())) { + QUIC_DLOG(INFO) << "Can't generate a new private key."; + return QuicString(); + } + + int key_len = i2d_ECPrivateKey(key.get(), nullptr); + if (key_len <= 0) { + QUIC_DLOG(INFO) << "Can't convert private key to string"; + return QuicString(); + } + std::unique_ptr<uint8_t[]> private_key(new uint8_t[key_len]); + uint8_t* keyp = private_key.get(); + if (!i2d_ECPrivateKey(key.get(), &keyp)) { + QUIC_DLOG(INFO) << "Can't convert private key to string."; + return QuicString(); + } + return QuicString(reinterpret_cast<char*>(private_key.get()), key_len); +} + +const KeyExchange::Factory& P256KeyExchange::GetFactory() const { + static const Factory* factory = new P256KeyExchangeFactory; + return *factory; +} + +bool P256KeyExchange::CalculateSharedKey(QuicStringPiece peer_public_value, + QuicString* out_result) const { + if (peer_public_value.size() != kUncompressedP256PointBytes) { + QUIC_DLOG(INFO) << "Peer public value is invalid"; + return false; + } + + bssl::UniquePtr<EC_POINT> point( + EC_POINT_new(EC_KEY_get0_group(private_key_.get()))); + if (!point.get() || + !EC_POINT_oct2point(/* also test if point is on curve */ + EC_KEY_get0_group(private_key_.get()), point.get(), + reinterpret_cast<const uint8_t*>( + peer_public_value.data()), + peer_public_value.size(), nullptr)) { + QUIC_DLOG(INFO) << "Can't convert peer public value to curve point."; + return false; + } + + uint8_t result[kP256FieldBytes]; + if (ECDH_compute_key(result, sizeof(result), point.get(), private_key_.get(), + nullptr) != sizeof(result)) { + QUIC_DLOG(INFO) << "Can't compute ECDH shared key."; + return false; + } + + out_result->assign(reinterpret_cast<char*>(result), sizeof(result)); + return true; +} + +void P256KeyExchange::CalculateSharedKey( + QuicStringPiece peer_public_value, + QuicString* shared_key, + std::unique_ptr<Callback> callback) const { + callback->Run(CalculateSharedKey(peer_public_value, shared_key)); +} + +QuicStringPiece P256KeyExchange::public_value() const { + return QuicStringPiece(reinterpret_cast<const char*>(public_key_), + sizeof(public_key_)); +} + +} // namespace quic
diff --git a/quic/core/crypto/p256_key_exchange.h b/quic/core/crypto/p256_key_exchange.h new file mode 100644 index 0000000..d70d9cc --- /dev/null +++ b/quic/core/crypto/p256_key_exchange.h
@@ -0,0 +1,69 @@ +// Copyright (c) 2013 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_CRYPTO_P256_KEY_EXCHANGE_H_ +#define QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/base.h" +#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// P256KeyExchange implements a KeyExchange using elliptic-curve +// Diffie-Hellman on NIST P-256. +class QUIC_EXPORT_PRIVATE P256KeyExchange : public KeyExchange { + public: + ~P256KeyExchange() override; + + // New creates a new key exchange object from a private key. If + // |private_key| is invalid, nullptr is returned. + static std::unique_ptr<P256KeyExchange> New(QuicStringPiece private_key); + + // |NewPrivateKey| returns a private key, suitable for passing to |New|. + // If |NewPrivateKey| can't generate a private key, it returns an empty + // string. + static QuicString NewPrivateKey(); + + // KeyExchange interface. + const Factory& GetFactory() const override; + bool CalculateSharedKey(QuicStringPiece peer_public_value, + QuicString* shared_key) const override; + void CalculateSharedKey(QuicStringPiece peer_public_value, + QuicString* shared_key, + std::unique_ptr<Callback> callback) const override; + QuicStringPiece public_value() const override; + + private: + enum { + // A P-256 field element consists of 32 bytes. + kP256FieldBytes = 32, + // A P-256 point in uncompressed form consists of 0x04 (to denote + // that the point is uncompressed) followed by two, 32-byte field + // elements. + kUncompressedP256PointBytes = 1 + 2 * kP256FieldBytes, + // The first byte in an uncompressed P-256 point. + kUncompressedECPointForm = 0x04, + }; + + // P256KeyExchange wraps |private_key|, and expects |public_key| consists of + // |kUncompressedP256PointBytes| bytes. + P256KeyExchange(bssl::UniquePtr<EC_KEY> private_key, + const uint8_t* public_key); + P256KeyExchange(const P256KeyExchange&) = delete; + P256KeyExchange& operator=(const P256KeyExchange&) = delete; + + bssl::UniquePtr<EC_KEY> private_key_; + // The public key stored as an uncompressed P-256 point. + uint8_t public_key_[kUncompressedP256PointBytes]; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_
diff --git a/quic/core/crypto/p256_key_exchange_test.cc b/quic/core/crypto/p256_key_exchange_test.cc new file mode 100644 index 0000000..5deee9d --- /dev/null +++ b/quic/core/crypto/p256_key_exchange_test.cc
@@ -0,0 +1,107 @@ +// Copyright (c) 2013 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/crypto/p256_key_exchange.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class P256KeyExchangeTest : public QuicTest { + public: + // Holds the result of a key exchange callback. + class TestCallbackResult { + public: + void set_ok(bool ok) { ok_ = ok; } + bool ok() { return ok_; } + + private: + bool ok_ = false; + }; + + // Key exchange callback which sets the result into the specified + // TestCallbackResult. + class TestCallback : public KeyExchange::Callback { + public: + TestCallback(TestCallbackResult* result) : result_(result) {} + virtual ~TestCallback() = default; + + void Run(bool ok) { result_->set_ok(ok); } + + private: + TestCallbackResult* result_; + }; +}; + +// SharedKeyAsync just tests that the basic asynchronouse key exchange identity +// holds: that both parties end up with the same key. +TEST_F(P256KeyExchangeTest, SharedKey) { + for (int i = 0; i < 5; i++) { + QuicString alice_private(P256KeyExchange::NewPrivateKey()); + QuicString bob_private(P256KeyExchange::NewPrivateKey()); + + ASSERT_FALSE(alice_private.empty()); + ASSERT_FALSE(bob_private.empty()); + ASSERT_NE(alice_private, bob_private); + + std::unique_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private)); + std::unique_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private)); + + ASSERT_TRUE(alice != nullptr); + ASSERT_TRUE(bob != nullptr); + + const QuicStringPiece alice_public(alice->public_value()); + const QuicStringPiece bob_public(bob->public_value()); + + QuicString alice_shared, bob_shared; + ASSERT_TRUE(alice->CalculateSharedKey(bob_public, &alice_shared)); + ASSERT_TRUE(bob->CalculateSharedKey(alice_public, &bob_shared)); + ASSERT_EQ(alice_shared, bob_shared); + } +} + +// SharedKey just tests that the basic key exchange identity holds: that both +// parties end up with the same key. +TEST_F(P256KeyExchangeTest, AsyncSharedKey) { + for (int i = 0; i < 5; i++) { + QuicString alice_private(P256KeyExchange::NewPrivateKey()); + QuicString bob_private(P256KeyExchange::NewPrivateKey()); + + ASSERT_FALSE(alice_private.empty()); + ASSERT_FALSE(bob_private.empty()); + ASSERT_NE(alice_private, bob_private); + + std::unique_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private)); + std::unique_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private)); + + ASSERT_TRUE(alice != nullptr); + ASSERT_TRUE(bob != nullptr); + + const QuicStringPiece alice_public(alice->public_value()); + const QuicStringPiece bob_public(bob->public_value()); + + QuicString alice_shared, bob_shared; + TestCallbackResult alice_result; + ASSERT_FALSE(alice_result.ok()); + alice->CalculateSharedKey(bob_public, &alice_shared, + QuicMakeUnique<TestCallback>(&alice_result)); + ASSERT_TRUE(alice_result.ok()); + TestCallbackResult bob_result; + ASSERT_FALSE(bob_result.ok()); + bob->CalculateSharedKey(alice_public, &bob_shared, + QuicMakeUnique<TestCallback>(&bob_result)); + ASSERT_TRUE(bob_result.ok()); + ASSERT_EQ(alice_shared, bob_shared); + ASSERT_NE(0u, alice_shared.length()); + ASSERT_NE(0u, bob_shared.length()); + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/proof_source.cc b/quic/core/crypto/proof_source.cc new file mode 100644 index 0000000..ce77d8b --- /dev/null +++ b/quic/core/crypto/proof_source.cc
@@ -0,0 +1,15 @@ +// 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/crypto/proof_source.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +ProofSource::Chain::Chain(const std::vector<QuicString>& certs) + : certs(certs) {} + +ProofSource::Chain::~Chain() {} + +} // namespace quic
diff --git a/quic/core/crypto/proof_source.h b/quic/core/crypto/proof_source.h new file mode 100644 index 0000000..3fec32b --- /dev/null +++ b/quic/core/crypto/proof_source.h
@@ -0,0 +1,145 @@ +// Copyright (c) 2013 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_CRYPTO_PROOF_SOURCE_H_ +#define QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_ + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// ProofSource is an interface by which a QUIC server can obtain certificate +// chains and signatures that prove its identity. +class QUIC_EXPORT_PRIVATE ProofSource { + public: + // Chain is a reference-counted wrapper for a vector of stringified + // certificates. + struct QUIC_EXPORT_PRIVATE Chain : public QuicReferenceCounted { + explicit Chain(const std::vector<QuicString>& certs); + Chain(const Chain&) = delete; + Chain& operator=(const Chain&) = delete; + + const std::vector<QuicString> certs; + + protected: + ~Chain() override; + }; + + // Details is an abstract class which acts as a container for any + // implementation-specific details that a ProofSource wants to return. + class Details { + public: + virtual ~Details() {} + }; + + // Callback base class for receiving the results of an async call to GetProof. + class Callback { + public: + Callback() {} + virtual ~Callback() {} + + // Invoked upon completion of GetProof. + // + // |ok| indicates whether the operation completed successfully. If false, + // the values of the remaining three arguments are undefined. + // + // |chain| is a reference-counted pointer to an object representing the + // certificate chain. + // + // |signature| contains the signature of the server config. + // + // |leaf_cert_sct| holds the signed timestamp (RFC6962) of the leaf cert. + // + // |details| holds a pointer to an object representing the statistics, if + // any, gathered during the operation of GetProof. If no stats are + // available, this will be nullptr. + virtual void Run(bool ok, + const QuicReferenceCountedPointer<Chain>& chain, + const QuicCryptoProof& proof, + std::unique_ptr<Details> details) = 0; + + private: + Callback(const Callback&) = delete; + Callback& operator=(const Callback&) = delete; + }; + + // Base class for signalling the completion of a call to ComputeTlsSignature. + class SignatureCallback { + public: + SignatureCallback() {} + virtual ~SignatureCallback() = default; + + // Invoked upon completion of ComputeTlsSignature. + // + // |ok| indicates whether the operation completed successfully. + // + // |signature| contains the signature of the data provided to + // ComputeTlsSignature. Its value is undefined if |ok| is false. + virtual void Run(bool ok, QuicString signature) = 0; + + private: + SignatureCallback(const SignatureCallback&) = delete; + SignatureCallback& operator=(const SignatureCallback&) = delete; + }; + + virtual ~ProofSource() {} + + // GetProof finds a certificate chain for |hostname| (in leaf-first order), + // and calculates a signature of |server_config| using that chain. + // + // The signature uses SHA-256 as the hash function and PSS padding when the + // key is RSA. + // + // The signature uses SHA-256 as the hash function when the key is ECDSA. + // The signature may use an ECDSA key. + // + // The signature depends on |chlo_hash| which means that the signature can not + // be cached. + // + // |hostname| may be empty to signify that a default certificate should be + // used. + // + // This function may be called concurrently. + // + // Callers should expect that |callback| might be invoked synchronously. + virtual void GetProof(const QuicSocketAddress& server_address, + const QuicString& hostname, + const QuicString& server_config, + QuicTransportVersion transport_version, + QuicStringPiece chlo_hash, + std::unique_ptr<Callback> callback) = 0; + + // Returns the certificate chain for |hostname| in leaf-first order. + virtual QuicReferenceCountedPointer<Chain> GetCertChain( + const QuicSocketAddress& server_address, + const QuicString& hostname) = 0; + + // Computes a signature using the private key of the certificate for + // |hostname|. The value in |in| is signed using the algorithm specified by + // |signature_algorithm|, which is an |SSL_SIGN_*| value (as defined in TLS + // 1.3). Implementations can only assume that |in| is valid during the call to + // ComputeTlsSignature - an implementation computing signatures asynchronously + // must copy it if the value to be signed is used outside of this function. + // + // Callers should expect that |callback| might be invoked synchronously. + virtual void ComputeTlsSignature( + const QuicSocketAddress& server_address, + const QuicString& hostname, + uint16_t signature_algorithm, + QuicStringPiece in, + std::unique_ptr<SignatureCallback> callback) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
diff --git a/quic/core/crypto/proof_verifier.h b/quic/core/crypto/proof_verifier.h new file mode 100644 index 0000000..3219203 --- /dev/null +++ b/quic/core/crypto/proof_verifier.h
@@ -0,0 +1,119 @@ +// Copyright (c) 2013 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_CRYPTO_PROOF_VERIFIER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_ + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// ProofVerifyDetails is an abstract class that acts as a container for any +// implementation specific details that a ProofVerifier wishes to return. These +// details are saved in the CachedState for the origin in question. +class QUIC_EXPORT_PRIVATE ProofVerifyDetails { + public: + virtual ~ProofVerifyDetails() {} + + // Returns an new ProofVerifyDetails object with the same contents + // as this one. + virtual ProofVerifyDetails* Clone() const = 0; +}; + +// ProofVerifyContext is an abstract class that acts as a container for any +// implementation specific context that a ProofVerifier needs. +class QUIC_EXPORT_PRIVATE ProofVerifyContext { + public: + virtual ~ProofVerifyContext() {} +}; + +// ProofVerifierCallback provides a generic mechanism for a ProofVerifier to +// call back after an asynchronous verification. +class QUIC_EXPORT_PRIVATE ProofVerifierCallback { + public: + virtual ~ProofVerifierCallback() {} + + // Run is called on the original thread to mark the completion of an + // asynchonous verification. If |ok| is true then the certificate is valid + // and |error_details| is unused. Otherwise, |error_details| contains a + // description of the error. |details| contains implementation-specific + // details of the verification. |Run| may take ownership of |details| by + // calling |release| on it. + virtual void Run(bool ok, + const QuicString& error_details, + std::unique_ptr<ProofVerifyDetails>* details) = 0; +}; + +// A ProofVerifier checks the signature on a server config, and the certificate +// chain that backs the public key. +class QUIC_EXPORT_PRIVATE ProofVerifier { + public: + virtual ~ProofVerifier() {} + + // VerifyProof checks that |signature| is a valid signature of + // |server_config| by the public key in the leaf certificate of |certs|, and + // that |certs| is a valid chain for |hostname|. On success, it returns + // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details| + // to a description of the problem. In either case it may set |*details|, + // which the caller takes ownership of. + // + // |context| specifies an implementation specific struct (which may be nullptr + // for some implementations) that provides useful information for the + // verifier, e.g. logging handles. + // + // This function may also return QUIC_PENDING, in which case the ProofVerifier + // will call back, on the original thread, via |callback| when complete. + // + // The signature uses SHA-256 as the hash function and PSS padding in the + // case of RSA. + virtual QuicAsyncStatus VerifyProof( + const QuicString& hostname, + const uint16_t port, + const QuicString& server_config, + QuicTransportVersion transport_version, + QuicStringPiece chlo_hash, + const std::vector<QuicString>& certs, + const QuicString& cert_sct, + const QuicString& signature, + const ProofVerifyContext* context, + QuicString* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) = 0; + + // VerifyCertChain checks that |certs| is a valid chain for |hostname|. On + // success, it returns QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and + // sets |*error_details| to a description of the problem. In either case it + // may set |*details|, which the caller takes ownership of. + // + // |context| specifies an implementation specific struct (which may be nullptr + // for some implementations) that provides useful information for the + // verifier, e.g. logging handles. + // + // This function may also return QUIC_PENDING, in which case the ProofVerifier + // will call back, on the original thread, via |callback| when complete. + // In this case, the ProofVerifier will take ownership of |callback|. + virtual QuicAsyncStatus VerifyCertChain( + const QuicString& hostname, + const std::vector<QuicString>& certs, + const ProofVerifyContext* context, + QuicString* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) = 0; + + // Returns a ProofVerifyContext instance which can be use for subsequent + // verifications. Applications may chose create a different context and + // supply it for verifications instead. + virtual std::unique_ptr<ProofVerifyContext> CreateDefaultContext() = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_
diff --git a/quic/core/crypto/quic_compressed_certs_cache.cc b/quic/core/crypto/quic_compressed_certs_cache.cc new file mode 100644 index 0000000..595ac31 --- /dev/null +++ b/quic/core/crypto/quic_compressed_certs_cache.cc
@@ -0,0 +1,128 @@ +// Copyright 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/crypto/quic_compressed_certs_cache.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace { + +// Inline helper function for extending a 64-bit |seed| in-place with a 64-bit +// |value|. Based on Boost's hash_combine function. +inline void hash_combine(uint64_t* seed, const uint64_t& val) { + (*seed) ^= val + 0x9e3779b9 + ((*seed) << 6) + ((*seed) >> 2); +} + +} // namespace + +const size_t QuicCompressedCertsCache::kQuicCompressedCertsCacheSize = 225; + +QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts() + : chain(nullptr), + client_common_set_hashes(nullptr), + client_cached_cert_hashes(nullptr) {} + +QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts( + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString* client_common_set_hashes, + const QuicString* client_cached_cert_hashes) + : chain(chain), + client_common_set_hashes(client_common_set_hashes), + client_cached_cert_hashes(client_cached_cert_hashes) {} + +QuicCompressedCertsCache::UncompressedCerts::~UncompressedCerts() {} + +QuicCompressedCertsCache::CachedCerts::CachedCerts() {} + +QuicCompressedCertsCache::CachedCerts::CachedCerts( + const UncompressedCerts& uncompressed_certs, + const QuicString& compressed_cert) + : chain_(uncompressed_certs.chain), + client_common_set_hashes_(*uncompressed_certs.client_common_set_hashes), + client_cached_cert_hashes_(*uncompressed_certs.client_cached_cert_hashes), + compressed_cert_(compressed_cert) {} + +QuicCompressedCertsCache::CachedCerts::CachedCerts(const CachedCerts& other) = + default; + +QuicCompressedCertsCache::CachedCerts::~CachedCerts() {} + +bool QuicCompressedCertsCache::CachedCerts::MatchesUncompressedCerts( + const UncompressedCerts& uncompressed_certs) const { + return (client_common_set_hashes_ == + *uncompressed_certs.client_common_set_hashes && + client_cached_cert_hashes_ == + *uncompressed_certs.client_cached_cert_hashes && + chain_ == uncompressed_certs.chain); +} + +const QuicString* QuicCompressedCertsCache::CachedCerts::compressed_cert() + const { + return &compressed_cert_; +} + +QuicCompressedCertsCache::QuicCompressedCertsCache(int64_t max_num_certs) + : certs_cache_(max_num_certs) {} + +QuicCompressedCertsCache::~QuicCompressedCertsCache() { + // Underlying cache must be cleared before destruction. + certs_cache_.Clear(); +} + +const QuicString* QuicCompressedCertsCache::GetCompressedCert( + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes) { + UncompressedCerts uncompressed_certs(chain, &client_common_set_hashes, + &client_cached_cert_hashes); + + uint64_t key = ComputeUncompressedCertsHash(uncompressed_certs); + + CachedCerts* cached_value = certs_cache_.Lookup(key); + if (cached_value != nullptr && + cached_value->MatchesUncompressedCerts(uncompressed_certs)) { + return cached_value->compressed_cert(); + } + return nullptr; +} + +void QuicCompressedCertsCache::Insert( + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes, + const QuicString& compressed_cert) { + UncompressedCerts uncompressed_certs(chain, &client_common_set_hashes, + &client_cached_cert_hashes); + + uint64_t key = ComputeUncompressedCertsHash(uncompressed_certs); + + // Insert one unit to the cache. + std::unique_ptr<CachedCerts> cached_certs( + new CachedCerts(uncompressed_certs, compressed_cert)); + certs_cache_.Insert(key, std::move(cached_certs)); +} + +size_t QuicCompressedCertsCache::MaxSize() { + return certs_cache_.MaxSize(); +} + +size_t QuicCompressedCertsCache::Size() { + return certs_cache_.Size(); +} + +uint64_t QuicCompressedCertsCache::ComputeUncompressedCertsHash( + const UncompressedCerts& uncompressed_certs) { + uint64_t hash = + std::hash<QuicString>()(*uncompressed_certs.client_common_set_hashes); + uint64_t h = + std::hash<QuicString>()(*uncompressed_certs.client_cached_cert_hashes); + hash_combine(&hash, h); + + hash_combine(&hash, + reinterpret_cast<uint64_t>(uncompressed_certs.chain.get())); + return hash; +} + +} // namespace quic
diff --git a/quic/core/crypto/quic_compressed_certs_cache.h b/quic/core/crypto/quic_compressed_certs_cache.h new file mode 100644 index 0000000..418bdb9 --- /dev/null +++ b/quic/core/crypto/quic_compressed_certs_cache.h
@@ -0,0 +1,108 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_ + +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h" +#include "net/third_party/quiche/src/quic/core/quic_lru_cache.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// QuicCompressedCertsCache is a cache to track most recently compressed certs. +class QUIC_EXPORT_PRIVATE QuicCompressedCertsCache { + public: + explicit QuicCompressedCertsCache(int64_t max_num_certs); + ~QuicCompressedCertsCache(); + + // Returns the pointer to the cached compressed cert if + // |chain, client_common_set_hashes, client_cached_cert_hashes| hits cache. + // Otherwise, return nullptr. + // Returned pointer might become invalid on the next call to Insert(). + const QuicString* GetCompressedCert( + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes); + + // Inserts the specified + // |chain, client_common_set_hashes, + // client_cached_cert_hashes, compressed_cert| tuple to the cache. + // If the insertion causes the cache to become overfull, entries will + // be deleted in an LRU order to make room. + void Insert(const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes, + const QuicString& compressed_cert); + + // Returns max number of cache entries the cache can carry. + size_t MaxSize(); + + // Returns current number of cache entries in the cache. + size_t Size(); + + // Default size of the QuicCompressedCertsCache per server side investigation. + static const size_t kQuicCompressedCertsCacheSize; + + private: + // A wrapper of the tuple: + // |chain, client_common_set_hashes, client_cached_cert_hashes| + // to identify uncompressed representation of certs. + struct UncompressedCerts { + UncompressedCerts(); + UncompressedCerts( + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString* client_common_set_hashes, + const QuicString* client_cached_cert_hashes); + ~UncompressedCerts(); + + const QuicReferenceCountedPointer<ProofSource::Chain> chain; + const QuicString* client_common_set_hashes; + const QuicString* client_cached_cert_hashes; + }; + + // Certs stored by QuicCompressedCertsCache where uncompressed certs data is + // used to identify the uncompressed representation of certs and + // |compressed_cert| is the cached compressed representation. + class CachedCerts { + public: + CachedCerts(); + CachedCerts(const UncompressedCerts& uncompressed_certs, + const QuicString& compressed_cert); + CachedCerts(const CachedCerts& other); + ~CachedCerts(); + + // Returns true if the |uncompressed_certs| matches uncompressed + // representation of this cert. + bool MatchesUncompressedCerts( + const UncompressedCerts& uncompressed_certs) const; + + const QuicString* compressed_cert() const; + + private: + // Uncompressed certs data. + QuicReferenceCountedPointer<ProofSource::Chain> chain_; + const QuicString client_common_set_hashes_; + const QuicString client_cached_cert_hashes_; + + // Cached compressed representation derived from uncompressed certs. + const QuicString compressed_cert_; + }; + + // Computes a uint64_t hash for |uncompressed_certs|. + uint64_t ComputeUncompressedCertsHash( + const UncompressedCerts& uncompressed_certs); + + // Key is a unit64_t hash for UncompressedCerts. Stored associated value is + // CachedCerts which has both original uncompressed certs data and the + // compressed representation of the certs. + QuicLRUCache<uint64_t, CachedCerts> certs_cache_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_
diff --git a/quic/core/crypto/quic_compressed_certs_cache_test.cc b/quic/core/crypto/quic_compressed_certs_cache_test.cc new file mode 100644 index 0000000..432a8c0 --- /dev/null +++ b/quic/core/crypto/quic_compressed_certs_cache_test.cc
@@ -0,0 +1,99 @@ +// Copyright 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/crypto/quic_compressed_certs_cache.h" + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" + +namespace quic { + +namespace test { + +namespace { + +class QuicCompressedCertsCacheTest : public testing::Test { + public: + QuicCompressedCertsCacheTest() + : certs_cache_(QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {} + + protected: + QuicCompressedCertsCache certs_cache_; +}; + +TEST_F(QuicCompressedCertsCacheTest, CacheHit) { + std::vector<QuicString> certs = {"leaf cert", "intermediate cert", + "root cert"}; + QuicReferenceCountedPointer<ProofSource::Chain> chain( + new ProofSource::Chain(certs)); + QuicString common_certs = "common certs"; + QuicString cached_certs = "cached certs"; + QuicString compressed = "compressed cert"; + + certs_cache_.Insert(chain, common_certs, cached_certs, compressed); + + const QuicString* cached_value = + certs_cache_.GetCompressedCert(chain, common_certs, cached_certs); + ASSERT_NE(nullptr, cached_value); + EXPECT_EQ(*cached_value, compressed); +} + +TEST_F(QuicCompressedCertsCacheTest, CacheMiss) { + std::vector<QuicString> certs = {"leaf cert", "intermediate cert", + "root cert"}; + QuicReferenceCountedPointer<ProofSource::Chain> chain( + new ProofSource::Chain(certs)); + + QuicString common_certs = "common certs"; + QuicString cached_certs = "cached certs"; + QuicString compressed = "compressed cert"; + + certs_cache_.Insert(chain, common_certs, cached_certs, compressed); + + EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert( + chain, "mismatched common certs", cached_certs)); + EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert(chain, common_certs, + "mismatched cached certs")); + + // A different chain though with equivalent certs should get a cache miss. + QuicReferenceCountedPointer<ProofSource::Chain> chain2( + new ProofSource::Chain(certs)); + EXPECT_EQ(nullptr, + certs_cache_.GetCompressedCert(chain2, common_certs, cached_certs)); +} + +TEST_F(QuicCompressedCertsCacheTest, CacheMissDueToEviction) { + // Test cache returns a miss when a queried uncompressed certs was cached but + // then evicted. + std::vector<QuicString> certs = {"leaf cert", "intermediate cert", + "root cert"}; + QuicReferenceCountedPointer<ProofSource::Chain> chain( + new ProofSource::Chain(certs)); + + QuicString common_certs = "common certs"; + QuicString cached_certs = "cached certs"; + QuicString compressed = "compressed cert"; + certs_cache_.Insert(chain, common_certs, cached_certs, compressed); + + // Insert another kQuicCompressedCertsCacheSize certs to evict the first + // cached cert. + for (unsigned int i = 0; + i < QuicCompressedCertsCache::kQuicCompressedCertsCacheSize; i++) { + EXPECT_EQ(certs_cache_.Size(), i + 1); + certs_cache_.Insert(chain, QuicTextUtils::Uint64ToString(i), "", + QuicTextUtils::Uint64ToString(i)); + } + EXPECT_EQ(certs_cache_.MaxSize(), certs_cache_.Size()); + + EXPECT_EQ(nullptr, + certs_cache_.GetCompressedCert(chain, common_certs, cached_certs)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/quic_crypter.h b/quic/core/crypto/quic_crypter.h new file mode 100644 index 0000000..c413c4c --- /dev/null +++ b/quic/core/crypto/quic_crypter.h
@@ -0,0 +1,80 @@ +// Copyright (c) 2012 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_CRYPTO_QUIC_CRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// QuicCrypter is the parent class for QuicEncrypter and QuicDecrypter. +// Its purpose is to provide an interface for using methods that are common to +// both classes when operations are being done that apply to both encrypters and +// decrypters. +class QUIC_EXPORT_PRIVATE QuicCrypter { + public: + virtual ~QuicCrypter() {} + + // Sets the symmetric encryption/decryption key. Returns true on success, + // false on failure. + // + // NOTE: The key is the client_write_key or server_write_key derived from + // the master secret. + virtual bool SetKey(QuicStringPiece key) = 0; + + // Sets the fixed initial bytes of the nonce. Returns true on success, + // false on failure. This method must only be used with Google QUIC crypters. + // + // NOTE: The nonce prefix is the client_write_iv or server_write_iv + // derived from the master secret. A 64-bit packet number will + // be appended to form the nonce. + // + // <------------ 64 bits -----------> + // +---------------------+----------------------------------+ + // | Fixed prefix | packet number | + // +---------------------+----------------------------------+ + // Nonce format + // + // The security of the nonce format requires that QUIC never reuse a + // packet number, even when retransmitting a lost packet. + virtual bool SetNoncePrefix(QuicStringPiece nonce_prefix) = 0; + + // Sets |iv| as the initialization vector to use when constructing the nonce. + // Returns true on success, false on failure. This method must only be used + // with IETF QUIC crypters. + // + // Google QUIC and IETF QUIC use different nonce constructions. This method + // must be used when using IETF QUIC; SetNoncePrefix must be used when using + // Google QUIC. + // + // The nonce is constructed as follows (draft-ietf-quic-tls-14 section 5.2): + // + // <---------------- max(8, N_MIN) bytes -----------------> + // +--------------------------------------------------------+ + // | packet protection IV | + // +--------------------------------------------------------+ + // XOR + // <------------ 64 bits -----------> + // +---------------------+----------------------------------+ + // | zeroes | reconstructed packet number | + // +---------------------+----------------------------------+ + // + // The nonce is the packet protection IV (|iv|) XOR'd with the left-padded + // reconstructed packet number. + // + // The security of the nonce format requires that QUIC never reuse a + // packet number, even when retransmitting a lost packet. + virtual bool SetIV(QuicStringPiece iv) = 0; + + // Returns the size in bytes of a key for the algorithm. + virtual size_t GetKeySize() const = 0; + // Returns the size in bytes of an IV to use with the algorithm. + virtual size_t GetIVSize() const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_
diff --git a/quic/core/crypto/quic_crypto_client_config.cc b/quic/core/crypto/quic_crypto_client_config.cc new file mode 100644 index 0000000..0bf1b41 --- /dev/null +++ b/quic/core/crypto/quic_crypto_client_config.cc
@@ -0,0 +1,1021 @@ +// Copyright 2013 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/crypto/quic_crypto_client_config.h" + +#include <algorithm> +#include <memory> + +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h" +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h" +#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.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/curve25519_key_exchange.h" +#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h" +#include "net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.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 { + +// Tracks the reason (the state of the server config) for sending inchoate +// ClientHello to the server. +void RecordInchoateClientHelloReason( + QuicCryptoClientConfig::CachedState::ServerConfigState state) { + QUIC_CLIENT_HISTOGRAM_ENUM( + "QuicInchoateClientHelloReason", state, + QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT, ""); +} + +// Tracks the state of the QUIC server information loaded from the disk cache. +void RecordDiskCacheServerConfigState( + QuicCryptoClientConfig::CachedState::ServerConfigState state) { + QUIC_CLIENT_HISTOGRAM_ENUM( + "QuicServerInfo.DiskCacheState", state, + QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT, ""); +} + +} // namespace + +QuicCryptoClientConfig::QuicCryptoClientConfig( + std::unique_ptr<ProofVerifier> proof_verifier, + bssl::UniquePtr<SSL_CTX> ssl_ctx) + : proof_verifier_(std::move(proof_verifier)), ssl_ctx_(std::move(ssl_ctx)) { + DCHECK(proof_verifier_.get()); + SetDefaults(); +} + +QuicCryptoClientConfig::~QuicCryptoClientConfig() {} + +QuicCryptoClientConfig::CachedState::CachedState() + : server_config_valid_(false), + expiration_time_(QuicWallTime::Zero()), + generation_counter_(0) {} + +QuicCryptoClientConfig::CachedState::~CachedState() {} + +bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { + if (server_config_.empty()) { + RecordInchoateClientHelloReason(SERVER_CONFIG_EMPTY); + return false; + } + + if (!server_config_valid_) { + RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID); + return false; + } + + const CryptoHandshakeMessage* scfg = GetServerConfig(); + if (!scfg) { + // Should be impossible short of cache corruption. + RecordInchoateClientHelloReason(SERVER_CONFIG_CORRUPTED); + DCHECK(false); + return false; + } + + if (now.IsBefore(expiration_time_)) { + return true; + } + + QUIC_CLIENT_HISTOGRAM_TIMES( + "QuicClientHelloServerConfig.InvalidDuration", + QuicTime::Delta::FromSeconds(now.ToUNIXSeconds() - + expiration_time_.ToUNIXSeconds()), + QuicTime::Delta::FromSeconds(60), // 1 min. + QuicTime::Delta::FromSeconds(20 * 24 * 3600), // 20 days. + 50, ""); + RecordInchoateClientHelloReason(SERVER_CONFIG_EXPIRED); + return false; +} + +bool QuicCryptoClientConfig::CachedState::IsEmpty() const { + return server_config_.empty(); +} + +const CryptoHandshakeMessage* +QuicCryptoClientConfig::CachedState::GetServerConfig() const { + if (server_config_.empty()) { + return nullptr; + } + + if (!scfg_.get()) { + scfg_ = CryptoFramer::ParseMessage(server_config_); + DCHECK(scfg_.get()); + } + return scfg_.get(); +} + +void QuicCryptoClientConfig::CachedState::add_server_designated_connection_id( + QuicConnectionId connection_id) { + server_designated_connection_ids_.push(connection_id); +} + +bool QuicCryptoClientConfig::CachedState::has_server_designated_connection_id() + const { + return !server_designated_connection_ids_.empty(); +} + +void QuicCryptoClientConfig::CachedState::add_server_nonce( + const QuicString& server_nonce) { + server_nonces_.push(server_nonce); +} + +bool QuicCryptoClientConfig::CachedState::has_server_nonce() const { + return !server_nonces_.empty(); +} + +QuicCryptoClientConfig::CachedState::ServerConfigState +QuicCryptoClientConfig::CachedState::SetServerConfig( + QuicStringPiece server_config, + QuicWallTime now, + QuicWallTime expiry_time, + QuicString* error_details) { + const bool matches_existing = server_config == server_config_; + + // Even if the new server config matches the existing one, we still wish to + // reject it if it has expired. + std::unique_ptr<CryptoHandshakeMessage> new_scfg_storage; + const CryptoHandshakeMessage* new_scfg; + + if (!matches_existing) { + new_scfg_storage = CryptoFramer::ParseMessage(server_config); + new_scfg = new_scfg_storage.get(); + } else { + new_scfg = GetServerConfig(); + } + + if (!new_scfg) { + *error_details = "SCFG invalid"; + return SERVER_CONFIG_INVALID; + } + + if (expiry_time.IsZero()) { + uint64_t expiry_seconds; + if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { + *error_details = "SCFG missing EXPY"; + return SERVER_CONFIG_INVALID_EXPIRY; + } + expiration_time_ = QuicWallTime::FromUNIXSeconds(expiry_seconds); + } else { + expiration_time_ = expiry_time; + } + + if (now.IsAfter(expiration_time_)) { + *error_details = "SCFG has expired"; + return SERVER_CONFIG_EXPIRED; + } + + if (!matches_existing) { + server_config_ = QuicString(server_config); + SetProofInvalid(); + scfg_ = std::move(new_scfg_storage); + } + return SERVER_CONFIG_VALID; +} + +void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { + server_config_.clear(); + scfg_.reset(); + SetProofInvalid(); + QuicQueue<QuicConnectionId> empty_queue; + using std::swap; + swap(server_designated_connection_ids_, empty_queue); +} + +void QuicCryptoClientConfig::CachedState::SetProof( + const std::vector<QuicString>& certs, + QuicStringPiece cert_sct, + QuicStringPiece chlo_hash, + QuicStringPiece signature) { + bool has_changed = signature != server_config_sig_ || + chlo_hash != chlo_hash_ || certs_.size() != certs.size(); + + if (!has_changed) { + for (size_t i = 0; i < certs_.size(); i++) { + if (certs_[i] != certs[i]) { + has_changed = true; + break; + } + } + } + + if (!has_changed) { + return; + } + + // If the proof has changed then it needs to be revalidated. + SetProofInvalid(); + certs_ = certs; + cert_sct_ = QuicString(cert_sct); + chlo_hash_ = QuicString(chlo_hash); + server_config_sig_ = QuicString(signature); +} + +void QuicCryptoClientConfig::CachedState::Clear() { + server_config_.clear(); + source_address_token_.clear(); + certs_.clear(); + cert_sct_.clear(); + chlo_hash_.clear(); + server_config_sig_.clear(); + server_config_valid_ = false; + proof_verify_details_.reset(); + scfg_.reset(); + ++generation_counter_; + QuicQueue<QuicConnectionId> empty_queue; + using std::swap; + swap(server_designated_connection_ids_, empty_queue); +} + +void QuicCryptoClientConfig::CachedState::ClearProof() { + SetProofInvalid(); + certs_.clear(); + cert_sct_.clear(); + chlo_hash_.clear(); + server_config_sig_.clear(); +} + +void QuicCryptoClientConfig::CachedState::SetProofValid() { + server_config_valid_ = true; +} + +void QuicCryptoClientConfig::CachedState::SetProofInvalid() { + server_config_valid_ = false; + ++generation_counter_; +} + +bool QuicCryptoClientConfig::CachedState::Initialize( + QuicStringPiece server_config, + QuicStringPiece source_address_token, + const std::vector<QuicString>& certs, + const QuicString& cert_sct, + QuicStringPiece chlo_hash, + QuicStringPiece signature, + QuicWallTime now, + QuicWallTime expiration_time) { + DCHECK(server_config_.empty()); + + if (server_config.empty()) { + RecordDiskCacheServerConfigState(SERVER_CONFIG_EMPTY); + return false; + } + + QuicString error_details; + ServerConfigState state = + SetServerConfig(server_config, now, expiration_time, &error_details); + RecordDiskCacheServerConfigState(state); + if (state != SERVER_CONFIG_VALID) { + QUIC_DVLOG(1) << "SetServerConfig failed with " << error_details; + return false; + } + + chlo_hash_.assign(chlo_hash.data(), chlo_hash.size()); + server_config_sig_.assign(signature.data(), signature.size()); + source_address_token_.assign(source_address_token.data(), + source_address_token.size()); + certs_ = certs; + cert_sct_ = cert_sct; + return true; +} + +const QuicString& QuicCryptoClientConfig::CachedState::server_config() const { + return server_config_; +} + +const QuicString& QuicCryptoClientConfig::CachedState::source_address_token() + const { + return source_address_token_; +} + +const std::vector<QuicString>& QuicCryptoClientConfig::CachedState::certs() + const { + return certs_; +} + +const QuicString& QuicCryptoClientConfig::CachedState::cert_sct() const { + return cert_sct_; +} + +const QuicString& QuicCryptoClientConfig::CachedState::chlo_hash() const { + return chlo_hash_; +} + +const QuicString& QuicCryptoClientConfig::CachedState::signature() const { + return server_config_sig_; +} + +bool QuicCryptoClientConfig::CachedState::proof_valid() const { + return server_config_valid_; +} + +uint64_t QuicCryptoClientConfig::CachedState::generation_counter() const { + return generation_counter_; +} + +const ProofVerifyDetails* +QuicCryptoClientConfig::CachedState::proof_verify_details() const { + return proof_verify_details_.get(); +} + +void QuicCryptoClientConfig::CachedState::set_source_address_token( + QuicStringPiece token) { + source_address_token_ = QuicString(token); +} + +void QuicCryptoClientConfig::CachedState::set_cert_sct( + QuicStringPiece cert_sct) { + cert_sct_ = QuicString(cert_sct); +} + +void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails( + ProofVerifyDetails* details) { + proof_verify_details_.reset(details); +} + +void QuicCryptoClientConfig::CachedState::InitializeFrom( + const QuicCryptoClientConfig::CachedState& other) { + DCHECK(server_config_.empty()); + DCHECK(!server_config_valid_); + server_config_ = other.server_config_; + source_address_token_ = other.source_address_token_; + certs_ = other.certs_; + cert_sct_ = other.cert_sct_; + chlo_hash_ = other.chlo_hash_; + server_config_sig_ = other.server_config_sig_; + server_config_valid_ = other.server_config_valid_; + server_designated_connection_ids_ = other.server_designated_connection_ids_; + expiration_time_ = other.expiration_time_; + if (other.proof_verify_details_ != nullptr) { + proof_verify_details_.reset(other.proof_verify_details_->Clone()); + } + ++generation_counter_; +} + +QuicConnectionId +QuicCryptoClientConfig::CachedState::GetNextServerDesignatedConnectionId() { + if (server_designated_connection_ids_.empty()) { + QUIC_BUG + << "Attempting to consume a connection id that was never designated."; + return EmptyQuicConnectionId(); + } + const QuicConnectionId next_id = server_designated_connection_ids_.front(); + server_designated_connection_ids_.pop(); + return next_id; +} + +QuicString QuicCryptoClientConfig::CachedState::GetNextServerNonce() { + if (server_nonces_.empty()) { + QUIC_BUG + << "Attempting to consume a server nonce that was never designated."; + return ""; + } + const QuicString server_nonce = server_nonces_.front(); + server_nonces_.pop(); + return server_nonce; +} + +void QuicCryptoClientConfig::SetDefaults() { + // Key exchange methods. + kexs = {kC255, kP256}; + + // Authenticated encryption algorithms. Prefer AES-GCM if hardware-supported + // fast implementation is available. + if (EVP_has_aes_hardware() == 1) { + aead = {kAESG, kCC20}; + } else { + aead = {kCC20, kAESG}; + } +} + +QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( + const QuicServerId& server_id) { + auto it = cached_states_.find(server_id); + if (it != cached_states_.end()) { + return it->second.get(); + } + + CachedState* cached = new CachedState; + cached_states_.insert(std::make_pair(server_id, QuicWrapUnique(cached))); + bool cache_populated = PopulateFromCanonicalConfig(server_id, cached); + QUIC_CLIENT_HISTOGRAM_BOOL( + "QuicCryptoClientConfig.PopulatedFromCanonicalConfig", cache_populated, + ""); + return cached; +} + +void QuicCryptoClientConfig::ClearCachedStates(const ServerIdFilter& filter) { + for (auto it = cached_states_.begin(); it != cached_states_.end(); ++it) { + if (filter.Matches(it->first)) + it->second->Clear(); + } +} + +void QuicCryptoClientConfig::FillInchoateClientHello( + const QuicServerId& server_id, + const ParsedQuicVersion preferred_version, + const CachedState* cached, + QuicRandom* rand, + bool demand_x509_proof, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + CryptoHandshakeMessage* out) const { + out->set_tag(kCHLO); + // TODO(rch): Remove this when we remove quic_use_chlo_packet_size flag. + if (pad_inchoate_hello_) { + out->set_minimum_size(kClientHelloMinimumSize); + } else { + out->set_minimum_size(1); + } + + // Server name indication. We only send SNI if it's a valid domain name, as + // per the spec. + if (QuicHostnameUtils::IsValidSNI(server_id.host())) { + out->SetStringPiece(kSNI, server_id.host()); + } + out->SetVersion(kVER, preferred_version); + + if (!user_agent_id_.empty()) { + out->SetStringPiece(kUAID, user_agent_id_); + } + + if (!alpn_.empty()) { + out->SetStringPiece(kALPN, alpn_); + } + + // Even though this is an inchoate CHLO, send the SCID so that + // the STK can be validated by the server. + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + if (scfg != nullptr) { + QuicStringPiece scid; + if (scfg->GetStringPiece(kSCID, &scid)) { + out->SetStringPiece(kSCID, scid); + } + } + + if (!cached->source_address_token().empty()) { + out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); + } + + if (!demand_x509_proof) { + return; + } + + char proof_nonce[32]; + rand->RandBytes(proof_nonce, QUIC_ARRAYSIZE(proof_nonce)); + out->SetStringPiece( + kNONP, QuicStringPiece(proof_nonce, QUIC_ARRAYSIZE(proof_nonce))); + + out->SetVector(kPDMD, QuicTagVector{kX509}); + + if (common_cert_sets) { + out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); + } + + out->SetStringPiece(kCertificateSCTTag, ""); + + const std::vector<QuicString>& certs = cached->certs(); + // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the + // client config is being used for multiple connections, another connection + // doesn't update the cached certificates and cause us to be unable to + // process the server's compressed certificate chain. + out_params->cached_certs = certs; + if (!certs.empty()) { + std::vector<uint64_t> hashes; + hashes.reserve(certs.size()); + for (auto i = certs.begin(); i != certs.end(); ++i) { + hashes.push_back(QuicUtils::FNV1a_64_Hash(*i)); + } + out->SetVector(kCCRT, hashes); + } +} + +QuicErrorCode QuicCryptoClientConfig::FillClientHello( + const QuicServerId& server_id, + QuicConnectionId connection_id, + const ParsedQuicVersion preferred_version, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + const ChannelIDKey* channel_id_key, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + CryptoHandshakeMessage* out, + QuicString* error_details) const { + DCHECK(error_details != nullptr); + QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( + connection_id, preferred_version.transport_version)) + << "FillClientHello: attempted to use connection ID " << connection_id + << " which is invalid with version " + << QuicVersionToString(preferred_version.transport_version); + + FillInchoateClientHello(server_id, preferred_version, cached, rand, + /* demand_x509_proof= */ true, out_params, out); + + if (pad_full_hello_) { + out->set_minimum_size(kClientHelloMinimumSize); + } else { + out->set_minimum_size(1); + } + + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + if (!scfg) { + // This should never happen as our caller should have checked + // cached->IsComplete() before calling this function. + *error_details = "Handshake not ready"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + QuicStringPiece scid; + if (!scfg->GetStringPiece(kSCID, &scid)) { + *error_details = "SCFG missing SCID"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + out->SetStringPiece(kSCID, scid); + + out->SetStringPiece(kCertificateSCTTag, ""); + + QuicTagVector their_aeads; + QuicTagVector their_key_exchanges; + if (scfg->GetTaglist(kAEAD, &their_aeads) != QUIC_NO_ERROR || + scfg->GetTaglist(kKEXS, &their_key_exchanges) != QUIC_NO_ERROR) { + *error_details = "Missing AEAD or KEXS"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + // AEAD: the work loads on the client and server are symmetric. Since the + // client is more likely to be CPU-constrained, break the tie by favoring + // the client's preference. + // Key exchange: the client does more work than the server, so favor the + // client's preference. + size_t key_exchange_index; + if (!FindMutualQuicTag(aead, their_aeads, &out_params->aead, nullptr) || + !FindMutualQuicTag(kexs, their_key_exchanges, &out_params->key_exchange, + &key_exchange_index)) { + *error_details = "Unsupported AEAD or KEXS"; + return QUIC_CRYPTO_NO_SUPPORT; + } + out->SetVector(kAEAD, QuicTagVector{out_params->aead}); + out->SetVector(kKEXS, QuicTagVector{out_params->key_exchange}); + + if (!tb_key_params.empty() && !server_id.privacy_mode_enabled()) { + QuicTagVector their_tbkps; + switch (scfg->GetTaglist(kTBKP, &their_tbkps)) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + break; + case QUIC_NO_ERROR: + if (FindMutualQuicTag(tb_key_params, their_tbkps, + &out_params->token_binding_key_param, nullptr)) { + out->SetVector(kTBKP, + QuicTagVector{out_params->token_binding_key_param}); + } + break; + default: + *error_details = "Invalid TBKP"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + } + + QuicStringPiece public_value; + if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) != + QUIC_NO_ERROR) { + *error_details = "Missing public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + QuicStringPiece orbit; + if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) { + *error_details = "SCFG missing OBIT"; + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce); + out->SetStringPiece(kNONC, out_params->client_nonce); + if (!out_params->server_nonce.empty()) { + out->SetStringPiece(kServerNonceTag, out_params->server_nonce); + } + + switch (out_params->key_exchange) { + case kC255: + out_params->client_key_exchange = Curve25519KeyExchange::New( + Curve25519KeyExchange::NewPrivateKey(rand)); + break; + case kP256: + out_params->client_key_exchange = + P256KeyExchange::New(P256KeyExchange::NewPrivateKey()); + break; + default: + DCHECK(false); + *error_details = "Configured to support an unknown key exchange"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + if (!out_params->client_key_exchange->CalculateSharedKey( + public_value, &out_params->initial_premaster_secret)) { + *error_details = "Key exchange failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); + + const std::vector<QuicString>& certs = cached->certs(); + if (certs.empty()) { + *error_details = "No certs to calculate XLCT"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + out->SetValue(kXLCT, CryptoUtils::ComputeLeafCertHash(certs[0])); + + if (channel_id_key) { + // In order to calculate the encryption key for the CETV block we need to + // serialise the client hello as it currently is (i.e. without the CETV + // block). For this, the client hello is serialized without padding. + const size_t orig_min_size = out->minimum_size(); + out->set_minimum_size(0); + + CryptoHandshakeMessage cetv; + cetv.set_tag(kCETV); + + QuicString hkdf_input; + const QuicData& client_hello_serialized = out->GetSerialized(); + hkdf_input.append(QuicCryptoConfig::kCETVLabel, + strlen(QuicCryptoConfig::kCETVLabel) + 1); + hkdf_input.append(connection_id.data(), connection_id.length()); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(cached->server_config()); + + QuicString key = channel_id_key->SerializeKey(); + QuicString signature; + if (!channel_id_key->Sign(hkdf_input, &signature)) { + *error_details = "Channel ID signature failed"; + return QUIC_INVALID_CHANNEL_ID_SIGNATURE; + } + + cetv.SetStringPiece(kCIDK, key); + cetv.SetStringPiece(kCIDS, signature); + + CrypterPair crypters; + if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, + out_params->aead, out_params->client_nonce, + out_params->server_nonce, pre_shared_key_, + hkdf_input, Perspective::IS_CLIENT, + CryptoUtils::Diversification::Never(), + &crypters, nullptr /* subkey secret */)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + const QuicData& cetv_plaintext = cetv.GetSerialized(); + const size_t encrypted_len = + crypters.encrypter->GetCiphertextSize(cetv_plaintext.length()); + std::unique_ptr<char[]> output(new char[encrypted_len]); + size_t output_size = 0; + if (!crypters.encrypter->EncryptPacket( + 0 /* packet number */, QuicStringPiece() /* associated data */, + cetv_plaintext.AsStringPiece(), output.get(), &output_size, + encrypted_len)) { + *error_details = "Packet encryption failed"; + return QUIC_ENCRYPTION_FAILURE; + } + + out->SetStringPiece(kCETV, QuicStringPiece(output.get(), output_size)); + out->MarkDirty(); + + out->set_minimum_size(orig_min_size); + } + + // Derive the symmetric keys and set up the encrypters and decrypters. + // Set the following members of out_params: + // out_params->hkdf_input_suffix + // out_params->initial_crypters + out_params->hkdf_input_suffix.clear(); + out_params->hkdf_input_suffix.append(connection_id.data(), + connection_id.length()); + const QuicData& client_hello_serialized = out->GetSerialized(); + out_params->hkdf_input_suffix.append(client_hello_serialized.data(), + client_hello_serialized.length()); + out_params->hkdf_input_suffix.append(cached->server_config()); + if (certs.empty()) { + *error_details = "No certs found to include in KDF"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + out_params->hkdf_input_suffix.append(certs[0]); + + QuicString hkdf_input; + const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; + hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); + hkdf_input.append(out_params->hkdf_input_suffix); + + QuicString* subkey_secret = &out_params->initial_subkey_secret; + + if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, + out_params->aead, out_params->client_nonce, + out_params->server_nonce, pre_shared_key_, + hkdf_input, Perspective::IS_CLIENT, + CryptoUtils::Diversification::Pending(), + &out_params->initial_crypters, subkey_secret)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig( + const CryptoHandshakeMessage& message, + QuicWallTime now, + QuicTransportVersion version, + QuicStringPiece chlo_hash, + const std::vector<QuicString>& cached_certs, + CachedState* cached, + QuicString* error_details) { + DCHECK(error_details != nullptr); + + QuicStringPiece scfg; + if (!message.GetStringPiece(kSCFG, &scfg)) { + *error_details = "Missing SCFG"; + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + QuicWallTime expiration_time = QuicWallTime::Zero(); + uint64_t expiry_seconds; + if (message.GetUint64(kSTTL, &expiry_seconds) == QUIC_NO_ERROR) { + // Only cache configs for a maximum of 1 week. + expiration_time = now.Add(QuicTime::Delta::FromSeconds( + std::min(expiry_seconds, kNumSecondsPerWeek))); + } + + CachedState::ServerConfigState state = + cached->SetServerConfig(scfg, now, expiration_time, error_details); + if (state == CachedState::SERVER_CONFIG_EXPIRED) { + return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; + } + // TODO(rtenneti): Return more specific error code than returning + // QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER. + if (state != CachedState::SERVER_CONFIG_VALID) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + QuicStringPiece token; + if (message.GetStringPiece(kSourceAddressTokenTag, &token)) { + cached->set_source_address_token(token); + } + + QuicStringPiece proof, cert_bytes, cert_sct; + bool has_proof = message.GetStringPiece(kPROF, &proof); + bool has_cert = message.GetStringPiece(kCertificateTag, &cert_bytes); + if (has_proof && has_cert) { + std::vector<QuicString> certs; + if (!CertCompressor::DecompressChain(cert_bytes, cached_certs, + common_cert_sets, &certs)) { + *error_details = "Certificate data invalid"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + message.GetStringPiece(kCertificateSCTTag, &cert_sct); + cached->SetProof(certs, cert_sct, chlo_hash, proof); + } else { + // Secure QUIC: clear existing proof as we have been sent a new SCFG + // without matching proof/certs. + cached->ClearProof(); + + if (has_proof && !has_cert) { + *error_details = "Certificate missing"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!has_proof && has_cert) { + *error_details = "Proof missing"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessRejection( + const CryptoHandshakeMessage& rej, + QuicWallTime now, + const QuicTransportVersion version, + QuicStringPiece chlo_hash, + CachedState* cached, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + QuicString* error_details) { + DCHECK(error_details != nullptr); + + if ((rej.tag() != kREJ) && (rej.tag() != kSREJ)) { + *error_details = "Message is not REJ or SREJ"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + QuicErrorCode error = + CacheNewServerConfig(rej, now, version, chlo_hash, + out_params->cached_certs, cached, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + QuicStringPiece nonce; + if (rej.GetStringPiece(kServerNonceTag, &nonce)) { + out_params->server_nonce = QuicString(nonce); + } + + if (rej.tag() == kSREJ) { + QuicConnectionId connection_id; + + QuicStringPiece connection_id_bytes; + if (!rej.GetStringPiece(kRCID, &connection_id_bytes)) { + *error_details = "Missing kRCID"; + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + connection_id = QuicConnectionId(connection_id_bytes.data(), + connection_id_bytes.length()); + if (!QuicUtils::IsConnectionIdValidForVersion(connection_id, version)) { + QUIC_PEER_BUG << "Received server-designated connection ID " + << connection_id << " which is invalid with version " + << QuicVersionToString(version); + *error_details = "Bad kRCID length"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + cached->add_server_designated_connection_id(connection_id); + if (!nonce.empty()) { + cached->add_server_nonce(QuicString(nonce)); + } + return QUIC_NO_ERROR; + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + QuicConnectionId connection_id, + ParsedQuicVersion version, + const ParsedQuicVersionVector& negotiated_versions, + CachedState* cached, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + QuicString* error_details) { + DCHECK(error_details != nullptr); + + QuicErrorCode valid = CryptoUtils::ValidateServerHello( + server_hello, negotiated_versions, error_details); + if (valid != QUIC_NO_ERROR) { + return valid; + } + + // Learn about updated source address tokens. + QuicStringPiece token; + if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) { + cached->set_source_address_token(token); + } + + QuicStringPiece shlo_nonce; + if (!server_hello.GetStringPiece(kServerNonceTag, &shlo_nonce)) { + *error_details = "server hello missing server nonce"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + // TODO(agl): + // learn about updated SCFGs. + + QuicStringPiece public_value; + if (!server_hello.GetStringPiece(kPUBS, &public_value)) { + *error_details = "server hello missing forward secure public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!out_params->client_key_exchange->CalculateSharedKey( + public_value, &out_params->forward_secure_premaster_secret)) { + *error_details = "Key exchange failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + QuicString hkdf_input; + const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; + hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len); + hkdf_input.append(out_params->hkdf_input_suffix); + + if (!CryptoUtils::DeriveKeys( + out_params->forward_secure_premaster_secret, out_params->aead, + out_params->client_nonce, + shlo_nonce.empty() ? out_params->server_nonce : shlo_nonce, + pre_shared_key_, hkdf_input, Perspective::IS_CLIENT, + CryptoUtils::Diversification::Never(), + &out_params->forward_secure_crypters, &out_params->subkey_secret)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessServerConfigUpdate( + const CryptoHandshakeMessage& server_config_update, + QuicWallTime now, + const QuicTransportVersion version, + QuicStringPiece chlo_hash, + CachedState* cached, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + QuicString* error_details) { + DCHECK(error_details != nullptr); + + if (server_config_update.tag() != kSCUP) { + *error_details = "ServerConfigUpdate must have kSCUP tag."; + return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; + } + return CacheNewServerConfig(server_config_update, now, version, chlo_hash, + out_params->cached_certs, cached, error_details); +} + +ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { + return proof_verifier_.get(); +} + +ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const { + return channel_id_source_.get(); +} + +SSL_CTX* QuicCryptoClientConfig::ssl_ctx() const { + return ssl_ctx_.get(); +} + +void QuicCryptoClientConfig::SetChannelIDSource(ChannelIDSource* source) { + channel_id_source_.reset(source); +} + +void QuicCryptoClientConfig::InitializeFrom( + const QuicServerId& server_id, + const QuicServerId& canonical_server_id, + QuicCryptoClientConfig* canonical_crypto_config) { + CachedState* canonical_cached = + canonical_crypto_config->LookupOrCreate(canonical_server_id); + if (!canonical_cached->proof_valid()) { + return; + } + CachedState* cached = LookupOrCreate(server_id); + cached->InitializeFrom(*canonical_cached); +} + +void QuicCryptoClientConfig::AddCanonicalSuffix(const QuicString& suffix) { + canonical_suffixes_.push_back(suffix); +} + +bool QuicCryptoClientConfig::PopulateFromCanonicalConfig( + const QuicServerId& server_id, + CachedState* server_state) { + DCHECK(server_state->IsEmpty()); + size_t i = 0; + for (; i < canonical_suffixes_.size(); ++i) { + if (QuicTextUtils::EndsWithIgnoreCase(server_id.host(), + canonical_suffixes_[i])) { + break; + } + } + if (i == canonical_suffixes_.size()) { + return false; + } + + QuicServerId suffix_server_id(canonical_suffixes_[i], server_id.port(), + server_id.privacy_mode_enabled()); + if (!QuicContainsKey(canonical_server_map_, suffix_server_id)) { + // This is the first host we've seen which matches the suffix, so make it + // canonical. + canonical_server_map_[suffix_server_id] = server_id; + return false; + } + + const QuicServerId& canonical_server_id = + canonical_server_map_[suffix_server_id]; + CachedState* canonical_state = cached_states_[canonical_server_id].get(); + if (!canonical_state->proof_valid()) { + return false; + } + + // Update canonical version to point at the "most recent" entry. + canonical_server_map_[suffix_server_id] = server_id; + + server_state->InitializeFrom(*canonical_state); + return true; +} + +} // namespace quic
diff --git a/quic/core/crypto/quic_crypto_client_config.h b/quic/core/crypto/quic_crypto_client_config.h new file mode 100644 index 0000000..32df726 --- /dev/null +++ b/quic/core/crypto/quic_crypto_client_config.h
@@ -0,0 +1,432 @@ +// Copyright 2013 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_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ + +#include <cstdint> +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/base.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class ChannelIDKey; +class ChannelIDSource; +class CryptoHandshakeMessage; +class ProofVerifier; +class ProofVerifyDetails; +class QuicRandom; + +// QuicCryptoClientConfig contains crypto-related configuration settings for a +// client. Note that this object isn't thread-safe. It's designed to be used on +// a single thread at a time. +class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { + public: + // A CachedState contains the information that the client needs in order to + // perform a 0-RTT handshake with a server. This information can be reused + // over several connections to the same server. + class QUIC_EXPORT_PRIVATE CachedState { + public: + // Enum to track if the server config is valid or not. If it is not valid, + // it specifies why it is invalid. + enum ServerConfigState { + // WARNING: Do not change the numerical values of any of server config + // state. Do not remove deprecated server config states - just comment + // them as deprecated. + SERVER_CONFIG_EMPTY = 0, + SERVER_CONFIG_INVALID = 1, + SERVER_CONFIG_CORRUPTED = 2, + SERVER_CONFIG_EXPIRED = 3, + SERVER_CONFIG_INVALID_EXPIRY = 4, + SERVER_CONFIG_VALID = 5, + // NOTE: Add new server config states only immediately above this line. + // Make sure to update the QuicServerConfigState enum in + // tools/metrics/histograms/histograms.xml accordingly. + SERVER_CONFIG_COUNT + }; + + CachedState(); + CachedState(const CachedState&) = delete; + CachedState& operator=(const CachedState&) = delete; + ~CachedState(); + + // IsComplete returns true if this object contains enough information to + // perform a handshake with the server. |now| is used to judge whether any + // cached server config has expired. + bool IsComplete(QuicWallTime now) const; + + // IsEmpty returns true if |server_config_| is empty. + bool IsEmpty() const; + + // GetServerConfig returns the parsed contents of |server_config|, or + // nullptr if |server_config| is empty. The return value is owned by this + // object and is destroyed when this object is. + const CryptoHandshakeMessage* GetServerConfig() const; + + // SetServerConfig checks that |server_config| parses correctly and stores + // it in |server_config_|. |now| is used to judge whether |server_config| + // has expired. + ServerConfigState SetServerConfig(QuicStringPiece server_config, + QuicWallTime now, + QuicWallTime expiry_time, + QuicString* error_details); + + // InvalidateServerConfig clears the cached server config (if any). + void InvalidateServerConfig(); + + // SetProof stores a cert chain, cert signed timestamp and signature. + void SetProof(const std::vector<QuicString>& certs, + QuicStringPiece cert_sct, + QuicStringPiece chlo_hash, + QuicStringPiece signature); + + // Clears all the data. + void Clear(); + + // Clears the certificate chain and signature and invalidates the proof. + void ClearProof(); + + // SetProofValid records that the certificate chain and signature have been + // validated and that it's safe to assume that the server is legitimate. + // (Note: this does not check the chain or signature.) + void SetProofValid(); + + // If the server config or the proof has changed then it needs to be + // revalidated. Helper function to keep server_config_valid_ and + // generation_counter_ in sync. + void SetProofInvalid(); + + const QuicString& server_config() const; + const QuicString& source_address_token() const; + const std::vector<QuicString>& certs() const; + const QuicString& cert_sct() const; + const QuicString& chlo_hash() const; + const QuicString& signature() const; + bool proof_valid() const; + uint64_t generation_counter() const; + const ProofVerifyDetails* proof_verify_details() const; + + void set_source_address_token(QuicStringPiece token); + + void set_cert_sct(QuicStringPiece cert_sct); + + // Adds the connection ID to the queue of server-designated connection-ids. + void add_server_designated_connection_id(QuicConnectionId connection_id); + + // If true, the crypto config contains at least one connection ID specified + // by the server, and the client should use one of these IDs when initiating + // the next connection. + bool has_server_designated_connection_id() const; + + // This function should only be called when + // has_server_designated_connection_id is true. Returns the next + // connection_id specified by the server and removes it from the + // queue of ids. + QuicConnectionId GetNextServerDesignatedConnectionId(); + + // Adds the servernonce to the queue of server nonces. + void add_server_nonce(const QuicString& server_nonce); + + // If true, the crypto config contains at least one server nonce, and the + // client should use one of these nonces. + bool has_server_nonce() const; + + // This function should only be called when has_server_nonce is true. + // Returns the next server_nonce specified by the server and removes it + // from the queue of nonces. + QuicString GetNextServerNonce(); + + // SetProofVerifyDetails takes ownership of |details|. + void SetProofVerifyDetails(ProofVerifyDetails* details); + + // Copy the |server_config_|, |source_address_token_|, |certs_|, + // |expiration_time_|, |cert_sct_|, |chlo_hash_| and |server_config_sig_| + // from the |other|. The remaining fields, |generation_counter_|, + // |proof_verify_details_|, and |scfg_| remain unchanged. + void InitializeFrom(const CachedState& other); + + // Initializes this cached state based on the arguments provided. + // Returns false if there is a problem parsing the server config. + bool Initialize(QuicStringPiece server_config, + QuicStringPiece source_address_token, + const std::vector<QuicString>& certs, + const QuicString& cert_sct, + QuicStringPiece chlo_hash, + QuicStringPiece signature, + QuicWallTime now, + QuicWallTime expiration_time); + + private: + QuicString server_config_; // A serialized handshake message. + QuicString source_address_token_; // An opaque proof of IP ownership. + std::vector<QuicString> certs_; // A list of certificates in leaf-first + // order. + QuicString cert_sct_; // Signed timestamp of the leaf cert. + QuicString chlo_hash_; // Hash of the CHLO message. + QuicString server_config_sig_; // A signature of |server_config_|. + bool server_config_valid_; // True if |server_config_| is correctly + // signed and |certs_| has been validated. + QuicWallTime expiration_time_; // Time when the config is no longer valid. + // Generation counter associated with the |server_config_|, |certs_| and + // |server_config_sig_| combination. It is incremented whenever we set + // server_config_valid_ to false. + uint64_t generation_counter_; + + std::unique_ptr<ProofVerifyDetails> proof_verify_details_; + + // scfg contains the cached, parsed value of |server_config|. + mutable std::unique_ptr<CryptoHandshakeMessage> scfg_; + + // TODO(jokulik): Consider using a hash-set as extra book-keeping to ensure + // that no connection-id is added twice. Also, consider keeping the server + // nonces and connection_ids together in one queue. + QuicQueue<QuicConnectionId> server_designated_connection_ids_; + QuicQueue<QuicString> server_nonces_; + }; + + // Used to filter server ids for partial config deletion. + class ServerIdFilter { + public: + virtual ~ServerIdFilter() {} + + // Returns true if |server_id| matches the filter. + virtual bool Matches(const QuicServerId& server_id) const = 0; + }; + + QuicCryptoClientConfig(std::unique_ptr<ProofVerifier> proof_verifier, + bssl::UniquePtr<SSL_CTX> ssl_ctx); + QuicCryptoClientConfig(const QuicCryptoClientConfig&) = delete; + QuicCryptoClientConfig& operator=(const QuicCryptoClientConfig&) = delete; + ~QuicCryptoClientConfig(); + + // LookupOrCreate returns a CachedState for the given |server_id|. If no such + // CachedState currently exists, it will be created and cached. + CachedState* LookupOrCreate(const QuicServerId& server_id); + + // Delete CachedState objects whose server ids match |filter| from + // cached_states. + void ClearCachedStates(const ServerIdFilter& filter); + + // FillInchoateClientHello sets |out| to be a CHLO message that elicits a + // source-address token or SCFG from a server. If |cached| is non-nullptr, the + // source-address token will be taken from it. |out_params| is used in order + // to store the cached certs that were sent as hints to the server in + // |out_params->cached_certs|. |preferred_version| is the version of the + // QUIC protocol that this client chose to use initially. This allows the + // server to detect downgrade attacks. If |demand_x509_proof| is true, + // then |out| will include an X509 proof demand, and the associated + // certificate related fields. + void FillInchoateClientHello( + const QuicServerId& server_id, + const ParsedQuicVersion preferred_version, + const CachedState* cached, + QuicRandom* rand, + bool demand_x509_proof, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + CryptoHandshakeMessage* out) const; + + // FillClientHello sets |out| to be a CHLO message based on the configuration + // of this object. This object must have cached enough information about + // the server's hostname in order to perform a handshake. This can be checked + // with the |IsComplete| member of |CachedState|. + // + // |now| and |rand| are used to generate the nonce and |out_params| is + // filled with the results of the handshake that the server is expected to + // accept. |preferred_version| is the version of the QUIC protocol that this + // client chose to use initially. This allows the server to detect downgrade + // attacks. + // + // If |channel_id_key| is not null, it is used to sign a secret value derived + // from the client and server's keys, and the Channel ID public key and the + // signature are placed in the CETV value of the CHLO. + QuicErrorCode FillClientHello( + const QuicServerId& server_id, + QuicConnectionId connection_id, + const ParsedQuicVersion preferred_version, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + const ChannelIDKey* channel_id_key, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + CryptoHandshakeMessage* out, + QuicString* error_details) const; + + // ProcessRejection processes a REJ message from a server and updates the + // cached information about that server. After this, |IsComplete| may return + // true for that server's CachedState. If the rejection message contains state + // about a future handshake (i.e. an nonce value from the server), then it + // will be saved in |out_params|. |now| is used to judge whether the server + // config in the rejection message has expired. + QuicErrorCode ProcessRejection( + const CryptoHandshakeMessage& rej, + QuicWallTime now, + QuicTransportVersion version, + QuicStringPiece chlo_hash, + CachedState* cached, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + QuicString* error_details); + + // ProcessServerHello processes the message in |server_hello|, updates the + // cached information about that server, writes the negotiated parameters to + // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable + // then it puts an error message in |error_details| and returns an error + // code. |version| is the QUIC version for the current connection. + // |negotiated_versions| contains the list of version, if any, that were + // present in a version negotiation packet previously recevied from the + // server. The contents of this list will be compared against the list of + // versions provided in the VER tag of the server hello. + QuicErrorCode ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + QuicConnectionId connection_id, + ParsedQuicVersion version, + const ParsedQuicVersionVector& negotiated_versions, + CachedState* cached, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + QuicString* error_details); + + // Processes the message in |server_update|, updating the cached source + // address token, and server config. + // If |server_update| is invalid then |error_details| will contain an error + // message, and an error code will be returned. If all has gone well + // QUIC_NO_ERROR is returned. + QuicErrorCode ProcessServerConfigUpdate( + const CryptoHandshakeMessage& server_update, + QuicWallTime now, + const QuicTransportVersion version, + QuicStringPiece chlo_hash, + CachedState* cached, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params, + QuicString* error_details); + + ProofVerifier* proof_verifier() const; + + ChannelIDSource* channel_id_source() const; + + SSL_CTX* ssl_ctx() const; + + // SetChannelIDSource sets a ChannelIDSource that will be called, when the + // server supports channel IDs, to obtain a channel ID for signing a message + // proving possession of the channel ID. This object takes ownership of + // |source|. + void SetChannelIDSource(ChannelIDSource* source); + + // Initialize the CachedState from |canonical_crypto_config| for the + // |canonical_server_id| as the initial CachedState for |server_id|. We will + // copy config data only if |canonical_crypto_config| has valid proof. + void InitializeFrom(const QuicServerId& server_id, + const QuicServerId& canonical_server_id, + QuicCryptoClientConfig* canonical_crypto_config); + + // Adds |suffix| as a domain suffix for which the server's crypto config + // is expected to be shared among servers with the domain suffix. If a server + // matches this suffix, then the server config from another server with the + // suffix will be used to initialize the cached state for this server. + void AddCanonicalSuffix(const QuicString& suffix); + + // Saves the |user_agent_id| that will be passed in QUIC's CHLO message. + void set_user_agent_id(const QuicString& user_agent_id) { + user_agent_id_ = user_agent_id; + } + + // Returns the user_agent_id that will be provided in the client hello + // handshake message. + const QuicString& user_agent_id() const { return user_agent_id_; } + + // Saves the |alpn| that will be passed in QUIC's CHLO message. + void set_alpn(const QuicString& alpn) { alpn_ = alpn; } + + void set_pre_shared_key(QuicStringPiece psk) { + pre_shared_key_ = QuicString(psk); + } + + bool pad_inchoate_hello() const { return pad_inchoate_hello_; } + void set_pad_inchoate_hello(bool new_value) { + pad_inchoate_hello_ = new_value; + } + + bool pad_full_hello() const { return pad_full_hello_; } + void set_pad_full_hello(bool new_value) { pad_full_hello_ = new_value; } + + private: + // Sets the members to reasonable, default values. + void SetDefaults(); + + // CacheNewServerConfig checks for SCFG, STK, PROF, and CRT tags in |message|, + // verifies them, and stores them in the cached state if they validate. + // This is used on receipt of a REJ from a server, or when a server sends + // updated server config during a connection. + QuicErrorCode CacheNewServerConfig( + const CryptoHandshakeMessage& message, + QuicWallTime now, + QuicTransportVersion version, + QuicStringPiece chlo_hash, + const std::vector<QuicString>& cached_certs, + CachedState* cached, + QuicString* error_details); + + // If the suffix of the hostname in |server_id| is in |canonical_suffixes_|, + // then populate |cached| with the canonical cached state from + // |canonical_server_map_| for that suffix. Returns true if |cached| is + // initialized with canonical cached state. + bool PopulateFromCanonicalConfig(const QuicServerId& server_id, + CachedState* cached); + + // cached_states_ maps from the server_id to the cached information about + // that server. + std::map<QuicServerId, std::unique_ptr<CachedState>> cached_states_; + + // Contains a map of servers which could share the same server config. Map + // from a canonical host suffix/port/scheme to a representative server with + // the canonical suffix, which has a plausible set of initial certificates + // (or at least server public key). + std::map<QuicServerId, QuicServerId> canonical_server_map_; + + // Contains list of suffixes (for exmaple ".c.youtube.com", + // ".googlevideo.com") of canonical hostnames. + std::vector<QuicString> canonical_suffixes_; + + std::unique_ptr<ProofVerifier> proof_verifier_; + std::unique_ptr<ChannelIDSource> channel_id_source_; + bssl::UniquePtr<SSL_CTX> ssl_ctx_; + + // The |user_agent_id_| passed in QUIC's CHLO message. + QuicString user_agent_id_; + + // The |alpn_| passed in QUIC's CHLO message. + QuicString alpn_; + + // If non-empty, the client will operate in the pre-shared key mode by + // incorporating |pre_shared_key_| into the key schedule. + QuicString pre_shared_key_; + + // In QUIC, technically, client hello should be fully padded. + // However, fully padding on slow network connection (e.g. 50kbps) can add + // 150ms latency to one roundtrip. Therefore, you can disable padding of + // individual messages. It is recommend to leave at least one message in + // each direction fully padded (e.g. full CHLO and SHLO), but if you know + // the lower-bound MTU, you don't need to pad all of them (keep in mind that + // it's not OK to do it according to the standard). + // + // Also, if you disable padding, you must disable (change) the + // anti-amplification protection. You should only do so if you have some + // other means of verifying the client. + bool pad_inchoate_hello_ = true; + bool pad_full_hello_ = true; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
diff --git a/quic/core/crypto/quic_crypto_client_config_test.cc b/quic/core/crypto/quic_crypto_client_config_test.cc new file mode 100644 index 0000000..5e0ca6e --- /dev/null +++ b/quic/core/crypto/quic_crypto_client_config_test.cc
@@ -0,0 +1,655 @@ +// Copyright 2013 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/crypto/quic_crypto_client_config.h" + +#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::StartsWith; + +namespace quic { +namespace test { +namespace { + +class TestProofVerifyDetails : public ProofVerifyDetails { + ~TestProofVerifyDetails() override {} + + // ProofVerifyDetails implementation + ProofVerifyDetails* Clone() const override { + return new TestProofVerifyDetails; + } +}; + +class OneServerIdFilter : public QuicCryptoClientConfig::ServerIdFilter { + public: + explicit OneServerIdFilter(const QuicServerId* server_id) + : server_id_(*server_id) {} + + bool Matches(const QuicServerId& server_id) const override { + return server_id == server_id_; + } + + private: + const QuicServerId server_id_; +}; + +class AllServerIdsFilter : public QuicCryptoClientConfig::ServerIdFilter { + public: + bool Matches(const QuicServerId& server_id) const override { return true; } +}; + +} // namespace + +class QuicCryptoClientConfigTest : public QuicTest {}; + +TEST_F(QuicCryptoClientConfigTest, CachedState_IsEmpty) { + QuicCryptoClientConfig::CachedState state; + EXPECT_TRUE(state.IsEmpty()); +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_IsComplete) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0))); +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_GenerationCounter) { + QuicCryptoClientConfig::CachedState state; + EXPECT_EQ(0u, state.generation_counter()); + state.SetProofInvalid(); + EXPECT_EQ(1u, state.generation_counter()); +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) { + QuicCryptoClientConfig::CachedState state; + EXPECT_TRUE(state.proof_verify_details() == nullptr); + ProofVerifyDetails* details = new TestProofVerifyDetails; + state.SetProofVerifyDetails(details); + EXPECT_EQ(details, state.proof_verify_details()); +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_ServerDesignatedConnectionId) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_designated_connection_id()); + + uint64_t conn_id = 1234; + QuicConnectionId connection_id = TestConnectionId(conn_id); + state.add_server_designated_connection_id(connection_id); + EXPECT_TRUE(state.has_server_designated_connection_id()); + EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); + EXPECT_FALSE(state.has_server_designated_connection_id()); + + // Allow the ID to be set multiple times. It's unusual that this would + // happen, but not impossible. + connection_id = TestConnectionId(++conn_id); + state.add_server_designated_connection_id(connection_id); + EXPECT_TRUE(state.has_server_designated_connection_id()); + EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); + connection_id = TestConnectionId(++conn_id); + state.add_server_designated_connection_id(connection_id); + EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); + EXPECT_FALSE(state.has_server_designated_connection_id()); + + // Test FIFO behavior. + const QuicConnectionId first_cid = TestConnectionId(0xdeadbeef); + const QuicConnectionId second_cid = TestConnectionId(0xfeedbead); + state.add_server_designated_connection_id(first_cid); + state.add_server_designated_connection_id(second_cid); + EXPECT_TRUE(state.has_server_designated_connection_id()); + EXPECT_EQ(first_cid, state.GetNextServerDesignatedConnectionId()); + EXPECT_EQ(second_cid, state.GetNextServerDesignatedConnectionId()); +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_ServerIdConsumedBeforeSet) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_designated_connection_id()); +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + EXPECT_DEBUG_DEATH(state.GetNextServerDesignatedConnectionId(), + "Attempting to consume a connection id " + "that was never designated."); +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_ServerNonce) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_nonce()); + + QuicString server_nonce = "nonce_1"; + state.add_server_nonce(server_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + EXPECT_FALSE(state.has_server_nonce()); + + // Allow the ID to be set multiple times. It's unusual that this would + // happen, but not impossible. + server_nonce = "nonce_2"; + state.add_server_nonce(server_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + server_nonce = "nonce_3"; + state.add_server_nonce(server_nonce); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + EXPECT_FALSE(state.has_server_nonce()); + + // Test FIFO behavior. + const QuicString first_nonce = "first_nonce"; + const QuicString second_nonce = "second_nonce"; + state.add_server_nonce(first_nonce); + state.add_server_nonce(second_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(first_nonce, state.GetNextServerNonce()); + EXPECT_EQ(second_nonce, state.GetNextServerNonce()); +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_ServerNonceConsumedBeforeSet) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_nonce()); +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + EXPECT_DEBUG_DEATH(state.GetNextServerNonce(), + "Attempting to consume a server nonce " + "that was never designated."); +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) +} + +TEST_F(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig::CachedState other; + state.set_source_address_token("TOKEN"); + // TODO(rch): Populate other fields of |state|. + other.InitializeFrom(state); + EXPECT_EQ(state.server_config(), other.server_config()); + EXPECT_EQ(state.source_address_token(), other.source_address_token()); + EXPECT_EQ(state.certs(), other.certs()); + EXPECT_EQ(1u, other.generation_counter()); + EXPECT_FALSE(state.has_server_designated_connection_id()); + EXPECT_FALSE(state.has_server_nonce()); +} + +TEST_F(QuicCryptoClientConfigTest, InchoateChlo) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + config.set_user_agent_id("quic-tester"); + config.set_alpn("hq"); + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params( + new QuicCryptoNegotiatedParameters); + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, false); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, params, &msg); + + QuicVersionLabel cver; + EXPECT_EQ(QUIC_NO_ERROR, msg.GetVersionLabel(kVER, &cver)); + EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver); + QuicStringPiece proof_nonce; + EXPECT_TRUE(msg.GetStringPiece(kNONP, &proof_nonce)); + EXPECT_EQ(QuicString(32, 'r'), proof_nonce); + QuicStringPiece user_agent_id; + EXPECT_TRUE(msg.GetStringPiece(kUAID, &user_agent_id)); + EXPECT_EQ("quic-tester", user_agent_id); + QuicStringPiece alpn; + EXPECT_TRUE(msg.GetStringPiece(kALPN, &alpn)); + EXPECT_EQ("hq", alpn); + + EXPECT_EQ(msg.minimum_size(), 1024u); +} + +TEST_F(QuicCryptoClientConfigTest, InchoateChloIsNotPadded) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + config.set_pad_inchoate_hello(false); + config.set_user_agent_id("quic-tester"); + config.set_alpn("hq"); + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params( + new QuicCryptoNegotiatedParameters); + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, false); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, params, &msg); + + EXPECT_EQ(msg.minimum_size(), 1u); +} + +// Make sure AES-GCM is the preferred encryption algorithm if it has hardware +// acceleration. +TEST_F(QuicCryptoClientConfigTest, PreferAesGcm) { + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + if (EVP_has_aes_hardware() == 1) { + EXPECT_EQ(kAESG, config.aead[0]); + } else { + EXPECT_EQ(kCC20, config.aead[0]); + } +} + +TEST_F(QuicCryptoClientConfigTest, InchoateChloSecure) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params( + new QuicCryptoNegotiatedParameters); + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, false); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, params, &msg); + + QuicTag pdmd; + EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd)); + EXPECT_EQ(kX509, pdmd); + QuicStringPiece scid; + EXPECT_FALSE(msg.GetStringPiece(kSCID, &scid)); +} + +TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCIDNoEXPY) { + // Test that a config with no EXPY is still valid when a non-zero + // expiry time is passed in. + QuicCryptoClientConfig::CachedState state; + CryptoHandshakeMessage scfg; + scfg.set_tag(kSCFG); + scfg.SetStringPiece(kSCID, "12345678"); + QuicString details; + QuicWallTime now = QuicWallTime::FromUNIXSeconds(1); + QuicWallTime expiry = QuicWallTime::FromUNIXSeconds(2); + state.SetServerConfig(scfg.GetSerialized().AsStringPiece(), now, expiry, + &details); + + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params( + new QuicCryptoNegotiatedParameters); + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, false); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, params, &msg); + + QuicStringPiece scid; + EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid)); + EXPECT_EQ("12345678", scid); +} + +TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCID) { + QuicCryptoClientConfig::CachedState state; + CryptoHandshakeMessage scfg; + scfg.set_tag(kSCFG); + uint64_t future = 1; + scfg.SetValue(kEXPY, future); + scfg.SetStringPiece(kSCID, "12345678"); + QuicString details; + state.SetServerConfig(scfg.GetSerialized().AsStringPiece(), + QuicWallTime::FromUNIXSeconds(1), + QuicWallTime::FromUNIXSeconds(0), &details); + + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params( + new QuicCryptoNegotiatedParameters); + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, false); + MockRandom rand; + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand, + /* demand_x509_proof= */ true, params, &msg); + + QuicStringPiece scid; + EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid)); + EXPECT_EQ("12345678", scid); +} + +TEST_F(QuicCryptoClientConfigTest, FillClientHello) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params( + new QuicCryptoNegotiatedParameters); + QuicConnectionId kConnectionId = TestConnectionId(1234); + QuicString error_details; + MockRandom rand; + CryptoHandshakeMessage chlo; + QuicServerId server_id("www.google.com", 443, false); + config.FillClientHello(server_id, kConnectionId, QuicVersionMax(), &state, + QuicWallTime::Zero(), &rand, + nullptr, // channel_id_key + params, &chlo, &error_details); + + // Verify that the version label has been set correctly in the CHLO. + QuicVersionLabel cver; + EXPECT_EQ(QUIC_NO_ERROR, chlo.GetVersionLabel(kVER, &cver)); + EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver); +} + +TEST_F(QuicCryptoClientConfigTest, FillClientHelloNoPadding) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + config.set_pad_full_hello(false); + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params( + new QuicCryptoNegotiatedParameters); + QuicConnectionId kConnectionId = TestConnectionId(1234); + QuicString error_details; + MockRandom rand; + CryptoHandshakeMessage chlo; + QuicServerId server_id("www.google.com", 443, false); + config.FillClientHello(server_id, kConnectionId, QuicVersionMax(), &state, + QuicWallTime::Zero(), &rand, + nullptr, // channel_id_key + params, &chlo, &error_details); + + // Verify that the version label has been set correctly in the CHLO. + QuicVersionLabel cver; + EXPECT_EQ(QUIC_NO_ERROR, chlo.GetVersionLabel(kVER, &cver)); + EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver); + EXPECT_EQ(chlo.minimum_size(), 1u); +} + +TEST_F(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) { + ParsedQuicVersionVector supported_versions = AllSupportedVersions(); + if (supported_versions.size() == 1) { + // No downgrade attack is possible if the client only supports one version. + return; + } + + ParsedQuicVersionVector supported_version_vector; + for (size_t i = supported_versions.size(); i > 0; --i) { + supported_version_vector.push_back(supported_versions[i - 1]); + } + + CryptoHandshakeMessage msg; + msg.set_tag(kSHLO); + msg.SetVersionVector(kVER, supported_version_vector); + + QuicCryptoClientConfig::CachedState cached; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params( + new QuicCryptoNegotiatedParameters); + QuicString error; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + EXPECT_EQ(QUIC_VERSION_NEGOTIATION_MISMATCH, + config.ProcessServerHello( + msg, EmptyQuicConnectionId(), supported_versions.front(), + supported_versions, &cached, out_params, &error)); + EXPECT_THAT(error, StartsWith("Downgrade attack detected: ServerVersions")); +} + +TEST_F(QuicCryptoClientConfigTest, InitializeFrom) { + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + QuicServerId canonical_server_id("www.google.com", 443, false); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_server_id); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + QuicServerId other_server_id("mail.google.com", 443, false); + config.InitializeFrom(other_server_id, canonical_server_id, &config); + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(other_server_id); + + EXPECT_EQ(state->server_config(), other->server_config()); + EXPECT_EQ(state->source_address_token(), other->source_address_token()); + EXPECT_EQ(state->certs(), other->certs()); + EXPECT_EQ(1u, other->generation_counter()); +} + +TEST_F(QuicCryptoClientConfigTest, Canonical) { + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + config.AddCanonicalSuffix(".google.com"); + QuicServerId canonical_id1("www.google.com", 443, false); + QuicServerId canonical_id2("mail.google.com", 443, false); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_id1); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(canonical_id2); + + EXPECT_TRUE(state->IsEmpty()); + EXPECT_EQ(state->server_config(), other->server_config()); + EXPECT_EQ(state->source_address_token(), other->source_address_token()); + EXPECT_EQ(state->certs(), other->certs()); + EXPECT_EQ(1u, other->generation_counter()); + + QuicServerId different_id("mail.google.org", 443, false); + EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty()); +} + +TEST_F(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) { + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + config.AddCanonicalSuffix(".google.com"); + QuicServerId canonical_id1("www.google.com", 443, false); + QuicServerId canonical_id2("mail.google.com", 443, false); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_id1); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + + // Do not set the proof as valid, and check that it is not used + // as a canonical entry. + EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty()); +} + +TEST_F(QuicCryptoClientConfigTest, ClearCachedStates) { + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + + // Create two states on different origins. + struct TestCase { + TestCase(const QuicString& host, QuicCryptoClientConfig* config) + : server_id(host, 443, false), + state(config->LookupOrCreate(server_id)) { + // TODO(rch): Populate other fields of |state|. + CryptoHandshakeMessage scfg; + scfg.set_tag(kSCFG); + uint64_t future = 1; + scfg.SetValue(kEXPY, future); + scfg.SetStringPiece(kSCID, "12345678"); + QuicString details; + state->SetServerConfig(scfg.GetSerialized().AsStringPiece(), + QuicWallTime::FromUNIXSeconds(0), + QuicWallTime::FromUNIXSeconds(future), &details); + + std::vector<QuicString> certs(1); + certs[0] = "Hello Cert for " + host; + state->SetProof(certs, "cert_sct", "chlo_hash", "signature"); + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + // The generation counter starts at 2, because proof has been once + // invalidated in SetServerConfig(). + EXPECT_EQ(2u, state->generation_counter()); + } + + QuicServerId server_id; + QuicCryptoClientConfig::CachedState* state; + } test_cases[] = {TestCase("www.google.com", &config), + TestCase("www.example.com", &config)}; + + // Verify LookupOrCreate returns the same data. + for (const TestCase& test_case : test_cases) { + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(test_case.server_id); + EXPECT_EQ(test_case.state, other); + EXPECT_EQ(2u, other->generation_counter()); + } + + // Clear the cached state for www.google.com. + OneServerIdFilter google_com_filter(&test_cases[0].server_id); + config.ClearCachedStates(google_com_filter); + + // Verify LookupOrCreate doesn't have any data for google.com. + QuicCryptoClientConfig::CachedState* cleared_cache = + config.LookupOrCreate(test_cases[0].server_id); + + EXPECT_EQ(test_cases[0].state, cleared_cache); + EXPECT_FALSE(cleared_cache->proof_valid()); + EXPECT_TRUE(cleared_cache->server_config().empty()); + EXPECT_TRUE(cleared_cache->certs().empty()); + EXPECT_TRUE(cleared_cache->cert_sct().empty()); + EXPECT_TRUE(cleared_cache->signature().empty()); + EXPECT_EQ(3u, cleared_cache->generation_counter()); + + // But it still does for www.example.com. + QuicCryptoClientConfig::CachedState* existing_cache = + config.LookupOrCreate(test_cases[1].server_id); + + EXPECT_EQ(test_cases[1].state, existing_cache); + EXPECT_TRUE(existing_cache->proof_valid()); + EXPECT_FALSE(existing_cache->server_config().empty()); + EXPECT_FALSE(existing_cache->certs().empty()); + EXPECT_FALSE(existing_cache->cert_sct().empty()); + EXPECT_FALSE(existing_cache->signature().empty()); + EXPECT_EQ(2u, existing_cache->generation_counter()); + + // Clear all cached states. + AllServerIdsFilter all_server_ids; + config.ClearCachedStates(all_server_ids); + + // The data for www.example.com should now be cleared as well. + cleared_cache = config.LookupOrCreate(test_cases[1].server_id); + + EXPECT_EQ(test_cases[1].state, cleared_cache); + EXPECT_FALSE(cleared_cache->proof_valid()); + EXPECT_TRUE(cleared_cache->server_config().empty()); + EXPECT_TRUE(cleared_cache->certs().empty()); + EXPECT_TRUE(cleared_cache->cert_sct().empty()); + EXPECT_TRUE(cleared_cache->signature().empty()); + EXPECT_EQ(3u, cleared_cache->generation_counter()); +} + +TEST_F(QuicCryptoClientConfigTest, ProcessReject) { + CryptoHandshakeMessage rej; + crypto_test_utils::FillInDummyReject(&rej, /* stateless */ false); + + // Now process the rejection. + QuicCryptoClientConfig::CachedState cached; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params( + new QuicCryptoNegotiatedParameters); + QuicString error; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + EXPECT_EQ(QUIC_NO_ERROR, + config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0), + AllSupportedTransportVersions().front(), "", + &cached, out_params, &error)); + EXPECT_FALSE(cached.has_server_designated_connection_id()); + EXPECT_FALSE(cached.has_server_nonce()); +} + +TEST_F(QuicCryptoClientConfigTest, ProcessRejectWithLongTTL) { + CryptoHandshakeMessage rej; + crypto_test_utils::FillInDummyReject(&rej, /* stateless */ false); + QuicTime::Delta one_week = QuicTime::Delta::FromSeconds(kNumSecondsPerWeek); + int64_t long_ttl = 3 * one_week.ToSeconds(); + rej.SetValue(kSTTL, long_ttl); + + // Now process the rejection. + QuicCryptoClientConfig::CachedState cached; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params( + new QuicCryptoNegotiatedParameters); + QuicString error; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + EXPECT_EQ(QUIC_NO_ERROR, + config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0), + AllSupportedTransportVersions().front(), "", + &cached, out_params, &error)); + cached.SetProofValid(); + EXPECT_FALSE(cached.IsComplete(QuicWallTime::FromUNIXSeconds(long_ttl))); + EXPECT_FALSE( + cached.IsComplete(QuicWallTime::FromUNIXSeconds(one_week.ToSeconds()))); + EXPECT_TRUE(cached.IsComplete( + QuicWallTime::FromUNIXSeconds(one_week.ToSeconds() - 1))); +} + +TEST_F(QuicCryptoClientConfigTest, ProcessStatelessReject) { + // Create a dummy reject message and mark it as stateless. + CryptoHandshakeMessage rej; + crypto_test_utils::FillInDummyReject(&rej, /* stateless */ true); + const QuicConnectionId kConnectionId = TestConnectionId(0xdeadbeef); + const QuicString server_nonce = "SERVER_NONCE"; + const uint64_t kConnectionId64 = TestConnectionIdToUInt64(kConnectionId); + rej.SetValue(kRCID, kConnectionId64); + rej.SetStringPiece(kServerNonceTag, server_nonce); + + // Now process the rejection. + QuicCryptoClientConfig::CachedState cached; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params( + new QuicCryptoNegotiatedParameters); + QuicString error; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + EXPECT_EQ(QUIC_NO_ERROR, + config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0), + AllSupportedTransportVersions().front(), "", + &cached, out_params, &error)); + EXPECT_TRUE(cached.has_server_designated_connection_id()); + EXPECT_EQ(TestConnectionId(QuicEndian::NetToHost64( + TestConnectionIdToUInt64(kConnectionId))), + cached.GetNextServerDesignatedConnectionId()); + EXPECT_EQ(server_nonce, cached.GetNextServerNonce()); +} + +TEST_F(QuicCryptoClientConfigTest, BadlyFormattedStatelessReject) { + // Create a dummy reject message and mark it as stateless. Do not + // add an server-designated connection-id. + CryptoHandshakeMessage rej; + crypto_test_utils::FillInDummyReject(&rej, /* stateless */ true); + + // Now process the rejection. + QuicCryptoClientConfig::CachedState cached; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params( + new QuicCryptoNegotiatedParameters); + QuicString error; + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, + config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0), + AllSupportedTransportVersions().front(), "", + &cached, out_params, &error)); + EXPECT_FALSE(cached.has_server_designated_connection_id()); + EXPECT_EQ("Missing kRCID", error); +} + +TEST_F(QuicCryptoClientConfigTest, ServerNonceinSHLO) { + // Test that the server must include a nonce in the SHLO. + CryptoHandshakeMessage msg; + msg.set_tag(kSHLO); + // Choose the latest version. + ParsedQuicVersionVector supported_versions; + ParsedQuicVersion version = AllSupportedVersions().front(); + supported_versions.push_back(version); + msg.SetVersionVector(kVER, supported_versions); + + QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()); + QuicCryptoClientConfig::CachedState cached; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params( + new QuicCryptoNegotiatedParameters); + QuicString error_details; + EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + config.ProcessServerHello(msg, EmptyQuicConnectionId(), version, + supported_versions, &cached, out_params, + &error_details)); + EXPECT_EQ("server hello missing server nonce", error_details); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/quic_crypto_proof.cc b/quic/core/crypto/quic_crypto_proof.cc new file mode 100644 index 0000000..33780b4 --- /dev/null +++ b/quic/core/crypto/quic_crypto_proof.cc
@@ -0,0 +1,11 @@ +// Copyright 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/crypto/quic_crypto_proof.h" + +namespace quic { + +QuicCryptoProof::QuicCryptoProof() : send_expect_ct_header(false) {} + +} // namespace quic
diff --git a/quic/core/crypto/quic_crypto_proof.h b/quic/core/crypto/quic_crypto_proof.h new file mode 100644 index 0000000..d70ad50 --- /dev/null +++ b/quic/core/crypto/quic_crypto_proof.h
@@ -0,0 +1,28 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// Contains the crypto-related data provided by ProofSource +struct QUIC_EXPORT_PRIVATE QuicCryptoProof { + QuicCryptoProof(); + + // Signature generated by ProofSource + QuicString signature; + // SCTList (RFC6962) to be sent to the client, if it supports receiving it. + QuicString leaf_cert_scts; + // Should the Expect-CT header be sent on the connection where the + // certificate is used. + bool send_expect_ct_header; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_
diff --git a/quic/core/crypto/quic_crypto_server_config.cc b/quic/core/crypto/quic_crypto_server_config.cc new file mode 100644 index 0000000..1cb0266 --- /dev/null +++ b/quic/core/crypto/quic_crypto_server_config.cc
@@ -0,0 +1,2073 @@ +// Copyright 2013 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/crypto/quic_crypto_server_config.h" + +#include <algorithm> +#include <cstdlib> +#include <memory> + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/sha.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h" +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" +#include "net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h" +#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h" +#include "net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_source.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/crypto/quic_hkdf.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h" +#include "net/third_party/quiche/src/quic/core/proto/source_address_token.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_cert_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.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 { + +// kMultiplier is the multiple of the CHLO message size that a REJ message +// must stay under when the client doesn't present a valid source-address +// token. This is used to protect QUIC from amplification attacks. +// TODO(rch): Reduce this to 2 again once b/25933682 is fixed. +const size_t kMultiplier = 3; + +const int kMaxTokenAddresses = 4; + +QuicString DeriveSourceAddressTokenKey( + QuicStringPiece source_address_token_secret) { + QuicHKDF hkdf(source_address_token_secret, QuicStringPiece() /* no salt */, + "QUIC source address token key", + CryptoSecretBoxer::GetKeySize(), 0 /* no fixed IV needed */, + 0 /* no subkey secret */); + return QuicString(hkdf.server_write_key()); +} + +// Default source for creating KeyExchange objects. +class DefaultKeyExchangeSource : public KeyExchangeSource { + public: + DefaultKeyExchangeSource() = default; + ~DefaultKeyExchangeSource() override = default; + + std::unique_ptr<KeyExchange> Create(QuicString /*server_config_id*/, + QuicTag type, + QuicStringPiece private_key) override { + if (private_key.empty()) { + QUIC_LOG(WARNING) << "Server config contains key exchange method without " + "corresponding private key: " + << type; + return nullptr; + } + + std::unique_ptr<KeyExchange> ka; + switch (type) { + case kC255: + ka = Curve25519KeyExchange::New(private_key); + if (!ka) { + QUIC_LOG(WARNING) << "Server config contained an invalid curve25519" + " private key."; + return nullptr; + } + break; + case kP256: + ka = P256KeyExchange::New(private_key); + if (!ka) { + QUIC_LOG(WARNING) << "Server config contained an invalid P-256" + " private key."; + return nullptr; + } + break; + default: + QUIC_LOG(WARNING) + << "Server config message contains unknown key exchange " + "method: " + << type; + return nullptr; + } + return ka; + } +}; + +} // namespace + +// static +std::unique_ptr<KeyExchangeSource> KeyExchangeSource::Default() { + return QuicMakeUnique<DefaultKeyExchangeSource>(); +} + +class ValidateClientHelloHelper { + public: + // Note: stores a pointer to a unique_ptr, and std::moves the unique_ptr when + // ValidationComplete is called. + ValidateClientHelloHelper( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ValidateClientHelloResultCallback>* done_cb) + : result_(std::move(result)), done_cb_(done_cb) {} + ValidateClientHelloHelper(const ValidateClientHelloHelper&) = delete; + ValidateClientHelloHelper& operator=(const ValidateClientHelloHelper&) = + delete; + + ~ValidateClientHelloHelper() { + QUIC_BUG_IF(done_cb_ != nullptr) + << "Deleting ValidateClientHelloHelper with a pending callback."; + } + + void ValidationComplete( + QuicErrorCode error_code, + const char* error_details, + std::unique_ptr<ProofSource::Details> proof_source_details) { + result_->error_code = error_code; + result_->error_details = error_details; + (*done_cb_)->Run(std::move(result_), std::move(proof_source_details)); + DetachCallback(); + } + + void DetachCallback() { + QUIC_BUG_IF(done_cb_ == nullptr) << "Callback already detached."; + done_cb_ = nullptr; + } + + private: + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result_; + std::unique_ptr<ValidateClientHelloResultCallback>* done_cb_; +}; + +// static +const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; + +ClientHelloInfo::ClientHelloInfo(const QuicIpAddress& in_client_ip, + QuicWallTime in_now) + : client_ip(in_client_ip), now(in_now), valid_source_address_token(false) {} + +ClientHelloInfo::ClientHelloInfo(const ClientHelloInfo& other) = default; + +ClientHelloInfo::~ClientHelloInfo() {} + +PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {} + +PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {} + +ValidateClientHelloResultCallback::Result::Result( + const CryptoHandshakeMessage& in_client_hello, + QuicIpAddress in_client_ip, + QuicWallTime in_now) + : client_hello(in_client_hello), + info(in_client_ip, in_now), + error_code(QUIC_NO_ERROR) {} + +ValidateClientHelloResultCallback::Result::~Result() {} + +ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {} + +ValidateClientHelloResultCallback::~ValidateClientHelloResultCallback() {} + +ProcessClientHelloResultCallback::ProcessClientHelloResultCallback() {} + +ProcessClientHelloResultCallback::~ProcessClientHelloResultCallback() {} + +QuicCryptoServerConfig::ConfigOptions::ConfigOptions() + : expiry_time(QuicWallTime::Zero()), + channel_id_enabled(false), + p256(false) {} + +QuicCryptoServerConfig::ConfigOptions::ConfigOptions( + const ConfigOptions& other) = default; + +QuicCryptoServerConfig::ConfigOptions::~ConfigOptions() {} + +QuicCryptoServerConfig::QuicCryptoServerConfig( + QuicStringPiece source_address_token_secret, + QuicRandom* server_nonce_entropy, + std::unique_ptr<ProofSource> proof_source, + std::unique_ptr<KeyExchangeSource> key_exchange_source, + bssl::UniquePtr<SSL_CTX> ssl_ctx) + : replay_protection_(true), + chlo_multiplier_(kMultiplier), + configs_lock_(), + primary_config_(nullptr), + next_config_promotion_time_(QuicWallTime::Zero()), + proof_source_(std::move(proof_source)), + key_exchange_source_(std::move(key_exchange_source)), + ssl_ctx_(std::move(ssl_ctx)), + source_address_token_future_secs_(3600), + source_address_token_lifetime_secs_(86400), + enable_serving_sct_(false), + rejection_observer_(nullptr), + pad_rej_(true), + pad_shlo_(true), + validate_chlo_size_(true), + validate_source_address_token_(true) { + DCHECK(proof_source_.get()); + source_address_token_boxer_.SetKeys( + {DeriveSourceAddressTokenKey(source_address_token_secret)}); + + // Generate a random key and orbit for server nonces. + server_nonce_entropy->RandBytes(server_nonce_orbit_, + sizeof(server_nonce_orbit_)); + const size_t key_size = server_nonce_boxer_.GetKeySize(); + std::unique_ptr<uint8_t[]> key_bytes(new uint8_t[key_size]); + server_nonce_entropy->RandBytes(key_bytes.get(), key_size); + + server_nonce_boxer_.SetKeys( + {QuicString(reinterpret_cast<char*>(key_bytes.get()), key_size)}); +} + +QuicCryptoServerConfig::~QuicCryptoServerConfig() {} + +// static +std::unique_ptr<QuicServerConfigProtobuf> +QuicCryptoServerConfig::GenerateConfig(QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options) { + CryptoHandshakeMessage msg; + + const QuicString curve25519_private_key = + Curve25519KeyExchange::NewPrivateKey(rand); + std::unique_ptr<Curve25519KeyExchange> curve25519( + Curve25519KeyExchange::New(curve25519_private_key)); + QuicStringPiece curve25519_public_value = curve25519->public_value(); + + QuicString encoded_public_values; + // First three bytes encode the length of the public value. + DCHECK_LT(curve25519_public_value.size(), (1U << 24)); + encoded_public_values.push_back( + static_cast<char>(curve25519_public_value.size())); + encoded_public_values.push_back( + static_cast<char>(curve25519_public_value.size() >> 8)); + encoded_public_values.push_back( + static_cast<char>(curve25519_public_value.size() >> 16)); + encoded_public_values.append(curve25519_public_value.data(), + curve25519_public_value.size()); + + QuicString p256_private_key; + if (options.p256) { + p256_private_key = P256KeyExchange::NewPrivateKey(); + std::unique_ptr<P256KeyExchange> p256( + P256KeyExchange::New(p256_private_key)); + QuicStringPiece p256_public_value = p256->public_value(); + + DCHECK_LT(p256_public_value.size(), (1U << 24)); + encoded_public_values.push_back( + static_cast<char>(p256_public_value.size())); + encoded_public_values.push_back( + static_cast<char>(p256_public_value.size() >> 8)); + encoded_public_values.push_back( + static_cast<char>(p256_public_value.size() >> 16)); + encoded_public_values.append(p256_public_value.data(), + p256_public_value.size()); + } + + msg.set_tag(kSCFG); + if (options.p256) { + msg.SetVector(kKEXS, QuicTagVector{kC255, kP256}); + } else { + msg.SetVector(kKEXS, QuicTagVector{kC255}); + } + msg.SetVector(kAEAD, QuicTagVector{kAESG, kCC20}); + msg.SetStringPiece(kPUBS, encoded_public_values); + + if (options.expiry_time.IsZero()) { + const QuicWallTime now = clock->WallNow(); + const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds( + 60 * 60 * 24 * 180 /* 180 days, ~six months */)); + const uint64_t expiry_seconds = expiry.ToUNIXSeconds(); + msg.SetValue(kEXPY, expiry_seconds); + } else { + msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds()); + } + + char orbit_bytes[kOrbitSize]; + if (options.orbit.size() == sizeof(orbit_bytes)) { + memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes)); + } else { + DCHECK(options.orbit.empty()); + rand->RandBytes(orbit_bytes, sizeof(orbit_bytes)); + } + msg.SetStringPiece(kORBT, QuicStringPiece(orbit_bytes, sizeof(orbit_bytes))); + + if (options.channel_id_enabled) { + msg.SetVector(kPDMD, QuicTagVector{kCHID}); + } + + if (!options.token_binding_params.empty()) { + msg.SetVector(kTBKP, options.token_binding_params); + } + + if (options.id.empty()) { + // We need to ensure that the SCID changes whenever the server config does + // thus we make it a hash of the rest of the server config. + std::unique_ptr<QuicData> serialized( + CryptoFramer::ConstructHandshakeMessage(msg)); + + uint8_t scid_bytes[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast<const uint8_t*>(serialized->data()), + serialized->length(), scid_bytes); + // The SCID is a truncated SHA-256 digest. + static_assert(16 <= SHA256_DIGEST_LENGTH, "SCID length too high."); + msg.SetStringPiece( + kSCID, QuicStringPiece(reinterpret_cast<const char*>(scid_bytes), 16)); + } else { + msg.SetStringPiece(kSCID, options.id); + } + // Don't put new tags below this point. The SCID generation should hash over + // everything but itself and so extra tags should be added prior to the + // preceding if block. + + std::unique_ptr<QuicData> serialized( + CryptoFramer::ConstructHandshakeMessage(msg)); + + std::unique_ptr<QuicServerConfigProtobuf> config( + new QuicServerConfigProtobuf); + config->set_config(QuicString(serialized->AsStringPiece())); + QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key(); + curve25519_key->set_tag(kC255); + curve25519_key->set_private_key(curve25519_private_key); + + if (options.p256) { + QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key(); + p256_key->set_tag(kP256); + p256_key->set_private_key(p256_private_key); + } + + return config; +} + +CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( + std::unique_ptr<QuicServerConfigProtobuf> protobuf, + const QuicWallTime now) { + std::unique_ptr<CryptoHandshakeMessage> msg( + CryptoFramer::ParseMessage(protobuf->config())); + + if (!msg.get()) { + QUIC_LOG(WARNING) << "Failed to parse server config message"; + return nullptr; + } + + QuicReferenceCountedPointer<Config> config(ParseConfigProtobuf(protobuf)); + if (!config.get()) { + QUIC_LOG(WARNING) << "Failed to parse server config message"; + return nullptr; + } + + { + QuicWriterMutexLock locked(&configs_lock_); + if (configs_.find(config->id) != configs_.end()) { + QUIC_LOG(WARNING) << "Failed to add config because another with the same " + "server config id already exists: " + << QuicTextUtils::HexEncode(config->id); + return nullptr; + } + + configs_[config->id] = config; + SelectNewPrimaryConfig(now); + DCHECK(primary_config_.get()); + DCHECK_EQ(configs_.find(primary_config_->id)->second.get(), + primary_config_.get()); + } + + return msg.release(); +} + +CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options) { + return AddConfig(GenerateConfig(rand, clock, options), clock->WallNow()); +} + +bool QuicCryptoServerConfig::SetConfigs( + const std::vector<std::unique_ptr<QuicServerConfigProtobuf>>& protobufs, + const QuicWallTime now) { + std::vector<QuicReferenceCountedPointer<Config>> parsed_configs; + bool ok = true; + + for (auto& protobuf : protobufs) { + QuicReferenceCountedPointer<Config> config(ParseConfigProtobuf(protobuf)); + if (!config) { + ok = false; + break; + } + + parsed_configs.push_back(config); + } + + if (parsed_configs.empty()) { + QUIC_LOG(WARNING) << "New config list is empty."; + ok = false; + } + + if (!ok) { + QUIC_LOG(WARNING) << "Rejecting QUIC configs because of above errors"; + } else { + QUIC_LOG(INFO) << "Updating configs:"; + + QuicWriterMutexLock locked(&configs_lock_); + ConfigMap new_configs; + + for (std::vector<QuicReferenceCountedPointer<Config>>::const_iterator i = + parsed_configs.begin(); + i != parsed_configs.end(); ++i) { + QuicReferenceCountedPointer<Config> config = *i; + + auto it = configs_.find(config->id); + if (it != configs_.end()) { + QUIC_LOG(INFO) << "Keeping scid: " + << QuicTextUtils::HexEncode(config->id) << " orbit: " + << QuicTextUtils::HexEncode( + reinterpret_cast<const char*>(config->orbit), + kOrbitSize) + << " new primary_time " + << config->primary_time.ToUNIXSeconds() + << " old primary_time " + << it->second->primary_time.ToUNIXSeconds() + << " new priority " << config->priority + << " old priority " << it->second->priority; + // Update primary_time and priority. + it->second->primary_time = config->primary_time; + it->second->priority = config->priority; + new_configs.insert(*it); + } else { + QUIC_LOG(INFO) << "Adding scid: " + << QuicTextUtils::HexEncode(config->id) << " orbit: " + << QuicTextUtils::HexEncode( + reinterpret_cast<const char*>(config->orbit), + kOrbitSize) + << " primary_time " + << config->primary_time.ToUNIXSeconds() << " priority " + << config->priority; + new_configs.insert(std::make_pair(config->id, config)); + } + } + + configs_.swap(new_configs); + SelectNewPrimaryConfig(now); + DCHECK(primary_config_.get()); + DCHECK_EQ(configs_.find(primary_config_->id)->second.get(), + primary_config_.get()); + } + + return ok; +} + +void QuicCryptoServerConfig::SetSourceAddressTokenKeys( + const std::vector<QuicString>& keys) { + source_address_token_boxer_.SetKeys(keys); +} + +void QuicCryptoServerConfig::GetConfigIds( + std::vector<QuicString>* scids) const { + QuicReaderMutexLock locked(&configs_lock_); + for (auto it = configs_.begin(); it != configs_.end(); ++it) { + scids->push_back(it->first); + } +} + +void QuicCryptoServerConfig::ValidateClientHello( + const CryptoHandshakeMessage& client_hello, + const QuicIpAddress& client_ip, + const QuicSocketAddress& server_address, + QuicTransportVersion version, + const QuicClock* clock, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const { + const QuicWallTime now(clock->WallNow()); + + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> result( + new ValidateClientHelloResultCallback::Result(client_hello, client_ip, + now)); + + QuicStringPiece requested_scid; + client_hello.GetStringPiece(kSCID, &requested_scid); + + QuicReferenceCountedPointer<Config> requested_config; + QuicReferenceCountedPointer<Config> primary_config; + { + QuicReaderMutexLock locked(&configs_lock_); + if (!primary_config_.get()) { + result->error_code = QUIC_CRYPTO_INTERNAL_ERROR; + result->error_details = "No configurations loaded"; + } else { + if (IsNextConfigReady(now)) { + configs_lock_.ReaderUnlock(); + configs_lock_.WriterLock(); + SelectNewPrimaryConfig(now); + DCHECK(primary_config_.get()); + DCHECK_EQ(configs_.find(primary_config_->id)->second.get(), + primary_config_.get()); + configs_lock_.WriterUnlock(); + configs_lock_.ReaderLock(); + } + } + + requested_config = GetConfigWithScid(requested_scid); + primary_config = primary_config_; + signed_config->config = primary_config_; + } + + if (result->error_code == QUIC_NO_ERROR) { + // QUIC requires a new proof for each CHLO so clear any existing proof. + signed_config->chain = nullptr; + signed_config->proof.signature = ""; + signed_config->proof.leaf_cert_scts = ""; + EvaluateClientHello(server_address, version, requested_config, + primary_config, signed_config, result, + std::move(done_cb)); + } else { + done_cb->Run(result, /* details = */ nullptr); + } +} + +class ProcessClientHelloHelper { + public: + explicit ProcessClientHelloHelper( + std::unique_ptr<ProcessClientHelloResultCallback>* done_cb) + : done_cb_(done_cb) {} + + ~ProcessClientHelloHelper() { + QUIC_BUG_IF(done_cb_ != nullptr) + << "Deleting ProcessClientHelloHelper with a pending callback."; + } + + void Fail(QuicErrorCode error, const QuicString& error_details) { + (*done_cb_)->Run(error, error_details, nullptr, nullptr, nullptr); + DetachCallback(); + } + + void Succeed(std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> proof_source_details) { + (*done_cb_)->Run(QUIC_NO_ERROR, QuicString(), std::move(message), + std::move(diversification_nonce), + std::move(proof_source_details)); + DetachCallback(); + } + + void DetachCallback() { + QUIC_BUG_IF(done_cb_ == nullptr) << "Callback already detached."; + done_cb_ = nullptr; + } + + private: + std::unique_ptr<ProcessClientHelloResultCallback>* done_cb_; +}; + +class QuicCryptoServerConfig::ProcessClientHelloCallback + : public ProofSource::Callback { + public: + ProcessClientHelloCallback( + const QuicCryptoServerConfig* config, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result, + bool reject_only, + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + bool use_stateless_rejects, + QuicConnectionId server_designated_connection_id, + const QuicClock* clock, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + QuicByteCount total_framing_overhead, + QuicByteCount chlo_packet_size, + const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>& + requested_config, + const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>& + primary_config, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) + : config_(config), + validate_chlo_result_(std::move(validate_chlo_result)), + reject_only_(reject_only), + connection_id_(connection_id), + client_address_(client_address), + version_(version), + supported_versions_(supported_versions), + use_stateless_rejects_(use_stateless_rejects), + server_designated_connection_id_(server_designated_connection_id), + clock_(clock), + rand_(rand), + compressed_certs_cache_(compressed_certs_cache), + params_(params), + signed_config_(signed_config), + total_framing_overhead_(total_framing_overhead), + chlo_packet_size_(chlo_packet_size), + requested_config_(requested_config), + primary_config_(primary_config), + done_cb_(std::move(done_cb)) {} + + void Run(bool ok, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicCryptoProof& proof, + std::unique_ptr<ProofSource::Details> details) override { + if (ok) { + signed_config_->chain = chain; + signed_config_->proof = proof; + } + config_->ProcessClientHelloAfterGetProof( + !ok, std::move(details), validate_chlo_result_, reject_only_, + connection_id_, client_address_, version_, supported_versions_, + use_stateless_rejects_, server_designated_connection_id_, clock_, rand_, + compressed_certs_cache_, params_, signed_config_, + total_framing_overhead_, chlo_packet_size_, requested_config_, + primary_config_, std::move(done_cb_)); + } + + private: + const QuicCryptoServerConfig* config_; + const QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result_; + const bool reject_only_; + const QuicConnectionId connection_id_; + const QuicSocketAddress client_address_; + const ParsedQuicVersion version_; + const ParsedQuicVersionVector supported_versions_; + const bool use_stateless_rejects_; + const QuicConnectionId server_designated_connection_id_; + const QuicClock* const clock_; + QuicRandom* const rand_; + QuicCompressedCertsCache* compressed_certs_cache_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + const QuicByteCount total_framing_overhead_; + const QuicByteCount chlo_packet_size_; + const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> + requested_config_; + const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> + primary_config_; + std::unique_ptr<ProcessClientHelloResultCallback> done_cb_; +}; + +class QuicCryptoServerConfig::ProcessClientHelloAfterGetProofCallback + : public KeyExchange::Callback { + public: + ProcessClientHelloAfterGetProofCallback( + const QuicCryptoServerConfig* config, + std::unique_ptr<ProofSource::Details> proof_source_details, + const KeyExchange::Factory& key_exchange_factory, + std::unique_ptr<CryptoHandshakeMessage> out, + QuicStringPiece public_value, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result, + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + const QuicClock* clock, + QuicRandom* rand, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + const QuicReferenceCountedPointer<Config>& requested_config, + const QuicReferenceCountedPointer<Config>& primary_config, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) + : config_(config), + proof_source_details_(std::move(proof_source_details)), + key_exchange_factory_(key_exchange_factory), + out_(std::move(out)), + public_value_(public_value), + validate_chlo_result_(std::move(validate_chlo_result)), + connection_id_(connection_id), + client_address_(client_address), + version_(version), + supported_versions_(supported_versions), + clock_(clock), + rand_(rand), + params_(params), + signed_config_(signed_config), + requested_config_(requested_config), + primary_config_(primary_config), + done_cb_(std::move(done_cb)) {} + + void Run(bool ok) override { + config_->ProcessClientHelloAfterCalculateSharedKeys( + !ok, std::move(proof_source_details_), key_exchange_factory_, + std::move(out_), public_value_, *validate_chlo_result_, connection_id_, + client_address_, version_, supported_versions_, clock_, rand_, params_, + signed_config_, requested_config_, primary_config_, + std::move(done_cb_)); + } + + private: + const QuicCryptoServerConfig* config_; + std::unique_ptr<ProofSource::Details> proof_source_details_; + const KeyExchange::Factory& key_exchange_factory_; + std::unique_ptr<CryptoHandshakeMessage> out_; + QuicString public_value_; + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result_; + QuicConnectionId connection_id_; + const QuicSocketAddress client_address_; + ParsedQuicVersion version_; + const ParsedQuicVersionVector supported_versions_; + const QuicClock* clock_; + QuicRandom* rand_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + const QuicReferenceCountedPointer<Config> requested_config_; + const QuicReferenceCountedPointer<Config> primary_config_; + std::unique_ptr<ProcessClientHelloResultCallback> done_cb_; +}; + +void QuicCryptoServerConfig::ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result, + bool reject_only, + QuicConnectionId connection_id, + const QuicSocketAddress& server_address, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + bool use_stateless_rejects, + QuicConnectionId server_designated_connection_id, + const QuicClock* clock, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + QuicByteCount total_framing_overhead, + QuicByteCount chlo_packet_size, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const { + DCHECK(done_cb); + + ProcessClientHelloHelper helper(&done_cb); + + const CryptoHandshakeMessage& client_hello = + validate_chlo_result->client_hello; + const ClientHelloInfo& info = validate_chlo_result->info; + + QuicString error_details; + QuicErrorCode valid = CryptoUtils::ValidateClientHello( + client_hello, version, supported_versions, &error_details); + if (valid != QUIC_NO_ERROR) { + helper.Fail(valid, error_details); + return; + } + + QuicStringPiece requested_scid; + client_hello.GetStringPiece(kSCID, &requested_scid); + const QuicWallTime now(clock->WallNow()); + + QuicReferenceCountedPointer<Config> requested_config; + QuicReferenceCountedPointer<Config> primary_config; + bool no_primary_config = false; + { + QuicReaderMutexLock locked(&configs_lock_); + + if (!primary_config_) { + no_primary_config = true; + } else { + if (IsNextConfigReady(now)) { + configs_lock_.ReaderUnlock(); + configs_lock_.WriterLock(); + SelectNewPrimaryConfig(now); + DCHECK(primary_config_.get()); + DCHECK_EQ(configs_.find(primary_config_->id)->second.get(), + primary_config_.get()); + configs_lock_.WriterUnlock(); + configs_lock_.ReaderLock(); + } + + // Use the config that the client requested in order to do key-agreement. + // Otherwise give it a copy of |primary_config_| to use. + primary_config = signed_config->config; + requested_config = GetConfigWithScid(requested_scid); + } + } + if (no_primary_config) { + helper.Fail(QUIC_CRYPTO_INTERNAL_ERROR, "No configurations loaded"); + return; + } + + if (validate_chlo_result->error_code != QUIC_NO_ERROR) { + helper.Fail(validate_chlo_result->error_code, + validate_chlo_result->error_details); + return; + } + + if (!ClientDemandsX509Proof(client_hello)) { + helper.Fail(QUIC_UNSUPPORTED_PROOF_DEMAND, "Missing or invalid PDMD"); + return; + } + DCHECK(proof_source_.get()); + QuicString chlo_hash; + CryptoUtils::HashHandshakeMessage(client_hello, &chlo_hash, + Perspective::IS_SERVER); + + // No need to get a new proof if one was already generated. + if (!signed_config->chain) { + std::unique_ptr<ProcessClientHelloCallback> cb( + new ProcessClientHelloCallback( + this, validate_chlo_result, reject_only, connection_id, + client_address, version, supported_versions, use_stateless_rejects, + server_designated_connection_id, clock, rand, + compressed_certs_cache, params, signed_config, + total_framing_overhead, chlo_packet_size, requested_config, + primary_config, std::move(done_cb))); + proof_source_->GetProof( + server_address, QuicString(info.sni), primary_config->serialized, + version.transport_version, chlo_hash, std::move(cb)); + helper.DetachCallback(); + return; + } + + helper.DetachCallback(); + ProcessClientHelloAfterGetProof( + /* found_error = */ false, /* proof_source_details = */ nullptr, + validate_chlo_result, reject_only, connection_id, client_address, version, + supported_versions, use_stateless_rejects, + server_designated_connection_id, clock, rand, compressed_certs_cache, + params, signed_config, total_framing_overhead, chlo_packet_size, + requested_config, primary_config, std::move(done_cb)); +} + +void QuicCryptoServerConfig::ProcessClientHelloAfterGetProof( + bool found_error, + std::unique_ptr<ProofSource::Details> proof_source_details, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result, + bool reject_only, + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + bool use_stateless_rejects, + QuicConnectionId server_designated_connection_id, + const QuicClock* clock, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + QuicByteCount total_framing_overhead, + QuicByteCount chlo_packet_size, + const QuicReferenceCountedPointer<Config>& requested_config, + const QuicReferenceCountedPointer<Config>& primary_config, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const { + QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( + connection_id, version.transport_version)) + << "ProcessClientHelloAfterGetProof: attempted to use connection ID " + << connection_id << " which is invalid with version " + << QuicVersionToString(version.transport_version); + ProcessClientHelloHelper helper(&done_cb); + + if (found_error) { + helper.Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof"); + return; + } + + const CryptoHandshakeMessage& client_hello = + validate_chlo_result->client_hello; + const ClientHelloInfo& info = validate_chlo_result->info; + std::unique_ptr<DiversificationNonce> out_diversification_nonce( + new DiversificationNonce); + + QuicStringPiece cert_sct; + if (client_hello.GetStringPiece(kCertificateSCTTag, &cert_sct) && + cert_sct.empty()) { + params->sct_supported_by_client = true; + } + + std::unique_ptr<CryptoHandshakeMessage> out(new CryptoHandshakeMessage); + if (!info.reject_reasons.empty() || !requested_config.get()) { + BuildRejection(version.transport_version, clock->WallNow(), *primary_config, + client_hello, info, + validate_chlo_result->cached_network_params, + use_stateless_rejects, server_designated_connection_id, rand, + compressed_certs_cache, params, *signed_config, + total_framing_overhead, chlo_packet_size, out.get()); + if (rejection_observer_ != nullptr) { + rejection_observer_->OnRejectionBuilt(info.reject_reasons, out.get()); + } + helper.Succeed(std::move(out), std::move(out_diversification_nonce), + std::move(proof_source_details)); + return; + } + + if (reject_only) { + helper.Succeed(std::move(out), std::move(out_diversification_nonce), + std::move(proof_source_details)); + return; + } + + QuicTagVector their_aeads; + QuicTagVector their_key_exchanges; + if (client_hello.GetTaglist(kAEAD, &their_aeads) != QUIC_NO_ERROR || + client_hello.GetTaglist(kKEXS, &their_key_exchanges) != QUIC_NO_ERROR || + their_aeads.size() != 1 || their_key_exchanges.size() != 1) { + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + "Missing or invalid AEAD or KEXS"); + return; + } + + size_t key_exchange_index; + if (!FindMutualQuicTag(requested_config->aead, their_aeads, ¶ms->aead, + nullptr) || + !FindMutualQuicTag(requested_config->kexs, their_key_exchanges, + ¶ms->key_exchange, &key_exchange_index)) { + helper.Fail(QUIC_CRYPTO_NO_SUPPORT, "Unsupported AEAD or KEXS"); + return; + } + + if (!requested_config->tb_key_params.empty()) { + QuicTagVector their_tbkps; + switch (client_hello.GetTaglist(kTBKP, &their_tbkps)) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + break; + case QUIC_NO_ERROR: + if (FindMutualQuicTag(requested_config->tb_key_params, their_tbkps, + ¶ms->token_binding_key_param, nullptr)) { + break; + } + QUIC_FALLTHROUGH_INTENDED; + default: + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + "Invalid Token Binding key parameter"); + return; + } + } + + QuicStringPiece public_value; + if (!client_hello.GetStringPiece(kPUBS, &public_value)) { + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "Missing public value"); + return; + } + + const KeyExchange* key_exchange = + requested_config->key_exchanges[key_exchange_index].get(); + // TODO(rch): Would it be better to implement a move operator and just + // std::move(helper) instead of done_cb? + helper.DetachCallback(); + auto cb = QuicMakeUnique<ProcessClientHelloAfterGetProofCallback>( + this, std::move(proof_source_details), key_exchange->GetFactory(), + std::move(out), public_value, validate_chlo_result, connection_id, + client_address, version, supported_versions, clock, rand, params, + signed_config, requested_config, primary_config, std::move(done_cb)); + key_exchange->CalculateSharedKey( + public_value, ¶ms->initial_premaster_secret, std::move(cb)); +} + +void QuicCryptoServerConfig::ProcessClientHelloAfterCalculateSharedKeys( + bool found_error, + std::unique_ptr<ProofSource::Details> proof_source_details, + const KeyExchange::Factory& key_exchange_factory, + std::unique_ptr<CryptoHandshakeMessage> out, + QuicStringPiece public_value, + const ValidateClientHelloResultCallback::Result& validate_chlo_result, + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + const QuicClock* clock, + QuicRandom* rand, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + const QuicReferenceCountedPointer<Config>& requested_config, + const QuicReferenceCountedPointer<Config>& primary_config, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const { + QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( + connection_id, version.transport_version)) + << "ProcessClientHelloAfterCalculateSharedKeys:" + " attempted to use connection ID " + << connection_id << " which is invalid with version " + << QuicVersionToString(version.transport_version); + ProcessClientHelloHelper helper(&done_cb); + + if (found_error) { + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "Invalid public value"); + return; + } + + const CryptoHandshakeMessage& client_hello = + validate_chlo_result.client_hello; + const ClientHelloInfo& info = validate_chlo_result.info; + auto out_diversification_nonce = QuicMakeUnique<DiversificationNonce>(); + + if (!info.sni.empty()) { + params->sni = QuicHostnameUtils::NormalizeHostname(info.sni); + } + + QuicString hkdf_suffix; + const QuicData& client_hello_serialized = client_hello.GetSerialized(); + hkdf_suffix.reserve(connection_id.length() + + client_hello_serialized.length() + + requested_config->serialized.size()); + hkdf_suffix.append(connection_id.data(), connection_id.length()); + hkdf_suffix.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_suffix.append(requested_config->serialized); + DCHECK(proof_source_.get()); + if (signed_config->chain->certs.empty()) { + helper.Fail(QUIC_CRYPTO_INTERNAL_ERROR, "Failed to get certs"); + return; + } + hkdf_suffix.append(signed_config->chain->certs.at(0)); + + QuicStringPiece cetv_ciphertext; + if (requested_config->channel_id_enabled && + client_hello.GetStringPiece(kCETV, &cetv_ciphertext)) { + CryptoHandshakeMessage client_hello_copy(client_hello); + client_hello_copy.Erase(kCETV); + client_hello_copy.Erase(kPAD); + + const QuicData& client_hello_copy_serialized = + client_hello_copy.GetSerialized(); + QuicString hkdf_input; + hkdf_input.append(QuicCryptoConfig::kCETVLabel, + strlen(QuicCryptoConfig::kCETVLabel) + 1); + hkdf_input.append(connection_id.data(), connection_id.length()); + hkdf_input.append(client_hello_copy_serialized.data(), + client_hello_copy_serialized.length()); + hkdf_input.append(requested_config->serialized); + + CrypterPair crypters; + if (!CryptoUtils::DeriveKeys( + params->initial_premaster_secret, params->aead, info.client_nonce, + info.server_nonce, pre_shared_key_, hkdf_input, + Perspective::IS_SERVER, CryptoUtils::Diversification::Never(), + &crypters, nullptr /* subkey secret */)) { + helper.Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED, + "Symmetric key setup failed"); + return; + } + + char plaintext[kMaxPacketSize]; + size_t plaintext_length = 0; + const bool success = crypters.decrypter->DecryptPacket( + 0 /* packet number */, QuicStringPiece() /* associated data */, + cetv_ciphertext, plaintext, &plaintext_length, kMaxPacketSize); + if (!success) { + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + "CETV decryption failure"); + return; + } + std::unique_ptr<CryptoHandshakeMessage> cetv(CryptoFramer::ParseMessage( + QuicStringPiece(plaintext, plaintext_length))); + if (!cetv.get()) { + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "CETV parse error"); + return; + } + + QuicStringPiece key, signature; + if (cetv->GetStringPiece(kCIDK, &key) && + cetv->GetStringPiece(kCIDS, &signature)) { + if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) { + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + "ChannelID signature failure"); + return; + } + + params->channel_id = QuicString(key); + } + } + + QuicString hkdf_input; + size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; + hkdf_input.reserve(label_len + hkdf_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); + hkdf_input.append(hkdf_suffix); + + rand->RandBytes(out_diversification_nonce->data(), + out_diversification_nonce->size()); + CryptoUtils::Diversification diversification = + CryptoUtils::Diversification::Now(out_diversification_nonce.get()); + if (!CryptoUtils::DeriveKeys( + params->initial_premaster_secret, params->aead, info.client_nonce, + info.server_nonce, pre_shared_key_, hkdf_input, + Perspective::IS_SERVER, diversification, ¶ms->initial_crypters, + ¶ms->initial_subkey_secret)) { + helper.Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED, + "Symmetric key setup failed"); + return; + } + + QuicString forward_secure_public_value; + std::unique_ptr<KeyExchange> forward_secure_key_exchange = + key_exchange_factory.Create(rand); + forward_secure_public_value = + QuicString(forward_secure_key_exchange->public_value()); + if (!forward_secure_key_exchange->CalculateSharedKey( + public_value, ¶ms->forward_secure_premaster_secret)) { + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "Invalid public value"); + return; + } + + QuicString forward_secure_hkdf_input; + label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; + forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size()); + forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, + label_len); + forward_secure_hkdf_input.append(hkdf_suffix); + + QuicString shlo_nonce; + shlo_nonce = NewServerNonce(rand, info.now); + out->SetStringPiece(kServerNonceTag, shlo_nonce); + + if (!CryptoUtils::DeriveKeys( + params->forward_secure_premaster_secret, params->aead, + info.client_nonce, + shlo_nonce.empty() ? info.server_nonce : shlo_nonce, pre_shared_key_, + forward_secure_hkdf_input, Perspective::IS_SERVER, + CryptoUtils::Diversification::Never(), + ¶ms->forward_secure_crypters, ¶ms->subkey_secret)) { + helper.Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED, + "Symmetric key setup failed"); + return; + } + + out->set_tag(kSHLO); + out->SetVersionVector(kVER, supported_versions); + out->SetStringPiece( + kSourceAddressTokenTag, + NewSourceAddressToken(*requested_config, info.source_address_tokens, + client_address.host(), rand, info.now, nullptr)); + QuicSocketAddressCoder address_coder(client_address); + out->SetStringPiece(kCADR, address_coder.Encode()); + out->SetStringPiece(kPUBS, forward_secure_public_value); + + helper.Succeed(std::move(out), std::move(out_diversification_nonce), + std::move(proof_source_details)); +} + +QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> +QuicCryptoServerConfig::GetConfigWithScid( + QuicStringPiece requested_scid) const { + configs_lock_.AssertReaderHeld(); + + if (!requested_scid.empty()) { + auto it = configs_.find((QuicString(requested_scid))); + if (it != configs_.end()) { + // We'll use the config that the client requested in order to do + // key-agreement. + return QuicReferenceCountedPointer<Config>(it->second); + } + } + + return QuicReferenceCountedPointer<Config>(); +} + +// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for +// Config's based on their primary_time. +// static +bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan( + const QuicReferenceCountedPointer<Config>& a, + const QuicReferenceCountedPointer<Config>& b) { + if (a->primary_time.IsBefore(b->primary_time) || + b->primary_time.IsBefore(a->primary_time)) { + // Primary times differ. + return a->primary_time.IsBefore(b->primary_time); + } else if (a->priority != b->priority) { + // Primary times are equal, sort backwards by priority. + return a->priority < b->priority; + } else { + // Primary times and priorities are equal, sort by config id. + return a->id < b->id; + } +} + +void QuicCryptoServerConfig::SelectNewPrimaryConfig( + const QuicWallTime now) const { + std::vector<QuicReferenceCountedPointer<Config>> configs; + configs.reserve(configs_.size()); + + for (auto it = configs_.begin(); it != configs_.end(); ++it) { + // TODO(avd) Exclude expired configs? + configs.push_back(it->second); + } + + if (configs.empty()) { + if (primary_config_ != nullptr) { + QUIC_BUG << "No valid QUIC server config. Keeping the current config."; + } else { + QUIC_BUG << "No valid QUIC server config."; + } + return; + } + + std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan); + + QuicReferenceCountedPointer<Config> best_candidate = configs[0]; + + for (size_t i = 0; i < configs.size(); ++i) { + const QuicReferenceCountedPointer<Config> config(configs[i]); + if (!config->primary_time.IsAfter(now)) { + if (config->primary_time.IsAfter(best_candidate->primary_time)) { + best_candidate = config; + } + continue; + } + + // This is the first config with a primary_time in the future. Thus the + // previous Config should be the primary and this one should determine the + // next_config_promotion_time_. + QuicReferenceCountedPointer<Config> new_primary = best_candidate; + if (i == 0) { + // We need the primary_time of the next config. + if (configs.size() > 1) { + next_config_promotion_time_ = configs[1]->primary_time; + } else { + next_config_promotion_time_ = QuicWallTime::Zero(); + } + } else { + next_config_promotion_time_ = config->primary_time; + } + + if (primary_config_) { + primary_config_->is_primary = false; + } + primary_config_ = new_primary; + new_primary->is_primary = true; + QUIC_DLOG(INFO) << "New primary config. orbit: " + << QuicTextUtils::HexEncode(reinterpret_cast<const char*>( + primary_config_->orbit), + kOrbitSize); + if (primary_config_changed_cb_ != nullptr) { + primary_config_changed_cb_->Run(primary_config_->id); + } + + return; + } + + // All config's primary times are in the past. We should make the most recent + // and highest priority candidate primary. + QuicReferenceCountedPointer<Config> new_primary = best_candidate; + if (primary_config_) { + primary_config_->is_primary = false; + } + primary_config_ = new_primary; + new_primary->is_primary = true; + QUIC_DLOG(INFO) << "New primary config. orbit: " + << QuicTextUtils::HexEncode( + reinterpret_cast<const char*>(primary_config_->orbit), + kOrbitSize) + << " scid: " << QuicTextUtils::HexEncode(primary_config_->id); + next_config_promotion_time_ = QuicWallTime::Zero(); + if (primary_config_changed_cb_ != nullptr) { + primary_config_changed_cb_->Run(primary_config_->id); + } +} + +class QuicCryptoServerConfig::EvaluateClientHelloCallback + : public ProofSource::Callback { + public: + EvaluateClientHelloCallback( + const QuicCryptoServerConfig& config, + const QuicIpAddress& server_ip, + QuicTransportVersion version, + QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> + requested_config, + QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> + primary_config, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + client_hello_state, + std::unique_ptr<ValidateClientHelloResultCallback> done_cb) + : config_(config), + server_ip_(server_ip), + version_(version), + requested_config_(std::move(requested_config)), + primary_config_(std::move(primary_config)), + signed_config_(signed_config), + client_hello_state_(std::move(client_hello_state)), + done_cb_(std::move(done_cb)) {} + + void Run(bool ok, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicCryptoProof& proof, + std::unique_ptr<ProofSource::Details> details) override { + if (ok) { + signed_config_->chain = chain; + signed_config_->proof = proof; + } + config_.EvaluateClientHelloAfterGetProof( + server_ip_, version_, requested_config_, primary_config_, + signed_config_, std::move(details), !ok, client_hello_state_, + std::move(done_cb_)); + } + + private: + const QuicCryptoServerConfig& config_; + const QuicIpAddress& server_ip_; + const QuicTransportVersion version_; + const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> + requested_config_; + const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> + primary_config_; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + client_hello_state_; + std::unique_ptr<ValidateClientHelloResultCallback> done_cb_; +}; + +void QuicCryptoServerConfig::EvaluateClientHello( + const QuicSocketAddress& server_address, + QuicTransportVersion version, + QuicReferenceCountedPointer<Config> requested_config, + QuicReferenceCountedPointer<Config> primary_config, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + client_hello_state, + std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const { + DCHECK(!signed_config->chain); + + ValidateClientHelloHelper helper(client_hello_state, &done_cb); + + const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello; + ClientHelloInfo* info = &(client_hello_state->info); + + if (validate_chlo_size_ && client_hello.size() < kClientHelloMinimumSize) { + helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH, + "Client hello too small", nullptr); + return; + } + + if (client_hello.GetStringPiece(kSNI, &info->sni) && + !QuicHostnameUtils::IsValidSNI(info->sni)) { + helper.ValidationComplete(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + "Invalid SNI name", nullptr); + return; + } + + client_hello.GetStringPiece(kUAID, &info->user_agent_id); + + HandshakeFailureReason source_address_token_error = MAX_FAILURE_REASON; + if (validate_source_address_token_) { + QuicStringPiece srct; + if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) { + Config& config = + requested_config != nullptr ? *requested_config : *primary_config; + source_address_token_error = + ParseSourceAddressToken(config, srct, &info->source_address_tokens); + + if (source_address_token_error == HANDSHAKE_OK) { + source_address_token_error = ValidateSourceAddressTokens( + info->source_address_tokens, info->client_ip, info->now, + &client_hello_state->cached_network_params); + } + info->valid_source_address_token = + (source_address_token_error == HANDSHAKE_OK); + } else { + source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE; + } + } else { + source_address_token_error = HANDSHAKE_OK; + info->valid_source_address_token = true; + } + + if (!requested_config.get()) { + QuicStringPiece requested_scid; + if (client_hello.GetStringPiece(kSCID, &requested_scid)) { + info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); + } else { + info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE); + } + // No server config with the requested ID. + helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr); + return; + } + + if (!client_hello.GetStringPiece(kNONC, &info->client_nonce)) { + info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE); + // Report no client nonce as INCHOATE_HELLO_FAILURE. + helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr); + return; + } + + if (source_address_token_error != HANDSHAKE_OK) { + info->reject_reasons.push_back(source_address_token_error); + // No valid source address token. + } + + QuicReferenceCountedPointer<ProofSource::Chain> chain = + proof_source_->GetCertChain(server_address, QuicString(info->sni)); + if (!chain) { + info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); + } else if (!ValidateExpectedLeafCertificate(client_hello, chain->certs)) { + info->reject_reasons.push_back(INVALID_EXPECTED_LEAF_CERTIFICATE); + } + EvaluateClientHelloAfterGetProof( + server_address.host(), version, requested_config, primary_config, + signed_config, /*proof_source_details=*/nullptr, + /*get_proof_failed=*/false, client_hello_state, std::move(done_cb)); + helper.DetachCallback(); +} + +void QuicCryptoServerConfig::EvaluateClientHelloAfterGetProof( + const QuicIpAddress& server_ip, + QuicTransportVersion version, + QuicReferenceCountedPointer<Config> requested_config, + QuicReferenceCountedPointer<Config> primary_config, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + std::unique_ptr<ProofSource::Details> proof_source_details, + bool get_proof_failed, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + client_hello_state, + std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const { + ValidateClientHelloHelper helper(client_hello_state, &done_cb); + const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello; + ClientHelloInfo* info = &(client_hello_state->info); + + if (info->client_nonce.size() != kNonceSize) { + info->reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE); + // Invalid client nonce. + QUIC_LOG_FIRST_N(ERROR, 2) + << "Invalid client nonce: " << client_hello.DebugString(); + QUIC_DLOG(INFO) << "Invalid client nonce."; + } + + // Server nonce is optional, and used for key derivation if present. + client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce); + + QUIC_DVLOG(1) << "No 0-RTT replay protection in QUIC_VERSION_33 and higher."; + // If the server nonce is empty and we're requiring handshake confirmation + // for DoS reasons then we must reject the CHLO. + if (GetQuicReloadableFlag(quic_require_handshake_confirmation) && + info->server_nonce.empty()) { + info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE); + } + helper.ValidationComplete(QUIC_NO_ERROR, "", std::move(proof_source_details)); +} + +void QuicCryptoServerConfig::BuildServerConfigUpdateMessage( + QuicTransportVersion version, + QuicStringPiece chlo_hash, + const SourceAddressTokens& previous_source_address_tokens, + const QuicSocketAddress& server_address, + const QuicIpAddress& client_ip, + const QuicClock* clock, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + const QuicCryptoNegotiatedParameters& params, + const CachedNetworkParameters* cached_network_params, + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const { + QuicString serialized; + QuicString source_address_token; + const CommonCertSets* common_cert_sets; + { + QuicReaderMutexLock locked(&configs_lock_); + serialized = primary_config_->serialized; + common_cert_sets = primary_config_->common_cert_sets; + source_address_token = NewSourceAddressToken( + *primary_config_, previous_source_address_tokens, client_ip, rand, + clock->WallNow(), cached_network_params); + } + + CryptoHandshakeMessage message; + message.set_tag(kSCUP); + message.SetStringPiece(kSCFG, serialized); + message.SetStringPiece(kSourceAddressTokenTag, source_address_token); + + std::unique_ptr<BuildServerConfigUpdateMessageProofSourceCallback> + proof_source_cb(new BuildServerConfigUpdateMessageProofSourceCallback( + this, version, compressed_certs_cache, common_cert_sets, params, + std::move(message), std::move(cb))); + + proof_source_->GetProof(server_address, params.sni, serialized, version, + chlo_hash, std::move(proof_source_cb)); +} + +QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback:: + ~BuildServerConfigUpdateMessageProofSourceCallback() {} + +QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback:: + BuildServerConfigUpdateMessageProofSourceCallback( + const QuicCryptoServerConfig* config, + QuicTransportVersion version, + QuicCompressedCertsCache* compressed_certs_cache, + const CommonCertSets* common_cert_sets, + const QuicCryptoNegotiatedParameters& params, + CryptoHandshakeMessage message, + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) + : config_(config), + version_(version), + compressed_certs_cache_(compressed_certs_cache), + common_cert_sets_(common_cert_sets), + client_common_set_hashes_(params.client_common_set_hashes), + client_cached_cert_hashes_(params.client_cached_cert_hashes), + sct_supported_by_client_(params.sct_supported_by_client), + sni_(params.sni), + message_(std::move(message)), + cb_(std::move(cb)) {} + +void QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback:: + Run(bool ok, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicCryptoProof& proof, + std::unique_ptr<ProofSource::Details> details) { + config_->FinishBuildServerConfigUpdateMessage( + version_, compressed_certs_cache_, common_cert_sets_, + client_common_set_hashes_, client_cached_cert_hashes_, + sct_supported_by_client_, sni_, ok, chain, proof.signature, + proof.leaf_cert_scts, std::move(details), std::move(message_), + std::move(cb_)); +} + +void QuicCryptoServerConfig::FinishBuildServerConfigUpdateMessage( + QuicTransportVersion version, + QuicCompressedCertsCache* compressed_certs_cache, + const CommonCertSets* common_cert_sets, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes, + bool sct_supported_by_client, + const QuicString& sni, + bool ok, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& signature, + const QuicString& leaf_cert_sct, + std::unique_ptr<ProofSource::Details> details, + CryptoHandshakeMessage message, + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const { + if (!ok) { + cb->Run(false, message); + return; + } + + const QuicString compressed = + CompressChain(compressed_certs_cache, chain, client_common_set_hashes, + client_cached_cert_hashes, common_cert_sets); + + message.SetStringPiece(kCertificateTag, compressed); + message.SetStringPiece(kPROF, signature); + if (sct_supported_by_client && enable_serving_sct_) { + if (leaf_cert_sct.empty()) { + QUIC_LOG_EVERY_N_SEC(WARNING, 60) + << "SCT is expected but it is empty. SNI: " << sni; + } else { + message.SetStringPiece(kCertificateSCTTag, leaf_cert_sct); + } + } + + cb->Run(true, message); +} + +void QuicCryptoServerConfig::BuildRejection( + QuicTransportVersion version, + QuicWallTime now, + const Config& config, + const CryptoHandshakeMessage& client_hello, + const ClientHelloInfo& info, + const CachedNetworkParameters& cached_network_params, + bool use_stateless_rejects, + QuicConnectionId server_designated_connection_id, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + const QuicSignedServerConfig& signed_config, + QuicByteCount total_framing_overhead, + QuicByteCount chlo_packet_size, + CryptoHandshakeMessage* out) const { + if (GetQuicReloadableFlag(enable_quic_stateless_reject_support) && + use_stateless_rejects) { + QUIC_DVLOG(1) << "QUIC Crypto server config returning stateless reject " + << "with server-designated connection ID " + << server_designated_connection_id; + out->set_tag(kSREJ); + if (!QuicUtils::IsConnectionIdValidForVersion( + server_designated_connection_id, version)) { + QUIC_BUG << "Tried to send server designated connection ID " + << server_designated_connection_id + << " which is invalid with version " + << QuicVersionToString(version); + return; + } + out->SetStringPiece( + kRCID, QuicStringPiece(server_designated_connection_id.data(), + server_designated_connection_id.length())); + } else { + out->set_tag(kREJ); + } + out->SetStringPiece(kSCFG, config.serialized); + out->SetStringPiece( + kSourceAddressTokenTag, + NewSourceAddressToken(config, info.source_address_tokens, info.client_ip, + rand, info.now, &cached_network_params)); + out->SetValue(kSTTL, config.expiry_time.AbsoluteDifference(now).ToSeconds()); + if (replay_protection_) { + out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now)); + } + + // Send client the reject reason for debugging purposes. + DCHECK_LT(0u, info.reject_reasons.size()); + out->SetVector(kRREJ, info.reject_reasons); + + // The client may have requested a certificate chain. + if (!ClientDemandsX509Proof(client_hello)) { + QUIC_BUG << "x509 certificates not supported in proof demand"; + return; + } + + QuicStringPiece client_common_set_hashes; + if (client_hello.GetStringPiece(kCCS, &client_common_set_hashes)) { + params->client_common_set_hashes = QuicString(client_common_set_hashes); + } + + QuicStringPiece client_cached_cert_hashes; + if (client_hello.GetStringPiece(kCCRT, &client_cached_cert_hashes)) { + params->client_cached_cert_hashes = QuicString(client_cached_cert_hashes); + } else { + params->client_cached_cert_hashes.clear(); + } + + const QuicString compressed = + CompressChain(compressed_certs_cache, signed_config.chain, + params->client_common_set_hashes, + params->client_cached_cert_hashes, config.common_cert_sets); + + DCHECK_GT(chlo_packet_size, client_hello.size()); + // kREJOverheadBytes is a very rough estimate of how much of a REJ + // message is taken up by things other than the certificates. + // STK: 56 bytes + // SNO: 56 bytes + // SCFG + // SCID: 16 bytes + // PUBS: 38 bytes + const size_t kREJOverheadBytes = 166; + // max_unverified_size is the number of bytes that the certificate chain, + // signature, and (optionally) signed certificate timestamp can consume before + // we will demand a valid source-address token. + const size_t max_unverified_size = + chlo_multiplier_ * (chlo_packet_size - total_framing_overhead) - + kREJOverheadBytes; + static_assert(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes, + "overhead calculation may underflow"); + bool should_return_sct = + params->sct_supported_by_client && enable_serving_sct_; + const QuicString& cert_sct = signed_config.proof.leaf_cert_scts; + const size_t sct_size = should_return_sct ? cert_sct.size() : 0; + const size_t total_size = + signed_config.proof.signature.size() + compressed.size() + sct_size; + if (info.valid_source_address_token || total_size < max_unverified_size) { + out->SetStringPiece(kCertificateTag, compressed); + out->SetStringPiece(kPROF, signed_config.proof.signature); + if (should_return_sct) { + if (cert_sct.empty()) { + if (!GetQuicReloadableFlag(quic_log_cert_name_for_empty_sct)) { + QUIC_LOG_EVERY_N_SEC(WARNING, 60) + << "SCT is expected but it is empty. sni :" << params->sni; + } else { + // Log SNI and subject name for the leaf cert if its SCT is empty. + // This is for debugging b/28342827. + const std::vector<quic::QuicString>& certs = + signed_config.chain->certs; + QuicStringPiece ca_subject; + if (!certs.empty()) { + QuicCertUtils::ExtractSubjectNameFromDERCert(certs[0], &ca_subject); + } + QUIC_LOG_EVERY_N_SEC(WARNING, 60) + << "SCT is expected but it is empty. sni: '" << params->sni + << "' cert subject: '" << ca_subject << "'"; + } + } else { + out->SetStringPiece(kCertificateSCTTag, cert_sct); + } + } + } else { + QUIC_LOG_EVERY_N_SEC(WARNING, 60) + << "Sending inchoate REJ for hostname: " << info.sni + << " signature: " << signed_config.proof.signature.size() + << " cert: " << compressed.size() << " sct:" << sct_size + << " total: " << total_size << " max: " << max_unverified_size; + } +} + +QuicString QuicCryptoServerConfig::CompressChain( + QuicCompressedCertsCache* compressed_certs_cache, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes, + const CommonCertSets* common_sets) { + // Check whether the compressed certs is available in the cache. + DCHECK(compressed_certs_cache); + const QuicString* cached_value = compressed_certs_cache->GetCompressedCert( + chain, client_common_set_hashes, client_cached_cert_hashes); + if (cached_value) { + return *cached_value; + } + QuicString compressed = + CertCompressor::CompressChain(chain->certs, client_common_set_hashes, + client_cached_cert_hashes, common_sets); + // Insert the newly compressed cert to cache. + compressed_certs_cache->Insert(chain, client_common_set_hashes, + client_cached_cert_hashes, compressed); + return compressed; +} + +QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> +QuicCryptoServerConfig::ParseConfigProtobuf( + const std::unique_ptr<QuicServerConfigProtobuf>& protobuf) { + std::unique_ptr<CryptoHandshakeMessage> msg( + CryptoFramer::ParseMessage(protobuf->config())); + + if (msg->tag() != kSCFG) { + QUIC_LOG(WARNING) << "Server config message has tag " << msg->tag() + << " expected " << kSCFG; + return nullptr; + } + + QuicReferenceCountedPointer<Config> config(new Config); + config->serialized = protobuf->config(); + config->source_address_token_boxer = &source_address_token_boxer_; + + if (protobuf->has_primary_time()) { + config->primary_time = + QuicWallTime::FromUNIXSeconds(protobuf->primary_time()); + } + + config->priority = protobuf->priority(); + + QuicStringPiece scid; + if (!msg->GetStringPiece(kSCID, &scid)) { + QUIC_LOG(WARNING) << "Server config message is missing SCID"; + return nullptr; + } + config->id = QuicString(scid); + + if (msg->GetTaglist(kAEAD, &config->aead) != QUIC_NO_ERROR) { + QUIC_LOG(WARNING) << "Server config message is missing AEAD"; + return nullptr; + } + + QuicTagVector kexs_tags; + if (msg->GetTaglist(kKEXS, &kexs_tags) != QUIC_NO_ERROR) { + QUIC_LOG(WARNING) << "Server config message is missing KEXS"; + return nullptr; + } + + QuicErrorCode err; + if ((err = msg->GetTaglist(kTBKP, &config->tb_key_params)) != + QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND && + err != QUIC_NO_ERROR) { + QUIC_LOG(WARNING) << "Server config message is missing or has invalid TBKP"; + return nullptr; + } + + QuicStringPiece orbit; + if (!msg->GetStringPiece(kORBT, &orbit)) { + QUIC_LOG(WARNING) << "Server config message is missing ORBT"; + return nullptr; + } + + if (orbit.size() != kOrbitSize) { + QUIC_LOG(WARNING) << "Orbit value in server config is the wrong length." + " Got " + << orbit.size() << " want " << kOrbitSize; + return nullptr; + } + static_assert(sizeof(config->orbit) == kOrbitSize, "incorrect orbit size"); + memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); + + if (kexs_tags.size() != static_cast<size_t>(protobuf->key_size())) { + QUIC_LOG(WARNING) << "Server config has " << kexs_tags.size() + << " key exchange methods configured, but " + << protobuf->key_size() << " private keys"; + return nullptr; + } + + QuicTagVector proof_demand_tags; + if (msg->GetTaglist(kPDMD, &proof_demand_tags) == QUIC_NO_ERROR) { + for (QuicTag tag : proof_demand_tags) { + if (tag == kCHID) { + config->channel_id_enabled = true; + break; + } + } + } + + for (size_t i = 0; i < kexs_tags.size(); i++) { + const QuicTag tag = kexs_tags[i]; + QuicString private_key; + + config->kexs.push_back(tag); + + for (int j = 0; j < protobuf->key_size(); j++) { + const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i); + if (key.tag() == tag) { + private_key = key.private_key(); + break; + } + } + + std::unique_ptr<KeyExchange> ka = + key_exchange_source_->Create(config->id, tag, private_key); + if (!ka) { + return nullptr; + } + for (const auto& key_exchange : config->key_exchanges) { + if (key_exchange->GetFactory().tag() == tag) { + QUIC_LOG(WARNING) << "Duplicate key exchange in config: " << tag; + return nullptr; + } + } + + config->key_exchanges.push_back(std::move(ka)); + } + + uint64_t expiry_seconds; + if (msg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { + QUIC_LOG(WARNING) << "Server config message is missing EXPY"; + return nullptr; + } + config->expiry_time = QuicWallTime::FromUNIXSeconds(expiry_seconds); + + return config; +} + +void QuicCryptoServerConfig::set_replay_protection(bool on) { + replay_protection_ = on; +} + +void QuicCryptoServerConfig::set_chlo_multiplier(size_t multiplier) { + chlo_multiplier_ = multiplier; +} + +void QuicCryptoServerConfig::set_source_address_token_future_secs( + uint32_t future_secs) { + source_address_token_future_secs_ = future_secs; +} + +void QuicCryptoServerConfig::set_source_address_token_lifetime_secs( + uint32_t lifetime_secs) { + source_address_token_lifetime_secs_ = lifetime_secs; +} + +void QuicCryptoServerConfig::set_enable_serving_sct(bool enable_serving_sct) { + enable_serving_sct_ = enable_serving_sct; +} + +void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb( + std::unique_ptr<PrimaryConfigChangedCallback> cb) { + QuicWriterMutexLock locked(&configs_lock_); + primary_config_changed_cb_ = std::move(cb); +} + +QuicString QuicCryptoServerConfig::NewSourceAddressToken( + const Config& config, + const SourceAddressTokens& previous_tokens, + const QuicIpAddress& ip, + QuicRandom* rand, + QuicWallTime now, + const CachedNetworkParameters* cached_network_params) const { + SourceAddressTokens source_address_tokens; + SourceAddressToken* source_address_token = source_address_tokens.add_tokens(); + source_address_token->set_ip(ip.DualStacked().ToPackedString()); + source_address_token->set_timestamp(now.ToUNIXSeconds()); + if (cached_network_params != nullptr) { + *(source_address_token->mutable_cached_network_parameters()) = + *cached_network_params; + } + + // Append previous tokens. + for (const SourceAddressToken& token : previous_tokens.tokens()) { + if (source_address_tokens.tokens_size() > kMaxTokenAddresses) { + break; + } + + if (token.ip() == source_address_token->ip()) { + // It's for the same IP address. + continue; + } + + if (ValidateSourceAddressTokenTimestamp(token, now) != HANDSHAKE_OK) { + continue; + } + + *(source_address_tokens.add_tokens()) = token; + } + + return config.source_address_token_boxer->Box( + rand, source_address_tokens.SerializeAsString()); +} + +int QuicCryptoServerConfig::NumberOfConfigs() const { + QuicReaderMutexLock locked(&configs_lock_); + return configs_.size(); +} + +ProofSource* QuicCryptoServerConfig::proof_source() const { + return proof_source_.get(); +} + +SSL_CTX* QuicCryptoServerConfig::ssl_ctx() const { + return ssl_ctx_.get(); +} + +HandshakeFailureReason QuicCryptoServerConfig::ParseSourceAddressToken( + const Config& config, + QuicStringPiece token, + SourceAddressTokens* tokens) const { + QuicString storage; + QuicStringPiece plaintext; + if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) { + return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE; + } + + if (!tokens->ParseFromArray(plaintext.data(), plaintext.size())) { + // Some clients might still be using the old source token format so + // attempt to parse that format. + // TODO(rch): remove this code once the new format is ubiquitous. + SourceAddressToken token; + if (!token.ParseFromArray(plaintext.data(), plaintext.size())) { + return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE; + } + *tokens->add_tokens() = token; + } + + return HANDSHAKE_OK; +} + +HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens( + const SourceAddressTokens& source_address_tokens, + const QuicIpAddress& ip, + QuicWallTime now, + CachedNetworkParameters* cached_network_params) const { + HandshakeFailureReason reason = + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE; + for (const SourceAddressToken& token : source_address_tokens.tokens()) { + reason = ValidateSingleSourceAddressToken(token, ip, now); + if (reason == HANDSHAKE_OK) { + if (token.has_cached_network_parameters()) { + *cached_network_params = token.cached_network_parameters(); + } + break; + } + } + return reason; +} + +HandshakeFailureReason QuicCryptoServerConfig::ValidateSingleSourceAddressToken( + const SourceAddressToken& source_address_token, + const QuicIpAddress& ip, + QuicWallTime now) const { + if (source_address_token.ip() != ip.DualStacked().ToPackedString()) { + // It's for a different IP address. + return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE; + } + + return ValidateSourceAddressTokenTimestamp(source_address_token, now); +} + +HandshakeFailureReason +QuicCryptoServerConfig::ValidateSourceAddressTokenTimestamp( + const SourceAddressToken& source_address_token, + QuicWallTime now) const { + const QuicWallTime timestamp( + QuicWallTime::FromUNIXSeconds(source_address_token.timestamp())); + const QuicTime::Delta delta(now.AbsoluteDifference(timestamp)); + + if (now.IsBefore(timestamp) && + delta.ToSeconds() > source_address_token_future_secs_) { + return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE; + } + + if (now.IsAfter(timestamp) && + delta.ToSeconds() > source_address_token_lifetime_secs_) { + return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE; + } + + return HANDSHAKE_OK; +} + +// kServerNoncePlaintextSize is the number of bytes in an unencrypted server +// nonce. +static const size_t kServerNoncePlaintextSize = + 4 /* timestamp */ + 20 /* random bytes */; + +QuicString QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand, + QuicWallTime now) const { + const uint32_t timestamp = static_cast<uint32_t>(now.ToUNIXSeconds()); + + uint8_t server_nonce[kServerNoncePlaintextSize]; + static_assert(sizeof(server_nonce) > sizeof(timestamp), "nonce too small"); + server_nonce[0] = static_cast<uint8_t>(timestamp >> 24); + server_nonce[1] = static_cast<uint8_t>(timestamp >> 16); + server_nonce[2] = static_cast<uint8_t>(timestamp >> 8); + server_nonce[3] = static_cast<uint8_t>(timestamp); + rand->RandBytes(&server_nonce[sizeof(timestamp)], + sizeof(server_nonce) - sizeof(timestamp)); + + return server_nonce_boxer_.Box( + rand, QuicStringPiece(reinterpret_cast<char*>(server_nonce), + sizeof(server_nonce))); +} + +bool QuicCryptoServerConfig::ValidateExpectedLeafCertificate( + const CryptoHandshakeMessage& client_hello, + const std::vector<QuicString>& certs) const { + if (certs.empty()) { + return false; + } + + uint64_t hash_from_client; + if (client_hello.GetUint64(kXLCT, &hash_from_client) != QUIC_NO_ERROR) { + return false; + } + return CryptoUtils::ComputeLeafCertHash(certs.at(0)) == hash_from_client; +} + +bool QuicCryptoServerConfig::ClientDemandsX509Proof( + const CryptoHandshakeMessage& client_hello) const { + QuicTagVector their_proof_demands; + + if (client_hello.GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) { + return false; + } + + for (const QuicTag tag : their_proof_demands) { + if (tag == kX509) { + return true; + } + } + return false; +} + +bool QuicCryptoServerConfig::IsNextConfigReady(QuicWallTime now) const { + if (GetQuicReloadableFlag(quic_fix_config_rotation)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_config_rotation); + return !next_config_promotion_time_.IsZero() && + !next_config_promotion_time_.IsAfter(now); + } + return !next_config_promotion_time_.IsZero() && + next_config_promotion_time_.IsAfter(now); +} + +QuicCryptoServerConfig::Config::Config() + : channel_id_enabled(false), + is_primary(false), + primary_time(QuicWallTime::Zero()), + expiry_time(QuicWallTime::Zero()), + priority(0), + source_address_token_boxer(nullptr) {} + +QuicCryptoServerConfig::Config::~Config() {} + +QuicSignedServerConfig::QuicSignedServerConfig() {} +QuicSignedServerConfig::~QuicSignedServerConfig() {} + +} // namespace quic
diff --git a/quic/core/crypto/quic_crypto_server_config.h b/quic/core/crypto/quic_crypto_server_config.h new file mode 100644 index 0000000..9f91c9a --- /dev/null +++ b/quic/core/crypto/quic_crypto_server_config.h
@@ -0,0 +1,877 @@ +// Copyright 2013 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_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ + +#include <cstddef> +#include <cstdint> +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/base.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_secret_boxer.h" +#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h" +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/proto/source_address_token.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class CryptoHandshakeMessage; +class ProofSource; +class QuicClock; +class QuicRandom; +class QuicServerConfigProtobuf; +struct QuicSignedServerConfig; + +// ClientHelloInfo contains information about a client hello message that is +// only kept for as long as it's being processed. +struct ClientHelloInfo { + ClientHelloInfo(const QuicIpAddress& in_client_ip, QuicWallTime in_now); + ClientHelloInfo(const ClientHelloInfo& other); + ~ClientHelloInfo(); + + // Inputs to EvaluateClientHello. + const QuicIpAddress client_ip; + const QuicWallTime now; + + // Outputs from EvaluateClientHello. + bool valid_source_address_token; + QuicStringPiece sni; + QuicStringPiece client_nonce; + QuicStringPiece server_nonce; + QuicStringPiece user_agent_id; + SourceAddressTokens source_address_tokens; + + // Errors from EvaluateClientHello. + std::vector<uint32_t> reject_reasons; + static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync"); +}; + +namespace test { +class QuicCryptoServerConfigPeer; +} // namespace test + +// Hook that allows application code to subscribe to primary config changes. +class PrimaryConfigChangedCallback { + public: + PrimaryConfigChangedCallback(); + PrimaryConfigChangedCallback(const PrimaryConfigChangedCallback&) = delete; + PrimaryConfigChangedCallback& operator=(const PrimaryConfigChangedCallback&) = + delete; + virtual ~PrimaryConfigChangedCallback(); + virtual void Run(const QuicString& scid) = 0; +}; + +// Callback used to accept the result of the |client_hello| validation step. +class QUIC_EXPORT_PRIVATE ValidateClientHelloResultCallback { + public: + // Opaque token that holds information about the client_hello and + // its validity. Can be interpreted by calling ProcessClientHello. + struct QUIC_EXPORT_PRIVATE Result : public QuicReferenceCounted { + Result(const CryptoHandshakeMessage& in_client_hello, + QuicIpAddress in_client_ip, + QuicWallTime in_now); + + CryptoHandshakeMessage client_hello; + ClientHelloInfo info; + QuicErrorCode error_code; + QuicString error_details; + + // Populated if the CHLO STK contained a CachedNetworkParameters proto. + CachedNetworkParameters cached_network_params; + + protected: + ~Result() override; + }; + + ValidateClientHelloResultCallback(); + ValidateClientHelloResultCallback(const ValidateClientHelloResultCallback&) = + delete; + ValidateClientHelloResultCallback& operator=( + const ValidateClientHelloResultCallback&) = delete; + virtual ~ValidateClientHelloResultCallback(); + virtual void Run(QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> details) = 0; +}; + +// Callback used to accept the result of the ProcessClientHello method. +class QUIC_EXPORT_PRIVATE ProcessClientHelloResultCallback { + public: + ProcessClientHelloResultCallback(); + ProcessClientHelloResultCallback(const ProcessClientHelloResultCallback&) = + delete; + ProcessClientHelloResultCallback& operator=( + const ProcessClientHelloResultCallback&) = delete; + virtual ~ProcessClientHelloResultCallback(); + virtual void Run(QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> details) = 0; +}; + +// Callback used to receive the results of a call to +// BuildServerConfigUpdateMessage. +class BuildServerConfigUpdateMessageResultCallback { + public: + BuildServerConfigUpdateMessageResultCallback() = default; + virtual ~BuildServerConfigUpdateMessageResultCallback() {} + BuildServerConfigUpdateMessageResultCallback( + const BuildServerConfigUpdateMessageResultCallback&) = delete; + BuildServerConfigUpdateMessageResultCallback& operator=( + const BuildServerConfigUpdateMessageResultCallback&) = delete; + virtual void Run(bool ok, const CryptoHandshakeMessage& message) = 0; +}; + +// Object that is interested in built rejections (which include REJ, SREJ and +// cheap SREJ). +class RejectionObserver { + public: + RejectionObserver() = default; + virtual ~RejectionObserver() {} + RejectionObserver(const RejectionObserver&) = delete; + RejectionObserver& operator=(const RejectionObserver&) = delete; + // Called after a rejection is built. + virtual void OnRejectionBuilt(const std::vector<uint32_t>& reasons, + CryptoHandshakeMessage* out) const = 0; +}; + +// Factory for creating KeyExchange objects. +class QUIC_EXPORT_PRIVATE KeyExchangeSource { + public: + virtual ~KeyExchangeSource() = default; + + // Returns the default KeyExchangeSource. + static std::unique_ptr<KeyExchangeSource> Default(); + + // Create a new KeyExchange of the specified type using the specified + // private key. + virtual std::unique_ptr<KeyExchange> Create(QuicString /*server_config_id*/, + QuicTag type, + QuicStringPiece private_key) = 0; +}; + +// QuicCryptoServerConfig contains the crypto configuration of a QUIC server. +// Unlike a client, a QUIC server can have multiple configurations active in +// order to support clients resuming with a previous configuration. +// TODO(agl): when adding configurations at runtime is added, this object will +// need to consider locking. +class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { + public: + // ConfigOptions contains options for generating server configs. + struct QUIC_EXPORT_PRIVATE ConfigOptions { + ConfigOptions(); + ConfigOptions(const ConfigOptions& other); + ~ConfigOptions(); + + // expiry_time is the time, in UNIX seconds, when the server config will + // expire. If unset, it defaults to the current time plus six months. + QuicWallTime expiry_time; + // channel_id_enabled controls whether the server config will indicate + // support for ChannelIDs. + bool channel_id_enabled; + // token_binding_params contains the list of Token Binding params (e.g. + // P256, TB10) that the server config will include. + QuicTagVector token_binding_params; + // id contains the server config id for the resulting config. If empty, a + // random id is generated. + QuicString id; + // orbit contains the kOrbitSize bytes of the orbit value for the server + // config. If |orbit| is empty then a random orbit is generated. + QuicString orbit; + // p256 determines whether a P-256 public key will be included in the + // server config. Note that this breaks deterministic server-config + // generation since P-256 key generation doesn't use the QuicRandom given + // to DefaultConfig(). + bool p256; + }; + + // |source_address_token_secret|: secret key material used for encrypting and + // decrypting source address tokens. It can be of any length as it is fed + // into a KDF before use. In tests, use TESTING. + // |server_nonce_entropy|: an entropy source used to generate the orbit and + // key for server nonces, which are always local to a given instance of a + // server. Not owned. + // |proof_source|: provides certificate chains and signatures. This class + // takes ownership of |proof_source|. + // |ssl_ctx|: The SSL_CTX used for doing TLS handshakes. + QuicCryptoServerConfig(QuicStringPiece source_address_token_secret, + QuicRandom* server_nonce_entropy, + std::unique_ptr<ProofSource> proof_source, + std::unique_ptr<KeyExchangeSource> key_exchange_source, + bssl::UniquePtr<SSL_CTX> ssl_ctx); + QuicCryptoServerConfig(const QuicCryptoServerConfig&) = delete; + QuicCryptoServerConfig& operator=(const QuicCryptoServerConfig&) = delete; + ~QuicCryptoServerConfig(); + + // TESTING is a magic parameter for passing to the constructor in tests. + static const char TESTING[]; + + // Generates a QuicServerConfigProtobuf protobuf suitable for + // AddConfig and SetConfigs. + static std::unique_ptr<QuicServerConfigProtobuf> GenerateConfig( + QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options); + + // AddConfig adds a QuicServerConfigProtobuf to the available configurations. + // It returns the SCFG message from the config if successful. The caller + // takes ownership of the CryptoHandshakeMessage. |now| is used in + // conjunction with |protobuf->primary_time()| to determine whether the + // config should be made primary. + CryptoHandshakeMessage* AddConfig( + std::unique_ptr<QuicServerConfigProtobuf> protobuf, + QuicWallTime now); + + // AddDefaultConfig calls DefaultConfig to create a config and then calls + // AddConfig to add it. See the comment for |DefaultConfig| for details of + // the arguments. + CryptoHandshakeMessage* AddDefaultConfig(QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options); + + // SetConfigs takes a vector of config protobufs and the current time. + // Configs are assumed to be uniquely identified by their server config ID. + // Previously unknown configs are added and possibly made the primary config + // depending on their |primary_time| and the value of |now|. Configs that are + // known, but are missing from the protobufs are deleted, unless they are + // currently the primary config. SetConfigs returns false if any errors were + // encountered and no changes to the QuicCryptoServerConfig will occur. + bool SetConfigs( + const std::vector<std::unique_ptr<QuicServerConfigProtobuf>>& protobufs, + QuicWallTime now); + + // SetSourceAddressTokenKeys sets the keys to be tried, in order, when + // decrypting a source address token. Note that these keys are used *without* + // passing them through a KDF, in contradistinction to the + // |source_address_token_secret| argument to the constructor. + void SetSourceAddressTokenKeys(const std::vector<QuicString>& keys); + + // Get the server config ids for all known configs. + void GetConfigIds(std::vector<QuicString>* scids) const; + + // Checks |client_hello| for gross errors and determines whether it can be + // shown to be fresh (i.e. not a replay). The result of the validation step + // must be interpreted by calling QuicCryptoServerConfig::ProcessClientHello + // from the done_cb. + // + // ValidateClientHello may invoke the done_cb before unrolling the + // stack if it is able to assess the validity of the client_nonce + // without asynchronous operations. + // + // client_hello: the incoming client hello message. + // client_ip: the IP address of the client, which is used to generate and + // validate source-address tokens. + // server_address: the IP address and port of the server. The IP address and + // port may be used for certificate selection. + // version: protocol version used for this connection. + // clock: used to validate client nonces and ephemeral keys. + // crypto_proof: in/out parameter to which will be written the crypto proof + // used in reply to a proof demand. The pointed-to-object must + // live until the callback is invoked. + // done_cb: single-use callback that accepts an opaque + // ValidatedClientHelloMsg token that holds information about + // the client hello. The callback will always be called exactly + // once, either under the current call stack, or after the + // completion of an asynchronous operation. + void ValidateClientHello( + const CryptoHandshakeMessage& client_hello, + const QuicIpAddress& client_ip, + const QuicSocketAddress& server_address, + QuicTransportVersion version, + const QuicClock* clock, + QuicReferenceCountedPointer<QuicSignedServerConfig> crypto_proof, + std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const; + + // ProcessClientHello processes |client_hello| and decides whether to accept + // or reject the connection. If the connection is to be accepted, |done_cb| is + // invoked with the contents of the ServerHello and QUIC_NO_ERROR. Otherwise + // |done_cb| is called with a REJ or SREJ message and QUIC_NO_ERROR. + // + // validate_chlo_result: Output from the asynchronous call to + // ValidateClientHello. Contains the client hello message and + // information about it. + // reject_only: Only generate rejections, not server hello messages. + // connection_id: the ConnectionId for the connection, which is used in key + // derivation. + // server_ip: the IP address of the server. The IP address may be used for + // certificate selection. + // client_address: the IP address and port of the client. The IP address is + // used to generate and validate source-address tokens. + // version: version of the QUIC protocol in use for this connection + // supported_versions: versions of the QUIC protocol that this server + // supports. + // clock: used to validate client nonces and ephemeral keys. + // rand: an entropy source + // compressed_certs_cache: the cache that caches a set of most recently used + // certs. Owned by QuicDispatcher. + // params: the state of the handshake. This may be updated with a server + // nonce when we send a rejection. + // crypto_proof: output structure containing the crypto proof used in reply to + // a proof demand. + // total_framing_overhead: the total per-packet overhead for a stream frame + // chlo_packet_size: the size, in bytes, of the CHLO packet + // done_cb: the callback invoked on completion + void ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result, + bool reject_only, + QuicConnectionId connection_id, + const QuicSocketAddress& server_address, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + bool use_stateless_rejects, + QuicConnectionId server_designated_connection_id, + const QuicClock* clock, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> crypto_proof, + QuicByteCount total_framing_overhead, + QuicByteCount chlo_packet_size, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const; + + // BuildServerConfigUpdateMessage invokes |cb| with a SCUP message containing + // the current primary config, an up to date source-address token, and cert + // chain and proof in the case of secure QUIC. Passes true to |cb| if the + // message was generated successfully, and false otherwise. This method + // assumes ownership of |cb|. + // + // |cached_network_params| is optional, and can be nullptr. + void BuildServerConfigUpdateMessage( + QuicTransportVersion version, + QuicStringPiece chlo_hash, + const SourceAddressTokens& previous_source_address_tokens, + const QuicSocketAddress& server_address, + const QuicIpAddress& client_ip, + const QuicClock* clock, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + const QuicCryptoNegotiatedParameters& params, + const CachedNetworkParameters* cached_network_params, + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const; + + // set_replay_protection controls whether replay protection is enabled. If + // replay protection is disabled then no strike registers are needed and + // frontends can share an orbit value without a shared strike-register. + // However, an attacker can duplicate a handshake and cause a client's + // request to be processed twice. + void set_replay_protection(bool on); + + // set_chlo_multiplier specifies the multiple of the CHLO message size + // that a REJ message must stay under when the client doesn't present a + // valid source-address token. + void set_chlo_multiplier(size_t multiplier); + + // When sender is allowed to not pad client hello (not standards compliant), + // we need to disable the client hello check. + void set_validate_chlo_size(bool new_value) { + validate_chlo_size_ = new_value; + } + + // Returns whether the sender is allowed to not pad the client hello. + bool validate_chlo_size() const { return validate_chlo_size_; } + + // When QUIC is tunneled through some other mechanism, source token validation + // may be disabled. Do not disable it if you are not providing other + // protection. (|true| protects against UDP amplification attack.). + void set_validate_source_address_token(bool new_value) { + validate_source_address_token_ = new_value; + } + + // set_source_address_token_future_secs sets the number of seconds into the + // future that source-address tokens will be accepted from. Since + // source-address tokens are authenticated, this should only happen if + // another, valid server has clock-skew. + void set_source_address_token_future_secs(uint32_t future_secs); + + // set_source_address_token_lifetime_secs sets the number of seconds that a + // source-address token will be valid for. + void set_source_address_token_lifetime_secs(uint32_t lifetime_secs); + + // set_enable_serving_sct enables or disables serving signed cert timestamp + // (RFC6962) in server hello. + void set_enable_serving_sct(bool enable_serving_sct); + + // Set and take ownership of the callback to invoke on primary config changes. + void AcquirePrimaryConfigChangedCb( + std::unique_ptr<PrimaryConfigChangedCallback> cb); + + // Returns the number of configs this object owns. + int NumberOfConfigs() const; + + // Callers retain the ownership of |rejection_observer| which must outlive the + // config. + void set_rejection_observer(RejectionObserver* rejection_observer) { + rejection_observer_ = rejection_observer; + } + + ProofSource* proof_source() const; + + SSL_CTX* ssl_ctx() const; + + void set_pre_shared_key(QuicStringPiece psk) { + pre_shared_key_ = QuicString(psk); + } + + bool pad_rej() const { return pad_rej_; } + void set_pad_rej(bool new_value) { pad_rej_ = new_value; } + + bool pad_shlo() const { return pad_shlo_; } + void set_pad_shlo(bool new_value) { pad_shlo_ = new_value; } + + private: + friend class test::QuicCryptoServerConfigPeer; + friend struct QuicSignedServerConfig; + + // Config represents a server config: a collection of preferences and + // Diffie-Hellman public values. + class QUIC_EXPORT_PRIVATE Config : public QuicCryptoConfig, + public QuicReferenceCounted { + public: + Config(); + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; + + // TODO(rtenneti): since this is a class, we should probably do + // getters/setters here. + // |serialized| contains the bytes of this server config, suitable for + // sending on the wire. + QuicString serialized; + // id contains the SCID of this server config. + QuicString id; + // orbit contains the orbit value for this config: an opaque identifier + // used to identify clusters of server frontends. + unsigned char orbit[kOrbitSize]; + + // key_exchanges contains key exchange objects with the private keys + // already loaded. The values correspond, one-to-one, with the tags in + // |kexs| from the parent class. + std::vector<std::unique_ptr<KeyExchange>> key_exchanges; + + // tag_value_map contains the raw key/value pairs for the config. + QuicTagValueMap tag_value_map; + + // channel_id_enabled is true if the config in |serialized| specifies that + // ChannelIDs are supported. + bool channel_id_enabled; + + // is_primary is true if this config is the one that we'll give out to + // clients as the current one. + bool is_primary; + + // primary_time contains the timestamp when this config should become the + // primary config. A value of QuicWallTime::Zero() means that this config + // will not be promoted at a specific time. + QuicWallTime primary_time; + + // expiry_time contains the timestamp when this config expires. + QuicWallTime expiry_time; + + // Secondary sort key for use when selecting primary configs and + // there are multiple configs with the same primary time. + // Smaller numbers mean higher priority. + uint64_t priority; + + // source_address_token_boxer_ is used to protect the + // source-address tokens that are given to clients. + // Points to either source_address_token_boxer_storage or the + // default boxer provided by QuicCryptoServerConfig. + const CryptoSecretBoxer* source_address_token_boxer; + + // Holds the override source_address_token_boxer instance if the + // Config is not using the default source address token boxer + // instance provided by QuicCryptoServerConfig. + std::unique_ptr<CryptoSecretBoxer> source_address_token_boxer_storage; + + private: + ~Config() override; + }; + + typedef std::map<ServerConfigID, QuicReferenceCountedPointer<Config>> + ConfigMap; + + // Get a ref to the config with a given server config id. + QuicReferenceCountedPointer<Config> GetConfigWithScid( + QuicStringPiece requested_scid) const + SHARED_LOCKS_REQUIRED(configs_lock_); + + // ConfigPrimaryTimeLessThan returns true if a->primary_time < + // b->primary_time. + static bool ConfigPrimaryTimeLessThan( + const QuicReferenceCountedPointer<Config>& a, + const QuicReferenceCountedPointer<Config>& b); + + // SelectNewPrimaryConfig reevaluates the primary config based on the + // "primary_time" deadlines contained in each. + void SelectNewPrimaryConfig(QuicWallTime now) const + EXCLUSIVE_LOCKS_REQUIRED(configs_lock_); + + // EvaluateClientHello checks |client_hello| for gross errors and determines + // whether it can be shown to be fresh (i.e. not a replay). The results are + // written to |info|. + void EvaluateClientHello( + const QuicSocketAddress& server_address, + QuicTransportVersion version, + QuicReferenceCountedPointer<Config> requested_config, + QuicReferenceCountedPointer<Config> primary_config, + QuicReferenceCountedPointer<QuicSignedServerConfig> crypto_proof, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + client_hello_state, + std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const; + + // Callback class for bridging between EvaluateClientHello and + // EvaluateClientHelloAfterGetProof. + class EvaluateClientHelloCallback; + friend class EvaluateClientHelloCallback; + + // Continuation of EvaluateClientHello after the call to + // ProofSource::GetProof. |get_proof_failed| indicates whether GetProof + // failed. If GetProof was not run, then |get_proof_failed| will be + // set to false. + void EvaluateClientHelloAfterGetProof( + const QuicIpAddress& server_ip, + QuicTransportVersion version, + QuicReferenceCountedPointer<Config> requested_config, + QuicReferenceCountedPointer<Config> primary_config, + QuicReferenceCountedPointer<QuicSignedServerConfig> crypto_proof, + std::unique_ptr<ProofSource::Details> proof_source_details, + bool get_proof_failed, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + client_hello_state, + std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const; + + // Callback class for bridging between ProcessClientHello and + // ProcessClientHelloAfterGetProof. + class ProcessClientHelloCallback; + friend class ProcessClientHelloCallback; + + // Portion of ProcessClientHello which executes after GetProof. + void ProcessClientHelloAfterGetProof( + bool found_error, + std::unique_ptr<ProofSource::Details> proof_source_details, + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + validate_chlo_result, + bool reject_only, + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + bool use_stateless_rejects, + QuicConnectionId server_designated_connection_id, + const QuicClock* clock, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> crypto_proof, + QuicByteCount total_framing_overhead, + QuicByteCount chlo_packet_size, + const QuicReferenceCountedPointer<Config>& requested_config, + const QuicReferenceCountedPointer<Config>& primary_config, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const; + + // Callback class for bridging between ProcessClientHelloAfterGetProof and + // ProcessClientHelloAfterCalculateSharedKeys. + class ProcessClientHelloAfterGetProofCallback; + friend class ProcessClientHelloAfterGetProofCallback; + + // Portion of ProcessClientHello which executes after CalculateSharedKeys. + void ProcessClientHelloAfterCalculateSharedKeys( + bool found_error, + std::unique_ptr<ProofSource::Details> proof_source_details, + const KeyExchange::Factory& key_exchange_factory, + std::unique_ptr<CryptoHandshakeMessage> out, + QuicStringPiece public_value, + const ValidateClientHelloResultCallback::Result& validate_chlo_result, + QuicConnectionId connection_id, + const QuicSocketAddress& client_address, + ParsedQuicVersion version, + const ParsedQuicVersionVector& supported_versions, + const QuicClock* clock, + QuicRandom* rand, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config, + const QuicReferenceCountedPointer<Config>& requested_config, + const QuicReferenceCountedPointer<Config>& primary_config, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const; + + // BuildRejection sets |out| to be a REJ message in reply to |client_hello|. + void BuildRejection( + QuicTransportVersion version, + QuicWallTime now, + const Config& config, + const CryptoHandshakeMessage& client_hello, + const ClientHelloInfo& info, + const CachedNetworkParameters& cached_network_params, + bool use_stateless_rejects, + QuicConnectionId server_designated_connection_id, + QuicRandom* rand, + QuicCompressedCertsCache* compressed_certs_cache, + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params, + const QuicSignedServerConfig& crypto_proof, + QuicByteCount total_framing_overhead, + QuicByteCount chlo_packet_size, + CryptoHandshakeMessage* out) const; + + // CompressChain compresses the certificates in |chain->certs| and returns a + // compressed representation. |common_sets| contains the common certificate + // sets known locally and |client_common_set_hashes| contains the hashes of + // the common sets known to the peer. |client_cached_cert_hashes| contains + // 64-bit, FNV-1a hashes of certificates that the peer already possesses. + static QuicString CompressChain( + QuicCompressedCertsCache* compressed_certs_cache, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes, + const CommonCertSets* common_sets); + + // ParseConfigProtobuf parses the given config protobuf and returns a + // QuicReferenceCountedPointer<Config> if successful. The caller adopts the + // reference to the Config. On error, ParseConfigProtobuf returns nullptr. + QuicReferenceCountedPointer<Config> ParseConfigProtobuf( + const std::unique_ptr<QuicServerConfigProtobuf>& protobuf); + + // NewSourceAddressToken returns a fresh source address token for the given + // IP address. |cached_network_params| is optional, and can be nullptr. + QuicString NewSourceAddressToken( + const Config& config, + const SourceAddressTokens& previous_tokens, + const QuicIpAddress& ip, + QuicRandom* rand, + QuicWallTime now, + const CachedNetworkParameters* cached_network_params) const; + + // ParseSourceAddressToken parses the source address tokens contained in + // the encrypted |token|, and populates |tokens| with the parsed tokens. + // Returns HANDSHAKE_OK if |token| could be parsed, or the reason for the + // failure. + HandshakeFailureReason ParseSourceAddressToken( + const Config& config, + QuicStringPiece token, + SourceAddressTokens* tokens) const; + + // ValidateSourceAddressTokens returns HANDSHAKE_OK if the source address + // tokens in |tokens| contain a valid and timely token for the IP address + // |ip| given that the current time is |now|. Otherwise it returns the + // reason for failure. |cached_network_params| is populated if the valid + // token contains a CachedNetworkParameters proto. + HandshakeFailureReason ValidateSourceAddressTokens( + const SourceAddressTokens& tokens, + const QuicIpAddress& ip, + QuicWallTime now, + CachedNetworkParameters* cached_network_params) const; + + // ValidateSingleSourceAddressToken returns HANDSHAKE_OK if the source + // address token in |token| is a timely token for the IP address |ip| + // given that the current time is |now|. Otherwise it returns the reason + // for failure. + HandshakeFailureReason ValidateSingleSourceAddressToken( + const SourceAddressToken& token, + const QuicIpAddress& ip, + QuicWallTime now) const; + + // Returns HANDSHAKE_OK if the source address token in |token| is a timely + // token given that the current time is |now|. Otherwise it returns the + // reason for failure. + HandshakeFailureReason ValidateSourceAddressTokenTimestamp( + const SourceAddressToken& token, + QuicWallTime now) const; + + // NewServerNonce generates and encrypts a random nonce. + QuicString NewServerNonce(QuicRandom* rand, QuicWallTime now) const; + + // ValidateExpectedLeafCertificate checks the |client_hello| to see if it has + // an XLCT tag, and if so, verifies that its value matches the hash of the + // server's leaf certificate. |certs| is used to compare against the XLCT + // value. This method returns true if the XLCT tag is not present, or if the + // XLCT tag is present and valid. It returns false otherwise. + bool ValidateExpectedLeafCertificate( + const CryptoHandshakeMessage& client_hello, + const std::vector<QuicString>& certs) const; + + // Returns true if the PDMD field from the client hello demands an X509 + // certificate. + bool ClientDemandsX509Proof(const CryptoHandshakeMessage& client_hello) const; + + // Callback to receive the results of ProofSource::GetProof. Note: this + // callback has no cancellation support, since the lifetime of the ProofSource + // is controlled by this object via unique ownership. If that ownership + // stricture changes, this decision may need to be revisited. + class BuildServerConfigUpdateMessageProofSourceCallback + : public ProofSource::Callback { + public: + BuildServerConfigUpdateMessageProofSourceCallback( + const BuildServerConfigUpdateMessageProofSourceCallback&) = delete; + ~BuildServerConfigUpdateMessageProofSourceCallback() override; + void operator=(const BuildServerConfigUpdateMessageProofSourceCallback&) = + delete; + BuildServerConfigUpdateMessageProofSourceCallback( + const QuicCryptoServerConfig* config, + QuicTransportVersion version, + QuicCompressedCertsCache* compressed_certs_cache, + const CommonCertSets* common_cert_sets, + const QuicCryptoNegotiatedParameters& params, + CryptoHandshakeMessage message, + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb); + + void Run(bool ok, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicCryptoProof& proof, + std::unique_ptr<ProofSource::Details> details) override; + + private: + const QuicCryptoServerConfig* config_; + const QuicTransportVersion version_; + QuicCompressedCertsCache* compressed_certs_cache_; + const CommonCertSets* common_cert_sets_; + const QuicString client_common_set_hashes_; + const QuicString client_cached_cert_hashes_; + const bool sct_supported_by_client_; + const QuicString sni_; + CryptoHandshakeMessage message_; + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb_; + }; + + // Invoked by BuildServerConfigUpdateMessageProofSourceCallback::Run once + // the proof has been acquired. Finishes building the server config update + // message and invokes |cb|. + void FinishBuildServerConfigUpdateMessage( + QuicTransportVersion version, + QuicCompressedCertsCache* compressed_certs_cache, + const CommonCertSets* common_cert_sets, + const QuicString& client_common_set_hashes, + const QuicString& client_cached_cert_hashes, + bool sct_supported_by_client, + const QuicString& sni, + bool ok, + const QuicReferenceCountedPointer<ProofSource::Chain>& chain, + const QuicString& signature, + const QuicString& leaf_cert_sct, + std::unique_ptr<ProofSource::Details> details, + CryptoHandshakeMessage message, + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const; + + // Returns true if the next config promotion should happen now. + bool IsNextConfigReady(QuicWallTime now) const + SHARED_LOCKS_REQUIRED(configs_lock_); + + // replay_protection_ controls whether the server enforces that handshakes + // aren't replays. + bool replay_protection_; + + // The multiple of the CHLO message size that a REJ message must stay under + // when the client doesn't present a valid source-address token. This is + // used to protect QUIC from amplification attacks. + size_t chlo_multiplier_; + + // configs_ satisfies the following invariants: + // 1) configs_.empty() <-> primary_config_ == nullptr + // 2) primary_config_ != nullptr -> primary_config_->is_primary + // 3) ∀ c∈configs_, c->is_primary <-> c == primary_config_ + mutable QuicMutex configs_lock_; + // configs_ contains all active server configs. It's expected that there are + // about half-a-dozen configs active at any one time. + ConfigMap configs_ GUARDED_BY(configs_lock_); + // primary_config_ points to a Config (which is also in |configs_|) which is + // the primary config - i.e. the one that we'll give out to new clients. + mutable QuicReferenceCountedPointer<Config> primary_config_ + GUARDED_BY(configs_lock_); + // next_config_promotion_time_ contains the nearest, future time when an + // active config will be promoted to primary. + mutable QuicWallTime next_config_promotion_time_ GUARDED_BY(configs_lock_); + // Callback to invoke when the primary config changes. + std::unique_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_ + GUARDED_BY(configs_lock_); + + // Used to protect the source-address tokens that are given to clients. + CryptoSecretBoxer source_address_token_boxer_; + + // server_nonce_boxer_ is used to encrypt and validate suggested server + // nonces. + CryptoSecretBoxer server_nonce_boxer_; + + // server_nonce_orbit_ contains the random, per-server orbit values that this + // server will use to generate server nonces (the moral equivalent of a SYN + // cookies). + uint8_t server_nonce_orbit_[8]; + + // proof_source_ contains an object that can provide certificate chains and + // signatures. + std::unique_ptr<ProofSource> proof_source_; + + // key_exchange_source_ contains an object that can provide key exchange + // objects. + std::unique_ptr<KeyExchangeSource> key_exchange_source_; + + // ssl_ctx_ contains the server configuration for doing TLS handshakes. + bssl::UniquePtr<SSL_CTX> ssl_ctx_; + + // These fields store configuration values. See the comments for their + // respective setter functions. + uint32_t source_address_token_future_secs_; + uint32_t source_address_token_lifetime_secs_; + + // Enable serving SCT or not. + bool enable_serving_sct_; + + // Does not own this observer. + RejectionObserver* rejection_observer_; + + // If non-empty, the server will operate in the pre-shared key mode by + // incorporating |pre_shared_key_| into the key schedule. + QuicString pre_shared_key_; + + // Whether REJ message should be padded to max packet size. + bool pad_rej_; + + // Whether SHLO message should be padded to max packet size. + bool pad_shlo_; + + // If client is allowed to send a small client hello (by disabling padding), + // server MUST not check for the client hello size. + // DO NOT disable this unless you have some other way of validating client. + // (e.g. in realtime scenarios, where quic is tunneled through ICE, ICE will + // do its own peer validation using STUN pings with ufrag/upass). + bool validate_chlo_size_; + + // When source address is validated by some other means (e.g. when using ICE), + // source address token validation may be disabled. + bool validate_source_address_token_; +}; + +struct QUIC_EXPORT_PRIVATE QuicSignedServerConfig + : public QuicReferenceCounted { + QuicSignedServerConfig(); + + QuicCryptoProof proof; + QuicReferenceCountedPointer<ProofSource::Chain> chain; + // The server config that is used for this proof (and the rest of the + // request). + QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> config; + QuicString primary_scid; + + protected: + ~QuicSignedServerConfig() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
diff --git a/quic/core/crypto/quic_crypto_server_config_test.cc b/quic/core/crypto/quic_crypto_server_config_test.cc new file mode 100644 index 0000000..101be1d --- /dev/null +++ b/quic/core/crypto/quic_crypto_server_config_test.cc
@@ -0,0 +1,496 @@ +// Copyright 2013 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/crypto/quic_crypto_server_config.h" + +#include <stdarg.h> + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h" +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h" + +namespace quic { +namespace test { + +class QuicCryptoServerConfigTest : public QuicTest {}; + +TEST_F(QuicCryptoServerConfigTest, ServerConfig) { + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + MockClock clock; + + std::unique_ptr<CryptoHandshakeMessage> message(server.AddDefaultConfig( + rand, &clock, QuicCryptoServerConfig::ConfigOptions())); + + // The default configuration should have AES-GCM and at least one ChaCha20 + // cipher. + QuicTagVector aead; + ASSERT_EQ(QUIC_NO_ERROR, message->GetTaglist(kAEAD, &aead)); + EXPECT_THAT(aead, ::testing::Contains(kAESG)); + EXPECT_LE(1u, aead.size()); +} + +TEST_F(QuicCryptoServerConfigTest, CompressCerts) { + QuicCompressedCertsCache compressed_certs_cache( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize); + + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + QuicCryptoServerConfigPeer peer(&server); + + std::vector<QuicString> certs = {"testcert"}; + QuicReferenceCountedPointer<ProofSource::Chain> chain( + new ProofSource::Chain(certs)); + + QuicString compressed = QuicCryptoServerConfigPeer::CompressChain( + &compressed_certs_cache, chain, "", "", nullptr); + + EXPECT_EQ(compressed_certs_cache.Size(), 1u); +} + +TEST_F(QuicCryptoServerConfigTest, CompressSameCertsTwice) { + QuicCompressedCertsCache compressed_certs_cache( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize); + + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + QuicCryptoServerConfigPeer peer(&server); + + // Compress the certs for the first time. + std::vector<QuicString> certs = {"testcert"}; + QuicReferenceCountedPointer<ProofSource::Chain> chain( + new ProofSource::Chain(certs)); + QuicString common_certs = ""; + QuicString cached_certs = ""; + + QuicString compressed = QuicCryptoServerConfigPeer::CompressChain( + &compressed_certs_cache, chain, common_certs, cached_certs, nullptr); + EXPECT_EQ(compressed_certs_cache.Size(), 1u); + + // Compress the same certs, should use cache if available. + QuicString compressed2 = QuicCryptoServerConfigPeer::CompressChain( + &compressed_certs_cache, chain, common_certs, cached_certs, nullptr); + EXPECT_EQ(compressed, compressed2); + EXPECT_EQ(compressed_certs_cache.Size(), 1u); +} + +TEST_F(QuicCryptoServerConfigTest, CompressDifferentCerts) { + // This test compresses a set of similar but not identical certs. Cache if + // used should return cache miss and add all the compressed certs. + QuicCompressedCertsCache compressed_certs_cache( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize); + + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + QuicCryptoServerConfigPeer peer(&server); + + std::vector<QuicString> certs = {"testcert"}; + QuicReferenceCountedPointer<ProofSource::Chain> chain( + new ProofSource::Chain(certs)); + QuicString common_certs = ""; + QuicString cached_certs = ""; + + QuicString compressed = QuicCryptoServerConfigPeer::CompressChain( + &compressed_certs_cache, chain, common_certs, cached_certs, nullptr); + EXPECT_EQ(compressed_certs_cache.Size(), 1u); + + // Compress a similar certs which only differs in the chain. + QuicReferenceCountedPointer<ProofSource::Chain> chain2( + new ProofSource::Chain(certs)); + + QuicString compressed2 = QuicCryptoServerConfigPeer::CompressChain( + &compressed_certs_cache, chain2, common_certs, cached_certs, nullptr); + EXPECT_EQ(compressed_certs_cache.Size(), 2u); + + // Compress a similar certs which only differs in common certs field. + static const uint64_t set_hash = 42; + std::unique_ptr<CommonCertSets> common_sets( + crypto_test_utils::MockCommonCertSets(certs[0], set_hash, 1)); + QuicStringPiece different_common_certs( + reinterpret_cast<const char*>(&set_hash), sizeof(set_hash)); + QuicString compressed3 = QuicCryptoServerConfigPeer::CompressChain( + &compressed_certs_cache, chain, QuicString(different_common_certs), + cached_certs, common_sets.get()); + EXPECT_EQ(compressed_certs_cache.Size(), 3u); +} + +class SourceAddressTokenTest : public QuicTest { + public: + SourceAddressTokenTest() + : ip4_(QuicIpAddress::Loopback4()), + ip4_dual_(ip4_.DualStacked()), + ip6_(QuicIpAddress::Loopback6()), + original_time_(QuicWallTime::Zero()), + rand_(QuicRandom::GetInstance()), + server_(QuicCryptoServerConfig::TESTING, + rand_, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + peer_(&server_) { + // Advance the clock to some non-zero time. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); + original_time_ = clock_.WallNow(); + + primary_config_.reset(server_.AddDefaultConfig( + rand_, &clock_, QuicCryptoServerConfig::ConfigOptions())); + } + + QuicString NewSourceAddressToken(QuicString config_id, + const QuicIpAddress& ip) { + return NewSourceAddressToken(config_id, ip, nullptr); + } + + QuicString NewSourceAddressToken(QuicString config_id, + const QuicIpAddress& ip, + const SourceAddressTokens& previous_tokens) { + return peer_.NewSourceAddressToken(config_id, previous_tokens, ip, rand_, + clock_.WallNow(), nullptr); + } + + QuicString NewSourceAddressToken( + QuicString config_id, + const QuicIpAddress& ip, + CachedNetworkParameters* cached_network_params) { + SourceAddressTokens previous_tokens; + return peer_.NewSourceAddressToken(config_id, previous_tokens, ip, rand_, + clock_.WallNow(), cached_network_params); + } + + HandshakeFailureReason ValidateSourceAddressTokens(QuicString config_id, + QuicStringPiece srct, + const QuicIpAddress& ip) { + return ValidateSourceAddressTokens(config_id, srct, ip, nullptr); + } + + HandshakeFailureReason ValidateSourceAddressTokens( + QuicString config_id, + QuicStringPiece srct, + const QuicIpAddress& ip, + CachedNetworkParameters* cached_network_params) { + return peer_.ValidateSourceAddressTokens( + config_id, srct, ip, clock_.WallNow(), cached_network_params); + } + + const QuicString kPrimary = "<primary>"; + const QuicString kOverride = "Config with custom source address token key"; + + QuicIpAddress ip4_; + QuicIpAddress ip4_dual_; + QuicIpAddress ip6_; + + MockClock clock_; + QuicWallTime original_time_; + QuicRandom* rand_ = QuicRandom::GetInstance(); + QuicCryptoServerConfig server_; + QuicCryptoServerConfigPeer peer_; + // Stores the primary config. + std::unique_ptr<CryptoHandshakeMessage> primary_config_; + std::unique_ptr<QuicServerConfigProtobuf> override_config_protobuf_; +}; + +// Test basic behavior of source address tokens including being specific +// to a single IP address and server config. +TEST_F(SourceAddressTokenTest, SourceAddressToken) { + // Primary config generates configs that validate successfully. + const QuicString token4 = NewSourceAddressToken(kPrimary, ip4_); + const QuicString token4d = NewSourceAddressToken(kPrimary, ip4_dual_); + const QuicString token6 = NewSourceAddressToken(kPrimary, ip6_); + EXPECT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token4, ip4_)); + ASSERT_EQ(HANDSHAKE_OK, + ValidateSourceAddressTokens(kPrimary, token4, ip4_dual_)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + ValidateSourceAddressTokens(kPrimary, token4, ip6_)); + ASSERT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token4d, ip4_)); + ASSERT_EQ(HANDSHAKE_OK, + ValidateSourceAddressTokens(kPrimary, token4d, ip4_dual_)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + ValidateSourceAddressTokens(kPrimary, token4d, ip6_)); + ASSERT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token6, ip6_)); +} + +TEST_F(SourceAddressTokenTest, SourceAddressTokenExpiration) { + const QuicString token = NewSourceAddressToken(kPrimary, ip4_); + + // Validation fails if the token is from the future. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(-3600 * 2)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE, + ValidateSourceAddressTokens(kPrimary, token, ip4_)); + + // Validation fails after tokens expire. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(86400 * 7)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE, + ValidateSourceAddressTokens(kPrimary, token, ip4_)); +} + +TEST_F(SourceAddressTokenTest, SourceAddressTokenWithNetworkParams) { + // Make sure that if the source address token contains CachedNetworkParameters + // that this gets written to ValidateSourceAddressToken output argument. + CachedNetworkParameters cached_network_params_input; + cached_network_params_input.set_bandwidth_estimate_bytes_per_second(1234); + const QuicString token4_with_cached_network_params = + NewSourceAddressToken(kPrimary, ip4_, &cached_network_params_input); + + CachedNetworkParameters cached_network_params_output; + EXPECT_NE(cached_network_params_output.SerializeAsString(), + cached_network_params_input.SerializeAsString()); + ValidateSourceAddressTokens(kPrimary, token4_with_cached_network_params, ip4_, + &cached_network_params_output); + EXPECT_EQ(cached_network_params_output.SerializeAsString(), + cached_network_params_input.SerializeAsString()); +} + +// Test the ability for a source address token to be valid for multiple +// addresses. +TEST_F(SourceAddressTokenTest, SourceAddressTokenMultipleAddresses) { + QuicWallTime now = clock_.WallNow(); + + // Now create a token which is usable for both addresses. + SourceAddressToken previous_token; + previous_token.set_ip(ip6_.DualStacked().ToPackedString()); + previous_token.set_timestamp(now.ToUNIXSeconds()); + SourceAddressTokens previous_tokens; + (*previous_tokens.add_tokens()) = previous_token; + const QuicString token4or6 = + NewSourceAddressToken(kPrimary, ip4_, previous_tokens); + + EXPECT_EQ(HANDSHAKE_OK, + ValidateSourceAddressTokens(kPrimary, token4or6, ip4_)); + ASSERT_EQ(HANDSHAKE_OK, + ValidateSourceAddressTokens(kPrimary, token4or6, ip6_)); +} + +class CryptoServerConfigsTest : public QuicTest { + public: + CryptoServerConfigsTest() + : rand_(QuicRandom::GetInstance()), + config_(QuicCryptoServerConfig::TESTING, + rand_, + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + test_peer_(&config_) {} + + void SetUp() override { + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000)); + } + + // SetConfigs constructs suitable config protobufs and calls SetConfigs on + // |config_|. + // Each struct in the input vector contains 3 elements. + // The first is the server config ID of a Config. The second is + // the |primary_time| of that Config, given in epoch seconds. (Although note + // that, in these tests, time is set to 1000 seconds since the epoch.). + // The third is the priority. + // + // For example: + // SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority>()); // calls + // |config_.SetConfigs| with no protobufs. + // + // // Calls |config_.SetConfigs| with two protobufs: one for a Config with + // // a |primary_time| of 900 and priority 1, and another with + // // a |primary_time| of 1000 and priority 2. + + // CheckConfigs( + // {{"id1", 900, 1}, + // {"id2", 1000, 2}}); + // + // If the server config id starts with "INVALID" then the generated protobuf + // will be invalid. + struct ServerConfigIDWithTimeAndPriority { + ServerConfigID server_config_id; + int primary_time; + int priority; + }; + void SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority> configs) { + const char kOrbit[] = "12345678"; + + bool has_invalid = false; + + std::vector<std::unique_ptr<QuicServerConfigProtobuf>> protobufs; + for (const auto& config : configs) { + const ServerConfigID& server_config_id = config.server_config_id; + const int primary_time = config.primary_time; + const int priority = config.priority; + + QuicCryptoServerConfig::ConfigOptions options; + options.id = server_config_id; + options.orbit = kOrbit; + std::unique_ptr<QuicServerConfigProtobuf> protobuf = + QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options); + protobuf->set_primary_time(primary_time); + protobuf->set_priority(priority); + if (QuicString(server_config_id).find("INVALID") == 0) { + protobuf->clear_key(); + has_invalid = true; + } + protobufs.push_back(std::move(protobuf)); + } + + ASSERT_EQ(!has_invalid && !configs.empty(), + config_.SetConfigs(protobufs, clock_.WallNow())); + } + + protected: + QuicRandom* const rand_; + MockClock clock_; + QuicCryptoServerConfig config_; + QuicCryptoServerConfigPeer test_peer_; +}; + +TEST_F(CryptoServerConfigsTest, NoConfigs) { + test_peer_.CheckConfigs(std::vector<std::pair<QuicString, bool>>()); +} + +TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) { + // Make sure that "b" is primary even though "a" comes first. + SetConfigs({{"a", 1100, 1}, {"b", 900, 1}}); + test_peer_.CheckConfigs({{"a", false}, {"b", true}}); +} + +TEST_F(CryptoServerConfigsTest, MakePrimarySecond) { + // Make sure that a remains primary after b is added. + SetConfigs({{"a", 900, 1}, {"b", 1100, 1}}); + test_peer_.CheckConfigs({{"a", true}, {"b", false}}); +} + +TEST_F(CryptoServerConfigsTest, Delete) { + // Ensure that configs get deleted when removed. + SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}}); + test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}}); + SetConfigs({{"b", 900, 1}, {"c", 1100, 1}}); + test_peer_.CheckConfigs({{"b", true}, {"c", false}}); +} + +TEST_F(CryptoServerConfigsTest, DeletePrimary) { + // Ensure that deleting the primary config works. + SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}}); + test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}}); + SetConfigs({{"a", 800, 1}, {"c", 1100, 1}}); + test_peer_.CheckConfigs({{"a", true}, {"c", false}}); +} + +TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) { + // Ensure that configs get deleted when removed. + SetConfigs({{"a", 800, 1}, {"b", 900, 1}}); + test_peer_.CheckConfigs({{"a", false}, {"b", true}}); + SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority>()); + // Config change is rejected, still using old configs. + test_peer_.CheckConfigs({{"a", false}, {"b", true}}); +} + +TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) { + // Check that updates to primary time get picked up. + SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}}); + test_peer_.SelectNewPrimaryConfig(500); + test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}}); + SetConfigs({{"a", 1200, 1}, {"b", 800, 1}, {"c", 400, 1}}); + test_peer_.SelectNewPrimaryConfig(500); + test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}}); +} + +TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) { + // Check that the most recent config is selected. + SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}}); + test_peer_.SelectNewPrimaryConfig(1500); + test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}}); +} + +TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) { + // Check that the first config is selected. + SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}}); + test_peer_.SelectNewPrimaryConfig(100); + test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}}); +} + +TEST_F(CryptoServerConfigsTest, SortByPriority) { + // Check that priority is used to decide on a primary config when + // configs have the same primary time. + SetConfigs({{"a", 900, 1}, {"b", 900, 2}, {"c", 900, 3}}); + test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}}); + test_peer_.SelectNewPrimaryConfig(800); + test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}}); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}}); + + // Change priorities and expect sort order to change. + SetConfigs({{"a", 900, 2}, {"b", 900, 1}, {"c", 900, 0}}); + test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}}); + test_peer_.SelectNewPrimaryConfig(800); + test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}}); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}}); +} + +TEST_F(CryptoServerConfigsTest, AdvancePrimary) { + // Check that a new primary config is enabled at the right time. + SetConfigs({{"a", 900, 1}, {"b", 1100, 1}}); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs({{"a", true}, {"b", false}}); + test_peer_.SelectNewPrimaryConfig(1101); + test_peer_.CheckConfigs({{"a", false}, {"b", true}}); +} + +class ValidateCallback : public ValidateClientHelloResultCallback { + public: + void Run(QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> /* details */) override {} +}; + +TEST_F(CryptoServerConfigsTest, AdvancePrimaryViaValidate) { + SetQuicReloadableFlag(quic_fix_config_rotation, true); + // Check that a new primary config is enabled at the right time. + SetConfigs({{"a", 900, 1}, {"b", 1100, 1}}); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs({{"a", true}, {"b", false}}); + CryptoHandshakeMessage client_hello; + QuicIpAddress client_ip; + QuicSocketAddress server_address; + QuicTransportVersion version = QUIC_VERSION_99; + MockClock clock; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config( + new QuicSignedServerConfig); + std::unique_ptr<ValidateClientHelloResultCallback> done_cb( + new ValidateCallback); + clock.AdvanceTime(QuicTime::Delta::FromSeconds(1100)); + config_.ValidateClientHello(client_hello, client_ip, server_address, version, + &clock, signed_config, std::move(done_cb)); + test_peer_.CheckConfigs({{"a", false}, {"b", true}}); +} + +TEST_F(CryptoServerConfigsTest, InvalidConfigs) { + // Ensure that invalid configs don't change anything. + SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}}); + test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}}); + SetConfigs({{"a", 800, 1}, {"c", 1100, 1}, {"INVALID1", 1000, 1}}); + test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}}); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/quic_decrypter.cc b/quic/core/crypto/quic_decrypter.cc new file mode 100644 index 0000000..d2aa28d --- /dev/null +++ b/quic/core/crypto/quic_decrypter.cc
@@ -0,0 +1,68 @@ +// Copyright (c) 2012 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/crypto/quic_decrypter.h" + +#include "third_party/boringssl/src/include/openssl/tls1.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// static +std::unique_ptr<QuicDecrypter> QuicDecrypter::Create(QuicTag algorithm) { + switch (algorithm) { + case kAESG: + return QuicMakeUnique<Aes128Gcm12Decrypter>(); + case kCC20: + return QuicMakeUnique<ChaCha20Poly1305Decrypter>(); + default: + QUIC_LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return nullptr; + } +} + +// static +std::unique_ptr<QuicDecrypter> QuicDecrypter::CreateFromCipherSuite( + uint32_t cipher_suite) { + switch (cipher_suite) { + case TLS1_CK_AES_128_GCM_SHA256: + return QuicMakeUnique<Aes128GcmDecrypter>(); + case TLS1_CK_AES_256_GCM_SHA384: + return QuicMakeUnique<Aes256GcmDecrypter>(); + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return QuicMakeUnique<ChaCha20Poly1305TlsDecrypter>(); + default: + QUIC_BUG << "TLS cipher suite is unknown to QUIC"; + return nullptr; + } +} + +// static +void QuicDecrypter::DiversifyPreliminaryKey(QuicStringPiece preliminary_key, + QuicStringPiece nonce_prefix, + const DiversificationNonce& nonce, + size_t key_size, + size_t nonce_prefix_size, + QuicString* out_key, + QuicString* out_nonce_prefix) { + QuicHKDF hkdf((QuicString(preliminary_key)) + (QuicString(nonce_prefix)), + QuicStringPiece(nonce.data(), nonce.size()), + "QUIC key diversification", 0, key_size, 0, nonce_prefix_size, + 0); + *out_key = QuicString(hkdf.server_write_key()); + *out_nonce_prefix = QuicString(hkdf.server_write_iv()); +} + +} // namespace quic
diff --git a/quic/core/crypto/quic_decrypter.h b/quic/core/crypto/quic_decrypter.h new file mode 100644 index 0000000..c7c2ccc --- /dev/null +++ b/quic/core/crypto/quic_decrypter.h
@@ -0,0 +1,82 @@ +// Copyright (c) 2012 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_CRYPTO_QUIC_DECRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_ + +#include <cstddef> +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypter.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicDecrypter : public QuicCrypter { + public: + virtual ~QuicDecrypter() {} + + static std::unique_ptr<QuicDecrypter> Create(QuicTag algorithm); + + // Creates an IETF QuicDecrypter based on |cipher_suite| which must be an id + // returned by SSL_CIPHER_get_id. The caller is responsible for taking + // ownership of the new QuicDecrypter. + static std::unique_ptr<QuicDecrypter> CreateFromCipherSuite( + uint32_t cipher_suite); + + // Sets the encryption key. Returns true on success, false on failure. + // |DecryptPacket| may not be called until |SetDiversificationNonce| is + // called and the preliminary keying material will be combined with that + // nonce in order to create the actual key and nonce-prefix. + // + // If this function is called, neither |SetKey| nor |SetNoncePrefix| may be + // called. + virtual bool SetPreliminaryKey(QuicStringPiece key) = 0; + + // SetDiversificationNonce uses |nonce| to derive final keys based on the + // input keying material given by calling |SetPreliminaryKey|. + // + // Calling this function is a no-op if |SetPreliminaryKey| hasn't been + // called. + virtual bool SetDiversificationNonce(const DiversificationNonce& nonce) = 0; + + // Populates |output| with the decrypted |ciphertext| and populates + // |output_length| with the length. Returns 0 if there is an error. + // |output| size is specified by |max_output_length| and must be + // at least as large as the ciphertext. |packet_number| is + // appended to the |nonce_prefix| value provided in SetNoncePrefix() + // to form the nonce. + // TODO(wtc): add a way for DecryptPacket to report decryption failure due + // to non-authentic inputs, as opposed to other reasons for failure. + virtual bool DecryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) = 0; + + // The ID of the cipher. Return 0x03000000 ORed with the 'cryptographic suite + // selector'. + virtual uint32_t cipher_id() const = 0; + + // For use by unit tests only. + virtual QuicStringPiece GetKey() const = 0; + virtual QuicStringPiece GetNoncePrefix() const = 0; + + static void DiversifyPreliminaryKey(QuicStringPiece preliminary_key, + QuicStringPiece nonce_prefix, + const DiversificationNonce& nonce, + size_t key_size, + size_t nonce_prefix_size, + QuicString* out_key, + QuicString* out_nonce_prefix); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_
diff --git a/quic/core/crypto/quic_encrypter.cc b/quic/core/crypto/quic_encrypter.cc new file mode 100644 index 0000000..f264bfd --- /dev/null +++ b/quic/core/crypto/quic_encrypter.cc
@@ -0,0 +1,50 @@ +// Copyright (c) 2012 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/crypto/quic_encrypter.h" + +#include "third_party/boringssl/src/include/openssl/tls1.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +// static +std::unique_ptr<QuicEncrypter> QuicEncrypter::Create(QuicTag algorithm) { + switch (algorithm) { + case kAESG: + return QuicMakeUnique<Aes128Gcm12Encrypter>(); + case kCC20: + return QuicMakeUnique<ChaCha20Poly1305Encrypter>(); + default: + QUIC_LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return nullptr; + } +} + +// static +std::unique_ptr<QuicEncrypter> QuicEncrypter::CreateFromCipherSuite( + uint32_t cipher_suite) { + switch (cipher_suite) { + case TLS1_CK_AES_128_GCM_SHA256: + return QuicMakeUnique<Aes128GcmEncrypter>(); + case TLS1_CK_AES_256_GCM_SHA384: + return QuicMakeUnique<Aes256GcmEncrypter>(); + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return QuicMakeUnique<ChaCha20Poly1305TlsEncrypter>(); + default: + QUIC_BUG << "TLS cipher suite is unknown to QUIC"; + return nullptr; + } +} + +} // namespace quic
diff --git a/quic/core/crypto/quic_encrypter.h b/quic/core/crypto/quic_encrypter.h new file mode 100644 index 0000000..cb53e33 --- /dev/null +++ b/quic/core/crypto/quic_encrypter.h
@@ -0,0 +1,67 @@ +// Copyright (c) 2012 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_CRYPTO_QUIC_ENCRYPTER_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_ + +#include <cstddef> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypter.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicEncrypter : public QuicCrypter { + public: + virtual ~QuicEncrypter() {} + + static std::unique_ptr<QuicEncrypter> Create(QuicTag algorithm); + + // Creates an IETF QuicEncrypter based on |cipher_suite| which must be an id + // returned by SSL_CIPHER_get_id. The caller is responsible for taking + // ownership of the new QuicEncrypter. + static std::unique_ptr<QuicEncrypter> CreateFromCipherSuite( + uint32_t cipher_suite); + + // Writes encrypted |plaintext| and a MAC over |plaintext| and + // |associated_data| into output. Sets |output_length| to the number of + // bytes written. Returns true on success or false if there was an error. + // |packet_number| is appended to the |nonce_prefix| value provided in + // SetNoncePrefix() to form the nonce. |output| must not overlap with + // |associated_data|. If |output| overlaps with |plaintext| then + // |plaintext| must be <= |output|. + virtual bool EncryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + char* output, + size_t* output_length, + size_t max_output_length) = 0; + + // GetKeySize() and GetNoncePrefixSize() tell the HKDF class how many bytes + // of key material needs to be derived from the master secret. + // NOTE: the sizes returned by GetKeySize() and GetNoncePrefixSize() are + // also correct for the QuicDecrypter of the same algorithm. + + // Returns the size in bytes of the fixed initial part of the nonce. + virtual size_t GetNoncePrefixSize() const = 0; + + // Returns the maximum length of plaintext that can be encrypted + // to ciphertext no larger than |ciphertext_size|. + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const = 0; + + // Returns the length of the ciphertext that would be generated by encrypting + // to plaintext of size |plaintext_size|. + virtual size_t GetCiphertextSize(size_t plaintext_size) const = 0; + + // For use by unit tests only. + virtual QuicStringPiece GetKey() const = 0; + virtual QuicStringPiece GetNoncePrefix() const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_
diff --git a/quic/core/crypto/quic_hkdf.cc b/quic/core/crypto/quic_hkdf.cc new file mode 100644 index 0000000..3754cab --- /dev/null +++ b/quic/core/crypto/quic_hkdf.cc
@@ -0,0 +1,93 @@ +// Copyright (c) 2018 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/crypto/quic_hkdf.h" + +#include <memory> + +#include "third_party/boringssl/src/include/openssl/digest.h" +#include "third_party/boringssl/src/include/openssl/hkdf.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +const size_t kSHA256HashLength = 32; +const size_t kMaxKeyMaterialSize = kSHA256HashLength * 256; + +QuicHKDF::QuicHKDF(QuicStringPiece secret, + QuicStringPiece salt, + QuicStringPiece info, + size_t key_bytes_to_generate, + size_t iv_bytes_to_generate, + size_t subkey_secret_bytes_to_generate) + : QuicHKDF(secret, + salt, + info, + key_bytes_to_generate, + key_bytes_to_generate, + iv_bytes_to_generate, + iv_bytes_to_generate, + subkey_secret_bytes_to_generate) {} + +QuicHKDF::QuicHKDF(QuicStringPiece secret, + QuicStringPiece salt, + QuicStringPiece info, + size_t client_key_bytes_to_generate, + size_t server_key_bytes_to_generate, + size_t client_iv_bytes_to_generate, + size_t server_iv_bytes_to_generate, + size_t subkey_secret_bytes_to_generate) { + const size_t material_length = + client_key_bytes_to_generate + client_iv_bytes_to_generate + + server_key_bytes_to_generate + server_iv_bytes_to_generate + + subkey_secret_bytes_to_generate; + DCHECK_LT(material_length, kMaxKeyMaterialSize); + + output_.resize(material_length); + // On Windows, when the size of output_ is zero, dereference of 0'th element + // results in a crash. C++11 solves this problem by adding a data() getter + // method to std::vector. + if (output_.empty()) { + return; + } + + ::HKDF(&output_[0], output_.size(), ::EVP_sha256(), + reinterpret_cast<const uint8_t*>(secret.data()), secret.size(), + reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), + reinterpret_cast<const uint8_t*>(info.data()), info.size()); + + size_t j = 0; + if (client_key_bytes_to_generate) { + client_write_key_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]), + client_key_bytes_to_generate); + j += client_key_bytes_to_generate; + } + + if (server_key_bytes_to_generate) { + server_write_key_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]), + server_key_bytes_to_generate); + j += server_key_bytes_to_generate; + } + + if (client_iv_bytes_to_generate) { + client_write_iv_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]), + client_iv_bytes_to_generate); + j += client_iv_bytes_to_generate; + } + + if (server_iv_bytes_to_generate) { + server_write_iv_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]), + server_iv_bytes_to_generate); + j += server_iv_bytes_to_generate; + } + + if (subkey_secret_bytes_to_generate) { + subkey_secret_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]), + subkey_secret_bytes_to_generate); + } +} + +QuicHKDF::~QuicHKDF() {} + +} // namespace quic
diff --git a/quic/core/crypto/quic_hkdf.h b/quic/core/crypto/quic_hkdf.h new file mode 100644 index 0000000..fb80f7b --- /dev/null +++ b/quic/core/crypto/quic_hkdf.h
@@ -0,0 +1,70 @@ +// Copyright 2018 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_CRYPTO_QUIC_HKDF_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_HKDF_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// QuicHKDF implements the key derivation function specified in RFC 5869 +// (using SHA-256) and outputs key material, as needed by QUIC. +// See https://tools.ietf.org/html/rfc5869 for details. +class QUIC_EXPORT QuicHKDF { + public: + // |secret|: the input shared secret (or, from RFC 5869, the IKM). + // |salt|: an (optional) public salt / non-secret random value. While + // optional, callers are strongly recommended to provide a salt. There is no + // added security value in making this larger than the SHA-256 block size of + // 64 bytes. + // |info|: an (optional) label to distinguish different uses of HKDF. It is + // optional context and application specific information (can be a zero-length + // string). + // |key_bytes_to_generate|: the number of bytes of key material to generate + // for both client and server. + // |iv_bytes_to_generate|: the number of bytes of IV to generate for both + // client and server. + // |subkey_secret_bytes_to_generate|: the number of bytes of subkey secret to + // generate, shared between client and server. + QuicHKDF(QuicStringPiece secret, + QuicStringPiece salt, + QuicStringPiece info, + size_t key_bytes_to_generate, + size_t iv_bytes_to_generate, + size_t subkey_secret_bytes_to_generate); + + // An alternative constructor that allows the client and server key/IV + // lengths to be different. + QuicHKDF(QuicStringPiece secret, + QuicStringPiece salt, + QuicStringPiece info, + size_t client_key_bytes_to_generate, + size_t server_key_bytes_to_generate, + size_t client_iv_bytes_to_generate, + size_t server_iv_bytes_to_generate, + size_t subkey_secret_bytes_to_generate); + + ~QuicHKDF(); + + QuicStringPiece client_write_key() const { return client_write_key_; } + QuicStringPiece client_write_iv() const { return client_write_iv_; } + QuicStringPiece server_write_key() const { return server_write_key_; } + QuicStringPiece server_write_iv() const { return server_write_iv_; } + QuicStringPiece subkey_secret() const { return subkey_secret_; } + + private: + std::vector<uint8_t> output_; + + QuicStringPiece client_write_key_; + QuicStringPiece server_write_key_; + QuicStringPiece client_write_iv_; + QuicStringPiece server_write_iv_; + QuicStringPiece subkey_secret_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_HKDF_H_
diff --git a/quic/core/crypto/quic_hkdf_test.cc b/quic/core/crypto/quic_hkdf_test.cc new file mode 100644 index 0000000..f8c53c6 --- /dev/null +++ b/quic/core/crypto/quic_hkdf_test.cc
@@ -0,0 +1,90 @@ +// Copyright 2018 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/crypto/quic_hkdf.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { +namespace test { +namespace { + +struct HKDFInput { + const char* key_hex; + const char* salt_hex; + const char* info_hex; + const char* output_hex; +}; + +// These test cases are taken from +// https://tools.ietf.org/html/rfc5869#appendix-A. +static const HKDFInput kHKDFInputs[] = { + { + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf340072" + "08d5" + "b887185865", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122" + "2324" + "25262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344454647" + "4849" + "4a4b4c4d4e4f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182" + "8384" + "85868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7" + "a8a9" + "aaabacadaeaf", + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2" + "d3d4" + "d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7" + "f8f9" + "fafbfcfdfeff", + "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a" + "99ca" + "c7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87" + "c14c" + "01d5c1f3434f1d87", + }, + { + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "", + "", + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d2013" + "95fa" + "a4b61a96c8", + }, +}; + +class QuicHKDFTest : public QuicTest {}; + +TEST_F(QuicHKDFTest, HKDF) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(kHKDFInputs); i++) { + const HKDFInput& test(kHKDFInputs[i]); + SCOPED_TRACE(i); + + const QuicString key = QuicTextUtils::HexDecode(test.key_hex); + const QuicString salt = QuicTextUtils::HexDecode(test.salt_hex); + const QuicString info = QuicTextUtils::HexDecode(test.info_hex); + const QuicString expected = QuicTextUtils::HexDecode(test.output_hex); + + // We set the key_length to the length of the expected output and then take + // the result from the first key, which is the client write key. + QuicHKDF hkdf(key, salt, info, expected.size(), 0, 0); + + ASSERT_EQ(expected.size(), hkdf.client_write_key().size()); + EXPECT_EQ(0, memcmp(expected.data(), hkdf.client_write_key().data(), + expected.size())); + } +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/quic_random.cc b/quic/core/crypto/quic_random.cc new file mode 100644 index 0000000..750ddab --- /dev/null +++ b/quic/core/crypto/quic_random.cc
@@ -0,0 +1,45 @@ +// Copyright (c) 2012 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/crypto/quic_random.h" + +#include "base/macros.h" +#include "third_party/boringssl/src/include/openssl/rand.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" + +namespace quic { + +namespace { + +class DefaultRandom : public QuicRandom { + public: + DefaultRandom() {} + DefaultRandom(const DefaultRandom&) = delete; + DefaultRandom& operator=(const DefaultRandom&) = delete; + ~DefaultRandom() override {} + + // QuicRandom implementation + void RandBytes(void* data, size_t len) override; + uint64_t RandUint64() override; +}; + +void DefaultRandom::RandBytes(void* data, size_t len) { + RAND_bytes(reinterpret_cast<uint8_t*>(data), len); +} + +uint64_t DefaultRandom::RandUint64() { + uint64_t value; + RandBytes(&value, sizeof(value)); + return value; +} + +} // namespace + +// static +QuicRandom* QuicRandom::GetInstance() { + static DefaultRandom* random = new DefaultRandom(); + return random; +} + +} // namespace quic
diff --git a/quic/core/crypto/quic_random.h b/quic/core/crypto/quic_random.h new file mode 100644 index 0000000..79e0953 --- /dev/null +++ b/quic/core/crypto/quic_random.h
@@ -0,0 +1,33 @@ +// Copyright (c) 2012 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_CRYPTO_QUIC_RANDOM_H_ +#define QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_ + +#include <cstddef> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// The interface for a random number generator. +class QUIC_EXPORT_PRIVATE QuicRandom { + public: + virtual ~QuicRandom() {} + + // Returns the default random number generator, which is cryptographically + // secure and thread-safe. + static QuicRandom* GetInstance(); + + // Generates |len| random bytes in the |data| buffer. + virtual void RandBytes(void* data, size_t len) = 0; + + // Returns a random number in the range [0, kuint64max]. + virtual uint64_t RandUint64() = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
diff --git a/quic/core/crypto/quic_random_test.cc b/quic/core/crypto/quic_random_test.cc new file mode 100644 index 0000000..d38bcf4 --- /dev/null +++ b/quic/core/crypto/quic_random_test.cc
@@ -0,0 +1,34 @@ +// Copyright (c) 2012 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/crypto/quic_random.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class QuicRandomTest : public QuicTest {}; + +TEST_F(QuicRandomTest, RandBytes) { + unsigned char buf1[16]; + unsigned char buf2[16]; + memset(buf1, 0xaf, sizeof(buf1)); + memset(buf2, 0xaf, sizeof(buf2)); + ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); + + QuicRandom* rng = QuicRandom::GetInstance(); + rng->RandBytes(buf1, sizeof(buf1)); + EXPECT_NE(0, memcmp(buf1, buf2, sizeof(buf1))); +} + +TEST_F(QuicRandomTest, RandUint64) { + QuicRandom* rng = QuicRandom::GetInstance(); + uint64_t value1 = rng->RandUint64(); + uint64_t value2 = rng->RandUint64(); + EXPECT_NE(value1, value2); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc new file mode 100644 index 0000000..f225785 --- /dev/null +++ b/quic/core/crypto/transport_parameters.cc
@@ -0,0 +1,302 @@ +// Copyright (c) 2018 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/crypto/transport_parameters.h" + +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" + +namespace quic { + +namespace { + +// Values of the TransportParameterId enum as defined in +// draft-ietf-quic-transport-08 section 7.4. When parameters are encoded, one of +// these enum values is used to indicate which parameter is encoded. +enum TransportParameterId : uint16_t { + kInitialMaxStreamDataId = 0, + kInitialMaxDataId = 1, + kInitialMaxBidiStreamsId = 2, + kIdleTimeoutId = 3, + kMaxPacketSizeId = 5, + kStatelessResetTokenId = 6, + kAckDelayExponentId = 7, + kInitialMaxUniStreamsId = 8, + + kMaxKnownParameterId = 9, +}; + +// Value for the TransportParameterId to use for non-standard Google QUIC params +// in Transport Parameters. +const uint16_t kGoogleQuicParamId = 18257; + +// The following constants define minimum and maximum allowed values for some of +// the parameters. These come from draft-ietf-quic-transport-08 section 7.4.1. +const uint16_t kMaxAllowedIdleTimeout = 600; +const uint16_t kMinAllowedMaxPacketSize = 1200; +const uint16_t kMaxAllowedMaxPacketSize = 65527; +const uint8_t kMaxAllowedAckDelayExponent = 20; + +static_assert(kMaxKnownParameterId <= 32, "too many parameters to bit pack"); + +// The initial_max_stream_data, initial_max_data, and idle_timeout parameters +// are always required to be present. When parsing the extension, a bitmask is +// used to keep track of which parameter have been seen so far, and that bitmask +// will be compared to this mask to check that all of the required parameters +// were present. +static constexpr uint16_t kRequiredParamsMask = (1 << kInitialMaxStreamDataId) | + (1 << kInitialMaxDataId) | + (1 << kIdleTimeoutId); + +} // namespace + +TransportParameters::TransportParameters() = default; + +TransportParameters::~TransportParameters() = default; + +bool TransportParameters::is_valid() const { + if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) { + return false; + } + if (perspective == Perspective::IS_SERVER && + stateless_reset_token.size() != 16) { + return false; + } + if (idle_timeout > kMaxAllowedIdleTimeout || + (max_packet_size.present && + (max_packet_size.value > kMaxAllowedMaxPacketSize || + max_packet_size.value < kMinAllowedMaxPacketSize)) || + (ack_delay_exponent.present && + ack_delay_exponent.value > kMaxAllowedAckDelayExponent)) { + return false; + } + return true; +} + +bool SerializeTransportParameters(const TransportParameters& in, + std::vector<uint8_t>* out) { + if (!in.is_valid()) { + return false; + } + bssl::ScopedCBB cbb; + // 28 is the minimum size that the serialized TransportParameters can be, + // which is when it is for a client and only the required parameters are + // present. The CBB will grow to fit larger serializations. + if (!CBB_init(cbb.get(), 28) || !CBB_add_u32(cbb.get(), in.version)) { + return false; + } + CBB versions; + if (in.perspective == Perspective::IS_SERVER) { + if (!CBB_add_u8_length_prefixed(cbb.get(), &versions)) { + return false; + } + for (QuicVersionLabel version : in.supported_versions) { + if (!CBB_add_u32(&versions, version)) { + return false; + } + } + } + + CBB params, initial_max_stream_data_param, initial_max_data_param, + idle_timeout_param; + // required parameters + if (!CBB_add_u16_length_prefixed(cbb.get(), ¶ms) || + // initial_max_stream_data + !CBB_add_u16(¶ms, kInitialMaxStreamDataId) || + !CBB_add_u16_length_prefixed(¶ms, &initial_max_stream_data_param) || + !CBB_add_u32(&initial_max_stream_data_param, + in.initial_max_stream_data) || + // initial_max_data + !CBB_add_u16(¶ms, kInitialMaxDataId) || + !CBB_add_u16_length_prefixed(¶ms, &initial_max_data_param) || + !CBB_add_u32(&initial_max_data_param, in.initial_max_data) || + // idle_timeout + !CBB_add_u16(¶ms, kIdleTimeoutId) || + !CBB_add_u16_length_prefixed(¶ms, &idle_timeout_param) || + !CBB_add_u16(&idle_timeout_param, in.idle_timeout)) { + return false; + } + + CBB stateless_reset_token_param; + if (!in.stateless_reset_token.empty()) { + if (!CBB_add_u16(¶ms, kStatelessResetTokenId) || + !CBB_add_u16_length_prefixed(¶ms, &stateless_reset_token_param) || + !CBB_add_bytes(&stateless_reset_token_param, + in.stateless_reset_token.data(), + in.stateless_reset_token.size())) { + return false; + } + } + + CBB initial_max_bidi_streams_param; + if (in.initial_max_bidi_streams.present) { + if (!CBB_add_u16(¶ms, kInitialMaxBidiStreamsId) || + !CBB_add_u16_length_prefixed(¶ms, + &initial_max_bidi_streams_param) || + !CBB_add_u16(&initial_max_bidi_streams_param, + in.initial_max_bidi_streams.value)) { + return false; + } + } + CBB initial_max_uni_streams_param; + if (in.initial_max_uni_streams.present) { + if (!CBB_add_u16(¶ms, kInitialMaxUniStreamsId) || + !CBB_add_u16_length_prefixed(¶ms, &initial_max_uni_streams_param) || + !CBB_add_u16(&initial_max_uni_streams_param, + in.initial_max_uni_streams.value)) { + return false; + } + } + CBB max_packet_size_param; + if (in.max_packet_size.present) { + if (!CBB_add_u16(¶ms, kMaxPacketSizeId) || + !CBB_add_u16_length_prefixed(¶ms, &max_packet_size_param) || + !CBB_add_u16(&max_packet_size_param, in.max_packet_size.value)) { + return false; + } + } + CBB ack_delay_exponent_param; + if (in.ack_delay_exponent.present) { + if (!CBB_add_u16(¶ms, kAckDelayExponentId) || + !CBB_add_u16_length_prefixed(¶ms, &ack_delay_exponent_param) || + !CBB_add_u8(&ack_delay_exponent_param, in.ack_delay_exponent.value)) { + return false; + } + } + CBB google_quic_params; + if (in.google_quic_params) { + const QuicData& serialized_google_quic_params = + in.google_quic_params->GetSerialized(); + if (!CBB_add_u16(¶ms, kGoogleQuicParamId) || + !CBB_add_u16_length_prefixed(¶ms, &google_quic_params) || + !CBB_add_bytes(&google_quic_params, + reinterpret_cast<const uint8_t*>( + serialized_google_quic_params.data()), + serialized_google_quic_params.length())) { + return false; + } + } + if (!CBB_flush(cbb.get())) { + return false; + } + out->resize(CBB_len(cbb.get())); + memcpy(out->data(), CBB_data(cbb.get()), CBB_len(cbb.get())); + return true; +} + +bool ParseTransportParameters(const uint8_t* in, + size_t in_len, + Perspective perspective, + TransportParameters* out) { + CBS cbs; + CBS_init(&cbs, in, in_len); + if (!CBS_get_u32(&cbs, &out->version)) { + return false; + } + if (perspective == Perspective::IS_SERVER) { + CBS versions; + if (!CBS_get_u8_length_prefixed(&cbs, &versions) || + CBS_len(&versions) % 4 != 0) { + return false; + } + while (CBS_len(&versions) > 0) { + QuicVersionLabel version; + if (!CBS_get_u32(&versions, &version)) { + return false; + } + out->supported_versions.push_back(version); + } + } + out->perspective = perspective; + + uint32_t present_params = 0; + bool has_google_quic_params = false; + CBS params; + if (!CBS_get_u16_length_prefixed(&cbs, ¶ms)) { + return false; + } + while (CBS_len(¶ms) > 0) { + uint16_t param_id; + CBS value; + if (!CBS_get_u16(¶ms, ¶m_id) || + !CBS_get_u16_length_prefixed(¶ms, &value)) { + return false; + } + if (param_id < kMaxKnownParameterId) { + uint16_t mask = 1 << param_id; + if (present_params & mask) { + return false; + } + present_params |= mask; + } + switch (param_id) { + case kInitialMaxStreamDataId: + if (!CBS_get_u32(&value, &out->initial_max_stream_data) || + CBS_len(&value) != 0) { + return false; + } + break; + case kInitialMaxDataId: + if (!CBS_get_u32(&value, &out->initial_max_data) || + CBS_len(&value) != 0) { + return false; + } + break; + case kInitialMaxBidiStreamsId: + if (!CBS_get_u16(&value, &out->initial_max_bidi_streams.value) || + CBS_len(&value) != 0) { + return false; + } + out->initial_max_bidi_streams.present = true; + break; + case kIdleTimeoutId: + if (!CBS_get_u16(&value, &out->idle_timeout) || CBS_len(&value) != 0) { + return false; + } + break; + case kMaxPacketSizeId: + if (!CBS_get_u16(&value, &out->max_packet_size.value) || + CBS_len(&value) != 0) { + return false; + } + out->max_packet_size.present = true; + break; + case kStatelessResetTokenId: + if (CBS_len(&value) == 0) { + return false; + } + out->stateless_reset_token.assign(CBS_data(&value), + CBS_data(&value) + CBS_len(&value)); + break; + case kAckDelayExponentId: + if (!CBS_get_u8(&value, &out->ack_delay_exponent.value) || + CBS_len(&value) != 0) { + return false; + } + out->ack_delay_exponent.present = true; + break; + case kInitialMaxUniStreamsId: + if (!CBS_get_u16(&value, &out->initial_max_uni_streams.value) || + CBS_len(&value) != 0) { + return false; + } + out->initial_max_uni_streams.present = true; + break; + case kGoogleQuicParamId: + if (has_google_quic_params) { + return false; + } + has_google_quic_params = true; + QuicStringPiece serialized_params( + reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value)); + out->google_quic_params = CryptoFramer::ParseMessage(serialized_params); + } + } + if ((present_params & kRequiredParamsMask) != kRequiredParamsMask) { + return false; + } + return out->is_valid(); +} + +} // namespace quic
diff --git a/quic/core/crypto/transport_parameters.h b/quic/core/crypto/transport_parameters.h new file mode 100644 index 0000000..b8abc0b --- /dev/null +++ b/quic/core/crypto/transport_parameters.h
@@ -0,0 +1,93 @@ +// Copyright (c) 2018 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_CRYPTO_TRANSPORT_PARAMETERS_H_ +#define QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_ + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" + +namespace quic { + +// TransportParameters contains parameters for QUIC's transport layer that are +// indicated during the TLS handshake. This struct is a mirror of the struct in +// section 6.4 of draft-ietf-quic-transport-11. +struct QUIC_EXPORT_PRIVATE TransportParameters { + TransportParameters(); + ~TransportParameters(); + + // When |perspective| is Perspective::IS_CLIENT, this struct is being used in + // the client_hello handshake message; when it is Perspective::IS_SERVER, it + // is being used in the encrypted_extensions handshake message. + Perspective perspective; + + // When Perspective::IS_CLIENT, |version| is the initial version offered by + // the client (before any version negotiation packets) for this connection. + // When Perspective::IS_SERVER, |version| is the version that is in use. + QuicVersionLabel version = 0; + + // Server-only parameters: + + // |supported_versions| contains a list of all versions that the server would + // send in a version negotiation packet. It is not used if |perspective == + // Perspective::IS_CLIENT|. + QuicVersionLabelVector supported_versions; + + // See section 6.4.1 of draft-ietf-quic-transport-11 for definition. + std::vector<uint8_t> stateless_reset_token; + + // Required parameters. See section 6.4.1 of draft-ietf-quic-transport-11 for + // definitions. + uint32_t initial_max_stream_data = 0; + uint32_t initial_max_data = 0; + uint16_t idle_timeout = 0; + + template <typename T> + struct OptionalParam { + bool present = false; + T value; + }; + + // Optional parameters. See section 6.4.1 of draft-ietf-quic-transport-11 for + // definitions. + OptionalParam<uint16_t> initial_max_bidi_streams; + OptionalParam<uint16_t> initial_max_uni_streams; + OptionalParam<uint16_t> max_packet_size; + OptionalParam<uint8_t> ack_delay_exponent; + + // Transport parameters used by Google QUIC but not IETF QUIC. This is + // serialized into a TransportParameter struct with a TransportParameterId of + // 18257. + std::unique_ptr<CryptoHandshakeMessage> google_quic_params; + + // Returns true if the contents of this struct are valid. + bool is_valid() const; +}; + +// Serializes a TransportParameters struct into the format for sending it in a +// TLS extension. The serialized bytes are put in |*out|, and this function +// returns true on success or false if |TransportParameters::is_valid| returns +// false. +QUIC_EXPORT_PRIVATE bool SerializeTransportParameters( + const TransportParameters& in, + std::vector<uint8_t>* out); + +// Parses bytes from the quic_transport_parameters TLS extension and writes the +// parsed parameters into |*out|. Input is read from |in| for |in_len| bytes. +// |perspective| indicates whether the input came from a client or a server. +// This method returns true if the input was successfully parsed, and false if +// it could not be parsed. +// TODO(nharper): Write fuzz tests for this method. +QUIC_EXPORT_PRIVATE bool ParseTransportParameters(const uint8_t* in, + size_t in_len, + Perspective perspective, + TransportParameters* out); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc new file mode 100644 index 0000000..8a81841 --- /dev/null +++ b/quic/core/crypto/transport_parameters_test.cc
@@ -0,0 +1,440 @@ +// Copyright (c) 2018 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/crypto/transport_parameters.h" + +#include "third_party/boringssl/src/include/openssl/mem.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class TransportParametersTest : public QuicTest {}; + +TEST_F(TransportParametersTest, RoundTripClient) { + TransportParameters orig_params; + orig_params.perspective = Perspective::IS_CLIENT; + orig_params.initial_max_stream_data = 12; + orig_params.initial_max_data = 34; + orig_params.idle_timeout = 56; + orig_params.initial_max_bidi_streams.present = true; + orig_params.initial_max_bidi_streams.value = 2000; + orig_params.initial_max_uni_streams.present = true; + orig_params.initial_max_uni_streams.value = 3000; + orig_params.max_packet_size.present = true; + orig_params.max_packet_size.value = 9001; + orig_params.ack_delay_exponent.present = true; + orig_params.ack_delay_exponent.value = 10; + orig_params.version = 0xff000005; + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized)); + + TransportParameters new_params; + ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(), + Perspective::IS_CLIENT, &new_params)); + + EXPECT_EQ(new_params.initial_max_stream_data, + orig_params.initial_max_stream_data); + EXPECT_EQ(new_params.initial_max_data, orig_params.initial_max_data); + EXPECT_EQ(new_params.idle_timeout, orig_params.idle_timeout); + EXPECT_EQ(new_params.version, orig_params.version); + EXPECT_TRUE(new_params.initial_max_bidi_streams.present); + EXPECT_EQ(new_params.initial_max_bidi_streams.value, + orig_params.initial_max_bidi_streams.value); + EXPECT_TRUE(new_params.initial_max_uni_streams.present); + EXPECT_EQ(new_params.initial_max_uni_streams.value, + orig_params.initial_max_uni_streams.value); + EXPECT_TRUE(new_params.max_packet_size.present); + EXPECT_EQ(new_params.max_packet_size.value, + orig_params.max_packet_size.value); + EXPECT_TRUE(new_params.ack_delay_exponent.present); + EXPECT_EQ(new_params.ack_delay_exponent.value, + orig_params.ack_delay_exponent.value); +} + +TEST_F(TransportParametersTest, RoundTripServer) { + TransportParameters orig_params; + orig_params.perspective = Perspective::IS_SERVER; + orig_params.initial_max_stream_data = 12; + orig_params.initial_max_data = 34; + orig_params.idle_timeout = 56; + orig_params.stateless_reset_token.resize(16); + orig_params.version = 0xff000005; + orig_params.supported_versions.push_back(0xff000005); + orig_params.supported_versions.push_back(0xff000004); + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized)); + + TransportParameters new_params; + ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(), + Perspective::IS_SERVER, &new_params)); + + EXPECT_EQ(new_params.initial_max_stream_data, + orig_params.initial_max_stream_data); + EXPECT_EQ(new_params.initial_max_data, orig_params.initial_max_data); + EXPECT_EQ(new_params.idle_timeout, orig_params.idle_timeout); + EXPECT_EQ(new_params.stateless_reset_token, + orig_params.stateless_reset_token); + EXPECT_EQ(new_params.version, orig_params.version); + ASSERT_EQ(new_params.supported_versions, orig_params.supported_versions); +} + +TEST_F(TransportParametersTest, IsValid) { + TransportParameters empty_params; + empty_params.perspective = Perspective::IS_CLIENT; + EXPECT_TRUE(empty_params.is_valid()); + + { + TransportParameters params; + params.perspective = Perspective::IS_CLIENT; + EXPECT_TRUE(params.is_valid()); + params.idle_timeout = 600; + EXPECT_TRUE(params.is_valid()); + params.idle_timeout = 601; + EXPECT_FALSE(params.is_valid()); + } + { + TransportParameters params; + params.perspective = Perspective::IS_CLIENT; + EXPECT_TRUE(params.is_valid()); + params.max_packet_size.present = true; + params.max_packet_size.value = 0; + EXPECT_FALSE(params.is_valid()); + params.max_packet_size.value = 1200; + EXPECT_TRUE(params.is_valid()); + params.max_packet_size.value = 65527; + EXPECT_TRUE(params.is_valid()); + params.max_packet_size.value = 65535; + EXPECT_FALSE(params.is_valid()); + } + { + TransportParameters params; + params.perspective = Perspective::IS_CLIENT; + EXPECT_TRUE(params.is_valid()); + params.ack_delay_exponent.present = true; + params.ack_delay_exponent.value = 0; + EXPECT_TRUE(params.is_valid()); + params.ack_delay_exponent.value = 20; + EXPECT_TRUE(params.is_valid()); + params.ack_delay_exponent.value = 21; + EXPECT_FALSE(params.is_valid()); + } +} + +TEST_F(TransportParametersTest, NoServerParamsWithoutStatelessResetToken) { + TransportParameters orig_params; + orig_params.perspective = Perspective::IS_SERVER; + orig_params.initial_max_stream_data = 12; + orig_params.initial_max_data = 34; + orig_params.idle_timeout = 56; + orig_params.version = 0xff000005; + orig_params.supported_versions.push_back(0xff000005); + orig_params.supported_versions.push_back(0xff000004); + + std::vector<uint8_t> out; + ASSERT_FALSE(SerializeTransportParameters(orig_params, &out)); +} + +TEST_F(TransportParametersTest, NoClientParamsWithStatelessResetToken) { + TransportParameters orig_params; + orig_params.perspective = Perspective::IS_CLIENT; + orig_params.initial_max_stream_data = 12; + orig_params.initial_max_data = 34; + orig_params.idle_timeout = 56; + orig_params.stateless_reset_token.resize(16); + orig_params.version = 0xff000005; + + std::vector<uint8_t> out; + ASSERT_FALSE(SerializeTransportParameters(orig_params, &out)); +} + +TEST_F(TransportParametersTest, ParseClientParams) { + const uint8_t kClientParams[] = { + 0xff, 0x00, 0x00, 0x05, // initial version + 0x00, 0x16, // length parameters array that follows + // initial_max_stream_data + 0x00, 0x00, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x0c, // value + // initial_max_data + 0x00, 0x01, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x22, // value + // idle_timeout + 0x00, 0x03, // parameter id + 0x00, 0x02, // length + 0x00, 0x38, // value + }; + + TransportParameters out_params; + ASSERT_TRUE(ParseTransportParameters(kClientParams, + QUIC_ARRAYSIZE(kClientParams), + Perspective::IS_CLIENT, &out_params)); +} + +TEST_F(TransportParametersTest, ParseClientParamsFailsWithStatelessResetToken) { + TransportParameters out_params; + + // clang-format off + const uint8_t kClientParamsWithFullToken[] = { + 0xff, 0x00, 0x00, 0x05, // initial version + 0x00, 0x2a, // length parameters array that follows + // initial_max_stream_data + 0x00, 0x00, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x0c, // value + // initial_max_data + 0x00, 0x01, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x22, // value + // idle_timeout + 0x00, 0x03, // parameter id + 0x00, 0x02, // length + 0x00, 0x38, // value + // stateless_reset_token + 0x00, 0x06, // parameter id + 0x00, 0x10, // length + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + }; + // clang-format on + + ASSERT_FALSE(ParseTransportParameters( + kClientParamsWithFullToken, QUIC_ARRAYSIZE(kClientParamsWithFullToken), + Perspective::IS_CLIENT, &out_params)); + + const uint8_t kClientParamsWithEmptyToken[] = { + 0xff, 0x00, 0x00, 0x05, // initial version + 0x00, 0x1a, // length parameters array that follows + // initial_max_stream_data + 0x00, 0x00, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x0c, // value + // initial_max_data + 0x00, 0x01, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x22, // value + // idle_timeout + 0x00, 0x03, // parameter id + 0x00, 0x02, // length + 0x00, 0x38, // value + // stateless_reset_token + 0x00, 0x06, // parameter id + 0x00, 0x00, // length + }; + + ASSERT_FALSE(ParseTransportParameters( + kClientParamsWithEmptyToken, QUIC_ARRAYSIZE(kClientParamsWithEmptyToken), + Perspective::IS_CLIENT, &out_params)); +} + +TEST_F(TransportParametersTest, ParseClientParametersWithInvalidParams) { + TransportParameters out_params; + + const uint8_t kClientParamsRepeated[] = { + 0xff, 0x00, 0x00, 0x05, // initial version + 0x00, 0x1c, // length parameters array that follows + // initial_max_stream_data + 0x00, 0x00, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x0c, // value + // initial_max_data + 0x00, 0x01, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x22, // value + // idle_timeout + 0x00, 0x03, // parameter id + 0x00, 0x02, // length + 0x00, 0x38, // value + // idle_timeout (repeat) + 0x00, 0x03, // parameter id + 0x00, 0x02, // length + 0x00, 0x38, // value + }; + ASSERT_FALSE(ParseTransportParameters(kClientParamsRepeated, + QUIC_ARRAYSIZE(kClientParamsRepeated), + Perspective::IS_CLIENT, &out_params)); + + const uint8_t kClientParamsMissing[] = { + 0xff, 0x00, 0x00, 0x05, // initial version + 0x00, 0x10, // length parameters array that follows + // initial_max_stream_data + 0x00, 0x00, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x0c, // value + // initial_max_data + 0x00, 0x01, // parameter id + 0x00, 0x04, // length + 0x00, 0x00, 0x00, 0x22, // value + }; + ASSERT_FALSE(ParseTransportParameters(kClientParamsMissing, + QUIC_ARRAYSIZE(kClientParamsMissing), + Perspective::IS_CLIENT, &out_params)); +} + +TEST_F(TransportParametersTest, ParseServerParams) { + // clang-format off + const uint8_t kServerParams[] = { + 0xff, 0x00, 0x00, 0x05, // negotiated_version + 0x08, // length of supported versions array + 0xff, 0x00, 0x00, 0x05, + 0xff, 0x00, 0x00, 0x04, + 0x00, 0x2a, // length of parameters array that follows + // initial_max_stream_data + 0x00, 0x00, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x0c, + // initial_max_data + 0x00, 0x01, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x22, + // idle_timeout + 0x00, 0x03, + 0x00, 0x02, + 0x00, 0x38, + // stateless_reset_token + 0x00, 0x06, + 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + + TransportParameters out_params; + ASSERT_TRUE(ParseTransportParameters(kServerParams, + QUIC_ARRAYSIZE(kServerParams), + Perspective::IS_SERVER, &out_params)); +} + +TEST_F(TransportParametersTest, ParseServerParamsWithoutToken) { + // clang-format off + const uint8_t kServerParams[] = { + 0xff, 0x00, 0x00, 0x05, // negotiated_version + 0x08, // length of supported versions array + 0xff, 0x00, 0x00, 0x05, + 0xff, 0x00, 0x00, 0x04, + 0x00, 0x16, // length of parameters array that follows + // initial_max_stream_data + 0x00, 0x00, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x0c, + // initial_max_data + 0x00, 0x01, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x22, + // idle_timeout + 0x00, 0x03, + 0x00, 0x02, + 0x00, 0x38, + }; + // clang-format on + + TransportParameters out_params; + ASSERT_FALSE(ParseTransportParameters(kServerParams, + QUIC_ARRAYSIZE(kServerParams), + Perspective::IS_SERVER, &out_params)); +} + +TEST_F(TransportParametersTest, ParseServerParametersWithInvalidParams) { + TransportParameters out_params; + + // clang-format off + const uint8_t kServerParamsRepeated[] = { + 0xff, 0x00, 0x00, 0x05, // negotiated_version + 0x08, // length of supported versions array + 0xff, 0x00, 0x00, 0x05, + 0xff, 0x00, 0x00, 0x04, + 0x00, 0x30, // length of parameters array that follows + // initial_max_stream_data + 0x00, 0x00, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x0c, + // initial_max_data + 0x00, 0x01, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x22, + // idle_timeout + 0x00, 0x03, + 0x00, 0x02, + 0x00, 0x38, + // idle_timeout (repeat) + 0x00, 0x03, + 0x00, 0x02, + 0x00, 0x38, + // stateless_reset_token + 0x00, 0x06, + 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + ASSERT_FALSE(ParseTransportParameters(kServerParamsRepeated, + QUIC_ARRAYSIZE(kServerParamsRepeated), + Perspective::IS_SERVER, &out_params)); + + // clang-format off + const uint8_t kServerParamsMissing[] = { + 0xff, 0x00, 0x00, 0x05, // negotiated_version + 0x08, // length of supported versions array + 0xff, 0x00, 0x00, 0x05, + 0xff, 0x00, 0x00, 0x04, + 0x00, 0x24, // length of parameters array that follows + // initial_max_stream_data + 0x00, 0x00, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x0c, + // initial_max_data + 0x00, 0x01, + 0x00, 0x04, + 0x00, 0x00, 0x00, 0x22, + // stateless_reset_token + 0x00, 0x06, + 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + ASSERT_FALSE(ParseTransportParameters(kServerParamsMissing, + QUIC_ARRAYSIZE(kServerParamsMissing), + Perspective::IS_SERVER, &out_params)); +} + +TEST_F(TransportParametersTest, CryptoHandshakeMessageRoundtrip) { + TransportParameters orig_params; + orig_params.perspective = Perspective::IS_CLIENT; + orig_params.initial_max_stream_data = 12; + orig_params.initial_max_data = 34; + orig_params.idle_timeout = 56; + + orig_params.google_quic_params = QuicMakeUnique<CryptoHandshakeMessage>(); + const QuicString kTestString = "test string"; + orig_params.google_quic_params->SetStringPiece(42, kTestString); + const uint32_t kTestValue = 12; + orig_params.google_quic_params->SetValue(1337, kTestValue); + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized)); + + TransportParameters new_params; + ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(), + Perspective::IS_CLIENT, &new_params)); + + ASSERT_NE(new_params.google_quic_params.get(), nullptr); + EXPECT_EQ(new_params.google_quic_params->tag(), + orig_params.google_quic_params->tag()); + QuicStringPiece test_string; + EXPECT_TRUE(new_params.google_quic_params->GetStringPiece(42, &test_string)); + EXPECT_EQ(test_string, kTestString); + uint32_t test_value; + EXPECT_EQ(new_params.google_quic_params->GetUint32(1337, &test_value), + QUIC_NO_ERROR); + EXPECT_EQ(test_value, kTestValue); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/frames/quic_ack_frame.cc b/quic/core/frames/quic_ack_frame.cc new file mode 100644 index 0000000..389f1c0 --- /dev/null +++ b/quic/core/frames/quic_ack_frame.cc
@@ -0,0 +1,326 @@ +// 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/frames/quic_ack_frame.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" + +namespace quic { + +namespace { + +const QuicPacketCount kMaxPrintRange = 128; + +uint64_t PacketNumberIntervalLength( + const QuicInterval<QuicPacketNumber>& interval) { + if (interval.Empty()) { + return 0u; + } + return interval.max() - interval.min(); +} +} // namespace + +bool IsAwaitingPacket(const QuicAckFrame& ack_frame, + QuicPacketNumber packet_number, + QuicPacketNumber peer_least_packet_awaiting_ack) { + DCHECK(packet_number.IsInitialized()); + return (!peer_least_packet_awaiting_ack.IsInitialized() || + packet_number >= peer_least_packet_awaiting_ack) && + !ack_frame.packets.Contains(packet_number); +} + +QuicAckFrame::QuicAckFrame() + : ack_delay_time(QuicTime::Delta::Infinite()), + ecn_counters_populated(false), + ect_0_count(0), + ect_1_count(0), + ecn_ce_count(0) {} + +QuicAckFrame::QuicAckFrame(const QuicAckFrame& other) = default; + +QuicAckFrame::~QuicAckFrame() {} + +std::ostream& operator<<(std::ostream& os, const QuicAckFrame& ack_frame) { + os << "{ largest_acked: " << LargestAcked(ack_frame) + << ", ack_delay_time: " << ack_frame.ack_delay_time.ToMicroseconds() + << ", packets: [ " << ack_frame.packets << " ]" + << ", received_packets: [ "; + for (const std::pair<QuicPacketNumber, QuicTime>& p : + ack_frame.received_packet_times) { + os << p.first << " at " << p.second.ToDebuggingValue() << " "; + } + os << " ]"; + os << ", ecn_counters_populated: " << ack_frame.ecn_counters_populated; + if (ack_frame.ecn_counters_populated) { + os << ", ect_0_count: " << ack_frame.ect_0_count + << ", ect_1_count: " << ack_frame.ect_1_count + << ", ecn_ce_count: " << ack_frame.ecn_ce_count; + } + + os << " }\n"; + return os; +} + +void QuicAckFrame::Clear() { + largest_acked.Clear(); + ack_delay_time = QuicTime::Delta::Infinite(); + received_packet_times.clear(); + packets.Clear(); +} + +PacketNumberQueue::PacketNumberQueue() {} +PacketNumberQueue::PacketNumberQueue(const PacketNumberQueue& other) = default; +PacketNumberQueue::PacketNumberQueue(PacketNumberQueue&& other) = default; +PacketNumberQueue::~PacketNumberQueue() {} + +PacketNumberQueue& PacketNumberQueue::operator=( + const PacketNumberQueue& other) = default; +PacketNumberQueue& PacketNumberQueue::operator=(PacketNumberQueue&& other) = + default; + +void PacketNumberQueue::Add(QuicPacketNumber packet_number) { + if (!packet_number.IsInitialized()) { + return; + } + // Check if the deque is empty + if (packet_number_deque_.empty()) { + packet_number_deque_.push_front( + QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1)); + return; + } + QuicInterval<QuicPacketNumber> back = packet_number_deque_.back(); + + // Check for the typical case, + // when the next packet in order is acked + if (back.max() == packet_number) { + packet_number_deque_.back().SetMax(packet_number + 1); + return; + } + // Check if the next packet in order is skipped + if (back.max() < packet_number) { + packet_number_deque_.push_back( + QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1)); + return; + } + + QuicInterval<QuicPacketNumber> front = packet_number_deque_.front(); + // Check if the packet can be popped on the front + if (front.min() > packet_number + 1) { + packet_number_deque_.push_front( + QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1)); + return; + } + if (front.min() == packet_number + 1) { + packet_number_deque_.front().SetMin(packet_number); + return; + } + + int i = packet_number_deque_.size() - 1; + // Iterating through the queue backwards + // to find a proper place for the packet + while (i >= 0) { + QuicInterval<QuicPacketNumber> packet_interval = packet_number_deque_[i]; + DCHECK(packet_interval.min() < packet_interval.max()); + // Check if the packet is contained in an interval already + if (packet_interval.Contains(packet_number)) { + return; + } + + // Check if the packet can extend an interval. + if (packet_interval.max() == packet_number) { + packet_number_deque_[i].SetMax(packet_number + 1); + return; + } + // Check if the packet can extend an interval + // and merge two intervals if needed. + // There is no need to merge an interval in the previous + // if statement, as all merges will happen here. + if (packet_interval.min() == packet_number + 1) { + packet_number_deque_[i].SetMin(packet_number); + if (i > 0 && packet_number == packet_number_deque_[i - 1].max()) { + packet_number_deque_[i - 1].SetMax(packet_interval.max()); + packet_number_deque_.erase(packet_number_deque_.begin() + i); + } + return; + } + + // Check if we need to make a new interval for the packet + if (packet_interval.max() < packet_number + 1) { + packet_number_deque_.insert( + packet_number_deque_.begin() + i + 1, + QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1)); + return; + } + i--; + } +} + +void PacketNumberQueue::AddRange(QuicPacketNumber lower, + QuicPacketNumber higher) { + if (!lower.IsInitialized() || !higher.IsInitialized() || lower >= higher) { + return; + } + if (packet_number_deque_.empty()) { + packet_number_deque_.push_front( + QuicInterval<QuicPacketNumber>(lower, higher)); + return; + } + QuicInterval<QuicPacketNumber> back = packet_number_deque_.back(); + + if (back.max() == lower) { + // Check for the typical case, + // when the next packet in order is acked + packet_number_deque_.back().SetMax(higher); + return; + } + if (back.max() < lower) { + // Check if the next packet in order is skipped + packet_number_deque_.push_back( + QuicInterval<QuicPacketNumber>(lower, higher)); + return; + } + QuicInterval<QuicPacketNumber> front = packet_number_deque_.front(); + // Check if the packets are being added in reverse order + if (front.min() == higher) { + packet_number_deque_.front().SetMin(lower); + } else if (front.min() > higher) { + packet_number_deque_.push_front( + QuicInterval<QuicPacketNumber>(lower, higher)); + + } else { + // Ranges must be above or below all existing ranges. + QUIC_BUG << "AddRange only supports adding packets above or below the " + << "current min:" << Min() << " and max:" << Max() + << ", but adding [" << lower << "," << higher << ")"; + } +} + +bool PacketNumberQueue::RemoveUpTo(QuicPacketNumber higher) { + if (!higher.IsInitialized() || Empty()) { + return false; + } + const QuicPacketNumber old_min = Min(); + while (!packet_number_deque_.empty()) { + QuicInterval<QuicPacketNumber> front = packet_number_deque_.front(); + if (front.max() < higher) { + packet_number_deque_.pop_front(); + } else if (front.min() < higher && front.max() >= higher) { + packet_number_deque_.front().SetMin(higher); + if (front.max() == higher) { + packet_number_deque_.pop_front(); + } + break; + } else { + break; + } + } + + return Empty() || old_min != Min(); +} + +void PacketNumberQueue::RemoveSmallestInterval() { + QUIC_BUG_IF(packet_number_deque_.size() < 2) + << (Empty() ? "No intervals to remove." + : "Can't remove the last interval."); + packet_number_deque_.pop_front(); +} + +void PacketNumberQueue::Clear() { + packet_number_deque_.clear(); +} + +bool PacketNumberQueue::Contains(QuicPacketNumber packet_number) const { + if (!packet_number.IsInitialized() || packet_number_deque_.empty()) { + return false; + } + if (packet_number_deque_.front().min() > packet_number || + packet_number_deque_.back().max() <= packet_number) { + return false; + } + for (QuicInterval<QuicPacketNumber> interval : packet_number_deque_) { + if (interval.Contains(packet_number)) { + return true; + } + } + return false; +} + +bool PacketNumberQueue::Empty() const { + return packet_number_deque_.empty(); +} + +QuicPacketNumber PacketNumberQueue::Min() const { + DCHECK(!Empty()); + return packet_number_deque_.front().min(); +} + +QuicPacketNumber PacketNumberQueue::Max() const { + DCHECK(!Empty()); + return packet_number_deque_.back().max() - 1; +} + +QuicPacketCount PacketNumberQueue::NumPacketsSlow() const { + QuicPacketCount n_packets = 0; + for (QuicInterval<QuicPacketNumber> interval : packet_number_deque_) { + n_packets += PacketNumberIntervalLength(interval); + } + return n_packets; +} + +size_t PacketNumberQueue::NumIntervals() const { + return packet_number_deque_.size(); +} + +PacketNumberQueue::const_iterator PacketNumberQueue::begin() const { + return packet_number_deque_.begin(); +} + +PacketNumberQueue::const_iterator PacketNumberQueue::end() const { + return packet_number_deque_.end(); +} + +PacketNumberQueue::const_reverse_iterator PacketNumberQueue::rbegin() const { + return packet_number_deque_.rbegin(); +} + +PacketNumberQueue::const_reverse_iterator PacketNumberQueue::rend() const { + return packet_number_deque_.rend(); +} + +QuicPacketCount PacketNumberQueue::LastIntervalLength() const { + DCHECK(!Empty()); + return PacketNumberIntervalLength(packet_number_deque_.back()); +} + +// Largest min...max range for packet numbers where we print the numbers +// explicitly. If bigger than this, we print as a range [a,d] rather +// than [a b c d] + +std::ostream& operator<<(std::ostream& os, const PacketNumberQueue& q) { + for (const QuicInterval<QuicPacketNumber>& interval : q) { + // Print as a range if there is a pathological condition. + if ((interval.min() >= interval.max()) || + (interval.max() - interval.min() > kMaxPrintRange)) { + // If min>max, it's really a bug, so QUIC_BUG it to + // catch it in development. + QUIC_BUG_IF(interval.min() >= interval.max()) + << "Ack Range minimum (" << interval.min() << "Not less than max (" + << interval.max() << ")"; + // print range as min...max rather than full list. + // in the event of a bug, the list could be very big. + os << interval.min() << "..." << (interval.max() - 1) << " "; + } else { + for (QuicPacketNumber packet_number = interval.min(); + packet_number < interval.max(); ++packet_number) { + os << packet_number << " "; + } + } + } + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_ack_frame.h b/quic/core/frames/quic_ack_frame.h new file mode 100644 index 0000000..771d93e --- /dev/null +++ b/quic/core/frames/quic_ack_frame.h
@@ -0,0 +1,144 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +// A sequence of packet numbers where each number is unique. Intended to be used +// in a sliding window fashion, where smaller old packet numbers are removed and +// larger new packet numbers are added, with the occasional random access. +class QUIC_EXPORT_PRIVATE PacketNumberQueue { + public: + PacketNumberQueue(); + PacketNumberQueue(const PacketNumberQueue& other); + PacketNumberQueue(PacketNumberQueue&& other); + ~PacketNumberQueue(); + + PacketNumberQueue& operator=(const PacketNumberQueue& other); + PacketNumberQueue& operator=(PacketNumberQueue&& other); + + typedef QuicDeque<QuicInterval<QuicPacketNumber>>::const_iterator + const_iterator; + typedef QuicDeque<QuicInterval<QuicPacketNumber>>::const_reverse_iterator + const_reverse_iterator; + + // Adds |packet_number| to the set of packets in the queue. + void Add(QuicPacketNumber packet_number); + + // Adds packets between [lower, higher) to the set of packets in the queue. It + // is undefined behavior to call this with |higher| < |lower|. + void AddRange(QuicPacketNumber lower, QuicPacketNumber higher); + + // Removes packets with values less than |higher| from the set of packets in + // the queue. Returns true if packets were removed. + bool RemoveUpTo(QuicPacketNumber higher); + + // Removes the smallest interval in the queue. + void RemoveSmallestInterval(); + + // Clear this packet number queue. + void Clear(); + + // Returns true if the queue contains |packet_number|. + bool Contains(QuicPacketNumber packet_number) const; + + // Returns true if the queue is empty. + bool Empty() const; + + // Returns the minimum packet number stored in the queue. It is undefined + // behavior to call this if the queue is empty. + QuicPacketNumber Min() const; + + // Returns the maximum packet number stored in the queue. It is undefined + // behavior to call this if the queue is empty. + QuicPacketNumber Max() const; + + // Returns the number of unique packets stored in the queue. Inefficient; only + // exposed for testing. + QuicPacketCount NumPacketsSlow() const; + + // Returns the number of disjoint packet number intervals contained in the + // queue. + size_t NumIntervals() const; + + // Returns the length of last interval. + QuicPacketCount LastIntervalLength() const; + + // Returns iterators over the packet number intervals. + const_iterator begin() const; + const_iterator end() const; + const_reverse_iterator rbegin() const; + const_reverse_iterator rend() const; + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const PacketNumberQueue& q); + + private: + QuicDeque<QuicInterval<QuicPacketNumber>> packet_number_deque_; +}; + +struct QUIC_EXPORT_PRIVATE QuicAckFrame { + QuicAckFrame(); + QuicAckFrame(const QuicAckFrame& other); + ~QuicAckFrame(); + + void Clear(); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicAckFrame& ack_frame); + + // The highest packet number we've observed from the peer. When |packets| is + // not empty, it should always be equal to packets.Max(). The |LargestAcked| + // function ensures this invariant in debug mode. + QuicPacketNumber largest_acked; + + // Time elapsed since largest_observed() was received until this Ack frame was + // sent. + QuicTime::Delta ack_delay_time; + + // Vector of <packet_number, time> for when packets arrived. + PacketTimeVector received_packet_times; + + // Set of packets. + PacketNumberQueue packets; + + // ECN counters, used only in version 99's ACK frame and valid only when + // |ecn_counters_populated| is true. + bool ecn_counters_populated; + QuicPacketCount ect_0_count; + QuicPacketCount ect_1_count; + QuicPacketCount ecn_ce_count; +}; + +// The highest acked packet number we've observed from the peer. If no packets +// have been observed, return 0. +inline QUIC_EXPORT_PRIVATE QuicPacketNumber +LargestAcked(const QuicAckFrame& frame) { + DCHECK(frame.packets.Empty() || frame.packets.Max() == frame.largest_acked); + return frame.largest_acked; +} + +// True if the packet number is greater than largest_observed or is listed +// as missing. +// Always returns false for packet numbers less than least_unacked. +QUIC_EXPORT_PRIVATE bool IsAwaitingPacket( + const QuicAckFrame& ack_frame, + QuicPacketNumber packet_number, + QuicPacketNumber peer_least_packet_awaiting_ack); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FRAME_H_
diff --git a/quic/core/frames/quic_application_close_frame.cc b/quic/core/frames/quic_application_close_frame.cc new file mode 100644 index 0000000..2535655 --- /dev/null +++ b/quic/core/frames/quic_application_close_frame.cc
@@ -0,0 +1,19 @@ +// 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/frames/quic_application_close_frame.h" + +namespace quic { + +QuicApplicationCloseFrame::QuicApplicationCloseFrame() + : error_code(QUIC_NO_ERROR) {} + +std::ostream& operator<<(std::ostream& os, + const QuicApplicationCloseFrame& frame) { + os << "{ error_code: " << frame.error_code << ", error_details: '" + << frame.error_details << "' }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_application_close_frame.h b/quic/core/frames/quic_application_close_frame.h new file mode 100644 index 0000000..b9b84dd --- /dev/null +++ b/quic/core/frames/quic_application_close_frame.h
@@ -0,0 +1,29 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_APPLICATION_CLOSE_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_APPLICATION_CLOSE_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicApplicationCloseFrame { + QuicApplicationCloseFrame(); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicApplicationCloseFrame& frame); + + QuicErrorCode error_code; + QuicString error_details; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_APPLICATION_CLOSE_FRAME_H_
diff --git a/quic/core/frames/quic_blocked_frame.cc b/quic/core/frames/quic_blocked_frame.cc new file mode 100644 index 0000000..41ba144 --- /dev/null +++ b/quic/core/frames/quic_blocked_frame.cc
@@ -0,0 +1,31 @@ +// 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/frames/quic_blocked_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +QuicBlockedFrame::QuicBlockedFrame() + : control_frame_id(kInvalidControlFrameId), stream_id(0), offset(0) {} + +QuicBlockedFrame::QuicBlockedFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id) + : control_frame_id(control_frame_id), stream_id(stream_id), offset(0) {} + +QuicBlockedFrame::QuicBlockedFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicStreamOffset offset) + : control_frame_id(control_frame_id), + stream_id(stream_id), + offset(offset) {} + +std::ostream& operator<<(std::ostream& os, + const QuicBlockedFrame& blocked_frame) { + os << "{ control_frame_id: " << blocked_frame.control_frame_id + << ", stream_id: " << blocked_frame.stream_id << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_blocked_frame.h b/quic/core/frames/quic_blocked_frame.h new file mode 100644 index 0000000..a4be664 --- /dev/null +++ b/quic/core/frames/quic_blocked_frame.h
@@ -0,0 +1,51 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_BLOCKED_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_BLOCKED_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +// The BLOCKED frame is used to indicate to the remote endpoint that this +// endpoint believes itself to be flow-control blocked but otherwise ready to +// send data. The BLOCKED frame is purely advisory and optional. +// Based on SPDY's BLOCKED frame (undocumented as of 2014-01-28). +struct QUIC_EXPORT_PRIVATE QuicBlockedFrame { + QuicBlockedFrame(); + QuicBlockedFrame(QuicControlFrameId control_frame_id, QuicStreamId stream_id); + QuicBlockedFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicStreamOffset offset); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicBlockedFrame& b); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + // The stream this frame applies to. 0 is a special case meaning the overall + // connection rather than a specific stream. + // + // For IETF QUIC, the stream_id controls whether an IETF QUIC + // BLOCKED or STREAM_BLOCKED frame is generated. + // If stream_id is 0 then a BLOCKED frame is generated and transmitted, + // if non-0, a STREAM_BLOCKED. + // TODO(fkastenholz): This should be converted to use + // QuicUtils::GetInvalidStreamId to get the correct invalid stream id value + // and not rely on 0. + QuicStreamId stream_id; + + // For Google QUIC, the offset is ignored. + QuicStreamOffset offset; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_BLOCKED_FRAME_H_
diff --git a/quic/core/frames/quic_connection_close_frame.cc b/quic/core/frames/quic_connection_close_frame.cc new file mode 100644 index 0000000..51969c6 --- /dev/null +++ b/quic/core/frames/quic_connection_close_frame.cc
@@ -0,0 +1,35 @@ +// 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/frames/quic_connection_close_frame.h" + +namespace quic { + +QuicConnectionCloseFrame::QuicConnectionCloseFrame() + : error_code(QUIC_NO_ERROR), frame_type(0) {} + +QuicConnectionCloseFrame::QuicConnectionCloseFrame(QuicErrorCode error_code, + QuicString error_details) + : error_code(error_code), + error_details(std::move(error_details)), + frame_type(0) {} + +QuicConnectionCloseFrame::QuicConnectionCloseFrame( + QuicIetfTransportErrorCodes ietf_error_code, + QuicString error_details, + uint64_t frame_type) + : ietf_error_code(ietf_error_code), + error_details(std::move(error_details)), + frame_type(frame_type) {} + +std::ostream& operator<<( + std::ostream& os, + const QuicConnectionCloseFrame& connection_close_frame) { + os << "{ error_code: " << connection_close_frame.error_code + << ", error_details: '" << connection_close_frame.error_details + << "', frame_type: " << connection_close_frame.frame_type << "}\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_connection_close_frame.h b/quic/core/frames/quic_connection_close_frame.h new file mode 100644 index 0000000..2bbbe73 --- /dev/null +++ b/quic/core/frames/quic_connection_close_frame.h
@@ -0,0 +1,46 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_CONNECTION_CLOSE_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_CONNECTION_CLOSE_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicConnectionCloseFrame { + QuicConnectionCloseFrame(); + QuicConnectionCloseFrame(QuicErrorCode error_code, QuicString error_details); + QuicConnectionCloseFrame(QuicIetfTransportErrorCodes ietf_error_code, + QuicString error_details, + uint64_t frame_type); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicConnectionCloseFrame& c); + + // Set error_code or ietf_error_code based on the transport version + // currently in use. + union { + // IETF QUIC has a different set of error codes. Include both + // code-sets. + QuicErrorCode error_code; + QuicIetfTransportErrorCodes ietf_error_code; + }; + QuicString error_details; + + // Contains the type of frame that triggered the connection close. Made a + // uint64, as opposed to the QuicIetfFrameType, to support possible + // extensions as well as reporting invalid frame types received from the peer. + uint64_t frame_type; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_CONNECTION_CLOSE_FRAME_H_
diff --git a/quic/core/frames/quic_crypto_frame.cc b/quic/core/frames/quic_crypto_frame.cc new file mode 100644 index 0000000..9d602bc --- /dev/null +++ b/quic/core/frames/quic_crypto_frame.cc
@@ -0,0 +1,42 @@ +// Copyright (c) 2018 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/frames/quic_crypto_frame.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicCryptoFrame::QuicCryptoFrame() + : QuicCryptoFrame(ENCRYPTION_NONE, 0, nullptr, 0) {} + +QuicCryptoFrame::QuicCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + QuicPacketLength data_length) + : QuicCryptoFrame(level, offset, nullptr, data_length) {} + +QuicCryptoFrame::QuicCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + QuicStringPiece data) + : QuicCryptoFrame(level, offset, data.data(), data.length()) {} + +QuicCryptoFrame::QuicCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + const char* data_buffer, + QuicPacketLength data_length) + : level(level), + data_length(data_length), + data_buffer(data_buffer), + offset(offset) {} + +QuicCryptoFrame::~QuicCryptoFrame() {} + +std::ostream& operator<<(std::ostream& os, + const QuicCryptoFrame& stream_frame) { + os << "{ offset: " << stream_frame.offset + << ", length: " << stream_frame.data_length << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_crypto_frame.h b/quic/core/frames/quic_crypto_frame.h new file mode 100644 index 0000000..a3968fe --- /dev/null +++ b/quic/core/frames/quic_crypto_frame.h
@@ -0,0 +1,51 @@ +// Copyright (c) 2018 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_FRAMES_QUIC_CRYPTO_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_ + +#include <memory> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicCryptoFrame { + QuicCryptoFrame(); + QuicCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + QuicPacketLength data_length); + QuicCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + QuicStringPiece data); + ~QuicCryptoFrame(); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const QuicCryptoFrame& s); + + // When writing a crypto frame to a packet, the packet must be encrypted at + // |level|. When a crypto frame is read, the encryption level of the packet it + // was received in is put in |level|. + EncryptionLevel level; + QuicPacketLength data_length; + // When reading, |data_buffer| points to the data that was received in the + // frame. |data_buffer| is not used when writing. + const char* data_buffer; + QuicStreamOffset offset; // Location of this data in the stream. + + QuicCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + const char* data_buffer, + QuicPacketLength data_length); +}; +static_assert(sizeof(QuicCryptoFrame) <= 64, + "Keep the QuicCryptoFrame size to a cacheline."); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_
diff --git a/quic/core/frames/quic_frame.cc b/quic/core/frames/quic_frame.cc new file mode 100644 index 0000000..fbe54cd --- /dev/null +++ b/quic/core/frames/quic_frame.cc
@@ -0,0 +1,349 @@ +// 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/frames/quic_frame.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicFrame::QuicFrame() {} + +QuicFrame::QuicFrame(QuicPaddingFrame padding_frame) + : padding_frame(padding_frame) {} + +QuicFrame::QuicFrame(QuicStreamFrame stream_frame) + : stream_frame(stream_frame) {} + +QuicFrame::QuicFrame(QuicCryptoFrame* crypto_frame) + : type(CRYPTO_FRAME), crypto_frame(crypto_frame) {} + +QuicFrame::QuicFrame(QuicAckFrame* frame) : type(ACK_FRAME), ack_frame(frame) {} + +QuicFrame::QuicFrame(QuicMtuDiscoveryFrame frame) + : mtu_discovery_frame(frame) {} + +QuicFrame::QuicFrame(QuicStopWaitingFrame frame) : stop_waiting_frame(frame) {} + +QuicFrame::QuicFrame(QuicPingFrame frame) : ping_frame(frame) {} + +QuicFrame::QuicFrame(QuicRstStreamFrame* frame) + : type(RST_STREAM_FRAME), rst_stream_frame(frame) {} + +QuicFrame::QuicFrame(QuicConnectionCloseFrame* frame) + : type(CONNECTION_CLOSE_FRAME), connection_close_frame(frame) {} + +QuicFrame::QuicFrame(QuicGoAwayFrame* frame) + : type(GOAWAY_FRAME), goaway_frame(frame) {} + +QuicFrame::QuicFrame(QuicWindowUpdateFrame* frame) + : type(WINDOW_UPDATE_FRAME), window_update_frame(frame) {} + +QuicFrame::QuicFrame(QuicBlockedFrame* frame) + : type(BLOCKED_FRAME), blocked_frame(frame) {} + +QuicFrame::QuicFrame(QuicApplicationCloseFrame* frame) + : type(APPLICATION_CLOSE_FRAME), application_close_frame(frame) {} + +QuicFrame::QuicFrame(QuicNewConnectionIdFrame* frame) + : type(NEW_CONNECTION_ID_FRAME), new_connection_id_frame(frame) {} + +QuicFrame::QuicFrame(QuicRetireConnectionIdFrame* frame) + : type(RETIRE_CONNECTION_ID_FRAME), retire_connection_id_frame(frame) {} + +QuicFrame::QuicFrame(QuicMaxStreamIdFrame frame) : max_stream_id_frame(frame) {} + +QuicFrame::QuicFrame(QuicStreamIdBlockedFrame frame) + : stream_id_blocked_frame(frame) {} + +QuicFrame::QuicFrame(QuicPathResponseFrame* frame) + : type(PATH_RESPONSE_FRAME), path_response_frame(frame) {} + +QuicFrame::QuicFrame(QuicPathChallengeFrame* frame) + : type(PATH_CHALLENGE_FRAME), path_challenge_frame(frame) {} + +QuicFrame::QuicFrame(QuicStopSendingFrame* frame) + : type(STOP_SENDING_FRAME), stop_sending_frame(frame) {} + +QuicFrame::QuicFrame(QuicMessageFrame* frame) + : type(MESSAGE_FRAME), message_frame(frame) {} + +QuicFrame::QuicFrame(QuicNewTokenFrame* frame) + : type(NEW_TOKEN_FRAME), new_token_frame(frame) {} + +void DeleteFrames(QuicFrames* frames) { + for (QuicFrame& frame : *frames) { + DeleteFrame(&frame); + } + frames->clear(); +} + +void DeleteFrame(QuicFrame* frame) { + switch (frame->type) { + // Frames smaller than a pointer are inlined, so don't need to be deleted. + case PADDING_FRAME: + case MTU_DISCOVERY_FRAME: + case PING_FRAME: + case MAX_STREAM_ID_FRAME: + case STOP_WAITING_FRAME: + case STREAM_ID_BLOCKED_FRAME: + case STREAM_FRAME: + break; + case ACK_FRAME: + delete frame->ack_frame; + break; + case RST_STREAM_FRAME: + delete frame->rst_stream_frame; + break; + case CONNECTION_CLOSE_FRAME: + delete frame->connection_close_frame; + break; + case GOAWAY_FRAME: + delete frame->goaway_frame; + break; + case BLOCKED_FRAME: + delete frame->blocked_frame; + break; + case WINDOW_UPDATE_FRAME: + delete frame->window_update_frame; + break; + case PATH_CHALLENGE_FRAME: + delete frame->path_challenge_frame; + break; + case STOP_SENDING_FRAME: + delete frame->stop_sending_frame; + break; + case APPLICATION_CLOSE_FRAME: + delete frame->application_close_frame; + break; + case NEW_CONNECTION_ID_FRAME: + delete frame->new_connection_id_frame; + break; + case RETIRE_CONNECTION_ID_FRAME: + delete frame->retire_connection_id_frame; + break; + case PATH_RESPONSE_FRAME: + delete frame->path_response_frame; + break; + case MESSAGE_FRAME: + delete frame->message_frame; + break; + case CRYPTO_FRAME: + delete frame->crypto_frame; + break; + case NEW_TOKEN_FRAME: + delete frame->new_token_frame; + break; + + case NUM_FRAME_TYPES: + DCHECK(false) << "Cannot delete type: " << frame->type; + } +} + +void RemoveFramesForStream(QuicFrames* frames, QuicStreamId stream_id) { + auto it = frames->begin(); + while (it != frames->end()) { + if (it->type != STREAM_FRAME || it->stream_frame.stream_id != stream_id) { + ++it; + continue; + } + it = frames->erase(it); + } +} + +bool IsControlFrame(QuicFrameType type) { + switch (type) { + case RST_STREAM_FRAME: + case GOAWAY_FRAME: + case WINDOW_UPDATE_FRAME: + case BLOCKED_FRAME: + case STREAM_ID_BLOCKED_FRAME: + case MAX_STREAM_ID_FRAME: + case PING_FRAME: + case STOP_SENDING_FRAME: + return true; + default: + return false; + } +} + +QuicControlFrameId GetControlFrameId(const QuicFrame& frame) { + switch (frame.type) { + case RST_STREAM_FRAME: + return frame.rst_stream_frame->control_frame_id; + case GOAWAY_FRAME: + return frame.goaway_frame->control_frame_id; + case WINDOW_UPDATE_FRAME: + return frame.window_update_frame->control_frame_id; + case BLOCKED_FRAME: + return frame.blocked_frame->control_frame_id; + case STREAM_ID_BLOCKED_FRAME: + return frame.stream_id_blocked_frame.control_frame_id; + case MAX_STREAM_ID_FRAME: + return frame.max_stream_id_frame.control_frame_id; + case PING_FRAME: + return frame.ping_frame.control_frame_id; + case STOP_SENDING_FRAME: + return frame.stop_sending_frame->control_frame_id; + default: + return kInvalidControlFrameId; + } +} + +void SetControlFrameId(QuicControlFrameId control_frame_id, QuicFrame* frame) { + switch (frame->type) { + case RST_STREAM_FRAME: + frame->rst_stream_frame->control_frame_id = control_frame_id; + return; + case GOAWAY_FRAME: + frame->goaway_frame->control_frame_id = control_frame_id; + return; + case WINDOW_UPDATE_FRAME: + frame->window_update_frame->control_frame_id = control_frame_id; + return; + case BLOCKED_FRAME: + frame->blocked_frame->control_frame_id = control_frame_id; + return; + case PING_FRAME: + frame->ping_frame.control_frame_id = control_frame_id; + return; + case STREAM_ID_BLOCKED_FRAME: + frame->stream_id_blocked_frame.control_frame_id = control_frame_id; + return; + case MAX_STREAM_ID_FRAME: + frame->max_stream_id_frame.control_frame_id = control_frame_id; + return; + case STOP_SENDING_FRAME: + frame->stop_sending_frame->control_frame_id = control_frame_id; + return; + default: + QUIC_BUG + << "Try to set control frame id of a frame without control frame id"; + } +} + +QuicFrame CopyRetransmittableControlFrame(const QuicFrame& frame) { + QuicFrame copy; + switch (frame.type) { + case RST_STREAM_FRAME: + copy = QuicFrame(new QuicRstStreamFrame(*frame.rst_stream_frame)); + break; + case GOAWAY_FRAME: + copy = QuicFrame(new QuicGoAwayFrame(*frame.goaway_frame)); + break; + case WINDOW_UPDATE_FRAME: + copy = QuicFrame(new QuicWindowUpdateFrame(*frame.window_update_frame)); + break; + case BLOCKED_FRAME: + copy = QuicFrame(new QuicBlockedFrame(*frame.blocked_frame)); + break; + case PING_FRAME: + copy = QuicFrame(QuicPingFrame(frame.ping_frame.control_frame_id)); + break; + case STOP_SENDING_FRAME: + copy = QuicFrame(new QuicStopSendingFrame(*frame.stop_sending_frame)); + break; + case STREAM_ID_BLOCKED_FRAME: + copy = QuicFrame(QuicStreamIdBlockedFrame(frame.stream_id_blocked_frame)); + break; + case MAX_STREAM_ID_FRAME: + copy = QuicFrame(QuicMaxStreamIdFrame(frame.max_stream_id_frame)); + break; + default: + QUIC_BUG << "Try to copy a non-retransmittable control frame: " << frame; + copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId)); + break; + } + return copy; +} + +std::ostream& operator<<(std::ostream& os, const QuicFrame& frame) { + switch (frame.type) { + case PADDING_FRAME: { + os << "type { PADDING_FRAME } " << frame.padding_frame; + break; + } + case RST_STREAM_FRAME: { + os << "type { RST_STREAM_FRAME } " << *(frame.rst_stream_frame); + break; + } + case CONNECTION_CLOSE_FRAME: { + os << "type { CONNECTION_CLOSE_FRAME } " + << *(frame.connection_close_frame); + break; + } + case GOAWAY_FRAME: { + os << "type { GOAWAY_FRAME } " << *(frame.goaway_frame); + break; + } + case WINDOW_UPDATE_FRAME: { + os << "type { WINDOW_UPDATE_FRAME } " << *(frame.window_update_frame); + break; + } + case BLOCKED_FRAME: { + os << "type { BLOCKED_FRAME } " << *(frame.blocked_frame); + break; + } + case STREAM_FRAME: { + os << "type { STREAM_FRAME } " << frame.stream_frame; + break; + } + case ACK_FRAME: { + os << "type { ACK_FRAME } " << *(frame.ack_frame); + break; + } + case STOP_WAITING_FRAME: { + os << "type { STOP_WAITING_FRAME } " << frame.stop_waiting_frame; + break; + } + case PING_FRAME: { + os << "type { PING_FRAME } " << frame.ping_frame; + break; + } + case MTU_DISCOVERY_FRAME: { + os << "type { MTU_DISCOVERY_FRAME } "; + break; + } + case APPLICATION_CLOSE_FRAME: + os << "type { APPLICATION_CLOSE } " << *(frame.application_close_frame); + break; + case NEW_CONNECTION_ID_FRAME: + os << "type { NEW_CONNECTION_ID } " << *(frame.new_connection_id_frame); + break; + case RETIRE_CONNECTION_ID_FRAME: + os << "type { RETIRE_CONNECTION_ID } " + << *(frame.retire_connection_id_frame); + break; + case MAX_STREAM_ID_FRAME: + os << "type { MAX_STREAM_ID } " << frame.max_stream_id_frame; + break; + case STREAM_ID_BLOCKED_FRAME: + os << "type { STREAM_ID_BLOCKED } " << frame.stream_id_blocked_frame; + break; + case PATH_RESPONSE_FRAME: + os << "type { PATH_RESPONSE } " << *(frame.path_response_frame); + break; + case PATH_CHALLENGE_FRAME: + os << "type { PATH_CHALLENGE } " << *(frame.path_challenge_frame); + break; + case STOP_SENDING_FRAME: + os << "type { STOP_SENDING } " << *(frame.stop_sending_frame); + break; + case MESSAGE_FRAME: + os << "type { MESSAGE_FRAME }" << *(frame.message_frame); + break; + case NEW_TOKEN_FRAME: + os << "type { NEW_TOKEN_FRAME }" << *(frame.new_token_frame); + break; + default: { + QUIC_LOG(ERROR) << "Unknown frame type: " << frame.type; + break; + } + } + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_frame.h b/quic/core/frames/quic_frame.h new file mode 100644 index 0000000..d753862 --- /dev/null +++ b/quic/core/frames/quic_frame.h
@@ -0,0 +1,148 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_ + +#include <ostream> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_application_close_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_message_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicFrame { + QuicFrame(); + // Please keep the constructors in the same order as the union below. + explicit QuicFrame(QuicPaddingFrame padding_frame); + explicit QuicFrame(QuicMtuDiscoveryFrame frame); + explicit QuicFrame(QuicPingFrame frame); + explicit QuicFrame(QuicMaxStreamIdFrame frame); + explicit QuicFrame(QuicStopWaitingFrame frame); + explicit QuicFrame(QuicStreamIdBlockedFrame frame); + explicit QuicFrame(QuicStreamFrame stream_frame); + + explicit QuicFrame(QuicAckFrame* frame); + explicit QuicFrame(QuicRstStreamFrame* frame); + explicit QuicFrame(QuicConnectionCloseFrame* frame); + explicit QuicFrame(QuicGoAwayFrame* frame); + explicit QuicFrame(QuicWindowUpdateFrame* frame); + explicit QuicFrame(QuicBlockedFrame* frame); + explicit QuicFrame(QuicApplicationCloseFrame* frame); + explicit QuicFrame(QuicNewConnectionIdFrame* frame); + explicit QuicFrame(QuicRetireConnectionIdFrame* frame); + explicit QuicFrame(QuicNewTokenFrame* frame); + explicit QuicFrame(QuicPathResponseFrame* frame); + explicit QuicFrame(QuicPathChallengeFrame* frame); + explicit QuicFrame(QuicStopSendingFrame* frame); + explicit QuicFrame(QuicMessageFrame* message_frame); + explicit QuicFrame(QuicCryptoFrame* crypto_frame); + + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os, + const QuicFrame& frame); + + union { + // Inlined frames. + // Overlapping inlined frames have a |type| field at the same 0 offset as + // QuicFrame does for out of line frames below, allowing use of the + // remaining 7 bytes after offset for frame-type specific fields. + QuicPaddingFrame padding_frame; + QuicMtuDiscoveryFrame mtu_discovery_frame; + QuicPingFrame ping_frame; + QuicMaxStreamIdFrame max_stream_id_frame; + QuicStopWaitingFrame stop_waiting_frame; + QuicStreamIdBlockedFrame stream_id_blocked_frame; + QuicStreamFrame stream_frame; + + // Out of line frames. + struct { + QuicFrameType type; + + // TODO(wub): These frames can also be inlined without increasing the size + // of QuicFrame: QuicRstStreamFrame, QuicWindowUpdateFrame, + // QuicBlockedFrame, QuicPathResponseFrame, QuicPathChallengeFrame and + // QuicStopSendingFrame. + union { + QuicAckFrame* ack_frame; + QuicRstStreamFrame* rst_stream_frame; + QuicConnectionCloseFrame* connection_close_frame; + QuicGoAwayFrame* goaway_frame; + QuicWindowUpdateFrame* window_update_frame; + QuicBlockedFrame* blocked_frame; + QuicApplicationCloseFrame* application_close_frame; + QuicNewConnectionIdFrame* new_connection_id_frame; + QuicRetireConnectionIdFrame* retire_connection_id_frame; + QuicPathResponseFrame* path_response_frame; + QuicPathChallengeFrame* path_challenge_frame; + QuicStopSendingFrame* stop_sending_frame; + QuicMessageFrame* message_frame; + QuicCryptoFrame* crypto_frame; + QuicNewTokenFrame* new_token_frame; + }; + }; + }; +}; + +static_assert(sizeof(QuicFrame) <= 24, + "Frames larger than 24 bytes should be referenced by pointer."); +static_assert(offsetof(QuicStreamFrame, type) == offsetof(QuicFrame, type), + "Offset of |type| must match in QuicFrame and QuicStreamFrame"); + +// A inline size of 1 is chosen to optimize the typical use case of +// 1-stream-frame in QuicTransmissionInfo.retransmittable_frames. +typedef QuicInlinedVector<QuicFrame, 1> QuicFrames; + +// Deletes all the sub-frames contained in |frames|. +QUIC_EXPORT_PRIVATE void DeleteFrames(QuicFrames* frames); + +// Delete the sub-frame contained in |frame|. +QUIC_EXPORT_PRIVATE void DeleteFrame(QuicFrame* frame); + +// Deletes all the QuicStreamFrames for the specified |stream_id|. +QUIC_EXPORT_PRIVATE void RemoveFramesForStream(QuicFrames* frames, + QuicStreamId stream_id); + +// Returns true if |type| is a retransmittable control frame. +QUIC_EXPORT_PRIVATE bool IsControlFrame(QuicFrameType type); + +// Returns control_frame_id of |frame|. Returns kInvalidControlFrameId if +// |frame| does not have a valid control_frame_id. +QUIC_EXPORT_PRIVATE QuicControlFrameId +GetControlFrameId(const QuicFrame& frame); + +// Sets control_frame_id of |frame| to |control_frame_id|. +QUIC_EXPORT_PRIVATE void SetControlFrameId(QuicControlFrameId control_frame_id, + QuicFrame* frame); + +// Returns a copy of |frame|. +QUIC_EXPORT_PRIVATE QuicFrame +CopyRetransmittableControlFrame(const QuicFrame& frame); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_
diff --git a/quic/core/frames/quic_frames_test.cc b/quic/core/frames/quic_frames_test.cc new file mode 100644 index 0000000..986b3c5 --- /dev/null +++ b/quic/core/frames/quic_frames_test.cc
@@ -0,0 +1,638 @@ +// 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/frames/quic_ack_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +class QuicFramesTest : public QuicTest {}; + +TEST_F(QuicFramesTest, AckFrameToString) { + QuicAckFrame frame; + frame.largest_acked = QuicPacketNumber(5); + frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(3); + frame.packets.Add(QuicPacketNumber(4)); + frame.packets.Add(QuicPacketNumber(5)); + frame.received_packet_times = { + {QuicPacketNumber(6), + QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}}; + std::ostringstream stream; + stream << frame; + EXPECT_EQ( + "{ largest_acked: 5, ack_delay_time: 3, packets: [ 4 5 ], " + "received_packets: [ 6 at 7 ], ecn_counters_populated: 0 }\n", + stream.str()); + QuicFrame quic_frame(&frame); + EXPECT_FALSE(IsControlFrame(quic_frame.type)); +} + +TEST_F(QuicFramesTest, BigAckFrameToString) { + QuicAckFrame frame; + frame.largest_acked = QuicPacketNumber(500); + frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(3); + frame.packets.AddRange(QuicPacketNumber(4), QuicPacketNumber(501)); + frame.received_packet_times = { + {QuicPacketNumber(500), + QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}}; + std::ostringstream stream; + stream << frame; + EXPECT_EQ( + "{ largest_acked: 500, ack_delay_time: 3, packets: [ 4...500 ], " + "received_packets: [ 500 at 7 ], ecn_counters_populated: 0 }\n", + stream.str()); + QuicFrame quic_frame(&frame); + EXPECT_FALSE(IsControlFrame(quic_frame.type)); +} + +TEST_F(QuicFramesTest, PaddingFrameToString) { + QuicPaddingFrame frame; + frame.num_padding_bytes = 1; + std::ostringstream stream; + stream << frame; + EXPECT_EQ("{ num_padding_bytes: 1 }\n", stream.str()); + QuicFrame quic_frame(frame); + EXPECT_FALSE(IsControlFrame(quic_frame.type)); +} + +TEST_F(QuicFramesTest, RstStreamFrameToString) { + QuicRstStreamFrame rst_stream; + QuicFrame frame(&rst_stream); + SetControlFrameId(1, &frame); + EXPECT_EQ(1u, GetControlFrameId(frame)); + rst_stream.stream_id = 1; + rst_stream.error_code = QUIC_STREAM_CANCELLED; + std::ostringstream stream; + stream << rst_stream; + EXPECT_EQ("{ control_frame_id: 1, stream_id: 1, error_code: 6 }\n", + stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, StopSendingFrameToString) { + QuicStopSendingFrame stop_sending; + QuicFrame frame(&stop_sending); + SetControlFrameId(1, &frame); + EXPECT_EQ(1u, GetControlFrameId(frame)); + stop_sending.stream_id = 321; + stop_sending.application_error_code = QUIC_STREAM_CANCELLED; + std::ostringstream stream; + stream << stop_sending; + EXPECT_EQ( + "{ control_frame_id: 1, stream_id: 321, application_error_code: 6 }\n", + stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, StreamIdBlockedFrameToString) { + QuicStreamIdBlockedFrame stream_id_blocked; + QuicFrame frame(stream_id_blocked); + SetControlFrameId(1, &frame); + EXPECT_EQ(1u, GetControlFrameId(frame)); + // QuicStreamIdBlocked is copied into a QuicFrame (as opposed to putting a + // pointer to it into QuicFrame) so need to work with the copy in |frame| and + // not the original one, stream_id_blocked. + frame.stream_id_blocked_frame.stream_id = 321; + std::ostringstream stream; + stream << frame.stream_id_blocked_frame; + EXPECT_EQ("{ control_frame_id: 1, stream id: 321 }\n", stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, MaxStreamIdFrameToString) { + QuicMaxStreamIdFrame max_stream_id; + QuicFrame frame(max_stream_id); + SetControlFrameId(1, &frame); + EXPECT_EQ(1u, GetControlFrameId(frame)); + // QuicMaxStreamId is copied into a QuicFrame (as opposed to putting a + // pointer to it into QuicFrame) so need to work with the copy in |frame| and + // not the original one, max_stream_id. + frame.max_stream_id_frame.max_stream_id = 321; + std::ostringstream stream; + stream << frame.max_stream_id_frame; + EXPECT_EQ("{ control_frame_id: 1, stream_id: 321 }\n", stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, ConnectionCloseFrameToString) { + QuicConnectionCloseFrame frame; + frame.error_code = QUIC_NETWORK_IDLE_TIMEOUT; + frame.error_details = "No recent network activity."; + std::ostringstream stream; + stream << frame; + EXPECT_EQ( + "{ error_code: 25, error_details: 'No recent network activity.', " + "frame_type: 0" + "}\n", + stream.str()); + QuicFrame quic_frame(&frame); + EXPECT_FALSE(IsControlFrame(quic_frame.type)); +} + +TEST_F(QuicFramesTest, GoAwayFrameToString) { + QuicGoAwayFrame goaway_frame; + QuicFrame frame(&goaway_frame); + SetControlFrameId(2, &frame); + EXPECT_EQ(2u, GetControlFrameId(frame)); + goaway_frame.error_code = QUIC_NETWORK_IDLE_TIMEOUT; + goaway_frame.last_good_stream_id = 2; + goaway_frame.reason_phrase = "Reason"; + std::ostringstream stream; + stream << goaway_frame; + EXPECT_EQ( + "{ control_frame_id: 2, error_code: 25, last_good_stream_id: 2, " + "reason_phrase: " + "'Reason' }\n", + stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, WindowUpdateFrameToString) { + QuicWindowUpdateFrame window_update; + QuicFrame frame(&window_update); + SetControlFrameId(3, &frame); + EXPECT_EQ(3u, GetControlFrameId(frame)); + std::ostringstream stream; + window_update.stream_id = 1; + window_update.byte_offset = 2; + stream << window_update; + EXPECT_EQ("{ control_frame_id: 3, stream_id: 1, byte_offset: 2 }\n", + stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, BlockedFrameToString) { + QuicBlockedFrame blocked; + QuicFrame frame(&blocked); + SetControlFrameId(4, &frame); + EXPECT_EQ(4u, GetControlFrameId(frame)); + blocked.stream_id = 1; + std::ostringstream stream; + stream << blocked; + EXPECT_EQ("{ control_frame_id: 4, stream_id: 1 }\n", stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, PingFrameToString) { + QuicPingFrame ping; + QuicFrame frame(ping); + SetControlFrameId(5, &frame); + EXPECT_EQ(5u, GetControlFrameId(frame)); + std::ostringstream stream; + stream << frame.ping_frame; + EXPECT_EQ("{ control_frame_id: 5 }\n", stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, StreamFrameToString) { + QuicStreamFrame frame; + frame.stream_id = 1; + frame.fin = false; + frame.offset = 2; + frame.data_length = 3; + std::ostringstream stream; + stream << frame; + EXPECT_EQ("{ stream_id: 1, fin: 0, offset: 2, length: 3 }\n", stream.str()); + EXPECT_FALSE(IsControlFrame(frame.type)); +} + +TEST_F(QuicFramesTest, StopWaitingFrameToString) { + QuicStopWaitingFrame frame; + frame.least_unacked = QuicPacketNumber(2); + std::ostringstream stream; + stream << frame; + EXPECT_EQ("{ least_unacked: 2 }\n", stream.str()); + QuicFrame quic_frame(frame); + EXPECT_FALSE(IsControlFrame(quic_frame.type)); +} + +TEST_F(QuicFramesTest, IsAwaitingPacket) { + QuicAckFrame ack_frame1; + ack_frame1.largest_acked = QuicPacketNumber(10u); + ack_frame1.packets.AddRange(QuicPacketNumber(1), QuicPacketNumber(11)); + EXPECT_TRUE( + IsAwaitingPacket(ack_frame1, QuicPacketNumber(11u), QuicPacketNumber())); + EXPECT_FALSE( + IsAwaitingPacket(ack_frame1, QuicPacketNumber(1u), QuicPacketNumber())); + + ack_frame1.packets.Add(QuicPacketNumber(12)); + EXPECT_TRUE( + IsAwaitingPacket(ack_frame1, QuicPacketNumber(11u), QuicPacketNumber())); + + QuicAckFrame ack_frame2; + ack_frame2.largest_acked = QuicPacketNumber(100u); + ack_frame2.packets.AddRange(QuicPacketNumber(21), QuicPacketNumber(100)); + EXPECT_FALSE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(11u), + QuicPacketNumber(20u))); + EXPECT_FALSE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(80u), + QuicPacketNumber(20u))); + EXPECT_TRUE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(101u), + QuicPacketNumber(20u))); + + ack_frame2.packets.AddRange(QuicPacketNumber(102), QuicPacketNumber(200)); + EXPECT_TRUE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(101u), + QuicPacketNumber(20u))); +} + +TEST_F(QuicFramesTest, AddPacket) { + QuicAckFrame ack_frame1; + ack_frame1.packets.Add(QuicPacketNumber(1)); + ack_frame1.packets.Add(QuicPacketNumber(99)); + + EXPECT_EQ(2u, ack_frame1.packets.NumIntervals()); + EXPECT_EQ(QuicPacketNumber(1u), ack_frame1.packets.Min()); + EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals; + expected_intervals.emplace_back( + QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2))); + expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(99), QuicPacketNumber(100))); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals, actual_intervals); + + ack_frame1.packets.Add(QuicPacketNumber(20)); + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals2; + expected_intervals2.emplace_back( + QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2))); + expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(20), QuicPacketNumber(21))); + expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(99), QuicPacketNumber(100))); + + EXPECT_EQ(3u, ack_frame1.packets.NumIntervals()); + EXPECT_EQ(expected_intervals2, actual_intervals2); + + ack_frame1.packets.Add(QuicPacketNumber(19)); + ack_frame1.packets.Add(QuicPacketNumber(21)); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals3( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals3; + expected_intervals3.emplace_back( + QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2))); + expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(19), QuicPacketNumber(22))); + expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(99), QuicPacketNumber(100))); + + EXPECT_EQ(expected_intervals3, actual_intervals3); + + ack_frame1.packets.Add(QuicPacketNumber(20)); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals4( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals3, actual_intervals4); + + QuicAckFrame ack_frame2; + ack_frame2.packets.Add(QuicPacketNumber(20)); + ack_frame2.packets.Add(QuicPacketNumber(40)); + ack_frame2.packets.Add(QuicPacketNumber(60)); + ack_frame2.packets.Add(QuicPacketNumber(10)); + ack_frame2.packets.Add(QuicPacketNumber(80)); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals5( + ack_frame2.packets.begin(), ack_frame2.packets.end()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals5; + expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(10), QuicPacketNumber(11))); + expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(20), QuicPacketNumber(21))); + expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(40), QuicPacketNumber(41))); + expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(60), QuicPacketNumber(61))); + expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(80), QuicPacketNumber(81))); + + EXPECT_EQ(expected_intervals5, actual_intervals5); +} + +TEST_F(QuicFramesTest, AddInterval) { + QuicAckFrame ack_frame1; + ack_frame1.packets.AddRange(QuicPacketNumber(1), QuicPacketNumber(10)); + ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(100)); + + EXPECT_EQ(2u, ack_frame1.packets.NumIntervals()); + EXPECT_EQ(QuicPacketNumber(1u), ack_frame1.packets.Min()); + EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals; + expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(1), QuicPacketNumber(10))); + expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(50), QuicPacketNumber(100))); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals, actual_intervals); + + // Ensure adding a range within the existing ranges fails. + EXPECT_QUIC_BUG( + ack_frame1.packets.AddRange(QuicPacketNumber(20), QuicPacketNumber(30)), + ""); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals2; + expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(1), QuicPacketNumber(10))); + expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(50), QuicPacketNumber(100))); + + EXPECT_EQ(expected_intervals2.size(), ack_frame1.packets.NumIntervals()); + EXPECT_EQ(expected_intervals2, actual_intervals2); + + // Add ranges at both ends. + QuicAckFrame ack_frame2; + ack_frame2.packets.AddRange(QuicPacketNumber(20), QuicPacketNumber(25)); + ack_frame2.packets.AddRange(QuicPacketNumber(40), QuicPacketNumber(45)); + ack_frame2.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(65)); + ack_frame2.packets.AddRange(QuicPacketNumber(10), QuicPacketNumber(15)); + ack_frame2.packets.AddRange(QuicPacketNumber(80), QuicPacketNumber(85)); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals8( + ack_frame2.packets.begin(), ack_frame2.packets.end()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals8; + expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(10), QuicPacketNumber(15))); + expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(20), QuicPacketNumber(25))); + expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(40), QuicPacketNumber(45))); + expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(60), QuicPacketNumber(65))); + expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(80), QuicPacketNumber(85))); + + EXPECT_EQ(expected_intervals8, actual_intervals8); +} + +TEST_F(QuicFramesTest, AddAdjacentForward) { + QuicAckFrame ack_frame1; + ack_frame1.packets.Add(QuicPacketNumber(49)); + ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(60)); + ack_frame1.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(70)); + ack_frame1.packets.AddRange(QuicPacketNumber(70), QuicPacketNumber(100)); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals; + expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(49), QuicPacketNumber(100))); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals, actual_intervals); +} + +TEST_F(QuicFramesTest, AddAdjacentReverse) { + QuicAckFrame ack_frame1; + ack_frame1.packets.AddRange(QuicPacketNumber(70), QuicPacketNumber(100)); + ack_frame1.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(70)); + ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(60)); + ack_frame1.packets.Add(QuicPacketNumber(49)); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals; + expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(49), QuicPacketNumber(100))); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals, actual_intervals); +} + +TEST_F(QuicFramesTest, RemoveSmallestInterval) { + QuicAckFrame ack_frame1; + ack_frame1.largest_acked = QuicPacketNumber(100u); + ack_frame1.packets.AddRange(QuicPacketNumber(51), QuicPacketNumber(60)); + ack_frame1.packets.AddRange(QuicPacketNumber(71), QuicPacketNumber(80)); + ack_frame1.packets.AddRange(QuicPacketNumber(91), QuicPacketNumber(100)); + ack_frame1.packets.RemoveSmallestInterval(); + EXPECT_EQ(2u, ack_frame1.packets.NumIntervals()); + EXPECT_EQ(QuicPacketNumber(71u), ack_frame1.packets.Min()); + EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max()); + + ack_frame1.packets.RemoveSmallestInterval(); + EXPECT_EQ(1u, ack_frame1.packets.NumIntervals()); + EXPECT_EQ(QuicPacketNumber(91u), ack_frame1.packets.Min()); + EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max()); +} + +class PacketNumberQueueTest : public QuicTest {}; + +// Tests that a queue contains the expected data after calls to Add(). +TEST_F(PacketNumberQueueTest, AddRange) { + PacketNumberQueue queue; + queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(51)); + queue.Add(QuicPacketNumber(53)); + + EXPECT_FALSE(queue.Contains(QuicPacketNumber())); + for (int i = 1; i < 51; ++i) { + EXPECT_TRUE(queue.Contains(QuicPacketNumber(i))); + } + EXPECT_FALSE(queue.Contains(QuicPacketNumber(51))); + EXPECT_FALSE(queue.Contains(QuicPacketNumber(52))); + EXPECT_TRUE(queue.Contains(QuicPacketNumber(53))); + EXPECT_FALSE(queue.Contains(QuicPacketNumber(54))); + EXPECT_EQ(51u, queue.NumPacketsSlow()); + EXPECT_EQ(QuicPacketNumber(1u), queue.Min()); + EXPECT_EQ(QuicPacketNumber(53u), queue.Max()); + + queue.Add(QuicPacketNumber(70)); + EXPECT_EQ(QuicPacketNumber(70u), queue.Max()); +} + +// Tests Contains function +TEST_F(PacketNumberQueueTest, Contains) { + PacketNumberQueue queue; + EXPECT_FALSE(queue.Contains(QuicPacketNumber())); + queue.AddRange(QuicPacketNumber(5), QuicPacketNumber(10)); + queue.Add(QuicPacketNumber(20)); + + for (int i = 1; i < 5; ++i) { + EXPECT_FALSE(queue.Contains(QuicPacketNumber(i))); + } + + for (int i = 5; i < 10; ++i) { + EXPECT_TRUE(queue.Contains(QuicPacketNumber(i))); + } + for (int i = 10; i < 20; ++i) { + EXPECT_FALSE(queue.Contains(QuicPacketNumber(i))); + } + EXPECT_TRUE(queue.Contains(QuicPacketNumber(20))); + EXPECT_FALSE(queue.Contains(QuicPacketNumber(21))); + + PacketNumberQueue queue2; + EXPECT_FALSE(queue2.Contains(QuicPacketNumber(1))); + for (int i = 1; i < 51; ++i) { + queue2.Add(QuicPacketNumber(2 * i)); + } + EXPECT_FALSE(queue2.Contains(QuicPacketNumber())); + for (int i = 1; i < 51; ++i) { + if (i % 2 == 0) { + EXPECT_TRUE(queue2.Contains(QuicPacketNumber(i))); + } else { + EXPECT_FALSE(queue2.Contains(QuicPacketNumber(i))); + } + } + EXPECT_FALSE(queue2.Contains(QuicPacketNumber(101))); +} + +// Tests that a queue contains the expected data after calls to RemoveUpTo(). +TEST_F(PacketNumberQueueTest, Removal) { + PacketNumberQueue queue; + EXPECT_FALSE(queue.Contains(QuicPacketNumber(51))); + queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100)); + + EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(51))); + EXPECT_FALSE(queue.RemoveUpTo(QuicPacketNumber(51))); + + EXPECT_FALSE(queue.Contains(QuicPacketNumber())); + for (int i = 1; i < 51; ++i) { + EXPECT_FALSE(queue.Contains(QuicPacketNumber(i))); + } + for (int i = 51; i < 100; ++i) { + EXPECT_TRUE(queue.Contains(QuicPacketNumber(i))); + } + EXPECT_EQ(49u, queue.NumPacketsSlow()); + EXPECT_EQ(QuicPacketNumber(51u), queue.Min()); + EXPECT_EQ(QuicPacketNumber(99u), queue.Max()); + + PacketNumberQueue queue2; + queue2.AddRange(QuicPacketNumber(1), QuicPacketNumber(5)); + EXPECT_TRUE(queue2.RemoveUpTo(QuicPacketNumber(3))); + EXPECT_TRUE(queue2.RemoveUpTo(QuicPacketNumber(50))); + EXPECT_TRUE(queue2.Empty()); +} + +// Tests that a queue is empty when all of its elements are removed. +TEST_F(PacketNumberQueueTest, Empty) { + PacketNumberQueue queue; + EXPECT_TRUE(queue.Empty()); + EXPECT_EQ(0u, queue.NumPacketsSlow()); + + queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100)); + EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(100))); + EXPECT_TRUE(queue.Empty()); + EXPECT_EQ(0u, queue.NumPacketsSlow()); +} + +// Tests that logging the state of a PacketNumberQueue does not crash. +TEST_F(PacketNumberQueueTest, LogDoesNotCrash) { + std::ostringstream oss; + PacketNumberQueue queue; + oss << queue; + + queue.Add(QuicPacketNumber(1)); + queue.AddRange(QuicPacketNumber(50), QuicPacketNumber(100)); + oss << queue; +} + +// Tests that the iterators returned from a packet queue iterate over the queue. +TEST_F(PacketNumberQueueTest, Iterators) { + PacketNumberQueue queue; + queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100)); + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals( + queue.begin(), queue.end()); + + PacketNumberQueue queue2; + for (int i = 1; i < 100; i++) { + queue2.AddRange(QuicPacketNumber(i), QuicPacketNumber(i + 1)); + } + + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2( + queue2.begin(), queue2.end()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals; + expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(1), QuicPacketNumber(100))); + EXPECT_EQ(expected_intervals, actual_intervals); + EXPECT_EQ(expected_intervals, actual_intervals2); + EXPECT_EQ(actual_intervals, actual_intervals2); +} + +TEST_F(PacketNumberQueueTest, ReversedIterators) { + PacketNumberQueue queue; + queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100)); + PacketNumberQueue queue2; + for (int i = 1; i < 100; i++) { + queue2.AddRange(QuicPacketNumber(i), QuicPacketNumber(i + 1)); + } + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals( + queue.rbegin(), queue.rend()); + const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2( + queue2.rbegin(), queue2.rend()); + + std::vector<QuicInterval<QuicPacketNumber>> expected_intervals; + expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>( + QuicPacketNumber(1), QuicPacketNumber(100))); + + EXPECT_EQ(expected_intervals, actual_intervals); + EXPECT_EQ(expected_intervals, actual_intervals2); + EXPECT_EQ(actual_intervals, actual_intervals2); + + PacketNumberQueue queue3; + for (int i = 1; i < 20; i++) { + queue3.Add(QuicPacketNumber(2 * i)); + } + + auto begin = queue3.begin(); + auto end = queue3.end(); + --end; + auto rbegin = queue3.rbegin(); + auto rend = queue3.rend(); + --rend; + + EXPECT_EQ(*begin, *rend); + EXPECT_EQ(*rbegin, *end); +} + +TEST_F(PacketNumberQueueTest, IntervalLengthAndRemoveInterval) { + PacketNumberQueue queue; + queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(10)); + queue.AddRange(QuicPacketNumber(20), QuicPacketNumber(30)); + queue.AddRange(QuicPacketNumber(40), QuicPacketNumber(50)); + EXPECT_EQ(3u, queue.NumIntervals()); + EXPECT_EQ(10u, queue.LastIntervalLength()); + + EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(25))); + EXPECT_EQ(2u, queue.NumIntervals()); + EXPECT_EQ(10u, queue.LastIntervalLength()); + EXPECT_EQ(QuicPacketNumber(25u), queue.Min()); + EXPECT_EQ(QuicPacketNumber(49u), queue.Max()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/frames/quic_goaway_frame.cc b/quic/core/frames/quic_goaway_frame.cc new file mode 100644 index 0000000..ff034f0 --- /dev/null +++ b/quic/core/frames/quic_goaway_frame.cc
@@ -0,0 +1,34 @@ +// 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/frames/quic_goaway_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicGoAwayFrame::QuicGoAwayFrame() + : control_frame_id(kInvalidControlFrameId), + error_code(QUIC_NO_ERROR), + last_good_stream_id(0) {} + +QuicGoAwayFrame::QuicGoAwayFrame(QuicControlFrameId control_frame_id, + QuicErrorCode error_code, + QuicStreamId last_good_stream_id, + const QuicString& reason) + : control_frame_id(control_frame_id), + error_code(error_code), + last_good_stream_id(last_good_stream_id), + reason_phrase(reason) {} + +std::ostream& operator<<(std::ostream& os, + const QuicGoAwayFrame& goaway_frame) { + os << "{ control_frame_id: " << goaway_frame.control_frame_id + << ", error_code: " << goaway_frame.error_code + << ", last_good_stream_id: " << goaway_frame.last_good_stream_id + << ", reason_phrase: '" << goaway_frame.reason_phrase << "' }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_goaway_frame.h b/quic/core/frames/quic_goaway_frame.h new file mode 100644 index 0000000..085e915 --- /dev/null +++ b/quic/core/frames/quic_goaway_frame.h
@@ -0,0 +1,36 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_GOAWAY_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_GOAWAY_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicGoAwayFrame { + QuicGoAwayFrame(); + QuicGoAwayFrame(QuicControlFrameId control_frame_id, + QuicErrorCode error_code, + QuicStreamId last_good_stream_id, + const QuicString& reason); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const QuicGoAwayFrame& g); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + QuicErrorCode error_code; + QuicStreamId last_good_stream_id; + QuicString reason_phrase; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_GOAWAY_FRAME_H_
diff --git a/quic/core/frames/quic_inlined_frame.h b/quic/core/frames/quic_inlined_frame.h new file mode 100644 index 0000000..08c4869 --- /dev/null +++ b/quic/core/frames/quic_inlined_frame.h
@@ -0,0 +1,30 @@ +// Copyright (c) 2018 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_FRAMES_QUIC_INLINED_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_INLINED_FRAME_H_ + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// QuicInlinedFrame is the base class of all frame types that is inlined in the +// QuicFrame class. It gurantees all inlined frame types contain a 'type' field +// at offset 0, such that QuicFrame.type can get the correct frame type for both +// inline and out-of-line frame types. +template <typename DerivedT> +struct QUIC_EXPORT_PRIVATE QuicInlinedFrame { + QuicInlinedFrame(QuicFrameType type) : type(type) { + static_assert(offsetof(DerivedT, type) == 0, + "type must be the first field."); + static_assert(sizeof(DerivedT) <= 24, + "Frames larger than 24 bytes should not be inlined."); + } + QuicFrameType type; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_INLINED_FRAME_H_
diff --git a/quic/core/frames/quic_max_stream_id_frame.cc b/quic/core/frames/quic_max_stream_id_frame.cc new file mode 100644 index 0000000..19270e8 --- /dev/null +++ b/quic/core/frames/quic_max_stream_id_frame.cc
@@ -0,0 +1,25 @@ +// 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/frames/quic_max_stream_id_frame.h" + +namespace quic { + +QuicMaxStreamIdFrame::QuicMaxStreamIdFrame() + : QuicInlinedFrame(MAX_STREAM_ID_FRAME), + control_frame_id(kInvalidControlFrameId) {} + +QuicMaxStreamIdFrame::QuicMaxStreamIdFrame(QuicControlFrameId control_frame_id, + QuicStreamId max_stream_id) + : QuicInlinedFrame(MAX_STREAM_ID_FRAME), + control_frame_id(control_frame_id), + max_stream_id(max_stream_id) {} + +std::ostream& operator<<(std::ostream& os, const QuicMaxStreamIdFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", stream_id: " << frame.max_stream_id << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_max_stream_id_frame.h b/quic/core/frames/quic_max_stream_id_frame.h new file mode 100644 index 0000000..6177687 --- /dev/null +++ b/quic/core/frames/quic_max_stream_id_frame.h
@@ -0,0 +1,40 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_MAX_STREAM_ID_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_MAX_STREAM_ID_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// IETF format MAX_STREAM_ID frame. +// This frame is used by the sender to inform the peer of the largest +// stream id that the peer may open and that the sender will accept. +struct QUIC_EXPORT_PRIVATE QuicMaxStreamIdFrame + : public QuicInlinedFrame<QuicMaxStreamIdFrame> { + QuicMaxStreamIdFrame(); + QuicMaxStreamIdFrame(QuicControlFrameId control_frame_id, + QuicStreamId max_stream_id); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicMaxStreamIdFrame& frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + // The maximum stream id to support. + QuicStreamId max_stream_id; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_MAX_STREAM_ID_FRAME_H_
diff --git a/quic/core/frames/quic_message_frame.cc b/quic/core/frames/quic_message_frame.cc new file mode 100644 index 0000000..c0de272 --- /dev/null +++ b/quic/core/frames/quic_message_frame.cc
@@ -0,0 +1,29 @@ +// Copyright (c) 2018 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/frames/quic_message_frame.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicMessageFrame::QuicMessageFrame() + : message_id(0), data(nullptr), message_length(0) {} + +QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id) + : message_id(message_id), data(nullptr), message_length(0) {} + +QuicMessageFrame::QuicMessageFrame(const char* data, QuicPacketLength length) + : message_id(0), data(data), message_length(length) {} + +QuicMessageFrame::~QuicMessageFrame() {} + +std::ostream& operator<<(std::ostream& os, const QuicMessageFrame& s) { + os << " message_id: " << s.message_id + << ", message_length: " << s.message_length << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_message_frame.h b/quic/core/frames/quic_message_frame.h new file mode 100644 index 0000000..4accff2 --- /dev/null +++ b/quic/core/frames/quic_message_frame.h
@@ -0,0 +1,49 @@ +// Copyright (c) 2018 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_FRAMES_QUIC_MESSAGE_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_ + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +typedef QuicInlinedVector<QuicMemSlice, 1> QuicMessageData; + +struct QUIC_EXPORT_PRIVATE QuicMessageFrame { + QuicMessageFrame(); + explicit QuicMessageFrame(QuicMessageId message_id); + QuicMessageFrame(const char* data, QuicPacketLength length); + + QuicMessageFrame(const QuicMessageFrame& other) = delete; + QuicMessageFrame& operator=(const QuicMessageFrame& other) = delete; + + QuicMessageFrame(QuicMessageFrame&& other) = default; + QuicMessageFrame& operator=(QuicMessageFrame&& other) = default; + + ~QuicMessageFrame(); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicMessageFrame& s); + + // message_id is only used on the sender side and does not get serialized on + // wire. + QuicMessageId message_id; + // Not owned, only used on read path. + const char* data; + // Total length of message_data, must be fit into one packet. + QuicPacketLength message_length; + + // The actual message data which is reference counted, used on write path. + QuicMessageData message_data; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_
diff --git a/quic/core/frames/quic_mtu_discovery_frame.h b/quic/core/frames/quic_mtu_discovery_frame.h new file mode 100644 index 0000000..330df21 --- /dev/null +++ b/quic/core/frames/quic_mtu_discovery_frame.h
@@ -0,0 +1,23 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_ + +#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A path MTU discovery frame contains no payload and is serialized as a ping +// frame. +struct QUIC_EXPORT_PRIVATE QuicMtuDiscoveryFrame + : public QuicInlinedFrame<QuicMtuDiscoveryFrame> { + QuicMtuDiscoveryFrame() : QuicInlinedFrame(MTU_DISCOVERY_FRAME) {} +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_
diff --git a/quic/core/frames/quic_new_connection_id_frame.cc b/quic/core/frames/quic_new_connection_id_frame.cc new file mode 100644 index 0000000..b7f63e2 --- /dev/null +++ b/quic/core/frames/quic_new_connection_id_frame.cc
@@ -0,0 +1,33 @@ +// 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/frames/quic_new_connection_id_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +QuicNewConnectionIdFrame::QuicNewConnectionIdFrame() + : control_frame_id(kInvalidControlFrameId), + connection_id(EmptyQuicConnectionId()), + sequence_number(0) {} + +QuicNewConnectionIdFrame::QuicNewConnectionIdFrame( + QuicControlFrameId control_frame_id, + QuicConnectionId connection_id, + QuicConnectionIdSequenceNumber sequence_number, + const QuicUint128 stateless_reset_token) + : control_frame_id(control_frame_id), + connection_id(connection_id), + sequence_number(sequence_number), + stateless_reset_token(stateless_reset_token) {} + +std::ostream& operator<<(std::ostream& os, + const QuicNewConnectionIdFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", connection_id: " << frame.connection_id + << ", sequence_number: " << frame.sequence_number << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_new_connection_id_frame.h b/quic/core/frames/quic_new_connection_id_frame.h new file mode 100644 index 0000000..1948d22 --- /dev/null +++ b/quic/core/frames/quic_new_connection_id_frame.h
@@ -0,0 +1,37 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_CONNECTION_ID_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_CONNECTION_ID_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicNewConnectionIdFrame { + QuicNewConnectionIdFrame(); + QuicNewConnectionIdFrame(QuicControlFrameId control_frame_id, + QuicConnectionId connection_id, + QuicConnectionIdSequenceNumber sequence_number, + const QuicUint128 stateless_reset_token); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicNewConnectionIdFrame& frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + QuicConnectionId connection_id; + QuicConnectionIdSequenceNumber sequence_number; + QuicUint128 stateless_reset_token; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_CONNECTION_ID_FRAME_H_
diff --git a/quic/core/frames/quic_new_token_frame.cc b/quic/core/frames/quic_new_token_frame.cc new file mode 100644 index 0000000..c2f4e74 --- /dev/null +++ b/quic/core/frames/quic_new_token_frame.cc
@@ -0,0 +1,24 @@ +// Copyright (c) 2018 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/frames/quic_new_token_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicNewTokenFrame::QuicNewTokenFrame() + : control_frame_id(kInvalidControlFrameId) {} + +QuicNewTokenFrame::QuicNewTokenFrame(QuicControlFrameId control_frame_id, + QuicString token) + : control_frame_id(control_frame_id), token(token) {} + +std::ostream& operator<<(std::ostream& os, const QuicNewTokenFrame& s) { + os << "{ control_frame_id: " << s.control_frame_id << ", token: " << s.token + << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_new_token_frame.h b/quic/core/frames/quic_new_token_frame.h new file mode 100644 index 0000000..6fb0ea4 --- /dev/null +++ b/quic/core/frames/quic_new_token_frame.h
@@ -0,0 +1,37 @@ +// Copyright (c) 2018 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_FRAMES_QUIC_NEW_TOKEN_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_ + +#include <memory> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicNewTokenFrame { + QuicNewTokenFrame(); + QuicNewTokenFrame(QuicControlFrameId control_frame_id, QuicString token); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicNewTokenFrame& s); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + QuicString token; +}; +static_assert(sizeof(QuicNewTokenFrame) <= 64, + "Keep the QuicNewTokenFrame size to a cacheline."); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_
diff --git a/quic/core/frames/quic_padding_frame.cc b/quic/core/frames/quic_padding_frame.cc new file mode 100644 index 0000000..098a664 --- /dev/null +++ b/quic/core/frames/quic_padding_frame.cc
@@ -0,0 +1,15 @@ +// 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/frames/quic_padding_frame.h" + +namespace quic { + +std::ostream& operator<<(std::ostream& os, + const QuicPaddingFrame& padding_frame) { + os << "{ num_padding_bytes: " << padding_frame.num_padding_bytes << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_padding_frame.h b/quic/core/frames/quic_padding_frame.h new file mode 100644 index 0000000..03e0a40 --- /dev/null +++ b/quic/core/frames/quic_padding_frame.h
@@ -0,0 +1,35 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_PADDING_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_PADDING_FRAME_H_ + +#include <cstdint> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A padding frame contains no payload. +struct QUIC_EXPORT_PRIVATE QuicPaddingFrame + : public QuicInlinedFrame<QuicPaddingFrame> { + QuicPaddingFrame() : QuicInlinedFrame(PADDING_FRAME), num_padding_bytes(-1) {} + explicit QuicPaddingFrame(int num_padding_bytes) + : QuicInlinedFrame(PADDING_FRAME), num_padding_bytes(num_padding_bytes) {} + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicPaddingFrame& s); + + // -1: full padding to the end of a max-sized packet + // otherwise: only pad up to num_padding_bytes bytes + int num_padding_bytes; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PADDING_FRAME_H_
diff --git a/quic/core/frames/quic_path_challenge_frame.cc b/quic/core/frames/quic_path_challenge_frame.cc new file mode 100644 index 0000000..c1ca6a8 --- /dev/null +++ b/quic/core/frames/quic_path_challenge_frame.cc
@@ -0,0 +1,37 @@ +// Copyright (c) 2018 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/frames/quic_path_challenge_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" + +namespace quic { + +QuicPathChallengeFrame::QuicPathChallengeFrame() + : control_frame_id(kInvalidControlFrameId) {} + +QuicPathChallengeFrame::QuicPathChallengeFrame( + QuicControlFrameId control_frame_id, + const QuicPathFrameBuffer& data_buff) + : control_frame_id(control_frame_id) { + memcpy(data_buffer.data(), data_buff.data(), data_buffer.size()); +} + +QuicPathChallengeFrame::~QuicPathChallengeFrame() {} + +std::ostream& operator<<(std::ostream& os, + const QuicPathChallengeFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", data: " << static_cast<unsigned>(frame.data_buffer[0]) << " " + << static_cast<unsigned>(frame.data_buffer[1]) << " " + << static_cast<unsigned>(frame.data_buffer[2]) << " " + << static_cast<unsigned>(frame.data_buffer[3]) << " " + << static_cast<unsigned>(frame.data_buffer[4]) << " " + << static_cast<unsigned>(frame.data_buffer[5]) << " " + << static_cast<unsigned>(frame.data_buffer[6]) << " " + << static_cast<unsigned>(frame.data_buffer[7]) << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_path_challenge_frame.h b/quic/core/frames/quic_path_challenge_frame.h new file mode 100644 index 0000000..46a010a --- /dev/null +++ b/quic/core/frames/quic_path_challenge_frame.h
@@ -0,0 +1,36 @@ +// Copyright (c) 2018 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_FRAMES_QUIC_PATH_CHALLENGE_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_CHALLENGE_FRAME_H_ + +#include <memory> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +// Size of the entire IETF Quic Path Challenge frame. +const size_t kQuicPathChallengeFrameSize = kQuicPathFrameBufferSize; + +struct QUIC_EXPORT_PRIVATE QuicPathChallengeFrame { + QuicPathChallengeFrame(); + QuicPathChallengeFrame(QuicControlFrameId control_frame_id, + const QuicPathFrameBuffer& data_buff); + ~QuicPathChallengeFrame(); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicPathChallengeFrame& frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + QuicPathFrameBuffer data_buffer; +}; +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_CHALLENGE_FRAME_H_
diff --git a/quic/core/frames/quic_path_response_frame.cc b/quic/core/frames/quic_path_response_frame.cc new file mode 100644 index 0000000..85e9712 --- /dev/null +++ b/quic/core/frames/quic_path_response_frame.cc
@@ -0,0 +1,36 @@ +// Copyright (c) 2018 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/frames/quic_path_response_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" + +namespace quic { + +QuicPathResponseFrame::QuicPathResponseFrame() + : control_frame_id(kInvalidControlFrameId) {} + +QuicPathResponseFrame::QuicPathResponseFrame( + QuicControlFrameId control_frame_id, + const QuicPathFrameBuffer& data_buff) + : control_frame_id(control_frame_id) { + memcpy(data_buffer.data(), data_buff.data(), data_buffer.size()); +} + +QuicPathResponseFrame::~QuicPathResponseFrame() {} + +std::ostream& operator<<(std::ostream& os, const QuicPathResponseFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", data: " << static_cast<unsigned>(frame.data_buffer[0]) << " " + << static_cast<unsigned>(frame.data_buffer[1]) << " " + << static_cast<unsigned>(frame.data_buffer[2]) << " " + << static_cast<unsigned>(frame.data_buffer[3]) << " " + << static_cast<unsigned>(frame.data_buffer[4]) << " " + << static_cast<unsigned>(frame.data_buffer[5]) << " " + << static_cast<unsigned>(frame.data_buffer[6]) << " " + << static_cast<unsigned>(frame.data_buffer[7]) << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_path_response_frame.h b/quic/core/frames/quic_path_response_frame.h new file mode 100644 index 0000000..e953ad8 --- /dev/null +++ b/quic/core/frames/quic_path_response_frame.h
@@ -0,0 +1,36 @@ +// Copyright (c) 2018 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_FRAMES_QUIC_PATH_RESPONSE_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_RESPONSE_FRAME_H_ + +#include <memory> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +// Size of the entire IETF Quic Path Response frame. +const size_t kQuicPathResponseFrameSize = kQuicPathFrameBufferSize; + +struct QUIC_EXPORT_PRIVATE QuicPathResponseFrame { + QuicPathResponseFrame(); + QuicPathResponseFrame(QuicControlFrameId control_frame_id, + const QuicPathFrameBuffer& data_buff); + ~QuicPathResponseFrame(); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicPathResponseFrame& frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + QuicPathFrameBuffer data_buffer; +}; +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_RESPONSE_FRAME_H_
diff --git a/quic/core/frames/quic_ping_frame.cc b/quic/core/frames/quic_ping_frame.cc new file mode 100644 index 0000000..d31efb0 --- /dev/null +++ b/quic/core/frames/quic_ping_frame.cc
@@ -0,0 +1,20 @@ +// Copyright (c) 2017 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/frames/quic_ping_frame.h" + +namespace quic { + +QuicPingFrame::QuicPingFrame() + : QuicInlinedFrame(PING_FRAME), control_frame_id(kInvalidControlFrameId) {} + +QuicPingFrame::QuicPingFrame(QuicControlFrameId control_frame_id) + : QuicInlinedFrame(PING_FRAME), control_frame_id(control_frame_id) {} + +std::ostream& operator<<(std::ostream& os, const QuicPingFrame& ping_frame) { + os << "{ control_frame_id: " << ping_frame.control_frame_id << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_ping_frame.h b/quic/core/frames/quic_ping_frame.h new file mode 100644 index 0000000..352d079 --- /dev/null +++ b/quic/core/frames/quic_ping_frame.h
@@ -0,0 +1,33 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_ + +#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A ping frame contains no payload, though it is retransmittable, +// and ACK'd just like other normal frames. +struct QUIC_EXPORT_PRIVATE QuicPingFrame + : public QuicInlinedFrame<QuicPingFrame> { + QuicPingFrame(); + explicit QuicPingFrame(QuicControlFrameId control_frame_id); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicPingFrame& ping_frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_
diff --git a/quic/core/frames/quic_retire_connection_id_frame.cc b/quic/core/frames/quic_retire_connection_id_frame.cc new file mode 100644 index 0000000..6828ce4 --- /dev/null +++ b/quic/core/frames/quic_retire_connection_id_frame.cc
@@ -0,0 +1,25 @@ +// Copyright (c) 2019 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/frames/quic_retire_connection_id_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +QuicRetireConnectionIdFrame::QuicRetireConnectionIdFrame() + : control_frame_id(kInvalidControlFrameId), sequence_number(0) {} + +QuicRetireConnectionIdFrame::QuicRetireConnectionIdFrame( + QuicControlFrameId control_frame_id, + QuicConnectionIdSequenceNumber sequence_number) + : control_frame_id(control_frame_id), sequence_number(sequence_number) {} + +std::ostream& operator<<(std::ostream& os, + const QuicRetireConnectionIdFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", sequence_number: " << frame.sequence_number << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_retire_connection_id_frame.h b/quic/core/frames/quic_retire_connection_id_frame.h new file mode 100644 index 0000000..79521f6 --- /dev/null +++ b/quic/core/frames/quic_retire_connection_id_frame.h
@@ -0,0 +1,33 @@ +// Copyright (c) 2019 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_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicRetireConnectionIdFrame { + QuicRetireConnectionIdFrame(); + QuicRetireConnectionIdFrame(QuicControlFrameId control_frame_id, + QuicConnectionIdSequenceNumber sequence_number); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicRetireConnectionIdFrame& frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + QuicConnectionIdSequenceNumber sequence_number; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_
diff --git a/quic/core/frames/quic_rst_stream_frame.cc b/quic/core/frames/quic_rst_stream_frame.cc new file mode 100644 index 0000000..7fbf365 --- /dev/null +++ b/quic/core/frames/quic_rst_stream_frame.cc
@@ -0,0 +1,42 @@ +// 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/frames/quic_rst_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +QuicRstStreamFrame::QuicRstStreamFrame() + : control_frame_id(kInvalidControlFrameId), + stream_id(0), + error_code(QUIC_STREAM_NO_ERROR), + byte_offset(0) {} + +QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicRstStreamErrorCode error_code, + QuicStreamOffset bytes_written) + : control_frame_id(control_frame_id), + stream_id(stream_id), + error_code(error_code), + byte_offset(bytes_written) {} + +QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + uint16_t ietf_error_code, + QuicStreamOffset bytes_written) + : control_frame_id(control_frame_id), + stream_id(stream_id), + ietf_error_code(ietf_error_code), + byte_offset(bytes_written) {} + +std::ostream& operator<<(std::ostream& os, + const QuicRstStreamFrame& rst_frame) { + os << "{ control_frame_id: " << rst_frame.control_frame_id + << ", stream_id: " << rst_frame.stream_id + << ", error_code: " << rst_frame.error_code << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_rst_stream_frame.h b/quic/core/frames/quic_rst_stream_frame.h new file mode 100644 index 0000000..0957614 --- /dev/null +++ b/quic/core/frames/quic_rst_stream_frame.h
@@ -0,0 +1,54 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_RST_STREAM_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_RST_STREAM_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicRstStreamFrame { + QuicRstStreamFrame(); + QuicRstStreamFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicRstStreamErrorCode error_code, + QuicStreamOffset bytes_written); + QuicRstStreamFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + uint16_t ietf_error_code, + QuicStreamOffset bytes_written); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicRstStreamFrame& r); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + QuicStreamId stream_id; + + // Caller must know whether IETF- or Google- QUIC is in use and + // set the appropriate error code. + union { + QuicRstStreamErrorCode error_code; + // In IETF QUIC the code is up to the app on top of quic, so is + // more general than QuicRstStreamErrorCode allows. + uint16_t ietf_error_code; + }; + + // Used to update flow control windows. On termination of a stream, both + // endpoints must inform the peer of the number of bytes they have sent on + // that stream. This can be done through normal termination (data packet with + // FIN) or through a RST. + QuicStreamOffset byte_offset; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_RST_STREAM_FRAME_H_
diff --git a/quic/core/frames/quic_stop_sending_frame.cc b/quic/core/frames/quic_stop_sending_frame.cc new file mode 100644 index 0000000..1afd512 --- /dev/null +++ b/quic/core/frames/quic_stop_sending_frame.cc
@@ -0,0 +1,30 @@ +// Copyright (c) 2018 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/frames/quic_stop_sending_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +QuicStopSendingFrame::QuicStopSendingFrame() + : control_frame_id(kInvalidControlFrameId), + stream_id(0), + application_error_code(0) {} + +QuicStopSendingFrame::QuicStopSendingFrame( + QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicApplicationErrorCode application_error_code) + : control_frame_id(control_frame_id), + stream_id(stream_id), + application_error_code(application_error_code) {} + +std::ostream& operator<<(std::ostream& os, const QuicStopSendingFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", stream_id: " << frame.stream_id + << ", application_error_code: " << frame.application_error_code << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_stop_sending_frame.h b/quic/core/frames/quic_stop_sending_frame.h new file mode 100644 index 0000000..7ca639d --- /dev/null +++ b/quic/core/frames/quic_stop_sending_frame.h
@@ -0,0 +1,34 @@ +// Copyright (c) 2018 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_FRAMES_QUIC_STOP_SENDING_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_SENDING_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicStopSendingFrame { + QuicStopSendingFrame(); + QuicStopSendingFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicApplicationErrorCode application_error_code); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicStopSendingFrame& frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + QuicStreamId stream_id; + QuicApplicationErrorCode application_error_code; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_SENDING_FRAME_H_
diff --git a/quic/core/frames/quic_stop_waiting_frame.cc b/quic/core/frames/quic_stop_waiting_frame.cc new file mode 100644 index 0000000..02e0a62 --- /dev/null +++ b/quic/core/frames/quic_stop_waiting_frame.cc
@@ -0,0 +1,20 @@ +// 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/frames/quic_stop_waiting_frame.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +QuicStopWaitingFrame::QuicStopWaitingFrame() + : QuicInlinedFrame(STOP_WAITING_FRAME) {} + +std::ostream& operator<<(std::ostream& os, + const QuicStopWaitingFrame& sent_info) { + os << "{ least_unacked: " << sent_info.least_unacked << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_stop_waiting_frame.h b/quic/core/frames/quic_stop_waiting_frame.h new file mode 100644 index 0000000..84bfc2a --- /dev/null +++ b/quic/core/frames/quic_stop_waiting_frame.h
@@ -0,0 +1,30 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_WAITING_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_WAITING_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicStopWaitingFrame + : public QuicInlinedFrame<QuicStopWaitingFrame> { + QuicStopWaitingFrame(); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicStopWaitingFrame& s); + + // The lowest packet we've sent which is unacked, and we expect an ack for. + QuicPacketNumber least_unacked; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_WAITING_FRAME_H_
diff --git a/quic/core/frames/quic_stream_frame.cc b/quic/core/frames/quic_stream_frame.cc new file mode 100644 index 0000000..b9413c7 --- /dev/null +++ b/quic/core/frames/quic_stream_frame.cc
@@ -0,0 +1,46 @@ +// 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/frames/quic_stream_frame.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicStreamFrame::QuicStreamFrame() + : QuicStreamFrame(-1, false, 0, nullptr, 0) {} + +QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + QuicStringPiece data) + : QuicStreamFrame(stream_id, fin, offset, data.data(), data.length()) {} + +QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + QuicPacketLength data_length) + : QuicStreamFrame(stream_id, fin, offset, nullptr, data_length) {} + +QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + const char* data_buffer, + QuicPacketLength data_length) + : QuicInlinedFrame(STREAM_FRAME), + fin(fin), + data_length(data_length), + stream_id(stream_id), + data_buffer(data_buffer), + offset(offset) {} + +std::ostream& operator<<(std::ostream& os, + const QuicStreamFrame& stream_frame) { + os << "{ stream_id: " << stream_frame.stream_id + << ", fin: " << stream_frame.fin << ", offset: " << stream_frame.offset + << ", length: " << stream_frame.data_length << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_stream_frame.h b/quic/core/frames/quic_stream_frame.h new file mode 100644 index 0000000..6cd510d --- /dev/null +++ b/quic/core/frames/quic_stream_frame.h
@@ -0,0 +1,51 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_FRAME_H_ + +#include <memory> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +struct QUIC_EXPORT_PRIVATE QuicStreamFrame + : public QuicInlinedFrame<QuicStreamFrame> { + QuicStreamFrame(); + QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + QuicStringPiece data); + QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + QuicPacketLength data_length); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const QuicStreamFrame& s); + + bool fin; + QuicPacketLength data_length; + QuicStreamId stream_id; + const char* data_buffer; // Not owned. + QuicStreamOffset offset; // Location of this data in the stream. + + QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + const char* data_buffer, + QuicPacketLength data_length); +}; +static_assert(sizeof(QuicStreamFrame) <= 64, + "Keep the QuicStreamFrame size to a cacheline."); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_FRAME_H_
diff --git a/quic/core/frames/quic_stream_id_blocked_frame.cc b/quic/core/frames/quic_stream_id_blocked_frame.cc new file mode 100644 index 0000000..627ad2a --- /dev/null +++ b/quic/core/frames/quic_stream_id_blocked_frame.cc
@@ -0,0 +1,27 @@ +// 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/frames/quic_stream_id_blocked_frame.h" + +namespace quic { + +QuicStreamIdBlockedFrame::QuicStreamIdBlockedFrame() + : QuicInlinedFrame(STREAM_ID_BLOCKED_FRAME), + control_frame_id(kInvalidControlFrameId) {} + +QuicStreamIdBlockedFrame::QuicStreamIdBlockedFrame( + QuicControlFrameId control_frame_id, + QuicStreamId stream_id) + : QuicInlinedFrame(STREAM_ID_BLOCKED_FRAME), + control_frame_id(control_frame_id), + stream_id(stream_id) {} + +std::ostream& operator<<(std::ostream& os, + const QuicStreamIdBlockedFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", stream id: " << frame.stream_id << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_stream_id_blocked_frame.h b/quic/core/frames/quic_stream_id_blocked_frame.h new file mode 100644 index 0000000..f9ccca2 --- /dev/null +++ b/quic/core/frames/quic_stream_id_blocked_frame.h
@@ -0,0 +1,40 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_ID_BLOCKED_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_ID_BLOCKED_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// IETF format STREAM_ID_BLOCKED frame. +// The sender uses this to inform the peer that the sender wished to +// open a new stream but was blocked from doing so due due to the +// maximum stream ID limit set by the peer (via a MAX_STREAM_ID frame) +struct QUIC_EXPORT_PRIVATE QuicStreamIdBlockedFrame + : public QuicInlinedFrame<QuicStreamIdBlockedFrame> { + QuicStreamIdBlockedFrame(); + QuicStreamIdBlockedFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicStreamIdBlockedFrame& frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + QuicStreamId stream_id; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_ID_BLOCKED_FRAME_H_
diff --git a/quic/core/frames/quic_window_update_frame.cc b/quic/core/frames/quic_window_update_frame.cc new file mode 100644 index 0000000..07e3168 --- /dev/null +++ b/quic/core/frames/quic_window_update_frame.cc
@@ -0,0 +1,29 @@ +// 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/frames/quic_window_update_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +QuicWindowUpdateFrame::QuicWindowUpdateFrame() + : control_frame_id(kInvalidControlFrameId) {} + +QuicWindowUpdateFrame::QuicWindowUpdateFrame( + QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicStreamOffset byte_offset) + : control_frame_id(control_frame_id), + stream_id(stream_id), + byte_offset(byte_offset) {} + +std::ostream& operator<<(std::ostream& os, + const QuicWindowUpdateFrame& window_update_frame) { + os << "{ control_frame_id: " << window_update_frame.control_frame_id + << ", stream_id: " << window_update_frame.stream_id + << ", byte_offset: " << window_update_frame.byte_offset << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_window_update_frame.h b/quic/core/frames/quic_window_update_frame.h new file mode 100644 index 0000000..73163ce --- /dev/null +++ b/quic/core/frames/quic_window_update_frame.h
@@ -0,0 +1,48 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_WINDOW_UPDATE_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_WINDOW_UPDATE_FRAME_H_ + +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +// Flow control updates per-stream and at the connection level. +// Based on SPDY's WINDOW_UPDATE frame, but uses an absolute byte offset rather +// than a window delta. +// TODO(rjshade): A possible future optimization is to make stream_id and +// byte_offset variable length, similar to stream frames. +struct QUIC_EXPORT_PRIVATE QuicWindowUpdateFrame { + QuicWindowUpdateFrame(); + QuicWindowUpdateFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicStreamOffset byte_offset); + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicWindowUpdateFrame& w); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id; + + // The stream this frame applies to. 0 is a special case meaning the overall + // connection rather than a specific stream. + QuicStreamId stream_id; + + // Byte offset in the stream or connection. The receiver of this frame must + // not send data which would result in this offset being exceeded. + // + // TODO(fkastenholz): Rename this to max_data and change the type to + // QuicByteCount because the IETF defines this as the "maximum + // amount of data that can be sent". + QuicStreamOffset byte_offset; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_WINDOW_UPDATE_FRAME_H_
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc new file mode 100644 index 0000000..c26ffa6 --- /dev/null +++ b/quic/core/http/end_to_end_test.cc
@@ -0,0 +1,3847 @@ +// Copyright (c) 2012 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 <cstddef> +#include <cstdint> +#include <list> +#include <memory> +#include <ostream> +#include <utility> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_framer.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_sleep.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/quic/platform/impl/quic_socket_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h" +#include "net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_client.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_server.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/server_thread.h" +#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h" +#include "net/third_party/quiche/src/quic/tools/quic_client.h" +#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" +#include "net/third_party/quiche/src/quic/tools/quic_server.h" +#include "net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h" +#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h" + +using spdy::kV3LowestPriority; +using spdy::SETTINGS_MAX_HEADER_LIST_SIZE; +using spdy::SpdyFramer; +using spdy::SpdyHeaderBlock; +using spdy::SpdySerializedFrame; +using spdy::SpdySettingsIR; + +namespace quic { +namespace test { +namespace { + +const char kFooResponseBody[] = "Artichoke hearts make me happy."; +const char kBarResponseBody[] = "Palm hearts are pretty delicious, also."; +const float kSessionToStreamRatio = 1.5; + +// Run all tests with the cross products of all versions. +struct TestParams { + TestParams(const ParsedQuicVersionVector& client_supported_versions, + const ParsedQuicVersionVector& server_supported_versions, + ParsedQuicVersion negotiated_version, + bool client_supports_stateless_rejects, + bool server_uses_stateless_rejects_if_peer_supported, + QuicTag congestion_control_tag, + bool use_cheap_stateless_reject) + : client_supported_versions(client_supported_versions), + server_supported_versions(server_supported_versions), + negotiated_version(negotiated_version), + client_supports_stateless_rejects(client_supports_stateless_rejects), + server_uses_stateless_rejects_if_peer_supported( + server_uses_stateless_rejects_if_peer_supported), + congestion_control_tag(congestion_control_tag), + use_cheap_stateless_reject(use_cheap_stateless_reject) {} + + friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { + os << "{ server_supported_versions: " + << ParsedQuicVersionVectorToString(p.server_supported_versions); + os << " client_supported_versions: " + << ParsedQuicVersionVectorToString(p.client_supported_versions); + os << " negotiated_version: " + << ParsedQuicVersionToString(p.negotiated_version); + os << " client_supports_stateless_rejects: " + << p.client_supports_stateless_rejects; + os << " server_uses_stateless_rejects_if_peer_supported: " + << p.server_uses_stateless_rejects_if_peer_supported; + os << " congestion_control_tag: " + << QuicTagToString(p.congestion_control_tag); + os << " use_cheap_stateless_reject: " << p.use_cheap_stateless_reject + << " }"; + return os; + } + + ParsedQuicVersionVector client_supported_versions; + ParsedQuicVersionVector server_supported_versions; + ParsedQuicVersion negotiated_version; + bool client_supports_stateless_rejects; + bool server_uses_stateless_rejects_if_peer_supported; + QuicTag congestion_control_tag; + bool use_cheap_stateless_reject; +}; + +// Constructs various test permutations. +std::vector<TestParams> GetTestParams(bool use_tls_handshake, + bool test_stateless_rejects) { + QuicFlagSaver flags; + // Divide the versions into buckets in which the intra-frame format + // is compatible. When clients encounter QUIC version negotiation + // they simply retransmit all packets using the new version's + // QUIC framing. However, they are unable to change the intra-frame + // layout (for example to change HTTP/2 headers to SPDY/3, or a change in the + // handshake protocol). So these tests need to ensure that clients are never + // attempting to do 0-RTT across incompatible versions. Chromium only + // supports a single version at a time anyway. :) + FLAGS_quic_supports_tls_handshake = use_tls_handshake; + ParsedQuicVersionVector all_supported_versions = + FilterSupportedVersions(AllSupportedVersions()); + + // Buckets are separated by versions: versions prior to QUIC_VERSION_47 use + // STREAM frames for the handshake, and only have QUIC crypto as the handshake + // protocol. Version 47 and greater use CRYPTO frames for the handshake, and + // must also be split based on the handshake protocol. If the handshake + // protocol (QUIC crypto or TLS) changes, the ClientHello/CHLO must be + // reconstructed for the correct protocol. + ParsedQuicVersionVector version_buckets[3]; + + for (const ParsedQuicVersion& version : all_supported_versions) { + if (version.transport_version < QUIC_VERSION_47) { + version_buckets[0].push_back(version); + } else if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { + version_buckets[1].push_back(version); + } else { + version_buckets[2].push_back(version); + } + } + + // This must be kept in sync with the number of nested for-loops below as it + // is used to prune the number of tests that are run. + const int kMaxEnabledOptions = 4; + int max_enabled_options = 0; + std::vector<TestParams> params; + for (const QuicTag congestion_control_tag : {kRENO, kTBBR, kQBIC, kTPCC}) { + for (bool server_uses_stateless_rejects_if_peer_supported : {true, false}) { + for (bool client_supports_stateless_rejects : {true, false}) { + for (bool use_cheap_stateless_reject : {true, false}) { + int enabled_options = 0; + if (congestion_control_tag != kQBIC) { + ++enabled_options; + } + if (client_supports_stateless_rejects) { + ++enabled_options; + } + if (server_uses_stateless_rejects_if_peer_supported) { + ++enabled_options; + } + if (use_cheap_stateless_reject) { + ++enabled_options; + } + CHECK_GE(kMaxEnabledOptions, enabled_options); + if (enabled_options > max_enabled_options) { + max_enabled_options = enabled_options; + } + + // Run tests with no options, a single option, or all the + // options enabled to avoid a combinatorial explosion. + if (enabled_options > 1 && enabled_options < kMaxEnabledOptions) { + continue; + } + + // There are many stateless reject combinations, so don't test them + // unless requested. + if ((server_uses_stateless_rejects_if_peer_supported || + client_supports_stateless_rejects || + use_cheap_stateless_reject) && + !test_stateless_rejects) { + continue; + } + + for (const ParsedQuicVersionVector& client_versions : + version_buckets) { + if (FilterSupportedVersions(client_versions).empty()) { + continue; + } + // Add an entry for server and client supporting all + // versions. + params.push_back(TestParams( + client_versions, all_supported_versions, + client_versions.front(), client_supports_stateless_rejects, + server_uses_stateless_rejects_if_peer_supported, + congestion_control_tag, use_cheap_stateless_reject)); + + // Run version negotiation tests tests with no options, or + // all the options enabled to avoid a combinatorial + // explosion. + if (enabled_options > 1 && enabled_options < kMaxEnabledOptions) { + continue; + } + + // Test client supporting all versions and server supporting + // 1 version. Simulate an old server and exercise version + // downgrade in the client. Protocol negotiation should + // occur. Skip the i = 0 case because it is essentially the + // same as the default case. + for (size_t i = 1; i < client_versions.size(); ++i) { + ParsedQuicVersionVector server_supported_versions; + server_supported_versions.push_back(client_versions[i]); + if (FilterSupportedVersions(server_supported_versions).empty()) { + continue; + } + params.push_back(TestParams( + client_versions, server_supported_versions, + server_supported_versions.front(), + client_supports_stateless_rejects, + server_uses_stateless_rejects_if_peer_supported, + congestion_control_tag, use_cheap_stateless_reject)); + } // End of inner version loop. + } // End of outer version loop. + } // End of use_cheap_stateless_reject loop. + } // End of client_supports_stateless_rejects loop. + } // End of server_uses_stateless_rejects_if_peer_supported loop. + } // End of congestion_control_tag loop. + CHECK_EQ(kMaxEnabledOptions, max_enabled_options); + return params; +} + +class ServerDelegate : public PacketDroppingTestWriter::Delegate { + public: + explicit ServerDelegate(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher) {} + ~ServerDelegate() override = default; + void OnCanWrite() override { dispatcher_->OnCanWrite(); } + + private: + QuicDispatcher* dispatcher_; +}; + +class ClientDelegate : public PacketDroppingTestWriter::Delegate { + public: + explicit ClientDelegate(QuicClient* client) : client_(client) {} + ~ClientDelegate() override = default; + void OnCanWrite() override { + QuicEpollEvent event(EPOLLOUT); + client_->epoll_network_helper()->OnEvent(client_->GetLatestFD(), &event); + } + + private: + QuicClient* client_; +}; + +class EndToEndTest : public QuicTestWithParam<TestParams> { + protected: + EndToEndTest() + : initialized_(false), + connect_to_server_on_initialize_(true), + server_address_( + QuicSocketAddress(TestLoopback(), QuicPickUnusedPortOrDie())), + server_hostname_("test.example.com"), + client_writer_(nullptr), + server_writer_(nullptr), + negotiated_version_(UnsupportedQuicVersion()), + chlo_multiplier_(0), + stream_factory_(nullptr), + support_server_push_(false), + override_connection_id_(nullptr) { + FLAGS_quic_supports_tls_handshake = true; + SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, true); + SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, true); + client_supported_versions_ = GetParam().client_supported_versions; + server_supported_versions_ = GetParam().server_supported_versions; + negotiated_version_ = GetParam().negotiated_version; + + QUIC_LOG(INFO) << "Using Configuration: " << GetParam(); + + // Use different flow control windows for client/server. + client_config_.SetInitialStreamFlowControlWindowToSend( + 2 * kInitialStreamFlowControlWindowForTest); + client_config_.SetInitialSessionFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + server_config_.SetInitialStreamFlowControlWindowToSend( + 3 * kInitialStreamFlowControlWindowForTest); + server_config_.SetInitialSessionFlowControlWindowToSend( + 3 * kInitialSessionFlowControlWindowForTest); + + // The default idle timeouts can be too strict when running on a busy + // machine. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(30); + client_config_.set_max_time_before_crypto_handshake(timeout); + client_config_.set_max_idle_time_before_crypto_handshake(timeout); + server_config_.set_max_time_before_crypto_handshake(timeout); + server_config_.set_max_idle_time_before_crypto_handshake(timeout); + + AddToCache("/foo", 200, kFooResponseBody); + AddToCache("/bar", 200, kBarResponseBody); + } + + ~EndToEndTest() override { QuicRecyclePort(server_address_.port()); } + + virtual void CreateClientWithWriter() { + client_.reset(CreateQuicClient(client_writer_)); + } + + QuicTestClient* CreateQuicClient(QuicPacketWriterWrapper* writer) { + QuicTestClient* client = + new QuicTestClient(server_address_, server_hostname_, client_config_, + client_supported_versions_, + crypto_test_utils::ProofVerifierForTesting()); + client->UseWriter(writer); + if (!pre_shared_key_client_.empty()) { + client->client()->SetPreSharedKey(pre_shared_key_client_); + } + if (override_connection_id_ != nullptr) { + client->UseConnectionId(*override_connection_id_); + } + client->Connect(); + return client; + } + + void set_smaller_flow_control_receive_window() { + const uint32_t kClientIFCW = 64 * 1024; + const uint32_t kServerIFCW = 1024 * 1024; + set_client_initial_stream_flow_control_receive_window(kClientIFCW); + set_client_initial_session_flow_control_receive_window( + kSessionToStreamRatio * kClientIFCW); + set_server_initial_stream_flow_control_receive_window(kServerIFCW); + set_server_initial_session_flow_control_receive_window( + kSessionToStreamRatio * kServerIFCW); + } + + void set_client_initial_stream_flow_control_receive_window(uint32_t window) { + CHECK(client_ == nullptr); + QUIC_DLOG(INFO) << "Setting client initial stream flow control window: " + << window; + client_config_.SetInitialStreamFlowControlWindowToSend(window); + } + + void set_client_initial_session_flow_control_receive_window(uint32_t window) { + CHECK(client_ == nullptr); + QUIC_DLOG(INFO) << "Setting client initial session flow control window: " + << window; + client_config_.SetInitialSessionFlowControlWindowToSend(window); + } + + void set_server_initial_stream_flow_control_receive_window(uint32_t window) { + CHECK(server_thread_ == nullptr); + QUIC_DLOG(INFO) << "Setting server initial stream flow control window: " + << window; + server_config_.SetInitialStreamFlowControlWindowToSend(window); + } + + void set_server_initial_session_flow_control_receive_window(uint32_t window) { + CHECK(server_thread_ == nullptr); + QUIC_DLOG(INFO) << "Setting server initial session flow control window: " + << window; + server_config_.SetInitialSessionFlowControlWindowToSend(window); + } + + const QuicSentPacketManager* GetSentPacketManagerFromFirstServerSession() { + return &GetServerConnection()->sent_packet_manager(); + } + + QuicConnection* GetServerConnection() { + return GetServerSession()->connection(); + } + + QuicSession* GetServerSession() { + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + EXPECT_EQ(1u, dispatcher->session_map().size()); + return dispatcher->session_map().begin()->second.get(); + } + + bool Initialize() { + QuicTagVector copt; + server_config_.SetConnectionOptionsToSend(copt); + copt = client_extra_copts_; + + // TODO(nimia): Consider setting the congestion control algorithm for the + // client as well according to the test parameter. + copt.push_back(GetParam().congestion_control_tag); + if (GetParam().congestion_control_tag == kTPCC && + GetQuicReloadableFlag(quic_enable_pcc3)) { + copt.push_back(kTPCC); + } + + if (GetParam().client_supports_stateless_rejects) { + copt.push_back(kSREJ); + } + client_config_.SetConnectionOptionsToSend(copt); + + // Start the server first, because CreateQuicClient() attempts + // to connect to the server. + StartServer(); + + if (!connect_to_server_on_initialize_) { + initialized_ = true; + return true; + } + + CreateClientWithWriter(); + static QuicEpollEvent event(EPOLLOUT); + if (client_writer_ != nullptr) { + client_writer_->Initialize( + QuicConnectionPeer::GetHelper( + client_->client()->client_session()->connection()), + QuicConnectionPeer::GetAlarmFactory( + client_->client()->client_session()->connection()), + QuicMakeUnique<ClientDelegate>(client_->client())); + } + initialized_ = true; + return client_->client()->connected(); + } + + void SetUp() override { + // The ownership of these gets transferred to the QuicPacketWriterWrapper + // when Initialize() is executed. + client_writer_ = new PacketDroppingTestWriter(); + server_writer_ = new PacketDroppingTestWriter(); + } + + void TearDown() override { + ASSERT_TRUE(initialized_) << "You must call Initialize() in every test " + << "case. Otherwise, your test will leak memory."; + StopServer(); + } + + void StartServer() { + SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, + GetParam().use_cheap_stateless_reject); + + uint8_t connection_id_length = override_connection_id_ != nullptr + ? override_connection_id_->length() + : kQuicDefaultConnectionIdLength; + auto* test_server = + new QuicTestServer(crypto_test_utils::ProofSourceForTesting(), + server_config_, server_supported_versions_, + &memory_cache_backend_, connection_id_length); + server_thread_ = QuicMakeUnique<ServerThread>(test_server, server_address_); + if (chlo_multiplier_ != 0) { + server_thread_->server()->SetChloMultiplier(chlo_multiplier_); + } + if (!pre_shared_key_server_.empty()) { + server_thread_->server()->SetPreSharedKey(pre_shared_key_server_); + } + server_thread_->Initialize(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicDispatcherPeer::UseWriter(dispatcher, server_writer_); + + SetQuicReloadableFlag( + enable_quic_stateless_reject_support, + GetParam().server_uses_stateless_rejects_if_peer_supported); + + server_writer_->Initialize(QuicDispatcherPeer::GetHelper(dispatcher), + QuicDispatcherPeer::GetAlarmFactory(dispatcher), + QuicMakeUnique<ServerDelegate>(dispatcher)); + if (stream_factory_ != nullptr) { + static_cast<QuicTestServer*>(server_thread_->server()) + ->SetSpdyStreamFactory(stream_factory_); + } + + server_thread_->Start(); + } + + void StopServer() { + if (server_thread_) { + server_thread_->Quit(); + server_thread_->Join(); + } + } + + void AddToCache(QuicStringPiece path, + int response_code, + QuicStringPiece body) { + memory_cache_backend_.AddSimpleResponse(server_hostname_, path, + response_code, body); + } + + void SetPacketLossPercentage(int32_t loss) { + client_writer_->set_fake_packet_loss_percentage(loss); + server_writer_->set_fake_packet_loss_percentage(loss); + } + + void SetPacketSendDelay(QuicTime::Delta delay) { + client_writer_->set_fake_packet_delay(delay); + server_writer_->set_fake_packet_delay(delay); + } + + void SetReorderPercentage(int32_t reorder) { + client_writer_->set_fake_reorder_percentage(reorder); + server_writer_->set_fake_reorder_percentage(reorder); + } + + // Verifies that the client and server connections were both free of packets + // being discarded, based on connection stats. + // Calls server_thread_ Pause() and Resume(), which may only be called once + // per test. + void VerifyCleanConnection(bool had_packet_loss) { + QuicConnectionStats client_stats = + client_->client()->client_session()->connection()->GetStats(); + // TODO(ianswett): Determine why this becomes even more flaky with BBR + // enabled. b/62141144 + if (!had_packet_loss && !GetQuicReloadableFlag(quic_default_to_bbr)) { + EXPECT_EQ(0u, client_stats.packets_lost); + } + EXPECT_EQ(0u, client_stats.packets_discarded); + // When doing 0-RTT with stateless rejects, the encrypted requests cause + // a retranmission of the SREJ packets which are dropped by the client. + // When client starts with an unsupported version, the version negotiation + // packet sent by server for the old connection (respond for the connection + // close packet) will be dropped by the client. + if (!BothSidesSupportStatelessRejects() && + !ServerSendsVersionNegotiation()) { + EXPECT_EQ(0u, client_stats.packets_dropped); + } + if (!ClientSupportsIetfQuicNotSupportedByServer()) { + // In this case, if client sends 0-RTT POST with v99, receives IETF + // version negotiation packet and speaks a GQUIC version. Server processes + // this connection in time wait list and keeps sending IETF version + // negotiation packet for incoming packets. But these version negotiation + // packets cannot be processed by the client speaking GQUIC. + EXPECT_EQ(client_stats.packets_received, client_stats.packets_processed); + } + + const int num_expected_stateless_rejects = + (BothSidesSupportStatelessRejects() && + client_->client()->client_session()->GetNumSentClientHellos() > 0) + ? 1 + : 0; + EXPECT_EQ(num_expected_stateless_rejects, + client_->client()->num_stateless_rejects_received()); + + server_thread_->Pause(); + QuicConnectionStats server_stats = GetServerConnection()->GetStats(); + if (!had_packet_loss) { + EXPECT_EQ(0u, server_stats.packets_lost); + } + EXPECT_EQ(0u, server_stats.packets_discarded); + // TODO(ianswett): Restore the check for packets_dropped equals 0. + // The expect for packets received is equal to packets processed fails + // due to version negotiation packets. + server_thread_->Resume(); + } + + bool BothSidesSupportStatelessRejects() { + return (GetParam().server_uses_stateless_rejects_if_peer_supported && + GetParam().client_supports_stateless_rejects); + } + + // Client supports IETF QUIC, while it is not supported by server. + bool ClientSupportsIetfQuicNotSupportedByServer() { + return GetParam().client_supported_versions[0].transport_version > + QUIC_VERSION_43 && + FilterSupportedVersions(GetParam().server_supported_versions)[0] + .transport_version <= QUIC_VERSION_43; + } + + // Returns true when client starts with an unsupported version, and client + // closes connection when version negotiation is received. + bool ServerSendsVersionNegotiation() { + return GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation) && + GetParam().client_supported_versions[0] != + GetParam().negotiated_version; + } + + bool SupportsIetfQuicWithTls(ParsedQuicVersion version) { + return version.transport_version > QUIC_VERSION_43 && + version.handshake_protocol == PROTOCOL_TLS1_3; + } + + void ExpectFlowControlsSynced(QuicFlowController* client, + QuicFlowController* server) { + EXPECT_EQ(QuicFlowControllerPeer::SendWindowSize(client), + QuicFlowControllerPeer::ReceiveWindowSize(server)); + EXPECT_EQ(QuicFlowControllerPeer::ReceiveWindowSize(client), + QuicFlowControllerPeer::SendWindowSize(server)); + } + + // Must be called before Initialize to have effect. + void SetSpdyStreamFactory(QuicTestServer::StreamFactory* factory) { + stream_factory_ = factory; + } + + QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { + return GetNthClientInitiatedBidirectionalStreamId( + client_->client()->client_session()->connection()->transport_version(), + n); + } + + QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { + return GetNthServerInitiatedBidirectionalStreamId( + client_->client()->client_session()->connection()->transport_version(), + n); + } + + ScopedEnvironmentForThreads environment_; + bool initialized_; + // If true, the Initialize() function will create |client_| and starts to + // connect to the server. + // Default is true. + bool connect_to_server_on_initialize_; + QuicSocketAddress server_address_; + QuicString server_hostname_; + QuicMemoryCacheBackend memory_cache_backend_; + std::unique_ptr<ServerThread> server_thread_; + std::unique_ptr<QuicTestClient> client_; + PacketDroppingTestWriter* client_writer_; + PacketDroppingTestWriter* server_writer_; + QuicConfig client_config_; + QuicConfig server_config_; + ParsedQuicVersionVector client_supported_versions_; + ParsedQuicVersionVector server_supported_versions_; + QuicTagVector client_extra_copts_; + ParsedQuicVersion negotiated_version_; + size_t chlo_multiplier_; + QuicTestServer::StreamFactory* stream_factory_; + bool support_server_push_; + QuicString pre_shared_key_client_; + QuicString pre_shared_key_server_; + QuicConnectionId* override_connection_id_; +}; + +// Run all end to end tests with all supported versions. +INSTANTIATE_TEST_SUITE_P(EndToEndTests, + EndToEndTest, + ::testing::ValuesIn(GetTestParams(false, false))); + +class EndToEndTestWithTls : public EndToEndTest {}; + +INSTANTIATE_TEST_SUITE_P(EndToEndTestsWithTls, + EndToEndTestWithTls, + ::testing::ValuesIn(GetTestParams(true, false))); + +class EndToEndTestWithStatelessReject : public EndToEndTest {}; + +INSTANTIATE_TEST_SUITE_P(WithStatelessReject, + EndToEndTestWithStatelessReject, + ::testing::ValuesIn(GetTestParams(false, true))); + +TEST_P(EndToEndTestWithTls, HandshakeSuccessful) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + // There have been occasions where it seemed that negotiated_version_ and the + // version in the connection are not in sync. If it is happening, it has not + // been recreatable; this assert is here just to check and raise a flag if it + // happens. + ASSERT_EQ( + client_->client()->client_session()->connection()->transport_version(), + negotiated_version_.transport_version); + + QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream( + client_->client()->client_session()); + QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(crypto_stream); + EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + server_thread_->Pause(); + crypto_stream = QuicSessionPeer::GetMutableCryptoStream(GetServerSession()); + sequencer = QuicStreamPeer::sequencer(crypto_stream); + EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); +} + +TEST_P(EndToEndTestWithStatelessReject, SimpleRequestResponseStatless) { + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + int expected_num_client_hellos = 2; + if (ServerSendsVersionNegotiation()) { + ++expected_num_client_hellos; + if (BothSidesSupportStatelessRejects()) { + ++expected_num_client_hellos; + } + } + EXPECT_EQ(expected_num_client_hellos, + client_->client()->GetNumSentClientHellos()); +} + +TEST_P(EndToEndTest, SimpleRequestResponse) { + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + int expected_num_client_hellos = 2; + if (ServerSendsVersionNegotiation()) { + ++expected_num_client_hellos; + if (BothSidesSupportStatelessRejects()) { + ++expected_num_client_hellos; + } + } + EXPECT_EQ(expected_num_client_hellos, + client_->client()->GetNumSentClientHellos()); +} + +TEST_P(EndToEndTest, SimpleRequestResponseZeroConnectionID) { + QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId( + GetParam().negotiated_version.transport_version); + override_connection_id_ = &connection_id; + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + int expected_num_client_hellos = 2; + if (ServerSendsVersionNegotiation()) { + ++expected_num_client_hellos; + if (BothSidesSupportStatelessRejects()) { + ++expected_num_client_hellos; + } + } + EXPECT_EQ(expected_num_client_hellos, + client_->client()->GetNumSentClientHellos()); + EXPECT_EQ(client_->client()->client_session()->connection()->connection_id(), + QuicUtils::CreateZeroConnectionId( + GetParam().negotiated_version.transport_version)); +} + +TEST_P(EndToEndTest, SimpleRequestResponseWithLargeReject) { + chlo_multiplier_ = 1; + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(4, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); + } +} + +TEST_P(EndToEndTestWithTls, SimpleRequestResponsev6) { + server_address_ = + QuicSocketAddress(QuicIpAddress::Loopback6(), server_address_.port()); + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTestWithTls, SeparateFinPacket) { + ASSERT_TRUE(Initialize()); + + // Send a request in two parts: the request and then an empty packet with FIN. + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + client_->SendMessage(headers, "", /*fin=*/false); + client_->SendData("", true); + client_->WaitForResponse(); + EXPECT_EQ(kFooResponseBody, client_->response_body()); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Now do the same thing but with a content length. + headers["content-length"] = "3"; + client_->SendMessage(headers, "", /*fin=*/false); + client_->SendData("foo", true); + client_->WaitForResponse(); + EXPECT_EQ(kFooResponseBody, client_->response_body()); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTestWithTls, MultipleRequestResponse) { + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTest, MultipleRequestResponseZeroConnectionID) { + QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId( + GetParam().negotiated_version.transport_version); + override_connection_id_ = &connection_id; + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTestWithTls, MultipleStreams) { + // Verifies quic_test_client can track responses of all active streams. + ASSERT_TRUE(Initialize()); + + const int kNumRequests = 10; + + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["content-length"] = "3"; + + for (int i = 0; i < kNumRequests; ++i) { + client_->SendMessage(headers, "bar", /*fin=*/true); + } + + while (kNumRequests > client_->num_responses()) { + client_->ClearPerRequestState(); + client_->WaitForResponse(); + EXPECT_EQ(kFooResponseBody, client_->response_body()); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + } +} + +TEST_P(EndToEndTestWithTls, MultipleClients) { + ASSERT_TRUE(Initialize()); + std::unique_ptr<QuicTestClient> client2(CreateQuicClient(nullptr)); + + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["content-length"] = "3"; + + client_->SendMessage(headers, "", /*fin=*/false); + client2->SendMessage(headers, "", /*fin=*/false); + + client_->SendData("bar", true); + client_->WaitForResponse(); + EXPECT_EQ(kFooResponseBody, client_->response_body()); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + client2->SendData("eep", true); + client2->WaitForResponse(); + EXPECT_EQ(kFooResponseBody, client2->response_body()); + EXPECT_EQ("200", client2->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTestWithTls, RequestOverMultiplePackets) { + // Send a large enough request to guarantee fragmentation. + QuicString huge_request = + "/some/path?query=" + QuicString(kMaxPacketSize, '.'); + AddToCache(huge_request, 200, kBarResponseBody); + + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest(huge_request)); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTestWithTls, MultiplePacketsRandomOrder) { + // Send a large enough request to guarantee fragmentation. + QuicString huge_request = + "/some/path?query=" + QuicString(kMaxPacketSize, '.'); + AddToCache(huge_request, 200, kBarResponseBody); + + ASSERT_TRUE(Initialize()); + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(50); + + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest(huge_request)); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTestWithTls, PostMissingBytes) { + ASSERT_TRUE(Initialize()); + + // Add a content length header with no body. + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["content-length"] = "3"; + + // This should be detected as stream fin without complete request, + // triggering an error response. + client_->SendCustomSynchronousRequest(headers, ""); + EXPECT_EQ(QuicSimpleServerStream::kErrorResponseBody, + client_->response_body()); + EXPECT_EQ("500", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTest, LargePostNoPacketLoss) { + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // 1 MB body. + QuicString body(1024 * 1024, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + // TODO(ianswett): There should not be packet loss in this test, but on some + // platforms the receive buffer overflows. + VerifyCleanConnection(true); +} + +TEST_P(EndToEndTest, LargePostNoPacketLoss1sRTT) { + ASSERT_TRUE(Initialize()); + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(1000)); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // 100 KB body. + QuicString body(100 * 1024, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, LargePostWithPacketLoss) { + if (!BothSidesSupportStatelessRejects()) { + // Connect with lower fake packet loss than we'd like to test. + // Until b/10126687 is fixed, losing handshake packets is pretty + // brutal. + // TODO(jokulik): Until we support redundant SREJ packets, don't + // drop handshake packets for stateless rejects. + SetPacketLossPercentage(5); + } + ASSERT_TRUE(Initialize()); + + // Wait for the server SHLO before upping the packet loss. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + SetPacketLossPercentage(30); + + // 10 KB body. + QuicString body(1024 * 10, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + VerifyCleanConnection(true); +} + +// Regression test for b/80090281. +TEST_P(EndToEndTest, LargePostWithPacketLossAndAlwaysBundleWindowUpdates) { + ASSERT_TRUE(Initialize()); + + // Wait for the server SHLO before upping the packet loss. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Normally server only bundles a retransmittable frame once every other + // kMaxConsecutiveNonRetransmittablePackets ack-only packets. Setting the max + // to 0 to reliably reproduce b/80090281. + server_thread_->Schedule([this]() { + QuicConnectionPeer::SetMaxConsecutiveNumPacketsWithNoRetransmittableFrames( + GetServerConnection(), 0); + }); + + SetPacketLossPercentage(30); + + // 10 KB body. + QuicString body(1024 * 10, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + VerifyCleanConnection(true); +} + +TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) { + if (!BothSidesSupportStatelessRejects()) { + // Connect with lower fake packet loss than we'd like to test. Until + // b/10126687 is fixed, losing handshake packets is pretty brutal. + // TODO(jokulik): Until we support redundant SREJ packets, don't + // drop handshake packets for stateless rejects. + SetPacketLossPercentage(5); + } + ASSERT_TRUE(Initialize()); + + // Wait for the server SHLO before upping the packet loss. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + SetPacketLossPercentage(10); + client_writer_->set_fake_blocked_socket_percentage(10); + + // 10 KB body. + QuicString body(1024 * 10, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); +} + +TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + // Both of these must be called when the writer is not actively used. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // 1 MB body. + QuicString body(1024 * 1024, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); +} + +TEST_P(EndToEndTest, LargePostZeroRTTFailure) { + // Send a request and then disconnect. This prepares the client to attempt + // a 0-RTT handshake for the next request. + ASSERT_TRUE(Initialize()); + + QuicString body(20480, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + // In the non-stateless case, the same session is used for both + // hellos, so the number of hellos sent on that session is 2. In + // the stateless case, the first client session will be completely + // torn down after the reject. The number of hellos on the latest + // session is 1. + const int expected_num_hellos_latest_session = + (BothSidesSupportStatelessRejects() && !ServerSendsVersionNegotiation()) + ? 1 + : 2; + EXPECT_EQ(expected_num_hellos_latest_session, + client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } + + client_->Disconnect(); + + // The 0-RTT handshake should succeed. + client_->Connect(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_->client()->connected()); + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + + EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(1, client_->client()->GetNumSentClientHellos()); + } + + client_->Disconnect(); + + // Restart the server so that the 0-RTT handshake will take 1 RTT. + StopServer(); + server_writer_ = new PacketDroppingTestWriter(); + StartServer(); + + client_->Connect(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_->client()->connected()); + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + // In the non-stateless case, the same session is used for both + // hellos, so the number of hellos sent on that session is 2. In + // the stateless case, the first client session will be completely + // torn down after the reject. The number of hellos sent on the + // latest session is 1. + EXPECT_EQ(expected_num_hellos_latest_session, + client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } + + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) { + // Send a request and then disconnect. This prepares the client to attempt + // a 0-RTT handshake for the next request. + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + // In the non-stateless case, the same session is used for both + // hellos, so the number of hellos sent on that session is 2. In + // the stateless case, the first client session will be completely + // torn down after the reject. The number of hellos on that second + // latest session is 1. + const int expected_num_hellos_latest_session = + (BothSidesSupportStatelessRejects() && !ServerSendsVersionNegotiation()) + ? 1 + : 2; + EXPECT_EQ(expected_num_hellos_latest_session, + client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } + + client_->Disconnect(); + + // The 0-RTT handshake should succeed. + client_->Connect(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_->client()->connected()); + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + + EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(1, client_->client()->GetNumSentClientHellos()); + } + + client_->Disconnect(); + + // Restart the server so that the 0-RTT handshake will take 1 RTT. + StopServer(); + server_writer_ = new PacketDroppingTestWriter(); + StartServer(); + + client_->Connect(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_->client()->connected()); + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + // In the non-stateless case, the same session is used for both + // hellos, so the number of hellos sent on that session is 2. In + // the stateless case, the first client session will be completely + // torn down after the reject. The number of hellos sent on the + // latest session is 1. + EXPECT_EQ(expected_num_hellos_latest_session, + client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } + + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, LargePostSynchronousRequest) { + // Send a request and then disconnect. This prepares the client to attempt + // a 0-RTT handshake for the next request. + ASSERT_TRUE(Initialize()); + + QuicString body(20480, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + // In the non-stateless case, the same session is used for both + // hellos, so the number of hellos sent on that session is 2. In + // the stateless case, the first client session will be completely + // torn down after the reject. The number of hellos on the latest + // session is 1. + const int expected_num_hellos_latest_session = + (BothSidesSupportStatelessRejects() && !ServerSendsVersionNegotiation()) + ? 1 + : 2; + EXPECT_EQ(expected_num_hellos_latest_session, + client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } + + client_->Disconnect(); + + // The 0-RTT handshake should succeed. + client_->Connect(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_->client()->connected()); + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + + EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(1, client_->client()->GetNumSentClientHellos()); + } + + client_->Disconnect(); + + // Restart the server so that the 0-RTT handshake will take 1 RTT. + StopServer(); + server_writer_ = new PacketDroppingTestWriter(); + StartServer(); + + client_->Connect(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_->client()->connected()); + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + // In the non-stateless case, the same session is used for both + // hellos, so the number of hellos sent on that session is 2. In + // the stateless case, the first client session will be completely + // torn down after the reject. The number of hellos sent on the + // latest session is 1. + EXPECT_EQ(expected_num_hellos_latest_session, + client_->client()->client_session()->GetNumSentClientHellos()); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(3, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } + + VerifyCleanConnection(false); +} + +TEST_P(EndToEndTest, StatelessRejectWithPacketLoss) { + // In this test, we intentionally drop the first packet from the + // server, which corresponds with the initial REJ/SREJ response from + // the server. + server_writer_->set_fake_drop_first_n_packets(1); + ASSERT_TRUE(Initialize()); +} + +TEST_P(EndToEndTest, SetInitialReceivedConnectionOptions) { + QuicTagVector initial_received_options; + initial_received_options.push_back(kTBBR); + initial_received_options.push_back(kIW10); + initial_received_options.push_back(kPRST); + EXPECT_TRUE(server_config_.SetInitialReceivedConnectionOptions( + initial_received_options)); + + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + EXPECT_FALSE(server_config_.SetInitialReceivedConnectionOptions( + initial_received_options)); + + // Verify that server's configuration is correct. + server_thread_->Pause(); + EXPECT_TRUE(server_config_.HasReceivedConnectionOptions()); + EXPECT_TRUE( + ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kTBBR)); + EXPECT_TRUE( + ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kIW10)); + EXPECT_TRUE( + ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kPRST)); +} + +TEST_P(EndToEndTest, LargePostSmallBandwidthLargeBuffer) { + ASSERT_TRUE(Initialize()); + SetPacketSendDelay(QuicTime::Delta::FromMicroseconds(1)); + // 256KB per second with a 256KB buffer from server to client. Wireless + // clients commonly have larger buffers, but our max CWND is 200. + server_writer_->set_max_bandwidth_and_buffer_size( + QuicBandwidth::FromBytesPerSecond(256 * 1024), 256 * 1024); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // 1 MB body. + QuicString body(1024 * 1024, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + // This connection may drop packets, because the buffer is smaller than the + // max CWND. + VerifyCleanConnection(true); +} + +TEST_P(EndToEndTestWithTls, DoNotSetSendAlarmIfConnectionFlowControlBlocked) { + // Regression test for b/14677858. + // Test that the resume write alarm is not set in QuicConnection::OnCanWrite + // if currently connection level flow control blocked. If set, this results in + // an infinite loop in the EpollServer, as the alarm fires and is immediately + // rescheduled. + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // Ensure both stream and connection level are flow control blocked by setting + // the send window offset to 0. + const uint64_t flow_control_window = + server_config_.GetInitialStreamFlowControlWindowToSend(); + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + QuicSession* session = client_->client()->client_session(); + QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0); + QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0); + EXPECT_TRUE(stream->flow_controller()->IsBlocked()); + EXPECT_TRUE(session->flow_controller()->IsBlocked()); + + // Make sure that the stream has data pending so that it will be marked as + // write blocked when it receives a stream level WINDOW_UPDATE. + stream->WriteOrBufferBody("hello", false); + + // The stream now attempts to write, fails because it is still connection + // level flow control blocked, and is added to the write blocked list. + QuicWindowUpdateFrame window_update(kInvalidControlFrameId, stream->id(), + 2 * flow_control_window); + stream->OnWindowUpdateFrame(window_update); + + // Prior to fixing b/14677858 this call would result in an infinite loop in + // Chromium. As a proxy for detecting this, we now check whether the + // send alarm is set after OnCanWrite. It should not be, as the + // connection is still flow control blocked. + session->connection()->OnCanWrite(); + + QuicAlarm* send_alarm = + QuicConnectionPeer::GetSendAlarm(session->connection()); + EXPECT_FALSE(send_alarm->IsSet()); +} + +// TODO(nharper): Needs to get turned back to EndToEndTestWithTls +// when we figure out why the test doesn't work on chrome. +TEST_P(EndToEndTest, InvalidStream) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + QuicString body(kMaxPacketSize, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + // Force the client to write with a stream ID belonging to a nonexistent + // server-side stream. + QuicSpdySession* session = client_->client()->client_session(); + QuicSessionPeer::SetNextOutgoingBidirectionalStreamId( + session, GetNthServerInitiatedBidirectionalId(0)); + + client_->SendCustomSynchronousRequest(headers, body); + EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); + EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error()); +} + +// Test that if the server will close the connection if the client attempts +// to send a request with overly large headers. +TEST_P(EndToEndTest, LargeHeaders) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + QuicString body(kMaxPacketSize, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["key1"] = QuicString(15 * 1024, 'a'); + headers["key2"] = QuicString(15 * 1024, 'a'); + headers["key3"] = QuicString(15 * 1024, 'a'); + + client_->SendCustomSynchronousRequest(headers, body); + EXPECT_EQ(QUIC_HEADERS_TOO_LARGE, client_->stream_error()); + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); +} + +TEST_P(EndToEndTest, EarlyResponseWithQuicStreamNoError) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + QuicString large_body(1024 * 1024, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + // Insert an invalid content_length field in request to trigger an early + // response from server. + headers["content-length"] = "-3"; + + client_->SendCustomSynchronousRequest(headers, large_body); + EXPECT_EQ("bad", client_->response_body()); + EXPECT_EQ("500", client_->response_headers()->find(":status")->second); + EXPECT_EQ(QUIC_STREAM_NO_ERROR, client_->stream_error()); + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); +} + +// TODO(rch): this test seems to cause net_unittests timeouts :| +TEST_P(EndToEndTestWithTls, QUIC_TEST_DISABLED_IN_CHROME(MultipleTermination)) { + ASSERT_TRUE(Initialize()); + + // Set the offset so we won't frame. Otherwise when we pick up termination + // before HTTP framing is complete, we send an error and close the stream, + // and the second write is picked up as writing on a closed stream. + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + ASSERT_TRUE(stream != nullptr); + QuicStreamPeer::SetStreamBytesWritten(3, stream); + + client_->SendData("bar", true); + client_->WaitForWriteToFlush(); + + // By default the stream protects itself from writes after terminte is set. + // Override this to test the server handling buggy clients. + QuicStreamPeer::SetWriteSideClosed(false, client_->GetOrCreateStream()); + + EXPECT_QUIC_BUG(client_->SendData("eep", true), "Fin already buffered"); +} + +// TODO(nharper): Needs to get turned back to EndToEndTestWithTls +// when we figure out why the test doesn't work on chrome. +TEST_P(EndToEndTest, Timeout) { + client_config_.SetIdleNetworkTimeout(QuicTime::Delta::FromMicroseconds(500), + QuicTime::Delta::FromMicroseconds(500)); + // Note: we do NOT ASSERT_TRUE: we may time out during initial handshake: + // that's enough to validate timeout in this case. + Initialize(); + while (client_->client()->connected()) { + client_->client()->WaitForEvents(); + } +} + +TEST_P(EndToEndTestWithTls, MaxIncomingDynamicStreamsLimitRespected) { + // Set a limit on maximum number of incoming dynamic streams. + // Make sure the limit is respected. + const uint32_t kServerMaxIncomingDynamicStreams = 1; + server_config_.SetMaxIncomingDynamicStreamsToSend( + kServerMaxIncomingDynamicStreams); + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + + // Make the client misbehave after negotiation. + const int kServerMaxStreams = kMaxStreamsMinimumIncrement + 1; + QuicSessionPeer::SetMaxOpenOutgoingStreams( + client_->client()->client_session(), kServerMaxStreams + 1); + + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["content-length"] = "3"; + + // The server supports a small number of additional streams beyond the + // negotiated limit. Open enough streams to go beyond that limit. + for (int i = 0; i < kServerMaxStreams + 1; ++i) { + client_->SendMessage(headers, "", /*fin=*/false); + } + client_->WaitForResponse(); + if (client_connection->transport_version() != QUIC_VERSION_99) { + EXPECT_TRUE(client_->connected()); + EXPECT_EQ(QUIC_REFUSED_STREAM, client_->stream_error()); + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); + } else { + // Version 99 disconnects the connection if we exceed the stream limit. + EXPECT_FALSE(client_->connected()); + EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); + EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error()); + } +} + +TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) { + // Each endpoint can set max incoming dynamic streams independently. + const uint32_t kClientMaxIncomingDynamicStreams = 2; + const uint32_t kServerMaxIncomingDynamicStreams = 1; + client_config_.SetMaxIncomingDynamicStreamsToSend( + kClientMaxIncomingDynamicStreams); + server_config_.SetMaxIncomingDynamicStreamsToSend( + kServerMaxIncomingDynamicStreams); + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // The client has received the server's limit and vice versa. + QuicSpdyClientSession* client_session = client_->client()->client_session(); + size_t client_max_open_outgoing_bidirectional_streams = + client_session->connection()->transport_version() == QUIC_VERSION_99 + ? QuicSessionPeer::v99_streamid_manager(client_session) + ->max_allowed_outgoing_bidirectional_streams() + : QuicSessionPeer::GetStreamIdManager(client_session) + ->max_open_outgoing_streams(); + size_t client_max_open_outgoing_unidirectional_streams = + client_session->connection()->transport_version() == QUIC_VERSION_99 + ? QuicSessionPeer::v99_streamid_manager(client_session) + ->max_allowed_outgoing_unidirectional_streams() + : QuicSessionPeer::GetStreamIdManager(client_session) + ->max_open_outgoing_streams(); + EXPECT_EQ(kServerMaxIncomingDynamicStreams, + client_max_open_outgoing_bidirectional_streams); + EXPECT_EQ(kServerMaxIncomingDynamicStreams, + client_max_open_outgoing_unidirectional_streams); + server_thread_->Pause(); + QuicSession* server_session = GetServerSession(); + size_t server_max_open_outgoing_bidirectional_streams = + server_session->connection()->transport_version() == QUIC_VERSION_99 + ? QuicSessionPeer::v99_streamid_manager(server_session) + ->max_allowed_outgoing_bidirectional_streams() + : QuicSessionPeer::GetStreamIdManager(server_session) + ->max_open_outgoing_streams(); + size_t server_max_open_outgoing_unidirectional_streams = + server_session->connection()->transport_version() == QUIC_VERSION_99 + ? QuicSessionPeer::v99_streamid_manager(server_session) + ->max_allowed_outgoing_unidirectional_streams() + : QuicSessionPeer::GetStreamIdManager(server_session) + ->max_open_outgoing_streams(); + EXPECT_EQ(kClientMaxIncomingDynamicStreams, + server_max_open_outgoing_bidirectional_streams); + EXPECT_EQ(kClientMaxIncomingDynamicStreams, + server_max_open_outgoing_unidirectional_streams); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, NegotiateCongestionControl) { + ASSERT_TRUE(Initialize()); + + // For PCC, the underlying implementation may be a stub with a + // different name-tag. Skip the rest of this test. + if (GetParam().congestion_control_tag == kTPCC) { + return; + } + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + CongestionControlType expected_congestion_control_type = kRenoBytes; + switch (GetParam().congestion_control_tag) { + case kRENO: + expected_congestion_control_type = kRenoBytes; + break; + case kTBBR: + expected_congestion_control_type = kBBR; + break; + case kQBIC: + expected_congestion_control_type = kCubicBytes; + break; + default: + QUIC_DLOG(FATAL) << "Unexpected congestion control tag"; + } + + server_thread_->Pause(); + EXPECT_EQ(expected_congestion_control_type, + QuicSentPacketManagerPeer::GetSendAlgorithm( + *GetSentPacketManagerFromFirstServerSession()) + ->GetCongestionControlType()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, ClientSuggestsRTT) { + // Client suggests initial RTT, verify it is used. + const QuicTime::Delta kInitialRTT = QuicTime::Delta::FromMicroseconds(20000); + client_config_.SetInitialRoundTripTimeUsToSend(kInitialRTT.ToMicroseconds()); + + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + const QuicSentPacketManager& client_sent_packet_manager = + client_->client()->client_session()->connection()->sent_packet_manager(); + const QuicSentPacketManager* server_sent_packet_manager = + GetSentPacketManagerFromFirstServerSession(); + + EXPECT_EQ(kInitialRTT, + client_sent_packet_manager.GetRttStats()->initial_rtt()); + EXPECT_EQ(kInitialRTT, + server_sent_packet_manager->GetRttStats()->initial_rtt()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, ClientSuggestsIgnoredRTT) { + // Client suggests initial RTT, but also specifies NRTT, so it's not used. + const QuicTime::Delta kInitialRTT = QuicTime::Delta::FromMicroseconds(20000); + client_config_.SetInitialRoundTripTimeUsToSend(kInitialRTT.ToMicroseconds()); + QuicTagVector options; + options.push_back(kNRTT); + client_config_.SetConnectionOptionsToSend(options); + + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + const QuicSentPacketManager& client_sent_packet_manager = + client_->client()->client_session()->connection()->sent_packet_manager(); + const QuicSentPacketManager* server_sent_packet_manager = + GetSentPacketManagerFromFirstServerSession(); + + EXPECT_EQ(kInitialRTT, + client_sent_packet_manager.GetRttStats()->initial_rtt()); + EXPECT_EQ(kInitialRTT, + server_sent_packet_manager->GetRttStats()->initial_rtt()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, MaxInitialRTT) { + // Client tries to suggest twice the server's max initial rtt and the server + // uses the max. + client_config_.SetInitialRoundTripTimeUsToSend(2 * + kMaxInitialRoundTripTimeUs); + + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + const QuicSentPacketManager& client_sent_packet_manager = + client_->client()->client_session()->connection()->sent_packet_manager(); + + // Now that acks have been exchanged, the RTT estimate has decreased on the + // server and is not infinite on the client. + EXPECT_FALSE( + client_sent_packet_manager.GetRttStats()->smoothed_rtt().IsInfinite()); + const RttStats& server_rtt_stats = + *GetServerConnection()->sent_packet_manager().GetRttStats(); + EXPECT_EQ(static_cast<int64_t>(kMaxInitialRoundTripTimeUs), + server_rtt_stats.initial_rtt().ToMicroseconds()); + EXPECT_GE(static_cast<int64_t>(kMaxInitialRoundTripTimeUs), + server_rtt_stats.smoothed_rtt().ToMicroseconds()); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, MinInitialRTT) { + // Client tries to suggest 0 and the server uses the default. + client_config_.SetInitialRoundTripTimeUsToSend(0); + + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + const QuicSentPacketManager& client_sent_packet_manager = + client_->client()->client_session()->connection()->sent_packet_manager(); + const QuicSentPacketManager& server_sent_packet_manager = + GetServerConnection()->sent_packet_manager(); + + // Now that acks have been exchanged, the RTT estimate has decreased on the + // server and is not infinite on the client. + EXPECT_FALSE( + client_sent_packet_manager.GetRttStats()->smoothed_rtt().IsInfinite()); + // Expect the default rtt of 100ms. + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), + server_sent_packet_manager.GetRttStats()->initial_rtt()); + // Ensure the bandwidth is valid. + client_sent_packet_manager.BandwidthEstimate(); + server_sent_packet_manager.BandwidthEstimate(); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, 0ByteConnectionId) { + client_config_.SetBytesForConnectionIdToSend(0); + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + QuicPacketHeader* header = + QuicConnectionPeer::GetLastHeader(client_connection); + EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included); +} + +TEST_P(EndToEndTestWithTls, 8ByteConnectionId) { + client_config_.SetBytesForConnectionIdToSend(8); + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + QuicPacketHeader* header = + QuicConnectionPeer::GetLastHeader(client_connection); + if (client_connection->transport_version() > QUIC_VERSION_43) { + EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included); + } else { + EXPECT_EQ(CONNECTION_ID_PRESENT, + header->destination_connection_id_included); + } +} + +TEST_P(EndToEndTestWithTls, 15ByteConnectionId) { + client_config_.SetBytesForConnectionIdToSend(15); + ASSERT_TRUE(Initialize()); + + // Our server is permissive and allows for out of bounds values. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + QuicPacketHeader* header = + QuicConnectionPeer::GetLastHeader(client_connection); + if (client_connection->transport_version() > QUIC_VERSION_43) { + EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included); + } else { + EXPECT_EQ(CONNECTION_ID_PRESENT, + header->destination_connection_id_included); + } +} + +TEST_P(EndToEndTestWithTls, ResetConnection) { + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + client_->ResetConnection(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +// TODO(nharper): Needs to get turned back to EndToEndTestWithTls +// when we figure out why the test doesn't work on chrome. +TEST_P(EndToEndTest, MaxStreamsUberTest) { + if (!BothSidesSupportStatelessRejects()) { + // Connect with lower fake packet loss than we'd like to test. Until + // b/10126687 is fixed, losing handshake packets is pretty brutal. + // TODO(jokulik): Until we support redundant SREJ packets, don't + // drop handshake packets for stateless rejects. + SetPacketLossPercentage(1); + } + ASSERT_TRUE(Initialize()); + QuicString large_body(10240, 'a'); + int max_streams = 100; + + AddToCache("/large_response", 200, large_body); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + SetPacketLossPercentage(10); + + for (int i = 0; i < max_streams; ++i) { + EXPECT_LT(0, client_->SendRequest("/large_response")); + } + + // WaitForEvents waits 50ms and returns true if there are outstanding + // requests. + while (client_->client()->WaitForEvents() == true) { + } +} + +TEST_P(EndToEndTestWithTls, StreamCancelErrorTest) { + ASSERT_TRUE(Initialize()); + QuicString small_body(256, 'a'); + + AddToCache("/small_response", 200, small_body); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + QuicSession* session = client_->client()->client_session(); + // Lose the request. + SetPacketLossPercentage(100); + EXPECT_LT(0, client_->SendRequest("/small_response")); + client_->client()->WaitForEvents(); + // Transmit the cancel, and ensure the connection is torn down properly. + SetPacketLossPercentage(0); + QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); + session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0); + + // WaitForEvents waits 50ms and returns true if there are outstanding + // requests. + while (client_->client()->WaitForEvents() == true) { + } + // It should be completely fine to RST a stream before any data has been + // received for that stream. + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); +} + +TEST_P(EndToEndTest, ConnectionMigrationClientIPChanged) { + ASSERT_TRUE(Initialize()); + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Store the client IP address which was used to send the first request. + QuicIpAddress old_host = + client_->client()->network_helper()->GetLatestClientAddress().host(); + + // Migrate socket to the new IP address. + QuicIpAddress new_host = TestLoopback(2); + EXPECT_NE(old_host, new_host); + ASSERT_TRUE(client_->client()->MigrateSocket(new_host)); + + // Send a request using the new socket. + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) { + // Tests that the client's port can change during an established QUIC + // connection, and that doing so does not result in the connection being + // closed by the server. + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Store the client address which was used to send the first request. + QuicSocketAddress old_address = + client_->client()->network_helper()->GetLatestClientAddress(); + int old_fd = client_->client()->GetLatestFD(); + + // Create a new socket before closing the old one, which will result in a new + // ephemeral port. + QuicClientPeer::CreateUDPSocketAndBind(client_->client()); + + // Stop listening and close the old FD. + QuicClientPeer::CleanUpUDPSocket(client_->client(), old_fd); + + // The packet writer needs to be updated to use the new FD. + client_->client()->network_helper()->CreateQuicPacketWriter(); + + // Change the internal state of the client and connection to use the new port, + // this is done because in a real NAT rebinding the client wouldn't see any + // port change, and so expects no change to incoming port. + // This is kind of ugly, but needed as we are simply swapping out the client + // FD rather than any more complex NAT rebinding simulation. + int new_port = + client_->client()->network_helper()->GetLatestClientAddress().port(); + QuicClientPeer::SetClientPort(client_->client(), new_port); + QuicConnectionPeer::SetSelfAddress( + client_->client()->client_session()->connection(), + QuicSocketAddress(client_->client() + ->client_session() + ->connection() + ->self_address() + .host(), + new_port)); + + // Register the new FD for epoll events. + int new_fd = client_->client()->GetLatestFD(); + QuicEpollServer* eps = client_->epoll_server(); + eps->RegisterFD(new_fd, client_->client()->epoll_network_helper(), + EPOLLIN | EPOLLOUT | EPOLLET); + + // Send a second request, using the new FD. + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Verify that the client's ephemeral port is different. + QuicSocketAddress new_address = + client_->client()->network_helper()->GetLatestClientAddress(); + EXPECT_EQ(old_address.host(), new_address.host()); + EXPECT_NE(old_address.port(), new_address.port()); +} + +TEST_P(EndToEndTest, NegotiatedInitialCongestionWindow) { + SetQuicReloadableFlag(quic_unified_iw_options, true); + client_extra_copts_.push_back(kIW03); + + ASSERT_TRUE(Initialize()); + + // Values are exchanged during crypto handshake, so wait for that to finish. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + server_thread_->Pause(); + + QuicPacketCount cwnd = + GetServerConnection()->sent_packet_manager().initial_congestion_window(); + EXPECT_EQ(3u, cwnd); +} + +TEST_P(EndToEndTest, DifferentFlowControlWindows) { + // Client and server can set different initial flow control receive windows. + // These are sent in CHLO/SHLO. Tests that these values are exchanged properly + // in the crypto handshake. + const uint32_t kClientStreamIFCW = 123456; + const uint32_t kClientSessionIFCW = 234567; + set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW); + set_client_initial_session_flow_control_receive_window(kClientSessionIFCW); + + uint32_t kServerStreamIFCW = 32 * 1024; + uint32_t kServerSessionIFCW = 48 * 1024; + set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW); + set_server_initial_session_flow_control_receive_window(kServerSessionIFCW); + + ASSERT_TRUE(Initialize()); + + // Values are exchanged during crypto handshake, so wait for that to finish. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Open a data stream to make sure the stream level flow control is updated. + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + stream->WriteOrBufferBody("hello", false); + + // Client should have the right values for server's receive window. + EXPECT_EQ(kServerStreamIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kServerSessionIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( + stream->flow_controller())); + EXPECT_EQ(kServerSessionIFCW, + QuicFlowControllerPeer::SendWindowOffset( + client_->client()->client_session()->flow_controller())); + + // Server should have the right values for client's receive window. + server_thread_->Pause(); + QuicSession* session = GetServerSession(); + EXPECT_EQ(kClientStreamIFCW, + session->config()->ReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kClientSessionIFCW, + session->config()->ReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( + session->flow_controller())); + server_thread_->Resume(); +} + +// Test negotiation of IFWA connection option. +TEST_P(EndToEndTest, NegotiatedServerInitialFlowControlWindow) { + const uint32_t kClientStreamIFCW = 123456; + const uint32_t kClientSessionIFCW = 234567; + set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW); + set_client_initial_session_flow_control_receive_window(kClientSessionIFCW); + + uint32_t kServerStreamIFCW = 32 * 1024; + uint32_t kServerSessionIFCW = 48 * 1024; + set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW); + set_server_initial_session_flow_control_receive_window(kServerSessionIFCW); + + // Bump the window. + const uint32_t kExpectedStreamIFCW = 1024 * 1024; + const uint32_t kExpectedSessionIFCW = 1.5 * 1024 * 1024; + client_extra_copts_.push_back(kIFWA); + + ASSERT_TRUE(Initialize()); + + // Values are exchanged during crypto handshake, so wait for that to finish. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + // Open a data stream to make sure the stream level flow control is updated. + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + stream->WriteOrBufferBody("hello", false); + + // Client should have the right values for server's receive window. + EXPECT_EQ(kExpectedStreamIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kExpectedSessionIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kExpectedStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( + stream->flow_controller())); + EXPECT_EQ(kExpectedSessionIFCW, + QuicFlowControllerPeer::SendWindowOffset( + client_->client()->client_session()->flow_controller())); +} + +TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { + // The special headers and crypto streams should be subject to per-stream flow + // control limits, but should not be subject to connection level flow control + const uint32_t kStreamIFCW = 32 * 1024; + const uint32_t kSessionIFCW = 48 * 1024; + set_client_initial_stream_flow_control_receive_window(kStreamIFCW); + set_client_initial_session_flow_control_receive_window(kSessionIFCW); + set_server_initial_stream_flow_control_receive_window(kStreamIFCW); + set_server_initial_session_flow_control_receive_window(kSessionIFCW); + + ASSERT_TRUE(Initialize()); + + // Wait for crypto handshake to finish. This should have contributed to the + // crypto stream flow control window, but not affected the session flow + // control window. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream( + client_->client()->client_session()); + // In v47 and later, the crypto handshake (sent in CRYPTO frames) is not + // subject to flow control. + if (client_->client()->client_session()->connection()->transport_version() < + QUIC_VERSION_47) { + EXPECT_LT(QuicFlowControllerPeer::SendWindowSize( + crypto_stream->flow_controller()), + kStreamIFCW); + } + EXPECT_EQ(kSessionIFCW, + QuicFlowControllerPeer::SendWindowSize( + client_->client()->client_session()->flow_controller())); + + // Send a request with no body, and verify that the connection level window + // has not been affected. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + + QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream( + client_->client()->client_session()); + EXPECT_LT( + QuicFlowControllerPeer::SendWindowSize(headers_stream->flow_controller()), + kStreamIFCW); + EXPECT_EQ(kSessionIFCW, + QuicFlowControllerPeer::SendWindowSize( + client_->client()->client_session()->flow_controller())); + + // Server should be in a similar state: connection flow control window should + // not have any bytes marked as received. + server_thread_->Pause(); + QuicSession* session = GetServerSession(); + QuicFlowController* server_connection_flow_controller = + session->flow_controller(); + EXPECT_EQ(kSessionIFCW, QuicFlowControllerPeer::ReceiveWindowSize( + server_connection_flow_controller)); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, FlowControlsSynced) { + set_smaller_flow_control_receive_window(); + + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + server_thread_->Pause(); + QuicSpdySession* const client_session = client_->client()->client_session(); + auto* server_session = static_cast<QuicSpdySession*>(GetServerSession()); + ExpectFlowControlsSynced(client_session->flow_controller(), + server_session->flow_controller()); + ExpectFlowControlsSynced( + QuicSessionPeer::GetMutableCryptoStream(client_session) + ->flow_controller(), + QuicSessionPeer::GetMutableCryptoStream(server_session) + ->flow_controller()); + SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION); + SpdySettingsIR settings_frame; + settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, + kDefaultMaxUncompressedHeaderSize); + SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame)); + QuicFlowController* client_header_stream_flow_controller = + QuicSpdySessionPeer::GetHeadersStream(client_session)->flow_controller(); + QuicFlowController* server_header_stream_flow_controller = + QuicSpdySessionPeer::GetHeadersStream(server_session)->flow_controller(); + // Both client and server are sending this SETTINGS frame, and the send + // window is consumed. But because of timing issue, the server may send or + // not send the frame, and the client may send/ not send / receive / not + // receive the frame. + // TODO(fayang): Rewrite this part because it is hacky. + QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize( + server_header_stream_flow_controller) - + QuicFlowControllerPeer::SendWindowSize( + client_header_stream_flow_controller); + QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize( + client_header_stream_flow_controller) - + QuicFlowControllerPeer::SendWindowSize( + server_header_stream_flow_controller); + EXPECT_TRUE(win_difference1 == 0 || win_difference1 == frame.size()); + EXPECT_TRUE(win_difference2 == 0 || win_difference2 == frame.size()); + + // Client *may* have received the SETTINGs frame. + // TODO(fayang): Rewrite this part because it is hacky. + float ratio1 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize( + client_session->flow_controller())) / + QuicFlowControllerPeer::ReceiveWindowSize( + QuicSpdySessionPeer::GetHeadersStream(client_session) + ->flow_controller()); + float ratio2 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize( + client_session->flow_controller())) / + (QuicFlowControllerPeer::ReceiveWindowSize( + QuicSpdySessionPeer::GetHeadersStream(client_session) + ->flow_controller()) + + frame.size()); + EXPECT_TRUE(ratio1 == kSessionToStreamRatio || + ratio2 == kSessionToStreamRatio); + + server_thread_->Resume(); +} + +TEST_P(EndToEndTestWithTls, RequestWithNoBodyWillNeverSendStreamFrameWithFIN) { + // A stream created on receipt of a simple request with no body will never get + // a stream frame with a FIN. Verify that we don't keep track of the stream in + // the locally closed streams map: it will never be removed if so. + ASSERT_TRUE(Initialize()); + + // Send a simple headers only request, and receive response. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Now verify that the server is not waiting for a final FIN or RST. + server_thread_->Pause(); + QuicSession* session = GetServerSession(); + EXPECT_EQ( + 0u, + QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(session).size()); + server_thread_->Resume(); +} + +// A TestAckListener verifies that its OnAckNotification method has been +// called exactly once on destruction. +class TestAckListener : public QuicAckListenerInterface { + public: + explicit TestAckListener(int bytes_to_ack) : bytes_to_ack_(bytes_to_ack) {} + + void OnPacketAcked(int acked_bytes, + QuicTime::Delta /*delta_largest_observed*/) override { + ASSERT_LE(acked_bytes, bytes_to_ack_); + bytes_to_ack_ -= acked_bytes; + } + + void OnPacketRetransmitted(int /*retransmitted_bytes*/) override {} + + bool has_been_notified() const { return bytes_to_ack_ == 0; } + + protected: + // Object is ref counted. + ~TestAckListener() override { EXPECT_EQ(0, bytes_to_ack_); } + + private: + int bytes_to_ack_; +}; + +class TestResponseListener : public QuicSpdyClientBase::ResponseListener { + public: + void OnCompleteResponse(QuicStreamId id, + const SpdyHeaderBlock& response_headers, + const QuicString& response_body) override { + QUIC_DVLOG(1) << "response for stream " << id << " " + << response_headers.DebugString() << "\n" + << response_body; + } +}; + +TEST_P(EndToEndTest, AckNotifierWithPacketLossAndBlockedSocket) { + // Verify that even in the presence of packet loss and occasionally blocked + // socket, an AckNotifierDelegate will get informed that the data it is + // interested in has been ACKed. This tests end-to-end ACK notification, and + // demonstrates that retransmissions do not break this functionality. + if (!BothSidesSupportStatelessRejects()) { + // TODO(jokulik): Until we support redundant SREJ packets, don't + // drop handshake packets for stateless rejects. + SetPacketLossPercentage(5); + } + ASSERT_TRUE(Initialize()); + + // Wait for the server SHLO before upping the packet loss. + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + SetPacketLossPercentage(30); + client_writer_->set_fake_blocked_socket_percentage(10); + + // Create a POST request and send the headers only. + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + client_->SendMessage(headers, "", /*fin=*/false); + + // Test the AckNotifier's ability to track multiple packets by making the + // request body exceed the size of a single packet. + QuicString request_string = + "a request body bigger than one packet" + QuicString(kMaxPacketSize, '.'); + + // The TestAckListener will cause a failure if not notified. + QuicReferenceCountedPointer<TestAckListener> ack_listener( + new TestAckListener(request_string.length())); + + // Send the request, and register the delegate for ACKs. + client_->SendData(request_string, true, ack_listener); + client_->WaitForResponse(); + EXPECT_EQ(kFooResponseBody, client_->response_body()); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Send another request to flush out any pending ACKs on the server. + client_->SendSynchronousRequest("/bar"); + + // Make sure the delegate does get the notification it expects. + while (!ack_listener->has_been_notified()) { + // Waits for up to 50 ms. + client_->client()->WaitForEvents(); + } +} + +// Send a public reset from the server. +TEST_P(EndToEndTestWithTls, ServerSendPublicReset) { + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + if (SupportsIetfQuicWithTls(client_connection->version())) { + // TLS handshake does not support stateless reset token yet. + return; + } + QuicUint128 stateless_reset_token = 0; + if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) { + QuicConfig* config = client_->client()->session()->config(); + EXPECT_TRUE(config->HasReceivedStatelessResetToken()); + stateless_reset_token = config->ReceivedStatelessResetToken(); + } + + // Send the public reset. + QuicConnectionId connection_id = client_connection->connection_id(); + QuicPublicResetPacket header; + header.connection_id = connection_id; + QuicFramer framer(server_supported_versions_, QuicTime::Zero(), + Perspective::IS_SERVER, kQuicDefaultConnectionIdLength); + std::unique_ptr<QuicEncryptedPacket> packet; + if (client_connection->transport_version() > QUIC_VERSION_43) { + packet = framer.BuildIetfStatelessResetPacket(connection_id, + stateless_reset_token); + } else { + packet = framer.BuildPublicResetPacket(header); + } + // We must pause the server's thread in order to call WritePacket without + // race conditions. + server_thread_->Pause(); + server_writer_->WritePacket( + packet->data(), packet->length(), server_address_.host(), + client_->client()->network_helper()->GetLatestClientAddress(), nullptr); + server_thread_->Resume(); + + // The request should fail. + EXPECT_EQ("", client_->SendSynchronousRequest("/foo")); + EXPECT_TRUE(client_->response_headers()->empty()); + EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error()); +} + +// Send a public reset from the server for a different connection ID. +// It should be ignored. +TEST_P(EndToEndTestWithTls, ServerSendPublicResetWithDifferentConnectionId) { + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + if (SupportsIetfQuicWithTls(client_connection->version())) { + // TLS handshake does not support stateless reset token yet. + return; + } + QuicUint128 stateless_reset_token = 0; + if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) { + QuicConfig* config = client_->client()->session()->config(); + EXPECT_TRUE(config->HasReceivedStatelessResetToken()); + stateless_reset_token = config->ReceivedStatelessResetToken(); + } + // Send the public reset. + QuicConnectionId incorrect_connection_id = TestConnectionId( + TestConnectionIdToUInt64(client_connection->connection_id()) + 1); + QuicPublicResetPacket header; + header.connection_id = incorrect_connection_id; + QuicFramer framer(server_supported_versions_, QuicTime::Zero(), + Perspective::IS_SERVER, kQuicDefaultConnectionIdLength); + std::unique_ptr<QuicEncryptedPacket> packet; + testing::NiceMock<MockQuicConnectionDebugVisitor> visitor; + client_->client()->client_session()->connection()->set_debug_visitor( + &visitor); + if (client_connection->transport_version() > QUIC_VERSION_43) { + packet = framer.BuildIetfStatelessResetPacket(incorrect_connection_id, + stateless_reset_token); + EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id)) + .Times(0); + } else { + packet = framer.BuildPublicResetPacket(header); + EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id)) + .Times(1); + } + // We must pause the server's thread in order to call WritePacket without + // race conditions. + server_thread_->Pause(); + server_writer_->WritePacket( + packet->data(), packet->length(), server_address_.host(), + client_->client()->network_helper()->GetLatestClientAddress(), nullptr); + server_thread_->Resume(); + + if (client_connection->transport_version() > QUIC_VERSION_43) { + // The request should fail. IETF stateless reset does not include connection + // ID. + EXPECT_EQ("", client_->SendSynchronousRequest("/foo")); + EXPECT_TRUE(client_->response_headers()->empty()); + EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error()); + return; + } + // The connection should be unaffected. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + client_->client()->client_session()->connection()->set_debug_visitor(nullptr); +} + +// Send a public reset from the client for a different connection ID. +// It should be ignored. +TEST_P(EndToEndTestWithTls, ClientSendPublicResetWithDifferentConnectionId) { + ASSERT_TRUE(Initialize()); + + // Send the public reset. + QuicConnectionId incorrect_connection_id = TestConnectionId( + TestConnectionIdToUInt64( + client_->client()->client_session()->connection()->connection_id()) + + 1); + QuicPublicResetPacket header; + header.connection_id = incorrect_connection_id; + QuicFramer framer(server_supported_versions_, QuicTime::Zero(), + Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength); + std::unique_ptr<QuicEncryptedPacket> packet( + framer.BuildPublicResetPacket(header)); + client_writer_->WritePacket( + packet->data(), packet->length(), + client_->client()->network_helper()->GetLatestClientAddress().host(), + server_address_, nullptr); + + // The connection should be unaffected. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +// Send a version negotiation packet from the server for a different +// connection ID. It should be ignored. +TEST_P(EndToEndTestWithTls, + ServerSendVersionNegotiationWithDifferentConnectionId) { + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // Send the version negotiation packet. + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + QuicConnectionId incorrect_connection_id = TestConnectionId( + TestConnectionIdToUInt64(client_connection->connection_id()) + 1); + std::unique_ptr<QuicEncryptedPacket> packet( + QuicFramer::BuildVersionNegotiationPacket( + incorrect_connection_id, + client_connection->transport_version() > QUIC_VERSION_43, + server_supported_versions_)); + testing::NiceMock<MockQuicConnectionDebugVisitor> visitor; + client_connection->set_debug_visitor(&visitor); + EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id)) + .Times(1); + // We must pause the server's thread in order to call WritePacket without + // race conditions. + server_thread_->Pause(); + server_writer_->WritePacket( + packet->data(), packet->length(), server_address_.host(), + client_->client()->network_helper()->GetLatestClientAddress(), nullptr); + server_thread_->Resume(); + + // The connection should be unaffected. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + client_connection->set_debug_visitor(nullptr); +} + +// A bad header shouldn't tear down the connection, because the receiver can't +// tell the connection ID. +TEST_P(EndToEndTestWithTls, BadPacketHeaderTruncated) { + ASSERT_TRUE(Initialize()); + + // Start the connection. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Packet with invalid public flags. + char packet[] = {// public flags (8 byte connection_id) + 0x3C, + // truncated connection ID + 0x11}; + client_writer_->WritePacket( + &packet[0], sizeof(packet), + client_->client()->network_helper()->GetLatestClientAddress().host(), + server_address_, nullptr); + // Give the server time to process the packet. + QuicSleep(QuicTime::Delta::FromMilliseconds(100)); + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, + QuicDispatcherPeer::GetAndClearLastError(dispatcher)); + server_thread_->Resume(); + + // The connection should not be terminated. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +// A bad header shouldn't tear down the connection, because the receiver can't +// tell the connection ID. +TEST_P(EndToEndTestWithTls, BadPacketHeaderFlags) { + ASSERT_TRUE(Initialize()); + + // Start the connection. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Packet with invalid public flags. + char packet[] = { + // invalid public flags + 0xFF, + // connection_id + 0x10, + 0x32, + 0x54, + 0x76, + 0x98, + 0xBA, + 0xDC, + 0xFE, + // packet sequence number + 0xBC, + 0x9A, + 0x78, + 0x56, + 0x34, + 0x12, + // private flags + 0x00, + }; + client_writer_->WritePacket( + &packet[0], sizeof(packet), + client_->client()->network_helper()->GetLatestClientAddress().host(), + server_address_, nullptr); + // Give the server time to process the packet. + QuicSleep(QuicTime::Delta::FromMilliseconds(100)); + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, + QuicDispatcherPeer::GetAndClearLastError(dispatcher)); + server_thread_->Resume(); + + // The connection should not be terminated. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +// Send a packet from the client with bad encrypted data. The server should not +// tear down the connection. +TEST_P(EndToEndTestWithTls, BadEncryptedData) { + ASSERT_TRUE(Initialize()); + + // Start the connection. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + client_->client()->client_session()->connection()->connection_id(), + EmptyQuicConnectionId(), false, false, 1, "At least 20 characters.", + CONNECTION_ID_PRESENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER)); + // Damage the encrypted data. + QuicString damaged_packet(packet->data(), packet->length()); + damaged_packet[30] ^= 0x01; + QUIC_DLOG(INFO) << "Sending bad packet."; + client_writer_->WritePacket( + damaged_packet.data(), damaged_packet.length(), + client_->client()->network_helper()->GetLatestClientAddress().host(), + server_address_, nullptr); + // Give the server time to process the packet. + QuicSleep(QuicTime::Delta::FromMilliseconds(100)); + // This error is sent to the connection's OnError (which ignores it), so the + // dispatcher doesn't see it. + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + EXPECT_EQ(QUIC_NO_ERROR, + QuicDispatcherPeer::GetAndClearLastError(dispatcher)); + server_thread_->Resume(); + + // The connection should not be terminated. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +TEST_P(EndToEndTestWithTls, CanceledStreamDoesNotBecomeZombie) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + // Lose the request. + SetPacketLossPercentage(100); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + client_->SendMessage(headers, "test_body", /*fin=*/false); + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + + // Cancel the stream. + stream->Reset(QUIC_STREAM_CANCELLED); + QuicSession* session = client_->client()->client_session(); + // Verify canceled stream does not become zombie. + EXPECT_TRUE(QuicSessionPeer::zombie_streams(session).empty()); + EXPECT_EQ(1u, QuicSessionPeer::closed_streams(session).size()); +} + +// A test stream that gives |response_body_| as an error response body. +class ServerStreamWithErrorResponseBody : public QuicSimpleServerStream { + public: + ServerStreamWithErrorResponseBody( + QuicStreamId id, + QuicSpdySession* session, + QuicSimpleServerBackend* quic_simple_server_backend, + QuicString response_body) + : QuicSimpleServerStream(id, + session, + BIDIRECTIONAL, + quic_simple_server_backend), + response_body_(std::move(response_body)) {} + + ~ServerStreamWithErrorResponseBody() override = default; + + protected: + void SendErrorResponse() override { + QUIC_DLOG(INFO) << "Sending error response for stream " << id(); + SpdyHeaderBlock headers; + headers[":status"] = "500"; + headers["content-length"] = + QuicTextUtils::Uint64ToString(response_body_.size()); + // This method must call CloseReadSide to cause the test case, StopReading + // is not sufficient. + QuicStreamPeer::CloseReadSide(this); + SendHeadersAndBody(std::move(headers), response_body_); + } + + QuicString response_body_; +}; + +class StreamWithErrorFactory : public QuicTestServer::StreamFactory { + public: + explicit StreamWithErrorFactory(QuicString response_body) + : response_body_(std::move(response_body)) {} + + ~StreamWithErrorFactory() override = default; + + QuicSimpleServerStream* CreateStream( + QuicStreamId id, + QuicSpdySession* session, + QuicSimpleServerBackend* quic_simple_server_backend) override { + return new ServerStreamWithErrorResponseBody( + id, session, quic_simple_server_backend, response_body_); + } + + private: + QuicString response_body_; +}; + +// A test server stream that drops all received body. +class ServerStreamThatDropsBody : public QuicSimpleServerStream { + public: + ServerStreamThatDropsBody(QuicStreamId id, + QuicSpdySession* session, + QuicSimpleServerBackend* quic_simple_server_backend) + : QuicSimpleServerStream(id, + session, + BIDIRECTIONAL, + quic_simple_server_backend) {} + + ~ServerStreamThatDropsBody() override = default; + + protected: + void OnBodyAvailable() override { + while (HasBytesToRead()) { + struct iovec iov; + if (GetReadableRegions(&iov, 1) == 0) { + // No more data to read. + break; + } + QUIC_DVLOG(1) << "Processed " << iov.iov_len << " bytes for stream " + << id(); + MarkConsumed(iov.iov_len); + } + + if (!sequencer()->IsClosed()) { + sequencer()->SetUnblocked(); + return; + } + + // If the sequencer is closed, then all the body, including the fin, has + // been consumed. + OnFinRead(); + + if (write_side_closed() || fin_buffered()) { + return; + } + + SendResponse(); + } +}; + +class ServerStreamThatDropsBodyFactory : public QuicTestServer::StreamFactory { + public: + ServerStreamThatDropsBodyFactory() = default; + + ~ServerStreamThatDropsBodyFactory() override = default; + + QuicSimpleServerStream* CreateStream( + QuicStreamId id, + QuicSpdySession* session, + QuicSimpleServerBackend* quic_simple_server_backend) override { + return new ServerStreamThatDropsBody(id, session, + quic_simple_server_backend); + } +}; + +// A test server stream that sends response with body size greater than 4GB. +class ServerStreamThatSendsHugeResponse : public QuicSimpleServerStream { + public: + ServerStreamThatSendsHugeResponse( + QuicStreamId id, + QuicSpdySession* session, + QuicSimpleServerBackend* quic_simple_server_backend, + int64_t body_bytes) + : QuicSimpleServerStream(id, + session, + BIDIRECTIONAL, + quic_simple_server_backend), + body_bytes_(body_bytes) {} + + ~ServerStreamThatSendsHugeResponse() override = default; + + protected: + void SendResponse() override { + QuicBackendResponse response; + QuicString body(body_bytes_, 'a'); + response.set_body(body); + SendHeadersAndBodyAndTrailers(response.headers().Clone(), response.body(), + response.trailers().Clone()); + } + + private: + // Use a explicit int64_t rather than size_t to simulate a 64-bit server + // talking to a 32-bit client. + int64_t body_bytes_; +}; + +class ServerStreamThatSendsHugeResponseFactory + : public QuicTestServer::StreamFactory { + public: + explicit ServerStreamThatSendsHugeResponseFactory(int64_t body_bytes) + : body_bytes_(body_bytes) {} + + ~ServerStreamThatSendsHugeResponseFactory() override = default; + + QuicSimpleServerStream* CreateStream( + QuicStreamId id, + QuicSpdySession* session, + QuicSimpleServerBackend* quic_simple_server_backend) override { + return new ServerStreamThatSendsHugeResponse( + id, session, quic_simple_server_backend, body_bytes_); + } + + int64_t body_bytes_; +}; + +TEST_P(EndToEndTest, EarlyResponseFinRecording) { + set_smaller_flow_control_receive_window(); + + // Verify that an incoming FIN is recorded in a stream object even if the read + // side has been closed. This prevents an entry from being made in + // locally_close_streams_highest_offset_ (which will never be deleted). + // To set up the test condition, the server must do the following in order: + // start sending the response and call CloseReadSide + // receive the FIN of the request + // send the FIN of the response + + // The response body must be larger than the flow control window so the server + // must receive a window update from the client before it can finish sending + // it. + uint32_t response_body_size = + 2 * client_config_.GetInitialStreamFlowControlWindowToSend(); + QuicString response_body(response_body_size, 'a'); + + StreamWithErrorFactory stream_factory(response_body); + SetSpdyStreamFactory(&stream_factory); + + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // A POST that gets an early error response, after the headers are received + // and before the body is received, due to invalid content-length. + // Set an invalid content-length, so the request will receive an early 500 + // response. + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/garbage"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["content-length"] = "-1"; + + // The body must be large enough that the FIN will be in a different packet + // than the end of the headers, but short enough to not require a flow control + // update. This allows headers processing to trigger the error response + // before the request FIN is processed but receive the request FIN before the + // response is sent completely. + const uint32_t kRequestBodySize = kMaxPacketSize + 10; + QuicString request_body(kRequestBodySize, 'a'); + + // Send the request. + client_->SendMessage(headers, request_body); + client_->WaitForResponse(); + EXPECT_EQ("500", client_->response_headers()->find(":status")->second); + + // Pause the server so we can access the server's internals without races. + server_thread_->Pause(); + + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicDispatcher::SessionMap const& map = + QuicDispatcherPeer::session_map(dispatcher); + auto it = map.begin(); + EXPECT_TRUE(it != map.end()); + QuicSession* server_session = it->second.get(); + + // The stream is not waiting for the arrival of the peer's final offset. + EXPECT_EQ( + 0u, QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(server_session) + .size()); + + server_thread_->Resume(); +} + +TEST_P(EndToEndTestWithTls, Trailers) { + // Test sending and receiving HTTP/2 Trailers (trailing HEADERS frames). + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // Set reordering to ensure that Trailers arriving before body is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // Add a response with headers, body, and trailers. + const QuicString kBody = "body content"; + + SpdyHeaderBlock headers; + headers[":status"] = "200"; + headers[":version"] = "HTTP/1.1"; + headers["content-length"] = QuicTextUtils::Uint64ToString(kBody.size()); + + SpdyHeaderBlock trailers; + trailers["some-trailing-header"] = "trailing-header-value"; + + memory_cache_backend_.AddResponse(server_hostname_, "/trailer_url", + std::move(headers), kBody, + trailers.Clone()); + + EXPECT_EQ(kBody, client_->SendSynchronousRequest("/trailer_url")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + EXPECT_EQ(trailers, client_->response_trailers()); +} + +class EndToEndTestServerPush : public EndToEndTest { + protected: + const size_t kNumMaxStreams = 10; + + EndToEndTestServerPush() : EndToEndTest() { + client_config_.SetMaxIncomingDynamicStreamsToSend(kNumMaxStreams); + server_config_.SetMaxIncomingDynamicStreamsToSend(kNumMaxStreams); + support_server_push_ = true; + } + + // Add a request with its response and |num_resources| push resources into + // cache. + // If |resource_size| == 0, response body of push resources use default string + // concatenating with resource url. Otherwise, generate a string of + // |resource_size| as body. + void AddRequestAndResponseWithServerPush(QuicString host, + QuicString path, + QuicString response_body, + QuicString* push_urls, + const size_t num_resources, + const size_t resource_size) { + bool use_large_response = resource_size != 0; + QuicString large_resource; + if (use_large_response) { + // Generate a response common body larger than flow control window for + // push response. + large_resource = QuicString(resource_size, 'a'); + } + std::list<QuicBackendResponse::ServerPushInfo> push_resources; + for (size_t i = 0; i < num_resources; ++i) { + QuicString url = push_urls[i]; + QuicUrl resource_url(url); + QuicString body = + use_large_response + ? large_resource + : QuicStrCat("This is server push response body for ", url); + SpdyHeaderBlock response_headers; + response_headers[":version"] = "HTTP/1.1"; + response_headers[":status"] = "200"; + response_headers["content-length"] = + QuicTextUtils::Uint64ToString(body.size()); + push_resources.push_back(QuicBackendResponse::ServerPushInfo( + resource_url, std::move(response_headers), kV3LowestPriority, body)); + } + + memory_cache_backend_.AddSimpleResponseWithServerPushResources( + host, path, 200, response_body, push_resources); + } +}; + +// Run all server push end to end tests with all supported versions. +INSTANTIATE_TEST_SUITE_P(EndToEndTestsServerPush, + EndToEndTestServerPush, + ::testing::ValuesIn(GetTestParams(false, false))); + +TEST_P(EndToEndTestServerPush, ServerPush) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // Add a response with headers, body, and push resources. + const QuicString kBody = "body content"; + size_t kNumResources = 4; + QuicString push_urls[] = {"https://example.com/font.woff", + "https://example.com/script.js", + "https://fonts.example.com/font.woff", + "https://example.com/logo-hires.jpg"}; + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, 0); + + client_->client()->set_response_listener( + std::unique_ptr<QuicSpdyClientBase::ResponseListener>( + new TestResponseListener)); + + QUIC_DVLOG(1) << "send request for /push_example"; + EXPECT_EQ(kBody, client_->SendSynchronousRequest( + "https://example.com/push_example")); + QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream( + client_->client()->client_session()); + QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream); + // Headers stream's sequencer buffer shouldn't be released because server push + // hasn't finished yet. + EXPECT_TRUE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + + for (const QuicString& url : push_urls) { + QUIC_DVLOG(1) << "send request for pushed stream on url " << url; + QuicString expected_body = + QuicStrCat("This is server push response body for ", url); + QuicString response_body = client_->SendSynchronousRequest(url); + QUIC_DVLOG(1) << "response body " << response_body; + EXPECT_EQ(expected_body, response_body); + } + EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); +} + +TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { + // Tests that sending a request which has 4 push resources will trigger server + // to push those 4 resources and client can handle pushed resources and match + // them with requests later. + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // Add a response with headers, body, and push resources. + const QuicString kBody = "body content"; + size_t const kNumResources = 4; + QuicString push_urls[] = { + "https://example.com/font.woff", + "https://example.com/script.js", + "https://fonts.example.com/font.woff", + "https://example.com/logo-hires.jpg", + }; + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, 0); + client_->client()->set_response_listener( + std::unique_ptr<QuicSpdyClientBase::ResponseListener>( + new TestResponseListener)); + + // Send the first request: this will trigger the server to send all the push + // resources associated with this request, and these will be cached by the + // client. + EXPECT_EQ(kBody, client_->SendSynchronousRequest( + "https://example.com/push_example")); + + for (const QuicString& url : push_urls) { + // Sending subsequent requesets will not actually send anything on the wire, + // as the responses are already in the client's cache. + QUIC_DVLOG(1) << "send request for pushed stream on url " << url; + QuicString expected_body = + QuicStrCat("This is server push response body for ", url); + QuicString response_body = client_->SendSynchronousRequest(url); + QUIC_DVLOG(1) << "response body " << response_body; + EXPECT_EQ(expected_body, response_body); + } + // Expect only original request has been sent and push responses have been + // received as normal response. + EXPECT_EQ(1u, client_->num_requests()); + EXPECT_EQ(1u + kNumResources, client_->num_responses()); +} + +TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { + // Tests that when streams are not blocked by flow control or congestion + // control, pushing even more resources than max number of open outgoing + // streams should still work because all response streams get closed + // immediately after pushing resources. + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // Add a response with headers, body, and push resources. + const QuicString kBody = "body content"; + + // One more resource than max number of outgoing stream of this session. + const size_t kNumResources = 1 + kNumMaxStreams; // 11. + QuicString push_urls[11]; + for (size_t i = 0; i < kNumResources; ++i) { + push_urls[i] = QuicStrCat("https://example.com/push_resources", i); + } + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, 0); + client_->client()->set_response_listener( + std::unique_ptr<QuicSpdyClientBase::ResponseListener>( + new TestResponseListener)); + + // Send the first request: this will trigger the server to send all the push + // resources associated with this request, and these will be cached by the + // client. + EXPECT_EQ(kBody, client_->SendSynchronousRequest( + "https://example.com/push_example")); + + for (const QuicString& url : push_urls) { + // Sending subsequent requesets will not actually send anything on the wire, + // as the responses are already in the client's cache. + EXPECT_EQ(QuicStrCat("This is server push response body for ", url), + client_->SendSynchronousRequest(url)); + } + + // Only 1 request should have been sent. + EXPECT_EQ(1u, client_->num_requests()); + // The responses to the original request and all the promised resources + // should have been received. + EXPECT_EQ(12u, client_->num_responses()); +} + +TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { + // Tests that when server tries to send more large resources(large enough to + // be blocked by flow control window or congestion control window) than max + // open outgoing streams , server can open upto max number of outgoing + // streams for them, and the rest will be queued up. + + // Reset flow control windows. + size_t kFlowControlWnd = 20 * 1024; // 20KB. + // Response body is larger than 1 flow controlblock window. + size_t kBodySize = kFlowControlWnd * 2; + set_client_initial_stream_flow_control_receive_window(kFlowControlWnd); + // Make sure conntection level flow control window is large enough not to + // block data being sent out though they will be blocked by stream level one. + set_client_initial_session_flow_control_receive_window( + kBodySize * kNumMaxStreams + 1024); + + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. + SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); + SetReorderPercentage(30); + + // Add a response with headers, body, and push resources. + const QuicString kBody = "body content"; + + const size_t kNumResources = kNumMaxStreams + 1; + QuicString push_urls[11]; + for (size_t i = 0; i < kNumResources; ++i) { + push_urls[i] = QuicStrCat("http://example.com/push_resources", i); + } + AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, + push_urls, kNumResources, kBodySize); + + client_->client()->set_response_listener( + std::unique_ptr<QuicSpdyClientBase::ResponseListener>( + new TestResponseListener)); + + client_->SendRequest("https://example.com/push_example"); + + // Pause after the first response arrives. + while (!client_->response_complete()) { + // Because of priority, the first response arrived should be to original + // request. + client_->WaitForResponse(); + } + + // Check server session to see if it has max number of outgoing streams opened + // though more resources need to be pushed. + server_thread_->Pause(); + EXPECT_EQ(kNumMaxStreams, GetServerSession()->GetNumOpenOutgoingStreams()); + server_thread_->Resume(); + + EXPECT_EQ(1u, client_->num_requests()); + EXPECT_EQ(1u, client_->num_responses()); + EXPECT_EQ(kBody, client_->response_body()); + + // "Send" request for a promised resources will not really send out it because + // its response is being pushed(but blocked). And the following ack and + // flow control behavior of SendSynchronousRequests() + // will unblock the stream to finish receiving response. + client_->SendSynchronousRequest(push_urls[0]); + EXPECT_EQ(1u, client_->num_requests()); + EXPECT_EQ(2u, client_->num_responses()); + + // Do same thing for the rest 10 resources. + for (size_t i = 1; i < kNumResources; ++i) { + client_->SendSynchronousRequest(push_urls[i]); + } + + // Because of server push, client gets all pushed resources without actually + // sending requests for them. + EXPECT_EQ(1u, client_->num_requests()); + // Including response to original request, 12 responses in total were + // received. + EXPECT_EQ(12u, client_->num_responses()); +} + +// TODO(fayang): this test seems to cause net_unittests timeouts :| +TEST_P(EndToEndTest, DISABLED_TestHugePostWithPacketLoss) { + // This test tests a huge post with introduced packet loss from client to + // server and body size greater than 4GB, making sure QUIC code does not break + // for 32-bit builds. + ServerStreamThatDropsBodyFactory stream_factory; + SetSpdyStreamFactory(&stream_factory); + ASSERT_TRUE(Initialize()); + // Set client's epoll server's time out to 0 to make this test be finished + // within a short time. + client_->epoll_server()->set_timeout_in_us(0); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + SetPacketLossPercentage(1); + // To avoid storing the whole request body in memory, use a loop to repeatedly + // send body size of kSizeBytes until the whole request body size is reached. + const int kSizeBytes = 128 * 1024; + // Request body size is 4G plus one more kSizeBytes. + int64_t request_body_size_bytes = pow(2, 32) + kSizeBytes; + ASSERT_LT(INT64_C(4294967296), request_body_size_bytes); + QuicString body(kSizeBytes, 'a'); + + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["content-length"] = + QuicTextUtils::Uint64ToString(request_body_size_bytes); + + client_->SendMessage(headers, "", /*fin=*/false); + + for (int i = 0; i < request_body_size_bytes / kSizeBytes; ++i) { + bool fin = (i == request_body_size_bytes - 1); + client_->SendData(QuicString(body.data(), kSizeBytes), fin); + client_->client()->WaitForEvents(); + } + VerifyCleanConnection(true); +} + +// TODO(fayang): this test seems to cause net_unittests timeouts :| +TEST_P(EndToEndTest, DISABLED_TestHugeResponseWithPacketLoss) { + // This test tests a huge response with introduced loss from server to client + // and body size greater than 4GB, making sure QUIC code does not break for + // 32-bit builds. + const int kSizeBytes = 128 * 1024; + int64_t response_body_size_bytes = pow(2, 32) + kSizeBytes; + ASSERT_LT(4294967296, response_body_size_bytes); + ServerStreamThatSendsHugeResponseFactory stream_factory( + response_body_size_bytes); + SetSpdyStreamFactory(&stream_factory); + + StartServer(); + + // Use a quic client that drops received body. + QuicTestClient* client = + new QuicTestClient(server_address_, server_hostname_, client_config_, + client_supported_versions_); + client->client()->set_drop_response_body(true); + client->UseWriter(client_writer_); + client->Connect(); + client_.reset(client); + static QuicEpollEvent event(EPOLLOUT); + client_writer_->Initialize( + QuicConnectionPeer::GetHelper( + client_->client()->client_session()->connection()), + QuicConnectionPeer::GetAlarmFactory( + client_->client()->client_session()->connection()), + QuicMakeUnique<ClientDelegate>(client_->client())); + initialized_ = true; + ASSERT_TRUE(client_->client()->connected()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + SetPacketLossPercentage(1); + client_->SendRequest("/huge_response"); + client_->WaitForResponse(); + // TODO(fayang): Fix this test to work with stateless rejects. + if (!BothSidesSupportStatelessRejects()) { + VerifyCleanConnection(true); + } +} + +// Regression test for b/111515567 +TEST_P(EndToEndTest, AgreeOnStopWaiting) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + server_thread_->Pause(); + QuicConnection* server_connection = GetServerConnection(); + // Verify client and server connections agree on the value of + // no_stop_waiting_frames. + EXPECT_EQ(QuicConnectionPeer::GetNoStopWaitingFrames(client_connection), + QuicConnectionPeer::GetNoStopWaitingFrames(server_connection)); + server_thread_->Resume(); +} + +// Regression test for b/111515567 +TEST_P(EndToEndTest, AgreeOnStopWaitingWithNoStopWaitingOption) { + QuicTagVector options; + options.push_back(kNSTP); + client_config_.SetConnectionOptionsToSend(options); + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + server_thread_->Pause(); + QuicConnection* server_connection = GetServerConnection(); + // Verify client and server connections agree on the value of + // no_stop_waiting_frames. + EXPECT_EQ(QuicConnectionPeer::GetNoStopWaitingFrames(client_connection), + QuicConnectionPeer::GetNoStopWaitingFrames(server_connection)); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, ReleaseHeadersStreamBufferWhenIdle) { + // Tests that when client side has no active request and no waiting + // PUSH_PROMISE, its headers stream's sequencer buffer should be released. + ASSERT_TRUE(Initialize()); + client_->SendSynchronousRequest("/foo"); + QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream( + client_->client()->client_session()); + QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream); + EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); +} + +TEST_P(EndToEndTest, WayTooLongRequestHeaders) { + ASSERT_TRUE(Initialize()); + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + headers["key"] = QuicString(64 * 1024, 'a'); + + client_->SendMessage(headers, ""); + client_->WaitForResponse(); + EXPECT_EQ(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE, + client_->connection_error()); +} + +class WindowUpdateObserver : public QuicConnectionDebugVisitor { + public: + WindowUpdateObserver() : num_window_update_frames_(0), num_ping_frames_(0) {} + + size_t num_window_update_frames() const { return num_window_update_frames_; } + + size_t num_ping_frames() const { return num_ping_frames_; } + + void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + const QuicTime& receive_time) override { + ++num_window_update_frames_; + } + + void OnPingFrame(const QuicPingFrame& frame) override { ++num_ping_frames_; } + + private: + size_t num_window_update_frames_; + size_t num_ping_frames_; +}; + +TEST_P(EndToEndTest, WindowUpdateInAck) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + WindowUpdateObserver observer; + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + client_connection->set_debug_visitor(&observer); + // 100KB body. + QuicString body(100 * 1024, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + EXPECT_EQ(kFooResponseBody, + client_->SendCustomSynchronousRequest(headers, body)); + client_->Disconnect(); + EXPECT_LT(0u, observer.num_window_update_frames()); + EXPECT_EQ(0u, observer.num_ping_frames()); +} + +TEST_P(EndToEndTest, SendStatelessResetTokenInShlo) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + QuicConfig* config = client_->client()->session()->config(); + EXPECT_TRUE(config->HasReceivedStatelessResetToken()); + EXPECT_EQ(QuicUtils::GenerateStatelessResetToken( + client_->client()->session()->connection()->connection_id()), + config->ReceivedStatelessResetToken()); + client_->Disconnect(); +} + +// Regression test for b/116200989. +TEST_P(EndToEndTest, + SendStatelessResetIfServerConnectionClosedLocallyDuringHandshake) { + connect_to_server_on_initialize_ = false; + ASSERT_TRUE(Initialize()); + + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(0u, dispatcher->session_map().size()); + // Note: this writer will only used by the server connection, not the time + // wait list. + QuicDispatcherPeer::UseWriter( + dispatcher, + // This cause the first server-sent packet, a.k.a REJ, to fail. + new BadPacketWriter(/*packet_causing_write_error=*/0, EPERM)); + server_thread_->Resume(); + + client_.reset(CreateQuicClient(client_writer_)); + EXPECT_EQ("", client_->SendSynchronousRequest("/foo")); + + if (client_->client()->client_session()->connection()->transport_version() > + QUIC_VERSION_43) { + EXPECT_EQ(QUIC_HANDSHAKE_FAILED, client_->connection_error()); + } else { + EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error()); + } +} + +// Regression test for b/116200989. +TEST_P(EndToEndTest, + SendStatelessResetIfServerConnectionClosedLocallyAfterHandshake) { + // Prevent the connection from expiring in the time wait list. + FLAGS_quic_time_wait_list_seconds = 10000; + connect_to_server_on_initialize_ = false; + ASSERT_TRUE(Initialize()); + + // big_response_body is 64K, which is about 48 full-sized packets. + const size_t kBigResponseBodySize = 65536; + QuicData big_response_body(new char[kBigResponseBodySize](), + kBigResponseBodySize, /*owns_buffer=*/true); + AddToCache("/big_response", 200, big_response_body.AsStringPiece()); + + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(0u, dispatcher->session_map().size()); + QuicDispatcherPeer::UseWriter( + dispatcher, + // This will cause an server write error with EPERM, while sending the + // response for /big_response. + new BadPacketWriter(/*packet_causing_write_error=*/20, EPERM)); + server_thread_->Resume(); + + client_.reset(CreateQuicClient(client_writer_)); + + // First, a /foo request with small response should succeed. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Second, a /big_response request with big response should fail. + EXPECT_LT(client_->SendSynchronousRequest("/big_response").length(), + kBigResponseBodySize); + EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error()); +} + +// Regression test of b/70782529. +TEST_P(EndToEndTest, DoNotCrashOnPacketWriteError) { + ASSERT_TRUE(Initialize()); + BadPacketWriter* bad_writer = + new BadPacketWriter(/*packet_causing_write_error=*/5, + /*error_code=*/90); + std::unique_ptr<QuicTestClient> client(CreateQuicClient(bad_writer)); + + // 1 MB body. + QuicString body(1024 * 1024, 'a'); + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + client->SendCustomSynchronousRequest(headers, body); +} + +// Regression test for b/71711996. This test sends a connectivity probing packet +// as its last sent packet, and makes sure the server's ACK of that packet does +// not cause the client to fail. +TEST_P(EndToEndTest, LastPacketSentIsConnectivityProbing) { + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + // Wait for the client's ACK (of the response) to be received by the server. + client_->WaitForDelayedAcks(); + + // We are sending a connectivity probing packet from an unchanged client + // address, so the server will not respond to us with a connectivity probing + // packet, however the server should send an ack-only packet to us. + client_->SendConnectivityProbing(); + + // Wait for the server's last ACK to be received by the client. + client_->WaitForDelayedAcks(); +} + +TEST_P(EndToEndTest, PreSharedKey) { + client_config_.set_max_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + client_config_.set_max_idle_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + pre_shared_key_client_ = "foobar"; + pre_shared_key_server_ = "foobar"; + ASSERT_TRUE(Initialize()); + + ASSERT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); +} + +// TODO: reenable once we have a way to make this run faster. +TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyMismatch)) { + client_config_.set_max_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + client_config_.set_max_idle_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + pre_shared_key_client_ = "foo"; + pre_shared_key_server_ = "bar"; + // One of two things happens when Initialize() returns: + // 1. Crypto handshake has completed, and it is unsuccessful. Initialize() + // returns false. + // 2. Crypto handshake has not completed, Initialize() returns true. The call + // to WaitForCryptoHandshakeConfirmed() will wait for the handshake and + // return whether it is successful. + ASSERT_FALSE(Initialize() && + client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_EQ(QUIC_HANDSHAKE_TIMEOUT, client_->connection_error()); +} + +// TODO: reenable once we have a way to make this run faster. +TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoClient)) { + client_config_.set_max_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + client_config_.set_max_idle_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + pre_shared_key_server_ = "foobar"; + ASSERT_FALSE(Initialize() && + client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_EQ(QUIC_HANDSHAKE_TIMEOUT, client_->connection_error()); +} + +// TODO: reenable once we have a way to make this run faster. +TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoServer)) { + client_config_.set_max_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + client_config_.set_max_idle_time_before_crypto_handshake( + QuicTime::Delta::FromSeconds(1)); + pre_shared_key_client_ = "foobar"; + ASSERT_FALSE(Initialize() && + client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_EQ(QUIC_HANDSHAKE_TIMEOUT, client_->connection_error()); +} + +TEST_P(EndToEndTest, RequestAndStreamRstInOnePacket) { + // Regression test for b/80234898. + ASSERT_TRUE(Initialize()); + + // INCOMPLETE_RESPONSE will cause the server to not to send the trailer + // (and the FIN) after the response body. + QuicString response_body(1305, 'a'); + SpdyHeaderBlock response_headers; + response_headers[":status"] = QuicTextUtils::Uint64ToString(200); + response_headers["content-length"] = + QuicTextUtils::Uint64ToString(response_body.length()); + memory_cache_backend_.AddSpecialResponse( + server_hostname_, "/test_url", std::move(response_headers), response_body, + QuicBackendResponse::INCOMPLETE_RESPONSE); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + client_->WaitForDelayedAcks(); + + QuicSession* session = client_->client()->client_session(); + const QuicPacketCount packets_sent_before = + session->connection()->GetStats().packets_sent; + + client_->SendRequestAndRstTogether("/test_url"); + + // Expect exactly one packet is sent from the block above. + ASSERT_EQ(packets_sent_before + 1, + session->connection()->GetStats().packets_sent); + + // Wait for the connection to become idle. + client_->WaitForDelayedAcks(); + + // The real expectation is the test does not crash or timeout. + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); +} + +TEST_P(EndToEndTest, ResetStreamOnTtlExpires) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + if (!client_->client()->client_session()->session_decides_what_to_write()) { + return; + } + SetPacketLossPercentage(30); + + QuicSpdyClientStream* stream = client_->GetOrCreateStream(); + // Set a TTL which expires immediately. + stream->MaybeSetTtl(QuicTime::Delta::FromMicroseconds(1)); + + // 1 MB body. + QuicString body(1024 * 1024, 'a'); + stream->WriteOrBufferBody(body, true); + client_->WaitForResponse(); + EXPECT_EQ(QUIC_STREAM_TTL_EXPIRED, client_->stream_error()); +} + +TEST_P(EndToEndTest, SendMessages) { + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + QuicSession* client_session = client_->client()->client_session(); + QuicConnection* client_connection = client_session->connection(); + if (client_connection->transport_version() <= QUIC_VERSION_44) { + return; + } + + SetPacketLossPercentage(30); + ASSERT_GT(kMaxPacketSize, client_session->GetLargestMessagePayload()); + ASSERT_LT(0, client_session->GetLargestMessagePayload()); + + QuicString message_string(kMaxPacketSize, 'a'); + QuicStringPiece message_buffer(message_string); + QuicRandom* random = + QuicConnectionPeer::GetHelper(client_connection)->GetRandomGenerator(); + QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); + { + QuicConnection::ScopedPacketFlusher flusher( + client_session->connection(), QuicConnection::SEND_ACK_IF_PENDING); + // Verify the largest message gets successfully sent. + EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1), + client_session->SendMessage(MakeSpan( + client_session->connection() + ->helper() + ->GetStreamSendBufferAllocator(), + QuicStringPiece(message_buffer.data(), + client_session->GetLargestMessagePayload()), + &storage))); + // Send more messages with size (0, largest_payload] until connection is + // write blocked. + const int kTestMaxNumberOfMessages = 100; + for (size_t i = 2; i <= kTestMaxNumberOfMessages; ++i) { + size_t message_length = + random->RandUint64() % client_session->GetLargestMessagePayload() + 1; + MessageResult result = client_session->SendMessage(MakeSpan( + client_session->connection() + ->helper() + ->GetStreamSendBufferAllocator(), + QuicStringPiece(message_buffer.data(), message_length), &storage)); + if (result.status == MESSAGE_STATUS_BLOCKED) { + // Connection is write blocked. + break; + } + EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, i), result); + } + } + + client_->WaitForDelayedAcks(); + EXPECT_EQ( + MESSAGE_STATUS_TOO_LARGE, + client_session + ->SendMessage(MakeSpan( + client_session->connection() + ->helper() + ->GetStreamSendBufferAllocator(), + QuicStringPiece(message_buffer.data(), + client_session->GetLargestMessagePayload() + 1), + &storage)) + .status); + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); +} + +class EndToEndPacketReorderingTest : public EndToEndTest { + public: + void CreateClientWithWriter() override { + QUIC_LOG(ERROR) << "create client with reorder_writer_"; + reorder_writer_ = new PacketReorderingWriter(); + client_.reset(EndToEndTest::CreateQuicClient(reorder_writer_)); + } + + void SetUp() override { + // Don't initialize client writer in base class. + server_writer_ = new PacketDroppingTestWriter(); + } + + protected: + PacketReorderingWriter* reorder_writer_; +}; + +INSTANTIATE_TEST_SUITE_P(EndToEndPacketReorderingTests, + EndToEndPacketReorderingTest, + testing::ValuesIn(GetTestParams(false, false))); + +TEST_P(EndToEndPacketReorderingTest, ReorderedConnectivityProbing) { + ASSERT_TRUE(Initialize()); + + // Finish one request to make sure handshake established. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + + // Wait for the connection to become idle, to make sure the packet gets + // delayed is the connectivity probing packet. + client_->WaitForDelayedAcks(); + + QuicSocketAddress old_addr = + client_->client()->network_helper()->GetLatestClientAddress(); + + // Migrate socket to the new IP address. + QuicIpAddress new_host = TestLoopback(2); + EXPECT_NE(old_addr.host(), new_host); + ASSERT_TRUE(client_->client()->MigrateSocket(new_host)); + + // Write a connectivity probing after the next /foo request. + reorder_writer_->SetDelay(1); + client_->SendConnectivityProbing(); + + ASSERT_TRUE(client_->MigrateSocketWithSpecifiedPort(old_addr.host(), + old_addr.port())); + + // The (delayed) connectivity probing will be sent after this request. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + + // Send yet another request after the connectivity probing, when this request + // returns, the probing is guaranteed to have been received by the server, and + // the server's response to probing is guaranteed to have been received by the + // client. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + + server_thread_->Pause(); + QuicConnection* server_connection = GetServerConnection(); + EXPECT_EQ(1u, + server_connection->GetStats().num_connectivity_probing_received); + server_thread_->Resume(); + + QuicConnection* client_connection = + client_->client()->client_session()->connection(); + EXPECT_EQ(1u, + client_connection->GetStats().num_connectivity_probing_received); +} + +TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) { + ASSERT_TRUE(Initialize()); + // Finish one request to make sure handshake established. + client_->SendSynchronousRequest("/foo"); + // Disconnect for next 0-rtt request. + client_->Disconnect(); + + // Client get valid STK now. Do a 0-rtt request. + // Buffer a CHLO till another packets sent out. + reorder_writer_->SetDelay(1); + // Only send out a CHLO. + client_->client()->Initialize(); + client_->client()->StartConnect(); + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client_->client()->connected()); + + // Send a request before handshake finishes. + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/bar"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + + client_->SendMessage(headers, ""); + client_->WaitForResponse(); + EXPECT_EQ(kBarResponseBody, client_->response_body()); + QuicConnectionStats client_stats = + client_->client()->client_session()->connection()->GetStats(); + EXPECT_EQ(0u, client_stats.packets_lost); + if (ServerSendsVersionNegotiation()) { + EXPECT_EQ(2, client_->client()->GetNumSentClientHellos()); + } else { + EXPECT_EQ(1, client_->client()->GetNumSentClientHellos()); + } +} + +// Test that STOP_SENDING makes it to the other side. Set up a client & server, +// create a stream (do not close it), and then send a STOP_SENDING from one +// side. The other side should get a call to QuicStream::OnStopSending. +// (aside, test cribbed from RequestAndStreamRstInOnePacket) +TEST_P(EndToEndTest, SimpleStopSendingTest) { + const uint16_t kStopSendingTestCode = 123; + ASSERT_TRUE(Initialize()); + if (negotiated_version_.transport_version != QUIC_VERSION_99) { + return; + } + QuicSession* client_session = client_->client()->client_session(); + ASSERT_NE(nullptr, client_session); + QuicConnection* client_connection = client_session->connection(); + ASSERT_NE(nullptr, client_connection); + + // STOP_SENDING will cause the server to not to send the trailer + // (and the FIN) after the response body. Instead, it sends a STOP_SENDING + // frame for the stream. + QuicString response_body(1305, 'a'); + SpdyHeaderBlock response_headers; + response_headers[":status"] = QuicTextUtils::Uint64ToString(200); + response_headers["content-length"] = + QuicTextUtils::Uint64ToString(response_body.length()); + memory_cache_backend_.AddStopSendingResponse( + server_hostname_, "/test_url", std::move(response_headers), response_body, + kStopSendingTestCode); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + client_->WaitForDelayedAcks(); + + QuicSession* session = client_->client()->client_session(); + const QuicPacketCount packets_sent_before = + session->connection()->GetStats().packets_sent; + + QuicStreamId stream_id = session->next_outgoing_bidirectional_stream_id(); + client_->SendRequest("/test_url"); + + // Expect exactly one packet is sent from the block above. + ASSERT_EQ(packets_sent_before + 1, + session->connection()->GetStats().packets_sent); + + // Wait for the connection to become idle. + client_->WaitForDelayedAcks(); + + // The real expectation is the test does not crash or timeout. + EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error()); + // And that the stop-sending code is received. + QuicSimpleClientStream* client_stream = + static_cast<QuicSimpleClientStream*>(client_->latest_created_stream()); + ASSERT_NE(nullptr, client_stream); + // Make sure we have the correct stream + EXPECT_EQ(stream_id, client_stream->id()); + EXPECT_EQ(kStopSendingTestCode, client_stream->last_stop_sending_code()); +} + +TEST_P(EndToEndTest, SimpleStopSendingRstStreamTest) { + ASSERT_TRUE(Initialize()); + + // Send a request without a fin, to keep the stream open + SpdyHeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/foo"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + client_->SendMessage(headers, "", /*fin=*/false); + // Stream should be open + ASSERT_NE(nullptr, client_->latest_created_stream()); + EXPECT_FALSE( + QuicStreamPeer::write_side_closed(client_->latest_created_stream())); + EXPECT_FALSE( + QuicStreamPeer::read_side_closed(client_->latest_created_stream())); + + // Send a RST_STREAM+STOP_SENDING on the stream + // Code is not important. + client_->latest_created_stream()->Reset(QUIC_BAD_APPLICATION_PAYLOAD); + client_->WaitForResponse(); + + // Stream should be gone. + ASSERT_EQ(nullptr, client_->latest_created_stream()); +} + +class BadShloPacketWriter : public QuicPacketWriterWrapper { + public: + BadShloPacketWriter() : error_returned_(false) {} + ~BadShloPacketWriter() override {} + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + quic::PerPacketOptions* options) override { + const WriteResult result = QuicPacketWriterWrapper::WritePacket( + buffer, buf_len, self_address, peer_address, options); + const uint8_t type_byte = buffer[0]; + if (!error_returned_ && (type_byte & FLAGS_LONG_HEADER) && + (((type_byte & 0x30) >> 4) == 1 || (type_byte & 0x7F) == 0x7C)) { + QUIC_DVLOG(1) << "Return write error for ZERO_RTT_PACKET"; + error_returned_ = true; + return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE); + } + return result; + } + + private: + bool error_returned_; +}; + +TEST_P(EndToEndTest, ZeroRttProtectedConnectionClose) { + // This test ensures ZERO_RTT_PROTECTED connection close could close a client + // which has switched to forward secure. + connect_to_server_on_initialize_ = + negotiated_version_.transport_version <= QUIC_VERSION_43; + ASSERT_TRUE(Initialize()); + if (negotiated_version_.transport_version <= QUIC_VERSION_43) { + // Only runs for IETF QUIC header. + return; + } + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(0u, dispatcher->session_map().size()); + // Note: this writer will only used by the server connection, not the time + // wait list. + QuicDispatcherPeer::UseWriter( + dispatcher, + // This causes the first server sent ZERO_RTT_PROTECTED packet (i.e., + // SHLO) to be sent, but WRITE_ERROR is returned. Such that a + // ZERO_RTT_PROTECTED connection close would be sent to a client with + // encryption level FORWARD_SECURE. + new BadShloPacketWriter()); + server_thread_->Resume(); + + client_.reset(CreateQuicClient(client_writer_)); + EXPECT_EQ("", client_->SendSynchronousRequest("/foo")); + // Verify ZERO_RTT_PROTECTED connection close is successfully processed by + // client which switches to FORWARD_SECURE. + EXPECT_EQ(QUIC_PACKET_WRITE_ERROR, client_->connection_error()); +} + +class BadShloPacketWriter2 : public QuicPacketWriterWrapper { + public: + BadShloPacketWriter2() : error_returned_(false) {} + ~BadShloPacketWriter2() override {} + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + quic::PerPacketOptions* options) override { + const uint8_t type_byte = buffer[0]; + if ((type_byte & FLAGS_LONG_HEADER) && + (((type_byte & 0x30) >> 4) == 1 || (type_byte & 0x7F) == 0x7C)) { + QUIC_DVLOG(1) << "Dropping ZERO_RTT_PACKET packet"; + return WriteResult(WRITE_STATUS_OK, buf_len); + } + if (!error_returned_ && !(type_byte & FLAGS_LONG_HEADER)) { + QUIC_DVLOG(1) << "Return write error for short header packet"; + error_returned_ = true; + return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE); + } + return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address, + peer_address, options); + } + + private: + bool error_returned_; +}; + +TEST_P(EndToEndTest, ForwardSecureConnectionClose) { + // This test ensures ZERO_RTT_PROTECTED connection close is sent to a client + // which has ZERO_RTT_PROTECTED encryption level. + SetQuicReloadableFlag(quic_fix_termination_packets, true); + connect_to_server_on_initialize_ = + negotiated_version_.transport_version <= QUIC_VERSION_43; + ASSERT_TRUE(Initialize()); + if (negotiated_version_.transport_version <= QUIC_VERSION_43) { + // Only runs for IETF QUIC header. + return; + } + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(0u, dispatcher->session_map().size()); + // Note: this writer will only used by the server connection, not the time + // wait list. + QuicDispatcherPeer::UseWriter( + dispatcher, + // This causes the all server sent ZERO_RTT_PROTECTED packets to be + // dropped, and first short header packet causes write error. + new BadShloPacketWriter2()); + server_thread_->Resume(); + client_.reset(CreateQuicClient(client_writer_)); + EXPECT_EQ("", client_->SendSynchronousRequest("/foo")); + // Verify ZERO_RTT_PROTECTED connection close is successfully processed by + // client. + EXPECT_EQ(QUIC_PACKET_WRITE_ERROR, client_->connection_error()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc new file mode 100644 index 0000000..e697821 --- /dev/null +++ b/quic/core/http/http_decoder.cc
@@ -0,0 +1,411 @@ +// Copyright (c) 2018 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/http/http_decoder.h" +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" + +namespace quic { + +namespace { + +// Create a mask that sets the last |num_bits| to 1 and the rest to 0. +inline uint8_t GetMaskFromNumBits(uint8_t num_bits) { + return (1u << num_bits) - 1; +} + +// Extract |num_bits| from |flags| offset by |offset|. +uint8_t ExtractBits(uint8_t flags, uint8_t num_bits, uint8_t offset) { + return (flags >> offset) & GetMaskFromNumBits(num_bits); +} + +// Length of the type field of HTTP/3 frames. +static const QuicByteCount kFrameTypeLength = 1; + +} // namespace + +HttpDecoder::HttpDecoder() + : visitor_(nullptr), + state_(STATE_READING_FRAME_LENGTH), + current_frame_type_(0), + current_length_field_size_(0), + remaining_length_field_length_(0), + current_frame_length_(0), + remaining_frame_length_(0), + error_(QUIC_NO_ERROR), + error_detail_(""), + has_payload_(false) {} + +HttpDecoder::~HttpDecoder() {} + +QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) { + has_payload_ = false; + QuicDataReader reader(data, len); + while (error_ == QUIC_NO_ERROR && reader.BytesRemaining() != 0) { + switch (state_) { + case STATE_READING_FRAME_LENGTH: + ReadFrameLength(&reader); + break; + case STATE_READING_FRAME_TYPE: + ReadFrameType(&reader); + break; + case STATE_READING_FRAME_PAYLOAD: + ReadFramePayload(&reader); + break; + case STATE_ERROR: + break; + default: + QUIC_BUG << "Invalid state: " << state_; + } + } + + if (error_ != QUIC_NO_ERROR) { + return 0; + } + + return len - reader.BytesRemaining(); +} + +void HttpDecoder::ReadFrameLength(QuicDataReader* reader) { + DCHECK_NE(0u, reader->BytesRemaining()); + BufferFrameLength(reader); + if (remaining_length_field_length_ != 0) { + return; + } + QuicDataReader length_reader(length_buffer_.data(), + current_length_field_size_); + if (!length_reader.ReadVarInt62(¤t_frame_length_)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame length"); + visitor_->OnError(this); + return; + } + + state_ = STATE_READING_FRAME_TYPE; + remaining_frame_length_ = current_frame_length_; +} + +void HttpDecoder::ReadFrameType(QuicDataReader* reader) { + DCHECK_NE(0u, reader->BytesRemaining()); + if (!reader->ReadUInt8(¤t_frame_type_)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame type"); + return; + } + + state_ = STATE_READING_FRAME_PAYLOAD; +} + +void HttpDecoder::ReadFramePayload(QuicDataReader* reader) { + DCHECK_NE(0u, reader->BytesRemaining()); + switch (current_frame_type_) { + case 0x0: { // DATA + if (current_frame_length_ == remaining_frame_length_) { + visitor_->OnDataFrameStart( + Http3FrameLengths(current_length_field_size_ + kFrameTypeLength, + current_frame_length_)); + } + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_frame_length_, reader->BytesRemaining()); + QuicStringPiece payload; + if (!reader->ReadStringPiece(&payload, bytes_to_read)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data"); + return; + } + has_payload_ = true; + visitor_->OnDataFramePayload(payload); + remaining_frame_length_ -= payload.length(); + if (remaining_frame_length_ == 0) { + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + visitor_->OnDataFrameEnd(); + } + return; + } + case 0x1: { // HEADERS + if (current_frame_length_ == remaining_frame_length_) { + visitor_->OnHeadersFrameStart(); + } + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_frame_length_, reader->BytesRemaining()); + QuicStringPiece payload; + if (!reader->ReadStringPiece(&payload, bytes_to_read)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data"); + return; + } + visitor_->OnHeadersFramePayload(payload); + remaining_frame_length_ -= payload.length(); + if (remaining_frame_length_ == 0) { + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + visitor_->OnHeadersFrameEnd(current_frame_length_); + } + return; + } + case 0x2: { // PRIORITY + // TODO(rch): avoid buffering if the entire frame is present, and + // instead parse directly out of |reader|. + BufferFramePayload(reader); + if (remaining_frame_length_ == 0) { + PriorityFrame frame; + QuicDataReader reader(buffer_.data(), current_frame_length_); + if (!ParsePriorityFrame(&reader, &frame)) { + return; + } + visitor_->OnPriorityFrame(frame); + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + } + return; + } + case 0x3: { // CANCEL_PUSH + // TODO(rch): Handle partial delivery. + BufferFramePayload(reader); + if (remaining_frame_length_ == 0) { + CancelPushFrame frame; + QuicDataReader reader(buffer_.data(), current_frame_length_); + if (!reader.ReadVarInt62(&frame.push_id)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + return; + } + visitor_->OnCancelPushFrame(frame); + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + } + return; + } + case 0x4: { // SETTINGS + // TODO(rch): Handle overly large SETTINGS frames. Either: + // 1. Impose a limit on SETTINGS frame size, and close the connection if + // exceeded + // 2. Implement a streaming parsing mode. + BufferFramePayload(reader); + if (remaining_frame_length_ == 0) { + SettingsFrame frame; + QuicDataReader reader(buffer_.data(), current_frame_length_); + if (!ParseSettingsFrame(&reader, &frame)) { + return; + } + visitor_->OnSettingsFrame(frame); + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + } + return; + } + case 0x5: { // PUSH_PROMISE + if (current_frame_length_ == remaining_frame_length_) { + QuicByteCount bytes_remaining = reader->BytesRemaining(); + PushId push_id; + // TODO(rch): Handle partial delivery of this field. + if (!reader->ReadVarInt62(&push_id)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + return; + } + remaining_frame_length_ -= bytes_remaining - reader->BytesRemaining(); + visitor_->OnPushPromiseFrameStart(push_id); + } + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_frame_length_, reader->BytesRemaining()); + if (bytes_to_read == 0) { + return; + } + QuicStringPiece payload; + if (!reader->ReadStringPiece(&payload, bytes_to_read)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data"); + return; + } + visitor_->OnPushPromiseFramePayload(payload); + remaining_frame_length_ -= payload.length(); + if (remaining_frame_length_ == 0) { + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + visitor_->OnPushPromiseFrameEnd(); + } + return; + } + case 0x7: { // GOAWAY + BufferFramePayload(reader); + if (remaining_frame_length_ == 0) { + GoAwayFrame frame; + QuicDataReader reader(buffer_.data(), current_frame_length_); + uint64_t stream_id; + if (!reader.ReadVarInt62(&stream_id)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read GOAWAY stream_id"); + return; + } + frame.stream_id = stream_id; + visitor_->OnGoAwayFrame(frame); + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + } + return; + } + + case 0xD: { // MAX_PUSH_ID + // TODO(rch): Handle partial delivery. + BufferFramePayload(reader); + if (remaining_frame_length_ == 0) { + QuicDataReader reader(buffer_.data(), current_frame_length_); + MaxPushIdFrame frame; + if (!reader.ReadVarInt62(&frame.push_id)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + return; + } + visitor_->OnMaxPushIdFrame(frame); + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + } + return; + } + + case 0xE: { // DUPLICATE_PUSH + BufferFramePayload(reader); + if (remaining_frame_length_ != 0) { + return; + } + QuicDataReader reader(buffer_.data(), current_frame_length_); + DuplicatePushFrame frame; + if (!reader.ReadVarInt62(&frame.push_id)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id"); + return; + } + visitor_->OnDuplicatePushFrame(frame); + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + return; + } + // Reserved frame types. + // TODO(rch): Since these are actually the same behavior as the + // default, we probably don't need to special case them here? + case 0xB: + QUIC_FALLTHROUGH_INTENDED; + case 0xB + 0x1F: + QUIC_FALLTHROUGH_INTENDED; + case 0xB + 0x1F * 2: + QUIC_FALLTHROUGH_INTENDED; + case 0xB + 0x1F * 3: + QUIC_FALLTHROUGH_INTENDED; + case 0xB + 0x1F * 4: + QUIC_FALLTHROUGH_INTENDED; + case 0xB + 0x1F * 5: + QUIC_FALLTHROUGH_INTENDED; + case 0xB + 0x1F * 6: + QUIC_FALLTHROUGH_INTENDED; + case 0xB + 0x1F * 7: + QUIC_FALLTHROUGH_INTENDED; + default: + DiscardFramePayload(reader); + } +} + +void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) { + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_frame_length_, reader->BytesRemaining()); + QuicStringPiece payload; + if (!reader->ReadStringPiece(&payload, bytes_to_read)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame payload"); + return; + } + remaining_frame_length_ -= payload.length(); + if (remaining_frame_length_ == 0) { + state_ = STATE_READING_FRAME_LENGTH; + current_length_field_size_ = 0; + } +} + +void HttpDecoder::BufferFramePayload(QuicDataReader* reader) { + if (current_frame_length_ == remaining_frame_length_) { + buffer_.erase(buffer_.size()); + buffer_.reserve(current_frame_length_); + } + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_frame_length_, reader->BytesRemaining()); + if (!reader->ReadBytes( + &(buffer_[0]) + current_frame_length_ - remaining_frame_length_, + bytes_to_read)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame payload"); + return; + } + remaining_frame_length_ -= bytes_to_read; +} + +void HttpDecoder::BufferFrameLength(QuicDataReader* reader) { + if (current_length_field_size_ == 0) { + current_length_field_size_ = reader->PeekVarInt62Length(); + if (current_length_field_size_ == 0) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame length"); + visitor_->OnError(this); + return; + } + remaining_length_field_length_ = current_length_field_size_; + } + if (current_length_field_size_ == remaining_length_field_length_) { + length_buffer_.erase(length_buffer_.size()); + length_buffer_.reserve(current_length_field_size_); + } + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_length_field_length_, reader->BytesRemaining()); + if (!reader->ReadBytes(&(length_buffer_[0]) + current_length_field_size_ - + remaining_length_field_length_, + bytes_to_read)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame length"); + visitor_->OnError(this); + return; + } + remaining_length_field_length_ -= bytes_to_read; +} + +void HttpDecoder::RaiseError(QuicErrorCode error, QuicString error_detail) { + state_ = STATE_ERROR; + error_ = error; + error_detail_ = std::move(error_detail); +} + +bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader, + PriorityFrame* frame) { + uint8_t flags; + if (!reader->ReadUInt8(&flags)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read priority frame flags"); + return false; + } + + frame->prioritized_type = + static_cast<PriorityElementType>(ExtractBits(flags, 2, 6)); + frame->dependency_type = + static_cast<PriorityElementType>(ExtractBits(flags, 2, 4)); + frame->exclusive = flags % 2 == 1; + if (!reader->ReadVarInt62(&frame->prioritized_element_id)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read prioritized_element_id"); + return false; + } + if (!reader->ReadVarInt62(&frame->element_dependency_id)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read element_dependency_id"); + return false; + } + if (!reader->ReadUInt8(&frame->weight)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read priority frame weight"); + return false; + } + return true; +} + +bool HttpDecoder::ParseSettingsFrame(QuicDataReader* reader, + SettingsFrame* frame) { + while (!reader->IsDoneReading()) { + uint16_t id; + if (!reader->ReadUInt16(&id)) { + RaiseError(QUIC_INTERNAL_ERROR, + "Unable to read settings frame identifier"); + return false; + } + uint64_t content; + if (!reader->ReadVarInt62(&content)) { + RaiseError(QUIC_INTERNAL_ERROR, "Unable to read settings frame content"); + return false; + } + frame->values[id] = content; + } + return true; +} + +} // namespace quic
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h new file mode 100644 index 0000000..910d189 --- /dev/null +++ b/quic/core/http/http_decoder.h
@@ -0,0 +1,185 @@ +// Copyright (c) 2018 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_HTTP_HTTP_DECODER_H_ +#define QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_ + +#include <cstddef> + +#include "net/third_party/quiche/src/quic/core/http/http_frames.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicDataReader; + +// Struct that stores meta data of a data frame. +// |header_length| stores number of bytes header occupies. +// |payload_length| stores number of bytes payload occupies. +struct QUIC_EXPORT_PRIVATE Http3FrameLengths { + Http3FrameLengths(QuicByteCount header, QuicByteCount payload) + : header_length(header), payload_length(payload) {} + + bool operator==(const Http3FrameLengths& other) const { + return (header_length == other.header_length) && + (payload_length == other.payload_length); + } + + QuicByteCount header_length; + QuicByteCount payload_length; +}; + +// A class for decoding the HTTP frames that are exchanged in an HTTP over QUIC +// session. +class QUIC_EXPORT_PRIVATE HttpDecoder { + public: + class QUIC_EXPORT_PRIVATE Visitor { + public: + virtual ~Visitor() {} + + // Called if an error is detected. + virtual void OnError(HttpDecoder* decoder) = 0; + + // Called when a PRIORITY frame has been successfully parsed. + virtual void OnPriorityFrame(const PriorityFrame& frame) = 0; + + // Called when a CANCEL_PUSH frame has been successfully parsed. + virtual void OnCancelPushFrame(const CancelPushFrame& frame) = 0; + + // Called when a MAX_PUSH_ID frame has been successfully parsed. + virtual void OnMaxPushIdFrame(const MaxPushIdFrame& frame) = 0; + + // Called when a GOAWAY frame has been successfully parsed. + virtual void OnGoAwayFrame(const GoAwayFrame& frame) = 0; + + // Called when a SETTINGS frame has been successfully parsed. + virtual void OnSettingsFrame(const SettingsFrame& frame) = 0; + + // Called when a DUPLICATE_PUSH frame has been successfully parsed. + virtual void OnDuplicatePushFrame(const DuplicatePushFrame& frame) = 0; + + // Called when a DATA frame has been received, |frame_lengths| will be + // passed to inform header length and payload length of the frame. + virtual void OnDataFrameStart(Http3FrameLengths frame_length) = 0; + // Called when the payload of a DATA frame has read. May be called + // multiple times for a single frame. + virtual void OnDataFramePayload(QuicStringPiece payload) = 0; + // Called when a DATA frame has been completely processed. + virtual void OnDataFrameEnd() = 0; + + // Called when a HEADERS frame has been recevied. + virtual void OnHeadersFrameStart() = 0; + // Called when the payload of a HEADERS frame has read. May be called + // multiple times for a single frame. + virtual void OnHeadersFramePayload(QuicStringPiece payload) = 0; + // Called when a HEADERS frame has been completely processed. + // |frame_len| is the length of the HEADERS frame payload. + virtual void OnHeadersFrameEnd(QuicByteCount frame_len) = 0; + + // Called when a PUSH_PROMISE frame has been recevied for |push_id|. + virtual void OnPushPromiseFrameStart(PushId push_id) = 0; + // Called when the payload of a PUSH_PROMISE frame has read. May be called + // multiple times for a single frame. + virtual void OnPushPromiseFramePayload(QuicStringPiece payload) = 0; + // Called when a PUSH_PROMISE frame has been completely processed. + virtual void OnPushPromiseFrameEnd() = 0; + + // TODO(rch): Consider adding methods like: + // OnUnknownFrame{Start,Payload,End}() + // to allow callers to handle unknown frames. + }; + + HttpDecoder(); + + ~HttpDecoder(); + + // Set callbacks to be called from the decoder. A visitor must be set, or + // else the decoder will crash. It is acceptable for the visitor to do + // nothing. If this is called multiple times, only the last visitor + // will be used. |visitor| will be owned by the caller. + void set_visitor(Visitor* visitor) { visitor_ = visitor; } + + // Processes the input and invokes the visitor for any frames. + // Returns the number of bytes consumed, or 0 if there was an error, in which + // case error() should be consulted. + QuicByteCount ProcessInput(const char* data, QuicByteCount len); + + bool has_payload() { return has_payload_; } + + QuicErrorCode error() const { return error_; } + const QuicString& error_detail() const { return error_detail_; } + + private: + // Represents the current state of the parsing state machine. + enum HttpDecoderState { + STATE_READING_FRAME_LENGTH, + STATE_READING_FRAME_TYPE, + STATE_READING_FRAME_PAYLOAD, + STATE_ERROR + }; + + // Reads the length of a frame from |reader|. Sets error_ and error_detail_ + // if there are any errors. + void ReadFrameLength(QuicDataReader* reader); + + // Reads the type of a frame from |reader|. Sets error_ and error_detail_ + // if there are any errors. + void ReadFrameType(QuicDataReader* reader); + + // Reads the payload of the current frame from |reader| and processes it, + // possibly buffering the data or invoking the visitor. + void ReadFramePayload(QuicDataReader* reader); + + // Discards any remaining frame payload from |reader|. + void DiscardFramePayload(QuicDataReader* reader); + + // Buffers any remaining frame payload from |reader| into |buffer_|. + void BufferFramePayload(QuicDataReader* reader); + + // Buffers any remaining frame length field from |reader| into + // |length_buffer_| + void BufferFrameLength(QuicDataReader* reader); + + // Sets |error_| and |error_detail_| accordingly. + void RaiseError(QuicErrorCode error, QuicString error_detail); + + // Parses the payload of a PRIORITY frame from |reader| into |frame|. + bool ParsePriorityFrame(QuicDataReader* reader, PriorityFrame* frame); + + // Parses the payload of a SETTINGS frame from |reader| into |frame|. + bool ParseSettingsFrame(QuicDataReader* reader, SettingsFrame* frame); + + // Visitor to invoke when messages are parsed. + Visitor* visitor_; // Unowned. + // Current state of the parsing. + HttpDecoderState state_; + // Type of the frame currently being parsed. + uint8_t current_frame_type_; + // Size of the frame's length field. + QuicByteCount current_length_field_size_; + // Remaining length that's needed for the frame's length field. + QuicByteCount remaining_length_field_length_; + // Length of the payload of the frame currently being parsed. + QuicByteCount current_frame_length_; + // Remaining payload bytes to be parsed. + QuicByteCount remaining_frame_length_; + // Last error. + QuicErrorCode error_; + // The issue which caused |error_| + QuicString error_detail_; + // True if the call to ProcessInput() generates any payload. Flushed every + // time ProcessInput() is called. + bool has_payload_; + // Remaining unparsed data. + QuicString buffer_; + // Remaining unparsed length field data. + QuicString length_buffer_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc new file mode 100644 index 0000000..c6d7d9f --- /dev/null +++ b/quic/core/http/http_decoder_test.cc
@@ -0,0 +1,409 @@ +// Copyright (c) 2018 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/http/http_decoder.h" +#include "net/third_party/quiche/src/quic/core/http/http_encoder.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +using testing::InSequence; + +namespace quic { + +class MockVisitor : public HttpDecoder::Visitor { + public: + virtual ~MockVisitor() = default; + + // Called if an error is detected. + MOCK_METHOD1(OnError, void(HttpDecoder* decoder)); + + MOCK_METHOD1(OnPriorityFrame, void(const PriorityFrame& frame)); + MOCK_METHOD1(OnCancelPushFrame, void(const CancelPushFrame& frame)); + MOCK_METHOD1(OnMaxPushIdFrame, void(const MaxPushIdFrame& frame)); + MOCK_METHOD1(OnGoAwayFrame, void(const GoAwayFrame& frame)); + MOCK_METHOD1(OnSettingsFrame, void(const SettingsFrame& frame)); + MOCK_METHOD1(OnDuplicatePushFrame, void(const DuplicatePushFrame& frame)); + + MOCK_METHOD1(OnDataFrameStart, void(Http3FrameLengths frame_lengths)); + MOCK_METHOD1(OnDataFramePayload, void(QuicStringPiece payload)); + MOCK_METHOD0(OnDataFrameEnd, void()); + + MOCK_METHOD0(OnHeadersFrameStart, void()); + MOCK_METHOD1(OnHeadersFramePayload, void(QuicStringPiece payload)); + MOCK_METHOD1(OnHeadersFrameEnd, void(QuicByteCount frame_len)); + + MOCK_METHOD1(OnPushPromiseFrameStart, void(PushId push_id)); + MOCK_METHOD1(OnPushPromiseFramePayload, void(QuicStringPiece payload)); + MOCK_METHOD0(OnPushPromiseFrameEnd, void()); +}; + +class HttpDecoderTest : public QuicTest { + public: + HttpDecoderTest() { decoder_.set_visitor(&visitor_); } + HttpDecoder decoder_; + testing::StrictMock<MockVisitor> visitor_; +}; + +TEST_F(HttpDecoderTest, InitialState) { + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, ReservedFramesNoPayload) { + for (int n = 0; n < 8; ++n) { + const uint8_t type = 0xB + 0x1F * n; + char input[] = {// length + 0x00, + // type + type}; + + EXPECT_EQ(2u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))) << n; + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + ASSERT_EQ("", decoder_.error_detail()); + } +} + +TEST_F(HttpDecoderTest, ReservedFramesSmallPayload) { + for (int n = 0; n < 8; ++n) { + const uint8_t type = 0xB + 0x1F * n; + const uint8_t payload_size = 50; + char input[payload_size + 2] = {// length + payload_size, + // type + type}; + + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))) + << n; + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + ASSERT_EQ("", decoder_.error_detail()); + } +} + +TEST_F(HttpDecoderTest, ReservedFramesLargePayload) { + for (int n = 0; n < 8; ++n) { + const uint8_t type = 0xB + 0x1F * n; + const QuicByteCount payload_size = 256; + char input[payload_size + 3] = {// length + 0x40 + 0x01, 0x00, + // type + type}; + + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))) + << n; + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + ASSERT_EQ("", decoder_.error_detail()); + } +} + +TEST_F(HttpDecoderTest, CancelPush) { + char input[] = {// length + 0x1, + // type (CANCEL_PUSH) + 0x03, + // Push Id + 0x01}; + + // Process the full frame. + EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1}))); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1}))); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, PushPromiseFrame) { + char input[] = {// length + 0x8, + // type (PUSH_PROMISE) + 0x05, + // Push Id + 0x01, + // Header Block + 'H', 'e', 'a', 'd', 'e', 'r', 's'}; + + // Process the full frame. + InSequence s; + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1)); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers"))); + EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1)); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("H"))); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e"))); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("a"))); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("d"))); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e"))); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("r"))); + EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("s"))); + EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, MaxPushId) { + char input[] = {// length + 0x1, + // type (MAX_PUSH_ID) + 0x0D, + // Push Id + 0x01}; + + // Process the full frame. + EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1}))); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1}))); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, DuplicatePush) { + char input[] = {// length + 0x1, + // type (DUPLICATE_PUSH) + 0x0E, + // Push Id + 0x01}; + // Process the full frame. + EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1}))); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1}))); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, PriorityFrame) { + char input[] = {// length + 0x4, + // type (PRIORITY) + 0x2, + // request stream, request stream, exclusive + 0x01, + // prioritized_element_id + 0x03, + // element_dependency_id + 0x04, + // weight + 0xFF}; + + PriorityFrame frame; + frame.prioritized_type = REQUEST_STREAM; + frame.dependency_type = REQUEST_STREAM; + frame.exclusive = true; + frame.prioritized_element_id = 0x03; + frame.element_dependency_id = 0x04; + frame.weight = 0xFF; + + // Process the full frame. + EXPECT_CALL(visitor_, OnPriorityFrame(frame)); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + /* + // Process the frame incremently. + EXPECT_CALL(visitor_, OnPriorityFrame(frame)); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + */ +} + +TEST_F(HttpDecoderTest, SettingsFrame) { + // clang-format off + char input[] = { + // length + 0x06, + // type (SETTINGS) + 0x04, + // identifier (SETTINGS_NUM_PLACEHOLDERS) + 0x00, + 0x03, + // content + 0x02, + // identifier (SETTINGS_MAX_HEADER_LIST_SIZE) + 0x00, + 0x06, + // content + 0x05, + }; + // clang-format on + + SettingsFrame frame; + frame.values[3] = 2; + frame.values[6] = 5; + + // Process the full frame. + EXPECT_CALL(visitor_, OnSettingsFrame(frame)); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnSettingsFrame(frame)); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, DataFrame) { + char input[] = {// length + 0x05, + // type (DATA) + 0x00, + // data + 'D', 'a', 't', 'a', '!'}; + + // Process the full frame. + InSequence s; + EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5))); + EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("Data!"))); + EXPECT_CALL(visitor_, OnDataFrameEnd()); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5))); + EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("D"))); + EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("a"))); + EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("t"))); + EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("a"))); + EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("!"))); + EXPECT_CALL(visitor_, OnDataFrameEnd()); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, FrameHeaderPartialDelivery) { + // A large input that will occupy more than 1 byte in the length field. + QuicString input(2048, 'x'); + HttpEncoder encoder; + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder.SerializeDataFrameHeader(input.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + // Partially send only 1 byte of the header to process. + EXPECT_EQ(1u, decoder_.ProcessInput(header.data(), 1)); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Send the rest of the header. + EXPECT_EQ(header_length - 1, + decoder_.ProcessInput(header.data() + 1, header_length - 1)); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Send data. + EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(3, 2048))); + EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece(input))); + EXPECT_CALL(visitor_, OnDataFrameEnd()); + EXPECT_EQ(2048u, decoder_.ProcessInput(input.data(), 2048)); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, GoAway) { + char input[] = {// length + 0x1, + // type (GOAWAY) + 0x07, + // StreamId + 0x01}; + + // Process the full frame. + EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1}))); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1}))); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, HeadersFrame) { + char input[] = {// length + 0x07, + // type (HEADERS) + 0x01, + // headers + 'H', 'e', 'a', 'd', 'e', 'r', 's'}; + + // Process the full frame. + InSequence s; + EXPECT_CALL(visitor_, OnHeadersFrameStart()); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers"))); + EXPECT_CALL(visitor_, OnHeadersFrameEnd(7)); + EXPECT_EQ(QUIC_ARRAYSIZE(input), + decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process the frame incremently. + EXPECT_CALL(visitor_, OnHeadersFrameStart()); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("H"))); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e"))); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("a"))); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("d"))); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e"))); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("r"))); + EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("s"))); + EXPECT_CALL(visitor_, OnHeadersFrameEnd(7)); + for (char c : input) { + EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1)); + } + EXPECT_EQ(QUIC_NO_ERROR, decoder_.error()); + EXPECT_EQ("", decoder_.error_detail()); +} + +} // namespace quic
diff --git a/quic/core/http/http_encoder.cc b/quic/core/http/http_encoder.cc new file mode 100644 index 0000000..d2d1699 --- /dev/null +++ b/quic/core/http/http_encoder.cc
@@ -0,0 +1,257 @@ +// Copyright (c) 2018 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/http/http_encoder.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace { + +// Set the first byte of a PRIORITY frame according to its fields. +uint8_t SetPriorityFields(uint8_t num, + PriorityElementType type, + bool prioritized) { + switch (type) { + case REQUEST_STREAM: + return num; + case PUSH_STREAM: + if (prioritized) { + return num | (1 << 6); + } + return num | (1 << 4); + case PLACEHOLDER: + if (prioritized) { + return num | (1 << 7); + } + return num | (1 << 5); + case ROOT_OF_TREE: + if (prioritized) { + num = num | (1 << 6); + return num | (1 << 7); + } + num = num | (1 << 4); + return num | (1 << 5); + default: + QUIC_NOTREACHED(); + return num; + } +} + +// Length of the type field of a frame. +static const size_t kFrameTypeLength = 1; +// Length of the weight field of a priority frame. +static const size_t kPriorityWeightLength = 1; +// Length of a priority frame's first byte. +static const size_t kPriorityFirstByteLength = 1; +// Length of a key in the map of a settings frame. +static const size_t kSettingsMapKeyLength = 2; + +} // namespace + +HttpEncoder::HttpEncoder() {} + +HttpEncoder::~HttpEncoder() {} + +QuicByteCount HttpEncoder::SerializeDataFrameHeader( + QuicByteCount payload_length, + std::unique_ptr<char[]>* output) { + DCHECK_NE(0u, payload_length); + QuicByteCount header_length = + QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength; + + output->reset(new char[header_length]); + QuicDataWriter writer(header_length, output->get()); + + if (WriteFrameHeader(payload_length, HttpFrameType::DATA, &writer)) { + return header_length; + } + return 0; +} + +QuicByteCount HttpEncoder::SerializeHeadersFrameHeader( + QuicByteCount payload_length, + std::unique_ptr<char[]>* output) { + DCHECK_NE(0u, payload_length); + QuicByteCount header_length = + QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength; + + output->reset(new char[header_length]); + QuicDataWriter writer(header_length, output->get()); + + if (WriteFrameHeader(payload_length, HttpFrameType::HEADERS, &writer)) { + return header_length; + } + return 0; +} + +QuicByteCount HttpEncoder::SerializePriorityFrame( + const PriorityFrame& priority, + std::unique_ptr<char[]>* output) { + QuicByteCount payload_length = + kPriorityFirstByteLength + + QuicDataWriter::GetVarInt62Len(priority.prioritized_element_id) + + QuicDataWriter::GetVarInt62Len(priority.element_dependency_id) + + kPriorityWeightLength; + QuicByteCount total_length = GetTotalLength(payload_length); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (!WriteFrameHeader(payload_length, HttpFrameType::PRIORITY, &writer)) { + return 0; + } + + // Set the first byte of the payload. + uint8_t bits = 0; + bits = SetPriorityFields(bits, priority.prioritized_type, true); + bits = SetPriorityFields(bits, priority.dependency_type, false); + if (priority.exclusive) { + bits |= 1; + } + + if (writer.WriteUInt8(bits) && + writer.WriteVarInt62(priority.prioritized_element_id) && + writer.WriteVarInt62(priority.element_dependency_id) && + writer.WriteUInt8(priority.weight)) { + return total_length; + } + return 0; +} + +QuicByteCount HttpEncoder::SerializeCancelPushFrame( + const CancelPushFrame& cancel_push, + std::unique_ptr<char[]>* output) { + QuicByteCount payload_length = + QuicDataWriter::GetVarInt62Len(cancel_push.push_id); + QuicByteCount total_length = GetTotalLength(payload_length); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (WriteFrameHeader(payload_length, HttpFrameType::CANCEL_PUSH, &writer) && + writer.WriteVarInt62(cancel_push.push_id)) { + return total_length; + } + return 0; +} + +QuicByteCount HttpEncoder::SerializeSettingsFrame( + const SettingsFrame& settings, + std::unique_ptr<char[]>* output) { + // Calculate the key sizes. + QuicByteCount payload_length = settings.values.size() * kSettingsMapKeyLength; + // Calculate the value sizes. + for (auto it = settings.values.begin(); it != settings.values.end(); ++it) { + payload_length += QuicDataWriter::GetVarInt62Len(it->second); + } + + QuicByteCount total_length = GetTotalLength(payload_length); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (!WriteFrameHeader(payload_length, HttpFrameType::SETTINGS, &writer)) { + return 0; + } + + for (auto it = settings.values.begin(); it != settings.values.end(); ++it) { + if (!writer.WriteUInt16(it->first) || !writer.WriteVarInt62(it->second)) { + return 0; + } + } + + return total_length; +} + +QuicByteCount HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( + const PushPromiseFrame& push_promise, + std::unique_ptr<char[]>* output) { + QuicByteCount payload_length = + QuicDataWriter::GetVarInt62Len(push_promise.push_id) + + push_promise.headers.length(); + // GetTotalLength() is not used because headers will not be serialized. + QuicByteCount total_length = + QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength + + QuicDataWriter::GetVarInt62Len(push_promise.push_id); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (WriteFrameHeader(payload_length, HttpFrameType::PUSH_PROMISE, &writer) && + writer.WriteVarInt62(push_promise.push_id)) { + return total_length; + } + return 0; +} + +QuicByteCount HttpEncoder::SerializeGoAwayFrame( + const GoAwayFrame& goaway, + std::unique_ptr<char[]>* output) { + QuicByteCount payload_length = + QuicDataWriter::GetVarInt62Len(goaway.stream_id); + QuicByteCount total_length = GetTotalLength(payload_length); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (WriteFrameHeader(payload_length, HttpFrameType::GOAWAY, &writer) && + writer.WriteVarInt62(goaway.stream_id)) { + return total_length; + } + return 0; +} + +QuicByteCount HttpEncoder::SerializeMaxPushIdFrame( + const MaxPushIdFrame& max_push_id, + std::unique_ptr<char[]>* output) { + QuicByteCount payload_length = + QuicDataWriter::GetVarInt62Len(max_push_id.push_id); + QuicByteCount total_length = GetTotalLength(payload_length); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (WriteFrameHeader(payload_length, HttpFrameType::MAX_PUSH_ID, &writer) && + writer.WriteVarInt62(max_push_id.push_id)) { + return total_length; + } + return 0; +} + +QuicByteCount HttpEncoder::SerializeDuplicatePushFrame( + const DuplicatePushFrame& duplicate_push, + std::unique_ptr<char[]>* output) { + QuicByteCount payload_length = + QuicDataWriter::GetVarInt62Len(duplicate_push.push_id); + QuicByteCount total_length = GetTotalLength(payload_length); + + output->reset(new char[total_length]); + QuicDataWriter writer(total_length, output->get()); + + if (WriteFrameHeader(payload_length, HttpFrameType::DUPLICATE_PUSH, + &writer) && + writer.WriteVarInt62(duplicate_push.push_id)) { + return total_length; + } + return 0; +} + +bool HttpEncoder::WriteFrameHeader(QuicByteCount length, + HttpFrameType type, + QuicDataWriter* writer) { + return writer->WriteVarInt62(length) && + writer->WriteUInt8(static_cast<uint8_t>(type)); +} + +QuicByteCount HttpEncoder::GetTotalLength(QuicByteCount payload_length) { + return QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength + + payload_length; +} + +} // namespace quic
diff --git a/quic/core/http/http_encoder.h b/quic/core/http/http_encoder.h new file mode 100644 index 0000000..f04e6e4 --- /dev/null +++ b/quic/core/http/http_encoder.h
@@ -0,0 +1,85 @@ +// Copyright (c) 2018 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_HTTP_HTTP_ENCODER_H_ +#define QUICHE_QUIC_CORE_HTTP_HTTP_ENCODER_H_ + +#include <cstddef> + +#include "net/third_party/quiche/src/quic/core/http/http_frames.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicDataWriter; + +// A class for encoding the HTTP frames that are exchanged in an HTTP over QUIC +// session. +class QUIC_EXPORT_PRIVATE HttpEncoder { + public: + HttpEncoder(); + + ~HttpEncoder(); + + // Serializes a DATA frame header into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializeDataFrameHeader(QuicByteCount payload_length, + std::unique_ptr<char[]>* output); + + // Serializes a HEADERS frame header into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializeHeadersFrameHeader(QuicByteCount payload_length, + std::unique_ptr<char[]>* output); + + // Serializes a PRIORITY frame into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializePriorityFrame(const PriorityFrame& priority, + std::unique_ptr<char[]>* output); + + // Serializes a CANCEL_PUSH frame into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializeCancelPushFrame(const CancelPushFrame& cancel_push, + std::unique_ptr<char[]>* output); + + // Serializes a SETTINGS frame into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializeSettingsFrame(const SettingsFrame& settings, + std::unique_ptr<char[]>* output); + + // Serializes the header and push_id of a PUSH_PROMISE frame into a new buffer + // stored in |output|. Returns the length of the buffer on success, or 0 + // otherwise. + QuicByteCount SerializePushPromiseFrameWithOnlyPushId( + const PushPromiseFrame& push_promise, + std::unique_ptr<char[]>* output); + + // Serializes a GOAWAY frame into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializeGoAwayFrame(const GoAwayFrame& goaway, + std::unique_ptr<char[]>* output); + + // Serializes a MAX_PUSH frame into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializeMaxPushIdFrame(const MaxPushIdFrame& max_push_id, + std::unique_ptr<char[]>* output); + + // Serialize a DUPLICATE_PUSH frame into a new buffer stored in |output|. + // Returns the length of the buffer on success, or 0 otherwise. + QuicByteCount SerializeDuplicatePushFrame( + const DuplicatePushFrame& duplicate_push, + std::unique_ptr<char[]>* output); + + private: + bool WriteFrameHeader(QuicByteCount length, + HttpFrameType type, + QuicDataWriter* writer); + + QuicByteCount GetTotalLength(QuicByteCount payload_length); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_HTTP_ENCODER_H_
diff --git a/quic/core/http/http_encoder_test.cc b/quic/core/http/http_encoder_test.cc new file mode 100644 index 0000000..a484aed --- /dev/null +++ b/quic/core/http/http_encoder_test.cc
@@ -0,0 +1,185 @@ +// Copyright (c) 2018 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/http/http_encoder.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { + +class HttpEncoderTest : public QuicTest { + public: + HttpEncoderTest() {} + HttpEncoder encoder_; +}; + +TEST_F(HttpEncoderTest, SerializeDataFrameHeader) { + std::unique_ptr<char[]> buffer; + uint64_t length = + encoder_.SerializeDataFrameHeader(/* payload_length = */ 5, &buffer); + char output[] = {// length + 0x05, + // type (DATA) + 0x00}; + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("DATA", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializeHeadersFrameHeader) { + std::unique_ptr<char[]> buffer; + uint64_t length = + encoder_.SerializeHeadersFrameHeader(/* payload_length = */ 7, &buffer); + char output[] = {// length + 0x07, + // type (HEADERS) + 0x01}; + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("HEADERS", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializePriorityFrame) { + PriorityFrame priority; + priority.prioritized_type = REQUEST_STREAM; + priority.dependency_type = REQUEST_STREAM; + priority.exclusive = true; + priority.prioritized_element_id = 0x03; + priority.element_dependency_id = 0x04; + priority.weight = 0xFF; + char output[] = {// length + 0x4, + // type (PRIORITY) + 0x2, + // request stream, request stream, exclusive + 0x01, + // prioritized_element_id + 0x03, + // element_dependency_id + 0x04, + // weight + 0xFF}; + + std::unique_ptr<char[]> buffer; + uint64_t length = encoder_.SerializePriorityFrame(priority, &buffer); + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("PRIORITY", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializeCancelPushFrame) { + CancelPushFrame cancel_push; + cancel_push.push_id = 0x01; + char output[] = {// length + 0x1, + // type (CANCEL_PUSH) + 0x03, + // Push Id + 0x01}; + std::unique_ptr<char[]> buffer; + uint64_t length = encoder_.SerializeCancelPushFrame(cancel_push, &buffer); + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("CANCEL_PUSH", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializeSettingsFrame) { + SettingsFrame settings; + settings.values[3] = 2; + settings.values[6] = 5; + char output[] = { + // length + 0x06, + // type (SETTINGS) + 0x04, + // identifier (SETTINGS_NUM_PLACEHOLDERS) + 0x00, + 0x03, + // content + 0x02, + // identifier (SETTINGS_MAX_HEADER_LIST_SIZE) + 0x00, + 0x06, + // content + 0x05, + }; + std::unique_ptr<char[]> buffer; + uint64_t length = encoder_.SerializeSettingsFrame(settings, &buffer); + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("SETTINGS", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializePushPromiseFrameWithOnlyPushId) { + PushPromiseFrame push_promise; + push_promise.push_id = 0x01; + push_promise.headers = "Headers"; + char output[] = {// length + 0x8, + // type (PUSH_PROMISE) + 0x05, + // Push Id + 0x01}; + std::unique_ptr<char[]> buffer; + uint64_t length = + encoder_.SerializePushPromiseFrameWithOnlyPushId(push_promise, &buffer); + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("PUSH_PROMISE", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializeGoAwayFrame) { + GoAwayFrame goaway; + goaway.stream_id = 0x1; + char output[] = {// length + 0x1, + // type (GOAWAY) + 0x07, + // StreamId + 0x01}; + std::unique_ptr<char[]> buffer; + uint64_t length = encoder_.SerializeGoAwayFrame(goaway, &buffer); + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("GOAWAY", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializeMaxPushIdFrame) { + MaxPushIdFrame max_push_id; + max_push_id.push_id = 0x1; + char output[] = {// length + 0x1, + // type (MAX_PUSH_ID) + 0x0D, + // Push Id + 0x01}; + std::unique_ptr<char[]> buffer; + uint64_t length = encoder_.SerializeMaxPushIdFrame(max_push_id, &buffer); + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("MAX_PUSH_ID", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +TEST_F(HttpEncoderTest, SerializeDuplicatePushFrame) { + DuplicatePushFrame duplicate_push; + duplicate_push.push_id = 0x1; + char output[] = {// length + 0x1, + // type (DUPLICATE_PUSH) + 0x0E, + // Push Id + 0x01}; + std::unique_ptr<char[]> buffer; + uint64_t length = + encoder_.SerializeDuplicatePushFrame(duplicate_push, &buffer); + EXPECT_EQ(QUIC_ARRAYSIZE(output), length); + CompareCharArraysWithHexError("DUPLICATE_PUSH", buffer.get(), length, output, + QUIC_ARRAYSIZE(output)); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/http/http_frames.h b/quic/core/http/http_frames.h new file mode 100644 index 0000000..ea11557 --- /dev/null +++ b/quic/core/http/http_frames.h
@@ -0,0 +1,156 @@ +// Copyright (c) 2018 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_HTTP_HTTP_FRAMES_H_ +#define QUICHE_QUIC_CORE_HTTP_HTTP_FRAMES_H_ + +#include <map> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" + +namespace quic { + +enum class HttpFrameType : uint8_t { + DATA = 0x0, + HEADERS = 0x1, + PRIORITY = 0X2, + CANCEL_PUSH = 0X3, + SETTINGS = 0x4, + PUSH_PROMISE = 0x5, + GOAWAY = 0x7, + MAX_PUSH_ID = 0xD, + DUPLICATE_PUSH = 0xE +}; + +// 4.2.1. DATA +// +// DATA frames (type=0x0) convey arbitrary, variable-length sequences of +// octets associated with an HTTP request or response payload. +struct DataFrame { + QuicStringPiece data; +}; + +// 4.2.2. HEADERS +// +// The HEADERS frame (type=0x1) is used to carry a header block, +// compressed using QPACK. +struct HeadersFrame { + QuicStringPiece headers; +}; + +// 4.2.3. PRIORITY +// +// The PRIORITY (type=0x02) frame specifies the sender-advised priority +// of a stream +enum PriorityElementType { + REQUEST_STREAM = 0, + PUSH_STREAM = 1, + PLACEHOLDER = 2, + ROOT_OF_TREE = 3 +}; + +struct PriorityFrame { + PriorityElementType prioritized_type; + PriorityElementType dependency_type; + bool exclusive; + uint64_t prioritized_element_id; + uint64_t element_dependency_id; + uint8_t weight; + + bool operator==(const PriorityFrame& rhs) const { + return prioritized_type == rhs.prioritized_type && + dependency_type == rhs.dependency_type && + exclusive == rhs.exclusive && + prioritized_element_id == rhs.prioritized_element_id && + element_dependency_id == rhs.element_dependency_id && + weight == rhs.weight; + } +}; + +// 4.2.4. CANCEL_PUSH +// +// The CANCEL_PUSH frame (type=0x3) is used to request cancellation of +// server push prior to the push stream being created. +using PushId = uint64_t; + +struct CancelPushFrame { + PushId push_id; + + bool operator==(const CancelPushFrame& rhs) const { + return push_id == rhs.push_id; + } +}; + +// 4.2.5. SETTINGS +// +// The SETTINGS frame (type=0x4) conveys configuration parameters that +// affect how endpoints communicate, such as preferences and constraints +// on peer behavior + +using SettingsId = uint16_t; +using SettingsMap = std::map<SettingsId, uint64_t>; + +struct SettingsFrame { + SettingsMap values; + + bool operator==(const SettingsFrame& rhs) const { + return values == rhs.values; + } +}; + +// 4.2.6. PUSH_PROMISE +// +// The PUSH_PROMISE frame (type=0x05) is used to carry a request header +// set from server to client, as in HTTP/2. +struct PushPromiseFrame { + PushId push_id; + QuicStringPiece headers; + + bool operator==(const PushPromiseFrame& rhs) const { + return push_id == rhs.push_id && headers == rhs.headers; + } +}; + +// 4.2.7. GOAWAY +// +// The GOAWAY frame (type=0x7) is used to initiate graceful shutdown of +// a connection by a server. +struct GoAwayFrame { + QuicStreamId stream_id; + + bool operator==(const GoAwayFrame& rhs) const { + return stream_id == rhs.stream_id; + } +}; + +// 4.2.8. MAX_PUSH_ID +// +// The MAX_PUSH_ID frame (type=0xD) is used by clients to control the +// number of server pushes that the server can initiate. +struct MaxPushIdFrame { + PushId push_id; + + bool operator==(const MaxPushIdFrame& rhs) const { + return push_id == rhs.push_id; + } +}; + +// 4.2.9. DUPLICATE_PUSH +// +// The DUPLICATE_PUSH frame (type=0xE) is used by servers to indicate +// that an existing pushed resource is related to multiple client +// requests. +struct DuplicatePushFrame { + PushId push_id; + + bool operator==(const DuplicatePushFrame& rhs) const { + return push_id == rhs.push_id; + } +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_HTTP_FRAMES_H_
diff --git a/quic/core/http/quic_client_promised_info.cc b/quic/core/http/quic_client_promised_info.cc new file mode 100644 index 0000000..abfc4b4 --- /dev/null +++ b/quic/core/http/quic_client_promised_info.cc
@@ -0,0 +1,144 @@ +// 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/http/quic_client_promised_info.h" + +#include <utility> + +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" + +using spdy::SpdyHeaderBlock; + +namespace quic { + +QuicClientPromisedInfo::QuicClientPromisedInfo( + QuicSpdyClientSessionBase* session, + QuicStreamId id, + QuicString url) + : session_(session), + id_(id), + url_(std::move(url)), + client_request_delegate_(nullptr) {} + +QuicClientPromisedInfo::~QuicClientPromisedInfo() {} + +void QuicClientPromisedInfo::CleanupAlarm::OnAlarm() { + QUIC_DVLOG(1) << "self GC alarm for stream " << promised_->id_; + promised_->session()->OnPushStreamTimedOut(promised_->id_); + promised_->Reset(QUIC_PUSH_STREAM_TIMED_OUT); +} + +void QuicClientPromisedInfo::Init() { + cleanup_alarm_.reset(session_->connection()->alarm_factory()->CreateAlarm( + new QuicClientPromisedInfo::CleanupAlarm(this))); + cleanup_alarm_->Set( + session_->connection()->helper()->GetClock()->ApproximateNow() + + QuicTime::Delta::FromSeconds(kPushPromiseTimeoutSecs)); +} + +bool QuicClientPromisedInfo::OnPromiseHeaders(const SpdyHeaderBlock& headers) { + // RFC7540, Section 8.2, requests MUST be safe [RFC7231], Section + // 4.2.1. GET and HEAD are the methods that are safe and required. + SpdyHeaderBlock::const_iterator it = headers.find(spdy::kHttp2MethodHeader); + if (it == headers.end()) { + QUIC_DVLOG(1) << "Promise for stream " << id_ << " has no method"; + Reset(QUIC_INVALID_PROMISE_METHOD); + return false; + } + if (!(it->second == "GET" || it->second == "HEAD")) { + QUIC_DVLOG(1) << "Promise for stream " << id_ << " has invalid method " + << it->second; + Reset(QUIC_INVALID_PROMISE_METHOD); + return false; + } + if (!SpdyUtils::PromisedUrlIsValid(headers)) { + QUIC_DVLOG(1) << "Promise for stream " << id_ << " has invalid URL " + << url_; + Reset(QUIC_INVALID_PROMISE_URL); + return false; + } + if (!session_->IsAuthorized( + SpdyUtils::GetPromisedHostNameFromHeaders(headers))) { + Reset(QUIC_UNAUTHORIZED_PROMISE_URL); + return false; + } + request_headers_ = headers.Clone(); + return true; +} + +void QuicClientPromisedInfo::OnResponseHeaders(const SpdyHeaderBlock& headers) { + response_headers_ = QuicMakeUnique<SpdyHeaderBlock>(headers.Clone()); + if (client_request_delegate_) { + // We already have a client request waiting. + FinalValidation(); + } +} + +void QuicClientPromisedInfo::Reset(QuicRstStreamErrorCode error_code) { + QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_; + session_->ResetPromised(id_, error_code); + session_->DeletePromised(this); + if (delegate) { + delegate->OnRendezvousResult(nullptr); + } +} + +QuicAsyncStatus QuicClientPromisedInfo::FinalValidation() { + if (!client_request_delegate_->CheckVary( + client_request_headers_, request_headers_, *response_headers_)) { + Reset(QUIC_PROMISE_VARY_MISMATCH); + return QUIC_FAILURE; + } + QuicSpdyStream* stream = session_->GetPromisedStream(id_); + if (!stream) { + // This shouldn't be possible, as |ClientRequest| guards against + // closed stream for the synchronous case. And in the + // asynchronous case, a RST can only be caught by |OnAlarm()|. + QUIC_BUG << "missing promised stream" << id_; + } + QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_; + session_->DeletePromised(this); + // Stream can start draining now + if (delegate) { + delegate->OnRendezvousResult(stream); + } + return QUIC_SUCCESS; +} + +QuicAsyncStatus QuicClientPromisedInfo::HandleClientRequest( + const SpdyHeaderBlock& request_headers, + QuicClientPushPromiseIndex::Delegate* delegate) { + if (session_->IsClosedStream(id_)) { + // There was a RST on the response stream. + session_->DeletePromised(this); + return QUIC_FAILURE; + } + + if (is_validating()) { + // The push promise has already been matched to another request though + // pending for validation. Returns QUIC_FAILURE to the caller as it couldn't + // match a new request any more. This will not affect the validation of the + // other request. + return QUIC_FAILURE; + } + + client_request_delegate_ = delegate; + client_request_headers_ = request_headers.Clone(); + if (response_headers_ == nullptr) { + return QUIC_PENDING; + } + return FinalValidation(); +} + +void QuicClientPromisedInfo::Cancel() { + // Don't fire OnRendezvousResult() for client initiated cancel. + client_request_delegate_ = nullptr; + Reset(QUIC_STREAM_CANCELLED); +} + +} // namespace quic
diff --git a/quic/core/http/quic_client_promised_info.h b/quic/core/http/quic_client_promised_info.h new file mode 100644 index 0000000..917c9f7 --- /dev/null +++ b/quic/core/http/quic_client_promised_info.h
@@ -0,0 +1,114 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PROMISED_INFO_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PROMISED_INFO_H_ + +#include <cstddef> + +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" + +namespace quic { + +namespace test { +class QuicClientPromisedInfoPeer; +} // namespace test + +// QuicClientPromisedInfo tracks the client state of a server push +// stream from the time a PUSH_PROMISE is received until rendezvous +// between the promised response and the corresponding client request +// is complete. +class QUIC_EXPORT_PRIVATE QuicClientPromisedInfo + : public QuicClientPushPromiseIndex::TryHandle { + public: + // Interface to QuicSpdyClientStream + QuicClientPromisedInfo(QuicSpdyClientSessionBase* session, + QuicStreamId id, + QuicString url); + QuicClientPromisedInfo(const QuicClientPromisedInfo&) = delete; + QuicClientPromisedInfo& operator=(const QuicClientPromisedInfo&) = delete; + virtual ~QuicClientPromisedInfo(); + + void Init(); + + // Validate promise headers etc. Returns true if headers are valid. + bool OnPromiseHeaders(const spdy::SpdyHeaderBlock& headers); + + // Store response, possibly proceed with final validation. + void OnResponseHeaders(const spdy::SpdyHeaderBlock& headers); + + // Rendezvous between this promised stream and a client request that + // has a matching URL. + virtual QuicAsyncStatus HandleClientRequest( + const spdy::SpdyHeaderBlock& headers, + QuicClientPushPromiseIndex::Delegate* delegate); + + void Cancel() override; + + void Reset(QuicRstStreamErrorCode error_code); + + // Client requests are initially associated to promises by matching + // URL in the client request against the URL in the promise headers, + // uing the |promised_by_url| map. The push can be cross-origin, so + // the client should validate that the session is authoritative for + // the promised URL. If not, it should call |RejectUnauthorized|. + QuicSpdyClientSessionBase* session() { return session_; } + + // If the promised response contains Vary header, then the fields + // specified by Vary must match between the client request header + // and the promise headers (see https://crbug.com//554220). Vary + // validation requires the response headers (for the actual Vary + // field list), the promise headers (taking the role of the "cached" + // request), and the client request headers. + spdy::SpdyHeaderBlock* request_headers() { return &request_headers_; } + + spdy::SpdyHeaderBlock* response_headers() { return response_headers_.get(); } + + // After validation, client will use this to access the pushed stream. + + QuicStreamId id() const { return id_; } + + const QuicString url() const { return url_; } + + // Return true if there's a request pending matching this push promise. + bool is_validating() const { return client_request_delegate_ != nullptr; } + + private: + friend class test::QuicClientPromisedInfoPeer; + + class CleanupAlarm : public QuicAlarm::Delegate { + public: + explicit CleanupAlarm(QuicClientPromisedInfo* promised) + : promised_(promised) {} + + void OnAlarm() override; + + QuicClientPromisedInfo* promised_; + }; + + QuicAsyncStatus FinalValidation(); + + QuicSpdyClientSessionBase* session_; + QuicStreamId id_; + QuicString url_; + spdy::SpdyHeaderBlock request_headers_; + std::unique_ptr<spdy::SpdyHeaderBlock> response_headers_; + spdy::SpdyHeaderBlock client_request_headers_; + QuicClientPushPromiseIndex::Delegate* client_request_delegate_; + + // The promise will commit suicide eventually if it is not claimed by a GET + // first. + std::unique_ptr<QuicAlarm> cleanup_alarm_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PROMISED_INFO_H_
diff --git a/quic/core/http/quic_client_promised_info_test.cc b/quic/core/http/quic_client_promised_info_test.cc new file mode 100644 index 0000000..2a7b1b0 --- /dev/null +++ b/quic/core/http/quic_client_promised_info_test.cc
@@ -0,0 +1,355 @@ +// Copyright 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/http/quic_client_promised_info.h" + +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using spdy::SpdyHeaderBlock; +using testing::_; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class MockQuicSpdyClientSession : public QuicSpdyClientSession { + public: + explicit MockQuicSpdyClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index) + : QuicSpdyClientSession(DefaultQuicConfig(), + supported_versions, + connection, + QuicServerId("example.com", 443, false), + &crypto_config_, + push_promise_index), + crypto_config_(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()), + authorized_(true) {} + MockQuicSpdyClientSession(const MockQuicSpdyClientSession&) = delete; + MockQuicSpdyClientSession& operator=(const MockQuicSpdyClientSession&) = + delete; + ~MockQuicSpdyClientSession() override {} + + bool IsAuthorized(const QuicString& authority) override { + return authorized_; + } + + void set_authorized(bool authorized) { authorized_ = authorized; } + + MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id)); + + private: + QuicCryptoClientConfig crypto_config_; + + bool authorized_; +}; + +class QuicClientPromisedInfoTest : public QuicTest { + public: + class StreamVisitor; + + QuicClientPromisedInfoTest() + : connection_(new StrictMock<MockQuicConnection>(&helper_, + &alarm_factory_, + Perspective::IS_CLIENT)), + session_(connection_->supported_versions(), + connection_, + &push_promise_index_), + body_("hello world"), + promise_id_( + QuicUtils::GetInvalidStreamId(connection_->transport_version())) { + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + session_.Initialize(); + + headers_[":status"] = "200"; + headers_["content-length"] = "11"; + + stream_ = QuicMakeUnique<QuicSpdyClientStream>( + GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 0), + &session_, BIDIRECTIONAL); + stream_visitor_ = QuicMakeUnique<StreamVisitor>(); + stream_->set_visitor(stream_visitor_.get()); + + push_promise_[":path"] = "/bar"; + push_promise_[":authority"] = "www.google.com"; + push_promise_[":version"] = "HTTP/1.1"; + push_promise_[":method"] = "GET"; + push_promise_[":scheme"] = "https"; + + promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_); + + client_request_ = push_promise_.Clone(); + promise_id_ = GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 0); + } + + class StreamVisitor : public QuicSpdyClientStream::Visitor { + void OnClose(QuicSpdyStream* stream) override { + QUIC_DVLOG(1) << "stream " << stream->id(); + } + }; + + void ReceivePromise(QuicStreamId id) { + auto headers = AsHeaderList(push_promise_); + stream_->OnPromiseHeaderList(id, headers.uncompressed_header_bytes(), + headers); + } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + QuicClientPushPromiseIndex push_promise_index_; + + MockQuicSpdyClientSession session_; + std::unique_ptr<QuicSpdyClientStream> stream_; + std::unique_ptr<StreamVisitor> stream_visitor_; + std::unique_ptr<QuicSpdyClientStream> promised_stream_; + SpdyHeaderBlock headers_; + QuicString body_; + SpdyHeaderBlock push_promise_; + QuicStreamId promise_id_; + QuicString promise_url_; + SpdyHeaderBlock client_request_; +}; + +TEST_F(QuicClientPromisedInfoTest, PushPromise) { + ReceivePromise(promise_id_); + + // Verify that the promise is in the unclaimed streams map. + EXPECT_NE(session_.GetPromisedById(promise_id_), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseCleanupAlarm) { + ReceivePromise(promise_id_); + + // Verify that the promise is in the unclaimed streams map. + QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_); + ASSERT_NE(promised, nullptr); + + // Fire the alarm that will cancel the promised stream. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promise_id_, QUIC_PUSH_STREAM_TIMED_OUT)); + alarm_factory_.FireAlarm(QuicClientPromisedInfoPeer::GetAlarm(promised)); + + // Verify that the promise is gone after the alarm fires. + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); + EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseInvalidMethod) { + // Promise with an unsafe method + push_promise_[":method"] = "PUT"; + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promise_id_, QUIC_INVALID_PROMISE_METHOD)); + ReceivePromise(promise_id_); + + // Verify that the promise headers were ignored + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); + EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseMissingMethod) { + // Promise with a missing method + push_promise_.erase(":method"); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promise_id_, QUIC_INVALID_PROMISE_METHOD)); + ReceivePromise(promise_id_); + + // Verify that the promise headers were ignored + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); + EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseInvalidUrl) { + // Remove required header field to make URL invalid + push_promise_.erase(":authority"); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promise_id_, QUIC_INVALID_PROMISE_URL)); + ReceivePromise(promise_id_); + + // Verify that the promise headers were ignored + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); + EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseUnauthorizedUrl) { + session_.set_authorized(false); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promise_id_, QUIC_UNAUTHORIZED_PROMISE_URL)); + + ReceivePromise(promise_id_); + + QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_); + ASSERT_EQ(promised, nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseMismatch) { + ReceivePromise(promise_id_); + + QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_); + ASSERT_NE(promised, nullptr); + + // Need to send the promised response headers and initiate the + // rendezvous for secondary validation to proceed. + QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>( + session_.GetOrCreateStream(promise_id_)); + auto headers = AsHeaderList(headers_); + promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + + TestPushPromiseDelegate delegate(/*match=*/false); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promise_id_, QUIC_PROMISE_VARY_MISMATCH)); + EXPECT_CALL(session_, CloseStream(promise_id_)); + + promised->HandleClientRequest(client_request_, &delegate); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseVaryWaits) { + ReceivePromise(promise_id_); + + QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_); + EXPECT_FALSE(promised->is_validating()); + ASSERT_NE(promised, nullptr); + + // Now initiate rendezvous. + TestPushPromiseDelegate delegate(/*match=*/true); + promised->HandleClientRequest(client_request_, &delegate); + EXPECT_TRUE(promised->is_validating()); + + // Promise is still there, waiting for response. + EXPECT_NE(session_.GetPromisedById(promise_id_), nullptr); + + // Send Response, should trigger promise validation and complete rendezvous + QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>( + session_.GetOrCreateStream(promise_id_)); + ASSERT_NE(promise_stream, nullptr); + auto headers = AsHeaderList(headers_); + promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + + // Promise is gone + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseVaryNoWait) { + ReceivePromise(promise_id_); + + QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_); + ASSERT_NE(promised, nullptr); + + QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>( + session_.GetOrCreateStream(promise_id_)); + ASSERT_NE(promise_stream, nullptr); + + // Send Response, should trigger promise validation and complete rendezvous + auto headers = AsHeaderList(headers_); + promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + + // Now initiate rendezvous. + TestPushPromiseDelegate delegate(/*match=*/true); + promised->HandleClientRequest(client_request_, &delegate); + + // Promise is gone + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); + // Have a push stream + EXPECT_TRUE(delegate.rendezvous_fired()); + + EXPECT_NE(delegate.rendezvous_stream(), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseWaitCancels) { + ReceivePromise(promise_id_); + + QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_); + ASSERT_NE(promised, nullptr); + + // Now initiate rendezvous. + TestPushPromiseDelegate delegate(/*match=*/true); + promised->HandleClientRequest(client_request_, &delegate); + + // Promise is still there, waiting for response. + EXPECT_NE(session_.GetPromisedById(promise_id_), nullptr); + + // Create response stream, but no data yet. + session_.GetOrCreateStream(promise_id_); + + // Cancel the promised stream. + EXPECT_CALL(session_, CloseStream(promise_id_)); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(promise_id_, QUIC_STREAM_CANCELLED)); + promised->Cancel(); + + // Promise is gone + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); +} + +TEST_F(QuicClientPromisedInfoTest, PushPromiseDataClosed) { + ReceivePromise(promise_id_); + + QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_); + ASSERT_NE(promised, nullptr); + + QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>( + session_.GetOrCreateStream(promise_id_)); + ASSERT_NE(promise_stream, nullptr); + + // Send response, rendezvous will be able to finish synchronously. + auto headers = AsHeaderList(headers_); + promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + + EXPECT_CALL(session_, CloseStream(promise_id_)); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promise_id_, QUIC_STREAM_PEER_GOING_AWAY)); + session_.SendRstStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0); + + // Now initiate rendezvous. + TestPushPromiseDelegate delegate(/*match=*/true); + EXPECT_EQ(promised->HandleClientRequest(client_request_, &delegate), + QUIC_FAILURE); + + // Got an indication of the stream failure, client should retry + // request. + EXPECT_FALSE(delegate.rendezvous_fired()); + EXPECT_EQ(delegate.rendezvous_stream(), nullptr); + + // Promise is gone + EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_client_push_promise_index.cc b/quic/core/http/quic_client_push_promise_index.cc new file mode 100644 index 0000000..2ba5951 --- /dev/null +++ b/quic/core/http/quic_client_push_promise_index.cc
@@ -0,0 +1,47 @@ +// Copyright 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/http/quic_client_push_promise_index.h" + +#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +using spdy::SpdyHeaderBlock; + +namespace quic { + +QuicClientPushPromiseIndex::QuicClientPushPromiseIndex() {} + +QuicClientPushPromiseIndex::~QuicClientPushPromiseIndex() {} + +QuicClientPushPromiseIndex::TryHandle::~TryHandle() {} + +QuicClientPromisedInfo* QuicClientPushPromiseIndex::GetPromised( + const QuicString& url) { + auto it = promised_by_url_.find(url); + if (it == promised_by_url_.end()) { + return nullptr; + } + return it->second; +} + +QuicAsyncStatus QuicClientPushPromiseIndex::Try( + const spdy::SpdyHeaderBlock& request, + QuicClientPushPromiseIndex::Delegate* delegate, + TryHandle** handle) { + QuicString url(SpdyUtils::GetPromisedUrlFromHeaders(request)); + auto it = promised_by_url_.find(url); + if (it != promised_by_url_.end()) { + QuicClientPromisedInfo* promised = it->second; + QuicAsyncStatus rv = promised->HandleClientRequest(request, delegate); + if (rv == QUIC_PENDING) { + *handle = promised; + } + return rv; + } + return QUIC_FAILURE; +} + +} // namespace quic
diff --git a/quic/core/http/quic_client_push_promise_index.h b/quic/core/http/quic_client_push_promise_index.h new file mode 100644 index 0000000..5dd29b4 --- /dev/null +++ b/quic/core/http/quic_client_push_promise_index.h
@@ -0,0 +1,98 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_ + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// QuicClientPushPromiseIndex is the interface to support rendezvous +// between client requests and resources delivered via server push. +// The same index can be shared across multiple sessions (e.g. for the +// same browser users profile), since cross-origin pushes are allowed +// (subject to authority constraints). + +class QUIC_EXPORT_PRIVATE QuicClientPushPromiseIndex { + public: + // Delegate is used to complete the rendezvous that began with + // |Try()|. + class QUIC_EXPORT_PRIVATE Delegate { + public: + virtual ~Delegate() {} + + // The primary lookup matched request with push promise by URL. A + // secondary match is necessary to ensure Vary (RFC 2616, 14.14) + // is honored. If Vary is not present, return true. If Vary is + // present, return whether designated header fields of + // |promise_request| and |client_request| match. + virtual bool CheckVary(const spdy::SpdyHeaderBlock& client_request, + const spdy::SpdyHeaderBlock& promise_request, + const spdy::SpdyHeaderBlock& promise_response) = 0; + + // On rendezvous success, provides the promised |stream|. Callee + // does not inherit ownership of |stream|. On rendezvous failure, + // |stream| is |nullptr| and the client should retry the request. + // Rendezvous can fail due to promise validation failure or RST on + // promised stream. |url| will have been removed from the index + // before |OnRendezvousResult()| is invoked, so a recursive call to + // |Try()| will return |QUIC_FAILURE|, which may be convenient for + // retry purposes. + virtual void OnRendezvousResult(QuicSpdyStream* stream) = 0; + }; + + class QUIC_EXPORT_PRIVATE TryHandle { + public: + // Cancel the request. + virtual void Cancel() = 0; + + protected: + TryHandle() {} + TryHandle(const TryHandle&) = delete; + TryHandle& operator=(const TryHandle&) = delete; + ~TryHandle(); + }; + + QuicClientPushPromiseIndex(); + QuicClientPushPromiseIndex(const QuicClientPushPromiseIndex&) = delete; + QuicClientPushPromiseIndex& operator=(const QuicClientPushPromiseIndex&) = + delete; + virtual ~QuicClientPushPromiseIndex(); + + // Called by client code, used to enforce affinity between requests + // for promised streams and the session the promise came from. + QuicClientPromisedInfo* GetPromised(const QuicString& url); + + // Called by client code, to initiate rendezvous between a request + // and a server push stream. If |request|'s url is in the index, + // rendezvous will be attempted and may complete immediately or + // asynchronously. If the matching promise and response headers + // have already arrived, the delegate's methods will fire + // recursively from within |Try()|. Returns |QUIC_SUCCESS| if the + // rendezvous was a success. Returns |QUIC_FAILURE| if there was no + // matching promise, or if there was but the rendezvous has failed. + // Returns QUIC_PENDING if a matching promise was found, but the + // rendezvous needs to complete asynchronously because the promised + // response headers are not yet available. If result is + // QUIC_PENDING, then |*handle| will set so that the caller may + // cancel the request if need be. The caller does not inherit + // ownership of |*handle|, and it ceases to be valid if the caller + // invokes |handle->Cancel()| or if |delegate->OnReponse()| fires. + QuicAsyncStatus Try(const spdy::SpdyHeaderBlock& request, + Delegate* delegate, + TryHandle** handle); + + QuicPromisedByUrlMap* promised_by_url() { return &promised_by_url_; } + + private: + QuicPromisedByUrlMap promised_by_url_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_
diff --git a/quic/core/http/quic_client_push_promise_index_test.cc b/quic/core/http/quic_client_push_promise_index_test.cc new file mode 100644 index 0000000..b68b41f --- /dev/null +++ b/quic/core/http/quic_client_push_promise_index_test.cc
@@ -0,0 +1,117 @@ +// Copyright 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/http/quic_client_push_promise_index.h" + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class MockQuicSpdyClientSession : public QuicSpdyClientSession { + public: + explicit MockQuicSpdyClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index) + : QuicSpdyClientSession(DefaultQuicConfig(), + supported_versions, + connection, + QuicServerId("example.com", 443, false), + &crypto_config_, + push_promise_index), + crypto_config_(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()) {} + MockQuicSpdyClientSession(const MockQuicSpdyClientSession&) = delete; + MockQuicSpdyClientSession& operator=(const MockQuicSpdyClientSession&) = + delete; + ~MockQuicSpdyClientSession() override {} + + MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id)); + + private: + QuicCryptoClientConfig crypto_config_; +}; + +class QuicClientPushPromiseIndexTest : public QuicTest { + public: + QuicClientPushPromiseIndexTest() + : connection_(new StrictMock<MockQuicConnection>(&helper_, + &alarm_factory_, + Perspective::IS_CLIENT)), + session_(connection_->supported_versions(), connection_, &index_), + promised_(&session_, + GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), + 0), + url_) { + request_[":path"] = "/bar"; + request_[":authority"] = "www.google.com"; + request_[":version"] = "HTTP/1.1"; + request_[":method"] = "GET"; + request_[":scheme"] = "https"; + url_ = SpdyUtils::GetPromisedUrlFromHeaders(request_); + } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + MockQuicSpdyClientSession session_; + QuicClientPushPromiseIndex index_; + spdy::SpdyHeaderBlock request_; + QuicString url_; + MockQuicClientPromisedInfo promised_; + QuicClientPushPromiseIndex::TryHandle* handle_; +}; + +TEST_F(QuicClientPushPromiseIndexTest, TryRequestSuccess) { + (*index_.promised_by_url())[url_] = &promised_; + EXPECT_CALL(promised_, HandleClientRequest(_, _)) + .WillOnce(Return(QUIC_SUCCESS)); + EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_SUCCESS); +} + +TEST_F(QuicClientPushPromiseIndexTest, TryRequestPending) { + (*index_.promised_by_url())[url_] = &promised_; + EXPECT_CALL(promised_, HandleClientRequest(_, _)) + .WillOnce(Return(QUIC_PENDING)); + EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_PENDING); +} + +TEST_F(QuicClientPushPromiseIndexTest, TryRequestFailure) { + (*index_.promised_by_url())[url_] = &promised_; + EXPECT_CALL(promised_, HandleClientRequest(_, _)) + .WillOnce(Return(QUIC_FAILURE)); + EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_FAILURE); +} + +TEST_F(QuicClientPushPromiseIndexTest, TryNoPromise) { + EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_FAILURE); +} + +TEST_F(QuicClientPushPromiseIndexTest, GetNoPromise) { + EXPECT_EQ(index_.GetPromised(url_), nullptr); +} + +TEST_F(QuicClientPushPromiseIndexTest, GetPromise) { + (*index_.promised_by_url())[url_] = &promised_; + EXPECT_EQ(index_.GetPromised(url_), &promised_); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_header_list.cc b/quic/core/http/quic_header_list.cc new file mode 100644 index 0000000..cb10248 --- /dev/null +++ b/quic/core/http/quic_header_list.cc
@@ -0,0 +1,72 @@ +// 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/http/quic_header_list.h" + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" + +namespace quic { + +QuicHeaderList::QuicHeaderList() + : max_header_list_size_(kDefaultMaxUncompressedHeaderSize), + current_header_list_size_(0), + uncompressed_header_bytes_(0), + compressed_header_bytes_(0) {} + +QuicHeaderList::QuicHeaderList(QuicHeaderList&& other) = default; + +QuicHeaderList::QuicHeaderList(const QuicHeaderList& other) = default; + +QuicHeaderList& QuicHeaderList::operator=(const QuicHeaderList& other) = + default; + +QuicHeaderList& QuicHeaderList::operator=(QuicHeaderList&& other) = default; + +QuicHeaderList::~QuicHeaderList() {} + +void QuicHeaderList::OnHeaderBlockStart() { + QUIC_BUG_IF(current_header_list_size_ != 0) + << "OnHeaderBlockStart called more than once!"; +} + +void QuicHeaderList::OnHeader(QuicStringPiece name, QuicStringPiece value) { + // Avoid infinite buffering of headers. No longer store headers + // once the current headers are over the limit. + if (current_header_list_size_ < max_header_list_size_) { + current_header_list_size_ += name.size(); + current_header_list_size_ += value.size(); + current_header_list_size_ += spdy::kPerHeaderOverhead; + header_list_.emplace_back(QuicString(name), QuicString(value)); + } +} + +void QuicHeaderList::OnHeaderBlockEnd(size_t uncompressed_header_bytes, + size_t compressed_header_bytes) { + uncompressed_header_bytes_ = uncompressed_header_bytes; + compressed_header_bytes_ = compressed_header_bytes; + if (current_header_list_size_ > max_header_list_size_) { + Clear(); + } +} + +void QuicHeaderList::Clear() { + header_list_.clear(); + current_header_list_size_ = 0; + uncompressed_header_bytes_ = 0; + compressed_header_bytes_ = 0; +} + +QuicString QuicHeaderList::DebugString() const { + QuicString s = "{ "; + for (const auto& p : *this) { + s.append(p.first + "=" + p.second + ", "); + } + s.append("}"); + return s; +} + +} // namespace quic
diff --git a/quic/core/http/quic_header_list.h b/quic/core/http/quic_header_list.h new file mode 100644 index 0000000..c7b4c80 --- /dev/null +++ b/quic/core/http/quic_header_list.h
@@ -0,0 +1,88 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_HEADER_LIST_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_HEADER_LIST_H_ + +#include <algorithm> +#include <functional> +#include <utility> + +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" +#include "net/third_party/quiche/src/spdy/core/spdy_headers_handler_interface.h" + +namespace quic { + +// A simple class that accumulates header pairs +class QUIC_EXPORT_PRIVATE QuicHeaderList + : public spdy::SpdyHeadersHandlerInterface { + public: + typedef QuicDeque<std::pair<QuicString, QuicString>> ListType; + typedef ListType::const_iterator const_iterator; + + QuicHeaderList(); + QuicHeaderList(QuicHeaderList&& other); + QuicHeaderList(const QuicHeaderList& other); + QuicHeaderList& operator=(QuicHeaderList&& other); + QuicHeaderList& operator=(const QuicHeaderList& other); + ~QuicHeaderList() override; + + // From SpdyHeadersHandlerInteface. + void OnHeaderBlockStart() override; + void OnHeader(QuicStringPiece name, QuicStringPiece value) override; + void OnHeaderBlockEnd(size_t uncompressed_header_bytes, + size_t compressed_header_bytes) override; + + void Clear(); + + const_iterator begin() const { return header_list_.begin(); } + const_iterator end() const { return header_list_.end(); } + + bool empty() const { return header_list_.empty(); } + size_t uncompressed_header_bytes() const { + return uncompressed_header_bytes_; + } + size_t compressed_header_bytes() const { return compressed_header_bytes_; } + + void set_max_header_list_size(size_t max_header_list_size) { + max_header_list_size_ = max_header_list_size; + } + + size_t max_header_list_size() const { return max_header_list_size_; } + + QuicString DebugString() const; + + private: + QuicDeque<std::pair<QuicString, QuicString>> header_list_; + + // The limit on the size of the header list (defined by spec as name + value + + // overhead for each header field). Headers over this limit will not be + // buffered, and the list will be cleared upon OnHeaderBlockEnd. + size_t max_header_list_size_; + + // Defined per the spec as the size of all header fields with an additional + // overhead for each field. + size_t current_header_list_size_; + + // TODO(dahollings) Are these fields necessary? + size_t uncompressed_header_bytes_; + size_t compressed_header_bytes_; +}; + +inline bool operator==(const QuicHeaderList& l1, const QuicHeaderList& l2) { + auto pred = [](const std::pair<QuicString, QuicString>& p1, + const std::pair<QuicString, QuicString>& p2) { + return p1.first == p2.first && p1.second == p2.second; + }; + return std::equal(l1.begin(), l1.end(), l2.begin(), pred); +} + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_HEADER_LIST_H_
diff --git a/quic/core/http/quic_header_list_test.cc b/quic/core/http/quic_header_list_test.cc new file mode 100644 index 0000000..1b2a394 --- /dev/null +++ b/quic/core/http/quic_header_list_test.cc
@@ -0,0 +1,67 @@ +// 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/http/quic_header_list.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { + +class QuicHeaderListTest : public QuicTest {}; + +// This test verifies that QuicHeaderList accumulates header pairs in order. +TEST_F(QuicHeaderListTest, OnHeader) { + QuicHeaderList headers; + headers.OnHeader("foo", "bar"); + headers.OnHeader("april", "fools"); + headers.OnHeader("beep", ""); + + EXPECT_EQ("{ foo=bar, april=fools, beep=, }", headers.DebugString()); +} + +TEST_F(QuicHeaderListTest, TooLarge) { + QuicHeaderList headers; + QuicString key = "key"; + QuicString value(1 << 18, '1'); + // Send a header that exceeds max_header_list_size. + headers.OnHeader(key, value); + // Send a second header exceeding max_header_list_size. + headers.OnHeader(key + "2", value); + // We should not allocate more memory after exceeding max_header_list_size. + EXPECT_LT(headers.DebugString().size(), 2 * value.size()); + size_t total_bytes = 2 * (key.size() + value.size()) + 1; + headers.OnHeaderBlockEnd(total_bytes, total_bytes); + EXPECT_TRUE(headers.empty()); + + EXPECT_EQ("{ }", headers.DebugString()); +} + +TEST_F(QuicHeaderListTest, NotTooLarge) { + QuicHeaderList headers; + headers.set_max_header_list_size(1 << 20); + QuicString key = "key"; + QuicString value(1 << 18, '1'); + headers.OnHeader(key, value); + size_t total_bytes = key.size() + value.size(); + headers.OnHeaderBlockEnd(total_bytes, total_bytes); + EXPECT_FALSE(headers.empty()); +} + +// This test verifies that QuicHeaderList is copyable and assignable. +TEST_F(QuicHeaderListTest, IsCopyableAndAssignable) { + QuicHeaderList headers; + headers.OnHeader("foo", "bar"); + headers.OnHeader("april", "fools"); + headers.OnHeader("beep", ""); + + QuicHeaderList headers2(headers); + QuicHeaderList headers3 = headers; + + EXPECT_EQ("{ foo=bar, april=fools, beep=, }", headers2.DebugString()); + EXPECT_EQ("{ foo=bar, april=fools, beep=, }", headers3.DebugString()); +} + +} // namespace quic
diff --git a/quic/core/http/quic_headers_stream.cc b/quic/core/http/quic_headers_stream.cc new file mode 100644 index 0000000..9c99ab7 --- /dev/null +++ b/quic/core/http/quic_headers_stream.cc
@@ -0,0 +1,157 @@ +// Copyright 2013 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/http/quic_headers_stream.h" + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo( + QuicStreamOffset headers_stream_offset, + QuicStreamOffset full_length, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) + : headers_stream_offset(headers_stream_offset), + full_length(full_length), + unacked_length(full_length), + ack_listener(std::move(ack_listener)) {} + +QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo( + const CompressedHeaderInfo& other) = default; + +QuicHeadersStream::CompressedHeaderInfo::~CompressedHeaderInfo() {} + +QuicHeadersStream::QuicHeadersStream(QuicSpdySession* session) + : QuicStream(QuicUtils::GetHeadersStreamId( + session->connection()->transport_version()), + session, + /*is_static=*/true, + BIDIRECTIONAL), + spdy_session_(session) { + // The headers stream is exempt from connection level flow control. + DisableConnectionFlowControlForThisStream(); +} + +QuicHeadersStream::~QuicHeadersStream() {} + +void QuicHeadersStream::OnDataAvailable() { + struct iovec iov; + while (sequencer()->GetReadableRegion(&iov)) { + if (spdy_session_->ProcessHeaderData(iov) != iov.iov_len) { + // Error processing data. + return; + } + sequencer()->MarkConsumed(iov.iov_len); + MaybeReleaseSequencerBuffer(); + } +} + +void QuicHeadersStream::MaybeReleaseSequencerBuffer() { + if (spdy_session_->ShouldReleaseHeadersStreamSequencerBuffer()) { + sequencer()->ReleaseBufferIfEmpty(); + } +} + +bool QuicHeadersStream::OnStreamFrameAcked(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_acked, + QuicTime::Delta ack_delay_time, + QuicByteCount* newly_acked_length) { + QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); + newly_acked.Difference(bytes_acked()); + for (const auto& acked : newly_acked) { + QuicStreamOffset acked_offset = acked.min(); + QuicByteCount acked_length = acked.max() - acked.min(); + for (CompressedHeaderInfo& header : unacked_headers_) { + if (acked_offset < header.headers_stream_offset) { + // This header frame offset belongs to headers with smaller offset, stop + // processing. + break; + } + + if (acked_offset >= header.headers_stream_offset + header.full_length) { + // This header frame belongs to headers with larger offset. + continue; + } + + QuicByteCount header_offset = acked_offset - header.headers_stream_offset; + QuicByteCount header_length = + std::min(acked_length, header.full_length - header_offset); + + if (header.unacked_length < header_length) { + QUIC_BUG << "Unsent stream data is acked. unacked_length: " + << header.unacked_length << " acked_length: " << header_length; + CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "Unsent stream data is acked"); + return false; + } + if (header.ack_listener != nullptr && header_length > 0) { + header.ack_listener->OnPacketAcked(header_length, ack_delay_time); + } + header.unacked_length -= header_length; + acked_offset += header_length; + acked_length -= header_length; + } + } + // Remove headers which are fully acked. Please note, header frames can be + // acked out of order, but unacked_headers_ is cleaned up in order. + while (!unacked_headers_.empty() && + unacked_headers_.front().unacked_length == 0) { + unacked_headers_.pop_front(); + } + return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked, + ack_delay_time, newly_acked_length); +} + +void QuicHeadersStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, + QuicByteCount data_length, + bool /*fin_retransmitted*/) { + QuicStream::OnStreamFrameRetransmitted(offset, data_length, false); + for (CompressedHeaderInfo& header : unacked_headers_) { + if (offset < header.headers_stream_offset) { + // This header frame offset belongs to headers with smaller offset, stop + // processing. + break; + } + + if (offset >= header.headers_stream_offset + header.full_length) { + // This header frame belongs to headers with larger offset. + continue; + } + + QuicByteCount header_offset = offset - header.headers_stream_offset; + QuicByteCount retransmitted_length = + std::min(data_length, header.full_length - header_offset); + if (header.ack_listener != nullptr && retransmitted_length > 0) { + header.ack_listener->OnPacketRetransmitted(retransmitted_length); + } + offset += retransmitted_length; + data_length -= retransmitted_length; + } +} + +void QuicHeadersStream::OnDataBuffered( + QuicStreamOffset offset, + QuicByteCount data_length, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) { + // Populate unacked_headers_. + if (!unacked_headers_.empty() && + (offset == unacked_headers_.back().headers_stream_offset + + unacked_headers_.back().full_length) && + ack_listener == unacked_headers_.back().ack_listener) { + // Try to combine with latest inserted entry if they belong to the same + // header (i.e., having contiguous offset and the same ack listener). + unacked_headers_.back().full_length += data_length; + unacked_headers_.back().unacked_length += data_length; + } else { + unacked_headers_.push_back( + CompressedHeaderInfo(offset, data_length, ack_listener)); + } +} + +} // namespace quic
diff --git a/quic/core/http/quic_headers_stream.h b/quic/core/http/quic_headers_stream.h new file mode 100644 index 0000000..abc0aeb --- /dev/null +++ b/quic/core/http/quic_headers_stream.h
@@ -0,0 +1,97 @@ +// Copyright 2013 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_HTTP_QUIC_HEADERS_STREAM_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_HEADERS_STREAM_H_ + +#include <cstddef> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" + +namespace quic { + +class QuicSpdySession; + +namespace test { +class QuicHeadersStreamPeer; +} // namespace test + +// Headers in QUIC are sent as HTTP/2 HEADERS or PUSH_PROMISE frames over a +// reserved stream with the id 3. Each endpoint (client and server) will +// allocate an instance of QuicHeadersStream to send and receive headers. +class QUIC_EXPORT_PRIVATE QuicHeadersStream : public QuicStream { + public: + explicit QuicHeadersStream(QuicSpdySession* session); + QuicHeadersStream(const QuicHeadersStream&) = delete; + QuicHeadersStream& operator=(const QuicHeadersStream&) = delete; + ~QuicHeadersStream() override; + + // QuicStream implementation + void OnDataAvailable() override; + + // Release underlying buffer if allowed. + void MaybeReleaseSequencerBuffer(); + + bool OnStreamFrameAcked(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_acked, + QuicTime::Delta ack_delay_time, + QuicByteCount* newly_acked_length) override; + + void OnStreamFrameRetransmitted(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_retransmitted) override; + + private: + friend class test::QuicHeadersStreamPeer; + + // CompressedHeaderInfo includes simple information of a header, including + // offset in headers stream, unacked length and ack listener of this header. + struct QUIC_EXPORT_PRIVATE CompressedHeaderInfo { + CompressedHeaderInfo( + QuicStreamOffset headers_stream_offset, + QuicStreamOffset full_length, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + CompressedHeaderInfo(const CompressedHeaderInfo& other); + ~CompressedHeaderInfo(); + + // Offset the header was sent on the headers stream. + QuicStreamOffset headers_stream_offset; + // The full length of the header. + QuicByteCount full_length; + // The remaining bytes to be acked. + QuicByteCount unacked_length; + // Ack listener of this header, and it is notified once any of the bytes has + // been acked or retransmitted. + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener; + }; + + // Returns true if the session is still connected. + bool IsConnected(); + + // Override to store mapping from offset, length to ack_listener. This + // ack_listener is notified once data within [offset, offset + length] is + // acked or retransmitted. + void OnDataBuffered( + QuicStreamOffset offset, + QuicByteCount data_length, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) + override; + + QuicSpdySession* spdy_session_; + + // Headers that have not been fully acked. + QuicDeque<CompressedHeaderInfo> unacked_headers_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_HEADERS_STREAM_H_
diff --git a/quic/core/http/quic_headers_stream_test.cc b/quic/core/http/quic_headers_stream_test.cc new file mode 100644 index 0000000..b6409b1 --- /dev/null +++ b/quic/core/http/quic_headers_stream_test.cc
@@ -0,0 +1,968 @@ +// Copyright 2013 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/http/quic_headers_stream.h" + +#include <cstdint> +#include <ostream> +#include <tuple> +#include <utility> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h" +#include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" +#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h" + +using spdy::ERROR_CODE_PROTOCOL_ERROR; +using spdy::SETTINGS_ENABLE_PUSH; +using spdy::SETTINGS_HEADER_TABLE_SIZE; +using spdy::SETTINGS_INITIAL_WINDOW_SIZE; +using spdy::SETTINGS_MAX_CONCURRENT_STREAMS; +using spdy::SETTINGS_MAX_FRAME_SIZE; +using spdy::SETTINGS_MAX_HEADER_LIST_SIZE; +using spdy::Spdy3PriorityToHttp2Weight; +using spdy::SpdyAltSvcWireFormat; +using spdy::SpdyDataIR; +using spdy::SpdyErrorCode; +using spdy::SpdyFramer; +using spdy::SpdyFramerVisitorInterface; +using spdy::SpdyGoAwayIR; +using spdy::SpdyHeaderBlock; +using spdy::SpdyHeadersHandlerInterface; +using spdy::SpdyHeadersIR; +using spdy::SpdyKnownSettingsId; +using spdy::SpdyPingId; +using spdy::SpdyPingIR; +using spdy::SpdyPriority; +using spdy::SpdyPriorityIR; +using spdy::SpdyPushPromiseIR; +using spdy::SpdyRstStreamIR; +using spdy::SpdySerializedFrame; +using spdy::SpdySettingsId; +using spdy::SpdySettingsIR; +using spdy::SpdyStreamId; +using spdy::SpdyWindowUpdateIR; +using spdy::test::TestHeadersHandler; +using testing::_; +using testing::AtLeast; +using testing::InSequence; +using testing::Invoke; +using testing::Return; +using testing::StrictMock; +using testing::WithArgs; + +namespace quic { +namespace test { + +class MockQuicHpackDebugVisitor : public QuicHpackDebugVisitor { + public: + MockQuicHpackDebugVisitor() : QuicHpackDebugVisitor() {} + MockQuicHpackDebugVisitor(const MockQuicHpackDebugVisitor&) = delete; + MockQuicHpackDebugVisitor& operator=(const MockQuicHpackDebugVisitor&) = + delete; + + MOCK_METHOD1(OnUseEntry, void(QuicTime::Delta elapsed)); +}; + +namespace { + +class MockVisitor : public SpdyFramerVisitorInterface { + public: + MOCK_METHOD1(OnError, + void(http2::Http2DecoderAdapter::SpdyFramerError error)); + MOCK_METHOD3(OnDataFrameHeader, + void(SpdyStreamId stream_id, size_t length, bool fin)); + MOCK_METHOD3(OnStreamFrameData, + void(SpdyStreamId stream_id, const char* data, size_t len)); + MOCK_METHOD1(OnStreamEnd, void(SpdyStreamId stream_id)); + MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len)); + MOCK_METHOD1(OnHeaderFrameStart, + SpdyHeadersHandlerInterface*(SpdyStreamId stream_id)); + MOCK_METHOD1(OnHeaderFrameEnd, void(SpdyStreamId stream_id)); + MOCK_METHOD3(OnControlFrameHeaderData, + bool(SpdyStreamId stream_id, + const char* header_data, + size_t len)); + MOCK_METHOD2(OnRstStream, + void(SpdyStreamId stream_id, SpdyErrorCode error_code)); + MOCK_METHOD0(OnSettings, void()); + MOCK_METHOD2(OnSetting, void(SpdySettingsId id, uint32_t value)); + MOCK_METHOD0(OnSettingsAck, void()); + MOCK_METHOD0(OnSettingsEnd, void()); + MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); + MOCK_METHOD2(OnGoAway, + void(SpdyStreamId last_accepted_stream_id, + SpdyErrorCode error_code)); + MOCK_METHOD7(OnHeaders, + void(SpdyStreamId stream_id, + bool has_priority, + int weight, + SpdyStreamId parent_stream_id, + bool exclusive, + bool fin, + bool end)); + MOCK_METHOD2(OnWindowUpdate, + void(SpdyStreamId stream_id, int delta_window_size)); + MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); + MOCK_METHOD3(OnPushPromise, + void(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + bool end)); + MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end)); + MOCK_METHOD3(OnAltSvc, + void(SpdyStreamId stream_id, + QuicStringPiece origin, + const SpdyAltSvcWireFormat::AlternativeServiceVector& + altsvc_vector)); + MOCK_METHOD4(OnPriority, + void(SpdyStreamId stream_id, + SpdyStreamId parent_stream_id, + int weight, + bool exclusive)); + MOCK_METHOD2(OnUnknownFrame, + bool(SpdyStreamId stream_id, uint8_t frame_type)); +}; + +struct TestParams { + TestParams(const ParsedQuicVersion& version, Perspective perspective) + : version(version), perspective(perspective) { + QUIC_LOG(INFO) << "TestParams: version: " + << ParsedQuicVersionToString(version) + << ", perspective: " << perspective; + } + + TestParams(const TestParams& other) + : version(other.version), perspective(other.perspective) {} + + ParsedQuicVersion version; + Perspective perspective; +}; + +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + ParsedQuicVersionVector all_supported_versions = AllSupportedVersions(); + for (size_t i = 0; i < all_supported_versions.size(); ++i) { + for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) { + params.emplace_back(all_supported_versions[i], p); + } + } + return params; +} + +class QuicHeadersStreamTest : public QuicTestWithParam<TestParams> { + public: + QuicHeadersStreamTest() + : connection_(new StrictMock<MockQuicConnection>(&helper_, + &alarm_factory_, + perspective(), + GetVersion())), + session_(connection_), + body_("hello world"), + stream_frame_( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*fin=*/false, + /*offset=*/0, + ""), + next_promised_stream_id_(2) { + session_.Initialize(); + headers_stream_ = QuicSpdySessionPeer::GetHeadersStream(&session_); + headers_[":version"] = "HTTP/1.1"; + headers_[":status"] = "200 Ok"; + headers_["content-length"] = "11"; + framer_ = std::unique_ptr<SpdyFramer>( + new SpdyFramer(SpdyFramer::ENABLE_COMPRESSION)); + deframer_ = std::unique_ptr<http2::Http2DecoderAdapter>( + new http2::Http2DecoderAdapter()); + deframer_->set_visitor(&visitor_); + EXPECT_EQ(transport_version(), session_.connection()->transport_version()); + EXPECT_TRUE(headers_stream_ != nullptr); + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + client_id_1_ = GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 0); + client_id_2_ = GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 1); + client_id_3_ = GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 2); + next_stream_id_ = + QuicUtils::StreamIdDelta(connection_->transport_version()); + } + + QuicStreamId GetNthClientInitiatedId(int n) { + return GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), n); + } + + QuicConsumedData SaveIov(size_t write_length) { + char* buf = new char[write_length]; + QuicDataWriter writer(write_length, buf, NETWORK_BYTE_ORDER); + headers_stream_->WriteStreamData(headers_stream_->stream_bytes_written(), + write_length, &writer); + saved_data_.append(buf, write_length); + delete[] buf; + return QuicConsumedData(write_length, false); + } + + void SavePayload(const char* data, size_t len) { + saved_payloads_.append(data, len); + } + + bool SaveHeaderData(const char* data, int len) { + saved_header_data_.append(data, len); + return true; + } + + void SaveHeaderDataStringPiece(QuicStringPiece data) { + saved_header_data_.append(data.data(), data.length()); + } + + void SavePromiseHeaderList(QuicStreamId /* stream_id */, + QuicStreamId /* promised_stream_id */, + size_t size, + const QuicHeaderList& header_list) { + SaveToHandler(size, header_list); + } + + void SaveHeaderList(QuicStreamId /* stream_id */, + bool /* fin */, + size_t size, + const QuicHeaderList& header_list) { + SaveToHandler(size, header_list); + } + + void SaveToHandler(size_t size, const QuicHeaderList& header_list) { + headers_handler_ = QuicMakeUnique<TestHeadersHandler>(); + headers_handler_->OnHeaderBlockStart(); + for (const auto& p : header_list) { + headers_handler_->OnHeader(p.first, p.second); + } + headers_handler_->OnHeaderBlockEnd(size, size); + } + + void WriteAndExpectRequestHeaders(QuicStreamId stream_id, + bool fin, + SpdyPriority priority) { + WriteHeadersAndCheckData(stream_id, fin, priority, true /*is_request*/); + } + + void WriteAndExpectResponseHeaders(QuicStreamId stream_id, bool fin) { + WriteHeadersAndCheckData(stream_id, fin, 0, false /*is_request*/); + } + + void WriteHeadersAndCheckData(QuicStreamId stream_id, + bool fin, + SpdyPriority priority, + bool is_request) { + // Write the headers and capture the outgoing data + EXPECT_CALL(session_, WritevData(headers_stream_, + QuicUtils::GetHeadersStreamId( + connection_->transport_version()), + _, _, NO_FIN)) + .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov))); + QuicSpdySessionPeer::WriteHeadersOnHeadersStream( + &session_, stream_id, headers_.Clone(), fin, priority, nullptr); + + // Parse the outgoing data and check that it matches was was written. + if (is_request) { + EXPECT_CALL(visitor_, + OnHeaders(stream_id, kHasPriority, + Spdy3PriorityToHttp2Weight(priority), + /*parent_stream_id=*/0, + /*exclusive=*/false, fin, kFrameComplete)); + } else { + EXPECT_CALL(visitor_, + OnHeaders(stream_id, !kHasPriority, + /*priority=*/0, + /*parent_stream_id=*/0, + /*exclusive=*/false, fin, kFrameComplete)); + } + headers_handler_ = QuicMakeUnique<TestHeadersHandler>(); + EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id)) + .WillOnce(Return(headers_handler_.get())); + EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id)).Times(1); + if (fin) { + EXPECT_CALL(visitor_, OnStreamEnd(stream_id)); + } + deframer_->ProcessInput(saved_data_.data(), saved_data_.length()); + EXPECT_FALSE(deframer_->HasError()) + << http2::Http2DecoderAdapter::SpdyFramerErrorToString( + deframer_->spdy_framer_error()); + + CheckHeaders(); + saved_data_.clear(); + } + + void CheckHeaders() { + EXPECT_EQ(headers_, headers_handler_->decoded_block()); + headers_handler_.reset(); + } + + Perspective perspective() const { return GetParam().perspective; } + + QuicTransportVersion transport_version() const { + return GetParam().version.transport_version; + } + + ParsedQuicVersionVector GetVersion() { + ParsedQuicVersionVector versions; + versions.push_back(GetParam().version); + return versions; + } + + void TearDownLocalConnectionState() { + QuicConnectionPeer::TearDownLocalConnectionState(connection_); + } + + QuicStreamId NextPromisedStreamId() { + return next_promised_stream_id_ += next_stream_id_; + } + + static const bool kFrameComplete = true; + static const bool kHasPriority = true; + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + StrictMock<MockQuicSpdySession> session_; + QuicHeadersStream* headers_stream_; + SpdyHeaderBlock headers_; + std::unique_ptr<TestHeadersHandler> headers_handler_; + QuicString body_; + QuicString saved_data_; + QuicString saved_header_data_; + QuicString saved_payloads_; + std::unique_ptr<SpdyFramer> framer_; + std::unique_ptr<http2::Http2DecoderAdapter> deframer_; + StrictMock<MockVisitor> visitor_; + QuicStreamFrame stream_frame_; + QuicStreamId next_promised_stream_id_; + QuicStreamId client_id_1_; + QuicStreamId client_id_2_; + QuicStreamId client_id_3_; + QuicStreamId next_stream_id_; +}; + +// Run all tests with each version and perspective (client or server). +INSTANTIATE_TEST_SUITE_P(Tests, QuicHeadersStreamTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QuicHeadersStreamTest, StreamId) { + EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_->transport_version()), + headers_stream_->id()); +} + +TEST_P(QuicHeadersStreamTest, WriteHeaders) { + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + for (bool fin : {false, true}) { + if (perspective() == Perspective::IS_SERVER) { + WriteAndExpectResponseHeaders(stream_id, fin); + } else { + for (SpdyPriority priority = 0; priority < 7; ++priority) { + // TODO(rch): implement priorities correctly. + WriteAndExpectRequestHeaders(stream_id, fin, 0); + } + } + } + } +} + +TEST_P(QuicHeadersStreamTest, WritePushPromises) { + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + QuicStreamId promised_stream_id = NextPromisedStreamId(); + if (perspective() == Perspective::IS_SERVER) { + // Write the headers and capture the outgoing data + EXPECT_CALL(session_, WritevData(headers_stream_, + QuicUtils::GetHeadersStreamId( + connection_->transport_version()), + _, _, NO_FIN)) + .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov))); + session_.WritePushPromise(stream_id, promised_stream_id, + headers_.Clone()); + + // Parse the outgoing data and check that it matches was was written. + EXPECT_CALL(visitor_, + OnPushPromise(stream_id, promised_stream_id, kFrameComplete)); + headers_handler_ = QuicMakeUnique<TestHeadersHandler>(); + EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id)) + .WillOnce(Return(headers_handler_.get())); + EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id)).Times(1); + deframer_->ProcessInput(saved_data_.data(), saved_data_.length()); + EXPECT_FALSE(deframer_->HasError()) + << http2::Http2DecoderAdapter::SpdyFramerErrorToString( + deframer_->spdy_framer_error()); + CheckHeaders(); + saved_data_.clear(); + } else { + EXPECT_QUIC_BUG(session_.WritePushPromise(stream_id, promised_stream_id, + headers_.Clone()), + "Client shouldn't send PUSH_PROMISE"); + } + } +} + +TEST_P(QuicHeadersStreamTest, ProcessRawData) { + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + for (bool fin : {false, true}) { + for (SpdyPriority priority = 0; priority < 7; ++priority) { + // Replace with "WriteHeadersAndSaveData" + SpdySerializedFrame frame; + if (perspective() == Perspective::IS_SERVER) { + SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); + headers_frame.set_fin(fin); + headers_frame.set_has_priority(true); + headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); + frame = framer_->SerializeFrame(headers_frame); + EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); + } else { + SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); + headers_frame.set_fin(fin); + frame = framer_->SerializeFrame(headers_frame); + } + EXPECT_CALL(session_, + OnStreamHeaderList(stream_id, fin, frame.size(), _)) + .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); + stream_frame_.offset += frame.size(); + CheckHeaders(); + } + } + } +} + +TEST_P(QuicHeadersStreamTest, ProcessPushPromise) { + if (perspective() == Perspective::IS_SERVER) { + return; + } + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + QuicStreamId promised_stream_id = NextPromisedStreamId(); + SpdyPushPromiseIR push_promise(stream_id, promised_stream_id, + headers_.Clone()); + SpdySerializedFrame frame(framer_->SerializeFrame(push_promise)); + if (perspective() == Perspective::IS_SERVER) { + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "PUSH_PROMISE not supported.", _)) + .WillRepeatedly(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + } else { + EXPECT_CALL(session_, OnPromiseHeaderList(stream_id, promised_stream_id, + frame.size(), _)) + .WillOnce( + Invoke(this, &QuicHeadersStreamTest::SavePromiseHeaderList)); + } + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); + if (perspective() == Perspective::IS_CLIENT) { + stream_frame_.offset += frame.size(); + CheckHeaders(); + } + } +} + +TEST_P(QuicHeadersStreamTest, ProcessPriorityFrame) { + QuicStreamId parent_stream_id = 0; + for (SpdyPriority priority = 0; priority < 7; ++priority) { + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + int weight = Spdy3PriorityToHttp2Weight(priority); + SpdyPriorityIR priority_frame(stream_id, parent_stream_id, weight, true); + SpdySerializedFrame frame(framer_->SerializeFrame(priority_frame)); + parent_stream_id = stream_id; + if (transport_version() <= QUIC_VERSION_39) { + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY PRIORITY frame received.", _)) + .WillRepeatedly(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + } else if (perspective() == Perspective::IS_CLIENT) { + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "Server must not send PRIORITY frames.", _)) + .WillRepeatedly(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + } else { + EXPECT_CALL(session_, OnPriorityFrame(stream_id, priority)).Times(1); + } + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); + stream_frame_.offset += frame.size(); + } + } +} + +TEST_P(QuicHeadersStreamTest, ProcessPushPromiseDisabledSetting) { + session_.OnConfigNegotiated(); + SpdySettingsIR data; + // Respect supported settings frames SETTINGS_ENABLE_PUSH. + data.AddSetting(SETTINGS_ENABLE_PUSH, 0); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + if (perspective() == Perspective::IS_CLIENT) { + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "Unsupported field of HTTP/2 SETTINGS frame: 2", _)); + } + headers_stream_->OnStreamFrame(stream_frame_); + EXPECT_EQ(session_.server_push_enabled(), + perspective() == Perspective::IS_CLIENT); +} + +TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) { + QuicSpdySessionPeer::SetMaxUncompressedHeaderBytes(&session_, 256 * 1024); + // We want to create a frame that is more than the SPDY Framer's max control + // frame size, which is 16K, but less than the HPACK decoders max decode + // buffer size, which is 32K. + headers_["key0"] = QuicString(1 << 13, '.'); + headers_["key1"] = QuicString(1 << 13, '.'); + headers_["key2"] = QuicString(1 << 13, '.'); + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + for (bool fin : {false, true}) { + for (SpdyPriority priority = 0; priority < 7; ++priority) { + // Replace with "WriteHeadersAndSaveData" + SpdySerializedFrame frame; + if (perspective() == Perspective::IS_SERVER) { + SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); + headers_frame.set_fin(fin); + headers_frame.set_has_priority(true); + headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); + frame = framer_->SerializeFrame(headers_frame); + EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); + } else { + SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); + headers_frame.set_fin(fin); + frame = framer_->SerializeFrame(headers_frame); + } + EXPECT_CALL(session_, + OnStreamHeaderList(stream_id, fin, frame.size(), _)) + .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); + stream_frame_.offset += frame.size(); + CheckHeaders(); + } + } + } +} + +TEST_P(QuicHeadersStreamTest, ProcessBadData) { + const char kBadData[] = "blah blah blah"; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _)) + .Times(::testing::AnyNumber()); + stream_frame_.data_buffer = kBadData; + stream_frame_.data_length = strlen(kBadData); + headers_stream_->OnStreamFrame(stream_frame_); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) { + SpdyDataIR data(/* stream_id = */ 2, "ping"); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY DATA frame received.", _)) + .WillOnce(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) { + SpdyRstStreamIR data(/* stream_id = */ 2, ERROR_CODE_PROTOCOL_ERROR); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY RST_STREAM frame received.", _)) + .WillOnce(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); +} + +TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameSupportedFields) { + const uint32_t kTestHeaderTableSize = 1000; + SpdySettingsIR data; + // Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE, + // SETTINGS_MAX_HEADER_LIST_SIZE. + data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, kTestHeaderTableSize); + data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, 2000); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); + EXPECT_EQ(kTestHeaderTableSize, QuicSpdySessionPeer::GetSpdyFramer(&session_) + .header_encoder_table_size()); +} + +TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameUnsupportedFields) { + SpdySettingsIR data; + // Does not support SETTINGS_MAX_CONCURRENT_STREAMS, + // SETTINGS_INITIAL_WINDOW_SIZE, SETTINGS_ENABLE_PUSH and + // SETTINGS_MAX_FRAME_SIZE. + data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 100); + data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 100); + data.AddSetting(SETTINGS_ENABLE_PUSH, 1); + data.AddSetting(SETTINGS_MAX_FRAME_SIZE, 1250); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", + SETTINGS_MAX_CONCURRENT_STREAMS), + _)); + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", + SETTINGS_INITIAL_WINDOW_SIZE), + _)); + if (session_.perspective() == Perspective::IS_CLIENT) { + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", + SETTINGS_ENABLE_PUSH), + _)); + } + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", + SETTINGS_MAX_FRAME_SIZE), + _)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) { + SpdyPingIR data(1); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY PING frame received.", _)) + .WillOnce(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) { + SpdyGoAwayIR data(/* last_good_stream_id = */ 1, ERROR_CODE_PROTOCOL_ERROR, + "go away"); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY GOAWAY frame received.", _)) + .WillOnce(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) { + SpdyWindowUpdateIR data(/* stream_id = */ 1, /* delta = */ 1); + SpdySerializedFrame frame(framer_->SerializeFrame(data)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY WINDOW_UPDATE frame received.", _)) + .WillOnce(InvokeWithoutArgs( + this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + headers_stream_->OnStreamFrame(stream_frame_); +} + +TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) { + EXPECT_FALSE(QuicStreamPeer::StreamContributesToConnectionFlowControl( + headers_stream_)); +} + +TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) { + auto hpack_decoder_visitor = + QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>(); + { + InSequence seq; + // Number of indexed representations generated in headers below. + for (int i = 1; i < 28; i++) { + EXPECT_CALL(*hpack_decoder_visitor, + OnUseEntry(QuicTime::Delta::FromMilliseconds(i))) + .Times(4); + } + } + QuicSpdySessionPeer::SetHpackDecoderDebugVisitor( + &session_, std::move(hpack_decoder_visitor)); + + // Create some headers we expect to generate entries in HPACK's + // dynamic table, in addition to content-length. + headers_["key0"] = QuicString(1 << 1, '.'); + headers_["key1"] = QuicString(1 << 2, '.'); + headers_["key2"] = QuicString(1 << 3, '.'); + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + for (bool fin : {false, true}) { + for (SpdyPriority priority = 0; priority < 7; ++priority) { + // Replace with "WriteHeadersAndSaveData" + SpdySerializedFrame frame; + if (perspective() == Perspective::IS_SERVER) { + SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); + headers_frame.set_fin(fin); + headers_frame.set_has_priority(true); + headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); + frame = framer_->SerializeFrame(headers_frame); + EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); + } else { + SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); + headers_frame.set_fin(fin); + frame = framer_->SerializeFrame(headers_frame); + } + EXPECT_CALL(session_, + OnStreamHeaderList(stream_id, fin, frame.size(), _)) + .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList)); + stream_frame_.data_buffer = frame.data(); + stream_frame_.data_length = frame.size(); + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + headers_stream_->OnStreamFrame(stream_frame_); + stream_frame_.offset += frame.size(); + CheckHeaders(); + } + } + } +} + +TEST_P(QuicHeadersStreamTest, HpackEncoderDebugVisitor) { + auto hpack_encoder_visitor = + QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>(); + if (perspective() == Perspective::IS_SERVER) { + InSequence seq; + for (int i = 1; i < 4; i++) { + EXPECT_CALL(*hpack_encoder_visitor, + OnUseEntry(QuicTime::Delta::FromMilliseconds(i))); + } + } else { + InSequence seq; + for (int i = 1; i < 28; i++) { + EXPECT_CALL(*hpack_encoder_visitor, + OnUseEntry(QuicTime::Delta::FromMilliseconds(i))); + } + } + QuicSpdySessionPeer::SetHpackEncoderDebugVisitor( + &session_, std::move(hpack_encoder_visitor)); + + for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; + stream_id += next_stream_id_) { + for (bool fin : {false, true}) { + if (perspective() == Perspective::IS_SERVER) { + WriteAndExpectResponseHeaders(stream_id, fin); + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + } else { + for (SpdyPriority priority = 0; priority < 7; ++priority) { + // TODO(rch): implement priorities correctly. + WriteAndExpectRequestHeaders(stream_id, fin, 0); + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + } + } + } + } +} + +TEST_P(QuicHeadersStreamTest, AckSentData) { + EXPECT_CALL(session_, WritevData(headers_stream_, + QuicUtils::GetHeadersStreamId( + connection_->transport_version()), + _, _, NO_FIN)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + InSequence s; + QuicReferenceCountedPointer<MockAckListener> ack_listener1( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener2( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener3( + new MockAckListener()); + + // Packet 1. + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + + // Packet 2. + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + + // Packet 3. + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + + // Packet 2 gets retransmitted. + EXPECT_CALL(*ack_listener3, OnPacketRetransmitted(7)).Times(1); + EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(7)).Times(1); + headers_stream_->OnStreamFrameRetransmitted(21, 7, false); + headers_stream_->OnStreamFrameRetransmitted(28, 7, false); + + // Packets are acked in order: 2, 3, 1. + QuicByteCount newly_acked_length = 0; + EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 21, 7, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(7u, newly_acked_length); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 28, 7, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(7u, newly_acked_length); + + EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 35, 7, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(7u, newly_acked_length); + + EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _)); + EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 0, 7, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(7u, newly_acked_length); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 7, 7, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(7u, newly_acked_length); + // Unsent data is acked. + EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 14, 10, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(7u, newly_acked_length); +} + +TEST_P(QuicHeadersStreamTest, FrameContainsMultipleHeaders) { + // In this test, a stream frame can contain multiple headers. + EXPECT_CALL(session_, WritevData(headers_stream_, + QuicUtils::GetHeadersStreamId( + connection_->transport_version()), + _, _, NO_FIN)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + InSequence s; + QuicReferenceCountedPointer<MockAckListener> ack_listener1( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener2( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener3( + new MockAckListener()); + + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + + // Frame 1 is retransmitted. + EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(14)); + EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(3)); + headers_stream_->OnStreamFrameRetransmitted(0, 17, false); + + // Frames are acked in order: 2, 3, 1. + QuicByteCount newly_acked_length = 0; + EXPECT_CALL(*ack_listener2, OnPacketAcked(4, _)); + EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(2, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 17, 13, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(13u, newly_acked_length); + + EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _)); + EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 30, 12, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(12u, newly_acked_length); + + EXPECT_CALL(*ack_listener1, OnPacketAcked(14, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(3, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 0, 17, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(17u, newly_acked_length); +} + +TEST_P(QuicHeadersStreamTest, HeadersGetAckedMultipleTimes) { + EXPECT_CALL(session_, WritevData(headers_stream_, + QuicUtils::GetHeadersStreamId( + connection_->transport_version()), + _, _, NO_FIN)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + InSequence s; + QuicReferenceCountedPointer<MockAckListener> ack_listener1( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener2( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener3( + new MockAckListener()); + + // Send [0, 42). + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + + // Ack [15, 20), [5, 25), [10, 17), [0, 12) and [22, 42). + QuicByteCount newly_acked_length = 0; + EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 15, 5, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(5u, newly_acked_length); + + EXPECT_CALL(*ack_listener1, OnPacketAcked(9, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _)); + EXPECT_CALL(*ack_listener3, OnPacketAcked(4, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 5, 20, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(15u, newly_acked_length); + + // Duplicate ack. + EXPECT_FALSE(headers_stream_->OnStreamFrameAcked( + 10, 7, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(0u, newly_acked_length); + + EXPECT_CALL(*ack_listener1, OnPacketAcked(5, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 0, 12, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(5u, newly_acked_length); + + EXPECT_CALL(*ack_listener3, OnPacketAcked(3, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _)); + EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _)); + EXPECT_TRUE(headers_stream_->OnStreamFrameAcked( + 22, 20, false, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(17u, newly_acked_length); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_server_session_base.cc b/quic/core/http/quic_server_session_base.cc new file mode 100644 index 0000000..f240ece --- /dev/null +++ b/quic/core/http/quic_server_session_base.cc
@@ -0,0 +1,277 @@ +// Copyright (c) 2012 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/http/quic_server_session_base.h" + +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicServerSessionBase::QuicServerSessionBase( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + Visitor* visitor, + QuicCryptoServerStream::Helper* helper, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache) + : QuicSpdySession(connection, visitor, config, supported_versions), + crypto_config_(crypto_config), + compressed_certs_cache_(compressed_certs_cache), + helper_(helper), + bandwidth_resumption_enabled_(false), + bandwidth_estimate_sent_to_client_(QuicBandwidth::Zero()), + last_scup_time_(QuicTime::Zero()) {} + +QuicServerSessionBase::~QuicServerSessionBase() {} + +void QuicServerSessionBase::Initialize() { + crypto_stream_.reset( + CreateQuicCryptoServerStream(crypto_config_, compressed_certs_cache_)); + QuicSpdySession::Initialize(); +} + +void QuicServerSessionBase::OnConfigNegotiated() { + QuicSpdySession::OnConfigNegotiated(); + + if (!config()->HasReceivedConnectionOptions()) { + return; + } + + // Enable bandwidth resumption if peer sent correct connection options. + const bool last_bandwidth_resumption = + ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE); + const bool max_bandwidth_resumption = + ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWMX); + bandwidth_resumption_enabled_ = + last_bandwidth_resumption || max_bandwidth_resumption; + + // If the client has provided a bandwidth estimate from the same serving + // region as this server, then decide whether to use the data for bandwidth + // resumption. + const CachedNetworkParameters* cached_network_params = + crypto_stream_->PreviousCachedNetworkParams(); + if (cached_network_params != nullptr && + cached_network_params->serving_region() == serving_region_) { + // Log the received connection parameters, regardless of how they + // get used for bandwidth resumption. + connection()->OnReceiveConnectionState(*cached_network_params); + + if (bandwidth_resumption_enabled_) { + // Only do bandwidth resumption if estimate is recent enough. + const uint64_t seconds_since_estimate = + connection()->clock()->WallNow().ToUNIXSeconds() - + cached_network_params->timestamp(); + if (seconds_since_estimate <= kNumSecondsPerHour) { + connection()->ResumeConnectionState(*cached_network_params, + max_bandwidth_resumption); + } + } + } +} + +void QuicServerSessionBase::OnConnectionClosed(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) { + QuicSession::OnConnectionClosed(error, error_details, source); + // In the unlikely event we get a connection close while doing an asynchronous + // crypto event, make sure we cancel the callback. + if (crypto_stream_ != nullptr) { + crypto_stream_->CancelOutstandingCallbacks(); + } +} + +void QuicServerSessionBase::OnCongestionWindowChange(QuicTime now) { + if (!bandwidth_resumption_enabled_) { + return; + } + // Only send updates when the application has no data to write. + if (HasDataToWrite()) { + return; + } + + // If not enough time has passed since the last time we sent an update to the + // client, or not enough packets have been sent, then return early. + const QuicSentPacketManager& sent_packet_manager = + connection()->sent_packet_manager(); + int64_t srtt_ms = + sent_packet_manager.GetRttStats()->smoothed_rtt().ToMilliseconds(); + int64_t now_ms = (now - last_scup_time_).ToMilliseconds(); + int64_t packets_since_last_scup = 0; + const QuicPacketNumber largest_sent_packet = + connection()->sent_packet_manager().GetLargestSentPacket(); + if (largest_sent_packet.IsInitialized()) { + packets_since_last_scup = + last_scup_packet_number_.IsInitialized() + ? largest_sent_packet - last_scup_packet_number_ + : largest_sent_packet.ToUint64(); + } + if (now_ms < (kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms) || + now_ms < kMinIntervalBetweenServerConfigUpdatesMs || + packets_since_last_scup < kMinPacketsBetweenServerConfigUpdates) { + return; + } + + // If the bandwidth recorder does not have a valid estimate, return early. + const QuicSustainedBandwidthRecorder* bandwidth_recorder = + sent_packet_manager.SustainedBandwidthRecorder(); + if (bandwidth_recorder == nullptr || !bandwidth_recorder->HasEstimate()) { + return; + } + + // The bandwidth recorder has recorded at least one sustained bandwidth + // estimate. Check that it's substantially different from the last one that + // we sent to the client, and if so, send the new one. + QuicBandwidth new_bandwidth_estimate = + bandwidth_recorder->BandwidthEstimate(); + + int64_t bandwidth_delta = + std::abs(new_bandwidth_estimate.ToBitsPerSecond() - + bandwidth_estimate_sent_to_client_.ToBitsPerSecond()); + + // Define "substantial" difference as a 50% increase or decrease from the + // last estimate. + bool substantial_difference = + bandwidth_delta > + 0.5 * bandwidth_estimate_sent_to_client_.ToBitsPerSecond(); + if (!substantial_difference) { + return; + } + + bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate; + QUIC_DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): " + << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond(); + + // Include max bandwidth in the update. + QuicBandwidth max_bandwidth_estimate = + bandwidth_recorder->MaxBandwidthEstimate(); + int32_t max_bandwidth_timestamp = bandwidth_recorder->MaxBandwidthTimestamp(); + + // Fill the proto before passing it to the crypto stream to send. + const int32_t bw_estimate_bytes_per_second = + BandwidthToCachedParameterBytesPerSecond( + bandwidth_estimate_sent_to_client_); + const int32_t max_bw_estimate_bytes_per_second = + BandwidthToCachedParameterBytesPerSecond(max_bandwidth_estimate); + QUIC_BUG_IF(max_bw_estimate_bytes_per_second < 0) + << max_bw_estimate_bytes_per_second; + QUIC_BUG_IF(bw_estimate_bytes_per_second < 0) << bw_estimate_bytes_per_second; + + CachedNetworkParameters cached_network_params; + cached_network_params.set_bandwidth_estimate_bytes_per_second( + bw_estimate_bytes_per_second); + cached_network_params.set_max_bandwidth_estimate_bytes_per_second( + max_bw_estimate_bytes_per_second); + cached_network_params.set_max_bandwidth_timestamp_seconds( + max_bandwidth_timestamp); + cached_network_params.set_min_rtt_ms( + sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds()); + cached_network_params.set_previous_connection_state( + bandwidth_recorder->EstimateRecordedDuringSlowStart() + ? CachedNetworkParameters::SLOW_START + : CachedNetworkParameters::CONGESTION_AVOIDANCE); + cached_network_params.set_timestamp( + connection()->clock()->WallNow().ToUNIXSeconds()); + if (!serving_region_.empty()) { + cached_network_params.set_serving_region(serving_region_); + } + + crypto_stream_->SendServerConfigUpdate(&cached_network_params); + + connection()->OnSendConnectionState(cached_network_params); + + last_scup_time_ = now; + last_scup_packet_number_ = + connection()->sent_packet_manager().GetLargestSentPacket(); +} + +bool QuicServerSessionBase::ShouldCreateIncomingStream(QuicStreamId id) { + if (!connection()->connected()) { + QUIC_BUG << "ShouldCreateIncomingStream called when disconnected"; + return false; + } + + if (QuicUtils::IsServerInitiatedStreamId(connection()->transport_version(), + id)) { + QUIC_DLOG(INFO) << "Invalid incoming even stream_id:" << id; + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Client created even numbered stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + return true; +} + +bool QuicServerSessionBase::ShouldCreateOutgoingBidirectionalStream() { + if (!connection()->connected()) { + QUIC_BUG + << "ShouldCreateOutgoingBidirectionalStream called when disconnected"; + return false; + } + if (!crypto_stream_->encryption_established()) { + QUIC_BUG << "Encryption not established so no outgoing stream created."; + return false; + } + + if (!GetQuicReloadableFlag(quic_use_common_stream_check) && + connection()->transport_version() != QUIC_VERSION_99) { + if (GetNumOpenOutgoingStreams() >= + stream_id_manager().max_open_outgoing_streams()) { + VLOG(1) << "No more streams should be created. " + << "Already " << GetNumOpenOutgoingStreams() << " open."; + return false; + } + } + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_common_stream_check, 2, 2); + return CanOpenNextOutgoingBidirectionalStream(); +} + +bool QuicServerSessionBase::ShouldCreateOutgoingUnidirectionalStream() { + if (!connection()->connected()) { + QUIC_BUG + << "ShouldCreateOutgoingUnidirectionalStream called when disconnected"; + return false; + } + if (!crypto_stream_->encryption_established()) { + QUIC_BUG << "Encryption not established so no outgoing stream created."; + return false; + } + + if (!GetQuicReloadableFlag(quic_use_common_stream_check) && + connection()->transport_version() != QUIC_VERSION_99) { + if (GetNumOpenOutgoingStreams() >= + stream_id_manager().max_open_outgoing_streams()) { + VLOG(1) << "No more streams should be created. " + << "Already " << GetNumOpenOutgoingStreams() << " open."; + return false; + } + } + + return CanOpenNextOutgoingUnidirectionalStream(); +} + +QuicCryptoServerStreamBase* QuicServerSessionBase::GetMutableCryptoStream() { + return crypto_stream_.get(); +} + +const QuicCryptoServerStreamBase* QuicServerSessionBase::GetCryptoStream() + const { + return crypto_stream_.get(); +} + +int32_t QuicServerSessionBase::BandwidthToCachedParameterBytesPerSecond( + const QuicBandwidth& bandwidth) { + return static_cast<int32_t>(std::min<int64_t>( + bandwidth.ToBytesPerSecond(), std::numeric_limits<uint32_t>::max())); +} + +} // namespace quic
diff --git a/quic/core/http/quic_server_session_base.h b/quic/core/http/quic_server_session_base.h new file mode 100644 index 0000000..55a8d60 --- /dev/null +++ b/quic/core/http/quic_server_session_base.h
@@ -0,0 +1,140 @@ +// Copyright (c) 2012 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. + +// A server specific QuicSession subclass. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_SESSION_BASE_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_SESSION_BASE_H_ + +#include <cstdint> +#include <memory> +#include <set> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class QuicConfig; +class QuicConnection; +class QuicCryptoServerConfig; + +namespace test { +class QuicServerSessionBasePeer; +class QuicSimpleServerSessionPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE QuicServerSessionBase : public QuicSpdySession { + public: + // Does not take ownership of |connection|. |crypto_config| must outlive the + // session. |helper| must outlive any created crypto streams. + QuicServerSessionBase(const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + QuicSession::Visitor* visitor, + QuicCryptoServerStream::Helper* helper, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache); + QuicServerSessionBase(const QuicServerSessionBase&) = delete; + QuicServerSessionBase& operator=(const QuicServerSessionBase&) = delete; + + // Override the base class to cancel any ongoing asychronous crypto. + void OnConnectionClosed(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) override; + + // Sends a server config update to the client, containing new bandwidth + // estimate. + void OnCongestionWindowChange(QuicTime now) override; + + ~QuicServerSessionBase() override; + + void Initialize() override; + + const QuicCryptoServerStreamBase* crypto_stream() const { + return crypto_stream_.get(); + } + + // Override base class to process bandwidth related config received from + // client. + void OnConfigNegotiated() override; + + void set_serving_region(const QuicString& serving_region) { + serving_region_ = serving_region; + } + + protected: + // QuicSession methods(override them with return type of QuicSpdyStream*): + QuicCryptoServerStreamBase* GetMutableCryptoStream() override; + + const QuicCryptoServerStreamBase* GetCryptoStream() const override; + + // If an outgoing stream can be created, return true. + // Return false when connection is closed or forward secure encryption hasn't + // established yet or number of server initiated streams already reaches the + // upper limit. + bool ShouldCreateOutgoingBidirectionalStream() override; + bool ShouldCreateOutgoingUnidirectionalStream() override; + + // If we should create an incoming stream, returns true. Otherwise + // does error handling, including communicating the error to the client and + // possibly closing the connection, and returns false. + bool ShouldCreateIncomingStream(QuicStreamId id) override; + + virtual QuicCryptoServerStreamBase* CreateQuicCryptoServerStream( + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache) = 0; + + const QuicCryptoServerConfig* crypto_config() { return crypto_config_; } + + QuicCryptoServerStream::Helper* stream_helper() { return helper_; } + + private: + friend class test::QuicServerSessionBasePeer; + friend class test::QuicSimpleServerSessionPeer; + + const QuicCryptoServerConfig* crypto_config_; + + // The cache which contains most recently compressed certs. + // Owned by QuicDispatcher. + QuicCompressedCertsCache* compressed_certs_cache_; + + std::unique_ptr<QuicCryptoServerStreamBase> crypto_stream_; + + // Pointer to the helper used to create crypto server streams. Must outlive + // streams created via CreateQuicCryptoServerStream. + QuicCryptoServerStream::Helper* helper_; + + // Whether bandwidth resumption is enabled for this connection. + bool bandwidth_resumption_enabled_; + + // The most recent bandwidth estimate sent to the client. + QuicBandwidth bandwidth_estimate_sent_to_client_; + + // Text describing server location. Sent to the client as part of the bandwith + // estimate in the source-address token. Optional, can be left empty. + QuicString serving_region_; + + // Time at which we send the last SCUP to the client. + QuicTime last_scup_time_; + + // Number of packets sent to the peer, at the time we last sent a SCUP. + QuicPacketNumber last_scup_packet_number_; + + // Converts QuicBandwidth to an int32 bytes/second that can be + // stored in CachedNetworkParameters. TODO(jokulik): This function + // should go away once we fix http://b//27897982 + int32_t BandwidthToCachedParameterBytesPerSecond( + const QuicBandwidth& bandwidth); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_SESSION_BASE_H_
diff --git a/quic/core/http/quic_server_session_base_test.cc b/quic/core/http/quic_server_session_base_test.cc new file mode 100644 index 0000000..efc2388 --- /dev/null +++ b/quic/core/http/quic_server_session_base_test.cc
@@ -0,0 +1,740 @@ +// Copyright 2013 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/http/quic_server_session_base.h" + +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_server_session_base_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" +#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h" + +using testing::_; +using testing::StrictMock; + +using testing::AtLeast; +using testing::Return; + +namespace quic { +namespace test { +namespace { + +class TestServerSession : public QuicServerSessionBase { + public: + TestServerSession(const QuicConfig& config, + QuicConnection* connection, + QuicSession::Visitor* visitor, + QuicCryptoServerStream::Helper* helper, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + QuicSimpleServerBackend* quic_simple_server_backend) + : QuicServerSessionBase(config, + CurrentSupportedVersions(), + connection, + visitor, + helper, + crypto_config, + compressed_certs_cache), + quic_simple_server_backend_(quic_simple_server_backend) {} + + ~TestServerSession() override { delete connection(); } + + protected: + QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override { + if (!ShouldCreateIncomingStream(id)) { + return nullptr; + } + QuicSpdyStream* stream = new QuicSimpleServerStream( + id, this, BIDIRECTIONAL, quic_simple_server_backend_); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + QuicSpdyStream* CreateIncomingStream(PendingStream pending) override { + QuicSpdyStream* stream = new QuicSimpleServerStream( + std::move(pending), this, BIDIRECTIONAL, quic_simple_server_backend_); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + QuicSpdyStream* CreateOutgoingBidirectionalStream() override { + DCHECK(false); + return nullptr; + } + + QuicSpdyStream* CreateOutgoingUnidirectionalStream() override { + if (!ShouldCreateOutgoingUnidirectionalStream()) { + return nullptr; + } + + QuicSpdyStream* stream = new QuicSimpleServerStream( + GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL, + quic_simple_server_backend_); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + QuicCryptoServerStreamBase* CreateQuicCryptoServerStream( + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache) override { + return new QuicCryptoServerStream( + crypto_config, compressed_certs_cache, + GetQuicReloadableFlag(enable_quic_stateless_reject_support), this, + stream_helper()); + } + + private: + QuicSimpleServerBackend* + quic_simple_server_backend_; // Owned by QuicServerSessionBaseTest +}; + +const size_t kMaxStreamsForTest = 10; + +class QuicServerSessionBaseTest : public QuicTestWithParam<ParsedQuicVersion> { + protected: + QuicServerSessionBaseTest() + : QuicServerSessionBaseTest(crypto_test_utils::ProofSourceForTesting()) {} + + explicit QuicServerSessionBaseTest(std::unique_ptr<ProofSource> proof_source) + : crypto_config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance(), + std::move(proof_source), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + compressed_certs_cache_( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) { + config_.SetMaxIncomingDynamicStreamsToSend(kMaxStreamsForTest); + QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(&config_, + kMaxStreamsForTest); + config_.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + config_.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + + ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam()); + connection_ = new StrictMock<MockQuicConnection>( + &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions); + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + session_ = QuicMakeUnique<TestServerSession>( + config_, connection_, &owner_, &stream_helper_, &crypto_config_, + &compressed_certs_cache_, &memory_cache_backend_); + MockClock clock; + handshake_message_.reset(crypto_config_.AddDefaultConfig( + QuicRandom::GetInstance(), &clock, + QuicCryptoServerConfig::ConfigOptions())); + session_->Initialize(); + QuicSessionPeer::GetMutableCryptoStream(session_.get()) + ->OnSuccessfulVersionNegotiation(supported_versions.front()); + visitor_ = QuicConnectionPeer::GetVisitor(connection_); + } + + QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { + return GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), n); + } + + QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) { + return quic::test::GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), n); + } + + QuicTransportVersion transport_version() const { + return connection_->transport_version(); + } + + // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a + // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a + // one-way close. This method can be used to inject a STOP_SENDING, which + // would cause a close in the opposite direction. This allows tests to do the + // extra work to get a two-way (full) close where desired. Also sets up + // expects needed to ensure that the STOP_SENDING worked as expected. + void InjectStopSendingFrame(QuicStreamId stream_id, + QuicRstStreamErrorCode rst_stream_code) { + if (transport_version() != QUIC_VERSION_99) { + // Only needed for version 99/IETF QUIC. Noop otherwise. + return; + } + QuicStopSendingFrame stop_sending( + kInvalidControlFrameId, stream_id, + static_cast<QuicApplicationErrorCode>(rst_stream_code)); + EXPECT_CALL(owner_, OnStopSendingReceived(_)).Times(1); + // Expect the RESET_STREAM that is generated in response to receiving a + // STOP_SENDING. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream_id, rst_stream_code)); + session_->OnStopSendingFrame(stop_sending); + } + + StrictMock<MockQuicSessionVisitor> owner_; + StrictMock<MockQuicCryptoServerStreamHelper> stream_helper_; + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + QuicConfig config_; + QuicCryptoServerConfig crypto_config_; + QuicCompressedCertsCache compressed_certs_cache_; + QuicMemoryCacheBackend memory_cache_backend_; + std::unique_ptr<TestServerSession> session_; + std::unique_ptr<CryptoHandshakeMessage> handshake_message_; + QuicConnectionVisitorInterface* visitor_; +}; + +// Compares CachedNetworkParameters. +MATCHER_P(EqualsProto, network_params, "") { + CachedNetworkParameters reference(network_params); + return (arg->bandwidth_estimate_bytes_per_second() == + reference.bandwidth_estimate_bytes_per_second() && + arg->bandwidth_estimate_bytes_per_second() == + reference.bandwidth_estimate_bytes_per_second() && + arg->max_bandwidth_estimate_bytes_per_second() == + reference.max_bandwidth_estimate_bytes_per_second() && + arg->max_bandwidth_timestamp_seconds() == + reference.max_bandwidth_timestamp_seconds() && + arg->min_rtt_ms() == reference.min_rtt_ms() && + arg->previous_connection_state() == + reference.previous_connection_state()); +} + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicServerSessionBaseTest, + ::testing::ValuesIn(AllSupportedVersions())); +TEST_P(QuicServerSessionBaseTest, CloseStreamDueToReset) { + // Open a stream, then reset it. + // Send two bytes of payload to open it. + QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece("HT")); + session_->OnStreamFrame(data1); + EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams()); + + // Send a reset (and expect the peer to send a RST in response). + QuicRstStreamFrame rst1(kInvalidControlFrameId, + GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM, 0); + EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); + if (transport_version() != QUIC_VERSION_99) { + // For non-version 99, the RESET_STREAM will do the full close. + // Set up expects accordingly. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(GetNthClientInitiatedBidirectionalId(0), + QUIC_RST_ACKNOWLEDGEMENT)); + } + visitor_->OnRstStream(rst1); + + // For version-99 will create and receive a stop-sending, completing + // the full-close expected by this test. + InjectStopSendingFrame(GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM); + + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); + + // Send the same two bytes of payload in a new packet. + visitor_->OnStreamFrame(data1); + + // The stream should not be re-opened. + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); + EXPECT_TRUE(connection_->connected()); +} + +TEST_P(QuicServerSessionBaseTest, NeverOpenStreamDueToReset) { + // Send a reset (and expect the peer to send a RST in response). + QuicRstStreamFrame rst1(kInvalidControlFrameId, + GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM, 0); + EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); + if (transport_version() != QUIC_VERSION_99) { + // For non-version 99, the RESET_STREAM will do the full close. + // Set up expects accordingly. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(GetNthClientInitiatedBidirectionalId(0), + QUIC_RST_ACKNOWLEDGEMENT)); + } + visitor_->OnRstStream(rst1); + + // For version-99 will create and receive a stop-sending, completing + // the full-close expected by this test. + InjectStopSendingFrame(GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM); + + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); + + // Send two bytes of payload. + QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece("HT")); + visitor_->OnStreamFrame(data1); + + // The stream should never be opened, now that the reset is received. + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); + EXPECT_TRUE(connection_->connected()); +} + +TEST_P(QuicServerSessionBaseTest, AcceptClosedStream) { + // Send (empty) compressed headers followed by two bytes of data. + QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece("\1\0\0\0\0\0\0\0HT")); + QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0, + QuicStringPiece("\2\0\0\0\0\0\0\0HT")); + visitor_->OnStreamFrame(frame1); + visitor_->OnStreamFrame(frame2); + EXPECT_EQ(2u, session_->GetNumOpenIncomingStreams()); + + // Send a reset (and expect the peer to send a RST in response). + QuicRstStreamFrame rst(kInvalidControlFrameId, + GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM, 0); + EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); + if (transport_version() != QUIC_VERSION_99) { + // For non-version 99, the RESET_STREAM will do the full close. + // Set up expects accordingly. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(GetNthClientInitiatedBidirectionalId(0), + QUIC_RST_ACKNOWLEDGEMENT)); + } + visitor_->OnRstStream(rst); + + // For version-99 will create and receive a stop-sending, completing + // the full-close expected by this test. + InjectStopSendingFrame(GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM); + + // If we were tracking, we'd probably want to reject this because it's data + // past the reset point of stream 3. As it's a closed stream we just drop the + // data on the floor, but accept the packet because it has data for stream 5. + QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false, 2, + QuicStringPiece("TP")); + QuicStreamFrame frame4(GetNthClientInitiatedBidirectionalId(1), false, 2, + QuicStringPiece("TP")); + visitor_->OnStreamFrame(frame3); + visitor_->OnStreamFrame(frame4); + // The stream should never be opened, now that the reset is received. + EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams()); + EXPECT_TRUE(connection_->connected()); +} + +TEST_P(QuicServerSessionBaseTest, MaxOpenStreams) { + // Test that the server refuses if a client attempts to open too many data + // streams. For versions other than version 99, the server accepts slightly + // more than the negotiated stream limit to deal with rare cases where a + // client FIN/RST is lost. + + session_->OnConfigNegotiated(); + if (transport_version() != QUIC_VERSION_99) { + // The slightly increased stream limit is set during config negotiation. It + // is either an increase of 10 over negotiated limit, or a fixed percentage + // scaling, whichever is larger. Test both before continuing. + EXPECT_LT(kMaxStreamsMultiplier * kMaxStreamsForTest, + kMaxStreamsForTest + kMaxStreamsMinimumIncrement); + EXPECT_EQ(kMaxStreamsForTest + kMaxStreamsMinimumIncrement, + session_->max_open_incoming_bidirectional_streams()); + } + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); + QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); + // Open the max configured number of streams, should be no problem. + for (size_t i = 0; i < kMaxStreamsForTest; ++i) { + EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), stream_id)); + stream_id += QuicUtils::StreamIdDelta(connection_->transport_version()); + } + + if (transport_version() != QUIC_VERSION_99) { + // Open more streams: server should accept slightly more than the limit. + // Excess streams are for non-version-99 only. + for (size_t i = 0; i < kMaxStreamsMinimumIncrement; ++i) { + EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), stream_id)); + stream_id += QuicUtils::StreamIdDelta(connection_->transport_version()); + } + } + // Now violate the server's internal stream limit. + stream_id += QuicUtils::StreamIdDelta(connection_->transport_version()); + + if (transport_version() != QUIC_VERSION_99) { + // For non-version 99, QUIC responds to an attempt to exceed the stream + // limit by resetting the stream. + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream_id, QUIC_REFUSED_STREAM)); + } else { + // In version 99 QUIC responds to an attempt to exceed the stream limit by + // closing the connection. + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); + } + // Even if the connection remains open, the stream creation should fail. + EXPECT_FALSE(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), stream_id)); +} + +TEST_P(QuicServerSessionBaseTest, MaxAvailableBidirectionalStreams) { + // Test that the server closes the connection if a client makes too many data + // streams available. The server accepts slightly more than the negotiated + // stream limit to deal with rare cases where a client FIN/RST is lost. + + session_->OnConfigNegotiated(); + const size_t kAvailableStreamLimit = + session_->MaxAvailableBidirectionalStreams(); + + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); + EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), GetNthClientInitiatedBidirectionalId(0))); + + // Establish available streams up to the server's limit. + QuicStreamId next_id = + QuicUtils::StreamIdDelta(connection_->transport_version()); + const int kLimitingStreamId = + GetNthClientInitiatedBidirectionalId(kAvailableStreamLimit + 1); + if (transport_version() != QUIC_VERSION_99) { + // This exceeds the stream limit. In versions other than 99 + // this is allowed. Version 99 hews to the IETF spec and does + // not allow it. + EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), kLimitingStreamId)); + // A further available stream will result in connection close. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _)); + } else { + // A further available stream will result in connection close. + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + } + + // This forces stream kLimitingStreamId + 2 to become available, which + // violates the quota. + EXPECT_FALSE(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), kLimitingStreamId + 2 * next_id)); +} + +TEST_P(QuicServerSessionBaseTest, GetEvenIncomingError) { + // Incoming streams on the server session must be odd. + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + EXPECT_EQ(nullptr, + QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), GetNthServerInitiatedUnidirectionalId(0))); +} + +TEST_P(QuicServerSessionBaseTest, GetStreamDisconnected) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (GetParam() != AllSupportedVersions()[0]) { + return; + } + + // Don't create new streams if the connection is disconnected. + QuicConnectionPeer::TearDownLocalConnectionState(connection_); + EXPECT_QUIC_BUG(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), GetNthClientInitiatedBidirectionalId(0)), + "ShouldCreateIncomingStream called when disconnected"); +} + +class MockQuicCryptoServerStream : public QuicCryptoServerStream { + public: + explicit MockQuicCryptoServerStream( + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + QuicServerSessionBase* session, + QuicCryptoServerStream::Helper* helper) + : QuicCryptoServerStream( + crypto_config, + compressed_certs_cache, + GetQuicReloadableFlag(enable_quic_stateless_reject_support), + session, + helper) {} + MockQuicCryptoServerStream(const MockQuicCryptoServerStream&) = delete; + MockQuicCryptoServerStream& operator=(const MockQuicCryptoServerStream&) = + delete; + ~MockQuicCryptoServerStream() override {} + + MOCK_METHOD1(SendServerConfigUpdate, + void(const CachedNetworkParameters* cached_network_parameters)); +}; + +TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { + // Test that bandwidth estimate updates are sent to the client, only when + // bandwidth resumption is enabled, the bandwidth estimate has changed + // sufficiently, enough time has passed, + // and we don't have any other data to write. + + // Client has sent kBWRE connection option to trigger bandwidth resumption. + QuicTagVector copt; + copt.push_back(kBWRE); + QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); + session_->OnConfigNegotiated(); + EXPECT_TRUE( + QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); + + int32_t bandwidth_estimate_kbytes_per_second = 123; + int32_t max_bandwidth_estimate_kbytes_per_second = 134; + int32_t max_bandwidth_estimate_timestamp = 1122334455; + const QuicString serving_region = "not a real region"; + session_->set_serving_region(serving_region); + + session_->UnregisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true); + QuicServerSessionBasePeer::SetCryptoStream(session_.get(), nullptr); + MockQuicCryptoServerStream* crypto_stream = + new MockQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_, + session_.get(), &stream_helper_); + QuicServerSessionBasePeer::SetCryptoStream(session_.get(), crypto_stream); + session_->RegisterStreamPriority( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), + /*is_static=*/true, QuicStream::kDefaultPriority); + + // Set some initial bandwidth values. + QuicSentPacketManager* sent_packet_manager = + QuicConnectionPeer::GetSentPacketManager(session_->connection()); + QuicSustainedBandwidthRecorder& bandwidth_recorder = + QuicSentPacketManagerPeer::GetBandwidthRecorder(sent_packet_manager); + // Seed an rtt measurement equal to the initial default rtt. + RttStats* rtt_stats = + const_cast<RttStats*>(sent_packet_manager->GetRttStats()); + rtt_stats->UpdateRtt(rtt_stats->initial_rtt(), QuicTime::Delta::Zero(), + QuicTime::Zero()); + QuicSustainedBandwidthRecorderPeer::SetBandwidthEstimate( + &bandwidth_recorder, bandwidth_estimate_kbytes_per_second); + QuicSustainedBandwidthRecorderPeer::SetMaxBandwidthEstimate( + &bandwidth_recorder, max_bandwidth_estimate_kbytes_per_second, + max_bandwidth_estimate_timestamp); + // Queue up some pending data. + session_->MarkConnectionLevelWriteBlocked(QuicUtils::GetCryptoStreamId( + session_->connection()->transport_version())); + EXPECT_TRUE(session_->HasDataToWrite()); + + // There will be no update sent yet - not enough time has passed. + QuicTime now = QuicTime::Zero(); + session_->OnCongestionWindowChange(now); + + // Bandwidth estimate has now changed sufficiently but not enough time has + // passed to send a Server Config Update. + bandwidth_estimate_kbytes_per_second = + bandwidth_estimate_kbytes_per_second * 1.6; + session_->OnCongestionWindowChange(now); + + // Bandwidth estimate has now changed sufficiently and enough time has passed, + // but not enough packets have been sent. + int64_t srtt_ms = + sent_packet_manager->GetRttStats()->smoothed_rtt().ToMilliseconds(); + now = now + QuicTime::Delta::FromMilliseconds( + kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms); + session_->OnCongestionWindowChange(now); + + // The connection no longer has pending data to be written. + session_->OnCanWrite(); + EXPECT_FALSE(session_->HasDataToWrite()); + session_->OnCongestionWindowChange(now); + + // Bandwidth estimate has now changed sufficiently, enough time has passed, + // and enough packets have been sent. + SerializedPacket packet( + QuicPacketNumber(1) + kMinPacketsBetweenServerConfigUpdates, + PACKET_4BYTE_PACKET_NUMBER, nullptr, 1000, false, false); + sent_packet_manager->OnPacketSent(&packet, QuicPacketNumber(), now, + NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA); + + // Verify that the proto has exactly the values we expect. + CachedNetworkParameters expected_network_params; + expected_network_params.set_bandwidth_estimate_bytes_per_second( + bandwidth_recorder.BandwidthEstimate().ToBytesPerSecond()); + expected_network_params.set_max_bandwidth_estimate_bytes_per_second( + bandwidth_recorder.MaxBandwidthEstimate().ToBytesPerSecond()); + expected_network_params.set_max_bandwidth_timestamp_seconds( + bandwidth_recorder.MaxBandwidthTimestamp()); + expected_network_params.set_min_rtt_ms(session_->connection() + ->sent_packet_manager() + .GetRttStats() + ->min_rtt() + .ToMilliseconds()); + expected_network_params.set_previous_connection_state( + CachedNetworkParameters::CONGESTION_AVOIDANCE); + expected_network_params.set_timestamp( + session_->connection()->clock()->WallNow().ToUNIXSeconds()); + expected_network_params.set_serving_region(serving_region); + + EXPECT_CALL(*crypto_stream, + SendServerConfigUpdate(EqualsProto(expected_network_params))) + .Times(1); + EXPECT_CALL(*connection_, OnSendConnectionState(_)).Times(1); + session_->OnCongestionWindowChange(now); +} + +TEST_P(QuicServerSessionBaseTest, BandwidthResumptionExperiment) { + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // This test relies on resumption, which is not currently supported by the + // TLS handshake. + // TODO(nharper): Add support for resumption to the TLS handshake. + return; + } + // Test that if a client provides a CachedNetworkParameters with the same + // serving region as the current server, and which was made within an hour of + // now, that this data is passed down to the send algorithm. + + // Client has sent kBWRE connection option to trigger bandwidth resumption. + QuicTagVector copt; + copt.push_back(kBWRE); + QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); + + const QuicString kTestServingRegion = "a serving region"; + session_->set_serving_region(kTestServingRegion); + + // Set the time to be one hour + one second from the 0 baseline. + connection_->AdvanceTime( + QuicTime::Delta::FromSeconds(kNumSecondsPerHour + 1)); + + QuicCryptoServerStream* crypto_stream = static_cast<QuicCryptoServerStream*>( + QuicSessionPeer::GetMutableCryptoStream(session_.get())); + + // No effect if no CachedNetworkParameters provided. + EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0); + session_->OnConfigNegotiated(); + + // No effect if CachedNetworkParameters provided, but different serving + // regions. + CachedNetworkParameters cached_network_params; + cached_network_params.set_bandwidth_estimate_bytes_per_second(1); + cached_network_params.set_serving_region("different serving region"); + crypto_stream->SetPreviousCachedNetworkParams(cached_network_params); + EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0); + session_->OnConfigNegotiated(); + + // Same serving region, but timestamp is too old, should have no effect. + cached_network_params.set_serving_region(kTestServingRegion); + cached_network_params.set_timestamp(0); + crypto_stream->SetPreviousCachedNetworkParams(cached_network_params); + EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0); + session_->OnConfigNegotiated(); + + // Same serving region, and timestamp is recent: estimate is stored. + cached_network_params.set_timestamp( + connection_->clock()->WallNow().ToUNIXSeconds()); + crypto_stream->SetPreviousCachedNetworkParams(cached_network_params); + EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(1); + session_->OnConfigNegotiated(); +} + +TEST_P(QuicServerSessionBaseTest, BandwidthMaxEnablesResumption) { + EXPECT_FALSE( + QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); + + // Client has sent kBWMX connection option to trigger bandwidth resumption. + QuicTagVector copt; + copt.push_back(kBWMX); + QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); + session_->OnConfigNegotiated(); + EXPECT_TRUE( + QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); +} + +TEST_P(QuicServerSessionBaseTest, NoBandwidthResumptionByDefault) { + EXPECT_FALSE( + QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); + session_->OnConfigNegotiated(); + EXPECT_FALSE( + QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); +} + +// Tests which check the lifetime management of data members of +// QuicCryptoServerStream objects when async GetProof is in use. +class StreamMemberLifetimeTest : public QuicServerSessionBaseTest { + public: + StreamMemberLifetimeTest() + : QuicServerSessionBaseTest( + std::unique_ptr<FakeProofSource>(new FakeProofSource())), + crypto_config_peer_(&crypto_config_) { + GetFakeProofSource()->Activate(); + } + + FakeProofSource* GetFakeProofSource() const { + return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource()); + } + + private: + QuicCryptoServerConfigPeer crypto_config_peer_; +}; + +INSTANTIATE_TEST_SUITE_P(StreamMemberLifetimeTests, + StreamMemberLifetimeTest, + ::testing::ValuesIn(AllSupportedVersions())); + +// Trigger an operation which causes an async invocation of +// ProofSource::GetProof. Delay the completion of the operation until after the +// stream has been destroyed, and verify that there are no memory bugs. +TEST_P(StreamMemberLifetimeTest, Basic) { + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // This test depends on the QUIC crypto protocol, so it is disabled for the + // TLS handshake. + // TODO(nharper): Fix this test so it doesn't rely on QUIC crypto. + return; + } + SetQuicReloadableFlag(enable_quic_stateless_reject_support, true); + SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true); + + const QuicClock* clock = helper_.GetClock(); + CryptoHandshakeMessage chlo = crypto_test_utils::GenerateDefaultInchoateCHLO( + clock, GetParam().transport_version, &crypto_config_); + chlo.SetVector(kCOPT, QuicTagVector{kSREJ}); + std::vector<ParsedQuicVersion> packet_version_list = {GetParam()}; + std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + TestConnectionId(1), EmptyQuicConnectionId(), true, false, 1, + QuicString(chlo.GetSerialized().AsStringPiece()), CONNECTION_ID_PRESENT, + CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &packet_version_list)); + + EXPECT_CALL(stream_helper_, CanAcceptClientHello(_, _, _, _, _)) + .WillOnce(testing::Return(true)); + EXPECT_CALL(stream_helper_, GenerateConnectionIdForReject(_, _)) + .WillOnce(testing::Return(TestConnectionId(12345))); + + // Set the current packet + QuicConnectionPeer::SetCurrentPacket(session_->connection(), + packet->AsStringPiece()); + + // Yes, this is horrible. But it's the easiest way to trigger the behavior we + // need to exercise. + QuicCryptoServerStreamBase* crypto_stream = + const_cast<QuicCryptoServerStreamBase*>(session_->crypto_stream()); + + // Feed the CHLO into the crypto stream, which will trigger a call to + // ProofSource::GetProof + crypto_test_utils::SendHandshakeMessageToStream(crypto_stream, chlo, + Perspective::IS_CLIENT); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Destroy the stream + session_.reset(); + + // Allow the async ProofSource::GetProof call to complete. Verify (under + // memory access checkers) that this does not result in accesses to any + // freed memory from the session or its subobjects. + GetFakeProofSource()->InvokePendingCallback(0); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_spdy_client_session.cc b/quic/core/http/quic_spdy_client_session.cc new file mode 100644 index 0000000..9e8170b --- /dev/null +++ b/quic/core/http/quic_spdy_client_session.cc
@@ -0,0 +1,182 @@ +// Copyright (c) 2012 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/http/quic_spdy_client_session.h" + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicSpdyClientSession::QuicSpdyClientSession( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index) + : QuicSpdyClientSessionBase(connection, + push_promise_index, + config, + supported_versions), + server_id_(server_id), + crypto_config_(crypto_config), + respect_goaway_(true) {} + +QuicSpdyClientSession::~QuicSpdyClientSession() = default; + +void QuicSpdyClientSession::Initialize() { + crypto_stream_ = CreateQuicCryptoStream(); + QuicSpdyClientSessionBase::Initialize(); +} + +void QuicSpdyClientSession::OnProofValid( + const QuicCryptoClientConfig::CachedState& /*cached*/) {} + +void QuicSpdyClientSession::OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& /*verify_details*/) {} + +bool QuicSpdyClientSession::ShouldCreateOutgoingBidirectionalStream() { + if (!crypto_stream_->encryption_established()) { + QUIC_DLOG(INFO) << "Encryption not active so no outgoing stream created."; + return false; + } + if (!GetQuicReloadableFlag(quic_use_common_stream_check) && + connection()->transport_version() != QUIC_VERSION_99) { + if (GetNumOpenOutgoingStreams() >= + stream_id_manager().max_open_outgoing_streams()) { + QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " + << "Already " << GetNumOpenOutgoingStreams() << " open."; + return false; + } + if (goaway_received() && respect_goaway_) { + QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " + << "Already received goaway."; + return false; + } + return true; + } + if (goaway_received() && respect_goaway_) { + QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " + << "Already received goaway."; + return false; + } + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_common_stream_check, 1, 2); + return CanOpenNextOutgoingBidirectionalStream(); +} + +bool QuicSpdyClientSession::ShouldCreateOutgoingUnidirectionalStream() { + QUIC_BUG << "Try to create outgoing unidirectional client data streams"; + return false; +} + +QuicSpdyClientStream* +QuicSpdyClientSession::CreateOutgoingBidirectionalStream() { + if (!ShouldCreateOutgoingBidirectionalStream()) { + return nullptr; + } + std::unique_ptr<QuicSpdyClientStream> stream = CreateClientStream(); + QuicSpdyClientStream* stream_ptr = stream.get(); + ActivateStream(std::move(stream)); + return stream_ptr; +} + +QuicSpdyClientStream* +QuicSpdyClientSession::CreateOutgoingUnidirectionalStream() { + QUIC_BUG << "Try to create outgoing unidirectional client data streams"; + return nullptr; +} + +std::unique_ptr<QuicSpdyClientStream> +QuicSpdyClientSession::CreateClientStream() { + return QuicMakeUnique<QuicSpdyClientStream>( + GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL); +} + +QuicCryptoClientStreamBase* QuicSpdyClientSession::GetMutableCryptoStream() { + return crypto_stream_.get(); +} + +const QuicCryptoClientStreamBase* QuicSpdyClientSession::GetCryptoStream() + const { + return crypto_stream_.get(); +} + +void QuicSpdyClientSession::CryptoConnect() { + DCHECK(flow_controller()); + crypto_stream_->CryptoConnect(); +} + +int QuicSpdyClientSession::GetNumSentClientHellos() const { + return crypto_stream_->num_sent_client_hellos(); +} + +int QuicSpdyClientSession::GetNumReceivedServerConfigUpdates() const { + return crypto_stream_->num_scup_messages_received(); +} + +bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) { + if (!connection()->connected()) { + QUIC_BUG << "ShouldCreateIncomingStream called when disconnected"; + return false; + } + if (goaway_received() && respect_goaway_) { + QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " + << "Already received goaway."; + return false; + } + if (QuicUtils::IsClientInitiatedStreamId(connection()->transport_version(), + id) || + (connection()->transport_version() == QUIC_VERSION_99 && + QuicUtils::IsBidirectionalStreamId(id))) { + QUIC_LOG(WARNING) << "Received invalid push stream id " << id; + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, + "Server created non write unidirectional stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + return true; +} + +QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream( + PendingStream pending) { + QuicSpdyStream* stream = + new QuicSpdyClientStream(std::move(pending), this, READ_UNIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; +} + +QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream(QuicStreamId id) { + if (!ShouldCreateIncomingStream(id)) { + return nullptr; + } + QuicSpdyStream* stream = + new QuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; +} + +std::unique_ptr<QuicCryptoClientStreamBase> +QuicSpdyClientSession::CreateQuicCryptoStream() { + return QuicMakeUnique<QuicCryptoClientStream>( + server_id_, this, + crypto_config_->proof_verifier()->CreateDefaultContext(), crypto_config_, + this); +} + +bool QuicSpdyClientSession::IsAuthorized(const QuicString& authority) { + return true; +} + +} // namespace quic
diff --git a/quic/core/http/quic_spdy_client_session.h b/quic/core/http/quic_spdy_client_session.h new file mode 100644 index 0000000..c92753b --- /dev/null +++ b/quic/core/http/quic_spdy_client_session.h
@@ -0,0 +1,104 @@ +// Copyright (c) 2012 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. + +// A client specific QuicSession subclass. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_H_ + +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class QuicConnection; +class QuicServerId; + +class QuicSpdyClientSession : public QuicSpdyClientSessionBase { + public: + // Takes ownership of |connection|. Caller retains ownership of + // |promised_by_url|. + QuicSpdyClientSession(const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index); + QuicSpdyClientSession(const QuicSpdyClientSession&) = delete; + QuicSpdyClientSession& operator=(const QuicSpdyClientSession&) = delete; + ~QuicSpdyClientSession() override; + // Set up the QuicSpdyClientSession. Must be called prior to use. + void Initialize() override; + + // QuicSession methods: + QuicSpdyClientStream* CreateOutgoingBidirectionalStream() override; + QuicSpdyClientStream* CreateOutgoingUnidirectionalStream() override; + QuicCryptoClientStreamBase* GetMutableCryptoStream() override; + const QuicCryptoClientStreamBase* GetCryptoStream() const override; + + bool IsAuthorized(const QuicString& authority) override; + + // QuicSpdyClientSessionBase methods: + void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override; + void OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) override; + + // Performs a crypto handshake with the server. + virtual void CryptoConnect(); + + // Returns the number of client hello messages that have been sent on the + // crypto stream. If the handshake has completed then this is one greater + // than the number of round-trips needed for the handshake. + int GetNumSentClientHellos() const; + + int GetNumReceivedServerConfigUpdates() const; + + void set_respect_goaway(bool respect_goaway) { + respect_goaway_ = respect_goaway; + } + + protected: + // QuicSession methods: + QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override; + QuicSpdyStream* CreateIncomingStream(PendingStream pending) override; + // If an outgoing stream can be created, return true. + bool ShouldCreateOutgoingBidirectionalStream() override; + bool ShouldCreateOutgoingUnidirectionalStream() override; + + // If an incoming stream can be created, return true. + // TODO(fayang): move this up to QuicSpdyClientSessionBase. + bool ShouldCreateIncomingStream(QuicStreamId id) override; + + // Create the crypto stream. Called by Initialize(). + virtual std::unique_ptr<QuicCryptoClientStreamBase> CreateQuicCryptoStream(); + + // Unlike CreateOutgoingBidirectionalStream, which applies a bunch of + // sanity checks, this simply returns a new QuicSpdyClientStream. This may be + // used by subclasses which want to use a subclass of QuicSpdyClientStream for + // streams but wish to use the sanity checks in + // CreateOutgoingBidirectionalStream. + virtual std::unique_ptr<QuicSpdyClientStream> CreateClientStream(); + + const QuicServerId& server_id() { return server_id_; } + QuicCryptoClientConfig* crypto_config() { return crypto_config_; } + + private: + std::unique_ptr<QuicCryptoClientStreamBase> crypto_stream_; + QuicServerId server_id_; + QuicCryptoClientConfig* crypto_config_; + + // If this is set to false, the client will ignore server GOAWAYs and allow + // the creation of streams regardless of the high chance they will fail. + bool respect_goaway_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_H_
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc new file mode 100644 index 0000000..5a8fe8d --- /dev/null +++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -0,0 +1,209 @@ +// Copyright 2014 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/http/quic_spdy_client_session_base.h" + +#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +using spdy::SpdyHeaderBlock; + +namespace quic { + +QuicSpdyClientSessionBase::QuicSpdyClientSessionBase( + QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions) + : QuicSpdySession(connection, nullptr, config, supported_versions), + push_promise_index_(push_promise_index), + largest_promised_stream_id_( + QuicUtils::GetInvalidStreamId(connection->transport_version())) {} + +QuicSpdyClientSessionBase::~QuicSpdyClientSessionBase() { + // all promised streams for this session + for (auto& it : promised_by_id_) { + QUIC_DVLOG(1) << "erase stream " << it.first << " url " << it.second->url(); + push_promise_index_->promised_by_url()->erase(it.second->url()); + } + delete connection(); +} + +void QuicSpdyClientSessionBase::OnConfigNegotiated() { + QuicSpdySession::OnConfigNegotiated(); +} + +void QuicSpdyClientSessionBase::OnCryptoHandshakeEvent( + CryptoHandshakeEvent event) { + QuicSpdySession::OnCryptoHandshakeEvent(event); +} + +void QuicSpdyClientSessionBase::OnInitialHeadersComplete( + QuicStreamId stream_id, + const SpdyHeaderBlock& response_headers) { + // Note that the strong ordering of the headers stream means that + // QuicSpdyClientStream::OnPromiseHeadersComplete must have already + // been called (on the associated stream) if this is a promised + // stream. However, this stream may not have existed at this time, + // hence the need to query the session. + QuicClientPromisedInfo* promised = GetPromisedById(stream_id); + if (!promised) + return; + + promised->OnResponseHeaders(response_headers); +} + +void QuicSpdyClientSessionBase::OnPromiseHeaderList( + QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len, + const QuicHeaderList& header_list) { + if (QuicContainsKey(static_streams(), stream_id)) { + connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + if (promised_stream_id != + QuicUtils::GetInvalidStreamId(connection()->transport_version()) && + largest_promised_stream_id_ != + QuicUtils::GetInvalidStreamId(connection()->transport_version()) && + promised_stream_id <= largest_promised_stream_id_) { + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, + "Received push stream id lesser or equal to the" + " last accepted before", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + if (!IsIncomingStream(promised_stream_id)) { + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Received push stream id for outgoing stream.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + largest_promised_stream_id_ = promised_stream_id; + + QuicSpdyStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnPromiseHeaderList(promised_stream_id, frame_len, header_list); +} + +bool QuicSpdyClientSessionBase::HandlePromised(QuicStreamId /* associated_id */, + QuicStreamId promised_id, + const SpdyHeaderBlock& headers) { + // Due to pathalogical packet re-ordering, it is possible that + // frames for the promised stream have already arrived, and the + // promised stream could be active or closed. + if (IsClosedStream(promised_id)) { + // There was a RST on the data stream already, perhaps + // QUIC_REFUSED_STREAM? + QUIC_DVLOG(1) << "Promise ignored for stream " << promised_id + << " that is already closed"; + return false; + } + + if (push_promise_index_->promised_by_url()->size() >= get_max_promises()) { + QUIC_DVLOG(1) << "Too many promises, rejecting promise for stream " + << promised_id; + ResetPromised(promised_id, QUIC_REFUSED_STREAM); + return false; + } + + const QuicString url = SpdyUtils::GetPromisedUrlFromHeaders(headers); + QuicClientPromisedInfo* old_promised = GetPromisedByUrl(url); + if (old_promised) { + QUIC_DVLOG(1) << "Promise for stream " << promised_id + << " is duplicate URL " << url + << " of previous promise for stream " << old_promised->id(); + ResetPromised(promised_id, QUIC_DUPLICATE_PROMISE_URL); + return false; + } + + if (GetPromisedById(promised_id)) { + // OnPromiseHeadersComplete() would have closed the connection if + // promised id is a duplicate. + QUIC_BUG << "Duplicate promise for id " << promised_id; + return false; + } + + QuicClientPromisedInfo* promised = + new QuicClientPromisedInfo(this, promised_id, url); + std::unique_ptr<QuicClientPromisedInfo> promised_owner(promised); + promised->Init(); + QUIC_DVLOG(1) << "stream " << promised_id << " emplace url " << url; + (*push_promise_index_->promised_by_url())[url] = promised; + promised_by_id_[promised_id] = std::move(promised_owner); + bool result = promised->OnPromiseHeaders(headers); + if (result) { + DCHECK(promised_by_id_.find(promised_id) != promised_by_id_.end()); + } + return result; +} + +QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedByUrl( + const QuicString& url) { + auto it = push_promise_index_->promised_by_url()->find(url); + if (it != push_promise_index_->promised_by_url()->end()) { + return it->second; + } + return nullptr; +} + +QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedById( + const QuicStreamId id) { + auto it = promised_by_id_.find(id); + if (it != promised_by_id_.end()) { + return it->second.get(); + } + return nullptr; +} + +QuicSpdyStream* QuicSpdyClientSessionBase::GetPromisedStream( + const QuicStreamId id) { + DynamicStreamMap::iterator it = dynamic_streams().find(id); + if (it != dynamic_streams().end()) { + return static_cast<QuicSpdyStream*>(it->second.get()); + } + return nullptr; +} + +void QuicSpdyClientSessionBase::DeletePromised( + QuicClientPromisedInfo* promised) { + push_promise_index_->promised_by_url()->erase(promised->url()); + // Since promised_by_id_ contains the unique_ptr, this will destroy + // promised. + promised_by_id_.erase(promised->id()); + headers_stream()->MaybeReleaseSequencerBuffer(); +} + +void QuicSpdyClientSessionBase::OnPushStreamTimedOut(QuicStreamId stream_id) {} + +void QuicSpdyClientSessionBase::ResetPromised( + QuicStreamId id, + QuicRstStreamErrorCode error_code) { + SendRstStream(id, error_code, 0); + if (!IsOpenStream(id)) { + MaybeIncreaseLargestPeerStreamId(id); + } +} + +void QuicSpdyClientSessionBase::CloseStreamInner(QuicStreamId stream_id, + bool locally_reset) { + QuicSpdySession::CloseStreamInner(stream_id, locally_reset); + headers_stream()->MaybeReleaseSequencerBuffer(); +} + +bool QuicSpdyClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() { + return num_active_requests() == 0 && promised_by_id_.empty(); +} + +} // namespace quic
diff --git a/quic/core/http/quic_spdy_client_session_base.h b/quic/core/http/quic_spdy_client_session_base.h new file mode 100644 index 0000000..f27708c --- /dev/null +++ b/quic/core/http/quic_spdy_client_session_base.h
@@ -0,0 +1,141 @@ +// Copyright 2014 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_HTTP_QUIC_SPDY_CLIENT_SESSION_BASE_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_BASE_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class QuicClientPromisedInfo; +class QuicClientPushPromiseIndex; +class QuicSpdyClientStream; + +// For client/http layer code. Lookup promised streams based on +// matching promised request url. The same map can be shared across +// multiple sessions, since cross-origin pushes are allowed (subject +// to authority constraints). Clients should use this map to enforce +// session affinity for requests corresponding to cross-origin push +// promised streams. +using QuicPromisedByUrlMap = + QuicUnorderedMap<QuicString, QuicClientPromisedInfo*>; + +// The maximum time a promises stream can be reserved without being +// claimed by a client request. +const int64_t kPushPromiseTimeoutSecs = 60; + +// Base class for all client-specific QuicSession subclasses. +class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase + : public QuicSpdySession, + public QuicCryptoClientStream::ProofHandler { + public: + // Takes ownership of |connection|. Caller retains ownership of + // |promised_by_url|. + QuicSpdyClientSessionBase(QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions); + QuicSpdyClientSessionBase(const QuicSpdyClientSessionBase&) = delete; + QuicSpdyClientSessionBase& operator=(const QuicSpdyClientSessionBase&) = + delete; + + ~QuicSpdyClientSessionBase() override; + + void OnConfigNegotiated() override; + + // Override base class to set FEC policy before any data is sent by client. + void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override; + + // Called by |headers_stream_| when push promise headers have been + // completely received. + void OnPromiseHeaderList(QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len, + const QuicHeaderList& header_list) override; + + // Called by |QuicSpdyClientStream| on receipt of response headers, + // needed to detect promised server push streams, as part of + // client-request to push-stream rendezvous. + void OnInitialHeadersComplete(QuicStreamId stream_id, + const spdy::SpdyHeaderBlock& response_headers); + + // Called by |QuicSpdyClientStream| on receipt of PUSH_PROMISE, does + // some session level validation and creates the + // |QuicClientPromisedInfo| inserting into maps by (promised) id and + // url. Returns true if a new push promise is accepted. Resets the promised + // stream and returns false otherwise. + virtual bool HandlePromised(QuicStreamId associated_id, + QuicStreamId promised_id, + const spdy::SpdyHeaderBlock& headers); + + // For cross-origin server push, this should verify the server is + // authoritative per [RFC2818], Section 3. Roughly, subjectAltName + // list in the certificate should contain a matching DNS name, or IP + // address. |hostname| is derived from the ":authority" header field of + // the PUSH_PROMISE frame, port if present there will be dropped. + virtual bool IsAuthorized(const QuicString& hostname) = 0; + + // Session retains ownership. + QuicClientPromisedInfo* GetPromisedByUrl(const QuicString& url); + // Session retains ownership. + QuicClientPromisedInfo* GetPromisedById(const QuicStreamId id); + + // + QuicSpdyStream* GetPromisedStream(const QuicStreamId id); + + // Removes |promised| from the maps by url. + void ErasePromisedByUrl(QuicClientPromisedInfo* promised); + + // Removes |promised| from the maps by url and id and destroys + // promised. + virtual void DeletePromised(QuicClientPromisedInfo* promised); + + virtual void OnPushStreamTimedOut(QuicStreamId stream_id); + + // Sends Rst for the stream, and makes sure that future calls to + // IsClosedStream(id) return true, which ensures that any subsequent + // frames related to this stream will be ignored (modulo flow + // control accounting). + void ResetPromised(QuicStreamId id, QuicRstStreamErrorCode error_code); + + // Release headers stream's sequencer buffer if it's empty. + void CloseStreamInner(QuicStreamId stream_id, bool locally_reset) override; + + // Returns true if there are no active requests and no promised streams. + bool ShouldReleaseHeadersStreamSequencerBuffer() override; + + size_t get_max_promises() const { + return max_open_incoming_unidirectional_streams() * + kMaxPromisedStreamsMultiplier; + } + + QuicClientPushPromiseIndex* push_promise_index() { + return push_promise_index_; + } + + private: + // For QuicSpdyClientStream to detect that a response corresponds to a + // promise. + using QuicPromisedByIdMap = + QuicUnorderedMap<QuicStreamId, std::unique_ptr<QuicClientPromisedInfo>>; + + // As per rfc7540, section 10.5: track promise streams in "reserved + // (remote)". The primary key is URL from the promise request + // headers. The promised stream id is a secondary key used to get + // promise info when the response headers of the promised stream + // arrive. + QuicClientPushPromiseIndex* push_promise_index_; + QuicPromisedByIdMap promised_by_id_; + QuicStreamId largest_promised_stream_id_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_BASE_H_
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc new file mode 100644 index 0000000..3a40043 --- /dev/null +++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -0,0 +1,788 @@ +// Copyright (c) 2012 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/http/quic_spdy_client_session.h" + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using spdy::SpdyHeaderBlock; +using testing::_; +using testing::AnyNumber; +using testing::Invoke; +using testing::Truly; + +namespace quic { +namespace test { +namespace { + +const char kServerHostname[] = "test.example.com"; +const uint16_t kPort = 443; + +class TestQuicSpdyClientSession : public QuicSpdyClientSession { + public: + explicit TestQuicSpdyClientSession( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index) + : QuicSpdyClientSession(config, + supported_versions, + connection, + server_id, + crypto_config, + push_promise_index) {} + + std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override { + return QuicMakeUnique<MockQuicSpdyClientStream>( + GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL); + } + + MockQuicSpdyClientStream* CreateIncomingStream(QuicStreamId id) override { + if (!ShouldCreateIncomingStream(id)) { + return nullptr; + } + MockQuicSpdyClientStream* stream = + new MockQuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } +}; + +class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> { + protected: + QuicSpdyClientSessionTest() + : crypto_config_(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()), + promised_stream_id_( + QuicUtils::GetInvalidStreamId(GetParam().transport_version)), + associated_stream_id_( + QuicUtils::GetInvalidStreamId(GetParam().transport_version)) { + Initialize(); + // Advance the time, because timers do not like uninitialized times. + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } + + ~QuicSpdyClientSessionTest() override { + // Session must be destroyed before promised_by_url_ + session_.reset(nullptr); + } + + void Initialize() { + session_.reset(); + connection_ = new PacketSavingConnection(&helper_, &alarm_factory_, + Perspective::IS_CLIENT, + SupportedVersions(GetParam())); + session_ = QuicMakeUnique<TestQuicSpdyClientSession>( + DefaultQuicConfig(), SupportedVersions(GetParam()), connection_, + QuicServerId(kServerHostname, kPort, false), &crypto_config_, + &push_promise_index_); + session_->Initialize(); + push_promise_[":path"] = "/bar"; + push_promise_[":authority"] = "www.google.com"; + push_promise_[":version"] = "HTTP/1.1"; + push_promise_[":method"] = "GET"; + push_promise_[":scheme"] = "https"; + promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_); + promised_stream_id_ = GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 0); + associated_stream_id_ = GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 0); + } + + // The function ensures that A) the max stream id frames get properly deleted + // (since the test uses a 'did we leak memory' check ... if we just lose the + // frame, the test fails) and B) returns true (instead of the default, false) + // which ensures that the rest of the system thinks that the frame actually + // was transmitted. + bool ClearMaxStreamIdControlFrame(const QuicFrame& frame) { + if (frame.type == MAX_STREAM_ID_FRAME) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + return false; + } + + public: + bool ClearStreamIdBlockedControlFrame(const QuicFrame& frame) { + if (frame.type == STREAM_ID_BLOCKED_FRAME) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + return false; + } + + protected: + void CompleteCryptoHandshake() { + CompleteCryptoHandshake(kDefaultMaxStreamsPerConnection); + } + + void CompleteCryptoHandshake(uint32_t server_max_incoming_streams) { + if (connection_->transport_version() == QUIC_VERSION_99) { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(testing::AnyNumber()) + .WillRepeatedly(Invoke( + this, &QuicSpdyClientSessionTest::ClearMaxStreamIdControlFrame)); + } + session_->CryptoConnect(); + QuicCryptoClientStream* stream = static_cast<QuicCryptoClientStream*>( + session_->GetMutableCryptoStream()); + crypto_test_utils::FakeServerOptions options; + QuicConfig config = DefaultQuicConfig(); + config.SetMaxIncomingDynamicStreamsToSend(server_max_incoming_streams); + crypto_test_utils::HandshakeWithFakeServer( + &config, &helper_, &alarm_factory_, connection_, stream, options); + } + + QuicCryptoClientConfig crypto_config_; + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + PacketSavingConnection* connection_; + std::unique_ptr<TestQuicSpdyClientSession> session_; + QuicClientPushPromiseIndex push_promise_index_; + SpdyHeaderBlock push_promise_; + QuicString promise_url_; + QuicStreamId promised_stream_id_; + QuicStreamId associated_stream_id_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicSpdyClientSessionTest, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicSpdyClientSessionTest, CryptoConnect) { + CompleteCryptoHandshake(); +} + +TEST_P(QuicSpdyClientSessionTest, NoEncryptionAfterInitialEncryption) { + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // This test relies on resumption and is QUIC crypto specific, so it is + // disabled for TLS. + // TODO(nharper): Add support for resumption to the TLS handshake, and fix + // this test to not rely on QUIC crypto. + return; + } + // Complete a handshake in order to prime the crypto config for 0-RTT. + CompleteCryptoHandshake(); + + // Now create a second session using the same crypto config. + Initialize(); + + EXPECT_CALL(*connection_, OnCanWrite()); + // Starting the handshake should move immediately to encryption + // established and will allow streams to be created. + session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + EXPECT_NE(QuicUtils::GetCryptoStreamId(connection_->transport_version()), + stream->id()); + + // Process an "inchoate" REJ from the server which will cause + // an inchoate CHLO to be sent and will leave the encryption level + // at NONE. + CryptoHandshakeMessage rej; + crypto_test_utils::FillInDummyReject(&rej, /* stateless */ false); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + crypto_test_utils::SendHandshakeMessageToStream( + session_->GetMutableCryptoStream(), rej, Perspective::IS_CLIENT); + EXPECT_FALSE(session_->IsEncryptionEstablished()); + EXPECT_EQ(ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetEncryptionLevel( + QuicConnectionPeer::GetPacketCreator(connection_))); + // Verify that no new streams may be created. + EXPECT_TRUE(session_->CreateOutgoingBidirectionalStream() == nullptr); + // Verify that no data may be send on existing streams. + char data[] = "hello world"; + QuicConsumedData consumed = session_->WritevData( + stream, stream->id(), QUIC_ARRAYSIZE(data), 0, NO_FIN); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(0u, consumed.bytes_consumed); +} + +TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) { + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // This test relies on the MIDS transport parameter, which is not yet + // supported in TLS 1.3. + // TODO(nharper): Add support for Transport Parameters in the TLS handshake. + return; + } + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AnyNumber()); + EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(AnyNumber()); + + const uint32_t kServerMaxIncomingStreams = 1; + CompleteCryptoHandshake(kServerMaxIncomingStreams); + + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_TRUE(stream); + EXPECT_FALSE(session_->CreateOutgoingBidirectionalStream()); + + // Close the stream, but without having received a FIN or a RST_STREAM + // or MAX_STREAM_ID (V99) and check that a new one can not be created. + session_->CloseStream(stream->id()); + EXPECT_EQ(1u, session_->GetNumOpenOutgoingStreams()); + + stream = session_->CreateOutgoingBidirectionalStream(); + EXPECT_FALSE(stream); +} + +TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithRst) { + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // This test relies on the MIDS transport parameter, which is not yet + // supported in TLS 1.3. + // TODO(nharper): Add support for Transport Parameters in the TLS handshake. + return; + } + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AnyNumber()); + EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(AnyNumber()); + + const uint32_t kServerMaxIncomingStreams = 1; + CompleteCryptoHandshake(kServerMaxIncomingStreams); + + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_NE(nullptr, stream); + EXPECT_EQ(nullptr, session_->CreateOutgoingBidirectionalStream()); + + // Close the stream and receive an RST frame to remove the unfinished stream + session_->CloseStream(stream->id()); + session_->OnRstStream(QuicRstStreamFrame(kInvalidControlFrameId, stream->id(), + QUIC_RST_ACKNOWLEDGEMENT, 0)); + // Check that a new one can be created. + EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams()); + if (GetParam().transport_version == QUIC_VERSION_99) { + // In V99 the stream limit increases only if we get a MAX_STREAM_ID + // frame; pretend we got one. + + // Note that this is to be the second stream created, but GetNth... starts + // numbering at 0 (the first stream is 0, second is 1...) + QuicMaxStreamIdFrame frame(0, GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 1)); + session_->OnMaxStreamIdFrame(frame); + } + stream = session_->CreateOutgoingBidirectionalStream(); + EXPECT_NE(nullptr, stream); +} + +TEST_P(QuicSpdyClientSessionTest, ResetAndTrailers) { + if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + // This test relies on the MIDS transport parameter, which is not yet + // supported in TLS 1.3. + // TODO(nharper): Add support for Transport Parameters in the TLS handshake. + return; + } + // Tests the situation in which the client sends a RST at the same time that + // the server sends trailing headers (trailers). Receipt of the trailers by + // the client should result in all outstanding stream state being tidied up + // (including flow control, and number of available outgoing streams). + const uint32_t kServerMaxIncomingStreams = 1; + CompleteCryptoHandshake(kServerMaxIncomingStreams); + + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_NE(nullptr, stream); + + if (GetParam().transport_version == QUIC_VERSION_99) { + // For v99, trying to open a stream and failing due to lack + // of stream ids will result in a STREAM_ID_BLOCKED. Make + // sure we get one. Also clear out the frame because if it's + // left sitting, the later SendRstStream will not actually + // transmit the RST_STREAM because the connection will be in write-blocked + // state. This means that the SendControlFrame that is expected w.r.t. the + // RST_STREAM, below, will not be satisfied. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke( + this, + &QuicSpdyClientSessionTest::ClearStreamIdBlockedControlFrame)); + } + + EXPECT_EQ(nullptr, session_->CreateOutgoingBidirectionalStream()); + + QuicStreamId stream_id = stream->id(); + + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1); + session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); + + // A new stream cannot be created as the reset stream still counts as an open + // outgoing stream until closed by the server. + EXPECT_EQ(1u, session_->GetNumOpenOutgoingStreams()); + stream = session_->CreateOutgoingBidirectionalStream(); + EXPECT_EQ(nullptr, stream); + + // The stream receives trailers with final byte offset: this is one of three + // ways that a peer can signal the end of a stream (the others being RST, + // stream data + FIN). + QuicHeaderList trailers; + trailers.OnHeaderBlockStart(); + trailers.OnHeader(kFinalOffsetHeaderKey, "0"); + trailers.OnHeaderBlockEnd(0, 0); + session_->OnStreamHeaderList(stream_id, /*fin=*/false, 0, trailers); + + // The stream is now complete from the client's perspective, and it should + // be able to create a new outgoing stream. + EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams()); + if (GetParam().transport_version == QUIC_VERSION_99) { + // Note that this is to be the second stream created, but GetNth... starts + // numbering at 0 (the first stream is 0, second is 1...) + QuicMaxStreamIdFrame frame(0, GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 1)); + session_->OnMaxStreamIdFrame(frame); + } + stream = session_->CreateOutgoingBidirectionalStream(); + EXPECT_NE(nullptr, stream); +} + +TEST_P(QuicSpdyClientSessionTest, ReceivedMalformedTrailersAfterSendingRst) { + // Tests the situation where the client has sent a RST to the server, and has + // received trailing headers with a malformed final byte offset value. + CompleteCryptoHandshake(); + + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_NE(nullptr, stream); + + // Send the RST, which results in the stream being closed locally (but some + // state remains while the client waits for a response from the server). + QuicStreamId stream_id = stream->id(); + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1); + session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); + + // The stream receives trailers with final byte offset, but the header value + // is non-numeric and should be treated as malformed. + QuicHeaderList trailers; + trailers.OnHeaderBlockStart(); + trailers.OnHeader(kFinalOffsetHeaderKey, "invalid non-numeric value"); + trailers.OnHeaderBlockEnd(0, 0); + + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); + session_->OnStreamHeaderList(stream_id, /*fin=*/false, 0, trailers); +} + +TEST_P(QuicSpdyClientSessionTest, OnStreamHeaderListWithStaticStream) { + // Test situation where OnStreamHeaderList is called by stream with static id. + CompleteCryptoHandshake(); + + QuicHeaderList trailers; + trailers.OnHeaderBlockStart(); + trailers.OnHeader(kFinalOffsetHeaderKey, "0"); + trailers.OnHeaderBlockEnd(0, 0); + + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); + session_->OnStreamHeaderList( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + /*fin=*/false, 0, trailers); +} + +TEST_P(QuicSpdyClientSessionTest, OnPromiseHeaderListWithStaticStream) { + // Test situation where OnPromiseHeaderList is called by stream with static + // id. + CompleteCryptoHandshake(); + + QuicHeaderList trailers; + trailers.OnHeaderBlockStart(); + trailers.OnHeader(kFinalOffsetHeaderKey, "0"); + trailers.OnHeaderBlockEnd(0, 0); + + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); + session_->OnPromiseHeaderList( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + promised_stream_id_, 0, trailers); +} + +TEST_P(QuicSpdyClientSessionTest, GoAwayReceived) { + CompleteCryptoHandshake(); + + // After receiving a GoAway, I should no longer be able to create outgoing + // streams. + session_->connection()->OnGoAwayFrame(QuicGoAwayFrame( + kInvalidControlFrameId, QUIC_PEER_GOING_AWAY, 1u, "Going away.")); + EXPECT_EQ(nullptr, session_->CreateOutgoingBidirectionalStream()); +} + +static bool CheckForDecryptionError(QuicFramer* framer) { + return framer->error() == QUIC_DECRYPTION_FAILURE; +} + +// Various sorts of invalid packets that should not cause a connection +// to be closed. +TEST_P(QuicSpdyClientSessionTest, InvalidPacketReceived) { + QuicSocketAddress server_address(TestPeerIPAddress(), kTestPort); + QuicSocketAddress client_address(TestPeerIPAddress(), kTestPort); + + EXPECT_CALL(*connection_, ProcessUdpPacket(server_address, client_address, _)) + .WillRepeatedly(Invoke(static_cast<MockQuicConnection*>(connection_), + &MockQuicConnection::ReallyProcessUdpPacket)); + EXPECT_CALL(*connection_, OnCanWrite()).Times(AnyNumber()); + EXPECT_CALL(*connection_, OnError(_)).Times(1); + + // Verify that empty packets don't close the connection. + QuicReceivedPacket zero_length_packet(nullptr, 0, QuicTime::Zero(), false); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + session_->ProcessUdpPacket(client_address, server_address, + zero_length_packet); + + // Verifiy that small, invalid packets don't close the connection. + char buf[2] = {0x00, 0x01}; + QuicConnectionId connection_id = session_->connection()->connection_id(); + QuicReceivedPacket valid_packet(buf, 2, QuicTime::Zero(), false); + // Close connection shouldn't be called. + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + if (connection_->transport_version() > QUIC_VERSION_44) { + // Illegal fixed bit value. + EXPECT_CALL(*connection_, OnError(_)).Times(1); + } + session_->ProcessUdpPacket(client_address, server_address, valid_packet); + + // Verify that a non-decryptable packet doesn't close the connection. + QuicFramerPeer::SetLastSerializedConnectionId( + QuicConnectionPeer::GetFramer(connection_), connection_id); + ParsedQuicVersionVector versions = SupportedVersions(GetParam()); + std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + connection_id, EmptyQuicConnectionId(), false, false, 100, "data", + CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, + &versions, Perspective::IS_SERVER)); + std::unique_ptr<QuicReceivedPacket> received( + ConstructReceivedPacket(*packet, QuicTime::Zero())); + // Change the last byte of the encrypted data. + *(const_cast<char*>(received->data() + received->length() - 1)) += 1; + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*connection_, OnError(Truly(CheckForDecryptionError))).Times(1); + session_->ProcessUdpPacket(client_address, server_address, *received); +} + +// A packet with invalid framing should cause a connection to be closed. +TEST_P(QuicSpdyClientSessionTest, InvalidFramedPacketReceived) { + QuicSocketAddress server_address(TestPeerIPAddress(), kTestPort); + QuicSocketAddress client_address(TestPeerIPAddress(), kTestPort); + + EXPECT_CALL(*connection_, ProcessUdpPacket(server_address, client_address, _)) + .WillRepeatedly(Invoke(static_cast<MockQuicConnection*>(connection_), + &MockQuicConnection::ReallyProcessUdpPacket)); + EXPECT_CALL(*connection_, OnError(_)).Times(1); + + // Verify that a decryptable packet with bad frames does close the connection. + QuicConnectionId connection_id = session_->connection()->connection_id(); + QuicFramerPeer::SetLastSerializedConnectionId( + QuicConnectionPeer::GetFramer(connection_), connection_id); + ParsedQuicVersionVector versions = {GetParam()}; + std::unique_ptr<QuicEncryptedPacket> packet(ConstructMisFramedEncryptedPacket( + connection_id, EmptyQuicConnectionId(), false, false, 100, "data", + CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, + &versions, Perspective::IS_SERVER)); + std::unique_ptr<QuicReceivedPacket> received( + ConstructReceivedPacket(*packet, QuicTime::Zero())); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); + session_->ProcessUdpPacket(client_address, server_address, *received); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeaders) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>( + session_->CreateOutgoingBidirectionalStream()); + + EXPECT_CALL(*stream, OnPromiseHeaderList(_, _, _)); + session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0, + QuicHeaderList()); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeadersAlreadyClosed) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + session_->CreateOutgoingBidirectionalStream(); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(associated_stream_id_, QUIC_REFUSED_STREAM)); + session_->ResetPromised(associated_stream_id_, QUIC_REFUSED_STREAM); + + session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0, + QuicHeaderList()); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseOutOfOrder) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>( + session_->CreateOutgoingBidirectionalStream()); + + EXPECT_CALL(*stream, OnPromiseHeaderList(promised_stream_id_, _, _)); + session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0, + QuicHeaderList()); + associated_stream_id_ += + QuicUtils::StreamIdDelta(connection_->transport_version()); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Received push stream id lesser or equal to the" + " last accepted before", + _)); + session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0, + QuicHeaderList()); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseOutgoingStreamId) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>( + session_->CreateOutgoingBidirectionalStream()); + + // Promise an illegal (outgoing) stream id. + promised_stream_id_ = GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 0); + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Received push stream id for outgoing stream.", _)); + + session_->OnPromiseHeaderList(stream->id(), promised_stream_id_, 0, + QuicHeaderList()); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseHandlePromise) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + session_->CreateOutgoingBidirectionalStream(); + + EXPECT_TRUE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + + EXPECT_NE(session_->GetPromisedById(promised_stream_id_), nullptr); + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseAlreadyClosed) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + session_->CreateOutgoingBidirectionalStream(); + session_->GetOrCreateStream(promised_stream_id_); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promised_stream_id_, QUIC_REFUSED_STREAM)); + + session_->ResetPromised(promised_stream_id_, QUIC_REFUSED_STREAM); + SpdyHeaderBlock promise_headers; + EXPECT_FALSE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, promise_headers)); + + // Verify that the promise was not created. + EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr); + EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseDuplicateUrl) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + session_->CreateOutgoingBidirectionalStream(); + + EXPECT_TRUE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + + EXPECT_NE(session_->GetPromisedById(promised_stream_id_), nullptr); + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr); + + promised_stream_id_ += + QuicUtils::StreamIdDelta(connection_->transport_version()); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promised_stream_id_, QUIC_DUPLICATE_PROMISE_URL)); + + EXPECT_FALSE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + + // Verify that the promise was not created. + EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, ReceivingPromiseEnhanceYourCalm) { + for (size_t i = 0u; i < session_->get_max_promises(); i++) { + push_promise_[":path"] = QuicStringPrintf("/bar%zu", i); + + QuicStreamId id = + promised_stream_id_ + + i * QuicUtils::StreamIdDelta(connection_->transport_version()); + + EXPECT_TRUE( + session_->HandlePromised(associated_stream_id_, id, push_promise_)); + + // Verify that the promise is in the unclaimed streams map. + QuicString promise_url(SpdyUtils::GetPromisedUrlFromHeaders(push_promise_)); + EXPECT_NE(session_->GetPromisedByUrl(promise_url), nullptr); + EXPECT_NE(session_->GetPromisedById(id), nullptr); + } + + // One more promise, this should be refused. + int i = session_->get_max_promises(); + push_promise_[":path"] = QuicStringPrintf("/bar%d", i); + + QuicStreamId id = + promised_stream_id_ + + i * QuicUtils::StreamIdDelta(connection_->transport_version()); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(id, QUIC_REFUSED_STREAM)); + EXPECT_FALSE( + session_->HandlePromised(associated_stream_id_, id, push_promise_)); + + // Verify that the promise was not created. + QuicString promise_url(SpdyUtils::GetPromisedUrlFromHeaders(push_promise_)); + EXPECT_EQ(session_->GetPromisedById(id), nullptr); + EXPECT_EQ(session_->GetPromisedByUrl(promise_url), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, IsClosedTrueAfterResetPromisedAlreadyOpen) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + session_->GetOrCreateStream(promised_stream_id_); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promised_stream_id_, QUIC_REFUSED_STREAM)); + session_->ResetPromised(promised_stream_id_, QUIC_REFUSED_STREAM); + EXPECT_TRUE(session_->IsClosedStream(promised_stream_id_)); +} + +TEST_P(QuicSpdyClientSessionTest, IsClosedTrueAfterResetPromisedNonexistant) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promised_stream_id_, QUIC_REFUSED_STREAM)); + session_->ResetPromised(promised_stream_id_, QUIC_REFUSED_STREAM); + EXPECT_TRUE(session_->IsClosedStream(promised_stream_id_)); +} + +TEST_P(QuicSpdyClientSessionTest, OnInitialHeadersCompleteIsPush) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + session_->GetOrCreateStream(promised_stream_id_); + EXPECT_TRUE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + EXPECT_NE(session_->GetPromisedById(promised_stream_id_), nullptr); + EXPECT_NE(session_->GetPromisedStream(promised_stream_id_), nullptr); + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr); + + session_->OnInitialHeadersComplete(promised_stream_id_, SpdyHeaderBlock()); +} + +TEST_P(QuicSpdyClientSessionTest, OnInitialHeadersCompleteIsNotPush) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + session_->CreateOutgoingBidirectionalStream(); + session_->OnInitialHeadersComplete(promised_stream_id_, SpdyHeaderBlock()); +} + +TEST_P(QuicSpdyClientSessionTest, DeletePromised) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + session_->GetOrCreateStream(promised_stream_id_); + EXPECT_TRUE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + QuicClientPromisedInfo* promised = + session_->GetPromisedById(promised_stream_id_); + EXPECT_NE(promised, nullptr); + EXPECT_NE(session_->GetPromisedStream(promised_stream_id_), nullptr); + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr); + + session_->DeletePromised(promised); + EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr); + EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, ResetPromised) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + session_->GetOrCreateStream(promised_stream_id_); + EXPECT_TRUE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY)); + session_->SendRstStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY, 0); + QuicClientPromisedInfo* promised = + session_->GetPromisedById(promised_stream_id_); + EXPECT_NE(promised, nullptr); + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr); + EXPECT_EQ(session_->GetPromisedStream(promised_stream_id_), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseInvalidMethod) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + session_->CreateOutgoingBidirectionalStream(); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promised_stream_id_, QUIC_INVALID_PROMISE_METHOD)); + + push_promise_[":method"] = "POST"; + EXPECT_FALSE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + + EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr); + EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, PushPromiseInvalidHost) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + + session_->CreateOutgoingBidirectionalStream(); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(promised_stream_id_, QUIC_INVALID_PROMISE_URL)); + + push_promise_[":authority"] = ""; + EXPECT_FALSE(session_->HandlePromised(associated_stream_id_, + promised_stream_id_, push_promise_)); + + EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr); + EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr); +} + +TEST_P(QuicSpdyClientSessionTest, + TryToCreateServerInitiatedBidirectionalStream) { + if (connection_->transport_version() == QUIC_VERSION_99) { + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + } else { + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + } + session_->GetOrCreateStream(GetNthServerInitiatedBidirectionalStreamId( + connection_->transport_version(), 0)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_spdy_client_stream.cc b/quic/core/http/quic_spdy_client_stream.cc new file mode 100644 index 0000000..d4e8a69 --- /dev/null +++ b/quic/core/http/quic_spdy_client_stream.cc
@@ -0,0 +1,160 @@ +// Copyright (c) 2012 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/http/quic_spdy_client_stream.h" + +#include <utility> + +#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" + +using spdy::SpdyHeaderBlock; + +namespace quic { + +QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id, + QuicSpdyClientSession* session, + StreamType type) + : QuicSpdyStream(id, session, type), + content_length_(-1), + response_code_(0), + header_bytes_read_(0), + header_bytes_written_(0), + session_(session), + has_preliminary_headers_(false) {} + +QuicSpdyClientStream::QuicSpdyClientStream(PendingStream pending, + QuicSpdyClientSession* session, + StreamType type) + : QuicSpdyStream(std::move(pending), session, type), + content_length_(-1), + response_code_(0), + header_bytes_read_(0), + header_bytes_written_(0), + session_(session), + has_preliminary_headers_(false) {} + +QuicSpdyClientStream::~QuicSpdyClientStream() = default; + +void QuicSpdyClientStream::OnInitialHeadersComplete( + bool fin, + size_t frame_len, + const QuicHeaderList& header_list) { + QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list); + + DCHECK(headers_decompressed()); + header_bytes_read_ += frame_len; + if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_, + &response_headers_)) { + QUIC_DLOG(ERROR) << "Failed to parse header list: " + << header_list.DebugString(); + Reset(QUIC_BAD_APPLICATION_PAYLOAD); + return; + } + + if (!ParseHeaderStatusCode(response_headers_, &response_code_)) { + QUIC_DLOG(ERROR) << "Received invalid response code: " + << response_headers_[":status"].as_string(); + Reset(QUIC_BAD_APPLICATION_PAYLOAD); + return; + } + + if (response_code_ == 100 && !has_preliminary_headers_) { + // These are preliminary 100 Continue headers, not the actual response + // headers. + set_headers_decompressed(false); + has_preliminary_headers_ = true; + preliminary_headers_ = std::move(response_headers_); + } + + ConsumeHeaderList(); + QUIC_DVLOG(1) << "headers complete for stream " << id(); + + session_->OnInitialHeadersComplete(id(), response_headers_); +} + +void QuicSpdyClientStream::OnTrailingHeadersComplete( + bool fin, + size_t frame_len, + const QuicHeaderList& header_list) { + QuicSpdyStream::OnTrailingHeadersComplete(fin, frame_len, header_list); + MarkTrailersConsumed(); +} + +void QuicSpdyClientStream::OnPromiseHeaderList( + QuicStreamId promised_id, + size_t frame_len, + const QuicHeaderList& header_list) { + header_bytes_read_ += frame_len; + int64_t content_length = -1; + SpdyHeaderBlock promise_headers; + if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length, + &promise_headers)) { + QUIC_DLOG(ERROR) << "Failed to parse promise headers: " + << header_list.DebugString(); + Reset(QUIC_BAD_APPLICATION_PAYLOAD); + return; + } + + session_->HandlePromised(id(), promised_id, promise_headers); + if (visitor() != nullptr) { + visitor()->OnPromiseHeadersComplete(promised_id, frame_len); + } +} + +void QuicSpdyClientStream::OnBodyAvailable() { + // For push streams, visitor will not be set until the rendezvous + // between server promise and client request is complete. + if (visitor() == nullptr) + return; + + while (HasBytesToRead()) { + struct iovec iov; + if (GetReadableRegions(&iov, 1) == 0) { + // No more data to read. + break; + } + QUIC_DVLOG(1) << "Client processed " << iov.iov_len << " bytes for stream " + << id(); + data_.append(static_cast<char*>(iov.iov_base), iov.iov_len); + + if (content_length_ >= 0 && + data_.size() > static_cast<uint64_t>(content_length_)) { + QUIC_DLOG(ERROR) << "Invalid content length (" << content_length_ + << ") with data of size " << data_.size(); + Reset(QUIC_BAD_APPLICATION_PAYLOAD); + return; + } + MarkConsumed(iov.iov_len); + } + if (sequencer()->IsClosed()) { + OnFinRead(); + } else { + sequencer()->SetUnblocked(); + } +} + +size_t QuicSpdyClientStream::SendRequest(SpdyHeaderBlock headers, + QuicStringPiece body, + bool fin) { + QuicConnection::ScopedPacketFlusher flusher( + session_->connection(), QuicConnection::SEND_ACK_IF_QUEUED); + bool send_fin_with_headers = fin && body.empty(); + size_t bytes_sent = body.size(); + header_bytes_written_ = + WriteHeaders(std::move(headers), send_fin_with_headers, nullptr); + bytes_sent += header_bytes_written_; + + if (!body.empty()) { + WriteOrBufferBody(body, fin); + } + + return bytes_sent; +} + +} // namespace quic
diff --git a/quic/core/http/quic_spdy_client_stream.h b/quic/core/http/quic_spdy_client_stream.h new file mode 100644 index 0000000..8a66740 --- /dev/null +++ b/quic/core/http/quic_spdy_client_stream.h
@@ -0,0 +1,101 @@ +// Copyright (c) 2012 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_HTTP_QUIC_SPDY_CLIENT_STREAM_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_STREAM_H_ + +#include <cstddef> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" + +namespace quic { + +class QuicSpdyClientSession; + +// All this does right now is send an SPDY request, and aggregate the +// SPDY response. +class QuicSpdyClientStream : public QuicSpdyStream { + public: + QuicSpdyClientStream(QuicStreamId id, + QuicSpdyClientSession* session, + StreamType type); + QuicSpdyClientStream(PendingStream pending, + QuicSpdyClientSession* spdy_session, + StreamType type); + QuicSpdyClientStream(const QuicSpdyClientStream&) = delete; + QuicSpdyClientStream& operator=(const QuicSpdyClientStream&) = delete; + ~QuicSpdyClientStream() override; + + // Override the base class to parse and store headers. + void OnInitialHeadersComplete(bool fin, + size_t frame_len, + const QuicHeaderList& header_list) override; + + // Override the base class to parse and store trailers. + void OnTrailingHeadersComplete(bool fin, + size_t frame_len, + const QuicHeaderList& header_list) override; + + // Override the base class to handle creation of the push stream. + void OnPromiseHeaderList(QuicStreamId promised_id, + size_t frame_len, + const QuicHeaderList& header_list) override; + + // QuicStream implementation called by the session when there's data for us. + void OnBodyAvailable() override; + + // Serializes the headers and body, sends it to the server, and + // returns the number of bytes sent. + size_t SendRequest(spdy::SpdyHeaderBlock headers, + QuicStringPiece body, + bool fin); + + // Returns the response data. + const QuicString& data() { return data_; } + + // Returns whatever headers have been received for this stream. + const spdy::SpdyHeaderBlock& response_headers() { return response_headers_; } + + const spdy::SpdyHeaderBlock& preliminary_headers() { + return preliminary_headers_; + } + + size_t header_bytes_read() const { return header_bytes_read_; } + + size_t header_bytes_written() const { return header_bytes_written_; } + + int response_code() const { return response_code_; } + + // While the server's SetPriority shouldn't be called externally, the creator + // of client-side streams should be able to set the priority. + using QuicSpdyStream::SetPriority; + + private: + // The parsed headers received from the server. + spdy::SpdyHeaderBlock response_headers_; + + // The parsed content-length, or -1 if none is specified. + int64_t content_length_; + int response_code_; + QuicString data_; + size_t header_bytes_read_; + size_t header_bytes_written_; + + QuicSpdyClientSession* session_; + + // These preliminary headers are used for the 100 Continue headers + // that may arrive before the response headers when the request has + // Expect: 100-continue. + bool has_preliminary_headers_; + spdy::SpdyHeaderBlock preliminary_headers_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_STREAM_H_
diff --git a/quic/core/http/quic_spdy_client_stream_test.cc b/quic/core/http/quic_spdy_client_stream_test.cc new file mode 100644 index 0000000..0b2c157 --- /dev/null +++ b/quic/core/http/quic_spdy_client_stream_test.cc
@@ -0,0 +1,233 @@ +// Copyright 2013 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/http/quic_spdy_client_stream.h" + +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using spdy::SpdyHeaderBlock; +using testing::_; +using testing::StrictMock; + +namespace quic { +namespace test { + +namespace { + +class MockQuicSpdyClientSession : public QuicSpdyClientSession { + public: + explicit MockQuicSpdyClientSession( + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index) + : QuicSpdyClientSession(DefaultQuicConfig(), + supported_versions, + connection, + QuicServerId("example.com", 443, false), + &crypto_config_, + push_promise_index), + crypto_config_(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()) {} + MockQuicSpdyClientSession(const MockQuicSpdyClientSession&) = delete; + MockQuicSpdyClientSession& operator=(const MockQuicSpdyClientSession&) = + delete; + ~MockQuicSpdyClientSession() override = default; + + MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id)); + + private: + QuicCryptoClientConfig crypto_config_; +}; + +class QuicSpdyClientStreamTest : public QuicTestWithParam<ParsedQuicVersion> { + public: + class StreamVisitor; + + QuicSpdyClientStreamTest() + : connection_( + new StrictMock<MockQuicConnection>(&helper_, + &alarm_factory_, + Perspective::IS_CLIENT, + SupportedVersions(GetParam()))), + session_(connection_->supported_versions(), + connection_, + &push_promise_index_), + body_("hello world") { + session_.Initialize(); + + headers_[":status"] = "200"; + headers_["content-length"] = "11"; + + stream_ = QuicMakeUnique<QuicSpdyClientStream>( + GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), 0), + &session_, BIDIRECTIONAL); + stream_visitor_ = QuicMakeUnique<StreamVisitor>(); + stream_->set_visitor(stream_visitor_.get()); + } + + class StreamVisitor : public QuicSpdyClientStream::Visitor { + void OnClose(QuicSpdyStream* stream) override { + QUIC_DVLOG(1) << "stream " << stream->id(); + } + }; + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + QuicClientPushPromiseIndex push_promise_index_; + + MockQuicSpdyClientSession session_; + std::unique_ptr<QuicSpdyClientStream> stream_; + std::unique_ptr<StreamVisitor> stream_visitor_; + SpdyHeaderBlock headers_; + QuicString body_; + HttpEncoder encoder_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicSpdyClientStreamTest, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicSpdyClientStreamTest, TestReceivingIllegalResponseStatusCode) { + headers_[":status"] = "200 ok"; + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD)); + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + EXPECT_EQ(QUIC_BAD_APPLICATION_PAYLOAD, stream_->stream_error()); +} + +TEST_P(QuicSpdyClientStreamTest, TestFraming) { + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body_.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + QuicString data = VersionHasDataFrameHeader(connection_->transport_version()) + ? header + body_ + : body_; + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data)); + EXPECT_EQ("200", stream_->response_headers().find(":status")->second); + EXPECT_EQ(200, stream_->response_code()); + EXPECT_EQ(body_, stream_->data()); +} + +TEST_P(QuicSpdyClientStreamTest, TestFraming100Continue) { + headers_[":status"] = "100"; + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_)); + EXPECT_EQ("100", stream_->preliminary_headers().find(":status")->second); + EXPECT_EQ(0u, stream_->response_headers().size()); + EXPECT_EQ(100, stream_->response_code()); + EXPECT_EQ("", stream_->data()); +} + +TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) { + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body_.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + QuicString data = VersionHasDataFrameHeader(connection_->transport_version()) + ? header + body_ + : body_; + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data)); + EXPECT_EQ("200", stream_->response_headers().find(":status")->second); + EXPECT_EQ(200, stream_->response_code()); + EXPECT_EQ(body_, stream_->data()); +} + +TEST_P(QuicSpdyClientStreamTest, + QUIC_TEST_DISABLED_IN_CHROME(TestFramingExtraData)) { + QuicString large_body = "hello world!!!!!!"; + + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + // The headers should parse successfully. + EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error()); + EXPECT_EQ("200", stream_->response_headers().find(":status")->second); + EXPECT_EQ(200, stream_->response_code()); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(large_body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + QuicString data = VersionHasDataFrameHeader(connection_->transport_version()) + ? header + large_body + : large_body; + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD)); + + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data)); + + EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error()); +} + +TEST_P(QuicSpdyClientStreamTest, ReceivingTrailers) { + // Test that receiving trailing headers, containing a final offset, results in + // the stream being closed at that byte offset. + + // Send headers as usual. + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + + // Send trailers before sending the body. Even though a FIN has been received + // the stream should not be closed, as it does not yet have all the data bytes + // promised by the final offset field. + SpdyHeaderBlock trailer_block; + trailer_block["trailer key"] = "trailer value"; + trailer_block[kFinalOffsetHeaderKey] = + QuicTextUtils::Uint64ToString(body_.size()); + auto trailers = AsHeaderList(trailer_block); + stream_->OnStreamHeaderList(true, trailers.uncompressed_header_bytes(), + trailers); + + // Now send the body, which should close the stream as the FIN has been + // received, as well as all data. + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body_.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + QuicString data = VersionHasDataFrameHeader(connection_->transport_version()) + ? header + body_ + : body_; + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data)); + EXPECT_TRUE(stream_->reading_stopped()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_spdy_server_stream_base.cc b/quic/core/http/quic_spdy_server_stream_base.cc new file mode 100644 index 0000000..cbe479e --- /dev/null +++ b/quic/core/http/quic_spdy_server_stream_base.cc
@@ -0,0 +1,48 @@ +// 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/http/quic_spdy_server_stream_base.h" + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(QuicStreamId id, + QuicSpdySession* session, + StreamType type) + : QuicSpdyStream(id, session, type) {} + +QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(PendingStream pending, + QuicSpdySession* session, + StreamType type) + : QuicSpdyStream(std::move(pending), session, type) {} + +void QuicSpdyServerStreamBase::CloseWriteSide() { + if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() && + !rst_sent()) { + // Early cancel the stream if it has stopped reading before receiving FIN + // or RST. + DCHECK(fin_sent() || !session()->connection()->connected()); + // Tell the peer to stop sending further data. + QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id(); + Reset(QUIC_STREAM_NO_ERROR); + } + + QuicSpdyStream::CloseWriteSide(); +} + +void QuicSpdyServerStreamBase::StopReading() { + if (!fin_received() && !rst_received() && write_side_closed() && + !rst_sent()) { + DCHECK(fin_sent()); + // Tell the peer to stop sending further data. + QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id(); + Reset(QUIC_STREAM_NO_ERROR); + } + QuicSpdyStream::StopReading(); +} + +} // namespace quic
diff --git a/quic/core/http/quic_spdy_server_stream_base.h b/quic/core/http/quic_spdy_server_stream_base.h new file mode 100644 index 0000000..438d152 --- /dev/null +++ b/quic/core/http/quic_spdy_server_stream_base.h
@@ -0,0 +1,31 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SERVER_STREAM_BASE_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SERVER_STREAM_BASE_H_ + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h" + +namespace quic { + +class QuicSpdyServerStreamBase : public QuicSpdyStream { + public: + QuicSpdyServerStreamBase(QuicStreamId id, + QuicSpdySession* session, + StreamType type); + QuicSpdyServerStreamBase(PendingStream pending, + QuicSpdySession* session, + StreamType type); + QuicSpdyServerStreamBase(const QuicSpdyServerStreamBase&) = delete; + QuicSpdyServerStreamBase& operator=(const QuicSpdyServerStreamBase&) = delete; + + // Override the base class to send QUIC_STREAM_NO_ERROR to the peer + // when the stream has not received all the data. + void CloseWriteSide() override; + void StopReading() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SERVER_STREAM_BASE_H_
diff --git a/quic/core/http/quic_spdy_server_stream_base_test.cc b/quic/core/http/quic_spdy_server_stream_base_test.cc new file mode 100644 index 0000000..4e1aa1b --- /dev/null +++ b/quic/core/http/quic_spdy_server_stream_base_test.cc
@@ -0,0 +1,94 @@ +// 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/http/quic_spdy_server_stream_base.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; + +namespace quic { +namespace test { +namespace { + +class TestQuicSpdyServerStream : public QuicSpdyServerStreamBase { + public: + TestQuicSpdyServerStream(QuicStreamId id, + QuicSpdySession* session, + StreamType type) + : QuicSpdyServerStreamBase(id, session, type) {} + + void OnBodyAvailable() override {} +}; + +class QuicSpdyServerStreamBaseTest : public QuicTest { + protected: + QuicSpdyServerStreamBaseTest() + : session_(new MockQuicConnection(&helper_, + &alarm_factory_, + Perspective::IS_SERVER)) { + stream_ = new TestQuicSpdyServerStream( + GetNthClientInitiatedBidirectionalStreamId( + session_.connection()->transport_version(), 0), + &session_, BIDIRECTIONAL); + session_.ActivateStream(QuicWrapUnique(stream_)); + helper_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } + + QuicSpdyServerStreamBase* stream_ = nullptr; + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicSpdySession session_; +}; + +TEST_F(QuicSpdyServerStreamBaseTest, + SendQuicRstStreamNoErrorWithEarlyResponse) { + stream_->StopReading(); + EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1); + stream_->set_fin_sent(true); + stream_->CloseWriteSide(); +} + +TEST_F(QuicSpdyServerStreamBaseTest, + DoNotSendQuicRstStreamNoErrorWithRstReceived) { + EXPECT_FALSE(stream_->reading_stopped()); + + EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0); + + if (session_.connection()->transport_version() != QUIC_VERSION_99) { + EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _)) + .Times(1); + } else { + // Intercept & check that the call to the QuicConnection's OnStreamReast + // has correct stream ID and error code -- for V99/IETF Quic, it should + // have the STREAM_CANCELLED error code, not RST_ACK... Capture + // OnStreamReset (rather than SendRstStream) because the V99 path bypasses + // SendRstStream, calling SendRstStreamInner directly. Mocking + // SendRstStreamInner is problematic since the test relies on it to perform + // the closing operations and getting the stream in the correct state. + EXPECT_CALL(*(static_cast<MockQuicConnection*>(session_.connection())), + OnStreamReset(stream_->id(), QUIC_STREAM_CANCELLED)); + } + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + stream_->OnStreamReset(rst_frame); + if (session_.connection()->transport_version() == QUIC_VERSION_99) { + // Create and inject a STOP SENDING frame to complete the close + // of the stream. This is only needed for version 99/IETF QUIC. + QuicStopSendingFrame stop_sending( + kInvalidControlFrameId, stream_->id(), + static_cast<QuicApplicationErrorCode>(QUIC_STREAM_CANCELLED)); + session_.OnStopSendingFrame(stop_sending); + } + + EXPECT_TRUE(stream_->reading_stopped()); + EXPECT_TRUE(stream_->write_side_closed()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc new file mode 100644 index 0000000..01e630d --- /dev/null +++ b/quic/core/http/quic_spdy_session.cc
@@ -0,0 +1,696 @@ +// Copyright (c) 2015 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/http/quic_spdy_session.h" + +#include <algorithm> +#include <cstdint> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h" + +using http2::Http2DecoderAdapter; +using spdy::HpackEntry; +using spdy::HpackHeaderTable; +using spdy::Http2WeightToSpdy3Priority; +using spdy::SETTINGS_ENABLE_PUSH; +using spdy::SETTINGS_HEADER_TABLE_SIZE; +using spdy::SETTINGS_MAX_HEADER_LIST_SIZE; +using spdy::Spdy3PriorityToHttp2Weight; +using spdy::SpdyErrorCode; +using spdy::SpdyFramer; +using spdy::SpdyFramerDebugVisitorInterface; +using spdy::SpdyFramerVisitorInterface; +using spdy::SpdyFrameType; +using spdy::SpdyHeaderBlock; +using spdy::SpdyHeadersHandlerInterface; +using spdy::SpdyHeadersIR; +using spdy::SpdyKnownSettingsId; +using spdy::SpdyPingId; +using spdy::SpdyPriority; +using spdy::SpdyPriorityIR; +using spdy::SpdyPushPromiseIR; +using spdy::SpdySerializedFrame; +using spdy::SpdySettingsId; +using spdy::SpdySettingsIR; +using spdy::SpdyStreamId; + +namespace quic { + +namespace { + +class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface { + public: + HeaderTableDebugVisitor(const QuicClock* clock, + std::unique_ptr<QuicHpackDebugVisitor> visitor) + : clock_(clock), headers_stream_hpack_visitor_(std::move(visitor)) {} + HeaderTableDebugVisitor(const HeaderTableDebugVisitor&) = delete; + HeaderTableDebugVisitor& operator=(const HeaderTableDebugVisitor&) = delete; + + int64_t OnNewEntry(const HpackEntry& entry) override { + QUIC_DVLOG(1) << entry.GetDebugString(); + return (clock_->ApproximateNow() - QuicTime::Zero()).ToMicroseconds(); + } + + void OnUseEntry(const HpackEntry& entry) override { + const QuicTime::Delta elapsed( + clock_->ApproximateNow() - + QuicTime::Delta::FromMicroseconds(entry.time_added()) - + QuicTime::Zero()); + QUIC_DVLOG(1) << entry.GetDebugString() << " " << elapsed.ToMilliseconds() + << " ms"; + headers_stream_hpack_visitor_->OnUseEntry(elapsed); + } + + private: + const QuicClock* clock_; + std::unique_ptr<QuicHpackDebugVisitor> headers_stream_hpack_visitor_; +}; + +} // namespace + +// A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and +// closes the connection if any unexpected frames are received. +class QuicSpdySession::SpdyFramerVisitor + : public SpdyFramerVisitorInterface, + public SpdyFramerDebugVisitorInterface { + public: + explicit SpdyFramerVisitor(QuicSpdySession* session) : session_(session) {} + SpdyFramerVisitor(const SpdyFramerVisitor&) = delete; + SpdyFramerVisitor& operator=(const SpdyFramerVisitor&) = delete; + + SpdyHeadersHandlerInterface* OnHeaderFrameStart( + SpdyStreamId /* stream_id */) override { + return &header_list_; + } + + void OnHeaderFrameEnd(SpdyStreamId /* stream_id */) override { + if (session_->IsConnected()) { + session_->OnHeaderList(header_list_); + } + header_list_.Clear(); + } + + void OnStreamFrameData(SpdyStreamId stream_id, + const char* data, + size_t len) override { + CloseConnection("SPDY DATA frame received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + } + + void OnStreamEnd(SpdyStreamId stream_id) override { + // The framer invokes OnStreamEnd after processing a frame that had the fin + // bit set. + } + + void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { + CloseConnection("SPDY frame padding received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + } + + void OnError(Http2DecoderAdapter::SpdyFramerError error) override { + QuicErrorCode code = QUIC_INVALID_HEADERS_STREAM_DATA; + switch (error) { + case Http2DecoderAdapter::SpdyFramerError::SPDY_DECOMPRESS_FAILURE: + code = QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE; + break; + default: + break; + } + CloseConnection( + QuicStrCat("SPDY framing error: ", + Http2DecoderAdapter::SpdyFramerErrorToString(error)), + code); + } + + void OnDataFrameHeader(SpdyStreamId stream_id, + size_t length, + bool fin) override { + CloseConnection("SPDY DATA frame received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + } + + void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override { + CloseConnection("SPDY RST_STREAM frame received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + } + + void OnSetting(SpdySettingsId id, uint32_t value) override { + switch (id) { + case SETTINGS_HEADER_TABLE_SIZE: + session_->UpdateHeaderEncoderTableSize(value); + break; + case SETTINGS_ENABLE_PUSH: + if (session_->perspective() == Perspective::IS_SERVER) { + // See rfc7540, Section 6.5.2. + if (value > 1) { + CloseConnection( + QuicStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value), + QUIC_INVALID_HEADERS_STREAM_DATA); + return; + } + session_->UpdateEnableServerPush(value > 0); + break; + } else { + CloseConnection( + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id), + QUIC_INVALID_HEADERS_STREAM_DATA); + } + break; + // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when + // clients are actually sending it. + case SETTINGS_MAX_HEADER_LIST_SIZE: + break; + default: + CloseConnection( + QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id), + QUIC_INVALID_HEADERS_STREAM_DATA); + } + } + + void OnSettingsEnd() override {} + + void OnPing(SpdyPingId unique_id, bool is_ack) override { + CloseConnection("SPDY PING frame received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + } + + void OnGoAway(SpdyStreamId last_accepted_stream_id, + SpdyErrorCode error_code) override { + CloseConnection("SPDY GOAWAY frame received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + } + + void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + int weight, + SpdyStreamId /*parent_stream_id*/, + bool /*exclusive*/, + bool fin, + bool end) override { + if (!session_->IsConnected()) { + return; + } + + // TODO(mpw): avoid down-conversion and plumb SpdyStreamPrecedence through + // QuicHeadersStream. + SpdyPriority priority = + has_priority ? Http2WeightToSpdy3Priority(weight) : 0; + session_->OnHeaders(stream_id, has_priority, priority, fin); + } + + void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override { + CloseConnection("SPDY WINDOW_UPDATE frame received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + } + + void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + bool end) override { + if (!session_->supports_push_promise()) { + CloseConnection("PUSH_PROMISE not supported.", + QUIC_INVALID_HEADERS_STREAM_DATA); + return; + } + if (!session_->IsConnected()) { + return; + } + session_->OnPushPromise(stream_id, promised_stream_id, end); + } + + void OnContinuation(SpdyStreamId stream_id, bool end) override {} + + void OnPriority(SpdyStreamId stream_id, + SpdyStreamId parent_id, + int weight, + bool exclusive) override { + if (session_->connection()->transport_version() <= QUIC_VERSION_39) { + CloseConnection("SPDY PRIORITY frame received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + return; + } + if (!session_->IsConnected()) { + return; + } + // TODO (wangyix): implement real HTTP/2 weights and dependencies instead of + // converting to SpdyPriority. + SpdyPriority priority = Http2WeightToSpdy3Priority(weight); + session_->OnPriority(stream_id, priority); + } + + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { + CloseConnection("Unknown frame type received.", + QUIC_INVALID_HEADERS_STREAM_DATA); + return false; + } + + // SpdyFramerDebugVisitorInterface implementation + void OnSendCompressedFrame(SpdyStreamId stream_id, + SpdyFrameType type, + size_t payload_len, + size_t frame_len) override { + if (payload_len == 0) { + QUIC_BUG << "Zero payload length."; + return; + } + int compression_pct = 100 - (100 * frame_len) / payload_len; + QUIC_DVLOG(1) << "Net.QuicHpackCompressionPercentage: " << compression_pct; + } + + void OnReceiveCompressedFrame(SpdyStreamId stream_id, + SpdyFrameType type, + size_t frame_len) override { + if (session_->IsConnected()) { + session_->OnCompressedFrameSize(frame_len); + } + } + + void set_max_uncompressed_header_bytes( + size_t set_max_uncompressed_header_bytes) { + header_list_.set_max_header_list_size(set_max_uncompressed_header_bytes); + } + + private: + void CloseConnection(const QuicString& details, QuicErrorCode code) { + if (session_->IsConnected()) { + session_->CloseConnectionWithDetails(code, details); + } + } + + private: + QuicSpdySession* session_; + QuicHeaderList header_list_; +}; + +QuicHpackDebugVisitor::QuicHpackDebugVisitor() {} + +QuicHpackDebugVisitor::~QuicHpackDebugVisitor() {} + +QuicSpdySession::QuicSpdySession( + QuicConnection* connection, + QuicSession::Visitor* visitor, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions) + : QuicSession(connection, visitor, config, supported_versions), + max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize), + server_push_enabled_(true), + stream_id_( + QuicUtils::GetInvalidStreamId(connection->transport_version())), + promised_stream_id_( + QuicUtils::GetInvalidStreamId(connection->transport_version())), + fin_(false), + frame_len_(0), + uncompressed_frame_len_(0), + supports_push_promise_(perspective() == Perspective::IS_CLIENT), + spdy_framer_(SpdyFramer::ENABLE_COMPRESSION), + spdy_framer_visitor_(new SpdyFramerVisitor(this)) { + h2_deframer_.set_visitor(spdy_framer_visitor_.get()); + h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get()); + spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); +} + +QuicSpdySession::~QuicSpdySession() { + // Set the streams' session pointers in closed and dynamic stream lists + // to null to avoid subsequent use of this session. + for (auto& stream : *closed_streams()) { + static_cast<QuicSpdyStream*>(stream.get())->ClearSession(); + } + for (auto const& kv : zombie_streams()) { + static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession(); + } + for (auto const& kv : dynamic_streams()) { + static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession(); + } +} + +void QuicSpdySession::Initialize() { + QuicSession::Initialize(); + + if (perspective() == Perspective::IS_SERVER) { + set_largest_peer_created_stream_id( + QuicUtils::GetHeadersStreamId(connection()->transport_version())); + } else { + QuicStreamId headers_stream_id = GetNextOutgoingBidirectionalStreamId(); + DCHECK_EQ(headers_stream_id, + QuicUtils::GetHeadersStreamId(connection()->transport_version())); + } + + if (VersionUsesQpack(connection()->transport_version())) { + qpack_encoder_ = QuicMakeUnique<QpackEncoder>(this, this); + qpack_decoder_ = QuicMakeUnique<QpackDecoder>(this, this); + } + + headers_stream_ = QuicMakeUnique<QuicHeadersStream>((this)); + DCHECK_EQ(QuicUtils::GetHeadersStreamId(connection()->transport_version()), + headers_stream_->id()); + RegisterStaticStream( + QuicUtils::GetHeadersStreamId(connection()->transport_version()), + headers_stream_.get()); + + set_max_uncompressed_header_bytes(max_inbound_header_list_size_); + + // Limit HPACK buffering to 2x header list size limit. + set_max_decode_buffer_size_bytes(2 * max_inbound_header_list_size_); +} + +void QuicSpdySession::OnDecoderStreamError(QuicStringPiece error_message) { + DCHECK(VersionUsesQpack(connection()->transport_version())); + + // TODO(112770235): Signal connection error on decoder stream errors. + QUIC_NOTREACHED(); +} + +void QuicSpdySession::WriteEncoderStreamData(QuicStringPiece data) { + DCHECK(VersionUsesQpack(connection()->transport_version())); + + // TODO(112770235): Send encoder stream data on encoder stream. + QUIC_NOTREACHED(); +} + +void QuicSpdySession::OnEncoderStreamError(QuicStringPiece error_message) { + DCHECK(VersionUsesQpack(connection()->transport_version())); + + // TODO(112770235): Signal connection error on encoder stream errors. + QUIC_NOTREACHED(); +} + +void QuicSpdySession::WriteDecoderStreamData(QuicStringPiece data) { + DCHECK(VersionUsesQpack(connection()->transport_version())); + + // TODO(112770235): Send decoder stream data on decoder stream. + QUIC_NOTREACHED(); +} + +void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id, + SpdyPriority priority) { + QuicSpdyStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnStreamHeadersPriority(priority); +} + +void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, + bool fin, + size_t frame_len, + const QuicHeaderList& header_list) { + if (QuicContainsKey(static_streams(), stream_id)) { + connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + QuicSpdyStream* stream = GetSpdyDataStream(stream_id); + if (stream == nullptr) { + // The stream no longer exists, but trailing headers may contain the final + // byte offset necessary for flow control and open stream accounting. + size_t final_byte_offset = 0; + for (const auto& header : header_list) { + const QuicString& header_key = header.first; + const QuicString& header_value = header.second; + if (header_key == kFinalOffsetHeaderKey) { + if (!QuicTextUtils::StringToSizeT(header_value, &final_byte_offset)) { + connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, + "Trailers are malformed (no final offset)", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + DVLOG(1) << "Received final byte offset in trailers for stream " + << stream_id << ", which no longer exists."; + OnFinalByteOffsetReceived(stream_id, final_byte_offset); + } + } + + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnStreamHeaderList(fin, frame_len, header_list); +} + +void QuicSpdySession::OnPriorityFrame(QuicStreamId stream_id, + SpdyPriority priority) { + QuicSpdyStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive a PRIORITY frame after a stream has been + // reset. + return; + } + stream->OnPriorityFrame(priority); +} + +size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) { + return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base), + iov.iov_len); +} + +size_t QuicSpdySession::WriteHeadersOnHeadersStream( + QuicStreamId id, + SpdyHeaderBlock headers, + bool fin, + SpdyPriority priority, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + return WriteHeadersOnHeadersStreamImpl( + id, std::move(headers), fin, + /* parent_stream_id = */ 0, Spdy3PriorityToHttp2Weight(priority), + /* exclusive = */ false, std::move(ack_listener)); +} + +size_t QuicSpdySession::WritePriority(QuicStreamId id, + QuicStreamId parent_stream_id, + int weight, + bool exclusive) { + if (connection()->transport_version() <= QUIC_VERSION_39) { + return 0; + } + SpdyPriorityIR priority_frame(id, parent_stream_id, weight, exclusive); + SpdySerializedFrame frame(spdy_framer_.SerializeFrame(priority_frame)); + headers_stream_->WriteOrBufferData( + QuicStringPiece(frame.data(), frame.size()), false, nullptr); + return frame.size(); +} + +size_t QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id, + QuicStreamId promised_stream_id, + SpdyHeaderBlock headers) { + if (perspective() == Perspective::IS_CLIENT) { + QUIC_BUG << "Client shouldn't send PUSH_PROMISE"; + return 0; + } + + SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id, + std::move(headers)); + // PUSH_PROMISE must not be the last frame sent out, at least followed by + // response headers. + push_promise.set_fin(false); + + SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise)); + headers_stream_->WriteOrBufferData( + QuicStringPiece(frame.data(), frame.size()), false, nullptr); + return frame.size(); +} + +size_t QuicSpdySession::SendMaxHeaderListSize(size_t value) { + SpdySettingsIR settings_frame; + settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value); + + SpdySerializedFrame frame(spdy_framer_.SerializeFrame(settings_frame)); + headers_stream_->WriteOrBufferData( + QuicStringPiece(frame.data(), frame.size()), false, nullptr); + return frame.size(); +} + +QpackEncoder* QuicSpdySession::qpack_encoder() { + DCHECK(VersionUsesQpack(connection()->transport_version())); + + return qpack_encoder_.get(); +} + +QpackDecoder* QuicSpdySession::qpack_decoder() { + DCHECK(VersionUsesQpack(connection()->transport_version())); + + return qpack_decoder_.get(); +} + +QuicSpdyStream* QuicSpdySession::GetSpdyDataStream( + const QuicStreamId stream_id) { + return static_cast<QuicSpdyStream*>(GetOrCreateDynamicStream(stream_id)); +} + +void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { + QuicSession::OnCryptoHandshakeEvent(event); + if (event == HANDSHAKE_CONFIRMED && config()->SupportMaxHeaderListSize()) { + SendMaxHeaderListSize(max_inbound_header_list_size_); + } +} + +// True if there are open HTTP requests. +bool QuicSpdySession::ShouldKeepConnectionAlive() const { + // Change to check if there are open HTTP requests. + // When IETF QUIC control and QPACK streams are used, those will need to be + // subtracted from this count to ensure only request streams are counted. + return GetNumOpenDynamicStreams() > 0; +} + +bool QuicSpdySession::ShouldBufferIncomingStream(QuicStreamId id) const { + DCHECK_EQ(QUIC_VERSION_99, connection()->transport_version()); + return !QuicUtils::IsBidirectionalStreamId(id); +} + +size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl( + QuicStreamId id, + spdy::SpdyHeaderBlock headers, + bool fin, + QuicStreamId parent_stream_id, + int weight, + bool exclusive, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + SpdyHeadersIR headers_frame(id, std::move(headers)); + headers_frame.set_fin(fin); + if (perspective() == Perspective::IS_CLIENT) { + headers_frame.set_has_priority(true); + headers_frame.set_parent_stream_id(parent_stream_id); + headers_frame.set_weight(weight); + headers_frame.set_exclusive(exclusive); + } + SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame)); + headers_stream_->WriteOrBufferData( + QuicStringPiece(frame.data(), frame.size()), false, + std::move(ack_listener)); + return frame.size(); +} + +void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len, + const QuicHeaderList& header_list) { + QuicString error = "OnPromiseHeaderList should be overridden in client code."; + QUIC_BUG << error; + connection()->CloseConnection(QUIC_INTERNAL_ERROR, error, + ConnectionCloseBehavior::SILENT_CLOSE); +} + +bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() { + return false; +} + +void QuicSpdySession::OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, + bool fin) { + if (has_priority) { + if (perspective() == Perspective::IS_CLIENT) { + CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "Server must not send priorities."); + return; + } + OnStreamHeadersPriority(stream_id, priority); + } else { + if (perspective() == Perspective::IS_SERVER) { + CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "Client must send priorities."); + return; + } + } + DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()), + stream_id_); + DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()), + promised_stream_id_); + stream_id_ = stream_id; + fin_ = fin; +} + +void QuicSpdySession::OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + bool end) { + DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()), + stream_id_); + DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()), + promised_stream_id_); + stream_id_ = stream_id; + promised_stream_id_ = promised_stream_id; +} + +// TODO (wangyix): Why is SpdyStreamId used instead of QuicStreamId? +// This occurs in many places in this file. +void QuicSpdySession::OnPriority(SpdyStreamId stream_id, + SpdyPriority priority) { + if (perspective() == Perspective::IS_CLIENT) { + CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "Server must not send PRIORITY frames."); + return; + } + OnPriorityFrame(stream_id, priority); +} + +void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) { + QUIC_DVLOG(1) << "Received header list for stream " << stream_id_ << ": " + << header_list.DebugString(); + if (promised_stream_id_ == + QuicUtils::GetInvalidStreamId(connection()->transport_version())) { + OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list); + } else { + OnPromiseHeaderList(stream_id_, promised_stream_id_, frame_len_, + header_list); + } + // Reset state for the next frame. + promised_stream_id_ = + QuicUtils::GetInvalidStreamId(connection()->transport_version()); + stream_id_ = QuicUtils::GetInvalidStreamId(connection()->transport_version()); + fin_ = false; + frame_len_ = 0; + uncompressed_frame_len_ = 0; +} + +void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) { + frame_len_ += frame_len; +} + +void QuicSpdySession::SetHpackEncoderDebugVisitor( + std::unique_ptr<QuicHpackDebugVisitor> visitor) { + spdy_framer_.SetEncoderHeaderTableDebugVisitor( + std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor( + connection()->helper()->GetClock(), std::move(visitor)))); +} + +void QuicSpdySession::SetHpackDecoderDebugVisitor( + std::unique_ptr<QuicHpackDebugVisitor> visitor) { + h2_deframer_.SetDecoderHeaderTableDebugVisitor( + QuicMakeUnique<HeaderTableDebugVisitor>( + connection()->helper()->GetClock(), std::move(visitor))); +} + +void QuicSpdySession::UpdateHeaderEncoderTableSize(uint32_t value) { + spdy_framer_.UpdateHeaderEncoderTableSize(value); +} + +void QuicSpdySession::UpdateEnableServerPush(bool value) { + set_server_push_enabled(value); +} + +void QuicSpdySession::set_max_uncompressed_header_bytes( + size_t set_max_uncompressed_header_bytes) { + spdy_framer_visitor_->set_max_uncompressed_header_bytes( + set_max_uncompressed_header_bytes); +} + +void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error, + const QuicString& details) { + connection()->CloseConnection( + error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +} // namespace quic
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h new file mode 100644 index 0000000..00f241e --- /dev/null +++ b/quic/core/http/quic_spdy_session.h
@@ -0,0 +1,283 @@ +// Copyright (c) 2015 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_HTTP_QUIC_SPDY_SESSION_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SESSION_H_ + +#include <cstddef> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" +#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h" + +namespace quic { + +namespace test { +class QuicSpdySessionPeer; +} // namespace test + +// QuicHpackDebugVisitor gathers data used for understanding HPACK HoL +// dynamics. Specifically, it is to help predict the compression +// penalty of avoiding HoL by chagning how the dynamic table is used. +// In chromium, the concrete instance populates an UMA +// histogram with the data. +class QUIC_EXPORT_PRIVATE QuicHpackDebugVisitor { + public: + QuicHpackDebugVisitor(); + QuicHpackDebugVisitor(const QuicHpackDebugVisitor&) = delete; + QuicHpackDebugVisitor& operator=(const QuicHpackDebugVisitor&) = delete; + + virtual ~QuicHpackDebugVisitor(); + + // For each HPACK indexed representation processed, |elapsed| is + // the time since the corresponding entry was added to the dynamic + // table. + virtual void OnUseEntry(QuicTime::Delta elapsed) = 0; +}; + +// A QUIC session with a headers stream. +class QUIC_EXPORT_PRIVATE QuicSpdySession + : public QuicSession, + public QpackEncoder::DecoderStreamErrorDelegate, + public QpackEncoderStreamSender::Delegate, + public QpackDecoder::EncoderStreamErrorDelegate, + public QpackDecoderStreamSender::Delegate { + public: + // Does not take ownership of |connection| or |visitor|. + QuicSpdySession(QuicConnection* connection, + QuicSession::Visitor* visitor, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions); + QuicSpdySession(const QuicSpdySession&) = delete; + QuicSpdySession& operator=(const QuicSpdySession&) = delete; + + ~QuicSpdySession() override; + + void Initialize() override; + + // QpackEncoder::DecoderStreamErrorDelegate implementation. + void OnDecoderStreamError(QuicStringPiece error_message) override; + + // QpackEncoderStreamSender::Delegate implemenation. + void WriteEncoderStreamData(QuicStringPiece data) override; + + // QpackDecoder::EncoderStreamErrorDelegate implementation. + void OnEncoderStreamError(QuicStringPiece error_message) override; + + // QpackDecoderStreamSender::Delegate implementation. + void WriteDecoderStreamData(QuicStringPiece data) override; + + // Called by |headers_stream_| when headers with a priority have been + // received for a stream. This method will only be called for server streams. + virtual void OnStreamHeadersPriority(QuicStreamId stream_id, + spdy::SpdyPriority priority); + + // Called by |headers_stream_| when headers have been completely received + // for a stream. |fin| will be true if the fin flag was set in the headers + // frame. + virtual void OnStreamHeaderList(QuicStreamId stream_id, + bool fin, + size_t frame_len, + const QuicHeaderList& header_list); + + // Called by |headers_stream_| when push promise headers have been + // completely received. |fin| will be true if the fin flag was set + // in the headers. + virtual void OnPromiseHeaderList(QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len, + const QuicHeaderList& header_list); + + // Called by |headers_stream_| when a PRIORITY frame has been received for a + // stream. This method will only be called for server streams. + virtual void OnPriorityFrame(QuicStreamId stream_id, + spdy::SpdyPriority priority); + + // Sends contents of |iov| to h2_deframer_, returns number of bytes processed. + size_t ProcessHeaderData(const struct iovec& iov); + + // Writes |headers| for the stream |id| to the dedicated headers stream. + // If |fin| is true, then no more data will be sent for the stream |id|. + // If provided, |ack_notifier_delegate| will be registered to be notified when + // we have seen ACKs for all packets resulting from this call. + virtual size_t WriteHeadersOnHeadersStream( + QuicStreamId id, + spdy::SpdyHeaderBlock headers, + bool fin, + spdy::SpdyPriority priority, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + + // Writes a PRIORITY frame the to peer. Returns the size in bytes of the + // resulting PRIORITY frame for QUIC_VERSION_43 and above. Otherwise, does + // nothing and returns 0. + size_t WritePriority(QuicStreamId id, + QuicStreamId parent_stream_id, + int weight, + bool exclusive); + + // Write |headers| for |promised_stream_id| on |original_stream_id| in a + // PUSH_PROMISE frame to peer. + // Return the size, in bytes, of the resulting PUSH_PROMISE frame. + virtual size_t WritePushPromise(QuicStreamId original_stream_id, + QuicStreamId promised_stream_id, + spdy::SpdyHeaderBlock headers); + + // Sends SETTINGS_MAX_HEADER_LIST_SIZE SETTINGS frame. + size_t SendMaxHeaderListSize(size_t value); + + QpackEncoder* qpack_encoder(); + QpackDecoder* qpack_decoder(); + QuicHeadersStream* headers_stream() { return headers_stream_.get(); } + + bool server_push_enabled() const { return server_push_enabled_; } + + // Called by |QuicHeadersStream::UpdateEnableServerPush()| with + // value from SETTINGS_ENABLE_PUSH. + void set_server_push_enabled(bool enable) { server_push_enabled_ = enable; } + + // Return true if this session wants to release headers stream's buffer + // aggressively. + virtual bool ShouldReleaseHeadersStreamSequencerBuffer(); + + void CloseConnectionWithDetails(QuicErrorCode error, + const QuicString& details); + + void set_max_inbound_header_list_size(size_t max_inbound_header_list_size) { + max_inbound_header_list_size_ = max_inbound_header_list_size; + } + + protected: + // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and + // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to + // make sure that all data streams are QuicSpdyStreams. + QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override = 0; + QuicSpdyStream* CreateIncomingStream(PendingStream pending) override = 0; + virtual QuicSpdyStream* CreateOutgoingBidirectionalStream() = 0; + virtual QuicSpdyStream* CreateOutgoingUnidirectionalStream() = 0; + + QuicSpdyStream* GetSpdyDataStream(const QuicStreamId stream_id); + + // If an incoming stream can be created, return true. + virtual bool ShouldCreateIncomingStream(QuicStreamId id) = 0; + + // If an outgoing bidirectional/unidirectional stream can be created, return + // true. + virtual bool ShouldCreateOutgoingBidirectionalStream() = 0; + virtual bool ShouldCreateOutgoingUnidirectionalStream() = 0; + + // Returns true if there are open HTTP requests. + bool ShouldKeepConnectionAlive() const override; + + // Overridden to buffer incoming streams for version 99. + bool ShouldBufferIncomingStream(QuicStreamId id) const override; + + size_t WriteHeadersOnHeadersStreamImpl( + QuicStreamId id, + spdy::SpdyHeaderBlock headers, + bool fin, + QuicStreamId parent_stream_id, + int weight, + bool exclusive, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + + void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override; + + bool supports_push_promise() { return supports_push_promise_; } + + // Optional, enables instrumentation related to go/quic-hpack. + void SetHpackEncoderDebugVisitor( + std::unique_ptr<QuicHpackDebugVisitor> visitor); + void SetHpackDecoderDebugVisitor( + std::unique_ptr<QuicHpackDebugVisitor> visitor); + + // Sets the maximum size of the header compression table spdy_framer_ is + // willing to use to encode header blocks. + void UpdateHeaderEncoderTableSize(uint32_t value); + + // Called when SETTINGS_ENABLE_PUSH is received, only supported on + // server side. + void UpdateEnableServerPush(bool value); + + bool IsConnected() { return connection()->connected(); } + + // Sets how much encoded data the hpack decoder of h2_deframer_ is willing to + // buffer. + void set_max_decode_buffer_size_bytes(size_t max_decode_buffer_size_bytes) { + h2_deframer_.GetHpackDecoder()->set_max_decode_buffer_size_bytes( + max_decode_buffer_size_bytes); + } + + void set_max_uncompressed_header_bytes( + size_t set_max_uncompressed_header_bytes); + + private: + friend class test::QuicSpdySessionPeer; + + class SpdyFramerVisitor; + + // The following methods are called by the SimpleVisitor. + + // Called when a HEADERS frame has been received. + void OnHeaders(spdy::SpdyStreamId stream_id, + bool has_priority, + spdy::SpdyPriority priority, + bool fin); + + // Called when a PUSH_PROMISE frame has been received. + void OnPushPromise(spdy::SpdyStreamId stream_id, + spdy::SpdyStreamId promised_stream_id, + bool end); + + // Called when a PRIORITY frame has been received. + void OnPriority(spdy::SpdyStreamId stream_id, spdy::SpdyPriority priority); + + // Called when the complete list of headers is available. + void OnHeaderList(const QuicHeaderList& header_list); + + // Called when the size of the compressed frame payload is available. + void OnCompressedFrameSize(size_t frame_len); + + std::unique_ptr<QpackEncoder> qpack_encoder_; + std::unique_ptr<QpackDecoder> qpack_decoder_; + + // TODO(123528590): Remove this member. + std::unique_ptr<QuicHeadersStream> headers_stream_; + + // The maximum size of a header block that will be accepted from the peer, + // defined per spec as key + value + overhead per field (uncompressed). + size_t max_inbound_header_list_size_; + + // Set during handshake. If true, resources in x-associated-content and link + // headers will be pushed. + bool server_push_enabled_; + + // Data about the stream whose headers are being processed. + QuicStreamId stream_id_; + QuicStreamId promised_stream_id_; + bool fin_; + size_t frame_len_; + size_t uncompressed_frame_len_; + + bool supports_push_promise_; + + spdy::SpdyFramer spdy_framer_; + http2::Http2DecoderAdapter h2_deframer_; + std::unique_ptr<SpdyFramerVisitor> spdy_framer_visitor_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SESSION_H_
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc new file mode 100644 index 0000000..1c53472 --- /dev/null +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -0,0 +1,1803 @@ +// Copyright (c) 2012 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/http/quic_spdy_session.h" + +#include <cstdint> +#include <set> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" + +using spdy::kV3HighestPriority; +using spdy::Spdy3PriorityToHttp2Weight; +using spdy::SpdyFramer; +using spdy::SpdyHeaderBlock; +using spdy::SpdyPriority; +using spdy::SpdyPriorityIR; +using spdy::SpdySerializedFrame; +using testing::_; +using testing::AtLeast; +using testing::InSequence; +using testing::Invoke; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { + public: + explicit TestCryptoStream(QuicSession* session) + : QuicCryptoStream(session), + QuicCryptoHandshaker(this, session), + encryption_established_(false), + handshake_confirmed_(false), + params_(new QuicCryptoNegotiatedParameters) {} + + void OnHandshakeMessage(const CryptoHandshakeMessage& /*message*/) override { + encryption_established_ = true; + handshake_confirmed_ = true; + CryptoHandshakeMessage msg; + QuicString error_details; + session()->config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session()->config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + session()->config()->ToHandshakeMessage(&msg); + const QuicErrorCode error = + session()->config()->ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + session()->OnConfigNegotiated(); + session()->connection()->SetDefaultEncryptionLevel( + ENCRYPTION_FORWARD_SECURE); + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + } + + // QuicCryptoStream implementation + bool encryption_established() const override { + return encryption_established_; + } + bool handshake_confirmed() const override { return handshake_confirmed_; } + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override { + return *params_; + } + CryptoMessageParser* crypto_message_parser() override { + return QuicCryptoHandshaker::crypto_message_parser(); + } + + MOCK_METHOD0(OnCanWrite, void()); + + bool HasPendingCryptoRetransmission() override { return false; } + + MOCK_CONST_METHOD0(HasPendingRetransmission, bool()); + + private: + using QuicCryptoStream::session; + + bool encryption_established_; + bool handshake_confirmed_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; +}; + +class TestHeadersStream : public QuicHeadersStream { + public: + explicit TestHeadersStream(QuicSpdySession* session) + : QuicHeadersStream(session) {} + + MOCK_METHOD0(OnCanWrite, void()); +}; + +class TestStream : public QuicSpdyStream { + public: + TestStream(QuicStreamId id, QuicSpdySession* session, StreamType type) + : QuicSpdyStream(id, session, type) {} + + TestStream(PendingStream pending, QuicSpdySession* session, StreamType type) + : QuicSpdyStream(std::move(pending), session, type) {} + + using QuicStream::CloseWriteSide; + + void OnBodyAvailable() override {} + + MOCK_METHOD0(OnCanWrite, void()); + MOCK_METHOD3(RetransmitStreamData, + bool(QuicStreamOffset, QuicByteCount, bool)); + + MOCK_CONST_METHOD0(HasPendingRetransmission, bool()); +}; + +class TestSession : public QuicSpdySession { + public: + explicit TestSession(QuicConnection* connection) + : QuicSpdySession(connection, + nullptr, + DefaultQuicConfig(), + CurrentSupportedVersions()), + crypto_stream_(this), + writev_consumes_all_data_(false) { + Initialize(); + this->connection()->SetEncrypter( + ENCRYPTION_FORWARD_SECURE, + QuicMakeUnique<NullEncrypter>(connection->perspective())); + } + + ~TestSession() override { delete connection(); } + + TestCryptoStream* GetMutableCryptoStream() override { + return &crypto_stream_; + } + + const TestCryptoStream* GetCryptoStream() const override { + return &crypto_stream_; + } + + TestStream* CreateOutgoingBidirectionalStream() override { + TestStream* stream = new TestStream(GetNextOutgoingBidirectionalStreamId(), + this, BIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + TestStream* CreateOutgoingUnidirectionalStream() override { + TestStream* stream = new TestStream(GetNextOutgoingUnidirectionalStreamId(), + this, WRITE_UNIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + TestStream* CreateIncomingStream(QuicStreamId id) override { + // Enforce the limit on the number of open streams. + if (GetNumOpenIncomingStreams() + 1 > + max_open_incoming_bidirectional_streams() && + connection()->transport_version() != QUIC_VERSION_99) { + connection()->CloseConnection( + QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams!", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return nullptr; + } else { + TestStream* stream = new TestStream( + id, this, + DetermineStreamType(id, connection()->transport_version(), + perspective(), /*is_incoming=*/true, + BIDIRECTIONAL)); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + } + + TestStream* CreateIncomingStream(PendingStream pending) override { + QuicStreamId id = pending.id(); + TestStream* stream = + new TestStream(std::move(pending), this, + DetermineStreamType( + id, connection()->transport_version(), perspective(), + /*is_incoming=*/true, BIDIRECTIONAL)); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + bool ShouldCreateIncomingStream(QuicStreamId /*id*/) override { return true; } + + bool ShouldCreateOutgoingBidirectionalStream() override { return true; } + bool ShouldCreateOutgoingUnidirectionalStream() override { return true; } + + bool IsClosedStream(QuicStreamId id) { + return QuicSession::IsClosedStream(id); + } + + QuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id) { + return QuicSpdySession::GetOrCreateDynamicStream(stream_id); + } + + QuicConsumedData WritevData(QuicStream* stream, + QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state) override { + bool fin = state != NO_FIN; + QuicConsumedData consumed(write_length, fin); + if (!writev_consumes_all_data_) { + consumed = + QuicSession::WritevData(stream, id, write_length, offset, state); + } + if (fin && consumed.fin_consumed) { + stream->set_fin_sent(true); + } + QuicSessionPeer::GetWriteBlockedStreams(this)->UpdateBytesForStream( + id, consumed.bytes_consumed); + return consumed; + } + + void set_writev_consumes_all_data(bool val) { + writev_consumes_all_data_ = val; + } + + QuicConsumedData SendStreamData(QuicStream* stream) { + struct iovec iov; + if (stream->id() != + QuicUtils::GetCryptoStreamId(connection()->transport_version()) && + connection()->encryption_level() != ENCRYPTION_FORWARD_SECURE) { + this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + } + MakeIOVector("not empty", &iov); + QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9); + QuicConsumedData consumed = WritevData(stream, stream->id(), 9, 0, FIN); + QuicStreamPeer::SendBuffer(stream).OnStreamDataConsumed( + consumed.bytes_consumed); + return consumed; + } + + bool ClearControlFrame(const QuicFrame& frame) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) { + DCHECK(writev_consumes_all_data_); + return WritevData(stream, stream->id(), bytes, 0, FIN); + } + + using QuicSession::closed_streams; + using QuicSession::zombie_streams; + using QuicSpdySession::ShouldBufferIncomingStream; + + private: + StrictMock<TestCryptoStream> crypto_stream_; + + bool writev_consumes_all_data_; +}; + +class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { + public: + bool ClearMaxStreamIdControlFrame(const QuicFrame& frame) { + if (frame.type == MAX_STREAM_ID_FRAME) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + return false; + } + + protected: + explicit QuicSpdySessionTestBase(Perspective perspective) + : connection_( + new StrictMock<MockQuicConnection>(&helper_, + &alarm_factory_, + perspective, + SupportedVersions(GetParam()))), + session_(connection_) { + session_.config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session_.config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + headers_[":host"] = "www.google.com"; + headers_[":path"] = "/index.hml"; + headers_[":scheme"] = "http"; + headers_["cookie"] = + "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; " + "__utmc=160408618; " + "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX" + "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX" + "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT" + "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0" + "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh" + "1zFMi5vzcns38-8_Sns; " + "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-" + "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339" + "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c" + "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%" + "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4" + "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1" + "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP" + "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6" + "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b" + "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6" + "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG" + "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk" + "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn" + "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr" + "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo "; + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) + .Times(testing::AnyNumber()); + } + + void CheckClosedStreams() { + for (QuicStreamId i = + QuicUtils::GetCryptoStreamId(connection_->transport_version()); + i < 100; i++) { + if (!QuicContainsKey(closed_streams_, i)) { + EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i; + } else { + EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i; + } + } + } + + void CloseStream(QuicStreamId id) { + if (!IsVersion99()) { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + } else { + // V99 has two frames, RST_STREAM and STOP_SENDING + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + } + EXPECT_CALL(*connection_, OnStreamReset(id, _)); + session_.CloseStream(id); + closed_streams_.insert(id); + } + + QuicTransportVersion transport_version() const { + return connection_->transport_version(); + } + + bool IsVersion99() const { return transport_version() == QUIC_VERSION_99; } + + QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { + return GetNthClientInitiatedBidirectionalStreamId(transport_version(), n); + } + + QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { + return GetNthServerInitiatedBidirectionalStreamId( + connection_->transport_version(), n); + } + + QuicStreamId IdDelta() { + return QuicUtils::StreamIdDelta(connection_->transport_version()); + } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + TestSession session_; + std::set<QuicStreamId> closed_streams_; + SpdyHeaderBlock headers_; +}; + +class QuicSpdySessionTestServer : public QuicSpdySessionTestBase { + protected: + QuicSpdySessionTestServer() + : QuicSpdySessionTestBase(Perspective::IS_SERVER) {} +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicSpdySessionTestServer, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamUnidirectional) { + if (!IsVersion99()) { + return; + } + EXPECT_TRUE(session_.ShouldBufferIncomingStream( + QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_CLIENT))); +} + +TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamBidirectional) { + if (!IsVersion99()) { + return; + } + EXPECT_FALSE(session_.ShouldBufferIncomingStream( + QuicUtils::GetFirstBidirectionalStreamId(connection_->transport_version(), + Perspective::IS_CLIENT))); +} + +TEST_P(QuicSpdySessionTestServer, PeerAddress) { + EXPECT_EQ(QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort), + session_.peer_address()); +} + +TEST_P(QuicSpdySessionTestServer, SelfAddress) { + EXPECT_TRUE(session_.self_address().IsInitialized()); +} + +TEST_P(QuicSpdySessionTestServer, IsCryptoHandshakeConfirmed) { + EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); + EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed()); +} + +TEST_P(QuicSpdySessionTestServer, IsClosedStreamDefault) { + // Ensure that no streams are initially closed. + for (QuicStreamId i = + QuicUtils::GetCryptoStreamId(connection_->transport_version()); + i < 100; i++) { + EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i; + } +} + +TEST_P(QuicSpdySessionTestServer, AvailableStreams) { + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(2)) != nullptr); + // Both client initiated streams with smaller stream IDs are available. + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedBidirectionalId(0))); + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedBidirectionalId(1))); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(1)) != nullptr); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(0)) != nullptr); +} + +TEST_P(QuicSpdySessionTestServer, IsClosedStreamLocallyCreated) { + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0), stream2->id()); + QuicSpdyStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1), stream4->id()); + + CheckClosedStreams(); + CloseStream(GetNthServerInitiatedBidirectionalId(0)); + CheckClosedStreams(); + CloseStream(GetNthServerInitiatedBidirectionalId(1)); + CheckClosedStreams(); +} + +TEST_P(QuicSpdySessionTestServer, IsClosedStreamPeerCreated) { + QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0); + QuicStreamId stream_id2 = GetNthClientInitiatedBidirectionalId(1); + session_.GetOrCreateDynamicStream(stream_id1); + session_.GetOrCreateDynamicStream(stream_id2); + + CheckClosedStreams(); + CloseStream(stream_id1); + CheckClosedStreams(); + CloseStream(stream_id2); + // Create a stream, and make another available. + QuicStream* stream3 = session_.GetOrCreateDynamicStream(stream_id2 + 4); + CheckClosedStreams(); + // Close one, but make sure the other is still not closed + CloseStream(stream3->id()); + CheckClosedStreams(); +} + +TEST_P(QuicSpdySessionTestServer, MaximumAvailableOpenedStreams) { + if (IsVersion99()) { + // For IETF QUIC, we should be able to obtain the max allowed + // stream ID, the next ID should fail. Since the actual limit + // is not the number of open streams, we allocate the max and the max+2. + // Get the max allowed stream ID, this should succeed. + EXPECT_NE(nullptr, + session_.GetOrCreateDynamicStream( + QuicSessionPeer::v99_streamid_manager( + dynamic_cast<QuicSession*>(&session_)) + ->actual_max_allowed_incoming_bidirectional_stream_id())); + EXPECT_NE( + nullptr, + session_.GetOrCreateDynamicStream( + QuicSessionPeer::v99_streamid_manager( + dynamic_cast<QuicSession*>(&session_)) + ->actual_max_allowed_incoming_unidirectional_stream_id())); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); + // Get the (max allowed stream ID)++, this should fail. + EXPECT_EQ(nullptr, + session_.GetOrCreateDynamicStream( + QuicSessionPeer::v99_streamid_manager( + dynamic_cast<QuicSession*>(&session_)) + ->actual_max_allowed_incoming_bidirectional_stream_id() + + IdDelta())); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1); + EXPECT_EQ(nullptr, + session_.GetOrCreateDynamicStream( + QuicSessionPeer::v99_streamid_manager( + dynamic_cast<QuicSession*>(&session_)) + ->actual_max_allowed_incoming_unidirectional_stream_id() + + IdDelta())); + } else { + QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); + session_.GetOrCreateDynamicStream(stream_id); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_NE( + nullptr, + session_.GetOrCreateDynamicStream( + stream_id + + IdDelta() * + (session_.max_open_incoming_bidirectional_streams() - 1))); + } +} + +TEST_P(QuicSpdySessionTestServer, TooManyAvailableStreams) { + QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0); + QuicStreamId stream_id2; + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id1)); + // A stream ID which is too large to create. + stream_id2 = GetNthClientInitiatedBidirectionalId( + 2 * session_.MaxAvailableBidirectionalStreams() + 4); + if (IsVersion99()) { + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + } else { + EXPECT_CALL(*connection_, + CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _)); + } + EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(stream_id2)); +} + +TEST_P(QuicSpdySessionTestServer, ManyAvailableStreams) { + // When max_open_streams_ is 200, should be able to create 200 streams + // out-of-order, that is, creating the one with the largest stream ID first. + QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200); + QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); + // Create one stream. + session_.GetOrCreateDynamicStream(stream_id); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + // Stream count is 200, GetNth... starts counting at 0, so the 200'th stream + // is 199. + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(199))); +} + +TEST_P(QuicSpdySessionTestServer, + DebugDFatalIfMarkingClosedStreamWriteBlocked) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (GetParam() != AllSupportedVersions()[0]) { + return; + } + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicStreamId closed_stream_id = stream2->id(); + // Close the stream. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(closed_stream_id, _)); + stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD); + QuicString msg = + QuicStrCat("Marking unknown stream ", closed_stream_id, " blocked."); + EXPECT_QUIC_BUG(session_.MarkConnectionLevelWriteBlocked(closed_stream_id), + msg); +} + +TEST_P(QuicSpdySessionTestServer, OnCanWrite) { + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + InSequence s; + + // Reregister, to test the loop limit. + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + // 2 will get called a second time as it didn't finish its block + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { + session_.SendStreamData(stream6); + })); + // 4 will not get called, as we exceeded the loop limit. + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSpdySessionTestServer, TestBatchedWrites) { + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.set_writev_consumes_all_data(true); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + // With two sessions blocked, we should get two write calls. They should both + // go to the first stream as it will only write 6k and mark itself blocked + // again. + InSequence s; + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + session_.OnCanWrite(); + + // We should get one more call for stream2, at which point it has used its + // write quota and we move over to stream 4. + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendLargeFakeData(stream4, 6000); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + })); + session_.OnCanWrite(); + + // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high + // priority stream 6. 4 should be preempted. 6 will write but *not* block so + // will cede back to 4. + stream6->SetPriority(kV3HighestPriority); + EXPECT_CALL(*stream4, OnCanWrite()) + .WillOnce(Invoke([this, stream4, stream6]() { + session_.SendLargeFakeData(stream4, 6000); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + })); + EXPECT_CALL(*stream6, OnCanWrite()) + .WillOnce(Invoke([this, stream4, stream6]() { + session_.SendStreamData(stream6); + session_.SendLargeFakeData(stream4, 6000); + })); + session_.OnCanWrite(); + + // Stream4 alread did 6k worth of writes, so after doing another 12k it should + // cede and 2 should resume. + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendLargeFakeData(stream4, 12000); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + })); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + session_.OnCanWrite(); +} + +TEST_P(QuicSpdySessionTestServer, OnCanWriteBundlesStreams) { + if (IsVersion99()) { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke( + this, &QuicSpdySessionTestServer::ClearMaxStreamIdControlFrame)); + } + // Encryption needs to be established before data can be sent. + CryptoHandshakeMessage msg; + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*send_algorithm, GetCongestionWindow()) + .WillRepeatedly(Return(kMaxPacketSize * 10)); + EXPECT_CALL(*send_algorithm, InRecovery()).WillRepeatedly(Return(false)); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + })); + EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { + session_.SendStreamData(stream6); + })); + + // Expect that we only send one packet, the writes from different streams + // should be bundled together. + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _)); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSpdySessionTestServer, OnCanWriteCongestionControlBlocks) { + session_.set_writev_consumes_all_data(true); + InSequence s; + + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { + session_.SendStreamData(stream6); + })); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false)); + // stream4->OnCanWrite is not called. + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // Still congestion-control blocked. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false)); + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // stream4->OnCanWrite is called once the connection stops being + // congestion-control blocked. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + })); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSpdySessionTestServer, OnCanWriteWriterBlocks) { + // Drive congestion control manually in order to ensure that + // application-limited signaling is handled correctly. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); + + // Drive packet writer manually. + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true)); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)).Times(0); + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + + EXPECT_CALL(*stream2, OnCanWrite()).Times(0); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)).Times(0); + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSpdySessionTestServer, BufferedHandshake) { + session_.set_writev_consumes_all_data(true); + EXPECT_FALSE(session_.HasPendingHandshake()); // Default value. + + // Test that blocking other streams does not change our status. + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + EXPECT_FALSE(session_.HasPendingHandshake()); + + TestStream* stream3 = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream3->id()); + EXPECT_FALSE(session_.HasPendingHandshake()); + + // Blocking (due to buffering of) the Crypto stream is detected. + session_.MarkConnectionLevelWriteBlocked( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); + EXPECT_TRUE(session_.HasPendingHandshake()); + + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + EXPECT_TRUE(session_.HasPendingHandshake()); + + InSequence s; + // Force most streams to re-register, which is common scenario when we block + // the Crypto stream, and only the crypto stream can "really" write. + + // Due to prioritization, we *should* be asked to write the crypto stream + // first. + // Don't re-register the crypto stream (which signals complete writing). + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_CALL(*crypto_stream, OnCanWrite()); + + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(Invoke([this, stream3]() { + session_.SendStreamData(stream3); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + })); + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote. +} + +TEST_P(QuicSpdySessionTestServer, OnCanWriteWithClosedStream) { + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + CloseStream(stream6->id()); + + InSequence s; + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + })); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSpdySessionTestServer, + OnCanWriteLimitsNumWritesIfFlowControlBlocked) { + // Drive congestion control manually in order to ensure that + // application-limited signaling is handled correctly. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); + + // Ensure connection level flow control blockage. + QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); + EXPECT_TRUE(session_.flow_controller()->IsBlocked()); + EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + + // Mark the crypto and headers streams as write blocked, we expect them to be + // allowed to write later. + session_.MarkConnectionLevelWriteBlocked( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); + + // Create a data stream, and although it is write blocked we never expect it + // to be allowed to write as we are connection level flow control blocked. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream->id()); + EXPECT_CALL(*stream, OnCanWrite()).Times(0); + + // The crypto and headers streams should be called even though we are + // connection flow control blocked. + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_CALL(*crypto_stream, OnCanWrite()); + QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr); + TestHeadersStream* headers_stream = new TestHeadersStream(&session_); + QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); + session_.MarkConnectionLevelWriteBlocked( + QuicUtils::GetHeadersStreamId(connection_->transport_version())); + EXPECT_CALL(*headers_stream, OnCanWrite()); + + // After the crypto and header streams perform a write, the connection will be + // blocked by the flow control, hence it should become application-limited. + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSpdySessionTestServer, SendGoAway) { + if (IsVersion99()) { + // GoAway frames are not in version 99 + return; + } + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallySendControlFrame)); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); + EXPECT_TRUE(session_.goaway_sent()); + + const QuicStreamId kTestStreamId = 5u; + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + EXPECT_CALL(*connection_, + OnStreamReset(kTestStreamId, QUIC_STREAM_PEER_GOING_AWAY)) + .Times(0); + EXPECT_TRUE(session_.GetOrCreateDynamicStream(kTestStreamId)); +} + +TEST_P(QuicSpdySessionTestServer, DoNotSendGoAwayTwice) { + if (IsVersion99()) { + // TODO(b/118808809): Enable this test for version 99 when GOAWAY is + // supported. + return; + } + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); + EXPECT_TRUE(session_.goaway_sent()); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); +} + +TEST_P(QuicSpdySessionTestServer, InvalidGoAway) { + if (IsVersion99()) { + // TODO(b/118808809): Enable this test for version 99 when GOAWAY is + // supported. + return; + } + QuicGoAwayFrame go_away(kInvalidControlFrameId, QUIC_PEER_GOING_AWAY, + session_.next_outgoing_bidirectional_stream_id(), ""); + session_.OnGoAway(go_away); +} + +// Test that server session will send a connectivity probe in response to a +// connectivity probe on the same path. +TEST_P(QuicSpdySessionTestServer, ServerReplyToConnecitivityProbe) { + QuicSocketAddress old_peer_address = + QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort); + EXPECT_EQ(old_peer_address, session_.peer_address()); + + QuicSocketAddress new_peer_address = + QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort + 1); + + EXPECT_CALL(*connection_, + SendConnectivityProbingResponsePacket(new_peer_address)); + if (IsVersion99()) { + // Need to explicitly do this to emulate the reception of a PathChallenge, + // which stores its payload for use in generating the response. + connection_->OnPathChallengeFrame( + QuicPathChallengeFrame(0, {{0, 1, 2, 3, 4, 5, 6, 7}})); + } + session_.OnConnectivityProbeReceived(session_.self_address(), + new_peer_address); + EXPECT_EQ(old_peer_address, session_.peer_address()); +} + +TEST_P(QuicSpdySessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { + EXPECT_EQ(kInitialIdleTimeoutSecs + 3, + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); + CryptoHandshakeMessage msg; + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + EXPECT_EQ(kMaximumIdleTimeoutSecs + 3, + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); +} + +TEST_P(QuicSpdySessionTestServer, RstStreamBeforeHeadersDecompressed) { + // Send two bytes of payload. + QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams()); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + if (!IsVersion99()) { + // For version99, OnStreamReset gets called because of the STOP_SENDING, + // below. EXPECT the call there. + EXPECT_CALL(*connection_, + OnStreamReset(GetNthClientInitiatedBidirectionalId(0), _)); + } + QuicRstStreamFrame rst1(kInvalidControlFrameId, + GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM, 0); + session_.OnRstStream(rst1); + + // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a + // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a + // one-way close. + if (IsVersion99()) { + // Only needed for version 99/IETF QUIC. + QuicStopSendingFrame stop_sending( + kInvalidControlFrameId, GetNthClientInitiatedBidirectionalId(0), + static_cast<QuicApplicationErrorCode>(QUIC_ERROR_PROCESSING_STREAM)); + // Expect the RESET_STREAM that is generated in response to receiving a + // STOP_SENDING. + EXPECT_CALL(*connection_, + OnStreamReset(GetNthClientInitiatedBidirectionalId(0), + QUIC_ERROR_PROCESSING_STREAM)); + session_.OnStopSendingFrame(stop_sending); + } + + EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); + // Connection should remain alive. + EXPECT_TRUE(connection_->connected()); +} + +TEST_P(QuicSpdySessionTestServer, OnStreamFrameFinStaticStreamId) { + // Send two bytes of payload. + QuicStreamFrame data1( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), true, 0, + QuicStringPiece("HT")); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Attempt to close a static stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnStreamFrame(data1); +} + +TEST_P(QuicSpdySessionTestServer, OnRstStreamStaticStreamId) { + // Send two bytes of payload. + QuicRstStreamFrame rst1( + kInvalidControlFrameId, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + QUIC_ERROR_PROCESSING_STREAM, 0); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnRstStream(rst1); +} + +TEST_P(QuicSpdySessionTestServer, OnStreamFrameInvalidStreamId) { + // Send two bytes of payload. + QuicStreamFrame data1( + QuicUtils::GetInvalidStreamId(connection_->transport_version()), true, 0, + QuicStringPiece("HT")); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Recevied data for an invalid stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnStreamFrame(data1); +} + +TEST_P(QuicSpdySessionTestServer, OnRstStreamInvalidStreamId) { + // Send two bytes of payload. + QuicRstStreamFrame rst1( + kInvalidControlFrameId, + QuicUtils::GetInvalidStreamId(connection_->transport_version()), + QUIC_ERROR_PROCESSING_STREAM, 0); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Recevied data for an invalid stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnRstStream(rst1); +} + +TEST_P(QuicSpdySessionTestServer, HandshakeUnblocksFlowControlBlockedStream) { + // Test that if a stream is flow control blocked, then on receipt of the SHLO + // containing a suitable send window offset, the stream becomes unblocked. + + // Ensure that Writev consumes all the data it is given (simulate no socket + // blocking). + session_.set_writev_consumes_all_data(true); + + // Create a stream, and send enough data to make it flow control blocked. + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicString body(kMinimumFlowControlSendWindow, '.'); + EXPECT_FALSE(stream2->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AtLeast(1)); + stream2->WriteOrBufferBody(body, false); + EXPECT_TRUE(stream2->flow_controller()->IsBlocked()); + EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); + EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); + + // Now complete the crypto handshake, resulting in an increased flow control + // send window. + CryptoHandshakeMessage msg; + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(&session_, stream2->id())); + // Stream is now unblocked. + EXPECT_FALSE(stream2->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); +} + +TEST_P(QuicSpdySessionTestServer, + HandshakeUnblocksFlowControlBlockedCryptoStream) { + if (GetParam().transport_version >= QUIC_VERSION_47) { + // QUIC version 47 onwards uses CRYPTO frames for the handshake, so this + // test doesn't make sense for those versions. + return; + } + // Test that if the crypto stream is flow control blocked, then if the SHLO + // contains a larger send window offset, the stream becomes unblocked. + session_.set_writev_consumes_all_data(true); + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + QuicHeadersStream* headers_stream = + QuicSpdySessionPeer::GetHeadersStream(&session_); + EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + if (IsVersion99()) { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + } else { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + } + for (QuicStreamId i = 0; + !crypto_stream->flow_controller()->IsBlocked() && i < 1000u; i++) { + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + QuicStreamOffset offset = crypto_stream->stream_bytes_written(); + QuicConfig config; + CryptoHandshakeMessage crypto_message; + config.ToHandshakeMessage(&crypto_message); + crypto_stream->SendHandshakeMessage(crypto_message); + char buf[1000]; + QuicDataWriter writer(1000, buf, NETWORK_BYTE_ORDER); + crypto_stream->WriteStreamData(offset, crypto_message.size(), &writer); + } + EXPECT_TRUE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); + EXPECT_FALSE(session_.HasDataToWrite()); + EXPECT_TRUE(crypto_stream->HasBufferedData()); + + // Now complete the crypto handshake, resulting in an increased flow control + // send window. + CryptoHandshakeMessage msg; + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked( + &session_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()))); + // Stream is now unblocked and will no longer have buffered data. + EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); +} + +#if !defined(OS_IOS) +// This test is failing flakily for iOS bots. +// http://crbug.com/425050 +// NOTE: It's not possible to use the standard MAYBE_ convention to disable +// this test on iOS because when this test gets instantiated it ends up with +// various names that are dependent on the parameters passed. +TEST_P(QuicSpdySessionTestServer, + HandshakeUnblocksFlowControlBlockedHeadersStream) { + // Test that if the header stream is flow control blocked, then if the SHLO + // contains a larger send window offset, the stream becomes unblocked. + session_.set_writev_consumes_all_data(true); + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + QuicHeadersStream* headers_stream = + QuicSpdySessionPeer::GetHeadersStream(&session_); + EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + QuicStreamId stream_id = 5; + // Write until the header stream is flow control blocked. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + SpdyHeaderBlock headers; + SimpleRandom random; + while (!headers_stream->flow_controller()->IsBlocked() && stream_id < 2000) { + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + headers["header"] = QuicStrCat(random.RandUint64(), random.RandUint64(), + random.RandUint64()); + session_.WriteHeadersOnHeadersStream(stream_id, headers.Clone(), true, 0, + nullptr); + stream_id += IdDelta(); + } + // Write once more to ensure that the headers stream has buffered data. The + // random headers may have exactly filled the flow control window. + session_.WriteHeadersOnHeadersStream(stream_id, std::move(headers), true, 0, + nullptr); + EXPECT_TRUE(headers_stream->HasBufferedData()); + + EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); + EXPECT_FALSE(session_.HasDataToWrite()); + + // Now complete the crypto handshake, resulting in an increased flow control + // send window. + CryptoHandshakeMessage msg; + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + + // Stream is now unblocked and will no longer have buffered data. + EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + EXPECT_TRUE(headers_stream->HasBufferedData()); + EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked( + &session_, + QuicUtils::GetHeadersStreamId(connection_->transport_version()))); +} +#endif // !defined(OS_IOS) + +TEST_P(QuicSpdySessionTestServer, + ConnectionFlowControlAccountingRstOutOfOrder) { + // Test that when we receive an out of order stream RST we correctly adjust + // our connection level flow control receive window. + // On close, the stream should mark as consumed all bytes between the highest + // byte consumed so far and the final byte offset from the RST frame. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + + const QuicStreamOffset kByteOffset = + 1 + kInitialSessionFlowControlWindowForTest / 2; + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + if (!IsVersion99()) { + // For version99 the call to OnStreamReset happens as a result of receiving + // the STOP_SENDING, so set up the EXPECT there. + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + } + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(), + QUIC_STREAM_CANCELLED, kByteOffset); + session_.OnRstStream(rst_frame); + // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a + // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a + // one-way close. + if (IsVersion99()) { + // Only needed for version 99/IETF QUIC. + QuicStopSendingFrame stop_sending( + kInvalidControlFrameId, stream->id(), + static_cast<QuicApplicationErrorCode>(QUIC_STREAM_CANCELLED)); + // Expect the RESET_STREAM that is generated in response to receiving a + // STOP_SENDING. + EXPECT_CALL(*connection_, + OnStreamReset(stream->id(), QUIC_STREAM_CANCELLED)); + session_.OnStopSendingFrame(stop_sending); + } + + EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); +} + +TEST_P(QuicSpdySessionTestServer, + ConnectionFlowControlAccountingFinAndLocalReset) { + // Test the situation where we receive a FIN on a stream, and before we fully + // consume all the data from the sequencer buffer we locally RST the stream. + // The bytes between highest consumed byte, and the final byte offset that we + // determined when the FIN arrived, should be marked as consumed at the + // connection level flow controller when the stream is reset. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + + const QuicStreamOffset kByteOffset = + kInitialSessionFlowControlWindowForTest / 2 - 1; + QuicStreamFrame frame(stream->id(), true, kByteOffset, "."); + session_.OnStreamFrame(frame); + EXPECT_TRUE(connection_->connected()); + + EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed()); + EXPECT_EQ(kByteOffset + frame.data_length, + stream->flow_controller()->highest_received_byte_offset()); + + // Reset stream locally. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + EXPECT_EQ(kByteOffset + frame.data_length, + session_.flow_controller()->bytes_consumed()); +} + +TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingFinAfterRst) { + // Test that when we RST the stream (and tear down stream state), and then + // receive a FIN from the peer, we correctly adjust our connection level flow + // control receive window. + + // Connection starts with some non-zero highest received byte offset, + // due to other active streams. + const uint64_t kInitialConnectionBytesConsumed = 567; + const uint64_t kInitialConnectionHighestReceivedOffset = 1234; + EXPECT_LT(kInitialConnectionBytesConsumed, + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->UpdateHighestReceivedOffset( + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); + + // Reset our stream: this results in the stream being closed locally. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + + // Now receive a response from the peer with a FIN. We should handle this by + // adjusting the connection level flow control receive window to take into + // account the total number of bytes sent by the peer. + const QuicStreamOffset kByteOffset = 5678; + QuicString body = "hello"; + QuicStreamFrame frame(stream->id(), true, kByteOffset, QuicStringPiece(body)); + session_.OnStreamFrame(frame); + + QuicStreamOffset total_stream_bytes_sent_by_peer = + kByteOffset + body.length(); + EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer, + session_.flow_controller()->bytes_consumed()); + EXPECT_EQ( + kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer, + session_.flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingRstAfterRst) { + // Test that when we RST the stream (and tear down stream state), and then + // receive a RST from the peer, we correctly adjust our connection level flow + // control receive window. + + // Connection starts with some non-zero highest received byte offset, + // due to other active streams. + const uint64_t kInitialConnectionBytesConsumed = 567; + const uint64_t kInitialConnectionHighestReceivedOffset = 1234; + EXPECT_LT(kInitialConnectionBytesConsumed, + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->UpdateHighestReceivedOffset( + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); + + // Reset our stream: this results in the stream being closed locally. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream)); + + // Now receive a RST from the peer. We should handle this by adjusting the + // connection level flow control receive window to take into account the total + // number of bytes sent by the peer. + const QuicStreamOffset kByteOffset = 5678; + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(), + QUIC_STREAM_CANCELLED, kByteOffset); + session_.OnRstStream(rst_frame); + + EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset, + session_.flow_controller()->bytes_consumed()); + EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset, + session_.flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicSpdySessionTestServer, InvalidStreamFlowControlWindowInHandshake) { + // Test that receipt of an invalid (< default) stream flow control window from + // the peer results in the connection being torn down. + const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1; + QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(), + kInvalidWindow); + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _)); + session_.OnConfigNegotiated(); +} + +TEST_P(QuicSpdySessionTestServer, InvalidSessionFlowControlWindowInHandshake) { + // Test that receipt of an invalid (< default) session flow control window + // from the peer results in the connection being torn down. + const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1; + QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(), + kInvalidWindow); + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _)); + session_.OnConfigNegotiated(); +} + +// Test negotiation of custom server initial flow control window. +TEST_P(QuicSpdySessionTestServer, CustomFlowControlWindow) { + QuicTagVector copt; + copt.push_back(kIFW7); + QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); + + session_.OnConfigNegotiated(); + EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize( + session_.flow_controller())); +} + +TEST_P(QuicSpdySessionTestServer, FlowControlWithInvalidFinalOffset) { + // Test that if we receive a stream RST with a highest byte offset that + // violates flow control, that we close the connection. + const uint64_t kLargeOffset = kInitialSessionFlowControlWindowForTest + 1; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)) + .Times(2); + + // Check that stream frame + FIN results in connection close. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + QuicStreamFrame frame(stream->id(), true, kLargeOffset, QuicStringPiece()); + session_.OnStreamFrame(frame); + + // Check that RST results in connection close. + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(), + QUIC_STREAM_CANCELLED, kLargeOffset); + session_.OnRstStream(rst_frame); +} + +TEST_P(QuicSpdySessionTestServer, WindowUpdateUnblocksHeadersStream) { + // Test that a flow control blocked headers stream gets unblocked on recipt of + // a WINDOW_UPDATE frame. + + // Set the headers stream to be flow control blocked. + QuicHeadersStream* headers_stream = + QuicSpdySessionPeer::GetHeadersStream(&session_); + QuicFlowControllerPeer::SetSendWindowOffset(headers_stream->flow_controller(), + 0); + EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); + + // Unblock the headers stream by supplying a WINDOW_UPDATE. + QuicWindowUpdateFrame window_update_frame(kInvalidControlFrameId, + headers_stream->id(), + 2 * kMinimumFlowControlSendWindow); + session_.OnWindowUpdateFrame(window_update_frame); + EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); +} + +TEST_P(QuicSpdySessionTestServer, + TooManyUnfinishedStreamsCauseServerRejectStream) { + // If a buggy/malicious peer creates too many streams that are not ended + // with a FIN or RST then we send an RST to refuse streams for versions other + // than version 99. In version 99 the connection gets closed. + const QuicStreamId kMaxStreams = 5; + QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams); + const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0); + const QuicStreamId kFinalStreamId = + GetNthClientInitiatedBidirectionalId(kMaxStreams); + // Create kMaxStreams data streams, and close them all without receiving a + // FIN or a RST_STREAM from the client. + const QuicStreamId kNextId = + QuicUtils::StreamIdDelta(connection_->transport_version()); + for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += kNextId) { + QuicStreamFrame data1(i, false, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + // EXPECT_EQ(1u, session_.GetNumOpenStreams()); + if (!IsVersion99()) { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + } else { + // V99 has two frames, RST_STREAM and STOP_SENDING + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + } + // Close the stream only if not version 99. If we are version 99 + // then closing the stream opens up the available stream id space, + // so we never bump into the limit. + EXPECT_CALL(*connection_, OnStreamReset(i, _)); + session_.CloseStream(i); + } + // Try and open a stream that exceeds the limit. + if (!IsVersion99()) { + // On versions other than 99, opening such a stream results in a + // RST_STREAM. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + EXPECT_CALL(*connection_, + OnStreamReset(kFinalStreamId, QUIC_REFUSED_STREAM)) + .Times(1); + } else { + // On version 99 opening such a stream results in a connection close. + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, + "Stream id 28 above 24", _)); + } + // Create one more data streams to exceed limit of open stream. + QuicStreamFrame data1(kFinalStreamId, false, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); +} + +TEST_P(QuicSpdySessionTestServer, DrainingStreamsDoNotCountAsOpened) { + // Verify that a draining stream (which has received a FIN but not consumed + // it) does not count against the open quota (because it is closed from the + // protocol point of view). + if (IsVersion99()) { + // Version 99 will result in a MAX_STREAM_ID frame as streams are consumed + // (via the OnStreamFrame call) and then released (via + // StreamDraining). Eventually this node will believe that the peer is + // running low on available stream ids and then send a MAX_STREAM_ID frame, + // caught by this EXPECT_CALL. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + } else { + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + } + EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)).Times(0); + const QuicStreamId kMaxStreams = 5; + QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams); + + // Create kMaxStreams + 1 data streams, and mark them draining. + const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0); + const QuicStreamId kFinalStreamId = + GetNthClientInitiatedBidirectionalId(kMaxStreams + 1); + for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += IdDelta()) { + QuicStreamFrame data1(i, true, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams()); + session_.StreamDraining(i); + EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); + } +} + +class QuicSpdySessionTestClient : public QuicSpdySessionTestBase { + protected: + QuicSpdySessionTestClient() + : QuicSpdySessionTestBase(Perspective::IS_CLIENT) {} +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicSpdySessionTestClient, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicSpdySessionTestClient, AvailableStreamsClient) { + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedBidirectionalId(2)) != nullptr); + // Both server initiated streams with smaller stream IDs should be available. + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthServerInitiatedBidirectionalId(0))); + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthServerInitiatedBidirectionalId(1))); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedBidirectionalId(0)) != nullptr); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedBidirectionalId(1)) != nullptr); + // And client initiated stream ID should be not available. + EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedBidirectionalId(0))); +} + +TEST_P(QuicSpdySessionTestClient, RecordFinAfterReadSideClosed) { + // Verify that an incoming FIN is recorded in a stream object even if the read + // side has been closed. This prevents an entry from being made in + // locally_closed_streams_highest_offset_ (which will never be deleted). + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + QuicStreamId stream_id = stream->id(); + + // Close the read side manually. + QuicStreamPeer::CloseReadSide(stream); + + // Receive a stream data frame with FIN. + QuicStreamFrame frame(stream_id, true, 0, QuicStringPiece()); + session_.OnStreamFrame(frame); + EXPECT_TRUE(stream->fin_received()); + + // Reset stream locally. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream)); + + EXPECT_TRUE(connection_->connected()); + EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id)); + EXPECT_FALSE(QuicSessionPeer::IsStreamCreated(&session_, stream_id)); + + // The stream is not waiting for the arrival of the peer's final offset as it + // was received with the FIN earlier. + EXPECT_EQ( + 0u, + QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(&session_).size()); +} + +TEST_P(QuicSpdySessionTestClient, WritePriority) { + QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr); + TestHeadersStream* headers_stream = new TestHeadersStream(&session_); + QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); + + // Make packet writer blocked so |headers_stream| will buffer its write data. + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true)); + + const QuicStreamId id = 4; + const QuicStreamId parent_stream_id = 9; + const SpdyPriority priority = kV3HighestPriority; + const bool exclusive = true; + session_.WritePriority(id, parent_stream_id, + Spdy3PriorityToHttp2Weight(priority), exclusive); + + QuicStreamSendBuffer& send_buffer = + QuicStreamPeer::SendBuffer(headers_stream); + if (transport_version() > QUIC_VERSION_39) { + ASSERT_EQ(1u, send_buffer.size()); + + SpdyPriorityIR priority_frame( + id, parent_stream_id, Spdy3PriorityToHttp2Weight(priority), exclusive); + SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION); + SpdySerializedFrame frame = spdy_framer.SerializeFrame(priority_frame); + + const QuicMemSlice& slice = + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer)->slice; + EXPECT_EQ(QuicStringPiece(frame.data(), frame.size()), + QuicStringPiece(slice.data(), slice.length())); + } else { + EXPECT_EQ(0u, send_buffer.size()); + } +} + +TEST_P(QuicSpdySessionTestServer, ZombieStreams) { + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicStreamPeer::SetStreamBytesWritten(3, stream2); + EXPECT_TRUE(stream2->IsWaitingForAcks()); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _)); + session_.CloseStream(stream2->id()); + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + ASSERT_EQ(1u, session_.closed_streams()->size()); + EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id()); + session_.OnStreamDoneWaitingForAcks(2); + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + EXPECT_EQ(1u, session_.closed_streams()->size()); + EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id()); +} + +TEST_P(QuicSpdySessionTestServer, OnStreamFrameLost) { + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + InSequence s; + + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + + QuicStreamFrame frame1( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), false, 0, + 1300); + QuicStreamFrame frame2(stream2->id(), false, 0, 9); + QuicStreamFrame frame3(stream4->id(), false, 0, 9); + + // Lost data on cryption stream, streams 2 and 4. + EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true)); + if (connection_->transport_version() < QUIC_VERSION_47) { + EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) + .WillOnce(Return(true)); + } + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true)); + session_.OnFrameLost(QuicFrame(frame3)); + if (connection_->transport_version() < QUIC_VERSION_47) { + session_.OnFrameLost(QuicFrame(frame1)); + } else { + QuicCryptoFrame crypto_frame(ENCRYPTION_NONE, 0, 1300); + session_.OnFrameLost(QuicFrame(&crypto_frame)); + } + session_.OnFrameLost(QuicFrame(frame2)); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // Mark streams 2 and 4 write blocked. + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + // Lost data is retransmitted before new data, and retransmissions for crypto + // stream go first. + // Do not check congestion window when crypto stream has lost data. + EXPECT_CALL(*send_algorithm, CanSend(_)).Times(0); + if (connection_->transport_version() < QUIC_VERSION_47) { + EXPECT_CALL(*crypto_stream, OnCanWrite()); + EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) + .WillOnce(Return(false)); + } + // Check congestion window for non crypto streams. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream4, OnCanWrite()); + EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(false)); + // Connection is blocked. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false)); + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // Unblock connection. + // Stream 2 retransmits lost data. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false)); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + // Stream 2 sends new data. + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream4, OnCanWrite()); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSpdySessionTestServer, DonotRetransmitDataOfClosedStreams) { + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + InSequence s; + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + QuicStreamFrame frame1(stream2->id(), false, 0, 9); + QuicStreamFrame frame2(stream4->id(), false, 0, 9); + QuicStreamFrame frame3(stream6->id(), false, 0, 9); + + EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(true)); + EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true)); + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true)); + session_.OnFrameLost(QuicFrame(frame3)); + session_.OnFrameLost(QuicFrame(frame2)); + session_.OnFrameLost(QuicFrame(frame1)); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + + // Reset stream 4 locally. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream4->id(), _)); + stream4->Reset(QUIC_STREAM_CANCELLED); + + // Verify stream 4 is removed from streams with lost data list. + EXPECT_CALL(*stream6, OnCanWrite()); + EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(false)); + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false)); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream6, OnCanWrite()); + session_.OnCanWrite(); +} + +TEST_P(QuicSpdySessionTestServer, RetransmitFrames) { + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + InSequence s; + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + session_.SendWindowUpdate(stream2->id(), 9); + + QuicStreamFrame frame1(stream2->id(), false, 0, 9); + QuicStreamFrame frame2(stream4->id(), false, 0, 9); + QuicStreamFrame frame3(stream6->id(), false, 0, 9); + QuicWindowUpdateFrame window_update(1, stream2->id(), 9); + QuicFrames frames; + frames.push_back(QuicFrame(frame1)); + frames.push_back(QuicFrame(&window_update)); + frames.push_back(QuicFrame(frame2)); + frames.push_back(QuicFrame(frame3)); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); + + EXPECT_CALL(*stream2, RetransmitStreamData(_, _, _)).WillOnce(Return(true)); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*stream4, RetransmitStreamData(_, _, _)).WillOnce(Return(true)); + EXPECT_CALL(*stream6, RetransmitStreamData(_, _, _)).WillOnce(Return(true)); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + session_.RetransmitFrames(frames, TLP_RETRANSMISSION); +} + +TEST_P(QuicSpdySessionTestServer, OnPriorityFrame) { + QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); + TestStream* stream = session_.CreateIncomingStream(stream_id); + session_.OnPriorityFrame(stream_id, kV3HighestPriority); + EXPECT_EQ(kV3HighestPriority, stream->priority()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc new file mode 100644 index 0000000..9e9f1b7 --- /dev/null +++ b/quic/core/http/quic_spdy_stream.cc
@@ -0,0 +1,623 @@ +// Copyright 2013 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/http/quic_spdy_stream.h" + +#include <utility> + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.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" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" + +using spdy::SpdyHeaderBlock; +using spdy::SpdyPriority; + +namespace quic { + +// Visitor of HttpDecoder that passes data frame to QuicSpdyStream and closes +// the connection on unexpected frames. +class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { + public: + explicit HttpDecoderVisitor(QuicSpdyStream* stream) : stream_(stream) {} + HttpDecoderVisitor(const HttpDecoderVisitor&) = delete; + HttpDecoderVisitor& operator=(const HttpDecoderVisitor&) = delete; + + void OnError(HttpDecoder* decoder) override { + stream_->session()->connection()->CloseConnection( + QUIC_HTTP_DECODER_ERROR, "Http decoder internal error", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } + + void OnPriorityFrame(const PriorityFrame& frame) override { + CloseConnectionOnWrongFrame("Priority"); + } + + void OnCancelPushFrame(const CancelPushFrame& frame) override { + CloseConnectionOnWrongFrame("Cancel Push"); + } + + void OnMaxPushIdFrame(const MaxPushIdFrame& frame) override { + CloseConnectionOnWrongFrame("Max Push Id"); + } + + void OnGoAwayFrame(const GoAwayFrame& frame) override { + CloseConnectionOnWrongFrame("Goaway"); + } + + void OnSettingsFrame(const SettingsFrame& frame) override { + CloseConnectionOnWrongFrame("Settings"); + } + + void OnDuplicatePushFrame(const DuplicatePushFrame& frame) override { + CloseConnectionOnWrongFrame("Duplicate Push"); + } + + void OnDataFrameStart(Http3FrameLengths frame_lengths) override { + stream_->OnDataFrameStart(frame_lengths); + } + + void OnDataFramePayload(QuicStringPiece payload) override { + stream_->OnDataFramePayload(payload); + } + + void OnDataFrameEnd() override { stream_->OnDataFrameEnd(); } + + void OnHeadersFrameStart() override { + CloseConnectionOnWrongFrame("Headers"); + } + + void OnHeadersFramePayload(QuicStringPiece payload) override { + CloseConnectionOnWrongFrame("Headers"); + } + + void OnHeadersFrameEnd(QuicByteCount frame_len) override { + CloseConnectionOnWrongFrame("Headers"); + } + + void OnPushPromiseFrameStart(PushId push_id) override { + CloseConnectionOnWrongFrame("Push Promise"); + } + + void OnPushPromiseFramePayload(QuicStringPiece payload) override { + CloseConnectionOnWrongFrame("Push Promise"); + } + + void OnPushPromiseFrameEnd() override { + CloseConnectionOnWrongFrame("Push Promise"); + } + + private: + void CloseConnectionOnWrongFrame(QuicString frame_type) { + stream_->session()->connection()->CloseConnection( + QUIC_HTTP_DECODER_ERROR, frame_type + " frame received on data stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } + + QuicSpdyStream* stream_; +}; + +#define ENDPOINT \ + (session()->perspective() == Perspective::IS_SERVER ? "Server: " \ + : "Client:" \ + " ") + +QuicSpdyStream::QuicSpdyStream(QuicStreamId id, + QuicSpdySession* spdy_session, + StreamType type) + : QuicStream(id, spdy_session, /*is_static=*/false, type), + spdy_session_(spdy_session), + visitor_(nullptr), + headers_decompressed_(false), + trailers_decompressed_(false), + trailers_consumed_(false), + http_decoder_visitor_(new HttpDecoderVisitor(this)), + body_buffer_(sequencer()), + ack_listener_(nullptr) { + DCHECK_NE(QuicUtils::GetCryptoStreamId( + spdy_session->connection()->transport_version()), + id); + // Don't receive any callbacks from the sequencer until headers + // are complete. + sequencer()->SetBlockedUntilFlush(); + + if (VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version())) { + sequencer()->set_level_triggered(true); + } + decoder_.set_visitor(http_decoder_visitor_.get()); +} + +QuicSpdyStream::QuicSpdyStream(PendingStream pending, + QuicSpdySession* spdy_session, + StreamType type) + : QuicStream(std::move(pending), type), + spdy_session_(spdy_session), + visitor_(nullptr), + headers_decompressed_(false), + trailers_decompressed_(false), + trailers_consumed_(false), + http_decoder_visitor_(new HttpDecoderVisitor(this)), + body_buffer_(sequencer()), + ack_listener_(nullptr) { + DCHECK_NE(QuicUtils::GetCryptoStreamId( + spdy_session->connection()->transport_version()), + id()); + // Don't receive any callbacks from the sequencer until headers + // are complete. + sequencer()->SetBlockedUntilFlush(); + + if (VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version())) { + sequencer()->set_level_triggered(true); + } + decoder_.set_visitor(http_decoder_visitor_.get()); +} + +QuicSpdyStream::~QuicSpdyStream() {} + +size_t QuicSpdyStream::WriteHeaders( + SpdyHeaderBlock header_block, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + size_t bytes_written = + WriteHeadersImpl(std::move(header_block), fin, std::move(ack_listener)); + if (fin) { + // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent. + set_fin_sent(true); + CloseWriteSide(); + } + return bytes_written; +} + +void QuicSpdyStream::WriteOrBufferBody(QuicStringPiece data, bool fin) { + if (!VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version()) || + data.length() == 0) { + WriteOrBufferData(data, fin, nullptr); + return; + } + QuicConnection::ScopedPacketFlusher flusher( + spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING); + + // Write frame header. + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(data.length(), &buffer); + unacked_frame_headers_offsets_.Add( + send_buffer().stream_offset(), + send_buffer().stream_offset() + header_length); + QUIC_DLOG(INFO) << "Stream " << id() << " is writing header of length " + << header_length; + WriteOrBufferData(QuicStringPiece(buffer.get(), header_length), false, + nullptr); + + // Write body. + QUIC_DLOG(INFO) << "Stream " << id() << " is writing body of length " + << data.length(); + WriteOrBufferData(data, fin, nullptr); +} + +size_t QuicSpdyStream::WriteTrailers( + SpdyHeaderBlock trailer_block, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + if (fin_sent()) { + QUIC_BUG << "Trailers cannot be sent after a FIN, on stream " << id(); + return 0; + } + + // The header block must contain the final offset for this stream, as the + // trailers may be processed out of order at the peer. + QUIC_DLOG(INFO) << "Inserting trailer: (" << kFinalOffsetHeaderKey << ", " + << stream_bytes_written() + BufferedDataBytes() << ")"; + trailer_block.insert( + std::make_pair(kFinalOffsetHeaderKey, + QuicTextUtils::Uint64ToString(stream_bytes_written() + + BufferedDataBytes()))); + + // Write the trailing headers with a FIN, and close stream for writing: + // trailers are the last thing to be sent on a stream. + const bool kFin = true; + size_t bytes_written = + WriteHeadersImpl(std::move(trailer_block), kFin, std::move(ack_listener)); + set_fin_sent(kFin); + + // Trailers are the last thing to be sent on a stream, but if there is still + // queued data then CloseWriteSide() will cause it never to be sent. + if (BufferedDataBytes() == 0) { + CloseWriteSide(); + } + + return bytes_written; +} + +QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov, + int count, + bool fin) { + if (!GetQuicReloadableFlag(quic_call_write_mem_slices)) { + return WritevData(iov, count, fin); + } + QUIC_RELOADABLE_FLAG_COUNT(quic_call_write_mem_slices); + QuicMemSliceStorage storage( + iov, count, + session()->connection()->helper()->GetStreamSendBufferAllocator(), + GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size)); + return WriteBodySlices(storage.ToSpan(), fin); +} + +QuicConsumedData QuicSpdyStream::WriteBodySlices(QuicMemSliceSpan slices, + bool fin) { + if (!VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version()) || + slices.empty()) { + return WriteMemSlices(slices, fin); + } + + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(slices.total_length(), &buffer); + if (!CanWriteNewDataAfterData(header_length)) { + return {0, false}; + } + + QuicConnection::ScopedPacketFlusher flusher( + spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING); + + // Write frame header. + struct iovec header_iov = {static_cast<void*>(buffer.get()), header_length}; + QuicMemSliceStorage storage( + &header_iov, 1, + spdy_session_->connection()->helper()->GetStreamSendBufferAllocator(), + GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size)); + unacked_frame_headers_offsets_.Add( + send_buffer().stream_offset(), + send_buffer().stream_offset() + header_length); + QUIC_DLOG(INFO) << "Stream " << id() << " is writing header of length " + << header_length; + WriteMemSlices(storage.ToSpan(), false); + + // Write body. + QUIC_DLOG(INFO) << "Stream " << id() << " is writing body of length " + << slices.total_length(); + return WriteMemSlices(slices, fin); +} + +size_t QuicSpdyStream::Readv(const struct iovec* iov, size_t iov_len) { + DCHECK(FinishedReadingHeaders()); + if (!VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version())) { + return sequencer()->Readv(iov, iov_len); + } + return body_buffer_.ReadBody(iov, iov_len); +} + +int QuicSpdyStream::GetReadableRegions(iovec* iov, size_t iov_len) const { + DCHECK(FinishedReadingHeaders()); + if (!VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version())) { + return sequencer()->GetReadableRegions(iov, iov_len); + } + return body_buffer_.PeekBody(iov, iov_len); +} + +void QuicSpdyStream::MarkConsumed(size_t num_bytes) { + DCHECK(FinishedReadingHeaders()); + if (!VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version())) { + sequencer()->MarkConsumed(num_bytes); + return; + } + body_buffer_.MarkBodyConsumed(num_bytes); +} + +bool QuicSpdyStream::IsDoneReading() const { + bool done_reading_headers = FinishedReadingHeaders(); + bool done_reading_body = sequencer()->IsClosed(); + bool done_reading_trailers = FinishedReadingTrailers(); + return done_reading_headers && done_reading_body && done_reading_trailers; +} + +bool QuicSpdyStream::HasBytesToRead() const { + if (!VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version())) { + return sequencer()->HasBytesToRead(); + } + return body_buffer_.HasBytesToRead(); +} + +void QuicSpdyStream::MarkTrailersConsumed() { + trailers_consumed_ = true; +} + +uint64_t QuicSpdyStream::total_body_bytes_read() const { + if (VersionHasDataFrameHeader( + spdy_session_->connection()->transport_version())) { + return body_buffer_.total_body_bytes_received(); + } + return sequencer()->NumBytesConsumed(); +} + +void QuicSpdyStream::ConsumeHeaderList() { + header_list_.Clear(); + if (FinishedReadingHeaders()) { + sequencer()->SetUnblocked(); + } +} + +void QuicSpdyStream::OnStreamHeadersPriority(SpdyPriority priority) { + DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective()); + SetPriority(priority); +} + +void QuicSpdyStream::OnStreamHeaderList(bool fin, + size_t frame_len, + const QuicHeaderList& header_list) { + // The headers list avoid infinite buffering by clearing the headers list + // if the current headers are too large. So if the list is empty here + // then the headers list must have been too large, and the stream should + // be reset. + // TODO(rch): Use an explicit "headers too large" signal. An empty header list + // might be acceptable if it corresponds to a trailing header frame. + if (header_list.empty()) { + OnHeadersTooLarge(); + if (IsDoneReading()) { + return; + } + } + if (!headers_decompressed_) { + OnInitialHeadersComplete(fin, frame_len, header_list); + } else { + OnTrailingHeadersComplete(fin, frame_len, header_list); + } +} + +void QuicSpdyStream::OnHeadersTooLarge() { + Reset(QUIC_HEADERS_TOO_LARGE); +} + +void QuicSpdyStream::OnInitialHeadersComplete( + bool fin, + size_t /*frame_len*/, + const QuicHeaderList& header_list) { + headers_decompressed_ = true; + header_list_ = header_list; + if (fin) { + OnStreamFrame(QuicStreamFrame(id(), fin, 0, QuicStringPiece())); + } + if (FinishedReadingHeaders()) { + sequencer()->SetUnblocked(); + } +} + +void QuicSpdyStream::OnPromiseHeaderList( + QuicStreamId /* promised_id */, + size_t /* frame_len */, + const QuicHeaderList& /*header_list */) { + // To be overridden in QuicSpdyClientStream. Not supported on + // server side. + session()->connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, "Promise headers received by server", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +void QuicSpdyStream::OnTrailingHeadersComplete( + bool fin, + size_t /*frame_len*/, + const QuicHeaderList& header_list) { + DCHECK(!trailers_decompressed_); + if (fin_received()) { + QUIC_DLOG(ERROR) << "Received Trailers after FIN, on stream: " << id(); + session()->connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers after fin", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + if (!fin) { + QUIC_DLOG(ERROR) << "Trailers must have FIN set, on stream: " << id(); + session()->connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, "Fin missing from trailers", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + size_t final_byte_offset = 0; + if (!SpdyUtils::CopyAndValidateTrailers(header_list, &final_byte_offset, + &received_trailers_)) { + QUIC_DLOG(ERROR) << "Trailers for stream " << id() << " are malformed."; + session()->connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers are malformed", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + trailers_decompressed_ = true; + OnStreamFrame( + QuicStreamFrame(id(), fin, final_byte_offset, QuicStringPiece())); +} + +size_t QuicSpdyStream::WriteHeadersImpl( + spdy::SpdyHeaderBlock header_block, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + return spdy_session_->WriteHeadersOnHeadersStream( + id(), std::move(header_block), fin, priority(), std::move(ack_listener)); +} + +void QuicSpdyStream::OnPriorityFrame(SpdyPriority priority) { + DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective()); + SetPriority(priority); +} + +void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { + if (frame.error_code != QUIC_STREAM_NO_ERROR) { + QuicStream::OnStreamReset(frame); + return; + } + QUIC_DVLOG(1) << "Received QUIC_STREAM_NO_ERROR, not discarding response"; + set_rst_received(true); + MaybeIncreaseHighestReceivedOffset(frame.byte_offset); + set_stream_error(frame.error_code); + CloseWriteSide(); +} + +void QuicSpdyStream::OnDataAvailable() { + if (!VersionHasDataFrameHeader( + session()->connection()->transport_version())) { + OnBodyAvailable(); + return; + } + + iovec iov; + bool has_payload = false; + while (sequencer()->PrefetchNextRegion(&iov)) { + decoder_.ProcessInput(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len); + if (decoder_.has_payload()) { + has_payload = true; + } + } + + if (has_payload) { + OnBodyAvailable(); + return; + } + + if (sequencer()->IsClosed()) { + OnBodyAvailable(); + return; + } +} + +void QuicSpdyStream::OnClose() { + QuicStream::OnClose(); + + if (visitor_) { + Visitor* visitor = visitor_; + // Calling Visitor::OnClose() may result the destruction of the visitor, + // so we need to ensure we don't call it again. + visitor_ = nullptr; + visitor->OnClose(this); + } +} + +void QuicSpdyStream::OnCanWrite() { + QuicStream::OnCanWrite(); + + // Trailers (and hence a FIN) may have been sent ahead of queued body bytes. + if (!HasBufferedData() && fin_sent()) { + CloseWriteSide(); + } +} + +bool QuicSpdyStream::FinishedReadingHeaders() const { + return headers_decompressed_ && header_list_.empty(); +} + +bool QuicSpdyStream::ParseHeaderStatusCode(const SpdyHeaderBlock& header, + int* status_code) const { + SpdyHeaderBlock::const_iterator it = header.find(spdy::kHttp2StatusHeader); + if (it == header.end()) { + return false; + } + const QuicStringPiece status(it->second); + if (status.size() != 3) { + return false; + } + // First character must be an integer in range [1,5]. + if (status[0] < '1' || status[0] > '5') { + return false; + } + // The remaining two characters must be integers. + if (!isdigit(status[1]) || !isdigit(status[2])) { + return false; + } + return QuicTextUtils::StringToInt(status, status_code); +} + +bool QuicSpdyStream::FinishedReadingTrailers() const { + // If no further trailing headers are expected, and the decompressed trailers + // (if any) have been consumed, then reading of trailers is finished. + if (!fin_received()) { + return false; + } else if (!trailers_decompressed_) { + return true; + } else { + return trailers_consumed_; + } +} + +void QuicSpdyStream::ClearSession() { + spdy_session_ = nullptr; +} + +void QuicSpdyStream::OnDataFrameStart(Http3FrameLengths frame_lengths) { + body_buffer_.OnDataHeader(frame_lengths); +} + +void QuicSpdyStream::OnDataFramePayload(QuicStringPiece payload) { + body_buffer_.OnDataPayload(payload); +} + +void QuicSpdyStream::OnDataFrameEnd() { + DVLOG(1) << "Reaches the end of a data frame. Total bytes received are " + << body_buffer_.total_body_bytes_received(); +} + +bool QuicSpdyStream::OnStreamFrameAcked(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_acked, + QuicTime::Delta ack_delay_time, + QuicByteCount* newly_acked_length) { + const bool new_data_acked = QuicStream::OnStreamFrameAcked( + offset, data_length, fin_acked, ack_delay_time, newly_acked_length); + + const QuicByteCount newly_acked_header_length = + GetNumFrameHeadersInInterval(offset, data_length); + DCHECK_LE(newly_acked_header_length, *newly_acked_length); + unacked_frame_headers_offsets_.Difference(offset, offset + data_length); + if (ack_listener_ != nullptr && new_data_acked) { + ack_listener_->OnPacketAcked( + *newly_acked_length - newly_acked_header_length, ack_delay_time); + } + return new_data_acked; +} + +void QuicSpdyStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_retransmitted) { + QuicStream::OnStreamFrameRetransmitted(offset, data_length, + fin_retransmitted); + + const QuicByteCount retransmitted_header_length = + GetNumFrameHeadersInInterval(offset, data_length); + DCHECK_LE(retransmitted_header_length, data_length); + + if (ack_listener_ != nullptr) { + ack_listener_->OnPacketRetransmitted(data_length - + retransmitted_header_length); + } +} + +QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval( + QuicStreamOffset offset, + QuicByteCount data_length) const { + QuicByteCount header_acked_length = 0; + QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); + newly_acked.Intersection(unacked_frame_headers_offsets_); + for (const auto& interval : newly_acked) { + header_acked_length += interval.Length(); + } + return header_acked_length; +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h new file mode 100644 index 0000000..2bad21a --- /dev/null +++ b/quic/core/http/quic_spdy_stream.h
@@ -0,0 +1,284 @@ +// Copyright 2013 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. + +// The base class for streams which deliver data to/from an application. +// In each direction, the data on such a stream first contains compressed +// headers then body data. + +#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_H_ + +#include <sys/types.h> + +#include <cstddef> +#include <list> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/http_decoder.h" +#include "net/third_party/quiche/src/quic/core/http/http_encoder.h" +#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.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" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" + +namespace quic { + +namespace test { +class QuicSpdyStreamPeer; +class QuicStreamPeer; +} // namespace test + +class QuicSpdySession; + +// A QUIC stream that can send and receive HTTP2 (SPDY) headers. +class QUIC_EXPORT_PRIVATE QuicSpdyStream : public QuicStream { + public: + // Visitor receives callbacks from the stream. + class QUIC_EXPORT_PRIVATE Visitor { + public: + Visitor() {} + Visitor(const Visitor&) = delete; + Visitor& operator=(const Visitor&) = delete; + + // Called when the stream is closed. + virtual void OnClose(QuicSpdyStream* stream) = 0; + + // Allows subclasses to override and do work. + virtual void OnPromiseHeadersComplete(QuicStreamId promised_id, + size_t frame_len) {} + + protected: + virtual ~Visitor() {} + }; + + QuicSpdyStream(QuicStreamId id, + QuicSpdySession* spdy_session, + StreamType type); + QuicSpdyStream(PendingStream pending, + QuicSpdySession* spdy_session, + StreamType type); + QuicSpdyStream(const QuicSpdyStream&) = delete; + QuicSpdyStream& operator=(const QuicSpdyStream&) = delete; + ~QuicSpdyStream() override; + + // QuicStream implementation + void OnClose() override; + + // Override to maybe close the write side after writing. + void OnCanWrite() override; + + // Called by the session when headers with a priority have been received + // for this stream. This method will only be called for server streams. + virtual void OnStreamHeadersPriority(spdy::SpdyPriority priority); + + // Called by the session when decompressed headers have been completely + // delivered to this stream. If |fin| is true, then this stream + // should be closed; no more data will be sent by the peer. + virtual void OnStreamHeaderList(bool fin, + size_t frame_len, + const QuicHeaderList& header_list); + + // Called when the received headers are too large. By default this will + // reset the stream. + virtual void OnHeadersTooLarge(); + + // Called by the session when decompressed push promise headers have + // been completely delivered to this stream. + virtual void OnPromiseHeaderList(QuicStreamId promised_id, + size_t frame_len, + const QuicHeaderList& header_list); + + // Called by the session when a PRIORITY frame has been been received for this + // stream. This method will only be called for server streams. + void OnPriorityFrame(spdy::SpdyPriority priority); + + // Override the base class to not discard response when receiving + // QUIC_STREAM_NO_ERROR. + void OnStreamReset(const QuicRstStreamFrame& frame) override; + + // Called by the sequencer when new data is available. Decodes the data and + // calls OnBodyAvailable() to pass to the upper layer. + void OnDataAvailable() override; + + // Called in OnDataAvailable() after it finishes the decoding job. + virtual void OnBodyAvailable() = 0; + + // Writes the headers contained in |header_block| to the dedicated + // headers stream. + virtual size_t WriteHeaders( + spdy::SpdyHeaderBlock header_block, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + + // Sends |data| to the peer, or buffers if it can't be sent immediately. + void WriteOrBufferBody(QuicStringPiece data, bool fin); + + // Writes the trailers contained in |trailer_block| to the dedicated + // headers stream. Trailers will always have the FIN set. + virtual size_t WriteTrailers( + spdy::SpdyHeaderBlock trailer_block, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + + // Override to report newly acked bytes via ack_listener_. + bool OnStreamFrameAcked(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_acked, + QuicTime::Delta ack_delay_time, + QuicByteCount* newly_acked_length) override; + + // Override to report bytes retransmitted via ack_listener_. + void OnStreamFrameRetransmitted(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_retransmitted) override; + + // Does the same thing as WriteOrBufferBody except this method takes iovec + // as the data input. Right now it only calls WritevData. + // TODO(renjietang): Write data frame header before writing body. + QuicConsumedData WritevBody(const struct iovec* iov, int count, bool fin); + + // Does the same thing as WriteOrBufferBody except this method takes + // memslicespan as the data input. Right now it only calls WriteMemSlices. + // TODO(renjietang): Write data frame header before writing body. + QuicConsumedData WriteBodySlices(QuicMemSliceSpan slices, bool fin); + + // Marks the trailers as consumed. This applies to the case where this object + // receives headers and trailers as QuicHeaderLists via calls to + // OnStreamHeaderList(). + void MarkTrailersConsumed(); + + // Clears |header_list_|. + void ConsumeHeaderList(); + + // This block of functions wraps the sequencer's functions of the same + // name. These methods return uncompressed data until that has + // been fully processed. Then they simply delegate to the sequencer. + virtual size_t Readv(const struct iovec* iov, size_t iov_len); + virtual int GetReadableRegions(iovec* iov, size_t iov_len) const; + void MarkConsumed(size_t num_bytes); + + // Returns true if header contains a valid 3-digit status and parse the status + // code to |status_code|. + bool ParseHeaderStatusCode(const spdy::SpdyHeaderBlock& header, + int* status_code) const; + + // Returns true when all data has been read from the peer, including the fin. + bool IsDoneReading() const; + bool HasBytesToRead() const; + + void set_visitor(Visitor* visitor) { visitor_ = visitor; } + + bool headers_decompressed() const { return headers_decompressed_; } + + // Returns total amount of body bytes that have been read. + uint64_t total_body_bytes_read() const; + + const QuicHeaderList& header_list() const { return header_list_; } + + bool trailers_decompressed() const { return trailers_decompressed_; } + + // Returns whatever trailers have been received for this stream. + const spdy::SpdyHeaderBlock& received_trailers() const { + return received_trailers_; + } + + // Returns true if headers have been fully read and consumed. + bool FinishedReadingHeaders() const; + + // Returns true if trailers have been fully read and consumed, or FIN has + // been received and there are no trailers. + bool FinishedReadingTrailers() const; + + // Called when owning session is getting deleted to avoid subsequent + // use of the spdy_session_ member. + void ClearSession(); + + // Returns true if the sequencer has delivered the FIN, and no more body bytes + // will be available. + bool IsClosed() { return sequencer()->IsClosed(); } + + void OnDataFrameStart(Http3FrameLengths frame_lengths); + void OnDataFramePayload(QuicStringPiece payload); + void OnDataFrameEnd(); + + using QuicStream::CloseWriteSide; + + protected: + virtual void OnInitialHeadersComplete(bool fin, + size_t frame_len, + const QuicHeaderList& header_list); + virtual void OnTrailingHeadersComplete(bool fin, + size_t frame_len, + const QuicHeaderList& header_list); + virtual size_t WriteHeadersImpl( + spdy::SpdyHeaderBlock header_block, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + + QuicSpdySession* spdy_session() const { return spdy_session_; } + Visitor* visitor() { return visitor_; } + + void set_headers_decompressed(bool val) { headers_decompressed_ = val; } + + void set_ack_listener( + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + ack_listener_ = std::move(ack_listener); + } + + const QuicIntervalSet<QuicStreamOffset>& unacked_frame_headers_offsets() { + return unacked_frame_headers_offsets_; + } + + private: + friend class test::QuicSpdyStreamPeer; + friend class test::QuicStreamPeer; + friend class QuicStreamUtils; + class HttpDecoderVisitor; + + // Given the interval marked by [|offset|, |offset| + |data_length|), return + // the number of frame header bytes contained in it. + QuicByteCount GetNumFrameHeadersInInterval(QuicStreamOffset offset, + QuicByteCount data_length) const; + + QuicSpdySession* spdy_session_; + + Visitor* visitor_; + // True if the headers have been completely decompressed. + bool headers_decompressed_; + // Contains a copy of the decompressed header (name, value) pairs until they + // are consumed via Readv. + QuicHeaderList header_list_; + + // True if the trailers have been completely decompressed. + bool trailers_decompressed_; + // True if the trailers have been consumed. + bool trailers_consumed_; + // The parsed trailers received from the peer. + spdy::SpdyHeaderBlock received_trailers_; + + // Http encoder for writing streams. + HttpEncoder encoder_; + // Http decoder for processing raw incoming stream frames. + HttpDecoder decoder_; + // Visitor of the HttpDecoder. + std::unique_ptr<HttpDecoderVisitor> http_decoder_visitor_; + // Buffer that contains decoded data of the stream. + QuicSpdyStreamBodyBuffer body_buffer_; + + // Ack listener of this stream, and it is notified when any of written bytes + // are acked or retransmitted. + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener_; + + // Offset of unacked frame headers. + QuicIntervalSet<QuicStreamOffset> unacked_frame_headers_offsets_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_H_
diff --git a/quic/core/http/quic_spdy_stream_body_buffer.cc b/quic/core/http/quic_spdy_stream_body_buffer.cc new file mode 100644 index 0000000..dcb8030 --- /dev/null +++ b/quic/core/http/quic_spdy_stream_body_buffer.cc
@@ -0,0 +1,127 @@ +// Copyright (c) 2018 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/http/quic_spdy_stream_body_buffer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicSpdyStreamBodyBuffer::QuicSpdyStreamBodyBuffer( + QuicStreamSequencer* sequencer) + : bytes_remaining_(0), + total_body_bytes_readable_(0), + total_body_bytes_received_(0), + total_payload_lengths_(0), + sequencer_(sequencer) {} + +QuicSpdyStreamBodyBuffer::~QuicSpdyStreamBodyBuffer() {} + +void QuicSpdyStreamBodyBuffer::OnDataHeader(Http3FrameLengths frame_lengths) { + frame_meta_.push_back(frame_lengths); + total_payload_lengths_ += frame_lengths.payload_length; +} + +void QuicSpdyStreamBodyBuffer::OnDataPayload(QuicStringPiece payload) { + bodies_.push_back(payload); + total_body_bytes_received_ += payload.length(); + total_body_bytes_readable_ += payload.length(); + DCHECK_LE(total_body_bytes_received_, total_payload_lengths_); +} + +void QuicSpdyStreamBodyBuffer::MarkBodyConsumed(size_t num_bytes) { + // Check if the stream has enough decoded data. + if (num_bytes > total_body_bytes_readable_) { + QUIC_BUG << "Invalid argument to MarkBodyConsumed." + << " expect to consume: " << num_bytes + << ", but not enough bytes available. " + << "Total bytes readable are: " << total_body_bytes_readable_; + return; + } + // Discard references in the stream before the sequencer marks them consumed. + size_t remaining = num_bytes; + while (remaining > 0) { + if (bodies_.empty()) { + QUIC_BUG << "Failed to consume because body buffer is empty."; + return; + } + auto body = bodies_.front(); + bodies_.pop_front(); + if (body.length() <= remaining) { + remaining -= body.length(); + } else { + body = body.substr(remaining, body.length() - remaining); + bodies_.push_front(body); + remaining = 0; + } + } + // Consume headers. + while (bytes_remaining_ < num_bytes) { + if (frame_meta_.empty()) { + QUIC_BUG << "Faild to consume because frame header buffer is empty."; + return; + } + auto meta = frame_meta_.front(); + frame_meta_.pop_front(); + bytes_remaining_ += meta.payload_length; + sequencer_->MarkConsumed(meta.header_length); + } + sequencer_->MarkConsumed(num_bytes); + // Update accountings. + bytes_remaining_ -= num_bytes; + total_body_bytes_readable_ -= num_bytes; +} + +int QuicSpdyStreamBodyBuffer::PeekBody(iovec* iov, size_t iov_len) const { + DCHECK(iov != nullptr); + DCHECK_GT(iov_len, 0u); + + if (bodies_.empty()) { + iov[0].iov_base = nullptr; + iov[0].iov_len = 0; + return 0; + } + // Fill iovs with references from the stream. + size_t iov_filled = 0; + while (iov_filled < bodies_.size() && iov_filled < iov_len) { + QuicStringPiece body = bodies_[iov_filled]; + iov[iov_filled].iov_base = const_cast<char*>(body.data()); + iov[iov_filled].iov_len = body.size(); + iov_filled++; + } + return iov_filled; +} + +size_t QuicSpdyStreamBodyBuffer::ReadBody(const struct iovec* iov, + size_t iov_len) { + size_t total_data_read = 0; + QuicByteCount total_remaining = total_body_bytes_readable_; + size_t index = 0; + size_t src_offset = 0; + for (size_t i = 0; i < iov_len && total_remaining > 0; ++i) { + char* dest = reinterpret_cast<char*>(iov[i].iov_base); + size_t dest_remaining = iov[i].iov_len; + while (dest_remaining > 0 && total_remaining > 0) { + auto body = bodies_[index]; + size_t bytes_to_copy = + std::min<size_t>(body.length() - src_offset, dest_remaining); + memcpy(dest, body.substr(src_offset, bytes_to_copy).data(), + bytes_to_copy); + dest += bytes_to_copy; + dest_remaining -= bytes_to_copy; + total_data_read += bytes_to_copy; + total_remaining -= bytes_to_copy; + if (bytes_to_copy < body.length() - src_offset) { + src_offset += bytes_to_copy; + } else { + index++; + src_offset = 0; + } + } + } + + MarkBodyConsumed(total_data_read); + return total_data_read; +} + +} // namespace quic
diff --git a/quic/core/http/quic_spdy_stream_body_buffer.h b/quic/core/http/quic_spdy_stream_body_buffer.h new file mode 100644 index 0000000..2485aac --- /dev/null +++ b/quic/core/http/quic_spdy_stream_body_buffer.h
@@ -0,0 +1,75 @@ +// Copyright (c) 2018 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_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_ + +#include "net/third_party/quiche/src/quic/core/http/http_decoder.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" + +namespace quic { + +// Buffer decoded body for QuicSpdyStream. It also talks to the sequencer to +// consume data. +class QUIC_EXPORT_PRIVATE QuicSpdyStreamBodyBuffer { + public: + // QuicSpdyStreamBodyBuffer doesn't own the sequencer and the sequencer can + // outlive the buffer. + explicit QuicSpdyStreamBodyBuffer(QuicStreamSequencer* sequencer); + + ~QuicSpdyStreamBodyBuffer(); + + // Add metadata of the frame to accountings. + // Called when QuicSpdyStream receives data frame header. + void OnDataHeader(Http3FrameLengths frame_lengths); + + // Add new data payload to buffer. + // Called when QuicSpdyStream received data payload. + // Data pointed by payload must be alive until consumed by + // QuicStreamSequencer::MarkConsumed(). + void OnDataPayload(QuicStringPiece payload); + + // Take |num_bytes| as the body size, calculate header sizes accordingly, and + // consume the right amount of data in the stream sequencer. + void MarkBodyConsumed(size_t num_bytes); + + // Fill up to |iov_len| with bodies available in buffer. No data is consumed. + // |iov|.iov_base will point to data in the buffer, and |iov|.iov_len will + // be set to the underlying data length accordingly. + // Returns the number of iov used. + int PeekBody(iovec* iov, size_t iov_len) const; + + // Copies from buffer into |iov| up to |iov_len|, and consume data in + // sequencer. |iov.iov_base| and |iov.iov_len| are preassigned and will not be + // changed. + // Returns the number of bytes read. + size_t ReadBody(const struct iovec* iov, size_t iov_len); + + bool HasBytesToRead() const { return !bodies_.empty(); } + + uint64_t total_body_bytes_received() const { + return total_body_bytes_received_; + } + + private: + // Storage for decoded data. + QuicDeque<QuicStringPiece> bodies_; + // Storage for header lengths. + QuicDeque<Http3FrameLengths> frame_meta_; + // Bytes in the first available data frame that are not consumed yet. + QuicByteCount bytes_remaining_; + // Total available body data in the stream. + QuicByteCount total_body_bytes_readable_; + // Total bytes read from the stream excluding headers. + QuicByteCount total_body_bytes_received_; + // Total length of payloads tracked by frame_meta_. + QuicByteCount total_payload_lengths_; + // Stream sequencer that directly manages data in the stream. + QuicStreamSequencer* sequencer_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_
diff --git a/quic/core/http/quic_spdy_stream_body_buffer_test.cc b/quic/core/http/quic_spdy_stream_body_buffer_test.cc new file mode 100644 index 0000000..7ea776f --- /dev/null +++ b/quic/core/http/quic_spdy_stream_body_buffer_test.cc
@@ -0,0 +1,240 @@ +// Copyright (c) 2018 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/http/quic_spdy_stream_body_buffer.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { + +namespace test { + +namespace { + +class MockStream : public QuicStreamSequencer::StreamInterface { + public: + MOCK_METHOD0(OnFinRead, void()); + MOCK_METHOD0(OnDataAvailable, void()); + MOCK_METHOD2(CloseConnectionWithDetails, + void(QuicErrorCode error, const QuicString& details)); + MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error)); + MOCK_METHOD0(OnCanWrite, void()); + MOCK_METHOD1(AddBytesConsumed, void(QuicByteCount bytes)); + + QuicStreamId id() const override { return 1; } + + const QuicSocketAddress& PeerAddressOfLatestPacket() const override { + return peer_address_; + } + + protected: + QuicSocketAddress peer_address_ = + QuicSocketAddress(QuicIpAddress::Any4(), 65535); +}; + +class MockSequencer : public QuicStreamSequencer { + public: + explicit MockSequencer(MockStream* stream) : QuicStreamSequencer(stream) {} + virtual ~MockSequencer() = default; + MOCK_METHOD1(MarkConsumed, void(size_t num_bytes_consumed)); +}; + +class QuicSpdyStreamBodyBufferTest : public QuicTest { + public: + QuicSpdyStreamBodyBufferTest() + : sequencer_(&stream_), body_buffer_(&sequencer_) {} + + protected: + MockStream stream_; + MockSequencer sequencer_; + QuicSpdyStreamBodyBuffer body_buffer_; + HttpEncoder encoder_; +}; + +TEST_F(QuicSpdyStreamBodyBufferTest, ReceiveBodies) { + QuicString body(1024, 'a'); + EXPECT_FALSE(body_buffer_.HasBytesToRead()); + body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024)); + body_buffer_.OnDataPayload(QuicStringPiece(body)); + EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received()); + EXPECT_TRUE(body_buffer_.HasBytesToRead()); +} + +TEST_F(QuicSpdyStreamBodyBufferTest, PeekBody) { + QuicString body(1024, 'a'); + body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024)); + body_buffer_.OnDataPayload(QuicStringPiece(body)); + EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received()); + iovec vec; + EXPECT_EQ(1, body_buffer_.PeekBody(&vec, 1)); + EXPECT_EQ(1024u, vec.iov_len); + EXPECT_EQ(body, + QuicStringPiece(static_cast<const char*>(vec.iov_base), 1024)); +} + +// Buffer only receives 1 frame. Stream consumes less or equal than a frame. +TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedPartialSingleFrame) { + testing::InSequence seq; + QuicString body(1024, 'a'); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + Http3FrameLengths lengths(header_length, 1024); + QuicString data = header + body; + QuicStreamFrame frame(1, false, 0, data); + sequencer_.OnStreamFrame(frame); + body_buffer_.OnDataHeader(lengths); + body_buffer_.OnDataPayload(QuicStringPiece(body)); + EXPECT_CALL(stream_, AddBytesConsumed(header_length)); + EXPECT_CALL(stream_, AddBytesConsumed(1024)); + body_buffer_.MarkBodyConsumed(1024); +} + +// Buffer received 2 frames. Stream consumes multiple times. +TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMultipleFrames) { + testing::InSequence seq; + // 1st frame. + QuicString body1(1024, 'a'); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length1 = + encoder_.SerializeDataFrameHeader(body1.length(), &buffer); + QuicString header1 = QuicString(buffer.get(), header_length1); + Http3FrameLengths lengths1(header_length1, 1024); + QuicString data1 = header1 + body1; + QuicStreamFrame frame1(1, false, 0, data1); + sequencer_.OnStreamFrame(frame1); + body_buffer_.OnDataHeader(lengths1); + body_buffer_.OnDataPayload(QuicStringPiece(body1)); + + // 2nd frame. + QuicString body2(2048, 'b'); + QuicByteCount header_length2 = + encoder_.SerializeDataFrameHeader(body2.length(), &buffer); + QuicString header2 = QuicString(buffer.get(), header_length2); + Http3FrameLengths lengths2(header_length2, 2048); + QuicString data2 = header2 + body2; + QuicStreamFrame frame2(1, false, data1.length(), data2); + sequencer_.OnStreamFrame(frame2); + body_buffer_.OnDataHeader(lengths2); + body_buffer_.OnDataPayload(QuicStringPiece(body2)); + + EXPECT_CALL(stream_, AddBytesConsumed(header_length1)); + EXPECT_CALL(stream_, AddBytesConsumed(512)); + body_buffer_.MarkBodyConsumed(512); + EXPECT_CALL(stream_, AddBytesConsumed(header_length2)); + EXPECT_CALL(stream_, AddBytesConsumed(2048)); + body_buffer_.MarkBodyConsumed(2048); + EXPECT_CALL(stream_, AddBytesConsumed(512)); + body_buffer_.MarkBodyConsumed(512); +} + +TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMoreThanBuffered) { + QuicString body(1024, 'a'); + Http3FrameLengths lengths(3, 1024); + body_buffer_.OnDataHeader(lengths); + body_buffer_.OnDataPayload(body); + EXPECT_QUIC_BUG( + body_buffer_.MarkBodyConsumed(2048), + "Invalid argument to MarkBodyConsumed. expect to consume: 2048, but not " + "enough bytes available. Total bytes readable are: 1024"); +} + +// Buffer receives 1 frame. Stream read from the buffer. +TEST_F(QuicSpdyStreamBodyBufferTest, ReadSingleBody) { + testing::InSequence seq; + QuicString body(1024, 'a'); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + Http3FrameLengths lengths(header_length, 1024); + QuicString data = header + body; + QuicStreamFrame frame(1, false, 0, data); + sequencer_.OnStreamFrame(frame); + body_buffer_.OnDataHeader(lengths); + body_buffer_.OnDataPayload(QuicStringPiece(body)); + + EXPECT_CALL(stream_, AddBytesConsumed(header_length)); + EXPECT_CALL(stream_, AddBytesConsumed(1024)); + + char base[1024]; + iovec iov = {&base[0], 1024}; + EXPECT_EQ(1024u, body_buffer_.ReadBody(&iov, 1)); + EXPECT_EQ(1024u, iov.iov_len); + EXPECT_EQ(body, + QuicStringPiece(static_cast<const char*>(iov.iov_base), 1024)); +} + +// Buffer receives 2 frames, stream read from the buffer multiple times. +TEST_F(QuicSpdyStreamBodyBufferTest, ReadMultipleBody) { + testing::InSequence seq; + // 1st frame. + QuicString body1(1024, 'a'); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length1 = + encoder_.SerializeDataFrameHeader(body1.length(), &buffer); + QuicString header1 = QuicString(buffer.get(), header_length1); + Http3FrameLengths lengths1(header_length1, 1024); + QuicString data1 = header1 + body1; + QuicStreamFrame frame1(1, false, 0, data1); + sequencer_.OnStreamFrame(frame1); + body_buffer_.OnDataHeader(lengths1); + body_buffer_.OnDataPayload(QuicStringPiece(body1)); + + // 2nd frame. + QuicString body2(2048, 'b'); + QuicByteCount header_length2 = + encoder_.SerializeDataFrameHeader(body2.length(), &buffer); + QuicString header2 = QuicString(buffer.get(), header_length2); + Http3FrameLengths lengths2(header_length2, 2048); + QuicString data2 = header2 + body2; + QuicStreamFrame frame2(1, false, data1.length(), data2); + sequencer_.OnStreamFrame(frame2); + body_buffer_.OnDataHeader(lengths2); + body_buffer_.OnDataPayload(QuicStringPiece(body2)); + + // First read of 512 bytes. + EXPECT_CALL(stream_, AddBytesConsumed(header_length1)); + EXPECT_CALL(stream_, AddBytesConsumed(512)); + char base[512]; + iovec iov = {&base[0], 512}; + EXPECT_EQ(512u, body_buffer_.ReadBody(&iov, 1)); + EXPECT_EQ(512u, iov.iov_len); + EXPECT_EQ(body1.substr(0, 512), + QuicStringPiece(static_cast<const char*>(iov.iov_base), 512)); + + // Second read of 2048 bytes. + EXPECT_CALL(stream_, AddBytesConsumed(header_length2)); + EXPECT_CALL(stream_, AddBytesConsumed(2048)); + char base2[2048]; + iovec iov2 = {&base2[0], 2048}; + EXPECT_EQ(2048u, body_buffer_.ReadBody(&iov2, 1)); + EXPECT_EQ(2048u, iov2.iov_len); + EXPECT_EQ(body1.substr(512, 512) + body2.substr(0, 1536), + QuicStringPiece(static_cast<const char*>(iov2.iov_base), 2048)); + + // Third read of the rest 512 bytes. + EXPECT_CALL(stream_, AddBytesConsumed(512)); + char base3[512]; + iovec iov3 = {&base3[0], 512}; + EXPECT_EQ(512u, body_buffer_.ReadBody(&iov3, 1)); + EXPECT_EQ(512u, iov3.iov_len); + EXPECT_EQ(body2.substr(1536, 512), + QuicStringPiece(static_cast<const char*>(iov3.iov_base), 512)); +} + +} // anonymous namespace + +} // namespace test + +} // namespace quic
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc new file mode 100644 index 0000000..1aabe0e --- /dev/null +++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -0,0 +1,1518 @@ +// Copyright 2013 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/http/quic_spdy_stream.h" + +#include <memory> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/http/http_encoder.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using spdy::kV3HighestPriority; +using spdy::kV3LowestPriority; +using spdy::SpdyHeaderBlock; +using spdy::SpdyPriority; +using testing::_; +using testing::AtLeast; +using testing::Invoke; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +const bool kShouldProcessData = true; + +class TestStream : public QuicSpdyStream { + public: + TestStream(QuicStreamId id, + QuicSpdySession* session, + bool should_process_data) + : QuicSpdyStream(id, session, BIDIRECTIONAL), + should_process_data_(should_process_data) {} + ~TestStream() override = default; + + using QuicSpdyStream::set_ack_listener; + using QuicStream::CloseWriteSide; + using QuicStream::WriteOrBufferData; + + void OnBodyAvailable() override { + if (!should_process_data_) { + return; + } + char buffer[2048]; + struct iovec vec; + vec.iov_base = buffer; + vec.iov_len = QUIC_ARRAYSIZE(buffer); + size_t bytes_read = Readv(&vec, 1); + data_ += QuicString(buffer, bytes_read); + } + + MOCK_METHOD1(WriteHeadersMock, void(bool fin)); + + size_t WriteHeadersImpl(spdy::SpdyHeaderBlock header_block, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> + ack_listener) override { + saved_headers_ = std::move(header_block); + WriteHeadersMock(fin); + return 0; + } + + const QuicString& data() const { return data_; } + const spdy::SpdyHeaderBlock& saved_headers() const { return saved_headers_; } + + private: + bool should_process_data_; + spdy::SpdyHeaderBlock saved_headers_; + QuicString data_; +}; + +class TestMockUpdateStreamSession : public MockQuicSpdySession { + public: + explicit TestMockUpdateStreamSession(QuicConnection* connection) + : MockQuicSpdySession(connection) {} + + void UpdateStreamPriority(QuicStreamId id, SpdyPriority priority) override { + EXPECT_EQ(id, expected_stream_->id()); + EXPECT_EQ(expected_priority_, priority); + EXPECT_EQ(expected_priority_, expected_stream_->priority()); + } + + void SetExpectedStream(QuicSpdyStream* stream) { expected_stream_ = stream; } + void SetExpectedPriority(SpdyPriority priority) { + expected_priority_ = priority; + } + + private: + QuicSpdyStream* expected_stream_; + SpdyPriority expected_priority_; +}; + +class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { + public: + QuicSpdyStreamTest() { + headers_[":host"] = "www.google.com"; + headers_[":path"] = "/index.hml"; + headers_[":scheme"] = "https"; + headers_["cookie"] = + "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; " + "__utmc=160408618; " + "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX" + "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX" + "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT" + "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0" + "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh" + "1zFMi5vzcns38-8_Sns; " + "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-" + "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339" + "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c" + "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%" + "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4" + "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1" + "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP" + "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6" + "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b" + "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6" + "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG" + "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk" + "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn" + "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr" + "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo "; + } + + void Initialize(bool stream_should_process_data) { + connection_ = new StrictMock<MockQuicConnection>( + &helper_, &alarm_factory_, Perspective::IS_SERVER, + SupportedVersions(GetParam())); + session_ = QuicMakeUnique<StrictMock<MockQuicSpdySession>>(connection_); + session_->Initialize(); + ON_CALL(*session_, WritevData(_, _, _, _, _)) + .WillByDefault(Invoke(MockQuicSession::ConsumeData)); + + stream_ = + new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(0), + session_.get(), stream_should_process_data); + session_->ActivateStream(QuicWrapUnique(stream_)); + stream2_ = + new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(1), + session_.get(), stream_should_process_data); + session_->ActivateStream(QuicWrapUnique(stream2_)); + } + + QuicHeaderList ProcessHeaders(bool fin, const SpdyHeaderBlock& headers) { + QuicHeaderList h = AsHeaderList(headers); + stream_->OnStreamHeaderList(fin, h.uncompressed_header_bytes(), h); + return h; + } + + QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { + return GetNthClientInitiatedBidirectionalStreamId( + connection_->transport_version(), n); + } + + bool HasFrameHeader() const { + return VersionHasDataFrameHeader(connection_->transport_version()); + } + + protected: + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* connection_; + std::unique_ptr<MockQuicSpdySession> session_; + + // Owned by the |session_|. + TestStream* stream_; + TestStream* stream2_; + + SpdyHeaderBlock headers_; + + HttpEncoder encoder_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdyStreamTest, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicSpdyStreamTest, ProcessHeaderList) { + Initialize(kShouldProcessData); + + stream_->OnStreamHeadersPriority(kV3HighestPriority); + ProcessHeaders(false, headers_); + EXPECT_EQ("", stream_->data()); + EXPECT_FALSE(stream_->header_list().empty()); + EXPECT_FALSE(stream_->IsDoneReading()); +} + +TEST_P(QuicSpdyStreamTest, ProcessTooLargeHeaderList) { + Initialize(kShouldProcessData); + + QuicHeaderList headers; + stream_->OnStreamHeadersPriority(kV3HighestPriority); + + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0)); + stream_->OnStreamHeaderList(false, 1 << 20, headers); + EXPECT_EQ(QUIC_HEADERS_TOO_LARGE, stream_->stream_error()); +} + +TEST_P(QuicSpdyStreamTest, ProcessHeaderListWithFin) { + Initialize(kShouldProcessData); + + size_t total_bytes = 0; + QuicHeaderList headers; + for (auto p : headers_) { + headers.OnHeader(p.first, p.second); + total_bytes += p.first.size() + p.second.size(); + } + stream_->OnStreamHeadersPriority(kV3HighestPriority); + stream_->OnStreamHeaderList(true, total_bytes, headers); + EXPECT_EQ("", stream_->data()); + EXPECT_FALSE(stream_->header_list().empty()); + EXPECT_FALSE(stream_->IsDoneReading()); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); +} + +TEST_P(QuicSpdyStreamTest, ParseHeaderStatusCode) { + // A valid status code should be 3-digit integer. The first digit should be in + // the range of [1, 5]. All the others are invalid. + Initialize(kShouldProcessData); + int status_code = 0; + + // Valid status codes. + headers_[":status"] = "404"; + EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + EXPECT_EQ(404, status_code); + + headers_[":status"] = "100"; + EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + EXPECT_EQ(100, status_code); + + headers_[":status"] = "599"; + EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + EXPECT_EQ(599, status_code); + + // Invalid status codes. + headers_[":status"] = "010"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "600"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "200 ok"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "2000"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "+200"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "+20"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "-10"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "-100"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + // Leading or trailing spaces are also invalid. + headers_[":status"] = " 200"; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = "200 "; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = " 200 "; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); + + headers_[":status"] = " "; + EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code)); +} + +TEST_P(QuicSpdyStreamTest, MarkHeadersConsumed) { + Initialize(kShouldProcessData); + + QuicString body = "this is the body"; + QuicHeaderList headers = ProcessHeaders(false, headers_); + EXPECT_EQ(headers, stream_->header_list()); + + stream_->ConsumeHeaderList(); + EXPECT_EQ(QuicHeaderList(), stream_->header_list()); +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBody) { + Initialize(kShouldProcessData); + + QuicString body = "this is the body"; + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + EXPECT_EQ("", stream_->data()); + QuicHeaderList headers = ProcessHeaders(false, headers_); + EXPECT_EQ(headers, stream_->header_list()); + stream_->ConsumeHeaderList(); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame); + EXPECT_EQ(QuicHeaderList(), stream_->header_list()); + EXPECT_EQ(body, stream_->data()); +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyFragments) { + Initialize(kShouldProcessData); + QuicString body = "this is the body"; + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) { + Initialize(kShouldProcessData); + QuicHeaderList headers = ProcessHeaders(false, headers_); + ASSERT_EQ(headers, stream_->header_list()); + stream_->ConsumeHeaderList(); + for (size_t offset = 0; offset < data.size(); offset += fragment_size) { + size_t remaining_data = data.size() - offset; + QuicStringPiece fragment(data.data() + offset, + std::min(fragment_size, remaining_data)); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, + offset, QuicStringPiece(fragment)); + stream_->OnStreamFrame(frame); + } + ASSERT_EQ(body, stream_->data()) << "fragment_size: " << fragment_size; + } +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyFragmentsSplit) { + Initialize(kShouldProcessData); + QuicString body = "this is the body"; + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + for (size_t split_point = 1; split_point < data.size() - 1; ++split_point) { + Initialize(kShouldProcessData); + QuicHeaderList headers = ProcessHeaders(false, headers_); + ASSERT_EQ(headers, stream_->header_list()); + stream_->ConsumeHeaderList(); + + QuicStringPiece fragment1(data.data(), split_point); + QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(fragment1)); + stream_->OnStreamFrame(frame1); + + QuicStringPiece fragment2(data.data() + split_point, + data.size() - split_point); + QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false, + split_point, QuicStringPiece(fragment2)); + stream_->OnStreamFrame(frame2); + + ASSERT_EQ(body, stream_->data()) << "split_point: " << split_point; + } +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyReadv) { + Initialize(!kShouldProcessData); + + QuicString body = "this is the body"; + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + ProcessHeaders(false, headers_); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame); + stream_->ConsumeHeaderList(); + + char buffer[2048]; + ASSERT_LT(data.length(), QUIC_ARRAYSIZE(buffer)); + struct iovec vec; + vec.iov_base = buffer; + vec.iov_len = QUIC_ARRAYSIZE(buffer); + + size_t bytes_read = stream_->Readv(&vec, 1); + EXPECT_EQ(body.length(), bytes_read); + EXPECT_EQ(body, QuicString(buffer, bytes_read)); +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndLargeBodySmallReadv) { + Initialize(kShouldProcessData); + QuicString body(12 * 1024, 'a'); + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + ProcessHeaders(false, headers_); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame); + stream_->ConsumeHeaderList(); + char buffer[2048]; + char buffer2[2048]; + struct iovec vec[2]; + vec[0].iov_base = buffer; + vec[0].iov_len = QUIC_ARRAYSIZE(buffer); + vec[1].iov_base = buffer2; + vec[1].iov_len = QUIC_ARRAYSIZE(buffer2); + size_t bytes_read = stream_->Readv(vec, 2); + EXPECT_EQ(2048u * 2, bytes_read); + EXPECT_EQ(body.substr(0, 2048), QuicString(buffer, 2048)); + EXPECT_EQ(body.substr(2048, 2048), QuicString(buffer2, 2048)); +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyMarkConsumed) { + Initialize(!kShouldProcessData); + + QuicString body = "this is the body"; + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + ProcessHeaders(false, headers_); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame); + stream_->ConsumeHeaderList(); + + struct iovec vec; + + EXPECT_EQ(1, stream_->GetReadableRegions(&vec, 1)); + EXPECT_EQ(body.length(), vec.iov_len); + EXPECT_EQ(body, QuicString(static_cast<char*>(vec.iov_base), vec.iov_len)); + + stream_->MarkConsumed(body.length()); + EXPECT_EQ(data.length(), stream_->flow_controller()->bytes_consumed()); +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndConsumeMultipleBody) { + Initialize(!kShouldProcessData); + QuicString body1 = "this is body 1"; + QuicString body2 = "body 2"; + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body1.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data1 = HasFrameHeader() ? header + body1 : body1; + header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buf); + QuicString data2 = HasFrameHeader() ? header + body2 : body2; + + ProcessHeaders(false, headers_); + QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data1)); + QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false, + data1.length(), QuicStringPiece(data2)); + stream_->OnStreamFrame(frame1); + stream_->OnStreamFrame(frame2); + stream_->ConsumeHeaderList(); + + stream_->MarkConsumed(body1.length() + body2.length()); + EXPECT_EQ(data1.length() + data2.length(), + stream_->flow_controller()->bytes_consumed()); +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyIncrementalReadv) { + Initialize(!kShouldProcessData); + + QuicString body = "this is the body"; + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + ProcessHeaders(false, headers_); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame); + stream_->ConsumeHeaderList(); + + char buffer[1]; + struct iovec vec; + vec.iov_base = buffer; + vec.iov_len = QUIC_ARRAYSIZE(buffer); + + for (size_t i = 0; i < body.length(); ++i) { + size_t bytes_read = stream_->Readv(&vec, 1); + ASSERT_EQ(1u, bytes_read); + EXPECT_EQ(body.data()[i], buffer[0]); + } +} + +TEST_P(QuicSpdyStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { + Initialize(!kShouldProcessData); + + QuicString body = "this is the body"; + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + ProcessHeaders(false, headers_); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame); + stream_->ConsumeHeaderList(); + + char buffer1[1]; + char buffer2[1]; + struct iovec vec[2]; + vec[0].iov_base = buffer1; + vec[0].iov_len = QUIC_ARRAYSIZE(buffer1); + vec[1].iov_base = buffer2; + vec[1].iov_len = QUIC_ARRAYSIZE(buffer2); + + for (size_t i = 0; i < body.length(); i += 2) { + size_t bytes_read = stream_->Readv(vec, 2); + ASSERT_EQ(2u, bytes_read) << i; + ASSERT_EQ(body.data()[i], buffer1[0]) << i; + ASSERT_EQ(body.data()[i + 1], buffer2[0]) << i; + } +} + +TEST_P(QuicSpdyStreamTest, StreamFlowControlBlocked) { + testing::InSequence seq; + // Tests that we send a BLOCKED frame to the peer when we attempt to write, + // but are flow control blocked. + Initialize(kShouldProcessData); + + // Set a small flow control limit. + const uint64_t kWindow = 36; + QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(), + kWindow); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset( + stream_->flow_controller())); + + // Try to send more data than the flow control limit allows. + const uint64_t kOverflow = 15; + QuicString body(kWindow + kOverflow, 'a'); + + const uint64_t kHeaderLength = HasFrameHeader() ? 2 : 0; + if (HasFrameHeader()) { + EXPECT_CALL(*session_, WritevData(_, _, kHeaderLength, _, NO_FIN)); + } + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(kWindow - kHeaderLength, true))); + EXPECT_CALL(*connection_, SendControlFrame(_)); + stream_->WriteOrBufferBody(body, false); + + // Should have sent as much as possible, resulting in no send window left. + EXPECT_EQ(0u, + QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller())); + + // And we should have queued the overflowed data. + EXPECT_EQ(kOverflow + kHeaderLength, + QuicStreamPeer::SizeOfQueuedData(stream_)); +} + +TEST_P(QuicSpdyStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) { + // The flow control receive window decreases whenever we add new bytes to the + // sequencer, whether they are consumed immediately or buffered. However we + // only send WINDOW_UPDATE frames based on increasing number of bytes + // consumed. + + // Don't process data - it will be buffered instead. + Initialize(!kShouldProcessData); + + // Expect no WINDOW_UPDATE frames to be sent. + EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0); + + // Set a small flow control receive window. + const uint64_t kWindow = 36; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), + kWindow); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Stream receives enough data to fill a fraction of the receive window. + QuicString body(kWindow / 3, 'a'); + QuicByteCount header_length = 0; + QuicString data; + + if (HasFrameHeader()) { + std::unique_ptr<char[]> buffer; + header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + data = header + body; + } else { + data = body; + } + + ProcessHeaders(false, headers_); + + QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame1); + EXPECT_EQ( + kWindow - (kWindow / 3) - header_length, + QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller())); + + // Now receive another frame which results in the receive window being over + // half full. This should all be buffered, decreasing the receive window but + // not sending WINDOW_UPDATE. + QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false, + kWindow / 3 + header_length, QuicStringPiece(data)); + stream_->OnStreamFrame(frame2); + EXPECT_EQ( + kWindow - (2 * kWindow / 3) - 2 * header_length, + QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller())); +} + +TEST_P(QuicSpdyStreamTest, StreamFlowControlWindowUpdate) { + // Tests that on receipt of data, the stream updates its receive window offset + // appropriately, and sends WINDOW_UPDATE frames when its receive window drops + // too low. + Initialize(kShouldProcessData); + + // Set a small flow control limit. + const uint64_t kWindow = 36; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), + kWindow); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Stream receives enough data to fill a fraction of the receive window. + QuicString body(kWindow / 3, 'a'); + QuicByteCount header_length = 0; + QuicString data; + + if (HasFrameHeader()) { + std::unique_ptr<char[]> buffer; + header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + data = header + body; + } else { + data = body; + } + + ProcessHeaders(false, headers_); + stream_->ConsumeHeaderList(); + + QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame1); + EXPECT_EQ( + kWindow - (kWindow / 3) - header_length, + QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller())); + + // Now receive another frame which results in the receive window being over + // half full. This will trigger the stream to increase its receive window + // offset and send a WINDOW_UPDATE. The result will be again an available + // window of kWindow bytes. + QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false, + kWindow / 3 + header_length, QuicStringPiece(data)); + EXPECT_CALL(*connection_, SendControlFrame(_)); + stream_->OnStreamFrame(frame2); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize( + stream_->flow_controller())); +} + +TEST_P(QuicSpdyStreamTest, ConnectionFlowControlWindowUpdate) { + // Tests that on receipt of data, the connection updates its receive window + // offset appropriately, and sends WINDOW_UPDATE frames when its receive + // window drops too low. + Initialize(kShouldProcessData); + + // Set a small flow control limit for streams and connection. + const uint64_t kWindow = 36; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetReceiveWindowOffset(stream2_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream2_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(session_->flow_controller(), + kWindow); + + // Supply headers to both streams so that they are happy to receive data. + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + stream_->ConsumeHeaderList(); + stream2_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + stream2_->ConsumeHeaderList(); + + // Each stream gets a quarter window of data. This should not trigger a + // WINDOW_UPDATE for either stream, nor for the connection. + QuicByteCount header_length = 0; + QuicString body; + QuicString data; + QuicString data2; + QuicString body2(1, 'a'); + + if (HasFrameHeader()) { + body = QuicString(kWindow / 4 - 2, 'a'); + std::unique_ptr<char[]> buffer; + header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + data = header + body; + std::unique_ptr<char[]> buffer2; + QuicByteCount header_length2 = + encoder_.SerializeDataFrameHeader(body2.length(), &buffer2); + QuicString header2 = QuicString(buffer2.get(), header_length2); + data2 = header2 + body2; + } else { + body = QuicString(kWindow / 4, 'a'); + data = body; + data2 = body2; + } + + QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + stream_->OnStreamFrame(frame1); + QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0, + QuicStringPiece(data)); + stream2_->OnStreamFrame(frame2); + + // Now receive a further single byte on one stream - again this does not + // trigger a stream WINDOW_UPDATE, but now the connection flow control window + // is over half full and thus a connection WINDOW_UPDATE is sent. + EXPECT_CALL(*connection_, SendControlFrame(_)); + QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false, + body.length() + header_length, QuicStringPiece(data2)); + stream_->OnStreamFrame(frame3); +} + +TEST_P(QuicSpdyStreamTest, StreamFlowControlViolation) { + // Tests that on if the peer sends too much data (i.e. violates the flow + // control protocol), then we terminate the connection. + + // Stream should not process data, so that data gets buffered in the + // sequencer, triggering flow control limits. + Initialize(!kShouldProcessData); + + // Set a small flow control limit. + const uint64_t kWindow = 50; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + + ProcessHeaders(false, headers_); + + // Receive data to overflow the window, violating flow control. + QuicString body(kWindow + 1, 'a'); + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + stream_->OnStreamFrame(frame); +} + +TEST_P(QuicSpdyStreamTest, TestHandlingQuicRstStreamNoError) { + Initialize(kShouldProcessData); + ProcessHeaders(false, headers_); + + stream_->OnStreamReset(QuicRstStreamFrame( + kInvalidControlFrameId, stream_->id(), QUIC_STREAM_NO_ERROR, 0)); + EXPECT_TRUE(stream_->write_side_closed()); + EXPECT_FALSE(stream_->reading_stopped()); +} + +TEST_P(QuicSpdyStreamTest, ConnectionFlowControlViolation) { + // Tests that on if the peer sends too much data (i.e. violates the flow + // control protocol), at the connection level (rather than the stream level) + // then we terminate the connection. + + // Stream should not process data, so that data gets buffered in the + // sequencer, triggering flow control limits. + Initialize(!kShouldProcessData); + + // Set a small flow control window on streams, and connection. + const uint64_t kStreamWindow = 50; + const uint64_t kConnectionWindow = 10; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kStreamWindow); + QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(), + kConnectionWindow); + + ProcessHeaders(false, headers_); + + // Send enough data to overflow the connection level flow control window. + QuicString body(kConnectionWindow + 1, 'a'); + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + EXPECT_LT(data.size(), kStreamWindow); + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0, + QuicStringPiece(data)); + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + stream_->OnStreamFrame(frame); +} + +TEST_P(QuicSpdyStreamTest, StreamFlowControlFinNotBlocked) { + // An attempt to write a FIN with no data should not be flow control blocked, + // even if the send window is 0. + + Initialize(kShouldProcessData); + + // Set a flow control limit of zero. + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0); + EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Send a frame with a FIN but no data. This should not be blocked. + QuicString body = ""; + bool fin = true; + + EXPECT_CALL(*connection_, + SendBlocked(GetNthClientInitiatedBidirectionalId(0))) + .Times(0); + EXPECT_CALL(*session_, WritevData(_, _, 0, _, FIN)); + + stream_->WriteOrBufferBody(body, fin); +} + +TEST_P(QuicSpdyStreamTest, ReceivingTrailersViaHeaderList) { + // Test that receiving trailing headers from the peer via + // OnStreamHeaderList() works, and can be read from the stream and consumed. + Initialize(kShouldProcessData); + + // Receive initial headers. + size_t total_bytes = 0; + QuicHeaderList headers; + for (const auto& p : headers_) { + headers.OnHeader(p.first, p.second); + total_bytes += p.first.size() + p.second.size(); + } + + stream_->OnStreamHeadersPriority(kV3HighestPriority); + stream_->OnStreamHeaderList(/*fin=*/false, total_bytes, headers); + stream_->ConsumeHeaderList(); + + // Receive trailing headers. + SpdyHeaderBlock trailers_block; + trailers_block["key1"] = "value1"; + trailers_block["key2"] = "value2"; + trailers_block["key3"] = "value3"; + SpdyHeaderBlock trailers_block_with_final_offset = trailers_block.Clone(); + trailers_block_with_final_offset[kFinalOffsetHeaderKey] = "0"; + total_bytes = 0; + QuicHeaderList trailers; + for (const auto& p : trailers_block_with_final_offset) { + trailers.OnHeader(p.first, p.second); + total_bytes += p.first.size() + p.second.size(); + } + stream_->OnStreamHeaderList(/*fin=*/true, total_bytes, trailers); + + // The trailers should be decompressed, and readable from the stream. + EXPECT_TRUE(stream_->trailers_decompressed()); + EXPECT_EQ(trailers_block, stream_->received_trailers()); + + // IsDoneReading() returns false until trailers marked consumed. + EXPECT_FALSE(stream_->IsDoneReading()); + stream_->MarkTrailersConsumed(); + EXPECT_TRUE(stream_->IsDoneReading()); +} + +TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithOffset) { + // Test that when receiving trailing headers with an offset before response + // body, stream is closed at the right offset. + Initialize(kShouldProcessData); + + // Receive initial headers. + QuicHeaderList headers = ProcessHeaders(false, headers_); + stream_->ConsumeHeaderList(); + + const QuicString body = "this is the body"; + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + // Receive trailing headers. + SpdyHeaderBlock trailers_block; + trailers_block["key1"] = "value1"; + trailers_block["key2"] = "value2"; + trailers_block["key3"] = "value3"; + trailers_block[kFinalOffsetHeaderKey] = + QuicTextUtils::Uint64ToString(data.size()); + + QuicHeaderList trailers = ProcessHeaders(true, trailers_block); + + // The trailers should be decompressed, and readable from the stream. + EXPECT_TRUE(stream_->trailers_decompressed()); + + // The final offset trailer will be consumed by QUIC. + trailers_block.erase(kFinalOffsetHeaderKey); + EXPECT_EQ(trailers_block, stream_->received_trailers()); + + // Consuming the trailers erases them from the stream. + stream_->MarkTrailersConsumed(); + EXPECT_TRUE(stream_->FinishedReadingTrailers()); + + EXPECT_FALSE(stream_->IsDoneReading()); + // Receive and consume body. + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/false, + 0, data); + stream_->OnStreamFrame(frame); + EXPECT_EQ(body, stream_->data()); + EXPECT_TRUE(stream_->IsDoneReading()); +} + +TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithoutOffset) { + // Test that receiving trailers without a final offset field is an error. + Initialize(kShouldProcessData); + + // Receive initial headers. + ProcessHeaders(false, headers_); + stream_->ConsumeHeaderList(); + + // Receive trailing headers, without kFinalOffsetHeaderKey. + SpdyHeaderBlock trailers_block; + trailers_block["key1"] = "value1"; + trailers_block["key2"] = "value2"; + trailers_block["key3"] = "value3"; + auto trailers = AsHeaderList(trailers_block); + + // Verify that the trailers block didn't contain a final offset. + EXPECT_EQ("", trailers_block[kFinalOffsetHeaderKey].as_string()); + + // Receipt of the malformed trailers will close the connection. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _)) + .Times(1); + stream_->OnStreamHeaderList(/*fin=*/true, + trailers.uncompressed_header_bytes(), trailers); +} + +TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithoutFin) { + // Test that received Trailers must always have the FIN set. + Initialize(kShouldProcessData); + + // Receive initial headers. + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(/*fin=*/false, + headers.uncompressed_header_bytes(), headers); + stream_->ConsumeHeaderList(); + + // Receive trailing headers with FIN deliberately set to false. + SpdyHeaderBlock trailers_block; + trailers_block["foo"] = "bar"; + auto trailers = AsHeaderList(trailers_block); + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _)) + .Times(1); + stream_->OnStreamHeaderList(/*fin=*/false, + trailers.uncompressed_header_bytes(), trailers); +} + +TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterHeadersWithFin) { + // If headers are received with a FIN, no trailers should then arrive. + Initialize(kShouldProcessData); + + // Receive initial headers with FIN set. + ProcessHeaders(true, headers_); + stream_->ConsumeHeaderList(); + + // Receive trailing headers after FIN already received. + SpdyHeaderBlock trailers_block; + trailers_block["foo"] = "bar"; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _)) + .Times(1); + ProcessHeaders(true, trailers_block); +} + +TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterBodyWithFin) { + // If body data are received with a FIN, no trailers should then arrive. + Initialize(kShouldProcessData); + + // Receive initial headers without FIN set. + ProcessHeaders(false, headers_); + stream_->ConsumeHeaderList(); + + // Receive body data, with FIN. + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/true, + 0, "body"); + stream_->OnStreamFrame(frame); + + // Receive trailing headers after FIN already received. + SpdyHeaderBlock trailers_block; + trailers_block["foo"] = "bar"; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _)) + .Times(1); + ProcessHeaders(true, trailers_block); +} + +TEST_P(QuicSpdyStreamTest, ClosingStreamWithNoTrailers) { + // Verify that a stream receiving headers, body, and no trailers is correctly + // marked as done reading on consumption of headers and body. + Initialize(kShouldProcessData); + + // Receive and consume initial headers with FIN not set. + auto h = AsHeaderList(headers_); + stream_->OnStreamHeaderList(/*fin=*/false, h.uncompressed_header_bytes(), h); + stream_->ConsumeHeaderList(); + + // Receive and consume body with FIN set, and no trailers. + QuicString body(1024, 'x'); + std::unique_ptr<char[]> buf; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buf); + QuicString header = QuicString(buf.get(), header_length); + QuicString data = HasFrameHeader() ? header + body : body; + + QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/true, + 0, data); + stream_->OnStreamFrame(frame); + + EXPECT_TRUE(stream_->IsDoneReading()); +} + +TEST_P(QuicSpdyStreamTest, WritingTrailersSendsAFin) { + // Test that writing trailers will send a FIN, as Trailers are the last thing + // to be sent on a stream. + Initialize(kShouldProcessData); + + // Write the initial headers, without a FIN. + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr); + + // Writing trailers implicitly sends a FIN. + SpdyHeaderBlock trailers; + trailers["trailer key"] = "trailer value"; + EXPECT_CALL(*stream_, WriteHeadersMock(true)); + stream_->WriteTrailers(std::move(trailers), nullptr); + EXPECT_TRUE(stream_->fin_sent()); +} + +TEST_P(QuicSpdyStreamTest, WritingTrailersFinalOffset) { + // Test that when writing trailers, the trailers that are actually sent to the + // peer contain the final offset field indicating last byte of data. + Initialize(kShouldProcessData); + + // Write the initial headers. + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr); + + // Write non-zero body data to force a non-zero final offset. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + QuicString body(1024, 'x'); // 1 kB + QuicByteCount header_length = 0; + if (HasFrameHeader()) { + std::unique_ptr<char[]> buf; + header_length = encoder_.SerializeDataFrameHeader(body.length(), &buf); + } + + stream_->WriteOrBufferBody(body, false); + + // The final offset field in the trailing headers is populated with the + // number of body bytes written (including queued bytes). + SpdyHeaderBlock trailers; + trailers["trailer key"] = "trailer value"; + SpdyHeaderBlock trailers_with_offset(trailers.Clone()); + trailers_with_offset[kFinalOffsetHeaderKey] = + QuicTextUtils::Uint64ToString(body.length() + header_length); + EXPECT_CALL(*stream_, WriteHeadersMock(true)); + stream_->WriteTrailers(std::move(trailers), nullptr); + EXPECT_EQ(trailers_with_offset, stream_->saved_headers()); +} + +TEST_P(QuicSpdyStreamTest, WritingTrailersClosesWriteSide) { + // Test that if trailers are written after all other data has been written + // (headers and body), that this closes the stream for writing. + Initialize(kShouldProcessData); + + // Write the initial headers. + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr); + + // Write non-zero body data. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + const int kBodySize = 1 * 1024; // 1 kB + stream_->WriteOrBufferBody(QuicString(kBodySize, 'x'), false); + EXPECT_EQ(0u, stream_->BufferedDataBytes()); + + // Headers and body have been fully written, there is no queued data. Writing + // trailers marks the end of this stream, and thus the write side is closed. + EXPECT_CALL(*stream_, WriteHeadersMock(true)); + stream_->WriteTrailers(SpdyHeaderBlock(), nullptr); + EXPECT_TRUE(stream_->write_side_closed()); +} + +TEST_P(QuicSpdyStreamTest, WritingTrailersWithQueuedBytes) { + // Test that the stream is not closed for writing when trailers are sent + // while there are still body bytes queued. + testing::InSequence seq; + Initialize(kShouldProcessData); + + // Write the initial headers. + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr); + + // Write non-zero body data, but only consume partially, ensuring queueing. + const int kBodySize = 1 * 1024; // 1 kB + if (HasFrameHeader()) { + EXPECT_CALL(*session_, WritevData(_, _, 3, _, NO_FIN)); + } + EXPECT_CALL(*session_, WritevData(_, _, kBodySize, _, NO_FIN)) + .WillOnce(Return(QuicConsumedData(kBodySize - 1, false))); + stream_->WriteOrBufferBody(QuicString(kBodySize, 'x'), false); + EXPECT_EQ(1u, stream_->BufferedDataBytes()); + + // Writing trailers will send a FIN, but not close the write side of the + // stream as there are queued bytes. + EXPECT_CALL(*stream_, WriteHeadersMock(true)); + stream_->WriteTrailers(SpdyHeaderBlock(), nullptr); + EXPECT_TRUE(stream_->fin_sent()); + EXPECT_FALSE(stream_->write_side_closed()); + + // Writing the queued bytes will close the write side of the stream. + EXPECT_CALL(*session_, WritevData(_, _, 1, _, NO_FIN)); + stream_->OnCanWrite(); + EXPECT_TRUE(stream_->write_side_closed()); +} + +TEST_P(QuicSpdyStreamTest, WritingTrailersAfterFIN) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (GetParam() != AllSupportedVersions()[0]) { + return; + } + + // Test that it is not possible to write Trailers after a FIN has been sent. + Initialize(kShouldProcessData); + + // Write the initial headers, with a FIN. + EXPECT_CALL(*stream_, WriteHeadersMock(true)); + stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/true, nullptr); + EXPECT_TRUE(stream_->fin_sent()); + + // Writing Trailers should fail, as the FIN has already been sent. + // populated with the number of body bytes written. + EXPECT_QUIC_BUG(stream_->WriteTrailers(SpdyHeaderBlock(), nullptr), + "Trailers cannot be sent after a FIN"); +} + +TEST_P(QuicSpdyStreamTest, HeaderStreamNotiferCorrespondingSpdyStream) { + Initialize(kShouldProcessData); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + testing::InSequence s; + QuicReferenceCountedPointer<MockAckListener> ack_listener1( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener2( + new MockAckListener()); + stream_->set_ack_listener(ack_listener1); + stream2_->set_ack_listener(ack_listener2); + + session_->headers_stream()->WriteOrBufferData("Header1", false, + ack_listener1); + stream_->WriteOrBufferBody("Test1", true); + + session_->headers_stream()->WriteOrBufferData("Header2", false, + ack_listener2); + stream2_->WriteOrBufferBody("Test2", false); + + QuicStreamFrame frame1( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), false, 0, + "Header1"); + QuicString header = ""; + if (HasFrameHeader()) { + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = encoder_.SerializeDataFrameHeader(5, &buffer); + header = QuicString(buffer.get(), header_length); + } + QuicStreamFrame frame2(stream_->id(), true, 0, header + "Test1"); + QuicStreamFrame frame3( + QuicUtils::GetHeadersStreamId(connection_->transport_version()), false, 7, + "Header2"); + QuicStreamFrame frame4(stream2_->id(), false, 0, header + "Test2"); + + EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(7)); + session_->OnStreamFrameRetransmitted(frame1); + + EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _)); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame1), QuicTime::Delta::Zero())); + EXPECT_CALL(*ack_listener1, OnPacketAcked(5, _)); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame2), QuicTime::Delta::Zero())); + EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _)); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame3), QuicTime::Delta::Zero())); + EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _)); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame4), QuicTime::Delta::Zero())); +} + +TEST_P(QuicSpdyStreamTest, StreamBecomesZombieWithWriteThatCloses) { + Initialize(kShouldProcessData); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + QuicStreamPeer::CloseReadSide(stream_); + // This write causes stream to be closed. + stream_->WriteOrBufferBody("Test1", true); + // stream_ has unacked data and should become zombie. + EXPECT_TRUE(QuicContainsKey(QuicSessionPeer::zombie_streams(session_.get()), + stream_->id())); + EXPECT_TRUE(QuicSessionPeer::closed_streams(session_.get()).empty()); +} + +TEST_P(QuicSpdyStreamTest, OnPriorityFrame) { + Initialize(kShouldProcessData); + stream_->OnPriorityFrame(kV3HighestPriority); + EXPECT_EQ(kV3HighestPriority, stream_->priority()); +} + +TEST_P(QuicSpdyStreamTest, OnPriorityFrameAfterSendingData) { + testing::InSequence seq; + Initialize(kShouldProcessData); + + if (HasFrameHeader()) { + EXPECT_CALL(*session_, WritevData(_, _, 2, _, NO_FIN)); + } + EXPECT_CALL(*session_, WritevData(_, _, 4, _, FIN)); + stream_->WriteOrBufferBody("data", true); + stream_->OnPriorityFrame(kV3HighestPriority); + EXPECT_EQ(kV3HighestPriority, stream_->priority()); +} + +TEST_P(QuicSpdyStreamTest, SetPriorityBeforeUpdateStreamPriority) { + MockQuicConnection* connection = new StrictMock<MockQuicConnection>( + &helper_, &alarm_factory_, Perspective::IS_SERVER, + SupportedVersions(GetParam())); + std::unique_ptr<TestMockUpdateStreamSession> session( + new StrictMock<TestMockUpdateStreamSession>(connection)); + auto stream = new StrictMock<TestStream>( + GetNthClientInitiatedBidirectionalStreamId( + session->connection()->transport_version(), 0), + session.get(), + /*should_process_data=*/true); + session->ActivateStream(QuicWrapUnique(stream)); + + // QuicSpdyStream::SetPriority() should eventually call UpdateStreamPriority() + // on the session. Make sure stream->priority() returns the updated priority + // if called within UpdateStreamPriority(). This expectation is enforced in + // TestMockUpdateStreamSession::UpdateStreamPriority(). + session->SetExpectedStream(stream); + session->SetExpectedPriority(kV3HighestPriority); + stream->SetPriority(kV3HighestPriority); + + session->SetExpectedPriority(kV3LowestPriority); + stream->SetPriority(kV3LowestPriority); +} + +TEST_P(QuicSpdyStreamTest, StreamWaitsForAcks) { + Initialize(kShouldProcessData); + QuicReferenceCountedPointer<MockAckListener> mock_ack_listener( + new StrictMock<MockAckListener>); + stream_->set_ack_listener(mock_ack_listener); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + // Stream is not waiting for acks initially. + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + // Send kData1. + stream_->WriteOrBufferData("FooAndBar", false, nullptr); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _)); + QuicByteCount newly_acked_length = 0; + EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + // Stream is not waiting for acks as all sent data is acked. + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + // Send kData2. + stream_->WriteOrBufferData("FooAndBar", false, nullptr); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Send FIN. + stream_->WriteOrBufferData("", true, nullptr); + // Fin only frame is not stored in send buffer. + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + + // kData2 is retransmitted. + EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(9)); + stream_->OnStreamFrameRetransmitted(9, 9, false); + + // kData2 is acked. + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _)); + EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + // Stream is waiting for acks as FIN is not acked. + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + // FIN is acked. + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _)); + EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); +} + +TEST_P(QuicSpdyStreamTest, StreamDataGetAckedMultipleTimes) { + Initialize(kShouldProcessData); + QuicReferenceCountedPointer<MockAckListener> mock_ack_listener( + new StrictMock<MockAckListener>); + stream_->set_ack_listener(mock_ack_listener); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + // Send [0, 27) and fin. + stream_->WriteOrBufferData("FooAndBar", false, nullptr); + stream_->WriteOrBufferData("FooAndBar", false, nullptr); + stream_->WriteOrBufferData("FooAndBar", true, nullptr); + + // Ack [0, 9), [5, 22) and [18, 26) + // Verify [0, 9) 9 bytes are acked. + QuicByteCount newly_acked_length = 0; + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _)); + EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(2u, QuicStreamPeer::SendBuffer(stream_).size()); + // Verify [9, 22) 13 bytes are acked. + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(13, _)); + EXPECT_TRUE(stream_->OnStreamFrameAcked(5, 17, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Verify [22, 26) 4 bytes are acked. + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(4, _)); + EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 8, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + // Ack [0, 27). + // Verify [26, 27) 1 byte is acked. + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(1, _)); + EXPECT_TRUE(stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + // Ack Fin. Verify OnPacketAcked is called. + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _)); + EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + + // Ack [10, 27) and fin. + // No new data is acked, verify OnPacketAcked is not called. + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(_, _)).Times(0); + EXPECT_FALSE(stream_->OnStreamFrameAcked( + 10, 17, true, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_FALSE(stream_->IsWaitingForAcks()); +} + +// HTTP/3 only. +TEST_P(QuicSpdyStreamTest, HeadersAckNotReportedWriteOrBufferBody) { + Initialize(kShouldProcessData); + if (!HasFrameHeader()) { + return; + } + QuicReferenceCountedPointer<MockAckListener> mock_ack_listener( + new StrictMock<MockAckListener>); + stream_->set_ack_listener(mock_ack_listener); + QuicString body = "Test1"; + QuicString body2(100, 'x'); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + stream_->WriteOrBufferBody(body, false); + stream_->WriteOrBufferBody(body2, true); + + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + + header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer); + QuicString header2 = QuicString(buffer.get(), header_length); + + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body.length(), _)); + QuicStreamFrame frame(stream_->id(), false, 0, header + body); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame), QuicTime::Delta::Zero())); + + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _)); + QuicStreamFrame frame2(stream_->id(), false, (header + body).length(), + header2); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame2), QuicTime::Delta::Zero())); + + EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body2.length(), _)); + QuicStreamFrame frame3(stream_->id(), true, + (header + body).length() + header2.length(), body2); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame3), QuicTime::Delta::Zero())); + + EXPECT_TRUE( + QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty()); +} + +// HTTP/3 only. +TEST_P(QuicSpdyStreamTest, HeadersAckNotReportedWriteBodySlices) { + Initialize(kShouldProcessData); + if (!HasFrameHeader()) { + return; + } + QuicReferenceCountedPointer<MockAckListener> mock_ack_listener( + new StrictMock<MockAckListener>); + stream_->set_ack_listener(mock_ack_listener); + QuicString body = "Test1"; + QuicString body2(100, 'x'); + struct iovec body1_iov = {const_cast<char*>(body.data()), body.length()}; + struct iovec body2_iov = {const_cast<char*>(body2.data()), body2.length()}; + QuicMemSliceStorage storage(&body1_iov, 1, + helper_.GetStreamSendBufferAllocator(), 1024); + QuicMemSliceStorage storage2(&body2_iov, 1, + helper_.GetStreamSendBufferAllocator(), 1024); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + stream_->WriteBodySlices(storage.ToSpan(), false); + stream_->WriteBodySlices(storage2.ToSpan(), true); + + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + + header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer); + QuicString header2 = QuicString(buffer.get(), header_length); + + EXPECT_CALL(*mock_ack_listener, + OnPacketAcked(body.length() + body2.length(), _)); + QuicStreamFrame frame(stream_->id(), true, 0, + header + body + header2 + body2); + EXPECT_TRUE( + session_->OnFrameAcked(QuicFrame(frame), QuicTime::Delta::Zero())); + + EXPECT_TRUE( + QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty()); +} + +// HTTP/3 only. +TEST_P(QuicSpdyStreamTest, HeaderBytesNotReportedOnRetransmission) { + Initialize(kShouldProcessData); + if (!HasFrameHeader()) { + return; + } + QuicReferenceCountedPointer<MockAckListener> mock_ack_listener( + new StrictMock<MockAckListener>); + stream_->set_ack_listener(mock_ack_listener); + QuicString body = "Test1"; + QuicString body2(100, 'x'); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1)); + stream_->WriteOrBufferBody(body, false); + stream_->WriteOrBufferBody(body2, true); + + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + encoder_.SerializeDataFrameHeader(body.length(), &buffer); + QuicString header = QuicString(buffer.get(), header_length); + + header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer); + QuicString header2 = QuicString(buffer.get(), header_length); + + EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(body.length())); + QuicStreamFrame frame(stream_->id(), false, 0, header + body); + session_->OnStreamFrameRetransmitted(frame); + + EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(body2.length())); + QuicStreamFrame frame2(stream_->id(), true, (header + body).length(), + header2 + body2); + session_->OnStreamFrameRetransmitted(frame2); + + EXPECT_FALSE( + QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/http/spdy_utils.cc b/quic/core/http/spdy_utils.cc new file mode 100644 index 0000000..7022ae2 --- /dev/null +++ b/quic/core/http/spdy_utils.cc
@@ -0,0 +1,353 @@ +// Copyright (c) 2013 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/http/spdy_utils.h" + +#include <memory> +#include <vector> + +#include "url/gurl.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.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" +#include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" + +using spdy::SpdyHeaderBlock; + +namespace quic { + +// static +bool SpdyUtils::ExtractContentLengthFromHeaders(int64_t* content_length, + SpdyHeaderBlock* headers) { + auto it = headers->find("content-length"); + if (it == headers->end()) { + return false; + } else { + // Check whether multiple values are consistent. + QuicStringPiece content_length_header = it->second; + std::vector<QuicStringPiece> values = + QuicTextUtils::Split(content_length_header, '\0'); + for (const QuicStringPiece& value : values) { + uint64_t new_value; + if (!QuicTextUtils::StringToUint64(value, &new_value)) { + QUIC_DLOG(ERROR) + << "Content length was either unparseable or negative."; + return false; + } + if (*content_length < 0) { + *content_length = new_value; + continue; + } + if (new_value != static_cast<uint64_t>(*content_length)) { + QUIC_DLOG(ERROR) + << "Parsed content length " << new_value << " is " + << "inconsistent with previously detected content length " + << *content_length; + return false; + } + } + return true; + } +} + +bool SpdyUtils::CopyAndValidateHeaders(const QuicHeaderList& header_list, + int64_t* content_length, + SpdyHeaderBlock* headers) { + for (const auto& p : header_list) { + const QuicString& name = p.first; + if (name.empty()) { + QUIC_DLOG(ERROR) << "Header name must not be empty."; + return false; + } + + if (QuicTextUtils::ContainsUpperCase(name)) { + QUIC_DLOG(ERROR) << "Malformed header: Header name " << name + << " contains upper-case characters."; + return false; + } + + headers->AppendValueOrAddHeader(name, p.second); + } + + if (QuicContainsKey(*headers, "content-length") && + !ExtractContentLengthFromHeaders(content_length, headers)) { + return false; + } + + QUIC_DVLOG(1) << "Successfully parsed headers: " << headers->DebugString(); + return true; +} + +bool SpdyUtils::CopyAndValidateTrailers(const QuicHeaderList& header_list, + size_t* final_byte_offset, + SpdyHeaderBlock* trailers) { + bool found_final_byte_offset = false; + for (const auto& p : header_list) { + const QuicString& name = p.first; + + // Pull out the final offset pseudo header which indicates the number of + // response body bytes expected. + if (!found_final_byte_offset && name == kFinalOffsetHeaderKey && + QuicTextUtils::StringToSizeT(p.second, final_byte_offset)) { + found_final_byte_offset = true; + continue; + } + + if (name.empty() || name[0] == ':') { + QUIC_DLOG(ERROR) + << "Trailers must not be empty, and must not contain pseudo-" + << "headers. Found: '" << name << "'"; + return false; + } + + if (QuicTextUtils::ContainsUpperCase(name)) { + QUIC_DLOG(ERROR) << "Malformed header: Header name " << name + << " contains upper-case characters."; + return false; + } + + trailers->AppendValueOrAddHeader(name, p.second); + } + + if (!found_final_byte_offset) { + QUIC_DLOG(ERROR) << "Required key '" << kFinalOffsetHeaderKey + << "' not present"; + return false; + } + + // TODO(rjshade): Check for other forbidden keys, following the HTTP/2 spec. + + QUIC_DVLOG(1) << "Successfully parsed Trailers: " << trailers->DebugString(); + return true; +} + +// static +QuicString SpdyUtils::GetPromisedUrlFromHeaders( + const SpdyHeaderBlock& headers) { + // RFC 7540, Section 8.1.2.3: All HTTP/2 requests MUST include exactly + // one valid value for the ":method", ":scheme", and ":path" pseudo-header + // fields, unless it is a CONNECT request. + + // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any + // subsequent CONTINUATION frames MUST be a valid and complete set of request + // header fields (Section 8.1.2.3). The server MUST include a method in the + // ":method" pseudo-header field that is safe and cacheable. + // + // RFC 7231, Section 4.2.1: Of the request methods defined by this + // specification, the GET, HEAD, OPTIONS, and TRACE methods are defined to be + // safe. + // + // RFC 7231, Section 4.2.1: ... this specification defines GET, HEAD, and + // POST as cacheable, ... + // + // So the only methods allowed in a PUSH_PROMISE are GET and HEAD. + SpdyHeaderBlock::const_iterator it = headers.find(":method"); + if (it == headers.end() || (it->second != "GET" && it->second != "HEAD")) { + return QuicString(); + } + + it = headers.find(":scheme"); + if (it == headers.end() || it->second.empty()) { + return QuicString(); + } + QuicStringPiece scheme = it->second; + + // RFC 7540, Section 8.2: The server MUST include a value in the + // ":authority" pseudo-header field for which the server is authoritative + // (see Section 10.1). + it = headers.find(":authority"); + if (it == headers.end() || it->second.empty()) { + return QuicString(); + } + QuicStringPiece authority = it->second; + + // RFC 7540, Section 8.1.2.3 requires that the ":path" pseudo-header MUST + // NOT be empty for "http" or "https" URIs; + // + // However, to ensure the scheme is consistently canonicalized, that check + // is deferred to implementations in QuicUrlUtils::GetPushPromiseUrl(). + it = headers.find(":path"); + if (it == headers.end()) { + return QuicString(); + } + QuicStringPiece path = it->second; + + return GetPushPromiseUrl(scheme, authority, path); +} + +// static +QuicString SpdyUtils::GetPromisedHostNameFromHeaders( + const SpdyHeaderBlock& headers) { + // TODO(fayang): Consider just checking out the value of the ":authority" key + // in headers. + return GURL(GetPromisedUrlFromHeaders(headers)).host(); +} + +// static +bool SpdyUtils::PromisedUrlIsValid(const SpdyHeaderBlock& headers) { + QuicString url(GetPromisedUrlFromHeaders(headers)); + return !url.empty() && GURL(url).is_valid(); +} + +// static +bool SpdyUtils::PopulateHeaderBlockFromUrl(const QuicString url, + SpdyHeaderBlock* headers) { + (*headers)[":method"] = "GET"; + size_t pos = url.find("://"); + if (pos == QuicString::npos) { + return false; + } + (*headers)[":scheme"] = url.substr(0, pos); + size_t start = pos + 3; + pos = url.find("/", start); + if (pos == QuicString::npos) { + (*headers)[":authority"] = url.substr(start); + (*headers)[":path"] = "/"; + return true; + } + (*headers)[":authority"] = url.substr(start, pos - start); + (*headers)[":path"] = url.substr(pos); + return true; +} + +// static +QuicString SpdyUtils::GetPushPromiseUrl(QuicStringPiece scheme, + QuicStringPiece authority, + QuicStringPiece path) { + // RFC 7540, Section 8.1.2.3: The ":path" pseudo-header field includes the + // path and query parts of the target URI (the "path-absolute" production + // and optionally a '?' character followed by the "query" production (see + // Sections 3.3 and 3.4 of RFC3986). A request in asterisk form includes the + // value '*' for the ":path" pseudo-header field. + // + // This pseudo-header field MUST NOT be empty for "http" or "https" URIs; + // "http" or "https" URIs that do not contain a path MUST include a value of + // '/'. The exception to this rule is an OPTIONS request for an "http" or + // "https" URI that does not include a path component; these MUST include a + // ":path" pseudo-header with a value of '*' (see RFC7230, Section 5.3.4). + // + // In addition to the above restriction from RFC 7540, note that RFC3986 + // defines the "path-absolute" construction as starting with "/" but not "//". + // + // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any + // subsequent CONTINUATION frames MUST be a valid and complete set of request + // header fields (Section 8.1.2.3). The server MUST include a method in the + // ":method" pseudo-header field that is safe and cacheable. + // + // RFC 7231, Section 4.2.1: + // ... this specification defines GET, HEAD, and POST as cacheable, ... + // + // Since the OPTIONS method is not cacheable, it cannot be the method of a + // PUSH_PROMISE. Therefore, the exception mentioned in RFC 7540, Section + // 8.1.2.3 about OPTIONS requests does not apply here (i.e. ":path" cannot be + // "*"). + if (path.empty() || path[0] != '/' || (path.size() >= 2 && path[1] == '/')) { + return QuicString(); + } + + // Validate the scheme; this is to ensure a scheme of "foo://bar" is not + // parsed as a URL of "foo://bar://baz" when combined with a host of "baz". + std::string canonical_scheme; + url::StdStringCanonOutput canon_scheme_output(&canonical_scheme); + url::Component canon_component; + url::Component scheme_component(0, scheme.size()); + + if (!url::CanonicalizeScheme(scheme.data(), scheme_component, + &canon_scheme_output, &canon_component) || + !canon_component.is_nonempty() || canon_component.begin != 0) { + return QuicString(); + } + canonical_scheme.resize(canon_component.len + 1); + + // Validate the authority; this is to ensure an authority such as + // "host/path" is not accepted, as when combined with a scheme like + // "http://", could result in a URL of "http://host/path". + url::Component auth_component(0, authority.size()); + url::Component username_component; + url::Component password_component; + url::Component host_component; + url::Component port_component; + + url::ParseAuthority(authority.data(), auth_component, &username_component, + &password_component, &host_component, &port_component); + + // RFC 7540, Section 8.1.2.3: The authority MUST NOT include the deprecated + // "userinfo" subcomponent for "http" or "https" schemed URIs. + // + // Note: Although |canonical_scheme| has not yet been checked for that, as + // it is performed later in processing, only "http" and "https" schemed + // URIs are supported for PUSH. + if (username_component.is_valid() || password_component.is_valid()) { + return QuicString(); + } + + // Failed parsing or no host present. ParseAuthority() will ensure that + // host_component + port_component cover the entire string, if + // username_component and password_component are not present. + if (!host_component.is_nonempty()) { + return QuicString(); + } + + // Validate the port (if present; it's optional). + int parsed_port_number = url::PORT_INVALID; + if (port_component.is_nonempty()) { + parsed_port_number = url::ParsePort(authority.data(), port_component); + if (parsed_port_number < 0 && parsed_port_number != url::PORT_UNSPECIFIED) { + return QuicString(); + } + } + + // Validate the host by attempting to canonicalize it. Invalid characters + // will result in a canonicalization failure (e.g. '/') + std::string canon_host; + url::StdStringCanonOutput canon_host_output(&canon_host); + canon_component.reset(); + if (!url::CanonicalizeHost(authority.data(), host_component, + &canon_host_output, &canon_component) || + !canon_component.is_nonempty() || canon_component.begin != 0) { + return QuicString(); + } + + // At this point, "authority" has been validated to either be of the form + // 'host:port' or 'host', with 'host' being a valid domain or IP address, + // and 'port' (if present), being a valid port. Attempt to construct a + // URL of just the (scheme, host, port), which should be safe and will not + // result in ambiguous parsing. + // + // This also enforces that all PUSHed URLs are either HTTP or HTTPS-schemed + // URIs, consistent with the other restrictions enforced above. + // + // Note: url::CanonicalizeScheme() will have added the ':' to + // |canonical_scheme|. + GURL origin_url(canonical_scheme + "//" + std::string(authority)); + if (!origin_url.is_valid() || !origin_url.SchemeIsHTTPOrHTTPS() || + // The following checks are merely defense in depth. + origin_url.has_username() || origin_url.has_password() || + (origin_url.has_path() && origin_url.path_piece() != "/") || + origin_url.has_query() || origin_url.has_ref()) { + return QuicString(); + } + + // Attempt to parse the path. + std::string spec = origin_url.GetWithEmptyPath().spec(); + spec.pop_back(); // Remove the '/', as ":path" must contain it. + spec.append(std::string(path)); + + // Attempt to parse the full URL, with the path as well. Ensure there is no + // fragment to the query. + GURL full_url(spec); + if (!full_url.is_valid() || full_url.has_ref()) { + return QuicString(); + } + + return full_url.spec(); +} + +} // namespace quic
diff --git a/quic/core/http/spdy_utils.h b/quic/core/http/spdy_utils.h new file mode 100644 index 0000000..073eeeb --- /dev/null +++ b/quic/core/http/spdy_utils.h
@@ -0,0 +1,70 @@ +// Copyright (c) 2013 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_HTTP_SPDY_UTILS_H_ +#define QUICHE_QUIC_CORE_HTTP_SPDY_UTILS_H_ + +#include <cstddef> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE SpdyUtils { + public: + SpdyUtils() = delete; + + // Populate |content length| with the value of the content-length header. + // Returns true on success, false if parsing fails or content-length header is + // missing. + static bool ExtractContentLengthFromHeaders(int64_t* content_length, + spdy::SpdyHeaderBlock* headers); + + // Copies a list of headers to a SpdyHeaderBlock. + static bool CopyAndValidateHeaders(const QuicHeaderList& header_list, + int64_t* content_length, + spdy::SpdyHeaderBlock* headers); + + // Copies a list of headers to a SpdyHeaderBlock. + static bool CopyAndValidateTrailers(const QuicHeaderList& header_list, + size_t* final_byte_offset, + spdy::SpdyHeaderBlock* trailers); + + // Returns a canonicalized URL composed from the :scheme, :authority, and + // :path headers of a PUSH_PROMISE. Returns empty string if the headers do not + // conform to HTTP/2 spec or if the ":method" header contains a forbidden + // method for PUSH_PROMISE. + static QuicString GetPromisedUrlFromHeaders( + const spdy::SpdyHeaderBlock& headers); + + // Returns hostname, or empty string if missing. + static QuicString GetPromisedHostNameFromHeaders( + const spdy::SpdyHeaderBlock& headers); + + // Returns true if result of |GetPromisedUrlFromHeaders()| is non-empty + // and is a well-formed URL. + static bool PromisedUrlIsValid(const spdy::SpdyHeaderBlock& headers); + + // Populates the fields of |headers| to make a GET request of |url|, + // which must be fully-qualified. + static bool PopulateHeaderBlockFromUrl(const QuicString url, + spdy::SpdyHeaderBlock* headers); + + // Returns a canonical, valid URL for a PUSH_PROMISE with the specified + // ":scheme", ":authority", and ":path" header fields, or an empty + // string if the resulting URL is not valid or supported. + static QuicString GetPushPromiseUrl(QuicStringPiece scheme, + QuicStringPiece authority, + QuicStringPiece path); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_SPDY_UTILS_H_
diff --git a/quic/core/http/spdy_utils_test.cc b/quic/core/http/spdy_utils_test.cc new file mode 100644 index 0000000..6cd2f1d --- /dev/null +++ b/quic/core/http/spdy_utils_test.cc
@@ -0,0 +1,519 @@ +// Copyright 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 <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using spdy::SpdyHeaderBlock; +using testing::Pair; +using testing::UnorderedElementsAre; + +namespace quic { +namespace test { + +static std::unique_ptr<QuicHeaderList> FromList( + const QuicHeaderList::ListType& src) { + std::unique_ptr<QuicHeaderList> headers(new QuicHeaderList); + headers->OnHeaderBlockStart(); + for (const auto& p : src) { + headers->OnHeader(p.first, p.second); + } + headers->OnHeaderBlockEnd(0, 0); + return headers; +} + +using CopyAndValidateHeaders = QuicTest; + +TEST_F(CopyAndValidateHeaders, NormalUsage) { + auto headers = FromList({// All cookie crumbs are joined. + {"cookie", " part 1"}, + {"cookie", "part 2 "}, + {"cookie", "part3"}, + + // Already-delimited headers are passed through. + {"passed-through", QuicString("foo\0baz", 7)}, + + // Other headers are joined on \0. + {"joined", "value 1"}, + {"joined", "value 2"}, + + // Empty headers remain empty. + {"empty", ""}, + + // Joined empty headers work as expected. + {"empty-joined", ""}, + {"empty-joined", "foo"}, + {"empty-joined", ""}, + {"empty-joined", ""}, + + // Non-continguous cookie crumb. + {"cookie", " fin!"}}); + + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_TRUE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); + EXPECT_THAT(block, + UnorderedElementsAre( + Pair("cookie", " part 1; part 2 ; part3; fin!"), + Pair("passed-through", QuicStringPiece("foo\0baz", 7)), + Pair("joined", QuicStringPiece("value 1\0value 2", 15)), + Pair("empty", ""), + Pair("empty-joined", QuicStringPiece("\0foo\0\0", 6)))); + EXPECT_EQ(-1, content_length); +} + +TEST_F(CopyAndValidateHeaders, EmptyName) { + auto headers = FromList({{"foo", "foovalue"}, {"", "barvalue"}, {"baz", ""}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_FALSE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); +} + +TEST_F(CopyAndValidateHeaders, UpperCaseName) { + auto headers = + FromList({{"foo", "foovalue"}, {"bar", "barvalue"}, {"bAz", ""}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_FALSE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); +} + +TEST_F(CopyAndValidateHeaders, MultipleContentLengths) { + auto headers = FromList({{"content-length", "9"}, + {"foo", "foovalue"}, + {"content-length", "9"}, + {"bar", "barvalue"}, + {"baz", ""}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_TRUE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); + EXPECT_THAT(block, UnorderedElementsAre( + Pair("foo", "foovalue"), Pair("bar", "barvalue"), + Pair("content-length", QuicStringPiece("9\09", 3)), + Pair("baz", ""))); + EXPECT_EQ(9, content_length); +} + +TEST_F(CopyAndValidateHeaders, InconsistentContentLengths) { + auto headers = FromList({{"content-length", "9"}, + {"foo", "foovalue"}, + {"content-length", "8"}, + {"bar", "barvalue"}, + {"baz", ""}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_FALSE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); +} + +TEST_F(CopyAndValidateHeaders, LargeContentLength) { + auto headers = FromList({{"content-length", "9000000000"}, + {"foo", "foovalue"}, + {"bar", "barvalue"}, + {"baz", ""}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_TRUE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); + EXPECT_THAT(block, UnorderedElementsAre( + Pair("foo", "foovalue"), Pair("bar", "barvalue"), + Pair("content-length", QuicStringPiece("9000000000")), + Pair("baz", ""))); + EXPECT_EQ(9000000000, content_length); +} + +TEST_F(CopyAndValidateHeaders, MultipleValues) { + auto headers = FromList({{"foo", "foovalue"}, + {"bar", "barvalue"}, + {"baz", ""}, + {"foo", "boo"}, + {"baz", "buzz"}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_TRUE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); + EXPECT_THAT(block, UnorderedElementsAre( + Pair("foo", QuicStringPiece("foovalue\0boo", 12)), + Pair("bar", "barvalue"), + Pair("baz", QuicStringPiece("\0buzz", 5)))); + EXPECT_EQ(-1, content_length); +} + +TEST_F(CopyAndValidateHeaders, MoreThanTwoValues) { + auto headers = FromList({{"set-cookie", "value1"}, + {"set-cookie", "value2"}, + {"set-cookie", "value3"}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_TRUE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); + EXPECT_THAT( + block, UnorderedElementsAre(Pair( + "set-cookie", QuicStringPiece("value1\0value2\0value3", 20)))); + EXPECT_EQ(-1, content_length); +} + +TEST_F(CopyAndValidateHeaders, Cookie) { + auto headers = FromList({{"foo", "foovalue"}, + {"bar", "barvalue"}, + {"cookie", "value1"}, + {"baz", ""}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_TRUE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); + EXPECT_THAT(block, UnorderedElementsAre( + Pair("foo", "foovalue"), Pair("bar", "barvalue"), + Pair("cookie", "value1"), Pair("baz", ""))); + EXPECT_EQ(-1, content_length); +} + +TEST_F(CopyAndValidateHeaders, MultipleCookies) { + auto headers = FromList({{"foo", "foovalue"}, + {"bar", "barvalue"}, + {"cookie", "value1"}, + {"baz", ""}, + {"cookie", "value2"}}); + int64_t content_length = -1; + SpdyHeaderBlock block; + ASSERT_TRUE( + SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block)); + EXPECT_THAT(block, UnorderedElementsAre( + Pair("foo", "foovalue"), Pair("bar", "barvalue"), + Pair("cookie", "value1; value2"), Pair("baz", ""))); + EXPECT_EQ(-1, content_length); +} + +using CopyAndValidateTrailers = QuicTest; + +TEST_F(CopyAndValidateTrailers, SimplestValidList) { + // Verify that the simplest trailers are valid: just a final byte offset that + // gets parsed successfully. + auto trailers = FromList({{kFinalOffsetHeaderKey, "1234"}}); + size_t final_byte_offset = 0; + SpdyHeaderBlock block; + EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset, + &block)); + EXPECT_EQ(1234u, final_byte_offset); +} + +TEST_F(CopyAndValidateTrailers, EmptyTrailerList) { + // An empty trailer list will fail as required key kFinalOffsetHeaderKey is + // not present. + QuicHeaderList trailers; + size_t final_byte_offset = 0; + SpdyHeaderBlock block; + EXPECT_FALSE( + SpdyUtils::CopyAndValidateTrailers(trailers, &final_byte_offset, &block)); +} + +TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotPresent) { + // Validation fails if required kFinalOffsetHeaderKey is not present, even if + // the rest of the header block is valid. + auto trailers = FromList({{"key", "value"}}); + size_t final_byte_offset = 0; + SpdyHeaderBlock block; + EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset, + &block)); +} + +TEST_F(CopyAndValidateTrailers, EmptyName) { + // Trailer validation will fail with an empty header key, in an otherwise + // valid block of trailers. + auto trailers = FromList({{"", "value"}, {kFinalOffsetHeaderKey, "1234"}}); + size_t final_byte_offset = 0; + SpdyHeaderBlock block; + EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset, + &block)); +} + +TEST_F(CopyAndValidateTrailers, PseudoHeaderInTrailers) { + // Pseudo headers are illegal in trailers. + auto trailers = + FromList({{":pseudo_key", "value"}, {kFinalOffsetHeaderKey, "1234"}}); + size_t final_byte_offset = 0; + SpdyHeaderBlock block; + EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset, + &block)); +} + +TEST_F(CopyAndValidateTrailers, DuplicateTrailers) { + // Duplicate trailers are allowed, and their values are concatenated into a + // single string delimted with '\0'. Some of the duplicate headers + // deliberately have an empty value. + auto trailers = FromList({{"key", "value0"}, + {"key", "value1"}, + {"key", ""}, + {"key", ""}, + {"key", "value2"}, + {"key", ""}, + {kFinalOffsetHeaderKey, "1234"}, + {"other_key", "value"}, + {"key", "non_contiguous_duplicate"}}); + size_t final_byte_offset = 0; + SpdyHeaderBlock block; + EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset, + &block)); + EXPECT_THAT( + block, + UnorderedElementsAre( + Pair("key", + QuicStringPiece( + "value0\0value1\0\0\0value2\0\0non_contiguous_duplicate", + 48)), + Pair("other_key", "value"))); +} + +TEST_F(CopyAndValidateTrailers, DuplicateCookies) { + // Duplicate cookie headers in trailers should be concatenated into a single + // "; " delimted string. + auto headers = FromList({{"cookie", " part 1"}, + {"cookie", "part 2 "}, + {"cookie", "part3"}, + {"key", "value"}, + {kFinalOffsetHeaderKey, "1234"}, + {"cookie", " non_contiguous_cookie!"}}); + + size_t final_byte_offset = 0; + SpdyHeaderBlock block; + EXPECT_TRUE( + SpdyUtils::CopyAndValidateTrailers(*headers, &final_byte_offset, &block)); + EXPECT_THAT( + block, + UnorderedElementsAre( + Pair("cookie", " part 1; part 2 ; part3; non_contiguous_cookie!"), + Pair("key", "value"))); +} + +using GetPromisedUrlFromHeaders = QuicTest; + +TEST_F(GetPromisedUrlFromHeaders, Basic) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":scheme"] = "https"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":authority"] = "www.google.com"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":path"] = "/index.html"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), + "https://www.google.com/index.html"); + headers["key1"] = "value1"; + headers["key2"] = "value2"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), + "https://www.google.com/index.html"); +} + +TEST_F(GetPromisedUrlFromHeaders, Connect) { + SpdyHeaderBlock headers; + headers[":method"] = "CONNECT"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":authority"] = "www.google.com"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":scheme"] = "https"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":path"] = "https"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); +} + +TEST_F(GetPromisedUrlFromHeaders, InvalidUserinfo) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + headers[":authority"] = "user@www.google.com"; + headers[":scheme"] = "https"; + headers[":path"] = "/"; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); +} + +TEST_F(GetPromisedUrlFromHeaders, InvalidPath) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + headers[":authority"] = "www.google.com"; + headers[":scheme"] = "https"; + headers[":path"] = ""; + EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); +} + +using GetPromisedHostNameFromHeaders = QuicTest; + +TEST_F(GetPromisedHostNameFromHeaders, NormalUsage) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), ""); + headers[":scheme"] = "https"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), ""); + headers[":authority"] = "www.google.com"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), ""); + headers[":path"] = "/index.html"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), + "www.google.com"); + headers["key1"] = "value1"; + headers["key2"] = "value2"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), + "www.google.com"); + headers[":authority"] = "www.google.com:6666"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), + "www.google.com"); + headers[":authority"] = "192.168.1.1"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "192.168.1.1"); + headers[":authority"] = "192.168.1.1:6666"; + EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "192.168.1.1"); +} + +using PopulateHeaderBlockFromUrl = QuicTest; + +TEST_F(PopulateHeaderBlockFromUrl, NormalUsage) { + QuicString url = "https://www.google.com/index.html"; + SpdyHeaderBlock headers; + EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers)); + EXPECT_EQ("https", headers[":scheme"].as_string()); + EXPECT_EQ("www.google.com", headers[":authority"].as_string()); + EXPECT_EQ("/index.html", headers[":path"].as_string()); +} + +TEST_F(PopulateHeaderBlockFromUrl, UrlWithNoPath) { + QuicString url = "https://www.google.com"; + SpdyHeaderBlock headers; + EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers)); + EXPECT_EQ("https", headers[":scheme"].as_string()); + EXPECT_EQ("www.google.com", headers[":authority"].as_string()); + EXPECT_EQ("/", headers[":path"].as_string()); +} + +TEST_F(PopulateHeaderBlockFromUrl, Failure) { + SpdyHeaderBlock headers; + EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/", &headers)); + EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/index.html", &headers)); + EXPECT_FALSE( + SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers)); +} + +using PushPromiseUrlTest = QuicTest; + +TEST_F(PushPromiseUrlTest, GetPushPromiseUrl) { + // Test rejection of various inputs. + EXPECT_EQ("", + SpdyUtils::GetPushPromiseUrl("file", "localhost", "/etc/password")); + EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("file", "", + "/C:/Windows/System32/Config/")); + EXPECT_EQ("", + SpdyUtils::GetPushPromiseUrl("", "https://www.google.com", "/")); + + EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https://www.google.com", + "www.google.com", "/")); + EXPECT_EQ("", + SpdyUtils::GetPushPromiseUrl("https://", "www.google.com", "/")); + EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "/")); + EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "www.google.com/")); + EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com/", "/")); + EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com", "")); + EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google", ".com/")); + + // Test acception/rejection of various input combinations. + // |input_headers| is an array of pairs. The first value of each pair is a + // string that will be used as one of the inputs of GetPushPromiseUrl(). The + // second value of each pair is a bitfield where the lowest 3 bits indicate + // for which headers that string is valid (in a PUSH_PROMISE). For example, + // the string "http" would be valid for both the ":scheme" and ":authority" + // headers, so the bitfield paired with it is set to SCHEME | AUTH. + const unsigned char SCHEME = (1u << 0); + const unsigned char AUTH = (1u << 1); + const unsigned char PATH = (1u << 2); + const std::pair<const char*, unsigned char> input_headers[] = { + {"http", SCHEME | AUTH}, + {"https", SCHEME | AUTH}, + {"hTtP", SCHEME | AUTH}, + {"HTTPS", SCHEME | AUTH}, + {"www.google.com", AUTH}, + {"90af90e0", AUTH}, + {"12foo%20-bar:00001233", AUTH}, + {"GOO\u200b\u2060\ufeffgoo", AUTH}, + {"192.168.0.5", AUTH}, + {"[::ffff:192.168.0.1.]", AUTH}, + {"http:", AUTH}, + {"bife l", AUTH}, + {"/", PATH}, + {"/foo/bar/baz", PATH}, + {"/%20-2DVdkj.cie/foe_.iif/", PATH}, + {"http://", 0}, + {":443", 0}, + {":80/eddd", 0}, + {"google.com:-0", 0}, + {"google.com:65536", 0}, + {"http://google.com", 0}, + {"http://google.com:39", 0}, + {"//google.com/foo", 0}, + {".com/", 0}, + {"http://www.google.com/", 0}, + {"http://foo:439", 0}, + {"[::ffff:192.168", 0}, + {"]/", 0}, + {"//", 0}}; + for (size_t i = 0; i < QUIC_ARRAYSIZE(input_headers); ++i) { + bool should_accept = (input_headers[i].second & SCHEME); + for (size_t j = 0; j < QUIC_ARRAYSIZE(input_headers); ++j) { + bool should_accept_2 = should_accept && (input_headers[j].second & AUTH); + for (size_t k = 0; k < QUIC_ARRAYSIZE(input_headers); ++k) { + // |should_accept_3| indicates whether or not GetPushPromiseUrl() is + // expected to accept this input combination. + bool should_accept_3 = + should_accept_2 && (input_headers[k].second & PATH); + + std::string url = SpdyUtils::GetPushPromiseUrl(input_headers[i].first, + input_headers[j].first, + input_headers[k].first); + + ::testing::AssertionResult result = ::testing::AssertionSuccess(); + if (url.empty() == should_accept_3) { + result = ::testing::AssertionFailure() + << "GetPushPromiseUrl() accepted/rejected the inputs when " + "it shouldn't have." + << std::endl + << " scheme: " << input_headers[i].first << std::endl + << " authority: " << input_headers[j].first << std::endl + << " path: " << input_headers[k].first << std::endl + << "Output: " << url << std::endl; + } + ASSERT_TRUE(result); + } + } + } + + // Test canonicalization of various valid inputs. + EXPECT_EQ("http://www.google.com/", + SpdyUtils::GetPushPromiseUrl("http", "www.google.com", "/")); + EXPECT_EQ( + "https://www.goo-gle.com/fOOo/baRR", + SpdyUtils::GetPushPromiseUrl("hTtPs", "wWw.gOo-gLE.cOm", "/fOOo/baRR")); + EXPECT_EQ("https://www.goo-gle.com:3278/pAth/To/reSOurce", + SpdyUtils::GetPushPromiseUrl("hTtPs", "Www.gOo-Gle.Com:000003278", + "/pAth/To/reSOurce")); + EXPECT_EQ("https://foo%20bar/foo/bar/baz", + SpdyUtils::GetPushPromiseUrl("https", "foo bar", "/foo/bar/baz")); + EXPECT_EQ("http://foo.com:70/e/", + SpdyUtils::GetPushPromiseUrl("http", "foo.com:0000070", "/e/")); + EXPECT_EQ( + "http://192.168.0.1:70/e/", + SpdyUtils::GetPushPromiseUrl("http", "0300.0250.00.01:0070", "/e/")); + EXPECT_EQ("http://192.168.0.1/e/", + SpdyUtils::GetPushPromiseUrl("http", "0xC0a80001", "/e/")); + EXPECT_EQ("http://[::c0a8:1]/", + SpdyUtils::GetPushPromiseUrl("http", "[::192.168.0.1]", "/")); + EXPECT_EQ( + "https://[::ffff:c0a8:1]/", + SpdyUtils::GetPushPromiseUrl("https", "[::ffff:0xC0.0Xa8.0x0.0x1]", "/")); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/legacy_quic_stream_id_manager.cc b/quic/core/legacy_quic_stream_id_manager.cc new file mode 100644 index 0000000..6fb662a --- /dev/null +++ b/quic/core/legacy_quic_stream_id_manager.cc
@@ -0,0 +1,139 @@ +// Copyright (c) 2018 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/legacy_quic_stream_id_manager.h" + +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" + +namespace quic { + +#define ENDPOINT \ + (session_->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") + +LegacyQuicStreamIdManager::LegacyQuicStreamIdManager( + QuicSession* session, + size_t max_open_outgoing_streams, + size_t max_open_incoming_streams) + : session_(session), + max_open_outgoing_streams_(max_open_outgoing_streams), + max_open_incoming_streams_(max_open_incoming_streams), + next_outgoing_stream_id_( + QuicUtils::GetCryptoStreamId( + session->connection()->transport_version()) + + (session->perspective() == Perspective::IS_SERVER ? 1 : 2)), + largest_peer_created_stream_id_( + session->perspective() == Perspective::IS_SERVER + ? QuicUtils::GetCryptoStreamId( + session->connection()->transport_version()) + : QuicUtils::GetInvalidStreamId( + session->connection()->transport_version())) {} + +LegacyQuicStreamIdManager::~LegacyQuicStreamIdManager() { + QUIC_LOG_IF(WARNING, + session_->num_locally_closed_incoming_streams_highest_offset() > + max_open_incoming_streams_) + << "Surprisingly high number of locally closed peer initiated streams" + "still waiting for final byte offset: " + << session_->num_locally_closed_incoming_streams_highest_offset(); + QUIC_LOG_IF(WARNING, + session_->GetNumLocallyClosedOutgoingStreamsHighestOffset() > + max_open_outgoing_streams_) + << "Surprisingly high number of locally closed self initiated streams" + "still waiting for final byte offset: " + << session_->GetNumLocallyClosedOutgoingStreamsHighestOffset(); +} + +bool LegacyQuicStreamIdManager::CanOpenNextOutgoingStream( + size_t current_num_open_outgoing_streams) const { + if (current_num_open_outgoing_streams >= max_open_outgoing_streams_) { + QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " + << "Already " << current_num_open_outgoing_streams + << " open."; + return false; + } + return true; +} + +bool LegacyQuicStreamIdManager::CanOpenIncomingStream( + size_t current_num_open_incoming_streams) const { + // Check if the new number of open streams would cause the number of + // open streams to exceed the limit. + return current_num_open_incoming_streams < max_open_incoming_streams_; +} + +bool LegacyQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( + const QuicStreamId stream_id) { + available_streams_.erase(stream_id); + + if (largest_peer_created_stream_id_ != + QuicUtils::GetInvalidStreamId( + session_->connection()->transport_version()) && + stream_id <= largest_peer_created_stream_id_) { + return true; + } + + // Check if the new number of available streams would cause the number of + // available streams to exceed the limit. Note that the peer can create + // only alternately-numbered streams. + size_t additional_available_streams = + (stream_id - largest_peer_created_stream_id_) / 2 - 1; + size_t new_num_available_streams = + GetNumAvailableStreams() + additional_available_streams; + if (new_num_available_streams > MaxAvailableStreams()) { + QUIC_DLOG(INFO) << ENDPOINT + << "Failed to create a new incoming stream with id:" + << stream_id << ". There are already " + << GetNumAvailableStreams() + << " streams available, which would become " + << new_num_available_streams << ", which exceeds the limit " + << MaxAvailableStreams() << "."; + session_->connection()->CloseConnection( + QUIC_TOO_MANY_AVAILABLE_STREAMS, + QuicStrCat(new_num_available_streams, " above ", MaxAvailableStreams()), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + for (QuicStreamId id = largest_peer_created_stream_id_ + 2; id < stream_id; + id += 2) { + available_streams_.insert(id); + } + largest_peer_created_stream_id_ = stream_id; + + return true; +} + +QuicStreamId LegacyQuicStreamIdManager::GetNextOutgoingStreamId() { + QuicStreamId id = next_outgoing_stream_id_; + next_outgoing_stream_id_ += 2; + return id; +} + +bool LegacyQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { + if (!IsIncomingStream(id)) { + // Stream IDs under next_ougoing_stream_id_ are either open or previously + // open but now closed. + return id >= next_outgoing_stream_id_; + } + // For peer created streams, we also need to consider available streams. + return largest_peer_created_stream_id_ == + QuicUtils::GetInvalidStreamId( + session_->connection()->transport_version()) || + id > largest_peer_created_stream_id_ || + QuicContainsKey(available_streams_, id); +} + +bool LegacyQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const { + return id % 2 != next_outgoing_stream_id_ % 2; +} + +size_t LegacyQuicStreamIdManager::GetNumAvailableStreams() const { + return available_streams_.size(); +} + +size_t LegacyQuicStreamIdManager::MaxAvailableStreams() const { + return max_open_incoming_streams_ * kMaxAvailableStreamsMultiplier; +} + +} // namespace quic
diff --git a/quic/core/legacy_quic_stream_id_manager.h b/quic/core/legacy_quic_stream_id_manager.h new file mode 100644 index 0000000..78d4545 --- /dev/null +++ b/quic/core/legacy_quic_stream_id_manager.h
@@ -0,0 +1,104 @@ +// Copyright (c) 2018 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_LEGACY_QUIC_STREAM_ID_MANAGER_H_ +#define QUICHE_QUIC_CORE_LEGACY_QUIC_STREAM_ID_MANAGER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h" + +namespace quic { + +namespace test { +class QuicSessionPeer; +} // namespace test + +class QuicSession; + +// Manages Google QUIC stream IDs. This manager is responsible for two +// questions: 1) can next outgoing stream ID be allocated (if yes, what is the +// next outgoing stream ID) and 2) can a new incoming stream be opened. +class QUIC_EXPORT_PRIVATE LegacyQuicStreamIdManager { + public: + LegacyQuicStreamIdManager(QuicSession* session, + size_t max_open_outgoing_streams, + size_t max_open_incoming_streams); + + ~LegacyQuicStreamIdManager(); + + // Returns true if the next outgoing stream ID can be allocated. + bool CanOpenNextOutgoingStream( + size_t current_num_open_outgoing_streams) const; + + // Returns true if a new incoming stream can be opened. + bool CanOpenIncomingStream(size_t current_num_open_incoming_streams) const; + + bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId id); + + // Returns true if |id| is still available. + bool IsAvailableStream(QuicStreamId id) const; + + // Returns the stream ID for a new outgoing stream, and increments the + // underlying counter. + QuicStreamId GetNextOutgoingStreamId(); + + // Return true if |id| is peer initiated. + bool IsIncomingStream(QuicStreamId id) const; + + size_t MaxAvailableStreams() const; + + void set_max_open_incoming_streams(size_t max_open_incoming_streams) { + max_open_incoming_streams_ = max_open_incoming_streams; + } + + void set_max_open_outgoing_streams(size_t max_open_outgoing_streams) { + max_open_outgoing_streams_ = max_open_outgoing_streams; + } + + void set_largest_peer_created_stream_id( + QuicStreamId largest_peer_created_stream_id) { + largest_peer_created_stream_id_ = largest_peer_created_stream_id; + } + + size_t max_open_incoming_streams() const { + return max_open_incoming_streams_; + } + + size_t max_open_outgoing_streams() const { + return max_open_outgoing_streams_; + } + + QuicStreamId next_outgoing_stream_id() const { + return next_outgoing_stream_id_; + } + + QuicStreamId largest_peer_created_stream_id() const { + return largest_peer_created_stream_id_; + } + + private: + friend class test::QuicSessionPeer; + + size_t GetNumAvailableStreams() const; + + // Not owned. + QuicSession* session_; + + // The maximum number of outgoing streams this connection can open. + size_t max_open_outgoing_streams_; + + // The maximum number of incoming streams this connection will allow. + size_t max_open_incoming_streams_; + + // The ID to use for the next outgoing stream. + QuicStreamId next_outgoing_stream_id_; + + // Set of stream ids that are less than the largest stream id that has been + // received, but are nonetheless available to be created. + QuicUnorderedSet<QuicStreamId> available_streams_; + + QuicStreamId largest_peer_created_stream_id_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_LEGACY_QUIC_STREAM_ID_MANAGER_H_
diff --git a/quic/core/legacy_quic_stream_id_manager_test.cc b/quic/core/legacy_quic_stream_id_manager_test.cc new file mode 100644 index 0000000..823cdc0 --- /dev/null +++ b/quic/core/legacy_quic_stream_id_manager_test.cc
@@ -0,0 +1,179 @@ +// Copyright (c) 2018 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/legacy_quic_stream_id_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +using testing::_; +using testing::StrictMock; + +class LegacyQuicStreamIdManagerTest : public QuicTest { + protected: + void Initialize(Perspective perspective) { + SetQuicReloadableFlag(quic_enable_version_99, false); + connection_ = new MockQuicConnection( + &helper_, &alarm_factory_, perspective, + ParsedVersionOfIndex(CurrentSupportedVersions(), 0)); + session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_); + manager_ = QuicSessionPeer::GetStreamIdManager(session_.get()); + session_->Initialize(); + } + + QuicStreamId GetNthClientInitiatedId(int n) { return 3 + 2 * n; } + + QuicStreamId GetNthServerInitiatedId(int n) { return 2 + 2 * n; } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* connection_; + std::unique_ptr<StrictMock<MockQuicSession>> session_; + LegacyQuicStreamIdManager* manager_; +}; + +class LegacyQuicStreamIdManagerTestServer + : public LegacyQuicStreamIdManagerTest { + protected: + LegacyQuicStreamIdManagerTestServer() { Initialize(Perspective::IS_SERVER); } +}; + +TEST_F(LegacyQuicStreamIdManagerTestServer, CanOpenNextOutgoingStream) { + EXPECT_TRUE(manager_->CanOpenNextOutgoingStream( + manager_->max_open_outgoing_streams() - 1)); + EXPECT_FALSE(manager_->CanOpenNextOutgoingStream( + manager_->max_open_outgoing_streams())); +} + +TEST_F(LegacyQuicStreamIdManagerTestServer, CanOpenIncomingStream) { + EXPECT_TRUE(manager_->CanOpenIncomingStream( + manager_->max_open_incoming_streams() - 1)); + EXPECT_FALSE( + manager_->CanOpenIncomingStream(manager_->max_open_incoming_streams())); +} + +TEST_F(LegacyQuicStreamIdManagerTestServer, AvailableStreams) { + ASSERT_TRUE( + manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(3))); + EXPECT_TRUE(manager_->IsAvailableStream(GetNthClientInitiatedId(1))); + EXPECT_TRUE(manager_->IsAvailableStream(GetNthClientInitiatedId(2))); + ASSERT_TRUE( + manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(2))); + ASSERT_TRUE( + manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(1))); +} + +TEST_F(LegacyQuicStreamIdManagerTestServer, MaxAvailableStreams) { + // Test that the server closes the connection if a client makes too many data + // streams available. The server accepts slightly more than the negotiated + // stream limit to deal with rare cases where a client FIN/RST is lost. + const size_t kMaxStreamsForTest = 10; + session_->OnConfigNegotiated(); + const size_t kAvailableStreamLimit = manager_->MaxAvailableStreams(); + EXPECT_EQ( + manager_->max_open_incoming_streams() * kMaxAvailableStreamsMultiplier, + manager_->MaxAvailableStreams()); + // The protocol specification requires that there can be at least 10 times + // as many available streams as the connection's maximum open streams. + EXPECT_LE(10 * kMaxStreamsForTest, kAvailableStreamLimit); + + EXPECT_TRUE( + manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(0))); + + // Establish available streams up to the server's limit. + const int kLimitingStreamId = + GetNthClientInitiatedId(kAvailableStreamLimit + 1); + // This exceeds the stream limit. In versions other than 99 + // this is allowed. Version 99 hews to the IETF spec and does + // not allow it. + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(kLimitingStreamId)); + // A further available stream will result in connection close. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _)); + + // This forces stream kLimitingStreamId + 2 to become available, which + // violates the quota. + EXPECT_FALSE( + manager_->MaybeIncreaseLargestPeerStreamId(kLimitingStreamId + 2 * 2)); +} + +TEST_F(LegacyQuicStreamIdManagerTestServer, MaximumAvailableOpenedStreams) { + QuicStreamId stream_id = GetNthClientInitiatedId(0); + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); + + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId( + stream_id + 2 * (manager_->max_open_incoming_streams() - 1))); +} + +TEST_F(LegacyQuicStreamIdManagerTestServer, TooManyAvailableStreams) { + QuicStreamId stream_id = GetNthClientInitiatedId(0); + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); + + // A stream ID which is too large to create. + QuicStreamId stream_id2 = + GetNthClientInitiatedId(2 * manager_->MaxAvailableStreams() + 4); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _)); + EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id2)); +} + +TEST_F(LegacyQuicStreamIdManagerTestServer, ManyAvailableStreams) { + // When max_open_streams_ is 200, should be able to create 200 streams + // out-of-order, that is, creating the one with the largest stream ID first. + manager_->set_max_open_incoming_streams(200); + QuicStreamId stream_id = GetNthClientInitiatedId(0); + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + + // Create the largest stream ID of a threatened total of 200 streams. + // GetNth... starts at 0, so for 200 streams, get the 199th. + EXPECT_TRUE( + manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(199))); +} + +TEST_F(LegacyQuicStreamIdManagerTestServer, + TestMaxIncomingAndOutgoingStreamsAllowed) { + // Tests that on server side, the value of max_open_incoming/outgoing + // streams are setup correctly during negotiation. The value for outgoing + // stream is limited to negotiated value and for incoming stream it is set to + // be larger than that. + session_->OnConfigNegotiated(); + // The max number of open outgoing streams is less than that of incoming + // streams, and it should be same as negotiated value. + EXPECT_LT(manager_->max_open_outgoing_streams(), + manager_->max_open_incoming_streams()); + EXPECT_EQ(manager_->max_open_outgoing_streams(), + kDefaultMaxStreamsPerConnection); + EXPECT_GT(manager_->max_open_incoming_streams(), + kDefaultMaxStreamsPerConnection); +} + +class LegacyQuicStreamIdManagerTestClient + : public LegacyQuicStreamIdManagerTest { + protected: + LegacyQuicStreamIdManagerTestClient() { Initialize(Perspective::IS_CLIENT); } +}; + +TEST_F(LegacyQuicStreamIdManagerTestClient, + TestMaxIncomingAndOutgoingStreamsAllowed) { + // Tests that on client side, the value of max_open_incoming/outgoing + // streams are setup correctly during negotiation. When flag is true, the + // value for outgoing stream is limited to negotiated value and for incoming + // stream it is set to be larger than that. + session_->OnConfigNegotiated(); + EXPECT_LT(manager_->max_open_outgoing_streams(), + manager_->max_open_incoming_streams()); + EXPECT_EQ(manager_->max_open_outgoing_streams(), + kDefaultMaxStreamsPerConnection); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/packet_number_indexed_queue.h b/quic/core/packet_number_indexed_queue.h new file mode 100644 index 0000000..ab2e20b --- /dev/null +++ b/quic/core/packet_number_indexed_queue.h
@@ -0,0 +1,211 @@ +// Copyright (c) 2017 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_PACKET_NUMBER_INDEXED_QUEUE_H_ +#define QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_ + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" + +namespace quic { + +// PacketNumberIndexedQueue is a queue of mostly continuous numbered entries +// which supports the following operations: +// - adding elements to the end of the queue, or at some point past the end +// - removing elements in any order +// - retrieving elements +// If all elements are inserted in order, all of the operations above are +// amortized O(1) time. +// +// Internally, the data structure is a deque where each element is marked as +// present or not. The deque starts at the lowest present index. Whenever an +// element is removed, it's marked as not present, and the front of the deque is +// cleared of elements that are not present. +// +// The tail of the queue is not cleared due to the assumption of entries being +// inserted in order, though removing all elements of the queue will return it +// to its initial state. +// +// Note that this data structure is inherently hazardous, since an addition of +// just two entries will cause it to consume all of the memory available. +// Because of that, it is not a general-purpose container and should not be used +// as one. +template <typename T> +class PacketNumberIndexedQueue { + public: + PacketNumberIndexedQueue() : number_of_present_entries_(0) {} + + // Retrieve the entry associated with the packet number. Returns the pointer + // to the entry in case of success, or nullptr if the entry does not exist. + T* GetEntry(QuicPacketNumber packet_number); + const T* GetEntry(QuicPacketNumber packet_number) const; + + // Inserts data associated |packet_number| into (or past) the end of the + // queue, filling up the missing intermediate entries as necessary. Returns + // true if the element has been inserted successfully, false if it was already + // in the queue or inserted out of order. + template <typename... Args> + bool Emplace(QuicPacketNumber packet_number, Args&&... args); + + // Removes data associated with |packet_number| and frees the slots in the + // queue as necessary. + bool Remove(QuicPacketNumber packet_number); + + bool IsEmpty() const { return number_of_present_entries_ == 0; } + + // Returns the number of entries in the queue. + size_t number_of_present_entries() const { + return number_of_present_entries_; + } + + // Returns the number of entries allocated in the underlying deque. This is + // proportional to the memory usage of the queue. + size_t entry_slots_used() const { return entries_.size(); } + + // Packet number of the first entry in the queue. + QuicPacketNumber first_packet() const { return first_packet_; } + + // Packet number of the last entry ever inserted in the queue. Note that the + // entry in question may have already been removed. Zero if the queue is + // empty. + QuicPacketNumber last_packet() const { + if (IsEmpty()) { + return QuicPacketNumber(); + } + return first_packet_ + entries_.size() - 1; + } + + private: + // Wrapper around T used to mark whether the entry is actually in the map. + struct EntryWrapper : T { + bool present; + + EntryWrapper() : present(false) {} + + template <typename... Args> + explicit EntryWrapper(Args&&... args) + : T(std::forward<Args>(args)...), present(true) {} + }; + + // Cleans up unused slots in the front after removing an element. + void Cleanup(); + + const EntryWrapper* GetEntryWrapper(QuicPacketNumber offset) const; + EntryWrapper* GetEntryWrapper(QuicPacketNumber offset) { + const auto* const_this = this; + return const_cast<EntryWrapper*>(const_this->GetEntryWrapper(offset)); + } + + QuicDeque<EntryWrapper> entries_; + size_t number_of_present_entries_; + QuicPacketNumber first_packet_; +}; + +template <typename T> +T* PacketNumberIndexedQueue<T>::GetEntry(QuicPacketNumber packet_number) { + EntryWrapper* entry = GetEntryWrapper(packet_number); + if (entry == nullptr) { + return nullptr; + } + return entry; +} + +template <typename T> +const T* PacketNumberIndexedQueue<T>::GetEntry( + QuicPacketNumber packet_number) const { + const EntryWrapper* entry = GetEntryWrapper(packet_number); + if (entry == nullptr) { + return nullptr; + } + return entry; +} + +template <typename T> +template <typename... Args> +bool PacketNumberIndexedQueue<T>::Emplace(QuicPacketNumber packet_number, + Args&&... args) { + if (!packet_number.IsInitialized()) { + QUIC_BUG << "Try to insert an uninitialized packet number"; + return false; + } + + if (IsEmpty()) { + DCHECK(entries_.empty()); + DCHECK(!first_packet_.IsInitialized()); + + entries_.emplace_back(std::forward<Args>(args)...); + number_of_present_entries_ = 1; + first_packet_ = packet_number; + return true; + } + + // Do not allow insertion out-of-order. + if (packet_number <= last_packet()) { + return false; + } + + // Handle potentially missing elements. + size_t offset = packet_number - first_packet_; + if (offset > entries_.size()) { + entries_.resize(offset); + } + + number_of_present_entries_++; + entries_.emplace_back(std::forward<Args>(args)...); + DCHECK_EQ(packet_number, last_packet()); + return true; +} + +template <typename T> +bool PacketNumberIndexedQueue<T>::Remove(QuicPacketNumber packet_number) { + EntryWrapper* entry = GetEntryWrapper(packet_number); + if (entry == nullptr) { + return false; + } + entry->present = false; + number_of_present_entries_--; + + if (packet_number == first_packet()) { + Cleanup(); + } + return true; +} + +template <typename T> +void PacketNumberIndexedQueue<T>::Cleanup() { + while (!entries_.empty() && !entries_.front().present) { + entries_.pop_front(); + first_packet_++; + } + if (entries_.empty()) { + first_packet_.Clear(); + } +} + +template <typename T> +auto PacketNumberIndexedQueue<T>::GetEntryWrapper( + QuicPacketNumber packet_number) const -> const EntryWrapper* { + if (!packet_number.IsInitialized() || IsEmpty() || + packet_number < first_packet_) { + return nullptr; + } + + uint64_t offset = packet_number - first_packet_; + if (offset >= entries_.size()) { + return nullptr; + } + + const EntryWrapper* entry = &entries_[offset]; + if (!entry->present) { + return nullptr; + } + + return entry; +} + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
diff --git a/quic/core/packet_number_indexed_queue_test.cc b/quic/core/packet_number_indexed_queue_test.cc new file mode 100644 index 0000000..294d2a4 --- /dev/null +++ b/quic/core/packet_number_indexed_queue_test.cc
@@ -0,0 +1,179 @@ +// Copyright (c) 2017 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/packet_number_indexed_queue.h" + +#include <limits> +#include <map> + +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace { + +class PacketNumberIndexedQueueTest : public QuicTest { + public: + PacketNumberIndexedQueueTest() {} + + protected: + PacketNumberIndexedQueue<QuicString> queue_; +}; + +TEST_F(PacketNumberIndexedQueueTest, InitialState) { + EXPECT_TRUE(queue_.IsEmpty()); + EXPECT_FALSE(queue_.first_packet().IsInitialized()); + EXPECT_FALSE(queue_.last_packet().IsInitialized()); + EXPECT_EQ(0u, queue_.number_of_present_entries()); + EXPECT_EQ(0u, queue_.entry_slots_used()); +} + +TEST_F(PacketNumberIndexedQueueTest, InsertingContinuousElements) { + ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1001), "one")); + EXPECT_EQ("one", *queue_.GetEntry(QuicPacketNumber(1001))); + + ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1002), "two")); + EXPECT_EQ("two", *queue_.GetEntry(QuicPacketNumber(1002))); + + EXPECT_FALSE(queue_.IsEmpty()); + EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(1002u), queue_.last_packet()); + EXPECT_EQ(2u, queue_.number_of_present_entries()); + EXPECT_EQ(2u, queue_.entry_slots_used()); +} + +TEST_F(PacketNumberIndexedQueueTest, InsertingOutOfOrder) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + + ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1003), "three")); + EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1002))); + EXPECT_EQ("three", *queue_.GetEntry(QuicPacketNumber(1003))); + + EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet()); + EXPECT_EQ(2u, queue_.number_of_present_entries()); + EXPECT_EQ(3u, queue_.entry_slots_used()); + + ASSERT_FALSE(queue_.Emplace(QuicPacketNumber(1002), "two")); +} + +TEST_F(PacketNumberIndexedQueueTest, InsertingIntoPast) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1000), "zero")); +} + +TEST_F(PacketNumberIndexedQueueTest, InsertingDuplicate) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1001), "one")); +} + +TEST_F(PacketNumberIndexedQueueTest, RemoveInTheMiddle) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + queue_.Emplace(QuicPacketNumber(1002), "two"); + queue_.Emplace(QuicPacketNumber(1003), "three"); + + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1002))); + EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1002))); + + EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet()); + EXPECT_EQ(2u, queue_.number_of_present_entries()); + EXPECT_EQ(3u, queue_.entry_slots_used()); + + EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1002), "two")); + EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(1004), "four")); +} + +TEST_F(PacketNumberIndexedQueueTest, RemoveAtImmediateEdges) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + queue_.Emplace(QuicPacketNumber(1002), "two"); + queue_.Emplace(QuicPacketNumber(1003), "three"); + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001))); + EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1001))); + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1003))); + EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1003))); + + EXPECT_EQ(QuicPacketNumber(1002u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet()); + EXPECT_EQ(1u, queue_.number_of_present_entries()); + EXPECT_EQ(2u, queue_.entry_slots_used()); + + EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(1004), "four")); +} + +TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantFront) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + queue_.Emplace(QuicPacketNumber(1002), "one (kinda)"); + queue_.Emplace(QuicPacketNumber(2001), "two"); + + EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet()); + EXPECT_EQ(3u, queue_.number_of_present_entries()); + EXPECT_EQ(1001u, queue_.entry_slots_used()); + + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1002))); + EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet()); + EXPECT_EQ(2u, queue_.number_of_present_entries()); + EXPECT_EQ(1001u, queue_.entry_slots_used()); + + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001))); + EXPECT_EQ(QuicPacketNumber(2001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet()); + EXPECT_EQ(1u, queue_.number_of_present_entries()); + EXPECT_EQ(1u, queue_.entry_slots_used()); +} + +TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantBack) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + queue_.Emplace(QuicPacketNumber(2001), "two"); + + EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet()); + + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(2001))); + EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet()); +} + +TEST_F(PacketNumberIndexedQueueTest, ClearAndRepopulate) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + queue_.Emplace(QuicPacketNumber(2001), "two"); + + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001))); + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(2001))); + EXPECT_TRUE(queue_.IsEmpty()); + EXPECT_FALSE(queue_.first_packet().IsInitialized()); + EXPECT_FALSE(queue_.last_packet().IsInitialized()); + + EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(101), "one")); + EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(201), "two")); + EXPECT_EQ(QuicPacketNumber(101u), queue_.first_packet()); + EXPECT_EQ(QuicPacketNumber(201u), queue_.last_packet()); +} + +TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsThatNeverExisted) { + ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1000))); + queue_.Emplace(QuicPacketNumber(1001), "one"); + ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1000))); + ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1002))); +} + +TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsTwice) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001))); + ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1001))); + ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1001))); +} + +TEST_F(PacketNumberIndexedQueueTest, ConstGetter) { + queue_.Emplace(QuicPacketNumber(1001), "one"); + const auto& const_queue = queue_; + + EXPECT_EQ("one", *const_queue.GetEntry(QuicPacketNumber(1001))); + EXPECT_EQ(nullptr, const_queue.GetEntry(QuicPacketNumber(1002))); +} + +} // namespace +} // namespace quic
diff --git a/quic/core/proto/cached_network_parameters.proto b/quic/core/proto/cached_network_parameters.proto new file mode 100644 index 0000000..d609be9 --- /dev/null +++ b/quic/core/proto/cached_network_parameters.proto
@@ -0,0 +1,43 @@ +// Copyright 2015 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package quic; + +// CachedNetworkParameters contains data that can be used to choose appropriate +// connection parameters (initial RTT, initial CWND, etc.) in new connections. +// Next id: 8 +message CachedNetworkParameters { + // Describes the state of the connection during which the supplied network + // parameters were calculated. + enum PreviousConnectionState { + SLOW_START = 0; + CONGESTION_AVOIDANCE = 1; + } + + // serving_region is used to decide whether or not the bandwidth estimate and + // min RTT are reasonable and if they should be used. + // For example a group of geographically close servers may share the same + // serving_region string if they are expected to have similar network + // performance. + optional string serving_region = 1; + // The server can supply a bandwidth estimate (in bytes/s) which it may re-use + // on receipt of a source-address token with this field set. + optional int32 bandwidth_estimate_bytes_per_second = 2; + // The maximum bandwidth seen to the client, not necessarily the latest. + optional int32 max_bandwidth_estimate_bytes_per_second = 5; + // Timestamp (seconds since UNIX epoch) that indicates when the max bandwidth + // was seen by the server. + optional int64 max_bandwidth_timestamp_seconds = 6; + // The min RTT seen on a previous connection can be used by the server to + // inform initial connection parameters for new connections. + optional int32 min_rtt_ms = 3; + // Encodes the PreviousConnectionState enum. + optional int32 previous_connection_state = 4; + // UNIX timestamp when this bandwidth estimate was created. + optional int64 timestamp = 7; +};
diff --git a/quic/core/proto/crypto_server_config.proto b/quic/core/proto/crypto_server_config.proto new file mode 100644 index 0000000..06db4a7 --- /dev/null +++ b/quic/core/proto/crypto_server_config.proto
@@ -0,0 +1,34 @@ +// Copyright 2018 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package quic; + +// QuicServerConfigProtobuf contains QUIC server config block and the private +// keys needed to prove ownership. +message QuicServerConfigProtobuf { + // config is a serialised config in QUIC wire format. + required bytes config = 1; + + // PrivateKey contains a QUIC tag of a key exchange algorithm and a + // serialised private key for that algorithm. The format of the serialised + // private key is specific to the algorithm in question. + message PrivateKey { + required uint32 tag = 1; + required string private_key = 2; + } + repeated PrivateKey key = 2; + + // primary_time contains a UNIX epoch seconds value that indicates when this + // config should become primary. + optional int64 primary_time = 3; + + // Relative priority of this config vs other configs with the same + // primary time. For use as a secondary sort key when selecting the + // primary config. + optional uint64 priority = 4; +};
diff --git a/quic/core/proto/source_address_token.proto b/quic/core/proto/source_address_token.proto new file mode 100644 index 0000000..1897a25 --- /dev/null +++ b/quic/core/proto/source_address_token.proto
@@ -0,0 +1,32 @@ +// Copyright 2015 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +import "cached_network_parameters.proto"; + +package quic; + +// A SourceAddressToken is serialised, encrypted and sent to clients so that +// they can prove ownership of an IP address. +message SourceAddressToken { + // ip contains either 4 (IPv4) or 16 (IPv6) bytes of IP address in network + // byte order. + required bytes ip = 1; + // timestamp contains a UNIX timestamp value of the time when the token was + // created. + required int64 timestamp = 2; + // The server can provide estimated network parameters to be used for + // initial parameter selection in future connections. + optional CachedNetworkParameters cached_network_parameters = 3; +}; + +// SourceAddressTokens are simply lists of SourceAddressToken messages. +message SourceAddressTokens { + // This field has id 4 to avoid ambiguity between the serialized form of + // SourceAddressToken vs SourceAddressTokens. + repeated SourceAddressToken tokens = 4; +};
diff --git a/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc new file mode 100644 index 0000000..cb66b27 --- /dev/null +++ b/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
@@ -0,0 +1,43 @@ +// Copyright 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/qpack/qpack_decoder.h" + +#include <cstddef> +#include <cstdint> +#include <limits> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { +namespace test { + +// This fuzzer exercises QpackDecoder. It should be able to cover all possible +// code paths. There is no point in encoding QpackDecoder's output to turn this +// into a roundtrip test, because the same header list can be encoded in many +// different ways, so the output could not be expected to match the original +// input. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + NoOpHeadersHandler handler; + QuicFuzzedDataProvider provider(data, size); + + // Process up to 64 kB fragments at a time. Too small upper bound might not + // provide enough coverage, too large would make fuzzing less efficient. + auto fragment_size_generator = + std::bind(&QuicFuzzedDataProvider::ConsumeIntegralInRange<uint16_t>, + &provider, 1, std::numeric_limits<uint16_t>::max()); + + NoopEncoderStreamErrorDelegate encoder_stream_error_delegate; + NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate; + QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate, + &handler, fragment_size_generator, + provider.ConsumeRemainingBytesAsString()); + + return 0; +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc new file mode 100644 index 0000000..980889d --- /dev/null +++ b/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc
@@ -0,0 +1,67 @@ +// Copyright 2018 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/qpack/qpack_encoder_stream_receiver.h" + +#include <cstddef> +#include <cstdint> + +#include "base/logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { +namespace test { +namespace { + +// A QpackEncoderStreamReceiver::Delegate implementation that ignores all +// decoded instructions but keeps track of whether an error has been detected. +class NoOpDelegate : public QpackEncoderStreamReceiver::Delegate { + public: + NoOpDelegate() : error_detected_(false) {} + ~NoOpDelegate() override = default; + + void OnInsertWithNameReference(bool is_static, + uint64_t name_index, + QuicStringPiece value) override {} + void OnInsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value) override {} + void OnDuplicate(uint64_t index) override {} + void OnSetDynamicTableCapacity(uint64_t capacity) override {} + void OnErrorDetected(QuicStringPiece error_message) override { + error_detected_ = true; + } + + bool error_detected() const { return error_detected_; } + + private: + bool error_detected_; +}; + +} // namespace + +// This fuzzer exercises QpackEncoderStreamReceiver. +// Note that since string literals may be encoded with or without Huffman +// encoding, one could not expect identical encoded data if the decoded +// instructions were fed into QpackEncoderStreamSender. Therefore there is no +// point in extending this fuzzer into a round-trip test. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + NoOpDelegate delegate; + QpackEncoderStreamReceiver receiver(&delegate); + + QuicFuzzedDataProvider provider(data, size); + + while (!delegate.error_detected() && provider.remaining_bytes() != 0) { + // Process up to 64 kB fragments at a time. Too small upper bound might not + // provide enough coverage, too large might make fuzzing too inefficient. + size_t fragment_size = provider.ConsumeIntegralInRange<uint16_t>( + 1, std::numeric_limits<uint16_t>::max()); + receiver.Decode(provider.ConsumeRandomLengthString(fragment_size)); + } + + return 0; +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc new file mode 100644 index 0000000..501a69f --- /dev/null +++ b/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc
@@ -0,0 +1,70 @@ +// Copyright 2018 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/qpack/qpack_encoder_stream_sender.h" + +#include <cstddef> +#include <cstdint> +#include <limits> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { +namespace test { + +// This fuzzer exercises QpackEncoderStreamSender. +// TODO(bnc): Encoded data could be fed into QpackEncoderStreamReceiver and +// decoded instructions directly compared to input. Figure out how to get gMock +// enabled for cc_fuzz_target target types. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + NoopEncoderStreamSenderDelegate delegate; + QpackEncoderStreamSender sender(&delegate); + + QuicFuzzedDataProvider provider(data, size); + // Limit string literal length to 2 kB for efficiency. + const uint16_t kMaxStringLength = 2048; + + while (provider.remaining_bytes() != 0) { + switch (provider.ConsumeIntegral<uint8_t>() % 4) { + case 0: { + bool is_static = provider.ConsumeBool(); + uint64_t name_index = provider.ConsumeIntegral<uint64_t>(); + uint16_t value_length = + provider.ConsumeIntegralInRange<uint16_t>(0, kMaxStringLength); + QuicString value = provider.ConsumeRandomLengthString(value_length); + + sender.SendInsertWithNameReference(is_static, name_index, value); + break; + } + case 1: { + uint16_t name_length = + provider.ConsumeIntegralInRange<uint16_t>(0, kMaxStringLength); + QuicString name = provider.ConsumeRandomLengthString(name_length); + uint16_t value_length = + provider.ConsumeIntegralInRange<uint16_t>(0, kMaxStringLength); + QuicString value = provider.ConsumeRandomLengthString(value_length); + sender.SendInsertWithoutNameReference(name, value); + break; + } + case 2: { + uint64_t index = provider.ConsumeIntegral<uint64_t>(); + sender.SendDuplicate(index); + break; + } + case 3: { + uint64_t capacity = provider.ConsumeIntegral<uint64_t>(); + sender.SendSetDynamicTableCapacity(capacity); + break; + } + } + } + + return 0; +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc new file mode 100644 index 0000000..7f75a0f --- /dev/null +++ b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
@@ -0,0 +1,152 @@ +// Copyright 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 <cstddef> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +namespace quic { +namespace test { + +// This fuzzer exercises QpackEncoder and QpackDecoder. It should be able to +// cover all possible code paths of QpackEncoder. However, since the resulting +// header block is always valid and is encoded in a particular way, this fuzzer +// is not expected to cover all code paths of QpackDecoder. On the other hand, +// encoding then decoding is expected to result in the original header list, and +// this fuzzer checks for that. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + QuicFuzzedDataProvider provider(data, size); + + // Build test header list. + spdy::SpdyHeaderBlock header_list; + uint8_t header_count = provider.ConsumeIntegral<uint8_t>(); + for (uint8_t header_index = 0; header_index < header_count; ++header_index) { + if (provider.remaining_bytes() == 0) { + // Do not add more headers if there is no more fuzzer data. + break; + } + + QuicString name; + QuicString value; + switch (provider.ConsumeIntegral<uint8_t>()) { + case 0: + // Static table entry with no header value. + name = ":authority"; + break; + case 1: + // Static table entry with no header value, using non-empty header + // value. + name = ":authority"; + value = "www.example.org"; + break; + case 2: + // Static table entry with header value, using that header value. + name = ":accept-encoding"; + value = "gzip, deflate"; + break; + case 3: + // Static table entry with header value, using empty header value. + name = ":accept-encoding"; + break; + case 4: + // Static table entry with header value, using different, non-empty + // header value. + name = ":accept-encoding"; + value = "brotli"; + break; + case 5: + // Header name that has multiple entries in the static table, + // using header value from one of them. + name = ":method"; + value = "GET"; + break; + case 6: + // Header name that has multiple entries in the static table, + // using empty header value. + name = ":method"; + break; + case 7: + // Header name that has multiple entries in the static table, + // using different, non-empty header value. + name = ":method"; + value = "CONNECT"; + break; + case 8: + // Header name not in the static table, empty header value. + name = "foo"; + value = ""; + break; + case 9: + // Header name not in the static table, non-empty fixed header value. + name = "foo"; + value = "bar"; + break; + case 10: + // Header name not in the static table, fuzzed header value. + name = "foo"; + value = provider.ConsumeRandomLengthString(128); + break; + case 11: + // Another header name not in the static table, empty header value. + name = "bar"; + value = ""; + break; + case 12: + // Another header name not in the static table, non-empty fixed header + // value. + name = "bar"; + value = "baz"; + break; + case 13: + // Another header name not in the static table, fuzzed header value. + name = "bar"; + value = provider.ConsumeRandomLengthString(128); + break; + default: + // Fuzzed header name and header value. + name = provider.ConsumeRandomLengthString(128); + value = provider.ConsumeRandomLengthString(128); + } + + header_list.AppendValueOrAddHeader(name, value); + } + + // Process up to 64 kB fragments at a time. Too small upper bound might not + // provide enough coverage, too large would make fuzzing less efficient. + auto fragment_size_generator = + std::bind(&QuicFuzzedDataProvider::ConsumeIntegralInRange<uint16_t>, + &provider, 1, std::numeric_limits<uint16_t>::max()); + + // Encode header list. + NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; + NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate; + QuicString encoded_header_block = QpackEncode( + &decoder_stream_error_delegate, &encoder_stream_sender_delegate, + fragment_size_generator, &header_list); + + // Decode header block. + TestHeadersHandler handler; + NoopEncoderStreamErrorDelegate encoder_stream_error_delegate; + NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate; + QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate, + &handler, fragment_size_generator, encoded_header_block); + + // Since header block has been produced by encoding a header list, it must be + // valid. + CHECK(handler.decoding_completed()); + CHECK(!handler.decoding_error_detected()); + + // Compare resulting header list to original. + CHECK(header_list == handler.ReleaseHeaderList()); + + return 0; +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/offline/qpack_offline_decoder.cc b/quic/core/qpack/offline/qpack_offline_decoder.cc new file mode 100644 index 0000000..2b02edd --- /dev/null +++ b/quic/core/qpack/offline/qpack_offline_decoder.cc
@@ -0,0 +1,273 @@ +// Copyright (c) 2018 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/qpack/offline/qpack_offline_decoder.h" + +#include <cstdint> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +QpackOfflineDecoder::QpackOfflineDecoder() + : encoder_stream_error_detected_(false), + decoder_(this, &decoder_stream_sender_delegate_) {} + +bool QpackOfflineDecoder::DecodeAndVerifyOfflineData( + QuicStringPiece input_filename, + QuicStringPiece expected_headers_filename) { + if (!ParseInputFilename(input_filename)) { + QUIC_LOG(ERROR) << "Error parsing input filename " << input_filename; + return false; + } + + if (!DecodeHeaderBlocksFromFile(input_filename)) { + QUIC_LOG(ERROR) << "Error decoding header blocks in " << input_filename; + return false; + } + + if (!VerifyDecodedHeaderLists(expected_headers_filename)) { + QUIC_LOG(ERROR) << "Header lists decoded from " << input_filename + << " to not match expected headers parsed from " + << expected_headers_filename; + return false; + } + + return true; +} + +void QpackOfflineDecoder::OnEncoderStreamError(QuicStringPiece error_message) { + QUIC_LOG(ERROR) << "Encoder stream error: " << error_message; + encoder_stream_error_detected_ = true; +} + +bool QpackOfflineDecoder::ParseInputFilename(QuicStringPiece input_filename) { + auto pieces = QuicTextUtils::Split(input_filename, '.'); + + if (pieces.size() < 3) { + QUIC_LOG(ERROR) << "Not enough fields in input filename " << input_filename; + return false; + } + + auto piece_it = pieces.rbegin(); + + // Acknowledgement mode: 1 for immediate, 0 for none. + bool immediate_acknowledgement = false; + if (*piece_it == "0") { + immediate_acknowledgement = false; + } else if (*piece_it == "1") { + immediate_acknowledgement = true; + } else { + QUIC_LOG(ERROR) + << "Header acknowledgement field must be 0 or 1 in input filename " + << input_filename; + return false; + } + + ++piece_it; + + // Maximum allowed number of blocked streams. + uint64_t max_blocked_streams = 0; + if (!QuicTextUtils::StringToUint64(*piece_it, &max_blocked_streams)) { + QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it + << "\" as an integer."; + return false; + } + + if (max_blocked_streams > 0) { + // TODO(bnc): Implement blocked streams. + QUIC_LOG(ERROR) << "Blocked streams not implemented."; + return false; + } + + ++piece_it; + + // Dynamic Table Size in bytes + uint64_t dynamic_table_size = 0; + if (!QuicTextUtils::StringToUint64(*piece_it, &dynamic_table_size)) { + QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it + << "\" as an integer."; + return false; + } + + decoder_.SetMaximumDynamicTableCapacity(dynamic_table_size); + + return true; +} + +bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile( + QuicStringPiece input_filename) { + // Store data in |input_data_storage|; use a QuicStringPiece to efficiently + // keep track of remaining portion yet to be decoded. + QuicString input_data_storage; + ReadFileContents(input_filename, &input_data_storage); + QuicStringPiece input_data(input_data_storage); + + while (!input_data.empty()) { + if (input_data.size() < sizeof(uint64_t) + sizeof(uint32_t)) { + QUIC_LOG(ERROR) << "Unexpected end of input file."; + return false; + } + + uint64_t stream_id = QuicEndian::NetToHost64( + *reinterpret_cast<const uint64_t*>(input_data.data())); + input_data = input_data.substr(sizeof(uint64_t)); + + uint32_t length = QuicEndian::NetToHost32( + *reinterpret_cast<const uint32_t*>(input_data.data())); + input_data = input_data.substr(sizeof(uint32_t)); + + if (input_data.size() < length) { + QUIC_LOG(ERROR) << "Unexpected end of input file."; + return false; + } + + QuicStringPiece data = input_data.substr(0, length); + input_data = input_data.substr(length); + + if (stream_id == 0) { + decoder_.DecodeEncoderStreamData(data); + + if (encoder_stream_error_detected_) { + QUIC_LOG(ERROR) << "Error detected on encoder stream."; + return false; + } + + continue; + } + + test::TestHeadersHandler headers_handler; + + auto progressive_decoder = + decoder_.DecodeHeaderBlock(stream_id, &headers_handler); + progressive_decoder->Decode(data); + progressive_decoder->EndHeaderBlock(); + + if (headers_handler.decoding_error_detected()) { + QUIC_LOG(ERROR) << "Decoding error on stream " << stream_id; + return false; + } + + decoded_header_lists_.push_back(headers_handler.ReleaseHeaderList()); + } + + return true; +} + +bool QpackOfflineDecoder::VerifyDecodedHeaderLists( + QuicStringPiece expected_headers_filename) { + // Store data in |expected_headers_data_storage|; use a QuicStringPiece to + // efficiently keep track of remaining portion yet to be decoded. + QuicString expected_headers_data_storage; + ReadFileContents(expected_headers_filename, &expected_headers_data_storage); + QuicStringPiece expected_headers_data(expected_headers_data_storage); + + while (!decoded_header_lists_.empty()) { + spdy::SpdyHeaderBlock decoded_header_list = + std::move(decoded_header_lists_.front()); + decoded_header_lists_.pop_front(); + + spdy::SpdyHeaderBlock expected_header_list; + if (!ReadNextExpectedHeaderList(&expected_headers_data, + &expected_header_list)) { + QUIC_LOG(ERROR) + << "Error parsing expected header list to match next decoded " + "header list."; + return false; + } + + if (!CompareHeaderBlocks(std::move(decoded_header_list), + std::move(expected_header_list))) { + QUIC_LOG(ERROR) << "Decoded header does not match expected header."; + return false; + } + } + + if (!expected_headers_data.empty()) { + QUIC_LOG(ERROR) + << "Not enough encoded header lists to match expected ones."; + return false; + } + + return true; +} + +bool QpackOfflineDecoder::ReadNextExpectedHeaderList( + QuicStringPiece* expected_headers_data, + spdy::SpdyHeaderBlock* expected_header_list) { + while (true) { + QuicStringPiece::size_type endline = expected_headers_data->find('\n'); + + // Even last header list must be followed by an empty line. + if (endline == QuicStringPiece::npos) { + QUIC_LOG(ERROR) << "Unexpected end of expected header list file."; + return false; + } + + if (endline == 0) { + // Empty line indicates end of header list. + *expected_headers_data = expected_headers_data->substr(1); + return true; + } + + QuicStringPiece header_field = expected_headers_data->substr(0, endline); + auto pieces = QuicTextUtils::Split(header_field, '\t'); + + if (pieces.size() != 2) { + QUIC_LOG(ERROR) << "Header key and value must be separated by TAB."; + return false; + } + + expected_header_list->AppendValueOrAddHeader(pieces[0], pieces[1]); + + *expected_headers_data = expected_headers_data->substr(endline + 1); + } +} + +bool QpackOfflineDecoder::CompareHeaderBlocks( + spdy::SpdyHeaderBlock decoded_header_list, + spdy::SpdyHeaderBlock expected_header_list) { + if (decoded_header_list == expected_header_list) { + return true; + } + + // The h2o decoder reshuffles the "content-length" header and pseudo-headers, + // see + // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md. + // Remove such headers one by one if they match. + const char* kContentLength = "content-length"; + const char* kPseudoHeaderPrefix = ":"; + for (spdy::SpdyHeaderBlock::iterator decoded_it = decoded_header_list.begin(); + decoded_it != decoded_header_list.end();) { + const QuicStringPiece key = decoded_it->first; + if (key != kContentLength && + !QuicTextUtils::StartsWith(key, kPseudoHeaderPrefix)) { + ++decoded_it; + continue; + } + spdy::SpdyHeaderBlock::iterator expected_it = + expected_header_list.find(key); + if (expected_it == expected_header_list.end() || + decoded_it->second != expected_it->second) { + ++decoded_it; + continue; + } + // SpdyHeaderBlock does not support erasing by iterator, only by key. + ++decoded_it; + expected_header_list.erase(key); + // This will invalidate |key|. + decoded_header_list.erase(key); + } + + return decoded_header_list == expected_header_list; +} + +} // namespace quic
diff --git a/quic/core/qpack/offline/qpack_offline_decoder.h b/quic/core/qpack/offline/qpack_offline_decoder.h new file mode 100644 index 0000000..922fd64 --- /dev/null +++ b/quic/core/qpack/offline/qpack_offline_decoder.h
@@ -0,0 +1,71 @@ +// Copyright (c) 2018 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_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_ +#define QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_ + +#include <list> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +namespace quic { + +// A decoder to read encoded data from a file, decode it, and compare to +// a list of expected header lists read from another file. File format is +// described at +// https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop. +class QpackOfflineDecoder : public QpackDecoder::EncoderStreamErrorDelegate { + public: + QpackOfflineDecoder(); + ~QpackOfflineDecoder() override = default; + + // Read encoded header blocks and encoder stream data from |input_filename| + // and decode them, read expected header lists from + // |expected_headers_filename|, and compare decoded header lists to expected + // ones. Returns true if there is an equal number of them and the + // corresponding ones match, false otherwise. + bool DecodeAndVerifyOfflineData(QuicStringPiece input_filename, + QuicStringPiece expected_headers_filename); + + // QpackDecoder::EncoderStreamErrorDelegate implementation: + void OnEncoderStreamError(QuicStringPiece error_message) override; + + private: + // Parse decoder parameters from |input_filename| and set up |decoder_| + // accordingly. + bool ParseInputFilename(QuicStringPiece input_filename); + + // Read encoded header blocks and encoder stream data from |input_filename|, + // pass them to |decoder_| for decoding, and add decoded header lists to + // |decoded_header_lists_|. + bool DecodeHeaderBlocksFromFile(QuicStringPiece input_filename); + + // Read expected header lists from |expected_headers_filename| and verify + // decoded header lists in |decoded_header_lists_| against them. + bool VerifyDecodedHeaderLists(QuicStringPiece expected_headers_filename); + + // Parse next header list from |*expected_headers_data| into + // |*expected_header_list|, removing consumed data from the beginning of + // |*expected_headers_data|. Returns true on success, false if parsing fails. + bool ReadNextExpectedHeaderList(QuicStringPiece* expected_headers_data, + spdy::SpdyHeaderBlock* expected_header_list); + + // Compare two header lists. Allow for different orders of certain headers as + // described at + // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md. + bool CompareHeaderBlocks(spdy::SpdyHeaderBlock decoded_header_list, + spdy::SpdyHeaderBlock expected_header_list); + + bool encoder_stream_error_detected_; + test::NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate_; + QpackDecoder decoder_; + std::list<spdy::SpdyHeaderBlock> decoded_header_lists_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
diff --git a/quic/core/qpack/offline/qpack_offline_decoder_bin.cc b/quic/core/qpack/offline/qpack_offline_decoder_bin.cc new file mode 100644 index 0000000..a0319e6 --- /dev/null +++ b/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
@@ -0,0 +1,44 @@ +// Copyright (c) 2018 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/qpack/offline/qpack_offline_decoder.h" + +#include <cstddef> +#include <iostream> + +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +int main(int argc, char* argv[]) { + const char* usage = + "Usage: qpack_offline_decoder input_filename expected_headers_filename " + "...."; + std::vector<quic::QuicString> args = + quic::QuicParseCommandLineFlags(usage, argc, argv); + + if (args.size() < 2 || args.size() % 2 != 0) { + quic::QuicPrintCommandLineFlagHelp(usage); + return 1; + } + + size_t i; + for (i = 0; 2 * i < args.size(); ++i) { + const quic::QuicStringPiece input_filename(args[2 * i]); + const quic::QuicStringPiece expected_headers_filename(args[2 * i + 1]); + + // Every file represents a different connection, + // therefore every file needs a fresh decoding context. + quic::QpackOfflineDecoder decoder; + if (!decoder.DecodeAndVerifyOfflineData(input_filename, + expected_headers_filename)) { + return 1; + } + } + + std::cout << "Successfully verified " << i << " pairs of input files." + << std::endl; + + return 0; +}
diff --git a/quic/core/qpack/qpack_constants.cc b/quic/core/qpack/qpack_constants.cc new file mode 100644 index 0000000..b9144e0 --- /dev/null +++ b/quic/core/qpack/qpack_constants.cc
@@ -0,0 +1,200 @@ +// Copyright 2018 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/qpack/qpack_constants.h" + +#include <limits> + +#include "base/logging.h" + +namespace quic { + +namespace { + +// Validate that +// * in each instruction, the bits of |value| that are zero in |mask| are zero; +// * every byte matches exactly one opcode. +void ValidateLangague(const QpackLanguage* language) { +#ifndef NDEBUG + for (const auto* instruction : *language) { + DCHECK_EQ(0, instruction->opcode.value & ~instruction->opcode.mask); + } + + for (uint8_t byte = 0; byte < std::numeric_limits<uint8_t>::max(); ++byte) { + size_t match_count = 0; + for (const auto* instruction : *language) { + if ((byte & instruction->opcode.mask) == instruction->opcode.value) { + ++match_count; + } + } + DCHECK_EQ(1u, match_count) << static_cast<int>(byte); + } +#endif +} + +} // namespace + +bool operator==(const QpackInstructionOpcode& a, + const QpackInstructionOpcode& b) { + return std::tie(a.value, a.mask) == std::tie(b.value, b.mask); +} + +const QpackInstruction* InsertWithNameReferenceInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b10000000, 0b10000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, + {{QpackInstructionFieldType::kSbit, 0b01000000}, + {QpackInstructionFieldType::kVarint, 6}, + {QpackInstructionFieldType::kValue, 7}}}; + return instruction; +} + +const QpackInstruction* InsertWithoutNameReferenceInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b01000000, 0b11000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, + {{QpackInstructionFieldType::kName, 5}, + {QpackInstructionFieldType::kValue, 7}}}; + return instruction; +} + +const QpackInstruction* DuplicateInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b00000000, 0b11100000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}}; + return instruction; +} + +const QpackInstruction* SetDynamicTableCapacityInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b00100000, 0b11100000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}}; + return instruction; +} + +const QpackLanguage* QpackEncoderStreamLanguage() { + static const QpackLanguage* const language = new QpackLanguage{ + InsertWithNameReferenceInstruction(), + InsertWithoutNameReferenceInstruction(), DuplicateInstruction(), + SetDynamicTableCapacityInstruction()}; + ValidateLangague(language); + return language; +} + +const QpackInstruction* InsertCountIncrementInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b00000000, 0b11000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}}; + return instruction; +} + +const QpackInstruction* HeaderAcknowledgementInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b10000000, 0b10000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 7}}}; + return instruction; +} + +const QpackInstruction* StreamCancellationInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b01000000, 0b11000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}}; + return instruction; +} + +const QpackLanguage* QpackDecoderStreamLanguage() { + static const QpackLanguage* const language = new QpackLanguage{ + InsertCountIncrementInstruction(), HeaderAcknowledgementInstruction(), + StreamCancellationInstruction()}; + ValidateLangague(language); + return language; +} + +const QpackInstruction* QpackPrefixInstruction() { + // This opcode matches every input. + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b00000000, 0b00000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, + {{QpackInstructionFieldType::kVarint, 8}, + {QpackInstructionFieldType::kSbit, 0b10000000}, + {QpackInstructionFieldType::kVarint2, 7}}}; + return instruction; +} + +const QpackLanguage* QpackPrefixLanguage() { + static const QpackLanguage* const language = + new QpackLanguage{QpackPrefixInstruction()}; + ValidateLangague(language); + return language; +} + +const QpackInstruction* QpackIndexedHeaderFieldInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b10000000, 0b10000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, + {{QpackInstructionFieldType::kSbit, 0b01000000}, + {QpackInstructionFieldType::kVarint, 6}}}; + return instruction; +} + +const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b00010000, 0b11110000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 4}}}; + return instruction; +} + +const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b01000000, 0b11000000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, + {{QpackInstructionFieldType::kSbit, 0b00010000}, + {QpackInstructionFieldType::kVarint, 4}, + {QpackInstructionFieldType::kValue, 7}}}; + return instruction; +} + +const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b00000000, 0b11110000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, + {{QpackInstructionFieldType::kVarint, 3}, + {QpackInstructionFieldType::kValue, 7}}}; + return instruction; +} + +const QpackInstruction* QpackLiteralHeaderFieldInstruction() { + static const QpackInstructionOpcode* const opcode = + new QpackInstructionOpcode{0b00100000, 0b11100000}; + static const QpackInstruction* const instruction = + new QpackInstruction{*opcode, + {{QpackInstructionFieldType::kName, 3}, + {QpackInstructionFieldType::kValue, 7}}}; + return instruction; +} + +const QpackLanguage* QpackRequestStreamLanguage() { + static const QpackLanguage* const language = + new QpackLanguage{QpackIndexedHeaderFieldInstruction(), + QpackIndexedHeaderFieldPostBaseInstruction(), + QpackLiteralHeaderFieldNameReferenceInstruction(), + QpackLiteralHeaderFieldPostBaseInstruction(), + QpackLiteralHeaderFieldInstruction()}; + ValidateLangague(language); + return language; +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_constants.h b/quic/core/qpack/qpack_constants.h new file mode 100644 index 0000000..7b7eb47 --- /dev/null +++ b/quic/core/qpack/qpack_constants.h
@@ -0,0 +1,147 @@ +// Copyright (c) 2018 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_QPACK_QPACK_CONSTANTS_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_ + +#include <cstdint> +#include <tuple> +#include <vector> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// Each instruction is identified with an opcode in the first byte. +// |mask| determines which bits are part of the opcode. +// |value| is the value of these bits. (Other bits in value must be zero.) +struct QUIC_EXPORT_PRIVATE QpackInstructionOpcode { + uint8_t value; + uint8_t mask; +}; + +bool operator==(const QpackInstructionOpcode& a, + const QpackInstructionOpcode& b); + +// Possible types of an instruction field. Decoding a static bit does not +// consume the current byte. Decoding an integer or a length-prefixed string +// literal consumes all bytes containing the field value. +enum class QpackInstructionFieldType { + // A single bit indicating whether the index refers to the static table, or + // indicating the sign of Delta Base. Called "S" bit because both "static" + // and "sign" start with the letter "S". + kSbit, + // An integer encoded with variable length encoding. This could be an index, + // stream ID, maximum size, or Encoded Required Insert Count. + kVarint, + // A second integer encoded with variable length encoding. This could be + // Delta Base. + kVarint2, + // A header name or header value encoded as: + // a bit indicating whether it is Huffman encoded; + // the encoded length of the string; + // the header name or value optionally Huffman encoded. + kName, + kValue +}; + +// Each instruction field has a type and a parameter. +// The meaning of the parameter depends on the field type. +struct QUIC_EXPORT_PRIVATE QpackInstructionField { + QpackInstructionFieldType type; + // For a kSbit field, |param| is a mask with exactly one bit set. + // For kVarint fields, |param| is the prefix length of the integer encoding. + // For kName and kValue fields, |param| is the prefix length of the length of + // the string, and the bit immediately preceding the prefix is interpreted as + // the Huffman bit. + uint8_t param; +}; + +using QpackInstructionFields = std::vector<QpackInstructionField>; + +// A QPACK instruction consists of an opcode identifying the instruction, +// followed by a non-empty list of fields. The last field must be integer or +// string literal type to guarantee that all bytes of the instruction are +// consumed. +struct QUIC_EXPORT_PRIVATE QpackInstruction { + QpackInstruction(const QpackInstruction&) = delete; + const QpackInstruction& operator=(const QpackInstruction&) = delete; + + QpackInstructionOpcode opcode; + QpackInstructionFields fields; +}; + +// A language is a collection of instructions. The order does not matter. +// Every possible input must match exactly one instruction. +using QpackLanguage = std::vector<const QpackInstruction*>; + +// TODO(bnc): Move this into HpackVarintEncoder. +// The integer encoder can encode up to 2^64-1, which can take up to 10 bytes +// (each carrying 7 bits) after the prefix. +const uint8_t kMaxExtensionBytesForVarintEncoding = 10; + +// Wire format defined in +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5 + +// 5.2 Encoder stream instructions + +// 5.2.1 Insert With Name Reference +const QpackInstruction* InsertWithNameReferenceInstruction(); + +// 5.2.2 Insert Without Name Reference +const QpackInstruction* InsertWithoutNameReferenceInstruction(); + +// 5.2.3 Duplicate +const QpackInstruction* DuplicateInstruction(); + +// 5.2.4 Dynamic Table Size Update +const QpackInstruction* SetDynamicTableCapacityInstruction(); + +// Encoder stream language. +const QpackLanguage* QpackEncoderStreamLanguage(); + +// 5.3 Decoder stream instructions + +// 5.3.1 Insert Count Increment +const QpackInstruction* InsertCountIncrementInstruction(); + +// 5.3.2 Header Acknowledgement +const QpackInstruction* HeaderAcknowledgementInstruction(); + +// 5.3.3 Stream Cancellation +const QpackInstruction* StreamCancellationInstruction(); + +// Decoder stream language. +const QpackLanguage* QpackDecoderStreamLanguage(); + +// 5.4.1. Header data prefix instructions + +const QpackInstruction* QpackPrefixInstruction(); + +const QpackLanguage* QpackPrefixLanguage(); + +// 5.4.2. Request and push stream instructions + +// 5.4.2.1. Indexed Header Field +const QpackInstruction* QpackIndexedHeaderFieldInstruction(); + +// 5.4.2.2. Indexed Header Field With Post-Base Index +const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction(); + +// 5.4.2.3. Literal Header Field With Name Reference +const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction(); + +// 5.4.2.4. Literal Header Field With Post-Base Name Reference +const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction(); + +// 5.4.2.5. Literal Header Field Without Name Reference +const QpackInstruction* QpackLiteralHeaderFieldInstruction(); + +// Request and push stream language. +const QpackLanguage* QpackRequestStreamLanguage(); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/quic/core/qpack/qpack_decoded_headers_accumulator.cc new file mode 100644 index 0000000..ac60fce --- /dev/null +++ b/quic/core/qpack/qpack_decoded_headers_accumulator.cc
@@ -0,0 +1,70 @@ +// Copyright (c) 2019 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/qpack/qpack_decoded_headers_accumulator.h" + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" + +namespace quic { + +QpackDecodedHeadersAccumulator::QpackDecodedHeadersAccumulator( + QuicStreamId id, + QpackDecoder* qpack_decoder) + : decoder_(qpack_decoder->DecodeHeaderBlock(id, this)), + uncompressed_header_bytes_(0), + compressed_header_bytes_(0), + error_detected_(false) { + quic_header_list_.OnHeaderBlockStart(); +} + +void QpackDecodedHeadersAccumulator::OnHeaderDecoded(QuicStringPiece name, + QuicStringPiece value) { + DCHECK(!error_detected_); + + uncompressed_header_bytes_ += name.size() + value.size(); + quic_header_list_.OnHeader(name, value); +} + +void QpackDecodedHeadersAccumulator::OnDecodingCompleted() {} + +void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected( + QuicStringPiece error_message) { + DCHECK(!error_detected_); + + error_detected_ = true; + // Copy error message to ensure it remains valid for the lifetime of |this|. + error_message_.assign(error_message.data(), error_message.size()); +} + +bool QpackDecodedHeadersAccumulator::Decode(QuicStringPiece data) { + DCHECK(!error_detected_); + + compressed_header_bytes_ += data.size(); + decoder_->Decode(data); + + return !error_detected_; +} + +bool QpackDecodedHeadersAccumulator::EndHeaderBlock() { + DCHECK(!error_detected_); + + decoder_->EndHeaderBlock(); + + quic_header_list_.OnHeaderBlockEnd(uncompressed_header_bytes_, + compressed_header_bytes_); + + return !error_detected_; +} + +const QuicHeaderList& QpackDecodedHeadersAccumulator::quic_header_list() const { + DCHECK(!error_detected_); + return quic_header_list_; +} + +QuicStringPiece QpackDecodedHeadersAccumulator::error_message() const { + DCHECK(error_detected_); + return error_message_; +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator.h b/quic/core/qpack/qpack_decoded_headers_accumulator.h new file mode 100644 index 0000000..47d4f63 --- /dev/null +++ b/quic/core/qpack/qpack_decoded_headers_accumulator.h
@@ -0,0 +1,64 @@ +// Copyright (c) 2019 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_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_ + +#include <cstddef> + +#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QpackDecoder; + +// A class that creates and owns a QpackProgressiveDecoder instance, accumulates +// decoded headers in a QuicHeaderList, and keeps track of uncompressed and +// compressed size so that it can be passed to QuicHeaderList::EndHeaderBlock(). +class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator + : public QpackProgressiveDecoder::HeadersHandlerInterface { + public: + QpackDecodedHeadersAccumulator(QuicStreamId id, QpackDecoder* qpack_decoder); + virtual ~QpackDecodedHeadersAccumulator() = default; + + // QpackProgressiveDecoder::HeadersHandlerInterface implementation. + // These methods should only be called by |decoder_|. + void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override; + void OnDecodingCompleted() override; + void OnDecodingErrorDetected(QuicStringPiece error_message) override; + + // Decode payload data. Returns true on success, false on error. + // Must not be called if an error has been detected. + // Must not be called after EndHeaderBlock(). + bool Decode(QuicStringPiece data); + + // Signal end of HEADERS frame. Returns true on success, false on error. + // Must not be called if an error has been detected. + // Must not be called more that once. + bool EndHeaderBlock(); + + // Returns accumulated header list. + const QuicHeaderList& quic_header_list() const; + + // Returns error message. + // Must not be called unless an error has been detected. + QuicStringPiece error_message() const; + + private: + std::unique_ptr<QpackProgressiveDecoder> decoder_; + QuicHeaderList quic_header_list_; + size_t uncompressed_header_bytes_; + size_t compressed_header_bytes_; + bool error_detected_; + QuicString error_message_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc new file mode 100644 index 0000000..0c71b19 --- /dev/null +++ b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
@@ -0,0 +1,100 @@ +// Copyright (c) 2019 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/qpack/qpack_decoded_headers_accumulator.h" + +#include <cstring> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using ::testing::Eq; +using ::testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +QuicStreamId kTestStreamId = 1; + +// Header Acknowledgement decoder stream instruction with stream_id = 1. +const char* const kHeaderAcknowledgement = "\x81"; + +} // anonymous namespace + +class QpackDecodedHeadersAccumulatorTest : public QuicTest { + protected: + QpackDecodedHeadersAccumulatorTest() + : qpack_decoder_(&encoder_stream_error_delegate_, + &decoder_stream_sender_delegate_), + accumulator_(kTestStreamId, &qpack_decoder_) {} + + NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_; + StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_; + QpackDecoder qpack_decoder_; + QpackDecodedHeadersAccumulator accumulator_; +}; + +// HEADERS frame payload must have a complete Header Block Prefix. +TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyPayload) { + EXPECT_FALSE(accumulator_.EndHeaderBlock()); + EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message()); +} + +// HEADERS frame payload must have a complete Header Block Prefix. +TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) { + EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00"))); + EXPECT_FALSE(accumulator_.EndHeaderBlock()); + EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message()); +} + +TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) { + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("0000"))); + EXPECT_TRUE(accumulator_.EndHeaderBlock()); + + EXPECT_TRUE(accumulator_.quic_header_list().empty()); +} + +// This payload is the prefix of a valid payload, but EndHeaderBlock() is called +// before it can be completely decoded. +TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedPayload) { + EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00002366"))); + EXPECT_FALSE(accumulator_.EndHeaderBlock()); + EXPECT_EQ("Incomplete header block.", accumulator_.error_message()); +} + +// This payload is invalid because it refers to a non-existing static entry. +TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) { + EXPECT_FALSE(accumulator_.Decode(QuicTextUtils::HexDecode("0000ff23ff24"))); + EXPECT_EQ("Static table entry not found.", accumulator_.error_message()); +} + +TEST_F(QpackDecodedHeadersAccumulatorTest, Success) { + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + QuicString encoded_data(QuicTextUtils::HexDecode("000023666f6f03626172")); + EXPECT_TRUE(accumulator_.Decode(encoded_data)); + EXPECT_TRUE(accumulator_.EndHeaderBlock()); + + auto header_list = accumulator_.quic_header_list(); + auto it = header_list.begin(); + EXPECT_TRUE(it != header_list.end()); + EXPECT_EQ("foo", it->first); + EXPECT_EQ("bar", it->second); + ++it; + EXPECT_TRUE(it == header_list.end()); + + EXPECT_EQ(strlen("foo") + strlen("bar"), + header_list.uncompressed_header_bytes()); + EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder.cc b/quic/core/qpack/qpack_decoder.cc new file mode 100644 index 0000000..fd0867a --- /dev/null +++ b/quic/core/qpack/qpack_decoder.cc
@@ -0,0 +1,141 @@ +// Copyright (c) 2018 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/qpack/qpack_decoder.h" + +#include <limits> + +#include "base/logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +QpackDecoder::QpackDecoder( + EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate) + : encoder_stream_error_delegate_(encoder_stream_error_delegate), + encoder_stream_receiver_(this), + decoder_stream_sender_(decoder_stream_sender_delegate) { + DCHECK(encoder_stream_error_delegate_); + DCHECK(decoder_stream_sender_delegate); +} + +QpackDecoder::~QpackDecoder() {} + +void QpackDecoder::SetMaximumDynamicTableCapacity( + uint64_t maximum_dynamic_table_capacity) { + header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity); +} + +void QpackDecoder::OnStreamReset(QuicStreamId stream_id) { + decoder_stream_sender_.SendStreamCancellation(stream_id); +} + +void QpackDecoder::DecodeEncoderStreamData(QuicStringPiece data) { + encoder_stream_receiver_.Decode(data); +} + +void QpackDecoder::OnInsertWithNameReference(bool is_static, + uint64_t name_index, + QuicStringPiece value) { + if (is_static) { + auto entry = header_table_.LookupEntry(/* is_static = */ true, name_index); + if (!entry) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Invalid static table entry."); + return; + } + + entry = header_table_.InsertEntry(entry->name(), value); + if (!entry) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Error inserting entry with name reference."); + } + return; + } + + uint64_t absolute_index; + if (!EncoderStreamRelativeIndexToAbsoluteIndex(name_index, &absolute_index)) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Invalid relative index."); + return; + } + + const QpackEntry* entry = + header_table_.LookupEntry(/* is_static = */ false, absolute_index); + if (!entry) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Dynamic table entry not found."); + return; + } + entry = header_table_.InsertEntry(entry->name(), value); + if (!entry) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Error inserting entry with name reference."); + } +} + +void QpackDecoder::OnInsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value) { + const QpackEntry* entry = header_table_.InsertEntry(name, value); + if (!entry) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Error inserting literal entry."); + } +} + +void QpackDecoder::OnDuplicate(uint64_t index) { + uint64_t absolute_index; + if (!EncoderStreamRelativeIndexToAbsoluteIndex(index, &absolute_index)) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Invalid relative index."); + return; + } + + const QpackEntry* entry = + header_table_.LookupEntry(/* is_static = */ false, absolute_index); + if (!entry) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Dynamic table entry not found."); + return; + } + entry = header_table_.InsertEntry(entry->name(), entry->value()); + if (!entry) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Error inserting duplicate entry."); + } +} + +void QpackDecoder::OnSetDynamicTableCapacity(uint64_t capacity) { + if (!header_table_.SetDynamicTableCapacity(capacity)) { + encoder_stream_error_delegate_->OnEncoderStreamError( + "Error updating dynamic table capacity."); + } +} + +void QpackDecoder::OnErrorDetected(QuicStringPiece error_message) { + encoder_stream_error_delegate_->OnEncoderStreamError(error_message); +} + +bool QpackDecoder::EncoderStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t* absolute_index) const { + if (relative_index == std::numeric_limits<uint64_t>::max() || + relative_index + 1 > std::numeric_limits<uint64_t>::max() - + header_table_.inserted_entry_count()) { + return false; + } + + *absolute_index = header_table_.inserted_entry_count() - relative_index - 1; + return true; +} + +std::unique_ptr<QpackProgressiveDecoder> QpackDecoder::DecodeHeaderBlock( + QuicStreamId stream_id, + QpackProgressiveDecoder::HeadersHandlerInterface* handler) { + return QuicMakeUnique<QpackProgressiveDecoder>( + stream_id, &header_table_, &decoder_stream_sender_, handler); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder.h b/quic/core/qpack/qpack_decoder.h new file mode 100644 index 0000000..c3b28a3 --- /dev/null +++ b/quic/core/qpack/qpack_decoder.h
@@ -0,0 +1,102 @@ +// Copyright (c) 2018 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_QPACK_QPACK_DECODER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_H_ + +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// QPACK decoder class. Exactly one instance should exist per QUIC connection. +// This class vends a new QpackProgressiveDecoder instance for each new header +// list to be encoded. +class QUIC_EXPORT_PRIVATE QpackDecoder + : public QpackEncoderStreamReceiver::Delegate { + public: + // Interface for receiving notification that an error has occurred on the + // encoder stream. This MUST be treated as a connection error of type + // HTTP_QPACK_ENCODER_STREAM_ERROR. + class QUIC_EXPORT_PRIVATE EncoderStreamErrorDelegate { + public: + virtual ~EncoderStreamErrorDelegate() {} + + virtual void OnEncoderStreamError(QuicStringPiece error_message) = 0; + }; + + QpackDecoder( + EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate); + ~QpackDecoder() override; + + // Set maximum capacity of dynamic table. + // This method must only be called at most once. + void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); + + // Signal to the peer's encoder that a stream is reset. This lets the peer's + // encoder know that no more header blocks will be processed on this stream, + // therefore references to dynamic table entries shall not prevent their + // eviction. + // This method should be called regardless of whether a header block is being + // decoded on that stream, because a header block might be in flight from the + // peer. + // This method should be called every time a request or push stream is reset + // for any reason: for example, client cancels request, or a decoding error + // occurs and HeadersHandlerInterface::OnDecodingErrorDetected() is called. + // This method should also be called if the stream is reset by the peer, + // because the peer's encoder can only evict entries referenced by header + // blocks once it receives acknowledgement from this endpoint that the stream + // is reset. + // However, this method should not be called if the stream is closed normally + // using the FIN bit. + void OnStreamReset(QuicStreamId stream_id); + + // Factory method to create a QpackProgressiveDecoder for decoding a header + // block. |handler| must remain valid until the returned + // QpackProgressiveDecoder instance is destroyed or the decoder calls + // |handler->OnHeaderBlockEnd()|. + std::unique_ptr<QpackProgressiveDecoder> DecodeHeaderBlock( + QuicStreamId stream_id, + QpackProgressiveDecoder::HeadersHandlerInterface* handler); + + // Decode data received on the encoder stream. + void DecodeEncoderStreamData(QuicStringPiece data); + + // QpackEncoderStreamReceiver::Delegate implementation + void OnInsertWithNameReference(bool is_static, + uint64_t name_index, + QuicStringPiece value) override; + void OnInsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value) override; + void OnDuplicate(uint64_t index) override; + void OnSetDynamicTableCapacity(uint64_t capacity) override; + void OnErrorDetected(QuicStringPiece error_message) override; + + private: + // The encoder stream uses relative index (but different from the kind of + // relative index used on a request stream). This method converts relative + // index to absolute index (zero based). It returns true on success, or false + // if conversion fails due to overflow/underflow. + bool EncoderStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t* absolute_index) const; + + EncoderStreamErrorDelegate* const encoder_stream_error_delegate_; + QpackEncoderStreamReceiver encoder_stream_receiver_; + QpackDecoderStreamSender decoder_stream_sender_; + QpackHeaderTable header_table_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_H_
diff --git a/quic/core/qpack/qpack_decoder_stream_receiver.cc b/quic/core/qpack/qpack_decoder_stream_receiver.cc new file mode 100644 index 0000000..559ce43 --- /dev/null +++ b/quic/core/qpack/qpack_decoder_stream_receiver.cc
@@ -0,0 +1,52 @@ +// Copyright (c) 2018 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/qpack/qpack_decoder_stream_receiver.h" + +#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" +#include "net/third_party/quiche/src/http2/decoder/decode_status.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" + +namespace quic { + +QpackDecoderStreamReceiver::QpackDecoderStreamReceiver(Delegate* delegate) + : instruction_decoder_(QpackDecoderStreamLanguage(), this), + delegate_(delegate), + error_detected_(false) { + DCHECK(delegate_); +} + +void QpackDecoderStreamReceiver::Decode(QuicStringPiece data) { + if (data.empty() || error_detected_) { + return; + } + + instruction_decoder_.Decode(data); +} + +bool QpackDecoderStreamReceiver::OnInstructionDecoded( + const QpackInstruction* instruction) { + if (instruction == InsertCountIncrementInstruction()) { + delegate_->OnInsertCountIncrement(instruction_decoder_.varint()); + return true; + } + + if (instruction == HeaderAcknowledgementInstruction()) { + delegate_->OnHeaderAcknowledgement(instruction_decoder_.varint()); + return true; + } + + DCHECK_EQ(instruction, StreamCancellationInstruction()); + delegate_->OnStreamCancellation(instruction_decoder_.varint()); + return true; +} + +void QpackDecoderStreamReceiver::OnError(QuicStringPiece error_message) { + DCHECK(!error_detected_); + + error_detected_ = true; + delegate_->OnErrorDetected(error_message); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_stream_receiver.h b/quic/core/qpack/qpack_decoder_stream_receiver.h new file mode 100644 index 0000000..61c2773 --- /dev/null +++ b/quic/core/qpack/qpack_decoder_stream_receiver.h
@@ -0,0 +1,63 @@ +// Copyright (c) 2018 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_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// This class decodes data received on the decoder stream, +// and passes it along to its Delegate. +class QUIC_EXPORT_PRIVATE QpackDecoderStreamReceiver + : public QpackInstructionDecoder::Delegate { + public: + // An interface for handling instructions decoded from the decoder stream, see + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3 + class Delegate { + public: + virtual ~Delegate() = default; + + // 5.3.1 Insert Count Increment + virtual void OnInsertCountIncrement(uint64_t increment) = 0; + // 5.3.2 Header Acknowledgement + virtual void OnHeaderAcknowledgement(QuicStreamId stream_id) = 0; + // 5.3.3 Stream Cancellation + virtual void OnStreamCancellation(QuicStreamId stream_id) = 0; + // Decoding error + virtual void OnErrorDetected(QuicStringPiece error_message) = 0; + }; + + explicit QpackDecoderStreamReceiver(Delegate* delegate); + QpackDecoderStreamReceiver() = delete; + QpackDecoderStreamReceiver(const QpackDecoderStreamReceiver&) = delete; + QpackDecoderStreamReceiver& operator=(const QpackDecoderStreamReceiver&) = + delete; + + // Decode data and call appropriate Delegate method after each decoded + // instruction. Once an error occurs, Delegate::OnErrorDetected() is called, + // and all further data is ignored. + void Decode(QuicStringPiece data); + + // QpackInstructionDecoder::Delegate implementation. + bool OnInstructionDecoded(const QpackInstruction* instruction) override; + void OnError(QuicStringPiece error_message) override; + + private: + QpackInstructionDecoder instruction_decoder_; + Delegate* const delegate_; + + // True if a decoding error has been detected. + bool error_detected_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_
diff --git a/quic/core/qpack/qpack_decoder_stream_receiver_test.cc b/quic/core/qpack/qpack_decoder_stream_receiver_test.cc new file mode 100644 index 0000000..fc7225f --- /dev/null +++ b/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
@@ -0,0 +1,91 @@ +// Copyright (c) 2018 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/qpack/qpack_decoder_stream_receiver.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using testing::Eq; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class MockDelegate : public QpackDecoderStreamReceiver::Delegate { + public: + ~MockDelegate() override = default; + + MOCK_METHOD1(OnInsertCountIncrement, void(uint64_t increment)); + MOCK_METHOD1(OnHeaderAcknowledgement, void(QuicStreamId stream_id)); + MOCK_METHOD1(OnStreamCancellation, void(QuicStreamId stream_id)); + MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message)); +}; + +class QpackDecoderStreamReceiverTest : public QuicTest { + protected: + QpackDecoderStreamReceiverTest() : stream_(&delegate_) {} + ~QpackDecoderStreamReceiverTest() override = default; + + QpackDecoderStreamReceiver stream_; + StrictMock<MockDelegate> delegate_; +}; + +TEST_F(QpackDecoderStreamReceiverTest, InsertCountIncrement) { + EXPECT_CALL(delegate_, OnInsertCountIncrement(0)); + stream_.Decode(QuicTextUtils::HexDecode("00")); + + EXPECT_CALL(delegate_, OnInsertCountIncrement(10)); + stream_.Decode(QuicTextUtils::HexDecode("0a")); + + EXPECT_CALL(delegate_, OnInsertCountIncrement(63)); + stream_.Decode(QuicTextUtils::HexDecode("3f00")); + + EXPECT_CALL(delegate_, OnInsertCountIncrement(200)); + stream_.Decode(QuicTextUtils::HexDecode("3f8901")); + + EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large."))); + stream_.Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff")); +} + +TEST_F(QpackDecoderStreamReceiverTest, HeaderAcknowledgement) { + EXPECT_CALL(delegate_, OnHeaderAcknowledgement(0)); + stream_.Decode(QuicTextUtils::HexDecode("80")); + + EXPECT_CALL(delegate_, OnHeaderAcknowledgement(37)); + stream_.Decode(QuicTextUtils::HexDecode("a5")); + + EXPECT_CALL(delegate_, OnHeaderAcknowledgement(127)); + stream_.Decode(QuicTextUtils::HexDecode("ff00")); + + EXPECT_CALL(delegate_, OnHeaderAcknowledgement(503)); + stream_.Decode(QuicTextUtils::HexDecode("fff802")); + + EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large."))); + stream_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff")); +} + +TEST_F(QpackDecoderStreamReceiverTest, StreamCancellation) { + EXPECT_CALL(delegate_, OnStreamCancellation(0)); + stream_.Decode(QuicTextUtils::HexDecode("40")); + + EXPECT_CALL(delegate_, OnStreamCancellation(19)); + stream_.Decode(QuicTextUtils::HexDecode("53")); + + EXPECT_CALL(delegate_, OnStreamCancellation(63)); + stream_.Decode(QuicTextUtils::HexDecode("7f00")); + + EXPECT_CALL(delegate_, OnStreamCancellation(110)); + stream_.Decode(QuicTextUtils::HexDecode("7f2f")); + + EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large."))); + stream_.Decode(QuicTextUtils::HexDecode("7fffffffffffffffffffff")); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_stream_sender.cc b/quic/core/qpack/qpack_decoder_stream_sender.cc new file mode 100644 index 0000000..141b64a --- /dev/null +++ b/quic/core/qpack/qpack_decoder_stream_sender.cc
@@ -0,0 +1,61 @@ +// Copyright (c) 2018 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/qpack/qpack_decoder_stream_sender.h" + +#include <cstddef> +#include <limits> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QpackDecoderStreamSender::QpackDecoderStreamSender(Delegate* delegate) + : delegate_(delegate) { + DCHECK(delegate_); +} + +void QpackDecoderStreamSender::SendInsertCountIncrement(uint64_t increment) { + instruction_encoder_.set_varint(increment); + + instruction_encoder_.Encode(InsertCountIncrementInstruction()); + + QuicString output; + + instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output); + DCHECK(!instruction_encoder_.HasNext()); + + delegate_->WriteDecoderStreamData(output); +} + +void QpackDecoderStreamSender::SendHeaderAcknowledgement( + QuicStreamId stream_id) { + instruction_encoder_.set_varint(stream_id); + + instruction_encoder_.Encode(HeaderAcknowledgementInstruction()); + + QuicString output; + + instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output); + DCHECK(!instruction_encoder_.HasNext()); + + delegate_->WriteDecoderStreamData(output); +} + +void QpackDecoderStreamSender::SendStreamCancellation(QuicStreamId stream_id) { + instruction_encoder_.set_varint(stream_id); + + instruction_encoder_.Encode(StreamCancellationInstruction()); + + QuicString output; + + instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output); + DCHECK(!instruction_encoder_.HasNext()); + + delegate_->WriteDecoderStreamData(output); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_stream_sender.h b/quic/core/qpack/qpack_decoder_stream_sender.h new file mode 100644 index 0000000..a791173 --- /dev/null +++ b/quic/core/qpack/qpack_decoder_stream_sender.h
@@ -0,0 +1,55 @@ +// Copyright (c) 2018 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_QPACK_QPACK_DECODER_STREAM_SENDER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// This class serializes (encodes) instructions for transmission on the decoder +// stream. +class QUIC_EXPORT_PRIVATE QpackDecoderStreamSender { + public: + // An interface for handling encoded data. + class Delegate { + public: + virtual ~Delegate() = default; + + // Encoded |data| is ready to be written on the decoder stream. + // WriteDecoderStreamData() is called exactly once for each instruction. + // |data| contains the entire encoded instruction and it is guaranteed to be + // not empty. + virtual void WriteDecoderStreamData(QuicStringPiece data) = 0; + }; + + explicit QpackDecoderStreamSender(Delegate* delegate); + QpackDecoderStreamSender() = delete; + QpackDecoderStreamSender(const QpackDecoderStreamSender&) = delete; + QpackDecoderStreamSender& operator=(const QpackDecoderStreamSender&) = delete; + + // Methods for sending instructions, see + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3 + + // 5.3.1 Insert Count Increment + void SendInsertCountIncrement(uint64_t increment); + // 5.3.2 Header Acknowledgement + void SendHeaderAcknowledgement(QuicStreamId stream_id); + // 5.3.3 Stream Cancellation + void SendStreamCancellation(QuicStreamId stream_id); + + private: + Delegate* const delegate_; + QpackInstructionEncoder instruction_encoder_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_
diff --git a/quic/core/qpack/qpack_decoder_stream_sender_test.cc b/quic/core/qpack/qpack_decoder_stream_sender_test.cc new file mode 100644 index 0000000..cc95759 --- /dev/null +++ b/quic/core/qpack/qpack_decoder_stream_sender_test.cc
@@ -0,0 +1,91 @@ +// Copyright (c) 2018 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/qpack/qpack_decoder_stream_sender.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using ::testing::Eq; +using ::testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class MockSenderDelegate : public QpackDecoderStreamSender::Delegate { + public: + ~MockSenderDelegate() override = default; + + MOCK_METHOD1(WriteDecoderStreamData, void(QuicStringPiece data)); +}; + +class QpackDecoderStreamSenderTest : public QuicTest { + protected: + QpackDecoderStreamSenderTest() : stream_(&delegate_) {} + ~QpackDecoderStreamSenderTest() override = default; + + StrictMock<MockSenderDelegate> delegate_; + QpackDecoderStreamSender stream_; +}; + +TEST_F(QpackDecoderStreamSenderTest, InsertCountIncrement) { + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("00")))); + stream_.SendInsertCountIncrement(0); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("0a")))); + stream_.SendInsertCountIncrement(10); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("3f00")))); + stream_.SendInsertCountIncrement(63); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("3f8901")))); + stream_.SendInsertCountIncrement(200); +} + +TEST_F(QpackDecoderStreamSenderTest, HeaderAcknowledgement) { + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("80")))); + stream_.SendHeaderAcknowledgement(0); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("a5")))); + stream_.SendHeaderAcknowledgement(37); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("ff00")))); + stream_.SendHeaderAcknowledgement(127); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("fff802")))); + stream_.SendHeaderAcknowledgement(503); +} + +TEST_F(QpackDecoderStreamSenderTest, StreamCancellation) { + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("40")))); + stream_.SendStreamCancellation(0); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("53")))); + stream_.SendStreamCancellation(19); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("7f00")))); + stream_.SendStreamCancellation(63); + + EXPECT_CALL(delegate_, + WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("7f2f")))); + stream_.SendStreamCancellation(110); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_test.cc b/quic/core/qpack/qpack_decoder_test.cc new file mode 100644 index 0000000..7292c50 --- /dev/null +++ b/quic/core/qpack/qpack_decoder_test.cc
@@ -0,0 +1,701 @@ +// Copyright (c) 2018 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/qpack/qpack_decoder.h" + +#include <algorithm> + +#include "base/logging.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +using ::testing::Eq; +using ::testing::Sequence; +using ::testing::StrictMock; +using ::testing::Values; + +namespace quic { +namespace test { +namespace { + +// Header Acknowledgement decoder stream instruction with stream_id = 1. +const char* const kHeaderAcknowledgement = "\x81"; + +class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { + protected: + QpackDecoderTest() + : qpack_decoder_(&encoder_stream_error_delegate_, + &decoder_stream_sender_delegate_), + fragment_mode_(GetParam()) { + qpack_decoder_.SetMaximumDynamicTableCapacity(1024); + } + + ~QpackDecoderTest() override = default; + + void DecodeEncoderStreamData(QuicStringPiece data) { + qpack_decoder_.DecodeEncoderStreamData(data); + } + + void DecodeHeaderBlock(QuicStringPiece data) { + auto fragment_size_generator = + FragmentModeToFragmentSizeGenerator(fragment_mode_); + auto progressive_decoder = + qpack_decoder_.DecodeHeaderBlock(/* stream_id = */ 1, &handler_); + while (!data.empty()) { + size_t fragment_size = std::min(fragment_size_generator(), data.size()); + progressive_decoder->Decode(data.substr(0, fragment_size)); + data = data.substr(fragment_size); + } + progressive_decoder->EndHeaderBlock(); + } + + StrictMock<MockEncoderStreamErrorDelegate> encoder_stream_error_delegate_; + StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_; + StrictMock<MockHeadersHandler> handler_; + + private: + QpackDecoder qpack_decoder_; + const FragmentMode fragment_mode_; +}; + +INSTANTIATE_TEST_SUITE_P(, QpackDecoderTest, Values(FragmentMode::kSingleChunk, + FragmentMode::kOctetByOctet)); + +TEST_P(QpackDecoderTest, NoPrefix) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Incomplete header data prefix."))); + + // Header Data Prefix is at least two bytes long. + DecodeHeaderBlock(QuicTextUtils::HexDecode("00")); +} + +TEST_P(QpackDecoderTest, EmptyHeaderBlock) { + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("0000")); +} + +TEST_P(QpackDecoderTest, LiteralEntryEmptyName) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("foo"))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("00002003666f6f")); +} + +TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f00")); +} + +TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq(""))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("00002000")); +} + +TEST_P(QpackDecoderTest, SimpleLiteralEntry) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f03626172")); +} + +TEST_P(QpackDecoderTest, MultipleLiteralEntries) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); + QuicString str(127, 'a'); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foobaar"), QuicStringPiece(str))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0000" // prefix + "23666f6f03626172" // foo: bar + "2700666f6f62616172" // 7 octet long header name, the smallest number + // that does not fit on a 3-bit prefix. + "7f0061616161616161" // 127 octet long header value, the smallest number + "616161616161616161" // that does not fit on a 7-bit prefix. + "6161616161616161616161616161616161616161616161616161616161616161616161" + "6161616161616161616161616161616161616161616161616161616161616161616161" + "6161616161616161616161616161616161616161616161616161616161616161616161" + "616161616161")); +} + +// Name Length value is too large for varint decoder to decode. +TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Encoded integer too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffffffffffffffffffff")); +} + +// Name Length value can be decoded by varint decoder but exceeds 1 MB limit. +TEST_P(QpackDecoderTest, NameLenExceedsLimit) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("String literal too long."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffff7f")); +} + +// Value Length value is too large for varint decoder to decode. +TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Encoded integer too large."))); + + DecodeHeaderBlock( + QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff")); +} + +// Value Length value can be decoded by varint decoder but exceeds 1 MB limit. +TEST_P(QpackDecoderTest, ValueLenExceedsLimit) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("String literal too long."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f7fffff7f")); +} + +TEST_P(QpackDecoderTest, IncompleteHeaderBlock) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Incomplete header block."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("00002366")); +} + +TEST_P(QpackDecoderTest, HuffmanSimple) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value"))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock( + QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); +} + +TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value"))) + .Times(4); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0000" // Prefix. + "2f0125a849e95ba97d7f" // Huffman-encoded name. + "8925a849e95bb8e8b4bf" // Huffman-encoded value. + "2703637573746f6d2d6b6579" // Non-Huffman encoded name. + "0c637573746f6d2d76616c7565" // Non-Huffman encoded value. + "2f0125a849e95ba97d7f" // Huffman-encoded name. + "0c637573746f6d2d76616c7565" // Non-Huffman encoded value. + "2703637573746f6d2d6b6579" // Non-Huffman encoded name. + "8925a849e95bb8e8b4bf")); // Huffman-encoded value. +} + +TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) { + EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( + "Error in Huffman-encoded string."))); + + // 'y' ends in 0b0 on the most significant bit of the last byte. + // The remaining 7 bits must be a prefix of EOS, which is all 1s. + DecodeHeaderBlock( + QuicTextUtils::HexDecode("00002f0125a849e95ba97d7e8925a849e95bb8e8b4bf")); +} + +TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) { + EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( + "Error in Huffman-encoded string."))); + + // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte. + // The remaining 5 bits must be a prefix of EOS, which is all 1s. + DecodeHeaderBlock( + QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4be")); +} + +TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) { + EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( + "Error in Huffman-encoded string."))); + + // The trailing EOS prefix must be at most 7 bits long. Appending one octet + // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a + // prefix of EOS. + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "00002f0225a849e95ba97d7fff8925a849e95bb8e8b4bf")); +} + +TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) { + EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( + "Error in Huffman-encoded string."))); + + // The trailing EOS prefix must be at most 7 bits long. Appending one octet + // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a + // prefix of EOS. + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "00002f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff")); +} + +TEST_P(QpackDecoderTest, StaticTable) { + // A header name that has multiple entries with different values. + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET"))); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("POST"))); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("TRACE"))); + + // A header name that has a single entry with non-empty value. + EXPECT_CALL(handler_, + OnHeaderDecoded(Eq("accept-encoding"), Eq("gzip, deflate, br"))); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("compress"))); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq(""))); + + // A header name that has a single entry with empty value. + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq(""))); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("foo"))); + + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0000d1dfccd45f108621e9aec2a11f5c8294e75f000554524143455f1000")); +} + +TEST_P(QpackDecoderTest, TooHighStaticTableIndex) { + // This is the last entry in the static table with index 98. + EXPECT_CALL(handler_, + OnHeaderDecoded(Eq("x-frame-options"), Eq("sameorigin"))); + + // Addressing entry 99 should trigger an error. + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Static table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode("0000ff23ff24")); +} + +TEST_P(QpackDecoderTest, DynamicTable) { + DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "6294e703626172" // Add literal entry with name "foo" and value "bar". + "80035a5a5a" // Add entry with name of dynamic table entry index 0 + // (relative index) and value "ZZZ". + "cf8294e7" // Add entry with name of static table entry index 15 + // and value "foo". + "01")); // Duplicate entry with relative index 1. + + // Now there are four entries in the dynamic table. + // Entry 0: "foo", "bar" + // Entry 1: "foo", "ZZZ" + // Entry 2: ":method", "foo" + // Entry 3: "foo", "ZZZ" + + // Use a Sequence to test that mock methods are called in order. + Sequence s; + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) + .InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))) + .InSequence(s); + EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Required Insert Count 4 and Delta Base 0. + // Base is 4 + 0 = 4. + "83" // Dynamic table entry with relative index 3, absolute index 0. + "82" // Dynamic table entry with relative index 2, absolute index 1. + "81" // Dynamic table entry with relative index 1, absolute index 2. + "80" // Dynamic table entry with relative index 0, absolute index 3. + "41025a5a")); // Name of entry 1 (relative index) from dynamic table, + // with value "ZZ". + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) + .InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))) + .InSequence(s); + EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0502" // Required Insert Count 4 and Delta Base 2. + // Base is 4 + 2 = 6. + "85" // Dynamic table entry with relative index 5, absolute index 0. + "84" // Dynamic table entry with relative index 4, absolute index 1. + "83" // Dynamic table entry with relative index 3, absolute index 2. + "82" // Dynamic table entry with relative index 2, absolute index 3. + "43025a5a")); // Name of entry 3 (relative index) from dynamic table, + // with value "ZZ". + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) + .InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))) + .InSequence(s); + EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0582" // Required Insert Count 4 and Delta Base 2 with sign bit set. + // Base is 4 - 2 - 1 = 1. + "80" // Dynamic table entry with relative index 0, absolute index 0. + "10" // Dynamic table entry with post-base index 0, absolute index 1. + "11" // Dynamic table entry with post-base index 1, absolute index 2. + "12" // Dynamic table entry with post-base index 2, absolute index 3. + "01025a5a")); // Name of entry 1 (post-base index) from dynamic table, + // with value "ZZ". +} + +TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Required Insert Count 1 and Delta Base 0. + // Base is 1 + 0 = 1. + "80")); // Dynamic table entry with relative index 0, absolute index 0. + + // Change dynamic table capacity to 32 bytes, smaller than the entry. + // This must cause the entry to be evicted. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f01")); + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Required Insert Count 1 and Delta Base 0. + // Base is 1 + 0 = 1. + "80")); // Dynamic table entry with relative index 0, absolute index 0. +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnEncoderStreamError(Eq("Error inserting literal entry."))); + + // Set dynamic table capacity to 34. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f03")); + // Add literal entry with name "foo" and value "bar", size is 32 + 3 + 3 = 38. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnEncoderStreamError(Eq("Invalid static table entry."))); + + // Address invalid static table entry index 99. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("ff2400")); +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnEncoderStreamError(Eq("Dynamic table entry not found."))); + + DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "6294e703626172" // Add literal entry with name "foo" and value "bar". + "8100")); // Address dynamic table entry with relative index 1. Such + // entry does not exist. The most recently added and only + // dynamic table entry has relative index 0. +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnEncoderStreamError(Eq("Dynamic table entry not found."))); + + DecodeEncoderStreamData(QuicTextUtils::HexDecode( + "6294e703626172" // Add literal entry with name "foo" and value "bar". + "01")); // Duplicate dynamic table entry with relative index 1. Such + // entry does not exist. The most recently added and only + // dynamic table entry has relative index 0. +} + +TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) { + EXPECT_CALL(encoder_stream_error_delegate_, + OnEncoderStreamError(Eq("Encoded integer too large."))); + + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fffffffffffffffffffff")); +} + +TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) { + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0280" // Required Insert Count is 1. Base 1 - 1 - 0 = 0 is explicitly + // permitted by the spec. + "80")); // However, addressing entry with relative index 0 would point to + // absolute index -1, which is invalid. +} + +TEST_P(QpackDecoderTest, InvalidNegativeBase) { + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Error calculating Base."))); + + // Required Insert Count 1, Delta Base 1 with sign bit set, Base would + // be 1 - 1 - 1 = -1, but it is not allowed to be negative. + DecodeHeaderBlock(QuicTextUtils::HexDecode("0281")); +} + +TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Required Insert Count 4 and Delta Base 0. + // Base is 4 + 0 = 4. + "82")); // Indexed Header Field instruction addressing relative index 2. + // This is absolute index 1. Such entry does not exist. + + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Required Insert Count 4 and Delta Base 0. + // Base is 4 + 0 = 4. + "84")); // Indexed Header Field instruction addressing relative index 4. + // This is absolute index -1, which is invalid. + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Required Insert Count 4 and Delta Base 0. + // Base is 4 + 0 = 4. + "4200")); // Literal Header Field with Name Reference instruction + // addressing relative index 2. This is absolute index 1. Such + // entry does not exist. + + EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0500" // Required Insert Count 4 and Delta Base 0. + // Base is 4 + 0 = 4. + "4400")); // Literal Header Field with Name Reference instruction + // addressing relative index 4. This is absolute index -1, + // which is invalid. +} + +TEST_P(QpackDecoderTest, InvalidDynamicEntryByPostBaseIndex) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set. + // Base is 2 - 0 - 1 = 1 + "10")); // Indexed Header Field instruction addressing dynamic table + // entry with post-base index 0, absolute index 1. Such entry + // does not exist. + + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set. + // Base is 2 - 0 - 1 = 1 + "0000")); // Literal Header Field With Name Reference instruction + // addressing dynamic table entry with post-base index 0, + // absolute index 1. Such entry does not exist. +} + +TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) { + EXPECT_CALL( + encoder_stream_error_delegate_, + OnEncoderStreamError(Eq("Error updating dynamic table capacity."))); + + // Try to update dynamic table capacity to 2048, which exceeds the maximum. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f")); +} + +TEST_P(QpackDecoderTest, SetMaximumDynamicTableCapacity) { + // Update dynamic table capacity to 128, which does not exceed the maximum. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61")); +} + +TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) { + // Maximum dynamic table capacity is 1024. + // MaxEntries is 1024 / 32 = 32. + // Required Insert Count is decoded modulo 2 * MaxEntries, that is, modulo 64. + // A value of 1 cannot be encoded as 65 even though it has the same remainder. + EXPECT_CALL(handler_, OnDecodingErrorDetected( + Eq("Error decoding Required Insert Count."))); + DecodeHeaderBlock(QuicTextUtils::HexDecode("4100")); +} + +TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) { + // Maximum dynamic table capacity is 1024. + // MaxEntries is 1024 / 32 = 32. + + // Add literal entry with name "foo" and a 600 byte long value. This will fit + // in the dynamic table once but not twice. + DecodeEncoderStreamData( + QuicTextUtils::HexDecode("6294e7" // Name "foo". + "7fd903")); // Value length 600. + QuicString header_value(600, 'Z'); + DecodeEncoderStreamData(header_value); + + // Duplicate most recent entry 200 times. + DecodeEncoderStreamData(QuicString(200, '\x00')); + + // Now there is only one entry in the dynamic table, with absolute index 200. + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(header_value))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + EXPECT_CALL(decoder_stream_sender_delegate_, + WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); + + // Send header block with Required Insert Count = 201. + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0a00" // Encoded Required Insert Count 10, Required Insert Count 201, + // Delta Base 0, Base 201. + "80")); // Emit dynamic table entry with relative index 0. +} + +TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET"))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Required Insert Count is 1. + "d1")); // But the only instruction references the static table. +} + +TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { + EXPECT_CALL( + handler_, + OnDecodingErrorDetected( + Eq("Absolute Index must be smaller than Required Insert Count."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0201" // Required Insert Count 1 and Delta Base 1. + // Base is 1 + 1 = 2. + "80")); // Indexed Header Field instruction addressing dynamic table + // entry with relative index 0, absolute index 1. This is not + // allowed by Required Insert Count. + + EXPECT_CALL( + handler_, + OnDecodingErrorDetected( + Eq("Absolute Index must be smaller than Required Insert Count."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0201" // Required Insert Count 1 and Delta Base 1. + // Base is 1 + 1 = 2. + "4000")); // Literal Header Field with Name Reference instruction + // addressing dynamic table entry with relative index 0, + // absolute index 1. This is not allowed by Required Index + // Count. + + EXPECT_CALL( + handler_, + OnDecodingErrorDetected( + Eq("Absolute Index must be smaller than Required Insert Count."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Required Insert Count 1 and Delta Base 0. + // Base is 1 + 0 = 1. + "10")); // Indexed Header Field with Post-Base Index instruction + // addressing dynamic table entry with post-base index 0, + // absolute index 1. This is not allowed by Required Insert + // Count. + + EXPECT_CALL( + handler_, + OnDecodingErrorDetected( + Eq("Absolute Index must be smaller than Required Insert Count."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0200" // Required Insert Count 1 and Delta Base 0. + // Base is 1 + 0 = 1. + "0000")); // Literal Header Field with Post-Base Name Reference + // instruction addressing dynamic table entry with post-base + // index 0, absolute index 1. This is not allowed by Required + // Index Count. +} + +TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) { + // Add literal entry with name "foo" and value "bar". + DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); + // Duplicate entry. + DecodeEncoderStreamData(QuicTextUtils::HexDecode("00")); + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0300" // Required Insert Count 2 and Delta Base 0. + // Base is 2 + 0 = 2. + "81")); // Indexed Header Field instruction addressing dynamic table + // entry with relative index 1, absolute index 0. Header block + // requires insert count of 1, even though Required Insert Count + // is 2. + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0300" // Required Insert Count 2 and Delta Base 0. + // Base is 2 + 0 = 2. + "4100")); // Literal Header Field with Name Reference instruction + // addressing dynamic table entry with relative index 1, + // absolute index 0. Header block requires insert count of 1, + // even though Required Insert Count is 2. + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set. + // Base is 3 - 1 - 1 = 1. + "10")); // Indexed Header Field with Post-Base Index instruction + // addressing dynamic table entry with post-base index 0, + // absolute index 1. Header block requires insert count of 2, + // even though Required Insert Count is 3. + + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + + DecodeHeaderBlock(QuicTextUtils::HexDecode( + "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set. + // Base is 3 - 1 - 1 = 1. + "0000")); // Literal Header Field with Post-Base Name Reference + // instruction addressing dynamic table entry with post-base + // index 0, absolute index 1. Header block requires insert + // count of 2, even though Required Insert Count is 3. +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_test_utils.cc b/quic/core/qpack/qpack_decoder_test_utils.cc new file mode 100644 index 0000000..e8bbd17 --- /dev/null +++ b/quic/core/qpack/qpack_decoder_test_utils.cc
@@ -0,0 +1,82 @@ +// Copyright (c) 2018 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/qpack/qpack_decoder_test_utils.h" + +#include <algorithm> +#include <cstddef> +#include <utility> + +#include "testing/gmock/include/gmock/gmock.h" + +namespace quic { +namespace test { + +void NoopEncoderStreamErrorDelegate::OnEncoderStreamError( + QuicStringPiece error_message) {} + +void NoopDecoderStreamSenderDelegate::WriteDecoderStreamData( + QuicStringPiece data) {} + +TestHeadersHandler::TestHeadersHandler() + : decoding_completed_(false), decoding_error_detected_(false) {} + +void TestHeadersHandler::OnHeaderDecoded(QuicStringPiece name, + QuicStringPiece value) { + ASSERT_FALSE(decoding_completed_); + ASSERT_FALSE(decoding_error_detected_); + + header_list_.AppendValueOrAddHeader(name, value); +} + +void TestHeadersHandler::OnDecodingCompleted() { + ASSERT_FALSE(decoding_completed_); + ASSERT_FALSE(decoding_error_detected_); + + decoding_completed_ = true; +} + +void TestHeadersHandler::OnDecodingErrorDetected( + QuicStringPiece error_message) { + ASSERT_FALSE(decoding_completed_); + ASSERT_FALSE(decoding_error_detected_); + + decoding_error_detected_ = true; +} + +spdy::SpdyHeaderBlock TestHeadersHandler::ReleaseHeaderList() { + DCHECK(decoding_completed_); + DCHECK(!decoding_error_detected_); + + return std::move(header_list_); +} + +bool TestHeadersHandler::decoding_completed() const { + return decoding_completed_; +} + +bool TestHeadersHandler::decoding_error_detected() const { + return decoding_error_detected_; +} + +void QpackDecode( + QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate, + QpackProgressiveDecoder::HeadersHandlerInterface* handler, + const FragmentSizeGenerator& fragment_size_generator, + QuicStringPiece data) { + QpackDecoder decoder(encoder_stream_error_delegate, + decoder_stream_sender_delegate); + auto progressive_decoder = + decoder.DecodeHeaderBlock(/* stream_id = */ 1, handler); + while (!data.empty()) { + size_t fragment_size = std::min(fragment_size_generator(), data.size()); + progressive_decoder->Decode(data.substr(0, fragment_size)); + data = data.substr(fragment_size); + } + progressive_decoder->EndHeaderBlock(); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_test_utils.h b/quic/core/qpack/qpack_decoder_test_utils.h new file mode 100644 index 0000000..ca5b608 --- /dev/null +++ b/quic/core/qpack/qpack_decoder_test_utils.h
@@ -0,0 +1,114 @@ +// Copyright (c) 2018 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_QPACK_QPACK_DECODER_TEST_UTILS_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_ + +#include "testing/gmock/include/gmock/gmock.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +namespace quic { +namespace test { + +// QpackDecoder::EncoderStreamErrorDelegate implementation that does nothing. +class NoopEncoderStreamErrorDelegate + : public QpackDecoder::EncoderStreamErrorDelegate { + public: + ~NoopEncoderStreamErrorDelegate() override = default; + + void OnEncoderStreamError(QuicStringPiece error_message) override; +}; + +// Mock QpackDecoder::EncoderStreamErrorDelegate implementation. +class MockEncoderStreamErrorDelegate + : public QpackDecoder::EncoderStreamErrorDelegate { + public: + ~MockEncoderStreamErrorDelegate() override = default; + + MOCK_METHOD1(OnEncoderStreamError, void(QuicStringPiece error_message)); +}; + +// QpackDecoderStreamSender::Delegate implementation that does nothing. +class NoopDecoderStreamSenderDelegate + : public QpackDecoderStreamSender::Delegate { + public: + ~NoopDecoderStreamSenderDelegate() override = default; + + void WriteDecoderStreamData(QuicStringPiece data) override; +}; + +// Mock QpackDecoderStreamSender::Delegate implementation. +class MockDecoderStreamSenderDelegate + : public QpackDecoderStreamSender::Delegate { + public: + ~MockDecoderStreamSenderDelegate() override = default; + + MOCK_METHOD1(WriteDecoderStreamData, void(QuicStringPiece data)); +}; + +// HeadersHandlerInterface implementation that collects decoded headers +// into a SpdyHeaderBlock. +class TestHeadersHandler + : public QpackProgressiveDecoder::HeadersHandlerInterface { + public: + TestHeadersHandler(); + ~TestHeadersHandler() override = default; + + // HeadersHandlerInterface implementation: + void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override; + void OnDecodingCompleted() override; + void OnDecodingErrorDetected(QuicStringPiece error_message) override; + + // Release decoded header list. Must only be called if decoding is complete + // and no errors have been detected. + spdy::SpdyHeaderBlock ReleaseHeaderList(); + + bool decoding_completed() const; + bool decoding_error_detected() const; + + private: + spdy::SpdyHeaderBlock header_list_; + bool decoding_completed_; + bool decoding_error_detected_; +}; + +class MockHeadersHandler + : public QpackProgressiveDecoder::HeadersHandlerInterface { + public: + MockHeadersHandler() = default; + MockHeadersHandler(const MockHeadersHandler&) = delete; + MockHeadersHandler& operator=(const MockHeadersHandler&) = delete; + ~MockHeadersHandler() override = default; + + MOCK_METHOD2(OnHeaderDecoded, + void(QuicStringPiece name, QuicStringPiece value)); + MOCK_METHOD0(OnDecodingCompleted, void()); + MOCK_METHOD1(OnDecodingErrorDetected, void(QuicStringPiece error_message)); +}; + +class NoOpHeadersHandler + : public QpackProgressiveDecoder::HeadersHandlerInterface { + public: + ~NoOpHeadersHandler() override = default; + + void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override {} + void OnDecodingCompleted() override {} + void OnDecodingErrorDetected(QuicStringPiece error_message) override {} +}; + +void QpackDecode( + QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate, + QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate, + QpackProgressiveDecoder::HeadersHandlerInterface* handler, + const FragmentSizeGenerator& fragment_size_generator, + QuicStringPiece data); + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_
diff --git a/quic/core/qpack/qpack_encoder.cc b/quic/core/qpack/qpack_encoder.cc new file mode 100644 index 0000000..6019229 --- /dev/null +++ b/quic/core/qpack/qpack_encoder.cc
@@ -0,0 +1,54 @@ +// Copyright (c) 2018 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/qpack/qpack_encoder.h" + +#include "base/logging.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +QpackEncoder::QpackEncoder( + DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate) + : decoder_stream_error_delegate_(decoder_stream_error_delegate), + decoder_stream_receiver_(this), + encoder_stream_sender_(encoder_stream_sender_delegate) { + DCHECK(decoder_stream_error_delegate_); + DCHECK(encoder_stream_sender_delegate); +} + +QpackEncoder::~QpackEncoder() {} + +std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> +QpackEncoder::EncodeHeaderList(QuicStreamId stream_id, + const spdy::SpdyHeaderBlock* header_list) { + return QuicMakeUnique<QpackProgressiveEncoder>( + stream_id, &header_table_, &encoder_stream_sender_, header_list); +} + +void QpackEncoder::DecodeDecoderStreamData(QuicStringPiece data) { + decoder_stream_receiver_.Decode(data); +} + +void QpackEncoder::OnInsertCountIncrement(uint64_t increment) { + // TODO(bnc): Implement dynamic table management for encoding. +} + +void QpackEncoder::OnHeaderAcknowledgement(QuicStreamId stream_id) { + // TODO(bnc): Implement dynamic table management for encoding. +} + +void QpackEncoder::OnStreamCancellation(QuicStreamId stream_id) { + // TODO(bnc): Implement dynamic table management for encoding. +} + +void QpackEncoder::OnErrorDetected(QuicStringPiece error_message) { + decoder_stream_error_delegate_->OnDecoderStreamError(error_message); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_encoder.h b/quic/core/qpack/qpack_encoder.h new file mode 100644 index 0000000..4e65532 --- /dev/null +++ b/quic/core/qpack/qpack_encoder.h
@@ -0,0 +1,72 @@ +// Copyright (c) 2018 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_QPACK_QPACK_ENCODER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_H_ + +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h" + +namespace spdy { + +class SpdyHeaderBlock; + +} + +namespace quic { + +// QPACK encoder class. Exactly one instance should exist per QUIC connection. +// This class vends a new QpackProgressiveEncoder instance for each new header +// list to be encoded. +class QUIC_EXPORT_PRIVATE QpackEncoder + : public QpackDecoderStreamReceiver::Delegate { + public: + // Interface for receiving notification that an error has occurred on the + // decoder stream. This MUST be treated as a connection error of type + // HTTP_QPACK_DECODER_STREAM_ERROR. + class QUIC_EXPORT_PRIVATE DecoderStreamErrorDelegate { + public: + virtual ~DecoderStreamErrorDelegate() {} + + virtual void OnDecoderStreamError(QuicStringPiece error_message) = 0; + }; + + QpackEncoder( + DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate); + ~QpackEncoder() override; + + // This factory method is called to start encoding a header list. + // |*header_list| must remain valid and must not change + // during the lifetime of the returned ProgressiveEncoder instance. + std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderList( + QuicStreamId stream_id, + const spdy::SpdyHeaderBlock* header_list); + + // Decode data received on the decoder stream. + void DecodeDecoderStreamData(QuicStringPiece data); + + // QpackDecoderStreamReceiver::Delegate implementation + void OnInsertCountIncrement(uint64_t increment) override; + void OnHeaderAcknowledgement(QuicStreamId stream_id) override; + void OnStreamCancellation(QuicStreamId stream_id) override; + void OnErrorDetected(QuicStringPiece error_message) override; + + private: + DecoderStreamErrorDelegate* const decoder_stream_error_delegate_; + QpackDecoderStreamReceiver decoder_stream_receiver_; + QpackEncoderStreamSender encoder_stream_sender_; + QpackHeaderTable header_table_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_H_
diff --git a/quic/core/qpack/qpack_encoder_stream_receiver.cc b/quic/core/qpack/qpack_encoder_stream_receiver.cc new file mode 100644 index 0000000..3f8ef08 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_stream_receiver.cc
@@ -0,0 +1,60 @@ +// Copyright (c) 2018 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/qpack/qpack_encoder_stream_receiver.h" + +#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" +#include "net/third_party/quiche/src/http2/decoder/decode_status.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" + +namespace quic { + +QpackEncoderStreamReceiver::QpackEncoderStreamReceiver(Delegate* delegate) + : instruction_decoder_(QpackEncoderStreamLanguage(), this), + delegate_(delegate), + error_detected_(false) { + DCHECK(delegate_); +} + +void QpackEncoderStreamReceiver::Decode(QuicStringPiece data) { + if (data.empty() || error_detected_) { + return; + } + + instruction_decoder_.Decode(data); +} + +bool QpackEncoderStreamReceiver::OnInstructionDecoded( + const QpackInstruction* instruction) { + if (instruction == InsertWithNameReferenceInstruction()) { + delegate_->OnInsertWithNameReference(instruction_decoder_.s_bit(), + instruction_decoder_.varint(), + instruction_decoder_.value()); + return true; + } + + if (instruction == InsertWithoutNameReferenceInstruction()) { + delegate_->OnInsertWithoutNameReference(instruction_decoder_.name(), + instruction_decoder_.value()); + return true; + } + + if (instruction == DuplicateInstruction()) { + delegate_->OnDuplicate(instruction_decoder_.varint()); + return true; + } + + DCHECK_EQ(instruction, SetDynamicTableCapacityInstruction()); + delegate_->OnSetDynamicTableCapacity(instruction_decoder_.varint()); + return true; +} + +void QpackEncoderStreamReceiver::OnError(QuicStringPiece error_message) { + DCHECK(!error_detected_); + + error_detected_ = true; + delegate_->OnErrorDetected(error_message); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_stream_receiver.h b/quic/core/qpack/qpack_encoder_stream_receiver.h new file mode 100644 index 0000000..66a2985 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_stream_receiver.h
@@ -0,0 +1,68 @@ +// Copyright (c) 2018 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_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// This class decodes data received on the encoder stream. +class QUIC_EXPORT_PRIVATE QpackEncoderStreamReceiver + : public QpackInstructionDecoder::Delegate { + public: + // An interface for handling instructions decoded from the encoder stream, see + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2 + class Delegate { + public: + virtual ~Delegate() = default; + + // 5.2.1. Insert With Name Reference + virtual void OnInsertWithNameReference(bool is_static, + uint64_t name_index, + QuicStringPiece value) = 0; + // 5.2.2. Insert Without Name Reference + virtual void OnInsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value) = 0; + // 5.2.3. Duplicate + virtual void OnDuplicate(uint64_t index) = 0; + // 5.2.4. Set Dynamic Table Capacity + virtual void OnSetDynamicTableCapacity(uint64_t capacity) = 0; + // Decoding error + virtual void OnErrorDetected(QuicStringPiece error_message) = 0; + }; + + explicit QpackEncoderStreamReceiver(Delegate* delegate); + QpackEncoderStreamReceiver() = delete; + QpackEncoderStreamReceiver(const QpackEncoderStreamReceiver&) = delete; + QpackEncoderStreamReceiver& operator=(const QpackEncoderStreamReceiver&) = + delete; + ~QpackEncoderStreamReceiver() override = default; + + // Decode data and call appropriate Delegate method after each decoded + // instruction. Once an error occurs, Delegate::OnErrorDetected() is called, + // and all further data is ignored. + void Decode(QuicStringPiece data); + + // QpackInstructionDecoder::Delegate implementation. + bool OnInstructionDecoded(const QpackInstruction* instruction) override; + void OnError(QuicStringPiece error_message) override; + + private: + QpackInstructionDecoder instruction_decoder_; + Delegate* const delegate_; + + // True if a decoding error has been detected. + bool error_detected_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
diff --git a/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/quic/core/qpack/qpack_encoder_stream_receiver_test.cc new file mode 100644 index 0000000..5685981 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -0,0 +1,169 @@ +// Copyright (c) 2018 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/qpack/qpack_encoder_stream_receiver.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using testing::Eq; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class MockDelegate : public QpackEncoderStreamReceiver::Delegate { + public: + ~MockDelegate() override = default; + + MOCK_METHOD3(OnInsertWithNameReference, + void(bool is_static, + uint64_t name_index, + QuicStringPiece value)); + MOCK_METHOD2(OnInsertWithoutNameReference, + void(QuicStringPiece name, QuicStringPiece value)); + MOCK_METHOD1(OnDuplicate, void(uint64_t index)); + MOCK_METHOD1(OnSetDynamicTableCapacity, void(uint64_t capacity)); + MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message)); +}; + +class QpackEncoderStreamReceiverTest : public QuicTest { + protected: + QpackEncoderStreamReceiverTest() : stream_(&delegate_) {} + ~QpackEncoderStreamReceiverTest() override = default; + + void Decode(QuicStringPiece data) { stream_.Decode(data); } + StrictMock<MockDelegate>* delegate() { return &delegate_; } + + private: + QpackEncoderStreamReceiver stream_; + StrictMock<MockDelegate> delegate_; +}; + +TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReference) { + // Static, index fits in prefix, empty value. + EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 5, Eq(""))); + // Static, index fits in prefix, Huffman encoded value. + EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 2, Eq("foo"))); + // Not static, index does not fit in prefix, not Huffman encoded value. + EXPECT_CALL(*delegate(), OnInsertWithNameReference(false, 137, Eq("bar"))); + // Value length does not fit in prefix. + // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used. + EXPECT_CALL(*delegate(), + OnInsertWithNameReference(false, 42, Eq(QuicString(127, 'Z')))); + + Decode(QuicTextUtils::HexDecode( + "c500" + "c28294e7" + "bf4a03626172" + "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a")); +} + +TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceIndexTooLarge) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); + + Decode(QuicTextUtils::HexDecode("bfffffffffffffffffffffff")); +} + +TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceValueTooLong) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); + + Decode(QuicTextUtils::HexDecode("c57fffffffffffffffffffff")); +} + +TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReference) { + // Empty name and value. + EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq(""), Eq(""))); + // Huffman encoded short strings. + EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("bar"), Eq("bar"))); + // Not Huffman encoded short strings. + EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("foo"), Eq("foo"))); + // Not Huffman encoded long strings; length does not fit on prefix. + // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used. + EXPECT_CALL(*delegate(), + OnInsertWithoutNameReference(Eq(QuicString(31, 'Z')), + Eq(QuicString(127, 'Z')))); + + Decode(QuicTextUtils::HexDecode( + "4000" + "4362617203626172" + "6294e78294e7" + "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f005a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a")); +} + +// Name Length value is too large for varint decoder to decode. +TEST_F(QpackEncoderStreamReceiverTest, + InsertWithoutNameReferenceNameTooLongForVarintDecoder) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); + + Decode(QuicTextUtils::HexDecode("5fffffffffffffffffffff")); +} + +// Name Length value can be decoded by varint decoder but exceeds 1 MB limit. +TEST_F(QpackEncoderStreamReceiverTest, + InsertWithoutNameReferenceNameExceedsLimit) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long."))); + + Decode(QuicTextUtils::HexDecode("5fffff7f")); +} + +// Value Length value is too large for varint decoder to decode. +TEST_F(QpackEncoderStreamReceiverTest, + InsertWithoutNameReferenceValueTooLongForVarintDecoder) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); + + Decode(QuicTextUtils::HexDecode("436261727fffffffffffffffffffff")); +} + +// Value Length value can be decoded by varint decoder but exceeds 1 MB limit. +TEST_F(QpackEncoderStreamReceiverTest, + InsertWithoutNameReferenceValueExceedsLimit) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long."))); + + Decode(QuicTextUtils::HexDecode("436261727fffff7f")); +} + +TEST_F(QpackEncoderStreamReceiverTest, Duplicate) { + // Small index fits in prefix. + EXPECT_CALL(*delegate(), OnDuplicate(17)); + // Large index requires two extension bytes. + EXPECT_CALL(*delegate(), OnDuplicate(500)); + + Decode(QuicTextUtils::HexDecode("111fd503")); +} + +TEST_F(QpackEncoderStreamReceiverTest, DuplicateIndexTooLarge) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); + + Decode(QuicTextUtils::HexDecode("1fffffffffffffffffffff")); +} + +TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacity) { + // Small capacity fits in prefix. + EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(17)); + // Large capacity requires two extension bytes. + EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(500)); + + Decode(QuicTextUtils::HexDecode("313fd503")); +} + +TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacityTooLarge) { + EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large."))); + + Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff")); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_stream_sender.cc b/quic/core/qpack/qpack_encoder_stream_sender.cc new file mode 100644 index 0000000..fb43046 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_stream_sender.cc
@@ -0,0 +1,81 @@ +// Copyright (c) 2018 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/qpack/qpack_encoder_stream_sender.h" + +#include <cstddef> +#include <limits> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QpackEncoderStreamSender::QpackEncoderStreamSender(Delegate* delegate) + : delegate_(delegate) { + DCHECK(delegate_); +} + +void QpackEncoderStreamSender::SendInsertWithNameReference( + bool is_static, + uint64_t name_index, + QuicStringPiece value) { + instruction_encoder_.set_s_bit(is_static); + instruction_encoder_.set_varint(name_index); + instruction_encoder_.set_value(value); + + instruction_encoder_.Encode(InsertWithNameReferenceInstruction()); + + QuicString output; + + instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output); + DCHECK(!instruction_encoder_.HasNext()); + + delegate_->WriteEncoderStreamData(output); +} + +void QpackEncoderStreamSender::SendInsertWithoutNameReference( + QuicStringPiece name, + QuicStringPiece value) { + instruction_encoder_.set_name(name); + instruction_encoder_.set_value(value); + + instruction_encoder_.Encode(InsertWithoutNameReferenceInstruction()); + + QuicString output; + + instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output); + DCHECK(!instruction_encoder_.HasNext()); + + delegate_->WriteEncoderStreamData(output); +} + +void QpackEncoderStreamSender::SendDuplicate(uint64_t index) { + instruction_encoder_.set_varint(index); + + instruction_encoder_.Encode(DuplicateInstruction()); + + QuicString output; + + instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output); + DCHECK(!instruction_encoder_.HasNext()); + + delegate_->WriteEncoderStreamData(output); +} + +void QpackEncoderStreamSender::SendSetDynamicTableCapacity(uint64_t capacity) { + instruction_encoder_.set_varint(capacity); + + instruction_encoder_.Encode(SetDynamicTableCapacityInstruction()); + + QuicString output; + + instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output); + DCHECK(!instruction_encoder_.HasNext()); + + delegate_->WriteEncoderStreamData(output); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_stream_sender.h b/quic/core/qpack/qpack_encoder_stream_sender.h new file mode 100644 index 0000000..ad34568 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_stream_sender.h
@@ -0,0 +1,58 @@ +// Copyright (c) 2018 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_QPACK_QPACK_ENCODER_STREAM_SENDER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// This class serializes instructions for transmission on the encoder stream. +class QUIC_EXPORT_PRIVATE QpackEncoderStreamSender { + public: + // An interface for handling encoded data. + class Delegate { + public: + virtual ~Delegate() = default; + + // Encoded |data| is ready to be written on the encoder stream. + // WriteEncoderStreamData() is called exactly once for each instruction. + // |data| contains the entire encoded instruction and it is guaranteed to be + // not empty. + virtual void WriteEncoderStreamData(QuicStringPiece data) = 0; + }; + + explicit QpackEncoderStreamSender(Delegate* delegate); + QpackEncoderStreamSender() = delete; + QpackEncoderStreamSender(const QpackEncoderStreamSender&) = delete; + QpackEncoderStreamSender& operator=(const QpackEncoderStreamSender&) = delete; + + // Methods for sending instructions, see + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2 + + // 5.2.1. Insert With Name Reference + void SendInsertWithNameReference(bool is_static, + uint64_t name_index, + QuicStringPiece value); + // 5.2.2. Insert Without Name Reference + void SendInsertWithoutNameReference(QuicStringPiece name, + QuicStringPiece value); + // 5.2.3. Duplicate + void SendDuplicate(uint64_t index); + // 5.2.4. Set Dynamic Table Capacity + void SendSetDynamicTableCapacity(uint64_t capacity); + + private: + Delegate* const delegate_; + QpackInstructionEncoder instruction_encoder_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
diff --git a/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/quic/core/qpack/qpack_encoder_stream_sender_test.cc new file mode 100644 index 0000000..a850f18 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_stream_sender_test.cc
@@ -0,0 +1,113 @@ +// Copyright (c) 2018 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/qpack/qpack_encoder_stream_sender.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using ::testing::Eq; +using ::testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class QpackEncoderStreamSenderTest : public QuicTest { + protected: + QpackEncoderStreamSenderTest() : stream_(&delegate_) {} + ~QpackEncoderStreamSenderTest() override = default; + + StrictMock<MockEncoderStreamSenderDelegate> delegate_; + QpackEncoderStreamSender stream_; +}; + +TEST_F(QpackEncoderStreamSenderTest, InsertWithNameReference) { + // Static, index fits in prefix, empty value. + EXPECT_CALL(delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("c500")))); + stream_.SendInsertWithNameReference(true, 5, ""); + + // Static, index fits in prefix, Huffman encoded value. + EXPECT_CALL(delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("c28294e7")))); + stream_.SendInsertWithNameReference(true, 2, "foo"); + + // Not static, index does not fit in prefix, not Huffman encoded value. + EXPECT_CALL(delegate_, WriteEncoderStreamData( + Eq(QuicTextUtils::HexDecode("bf4a03626172")))); + stream_.SendInsertWithNameReference(false, 137, "bar"); + + // Value length does not fit in prefix. + // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used. + EXPECT_CALL( + delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode( + "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a")))); + stream_.SendInsertWithNameReference(false, 42, QuicString(127, 'Z')); +} + +TEST_F(QpackEncoderStreamSenderTest, InsertWithoutNameReference) { + // Empty name and value. + EXPECT_CALL(delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("4000")))); + stream_.SendInsertWithoutNameReference("", ""); + + // Huffman encoded short strings. + EXPECT_CALL(delegate_, WriteEncoderStreamData( + Eq(QuicTextUtils::HexDecode("4362617203626172")))); + stream_.SendInsertWithoutNameReference("bar", "bar"); + + // Not Huffman encoded short strings. + EXPECT_CALL(delegate_, WriteEncoderStreamData( + Eq(QuicTextUtils::HexDecode("6294e78294e7")))); + stream_.SendInsertWithoutNameReference("foo", "foo"); + + // Not Huffman encoded long strings; length does not fit on prefix. + // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used. + EXPECT_CALL( + delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode( + "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f" + "005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a")))); + stream_.SendInsertWithoutNameReference(QuicString(31, 'Z'), + QuicString(127, 'Z')); +} + +TEST_F(QpackEncoderStreamSenderTest, Duplicate) { + // Small index fits in prefix. + EXPECT_CALL(delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("11")))); + stream_.SendDuplicate(17); + + // Large index requires two extension bytes. + EXPECT_CALL(delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("1fd503")))); + stream_.SendDuplicate(500); +} + +TEST_F(QpackEncoderStreamSenderTest, SetDynamicTableCapacity) { + // Small capacity fits in prefix. + EXPECT_CALL(delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("31")))); + stream_.SendSetDynamicTableCapacity(17); + + // Large capacity requires two extension bytes. + EXPECT_CALL(delegate_, + WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("3fd503")))); + stream_.SendSetDynamicTableCapacity(500); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_test.cc b/quic/core/qpack/qpack_encoder_test.cc new file mode 100644 index 0000000..6dea968 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_test.cc
@@ -0,0 +1,167 @@ +// Copyright (c) 2018 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/qpack/qpack_encoder.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using ::testing::Eq; +using ::testing::StrictMock; +using ::testing::Values; + +namespace quic { +namespace test { +namespace { + +class QpackEncoderTest : public QuicTestWithParam<FragmentMode> { + protected: + QpackEncoderTest() : fragment_mode_(GetParam()) {} + ~QpackEncoderTest() override = default; + + QuicString Encode(const spdy::SpdyHeaderBlock* header_list) { + return QpackEncode( + &decoder_stream_error_delegate_, &encoder_stream_sender_delegate_, + FragmentModeToFragmentSizeGenerator(fragment_mode_), header_list); + } + + StrictMock<MockDecoderStreamErrorDelegate> decoder_stream_error_delegate_; + NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate_; + + private: + const FragmentMode fragment_mode_; +}; + +INSTANTIATE_TEST_SUITE_P(, + QpackEncoderTest, + Values(FragmentMode::kSingleChunk, + FragmentMode::kOctetByOctet)); + +TEST_P(QpackEncoderTest, Empty) { + spdy::SpdyHeaderBlock header_list; + QuicString output = Encode(&header_list); + + EXPECT_EQ(QuicTextUtils::HexDecode("0000"), output); +} + +TEST_P(QpackEncoderTest, EmptyName) { + spdy::SpdyHeaderBlock header_list; + header_list[""] = "foo"; + QuicString output = Encode(&header_list); + + EXPECT_EQ(QuicTextUtils::HexDecode("0000208294e7"), output); +} + +TEST_P(QpackEncoderTest, EmptyValue) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = ""; + QuicString output = Encode(&header_list); + + EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e700"), output); +} + +TEST_P(QpackEncoderTest, EmptyNameAndValue) { + spdy::SpdyHeaderBlock header_list; + header_list[""] = ""; + QuicString output = Encode(&header_list); + + EXPECT_EQ(QuicTextUtils::HexDecode("00002000"), output); +} + +TEST_P(QpackEncoderTest, Simple) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = "bar"; + QuicString output = Encode(&header_list); + + EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e703626172"), output); +} + +TEST_P(QpackEncoderTest, Multiple) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = "bar"; + // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used. + header_list["ZZZZZZZ"] = QuicString(127, 'Z'); + QuicString output = Encode(&header_list); + + EXPECT_EQ( + QuicTextUtils::HexDecode( + "0000" // prefix + "2a94e703626172" // foo: bar + "27005a5a5a5a5a5a5a" // 7 octet long header name, the smallest number + // that does not fit on a 3-bit prefix. + "7f005a5a5a5a5a5a5a" // 127 octet long header value, the smallest + "5a5a5a5a5a5a5a5a5a" // number that does not fit on a 7-bit prefix. + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + "5a5a5a5a5a5a5a5a5a"), + output); +} + +TEST_P(QpackEncoderTest, StaticTable) { + { + spdy::SpdyHeaderBlock header_list; + header_list[":method"] = "GET"; + header_list["accept-encoding"] = "gzip, deflate, br"; + header_list["location"] = ""; + + QuicString output = Encode(&header_list); + EXPECT_EQ(QuicTextUtils::HexDecode("0000d1dfcc"), output); + } + { + spdy::SpdyHeaderBlock header_list; + header_list[":method"] = "POST"; + header_list["accept-encoding"] = "compress"; + header_list["location"] = "foo"; + + QuicString output = Encode(&header_list); + EXPECT_EQ(QuicTextUtils::HexDecode("0000d45f108621e9aec2a11f5c8294e7"), + output); + } + { + spdy::SpdyHeaderBlock header_list; + header_list[":method"] = "TRACE"; + header_list["accept-encoding"] = ""; + + QuicString output = Encode(&header_list); + EXPECT_EQ(QuicTextUtils::HexDecode("00005f000554524143455f1000"), output); + } +} + +TEST_P(QpackEncoderTest, SimpleIndexed) { + spdy::SpdyHeaderBlock header_list; + header_list[":path"] = "/"; + + QpackEncoder encoder(&decoder_stream_error_delegate_, + &encoder_stream_sender_delegate_); + auto progressive_encoder = + encoder.EncodeHeaderList(/* stream_id = */ 1, &header_list); + EXPECT_TRUE(progressive_encoder->HasNext()); + + // This indexed header field takes exactly three bytes: + // two for the prefix, one for the indexed static entry. + QuicString output; + progressive_encoder->Next(3, &output); + + EXPECT_EQ(QuicTextUtils::HexDecode("0000c1"), output); + EXPECT_FALSE(progressive_encoder->HasNext()); +} + +TEST_P(QpackEncoderTest, DecoderStreamError) { + EXPECT_CALL(decoder_stream_error_delegate_, + OnDecoderStreamError(Eq("Encoded integer too large."))); + + QpackEncoder encoder(&decoder_stream_error_delegate_, + &encoder_stream_sender_delegate_); + encoder.DecodeDecoderStreamData( + QuicTextUtils::HexDecode("ffffffffffffffffffffff")); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_test_utils.cc b/quic/core/qpack/qpack_encoder_test_utils.cc new file mode 100644 index 0000000..b9a3263 --- /dev/null +++ b/quic/core/qpack/qpack_encoder_test_utils.cc
@@ -0,0 +1,37 @@ +// Copyright (c) 2018 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/qpack/qpack_encoder_test_utils.h" + +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h" + +namespace quic { +namespace test { + +void NoopDecoderStreamErrorDelegate::OnDecoderStreamError( + QuicStringPiece error_message) {} + +void NoopEncoderStreamSenderDelegate::WriteEncoderStreamData( + QuicStringPiece data) {} + +QuicString QpackEncode( + QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate, + const FragmentSizeGenerator& fragment_size_generator, + const spdy::SpdyHeaderBlock* header_list) { + QpackEncoder encoder(decoder_stream_error_delegate, + encoder_stream_sender_delegate); + auto progressive_encoder = + encoder.EncodeHeaderList(/* stream_id = */ 1, header_list); + + QuicString output; + while (progressive_encoder->HasNext()) { + progressive_encoder->Next(fragment_size_generator(), &output); + } + + return output; +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_test_utils.h b/quic/core/qpack/qpack_encoder_test_utils.h new file mode 100644 index 0000000..03d05cb --- /dev/null +++ b/quic/core/qpack/qpack_encoder_test_utils.h
@@ -0,0 +1,63 @@ +// Copyright (c) 2018 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_QPACK_QPACK_ENCODER_TEST_UTILS_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_ + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +namespace quic { +namespace test { + +// QpackEncoder::DecoderStreamErrorDelegate implementation that does nothing. +class NoopDecoderStreamErrorDelegate + : public QpackEncoder::DecoderStreamErrorDelegate { + public: + ~NoopDecoderStreamErrorDelegate() override = default; + + void OnDecoderStreamError(QuicStringPiece error_message) override; +}; + +// Mock QpackEncoder::DecoderStreamErrorDelegate implementation. +class MockDecoderStreamErrorDelegate + : public QpackEncoder::DecoderStreamErrorDelegate { + public: + ~MockDecoderStreamErrorDelegate() override = default; + + MOCK_METHOD1(OnDecoderStreamError, void(QuicStringPiece error_message)); +}; + +// QpackEncoderStreamSender::Delegate implementation that does nothing. +class NoopEncoderStreamSenderDelegate + : public QpackEncoderStreamSender::Delegate { + public: + ~NoopEncoderStreamSenderDelegate() override = default; + + void WriteEncoderStreamData(QuicStringPiece data) override; +}; + +// Mock QpackEncoderStreamSender::Delegate implementation. +class MockEncoderStreamSenderDelegate + : public QpackEncoderStreamSender::Delegate { + public: + ~MockEncoderStreamSenderDelegate() override = default; + + MOCK_METHOD1(WriteEncoderStreamData, void(QuicStringPiece data)); +}; + +QuicString QpackEncode( + QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate, + QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate, + const FragmentSizeGenerator& fragment_size_generator, + const spdy::SpdyHeaderBlock* header_list); + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_
diff --git a/quic/core/qpack/qpack_header_table.cc b/quic/core/qpack/qpack_header_table.cc new file mode 100644 index 0000000..4ba3ee3 --- /dev/null +++ b/quic/core/qpack/qpack_header_table.cc
@@ -0,0 +1,204 @@ +// Copyright (c) 2018 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/qpack/qpack_header_table.h" + +#include "base/logging.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h" + +namespace quic { + +namespace { + +const uint64_t kEntrySizeOverhead = 32; + +uint64_t EntrySize(QuicStringPiece name, QuicStringPiece value) { + return name.size() + value.size() + kEntrySizeOverhead; +} + +} // anonymous namespace + +QpackHeaderTable::QpackHeaderTable() + : static_entries_(ObtainQpackStaticTable().GetStaticEntries()), + static_index_(ObtainQpackStaticTable().GetStaticIndex()), + static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()), + dynamic_table_size_(0), + dynamic_table_capacity_(0), + maximum_dynamic_table_capacity_(0), + max_entries_(0), + dropped_entry_count_(0) {} + +QpackHeaderTable::~QpackHeaderTable() = default; + +const QpackEntry* QpackHeaderTable::LookupEntry(bool is_static, + uint64_t index) const { + if (is_static) { + if (index >= static_entries_.size()) { + return nullptr; + } + + return &static_entries_[index]; + } + + if (index < dropped_entry_count_) { + return nullptr; + } + + index -= dropped_entry_count_; + + if (index >= dynamic_entries_.size()) { + return nullptr; + } + + return &dynamic_entries_[index]; +} + +QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField( + QuicStringPiece name, + QuicStringPiece value, + bool* is_static, + uint64_t* index) const { + QpackEntry query(name, value); + + // Look for exact match in static table. + auto index_it = static_index_.find(&query); + if (index_it != static_index_.end()) { + DCHECK((*index_it)->IsStatic()); + *index = (*index_it)->InsertionIndex(); + *is_static = true; + return MatchType::kNameAndValue; + } + + // Look for exact match in dynamic table. + index_it = dynamic_index_.find(&query); + if (index_it != dynamic_index_.end()) { + DCHECK(!(*index_it)->IsStatic()); + *index = (*index_it)->InsertionIndex(); + *is_static = false; + return MatchType::kNameAndValue; + } + + // Look for name match in static table. + auto name_index_it = static_name_index_.find(name); + if (name_index_it != static_name_index_.end()) { + DCHECK(name_index_it->second->IsStatic()); + *index = name_index_it->second->InsertionIndex(); + *is_static = true; + return MatchType::kName; + } + + // Look for name match in dynamic table. + name_index_it = dynamic_name_index_.find(name); + if (name_index_it != dynamic_name_index_.end()) { + DCHECK(!name_index_it->second->IsStatic()); + *index = name_index_it->second->InsertionIndex(); + *is_static = false; + return MatchType::kName; + } + + return MatchType::kNoMatch; +} + +const QpackEntry* QpackHeaderTable::InsertEntry(QuicStringPiece name, + QuicStringPiece value) { + const uint64_t entry_size = EntrySize(name, value); + if (entry_size > dynamic_table_capacity_) { + return nullptr; + } + + const uint64_t index = dropped_entry_count_ + dynamic_entries_.size(); + dynamic_entries_.push_back({name, value, /* is_static = */ false, index}); + QpackEntry* const new_entry = &dynamic_entries_.back(); + + // Evict entries after inserting the new entry instead of before + // in order to avoid invalidating |name| and |value|. + dynamic_table_size_ += entry_size; + EvictDownToCurrentCapacity(); + + auto index_result = dynamic_index_.insert(new_entry); + if (!index_result.second) { + // An entry with the same name and value already exists. It needs to be + // replaced, because |dynamic_index_| tracks the most recent entry for a + // given name and value. + DCHECK_GT(new_entry->InsertionIndex(), + (*index_result.first)->InsertionIndex()); + dynamic_index_.erase(index_result.first); + auto result = dynamic_index_.insert(new_entry); + CHECK(result.second); + } + + auto name_result = dynamic_name_index_.insert({new_entry->name(), new_entry}); + if (!name_result.second) { + // An entry with the same name already exists. It needs to be replaced, + // because |dynamic_name_index_| tracks the most recent entry for a given + // name. + DCHECK_GT(new_entry->InsertionIndex(), + name_result.first->second->InsertionIndex()); + dynamic_name_index_.erase(name_result.first); + auto result = dynamic_name_index_.insert({new_entry->name(), new_entry}); + CHECK(result.second); + } + + return new_entry; +} + +bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) { + if (capacity > maximum_dynamic_table_capacity_) { + return false; + } + + dynamic_table_capacity_ = capacity; + EvictDownToCurrentCapacity(); + + DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_); + + return true; +} + +void QpackHeaderTable::SetMaximumDynamicTableCapacity( + uint64_t maximum_dynamic_table_capacity) { + // This method can only be called once: in the decoding context, shortly after + // construction; in the encoding context, upon receiving the SETTINGS frame. + DCHECK_EQ(0u, dynamic_table_capacity_); + DCHECK_EQ(0u, maximum_dynamic_table_capacity_); + DCHECK_EQ(0u, max_entries_); + + dynamic_table_capacity_ = maximum_dynamic_table_capacity; + maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity; + max_entries_ = maximum_dynamic_table_capacity / 32; +} + +void QpackHeaderTable::EvictDownToCurrentCapacity() { + while (dynamic_table_size_ > dynamic_table_capacity_) { + DCHECK(!dynamic_entries_.empty()); + + QpackEntry* const entry = &dynamic_entries_.front(); + const uint64_t entry_size = EntrySize(entry->name(), entry->value()); + + DCHECK_GE(dynamic_table_size_, entry_size); + dynamic_table_size_ -= entry_size; + + auto index_it = dynamic_index_.find(entry); + // Remove |dynamic_index_| entry only if it points to the same + // QpackEntry in |dynamic_entries_|. Note that |dynamic_index_| has a + // comparison function that only considers name and value, not actual + // QpackEntry* address, so find() can return a different entry if name and + // value match. + if (index_it != dynamic_index_.end() && *index_it == entry) { + dynamic_index_.erase(index_it); + } + + auto name_it = dynamic_name_index_.find(entry->name()); + // Remove |dynamic_name_index_| entry only if it points to the same + // QpackEntry in |dynamic_entries_|. + if (name_it != dynamic_name_index_.end() && name_it->second == entry) { + dynamic_name_index_.erase(name_it); + } + + dynamic_entries_.pop_front(); + ++dropped_entry_count_; + } +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_header_table.h b/quic/core/qpack/qpack_header_table.h new file mode 100644 index 0000000..eda7ab5 --- /dev/null +++ b/quic/core/qpack/qpack_header_table.h
@@ -0,0 +1,144 @@ +// Copyright (c) 2018 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_QPACK_QPACK_HEADER_TABLE_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h" +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h" + +namespace quic { + +using QpackEntry = spdy::HpackEntry; + +// This class manages the QPACK static and dynamic tables. For dynamic entries, +// it only has a concept of absolute indices. The caller needs to perform the +// necessary transformations to and from relative indices and post-base indices. +class QUIC_EXPORT_PRIVATE QpackHeaderTable { + public: + using EntryTable = spdy::HpackHeaderTable::EntryTable; + using EntryHasher = spdy::HpackHeaderTable::EntryHasher; + using EntriesEq = spdy::HpackHeaderTable::EntriesEq; + using UnorderedEntrySet = spdy::HpackHeaderTable::UnorderedEntrySet; + using NameToEntryMap = spdy::HpackHeaderTable::NameToEntryMap; + + // Result of header table lookup. + enum class MatchType { kNameAndValue, kName, kNoMatch }; + + QpackHeaderTable(); + QpackHeaderTable(const QpackHeaderTable&) = delete; + QpackHeaderTable& operator=(const QpackHeaderTable&) = delete; + + ~QpackHeaderTable(); + + // Returns the entry at absolute index |index| from the static or dynamic + // table according to |is_static|. |index| is zero based for both the static + // and the dynamic table. The returned pointer is valid until the entry is + // evicted, even if other entries are inserted into the dynamic table. + // Returns nullptr if entry does not exist. + const QpackEntry* LookupEntry(bool is_static, uint64_t index) const; + + // Returns the absolute index of an entry with matching name and value if such + // exists, otherwise one with matching name is such exists. |index| is zero + // based for both the static and the dynamic table. + MatchType FindHeaderField(QuicStringPiece name, + QuicStringPiece value, + bool* is_static, + uint64_t* index) const; + + // Insert (name, value) into the dynamic table. May evict entries. Returns a + // pointer to the inserted owned entry on success. Returns nullptr if entry + // is larger than the capacity of the dynamic table. + const QpackEntry* InsertEntry(QuicStringPiece name, QuicStringPiece value); + + // Change dynamic table capacity to |capacity|. Returns true on success. + // Returns false is |capacity| exceeds maximum dynamic table capacity. + bool SetDynamicTableCapacity(uint64_t capacity); + + // Set |maximum_dynamic_table_capacity_|. The initial value is zero. The + // final value is determined by the decoder and is sent to the encoder as + // SETTINGS_HEADER_TABLE_SIZE. Therefore in the decoding context the final + // value can be set upon connection establishment, whereas in the encoding + // context it can be set when the SETTINGS frame is received. + // This method must only be called at most once. + void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); + + // Used on request streams to encode and decode Required Insert Count. + uint64_t max_entries() const { return max_entries_; } + + // The number of entries inserted to the dynamic table (including ones that + // were dropped since). Used for relative indexing on the encoder stream. + uint64_t inserted_entry_count() const { + return dynamic_entries_.size() + dropped_entry_count_; + } + + // The number of entries dropped from the dynamic table. + uint64_t dropped_entry_count() const { return dropped_entry_count_; } + + private: + // Evict entries from the dynamic table until table size is less than or equal + // to current value of |dynamic_table_capacity_|. + void EvictDownToCurrentCapacity(); + + // Static Table + + // |static_entries_|, |static_index_|, |static_name_index_| are owned by + // QpackStaticTable singleton. + + // Tracks QpackEntries by index. + const EntryTable& static_entries_; + + // Tracks the unique static entry for a given header name and value. + const UnorderedEntrySet& static_index_; + + // Tracks the first static entry for a given header name. + const NameToEntryMap& static_name_index_; + + // Dynamic Table + + // Queue of dynamic table entries, for lookup by index. + // |dynamic_entries_| owns the entries in the dynamic table. + EntryTable dynamic_entries_; + + // An unordered set of QpackEntry pointers with a comparison operator that + // only cares about name and value. This allows fast lookup of the most + // recently inserted dynamic entry for a given header name and value pair. + // Entries point to entries owned by |dynamic_entries_|. + UnorderedEntrySet dynamic_index_; + + // An unordered map of QpackEntry pointers keyed off header name. This allows + // fast lookup of the most recently inserted dynamic entry for a given header + // name. Entries point to entries owned by |dynamic_entries_|. + NameToEntryMap dynamic_name_index_; + + // Size of the dynamic table. This is the sum of the size of its entries. + uint64_t dynamic_table_size_; + + // Dynamic Table Capacity is the maximum allowed value of + // |dynamic_table_size_|. Entries are evicted if necessary before inserting a + // new entry to ensure that dynamic table size never exceeds capacity. + // Initial value is |maximum_dynamic_table_capacity_|. Capacity can be + // changed by the encoder, as long as it does not exceed + // |maximum_dynamic_table_capacity_|. + uint64_t dynamic_table_capacity_; + + // Maximum allowed value of |dynamic_table_capacity|. The initial value is + // zero. Can be changed by SetMaximumDynamicTableCapacity(). + uint64_t maximum_dynamic_table_capacity_; + + // MaxEntries, see Section 3.2.2. Calculated based on + // |maximum_dynamic_table_capacity_|. + uint64_t max_entries_; + + // The number of entries dropped from the dynamic table. + uint64_t dropped_entry_count_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
diff --git a/quic/core/qpack/qpack_header_table_test.cc b/quic/core/qpack/qpack_header_table_test.cc new file mode 100644 index 0000000..f3ac5b5 --- /dev/null +++ b/quic/core/qpack/qpack_header_table_test.cc
@@ -0,0 +1,356 @@ +// Copyright (c) 2018 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/qpack/qpack_header_table.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h" + +namespace quic { +namespace test { +namespace { + +const uint64_t kMaximumDynamicTableCapacityForTesting = 1024 * 1024; + +class QpackHeaderTableTest : public QuicTest { + protected: + QpackHeaderTableTest() { + table_.SetMaximumDynamicTableCapacity( + kMaximumDynamicTableCapacityForTesting); + } + ~QpackHeaderTableTest() override = default; + + void ExpectEntryAtIndex(bool is_static, + uint64_t index, + QuicStringPiece expected_name, + QuicStringPiece expected_value) const { + const auto* entry = table_.LookupEntry(is_static, index); + ASSERT_TRUE(entry); + EXPECT_EQ(expected_name, entry->name()); + EXPECT_EQ(expected_value, entry->value()); + } + + void ExpectNoEntryAtIndex(bool is_static, uint64_t index) const { + EXPECT_FALSE(table_.LookupEntry(is_static, index)); + } + + void ExpectMatch(QuicStringPiece name, + QuicStringPiece value, + QpackHeaderTable::MatchType expected_match_type, + bool expected_is_static, + uint64_t expected_index) const { + // Initialize outparams to a value different from the expected to ensure + // that FindHeaderField() sets them. + bool is_static = !expected_is_static; + uint64_t index = expected_index + 1; + + QpackHeaderTable::MatchType matchtype = + table_.FindHeaderField(name, value, &is_static, &index); + + EXPECT_EQ(expected_match_type, matchtype) << name << ": " << value; + EXPECT_EQ(expected_is_static, is_static) << name << ": " << value; + EXPECT_EQ(expected_index, index) << name << ": " << value; + } + + void ExpectNoMatch(QuicStringPiece name, QuicStringPiece value) const { + bool is_static = false; + uint64_t index = 0; + + QpackHeaderTable::MatchType matchtype = + table_.FindHeaderField(name, value, &is_static, &index); + + EXPECT_EQ(QpackHeaderTable::MatchType::kNoMatch, matchtype) + << name << ": " << value; + } + + void InsertEntry(QuicStringPiece name, QuicStringPiece value) { + EXPECT_TRUE(table_.InsertEntry(name, value)); + } + + void ExpectToFailInsertingEntry(QuicStringPiece name, QuicStringPiece value) { + EXPECT_FALSE(table_.InsertEntry(name, value)); + } + + bool SetDynamicTableCapacity(uint64_t capacity) { + return table_.SetDynamicTableCapacity(capacity); + } + + uint64_t max_entries() const { return table_.max_entries(); } + uint64_t inserted_entry_count() const { + return table_.inserted_entry_count(); + } + uint64_t dropped_entry_count() const { return table_.dropped_entry_count(); } + + private: + QpackHeaderTable table_; +}; + +TEST_F(QpackHeaderTableTest, LookupStaticEntry) { + ExpectEntryAtIndex(/* is_static = */ true, 0, ":authority", ""); + + ExpectEntryAtIndex(/* is_static = */ true, 1, ":path", "/"); + + // 98 is the last entry. + ExpectEntryAtIndex(/* is_static = */ true, 98, "x-frame-options", + "sameorigin"); + + ASSERT_EQ(99u, QpackStaticTableVector().size()); + ExpectNoEntryAtIndex(/* is_static = */ true, 99); +} + +TEST_F(QpackHeaderTableTest, InsertAndLookupDynamicEntry) { + // Dynamic table is initially entry. + ExpectNoEntryAtIndex(/* is_static = */ false, 0); + ExpectNoEntryAtIndex(/* is_static = */ false, 1); + ExpectNoEntryAtIndex(/* is_static = */ false, 2); + ExpectNoEntryAtIndex(/* is_static = */ false, 3); + + // Insert one entry. + InsertEntry("foo", "bar"); + + ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + + ExpectNoEntryAtIndex(/* is_static = */ false, 1); + ExpectNoEntryAtIndex(/* is_static = */ false, 2); + ExpectNoEntryAtIndex(/* is_static = */ false, 3); + + // Insert a different entry. + InsertEntry("baz", "bing"); + + ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + + ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing"); + + ExpectNoEntryAtIndex(/* is_static = */ false, 2); + ExpectNoEntryAtIndex(/* is_static = */ false, 3); + + // Insert an entry identical to the most recently inserted one. + InsertEntry("baz", "bing"); + + ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + + ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing"); + + ExpectEntryAtIndex(/* is_static = */ false, 2, "baz", "bing"); + + ExpectNoEntryAtIndex(/* is_static = */ false, 3); +} + +TEST_F(QpackHeaderTableTest, FindStaticHeaderField) { + // A header name that has multiple entries with different values. + ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue, + true, 17u); + + ExpectMatch(":method", "POST", QpackHeaderTable::MatchType::kNameAndValue, + true, 20u); + + ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true, + 15u); + + // A header name that has a single entry with non-empty value. + ExpectMatch("accept-encoding", "gzip, deflate, br", + QpackHeaderTable::MatchType::kNameAndValue, true, 31u); + + ExpectMatch("accept-encoding", "compress", QpackHeaderTable::MatchType::kName, + true, 31u); + + ExpectMatch("accept-encoding", "", QpackHeaderTable::MatchType::kName, true, + 31u); + + // A header name that has a single entry with empty value. + ExpectMatch("location", "", QpackHeaderTable::MatchType::kNameAndValue, true, + 12u); + + ExpectMatch("location", "foo", QpackHeaderTable::MatchType::kName, true, 12u); + + // No matching header name. + ExpectNoMatch("foo", ""); + ExpectNoMatch("foo", "bar"); +} + +TEST_F(QpackHeaderTableTest, FindDynamicHeaderField) { + // Dynamic table is initially entry. + ExpectNoMatch("foo", "bar"); + ExpectNoMatch("foo", "baz"); + + // Insert one entry. + InsertEntry("foo", "bar"); + + // Match name and value. + ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false, + 0u); + + // Match name only. + ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 0u); + + // Insert an identical entry. FindHeaderField() should return the index of + // the most recently inserted matching entry. + InsertEntry("foo", "bar"); + + // Match name and value. + ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false, + 1u); + + // Match name only. + ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 1u); +} + +TEST_F(QpackHeaderTableTest, FindHeaderFieldPrefersStaticTable) { + // Insert an entry to the dynamic table that exists in the static table. + InsertEntry(":method", "GET"); + + // Insertion works. + ExpectEntryAtIndex(/* is_static = */ false, 0, ":method", "GET"); + + // FindHeaderField() prefers static table if both have name-and-value match. + ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue, + true, 17u); + + // FindHeaderField() prefers static table if both have name match but no value + // match, and prefers the first entry with matching name. + ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true, + 15u); + + // Add new entry to the dynamic table. + InsertEntry(":method", "TRACE"); + + // FindHeaderField prefers name-and-value match in dynamic table over name + // only match in static table. + ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kNameAndValue, + false, 1u); +} + +// MaxEntries is determined by maximum dynamic table capacity, +// which is set at construction time. +TEST_F(QpackHeaderTableTest, MaxEntries) { + QpackHeaderTable table1; + table1.SetMaximumDynamicTableCapacity(1024); + EXPECT_EQ(32u, table1.max_entries()); + + QpackHeaderTable table2; + table2.SetMaximumDynamicTableCapacity(500); + EXPECT_EQ(15u, table2.max_entries()); +} + +TEST_F(QpackHeaderTableTest, SetDynamicTableCapacity) { + // Dynamic table capacity does not affect MaxEntries. + EXPECT_TRUE(SetDynamicTableCapacity(1024)); + EXPECT_EQ(32u * 1024, max_entries()); + + EXPECT_TRUE(SetDynamicTableCapacity(500)); + EXPECT_EQ(32u * 1024, max_entries()); + + // Dynamic table capacity cannot exceed maximum dynamic table capacity. + EXPECT_FALSE( + SetDynamicTableCapacity(2 * kMaximumDynamicTableCapacityForTesting)); +} + +TEST_F(QpackHeaderTableTest, EvictByInsertion) { + EXPECT_TRUE(SetDynamicTableCapacity(40)); + + // Entry size is 3 + 3 + 32 = 38. + InsertEntry("foo", "bar"); + EXPECT_EQ(1u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 0u); + + // Inserting second entry evicts the first one. + InsertEntry("baz", "qux"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectNoMatch("foo", "bar"); + ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 1u); + + // Inserting an entry that does not fit results in error. + ExpectToFailInsertingEntry("foobar", "foobar"); +} + +TEST_F(QpackHeaderTableTest, EvictByUpdateTableSize) { + // Entry size is 3 + 3 + 32 = 38. + InsertEntry("foo", "bar"); + InsertEntry("baz", "qux"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 0u); + ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 1u); + + EXPECT_TRUE(SetDynamicTableCapacity(40)); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectNoMatch("foo", "bar"); + ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 1u); + + EXPECT_TRUE(SetDynamicTableCapacity(20)); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(2u, dropped_entry_count()); + + ExpectNoMatch("foo", "bar"); + ExpectNoMatch("baz", "qux"); +} + +TEST_F(QpackHeaderTableTest, EvictOldestOfIdentical) { + EXPECT_TRUE(SetDynamicTableCapacity(80)); + + // Entry size is 3 + 3 + 32 = 38. + // Insert same entry twice. + InsertEntry("foo", "bar"); + InsertEntry("foo", "bar"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + // Find most recently inserted entry. + ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 1u); + + // Inserting third entry evicts the first one, not the second. + InsertEntry("baz", "qux"); + EXPECT_EQ(3u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 1u); + ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 2u); +} + +TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) { + EXPECT_TRUE(SetDynamicTableCapacity(80)); + + // Entry size is 3 + 3 + 32 = 38. + // Insert two entries with same name but different values. + InsertEntry("foo", "bar"); + InsertEntry("foo", "baz"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + // Find most recently inserted entry with matching name. + ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName, + /* expected_is_static = */ false, 1u); + + // Inserting third entry evicts the first one, not the second. + InsertEntry("baz", "qux"); + EXPECT_EQ(3u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName, + /* expected_is_static = */ false, 1u); + ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + /* expected_is_static = */ false, 2u); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_instruction_decoder.cc b/quic/core/qpack/qpack_instruction_decoder.cc new file mode 100644 index 0000000..dbb2076 --- /dev/null +++ b/quic/core/qpack/qpack_instruction_decoder.cc
@@ -0,0 +1,310 @@ +// Copyright 2018 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/qpack/qpack_instruction_decoder.h" + +#include <algorithm> +#include <utility> + +#include "base/logging.h" + +namespace quic { + +namespace { + +// Maximum length of header name and header value. This limits the amount of +// memory the peer can make the decoder allocate when sending string literals. +const size_t kStringLiteralLengthLimit = 1024 * 1024; + +} // namespace + +QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language, + Delegate* delegate) + : language_(language), + delegate_(delegate), + s_bit_(false), + varint_(0), + varint2_(0), + is_huffman_encoded_(false), + string_length_(0), + error_detected_(false), + state_(State::kStartInstruction) {} + +void QpackInstructionDecoder::Decode(QuicStringPiece data) { + DCHECK(!data.empty()); + DCHECK(!error_detected_); + + while (true) { + size_t bytes_consumed = 0; + + switch (state_) { + case State::kStartInstruction: + DoStartInstruction(data); + break; + case State::kStartField: + DoStartField(); + break; + case State::kReadBit: + DoReadBit(data); + break; + case State::kVarintStart: + bytes_consumed = DoVarintStart(data); + break; + case State::kVarintResume: + bytes_consumed = DoVarintResume(data); + break; + case State::kVarintDone: + DoVarintDone(); + break; + case State::kReadString: + bytes_consumed = DoReadString(data); + break; + case State::kReadStringDone: + DoReadStringDone(); + break; + } + + if (error_detected_) { + return; + } + + DCHECK_LE(bytes_consumed, data.size()); + + data = QuicStringPiece(data.data() + bytes_consumed, + data.size() - bytes_consumed); + + // Stop processing if no more data but next state would require it. + if (data.empty() && (state_ != State::kStartField) && + (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) { + return; + } + } +} + +bool QpackInstructionDecoder::AtInstructionBoundary() const { + return state_ == State::kStartInstruction; +} + +void QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) { + DCHECK(!data.empty()); + + instruction_ = LookupOpcode(data[0]); + field_ = instruction_->fields.begin(); + + state_ = State::kStartField; +} + +void QpackInstructionDecoder::DoStartField() { + if (field_ == instruction_->fields.end()) { + // Completed decoding this instruction. + + if (!delegate_->OnInstructionDecoded(instruction_)) { + error_detected_ = true; + return; + } + + state_ = State::kStartInstruction; + return; + } + + switch (field_->type) { + case QpackInstructionFieldType::kSbit: + case QpackInstructionFieldType::kName: + case QpackInstructionFieldType::kValue: + state_ = State::kReadBit; + return; + case QpackInstructionFieldType::kVarint: + case QpackInstructionFieldType::kVarint2: + state_ = State::kVarintStart; + return; + } +} + +void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) { + DCHECK(!data.empty()); + + switch (field_->type) { + case QpackInstructionFieldType::kSbit: { + const uint8_t bitmask = field_->param; + s_bit_ = (data[0] & bitmask) == bitmask; + + ++field_; + state_ = State::kStartField; + + return; + } + case QpackInstructionFieldType::kName: + case QpackInstructionFieldType::kValue: { + const uint8_t prefix_length = field_->param; + DCHECK_GE(7, prefix_length); + const uint8_t bitmask = 1 << prefix_length; + is_huffman_encoded_ = (data[0] & bitmask) == bitmask; + + state_ = State::kVarintStart; + + return; + } + default: + DCHECK(false); + } +} + +size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) { + DCHECK(!data.empty()); + DCHECK(field_->type == QpackInstructionFieldType::kVarint || + field_->type == QpackInstructionFieldType::kVarint2 || + field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + + http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1); + http2::DecodeStatus status = + varint_decoder_.Start(data[0], field_->param, &buffer); + + size_t bytes_consumed = 1 + buffer.Offset(); + switch (status) { + case http2::DecodeStatus::kDecodeDone: + state_ = State::kVarintDone; + return bytes_consumed; + case http2::DecodeStatus::kDecodeInProgress: + state_ = State::kVarintResume; + return bytes_consumed; + case http2::DecodeStatus::kDecodeError: + OnError("Encoded integer too large."); + return bytes_consumed; + } +} + +size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) { + DCHECK(!data.empty()); + DCHECK(field_->type == QpackInstructionFieldType::kVarint || + field_->type == QpackInstructionFieldType::kVarint2 || + field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + + http2::DecodeBuffer buffer(data); + http2::DecodeStatus status = varint_decoder_.Resume(&buffer); + + size_t bytes_consumed = buffer.Offset(); + switch (status) { + case http2::DecodeStatus::kDecodeDone: + state_ = State::kVarintDone; + return bytes_consumed; + case http2::DecodeStatus::kDecodeInProgress: + DCHECK_EQ(bytes_consumed, data.size()); + DCHECK(buffer.Empty()); + return bytes_consumed; + case http2::DecodeStatus::kDecodeError: + OnError("Encoded integer too large."); + return bytes_consumed; + } +} + +void QpackInstructionDecoder::DoVarintDone() { + DCHECK(field_->type == QpackInstructionFieldType::kVarint || + field_->type == QpackInstructionFieldType::kVarint2 || + field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + + if (field_->type == QpackInstructionFieldType::kVarint) { + varint_ = varint_decoder_.value(); + + ++field_; + state_ = State::kStartField; + return; + } + + if (field_->type == QpackInstructionFieldType::kVarint2) { + varint2_ = varint_decoder_.value(); + + ++field_; + state_ = State::kStartField; + return; + } + + string_length_ = varint_decoder_.value(); + if (string_length_ > kStringLiteralLengthLimit) { + OnError("String literal too long."); + return; + } + + QuicString* const string = + (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; + string->clear(); + + if (string_length_ == 0) { + ++field_; + state_ = State::kStartField; + return; + } + + string->reserve(string_length_); + + state_ = State::kReadString; +} + +size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) { + DCHECK(!data.empty()); + DCHECK(field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + + QuicString* const string = + (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; + DCHECK_LT(string->size(), string_length_); + + size_t bytes_consumed = + std::min(string_length_ - string->size(), data.size()); + string->append(data.data(), bytes_consumed); + + DCHECK_LE(string->size(), string_length_); + if (string->size() == string_length_) { + state_ = State::kReadStringDone; + } + return bytes_consumed; +} + +void QpackInstructionDecoder::DoReadStringDone() { + DCHECK(field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + + QuicString* const string = + (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_; + DCHECK_EQ(string->size(), string_length_); + + if (is_huffman_encoded_) { + huffman_decoder_.Reset(); + // HpackHuffmanDecoder::Decode() cannot perform in-place decoding. + QuicString decoded_value; + huffman_decoder_.Decode(*string, &decoded_value); + if (!huffman_decoder_.InputProperlyTerminated()) { + OnError("Error in Huffman-encoded string."); + return; + } + *string = std::move(decoded_value); + } + + ++field_; + state_ = State::kStartField; +} + +const QpackInstruction* QpackInstructionDecoder::LookupOpcode( + uint8_t byte) const { + for (const auto* instruction : *language_) { + if ((byte & instruction->opcode.mask) == instruction->opcode.value) { + return instruction; + } + } + // |language_| should be defined such that instruction opcodes cover every + // possible input. + DCHECK(false); + return nullptr; +} + +void QpackInstructionDecoder::OnError(QuicStringPiece error_message) { + DCHECK(!error_detected_); + + error_detected_ = true; + delegate_->OnError(error_message); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_instruction_decoder.h b/quic/core/qpack/qpack_instruction_decoder.h new file mode 100644 index 0000000..9d674c3 --- /dev/null +++ b/quic/core/qpack/qpack_instruction_decoder.h
@@ -0,0 +1,146 @@ +// Copyright 2018 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_QPACK_QPACK_INSTRUCTION_DECODER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_ + +#include <cstddef> +#include <cstdint> + +#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h" +#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// Generic instruction decoder class. Takes a QpackLanguage that describes a +// language, that is, a set of instruction opcodes together with a list of +// fields that follow each instruction. +class QUIC_EXPORT_PRIVATE QpackInstructionDecoder { + public: + // Delegate is notified each time an instruction is decoded or when an error + // occurs. + class QUIC_EXPORT_PRIVATE Delegate { + public: + virtual ~Delegate() = default; + + // Called when an instruction (including all its fields) is decoded. + // |instruction| points to an entry in |language|. + // Returns true if decoded fields are valid. + // Returns false otherwise, in which case QpackInstructionDecoder stops + // decoding: Delegate methods will not be called, and Decode() must not be + // called. + virtual bool OnInstructionDecoded(const QpackInstruction* instruction) = 0; + + // Called by QpackInstructionDecoder if an error has occurred. + // No more data is processed afterwards. + virtual void OnError(QuicStringPiece error_message) = 0; + }; + + // Both |*language| and |*delegate| must outlive this object. + QpackInstructionDecoder(const QpackLanguage* language, Delegate* delegate); + QpackInstructionDecoder() = delete; + QpackInstructionDecoder(const QpackInstructionDecoder&) = delete; + QpackInstructionDecoder& operator=(const QpackInstructionDecoder&) = delete; + + // Provide a data fragment to decode. Must not be called after an error has + // occurred. Must not be called with empty |data|. + void Decode(QuicStringPiece data); + + // Returns true if no decoding has taken place yet or if the last instruction + // has been entirely parsed. + bool AtInstructionBoundary() const; + + // Accessors for decoded values. Should only be called for fields that are + // part of the most recently decoded instruction, and only after |this| calls + // Delegate::OnInstructionDecoded() but before Decode() is called again. + bool s_bit() const { return s_bit_; } + uint64_t varint() const { return varint_; } + uint64_t varint2() const { return varint2_; } + const QuicString& name() const { return name_; } + const QuicString& value() const { return value_; } + + private: + enum class State { + // Identify instruction. + kStartInstruction, + // Start decoding next field. + kStartField, + // Read a single bit. + kReadBit, + // Start reading integer. + kVarintStart, + // Resume reading integer. + kVarintResume, + // Done reading integer. + kVarintDone, + // Read string. + kReadString, + // Done reading string. + kReadStringDone + }; + + // One method for each state. Some take input data and return the number of + // octets processed. Some take input data but do have void return type + // because they not consume any bytes. Some do not take any arguments because + // they only change internal state. + void DoStartInstruction(QuicStringPiece data); + void DoStartField(); + void DoReadBit(QuicStringPiece data); + size_t DoVarintStart(QuicStringPiece data); + size_t DoVarintResume(QuicStringPiece data); + void DoVarintDone(); + size_t DoReadString(QuicStringPiece data); + void DoReadStringDone(); + + // Identify instruction based on opcode encoded in |byte|. + // Returns a pointer to an element of |*language_|. + const QpackInstruction* LookupOpcode(uint8_t byte) const; + + // Stops decoding and calls Delegate::OnError(). + void OnError(QuicStringPiece error_message); + + // Describes the language used for decoding. + const QpackLanguage* const language_; + + // The Delegate to notify of decoded instructions and errors. + Delegate* const delegate_; + + // Storage for decoded field values. + bool s_bit_; + uint64_t varint_; + uint64_t varint2_; + QuicString name_; + QuicString value_; + // Whether the currently decoded header name or value is Huffman encoded. + bool is_huffman_encoded_; + // Length of string being read into |name_| or |value_|. + size_t string_length_; + + // Decoder instance for decoding integers. + http2::HpackVarintDecoder varint_decoder_; + + // Decoder instance for decoding Huffman encoded strings. + http2::HpackHuffmanDecoder huffman_decoder_; + + // True if a decoding error has been detected either by + // QpackInstructionDecoder or by Delegate. + bool error_detected_; + + // Decoding state. + State state_; + + // Instruction currently being decoded. + const QpackInstruction* instruction_; + + // Field currently being decoded. + QpackInstructionFields::const_iterator field_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
diff --git a/quic/core/qpack/qpack_instruction_decoder_test.cc b/quic/core/qpack/qpack_instruction_decoder_test.cc new file mode 100644 index 0000000..08557ec --- /dev/null +++ b/quic/core/qpack/qpack_instruction_decoder_test.cc
@@ -0,0 +1,171 @@ +// Copyright 2018 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/qpack/qpack_instruction_decoder.h" + +#include <algorithm> + +#include "base/logging.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using ::testing::_; +using ::testing::Eq; +using ::testing::Expectation; +using ::testing::Return; +using ::testing::StrictMock; +using ::testing::Values; + +namespace quic { +namespace test { +namespace { + +// This instruction has three fields: an S bit and two varints. +const QpackInstruction* TestInstruction1() { + static const QpackInstruction* const instruction = + new QpackInstruction{QpackInstructionOpcode{0x00, 0x80}, + {{QpackInstructionFieldType::kSbit, 0x40}, + {QpackInstructionFieldType::kVarint, 6}, + {QpackInstructionFieldType::kVarint2, 8}}}; + return instruction; +} + +// This instruction has two fields: a header name with a 6-bit prefix, and a +// header value with a 7-bit prefix, both preceded by a Huffman bit. +const QpackInstruction* TestInstruction2() { + static const QpackInstruction* const instruction = + new QpackInstruction{QpackInstructionOpcode{0x80, 0x80}, + {{QpackInstructionFieldType::kName, 6}, + {QpackInstructionFieldType::kValue, 7}}}; + return instruction; +} + +const QpackLanguage* TestLanguage() { + static const QpackLanguage* const language = + new QpackLanguage{TestInstruction1(), TestInstruction2()}; + return language; +} + +class MockDelegate : public QpackInstructionDecoder::Delegate { + public: + MockDelegate() { + ON_CALL(*this, OnInstructionDecoded(_)).WillByDefault(Return(true)); + } + + MockDelegate(const MockDelegate&) = delete; + MockDelegate& operator=(const MockDelegate&) = delete; + ~MockDelegate() override = default; + + MOCK_METHOD1(OnInstructionDecoded, bool(const QpackInstruction* instruction)); + MOCK_METHOD1(OnError, void(QuicStringPiece error_message)); +}; + +class QpackInstructionDecoderTest : public QuicTestWithParam<FragmentMode> { + public: + QpackInstructionDecoderTest() + : decoder_(TestLanguage(), &delegate_), fragment_mode_(GetParam()) {} + ~QpackInstructionDecoderTest() override = default; + + protected: + // Decode one full instruction with fragment sizes dictated by + // |fragment_mode_|. + // Verifies that AtInstructionBoundary() returns true before and after the + // instruction, and returns false while decoding is in progress. + void DecodeInstruction(QuicStringPiece data) { + EXPECT_TRUE(decoder_.AtInstructionBoundary()); + + FragmentSizeGenerator fragment_size_generator = + FragmentModeToFragmentSizeGenerator(fragment_mode_); + + while (!data.empty()) { + size_t fragment_size = std::min(fragment_size_generator(), data.size()); + decoder_.Decode(data.substr(0, fragment_size)); + data = data.substr(fragment_size); + if (!data.empty()) { + EXPECT_FALSE(decoder_.AtInstructionBoundary()); + } + } + + EXPECT_TRUE(decoder_.AtInstructionBoundary()); + } + + StrictMock<MockDelegate> delegate_; + QpackInstructionDecoder decoder_; + + private: + const FragmentMode fragment_mode_; +}; + +INSTANTIATE_TEST_SUITE_P(, + QpackInstructionDecoderTest, + Values(FragmentMode::kSingleChunk, + FragmentMode::kOctetByOctet)); + +TEST_P(QpackInstructionDecoderTest, SBitAndVarint2) { + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())); + DecodeInstruction(QuicTextUtils::HexDecode("7f01ff65")); + + EXPECT_TRUE(decoder_.s_bit()); + EXPECT_EQ(64u, decoder_.varint()); + EXPECT_EQ(356u, decoder_.varint2()); + + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())); + DecodeInstruction(QuicTextUtils::HexDecode("05c8")); + + EXPECT_FALSE(decoder_.s_bit()); + EXPECT_EQ(5u, decoder_.varint()); + EXPECT_EQ(200u, decoder_.varint2()); +} + +TEST_P(QpackInstructionDecoderTest, NameAndValue) { + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); + DecodeInstruction(QuicTextUtils::HexDecode("83666f6f03626172")); + + EXPECT_EQ("foo", decoder_.name()); + EXPECT_EQ("bar", decoder_.value()); + + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); + DecodeInstruction(QuicTextUtils::HexDecode("8000")); + + EXPECT_EQ("", decoder_.name()); + EXPECT_EQ("", decoder_.value()); + + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); + DecodeInstruction(QuicTextUtils::HexDecode("c294e7838c767f")); + + EXPECT_EQ("foo", decoder_.name()); + EXPECT_EQ("bar", decoder_.value()); +} + +TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) { + EXPECT_CALL(delegate_, OnError(Eq("Error in Huffman-encoded string."))); + decoder_.Decode(QuicTextUtils::HexDecode("c1ff")); +} + +TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) { + EXPECT_CALL(delegate_, OnError(Eq("Encoded integer too large."))); + decoder_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff")); +} + +TEST_P(QpackInstructionDecoderTest, DelegateSignalsError) { + // First instruction is valid. + Expectation first_call = + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())) + .WillOnce(Return(true)); + // Second instruction is invalid. Decoding must halt. + EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())) + .After(first_call) + .WillOnce(Return(false)); + decoder_.Decode(QuicTextUtils::HexDecode("01000200030004000500")); + + EXPECT_EQ(2u, decoder_.varint()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_instruction_encoder.cc b/quic/core/qpack/qpack_instruction_encoder.cc new file mode 100644 index 0000000..72aa7fd --- /dev/null +++ b/quic/core/qpack/qpack_instruction_encoder.cc
@@ -0,0 +1,217 @@ +// Copyright 2018 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/qpack/qpack_instruction_encoder.h" + +#include "base/logging.h" +#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h" + +namespace quic { + +QpackInstructionEncoder::QpackInstructionEncoder() + : s_bit_(false), + varint_(0), + varint2_(0), + byte_(0), + state_(State::kOpcode), + instruction_(nullptr) {} + +void QpackInstructionEncoder::Encode(const QpackInstruction* instruction) { + DCHECK(!HasNext()); + + state_ = State::kOpcode; + instruction_ = instruction; + field_ = instruction_->fields.begin(); + + // Field list must not be empty. + DCHECK(field_ != instruction_->fields.end()); +} + +bool QpackInstructionEncoder::HasNext() const { + return instruction_ && (field_ != instruction_->fields.end()); +} + +void QpackInstructionEncoder::Next(size_t max_encoded_bytes, + QuicString* output) { + DCHECK(HasNext()); + DCHECK_NE(0u, max_encoded_bytes); + + while (max_encoded_bytes > 0 && HasNext()) { + size_t encoded_bytes = 0; + + switch (state_) { + case State::kOpcode: + DoOpcode(); + break; + case State::kStartField: + DoStartField(); + break; + case State::kSbit: + DoStaticBit(); + break; + case State::kVarintStart: + encoded_bytes = DoVarintStart(max_encoded_bytes, output); + break; + case State::kVarintResume: + encoded_bytes = DoVarintResume(max_encoded_bytes, output); + break; + case State::kStartString: + DoStartString(); + break; + case State::kWriteString: + encoded_bytes = DoWriteString(max_encoded_bytes, output); + break; + } + + DCHECK_LE(encoded_bytes, max_encoded_bytes); + max_encoded_bytes -= encoded_bytes; + } +} + +void QpackInstructionEncoder::DoOpcode() { + DCHECK_EQ(0u, byte_); + + byte_ = instruction_->opcode.value; + + state_ = State::kStartField; +} + +void QpackInstructionEncoder::DoStartField() { + switch (field_->type) { + case QpackInstructionFieldType::kSbit: + state_ = State::kSbit; + return; + case QpackInstructionFieldType::kVarint: + case QpackInstructionFieldType::kVarint2: + state_ = State::kVarintStart; + return; + case QpackInstructionFieldType::kName: + case QpackInstructionFieldType::kValue: + state_ = State::kStartString; + return; + } +} + +void QpackInstructionEncoder::DoStaticBit() { + DCHECK(field_->type == QpackInstructionFieldType::kSbit); + + if (s_bit_) { + DCHECK_EQ(0, byte_ & field_->param); + + byte_ |= field_->param; + } + + ++field_; + state_ = State::kStartField; +} + +size_t QpackInstructionEncoder::DoVarintStart(size_t max_encoded_bytes, + QuicString* output) { + DCHECK(field_->type == QpackInstructionFieldType::kVarint || + field_->type == QpackInstructionFieldType::kVarint2 || + field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + DCHECK(!varint_encoder_.IsEncodingInProgress()); + + uint64_t integer_to_encode; + switch (field_->type) { + case QpackInstructionFieldType::kVarint: + integer_to_encode = varint_; + break; + case QpackInstructionFieldType::kVarint2: + integer_to_encode = varint2_; + break; + default: + integer_to_encode = string_to_write_.size(); + break; + } + + output->push_back( + varint_encoder_.StartEncoding(byte_, field_->param, integer_to_encode)); + byte_ = 0; + + if (varint_encoder_.IsEncodingInProgress()) { + state_ = State::kVarintResume; + return 1; + } + + if (field_->type == QpackInstructionFieldType::kVarint || + field_->type == QpackInstructionFieldType::kVarint2) { + ++field_; + state_ = State::kStartField; + return 1; + } + + state_ = State::kWriteString; + return 1; +} + +size_t QpackInstructionEncoder::DoVarintResume(size_t max_encoded_bytes, + QuicString* output) { + DCHECK(field_->type == QpackInstructionFieldType::kVarint || + field_->type == QpackInstructionFieldType::kVarint2 || + field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + DCHECK(varint_encoder_.IsEncodingInProgress()); + + const size_t encoded_bytes = + varint_encoder_.ResumeEncoding(max_encoded_bytes, output); + if (varint_encoder_.IsEncodingInProgress()) { + DCHECK_EQ(encoded_bytes, max_encoded_bytes); + return encoded_bytes; + } + + DCHECK_LE(encoded_bytes, max_encoded_bytes); + + if (field_->type == QpackInstructionFieldType::kVarint || + field_->type == QpackInstructionFieldType::kVarint2) { + ++field_; + state_ = State::kStartField; + return encoded_bytes; + } + + state_ = State::kWriteString; + return encoded_bytes; +} + +void QpackInstructionEncoder::DoStartString() { + DCHECK(field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + + string_to_write_ = + (field_->type == QpackInstructionFieldType::kName) ? name_ : value_; + http2::HuffmanEncode(string_to_write_, &huffman_encoded_string_); + + if (huffman_encoded_string_.size() < string_to_write_.size()) { + DCHECK_EQ(0, byte_ & (1 << field_->param)); + + byte_ |= (1 << field_->param); + string_to_write_ = huffman_encoded_string_; + } + + state_ = State::kVarintStart; +} + +size_t QpackInstructionEncoder::DoWriteString(size_t max_encoded_bytes, + QuicString* output) { + DCHECK(field_->type == QpackInstructionFieldType::kName || + field_->type == QpackInstructionFieldType::kValue); + + if (max_encoded_bytes < string_to_write_.size()) { + const size_t encoded_bytes = max_encoded_bytes; + QuicStrAppend(output, string_to_write_.substr(0, encoded_bytes)); + string_to_write_ = string_to_write_.substr(encoded_bytes); + return encoded_bytes; + } + + const size_t encoded_bytes = string_to_write_.size(); + QuicStrAppend(output, string_to_write_); + + ++field_; + state_ = State::kStartField; + return encoded_bytes; +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_instruction_encoder.h b/quic/core/qpack/qpack_instruction_encoder.h new file mode 100644 index 0000000..15d908a --- /dev/null +++ b/quic/core/qpack/qpack_instruction_encoder.h
@@ -0,0 +1,118 @@ +// Copyright 2018 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_QPACK_QPACK_INSTRUCTION_ENCODER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_ + +#include <cstddef> +#include <cstdint> + +#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// Generic instruction encoder class. Takes a QpackLanguage that describes a +// language, that is, a set of instruction opcodes together with a list of +// fields that follow each instruction. +class QUIC_EXPORT_PRIVATE QpackInstructionEncoder { + public: + QpackInstructionEncoder(); + QpackInstructionEncoder(const QpackInstructionEncoder&) = delete; + QpackInstructionEncoder& operator=(const QpackInstructionEncoder&) = delete; + + // Setters for values to be encoded. + // |name| and |value| must remain valid until the instruction is encoded. + void set_s_bit(bool s_bit) { s_bit_ = s_bit; } + void set_varint(uint64_t varint) { varint_ = varint; } + void set_varint2(uint64_t varint2) { varint2_ = varint2; } + void set_name(QuicStringPiece name) { name_ = name; } + void set_value(QuicStringPiece value) { value_ = value; } + + // Start encoding an instruction. Must only be called after the previous + // instruction has been completely encoded. + void Encode(const QpackInstruction* instruction); + + // Returns true iff more data remains to be encoded for the current + // instruction. Returns false if there is no current instruction, that is, if + // Encode() has never been called. + bool HasNext() const; + + // Encodes the next up to |max_encoded_bytes| octets of the current + // instruction, appending to |output|. Must only be called when HasNext() + // returns true. |max_encoded_bytes| must be positive. + void Next(size_t max_encoded_bytes, QuicString* output); + + private: + enum class State { + // Write instruction opcode to |byte_|. + kOpcode, + // Select state based on type of current field. + kStartField, + // Write static bit to |byte_|. + kSbit, + // Start encoding an integer (|varint_| or |varint2_| or string length) with + // a prefix, using |byte_| for the high bits. + kVarintStart, + // Resume encoding an integer. + kVarintResume, + // Determine if Huffman encoding should be used for |name_| or |value_|, set + // up |name_| or |value_| and |huffman_encoded_string_| accordingly, and + // write the Huffman bit to |byte_|. + kStartString, + // Write string. + kWriteString + }; + + // One method for each state. Some encode up to |max_encoded_bytes| octets, + // appending to |output|. Some only change internal state. + void DoOpcode(); + void DoStartField(); + void DoStaticBit(); + size_t DoVarintStart(size_t max_encoded_bytes, QuicString* output); + size_t DoVarintResume(size_t max_encoded_bytes, QuicString* output); + void DoStartString(); + size_t DoWriteString(size_t max_encoded_bytes, QuicString* output); + + // Storage for field values to be encoded. + bool s_bit_; + uint64_t varint_; + uint64_t varint2_; + // The caller must keep the string that |name_| and |value_| point to + // valid until they are encoded. + QuicStringPiece name_; + QuicStringPiece value_; + + // Storage for the Huffman encoded string literal to be written if Huffman + // encoding is used. + QuicString huffman_encoded_string_; + + // If Huffman encoding is used, points to a substring of + // |huffman_encoded_string_|. + // Otherwise points to a substring of |name_| or |value_|. + QuicStringPiece string_to_write_; + + // Storage for a single byte that contains multiple fields, that is, multiple + // states are writing it. + uint8_t byte_; + + // Encoding state. + State state_; + + // Instruction currently being decoded. + const QpackInstruction* instruction_; + + // Field currently being decoded. + QpackInstructionFields::const_iterator field_; + + // Decoder instance for decoding integers. + http2::HpackVarintEncoder varint_encoder_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_
diff --git a/quic/core/qpack/qpack_instruction_encoder_test.cc b/quic/core/qpack/qpack_instruction_encoder_test.cc new file mode 100644 index 0000000..006476f --- /dev/null +++ b/quic/core/qpack/qpack_instruction_encoder_test.cc
@@ -0,0 +1,152 @@ +// Copyright 2018 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/qpack/qpack_instruction_encoder.h" + +#include "base/logging.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using ::testing::Values; + +namespace quic { +namespace test { +namespace { + +class QpackInstructionEncoderTest : public QuicTestWithParam<FragmentMode> { + protected: + QpackInstructionEncoderTest() : fragment_mode_(GetParam()) {} + ~QpackInstructionEncoderTest() override = default; + + // Encode |instruction| with fragment sizes dictated by |fragment_mode_|. + QuicString EncodeInstruction(const QpackInstruction* instruction) { + EXPECT_FALSE(encoder_.HasNext()); + + FragmentSizeGenerator fragment_size_generator = + FragmentModeToFragmentSizeGenerator(fragment_mode_); + QuicString output; + encoder_.Encode(instruction); + while (encoder_.HasNext()) { + encoder_.Next(fragment_size_generator(), &output); + } + + return output; + } + + QpackInstructionEncoder encoder_; + + private: + const FragmentMode fragment_mode_; +}; + +INSTANTIATE_TEST_SUITE_P(, + QpackInstructionEncoderTest, + Values(FragmentMode::kSingleChunk, + FragmentMode::kOctetByOctet)); + +TEST_P(QpackInstructionEncoderTest, Varint) { + const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80}, + {{QpackInstructionFieldType::kVarint, 7}}}; + + encoder_.set_varint(5); + EXPECT_EQ(QuicTextUtils::HexDecode("05"), EncodeInstruction(&instruction)); + + encoder_.set_varint(127); + EXPECT_EQ(QuicTextUtils::HexDecode("7f00"), EncodeInstruction(&instruction)); +} + +TEST_P(QpackInstructionEncoderTest, SBitAndTwoVarint2) { + const QpackInstruction instruction{ + QpackInstructionOpcode{0x80, 0xc0}, + {{QpackInstructionFieldType::kSbit, 0x20}, + {QpackInstructionFieldType::kVarint, 5}, + {QpackInstructionFieldType::kVarint2, 8}}}; + + encoder_.set_s_bit(true); + encoder_.set_varint(5); + encoder_.set_varint2(200); + EXPECT_EQ(QuicTextUtils::HexDecode("a5c8"), EncodeInstruction(&instruction)); + + encoder_.set_s_bit(false); + encoder_.set_varint(31); + encoder_.set_varint2(356); + EXPECT_EQ(QuicTextUtils::HexDecode("9f00ff65"), + EncodeInstruction(&instruction)); +} + +TEST_P(QpackInstructionEncoderTest, SBitAndVarintAndValue) { + const QpackInstruction instruction{QpackInstructionOpcode{0xc0, 0xc0}, + {{QpackInstructionFieldType::kSbit, 0x20}, + {QpackInstructionFieldType::kVarint, 5}, + {QpackInstructionFieldType::kValue, 7}}}; + + encoder_.set_s_bit(true); + encoder_.set_varint(100); + encoder_.set_value("foo"); + EXPECT_EQ(QuicTextUtils::HexDecode("ff458294e7"), + EncodeInstruction(&instruction)); + + encoder_.set_s_bit(false); + encoder_.set_varint(3); + encoder_.set_value("bar"); + EXPECT_EQ(QuicTextUtils::HexDecode("c303626172"), + EncodeInstruction(&instruction)); +} + +TEST_P(QpackInstructionEncoderTest, Name) { + const QpackInstruction instruction{QpackInstructionOpcode{0xe0, 0xe0}, + {{QpackInstructionFieldType::kName, 4}}}; + + encoder_.set_name(""); + EXPECT_EQ(QuicTextUtils::HexDecode("e0"), EncodeInstruction(&instruction)); + + encoder_.set_name("foo"); + EXPECT_EQ(QuicTextUtils::HexDecode("f294e7"), + EncodeInstruction(&instruction)); + + encoder_.set_name("bar"); + EXPECT_EQ(QuicTextUtils::HexDecode("e3626172"), + EncodeInstruction(&instruction)); +} + +TEST_P(QpackInstructionEncoderTest, Value) { + const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0}, + {{QpackInstructionFieldType::kValue, 3}}}; + + encoder_.set_value(""); + EXPECT_EQ(QuicTextUtils::HexDecode("f0"), EncodeInstruction(&instruction)); + + encoder_.set_value("foo"); + EXPECT_EQ(QuicTextUtils::HexDecode("fa94e7"), + EncodeInstruction(&instruction)); + + encoder_.set_value("bar"); + EXPECT_EQ(QuicTextUtils::HexDecode("f3626172"), + EncodeInstruction(&instruction)); +} + +TEST_P(QpackInstructionEncoderTest, SBitAndNameAndValue) { + const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0}, + {{QpackInstructionFieldType::kSbit, 0x08}, + {QpackInstructionFieldType::kName, 2}, + {QpackInstructionFieldType::kValue, 7}}}; + + encoder_.set_s_bit(false); + encoder_.set_name(""); + encoder_.set_value(""); + EXPECT_EQ(QuicTextUtils::HexDecode("f000"), EncodeInstruction(&instruction)); + + encoder_.set_s_bit(true); + encoder_.set_name("foo"); + encoder_.set_value("bar"); + EXPECT_EQ(QuicTextUtils::HexDecode("fe94e703626172"), + EncodeInstruction(&instruction)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_progressive_decoder.cc b/quic/core/qpack/qpack_progressive_decoder.cc new file mode 100644 index 0000000..430e707 --- /dev/null +++ b/quic/core/qpack/qpack_progressive_decoder.cc
@@ -0,0 +1,369 @@ +// Copyright (c) 2018 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/qpack/qpack_progressive_decoder.h" + +#include <algorithm> +#include <limits> + +#include "base/logging.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +QpackProgressiveDecoder::QpackProgressiveDecoder( + QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackDecoderStreamSender* decoder_stream_sender, + HeadersHandlerInterface* handler) + : stream_id_(stream_id), + prefix_decoder_( + QuicMakeUnique<QpackInstructionDecoder>(QpackPrefixLanguage(), this)), + instruction_decoder_(QpackRequestStreamLanguage(), this), + header_table_(header_table), + decoder_stream_sender_(decoder_stream_sender), + handler_(handler), + required_insert_count_(0), + base_(0), + required_insert_count_so_far_(0), + prefix_decoded_(false), + decoding_(true), + error_detected_(false) {} + +// static +bool QpackProgressiveDecoder::DecodeRequiredInsertCount( + uint64_t encoded_required_insert_count, + uint64_t max_entries, + uint64_t total_number_of_inserts, + uint64_t* required_insert_count) { + if (encoded_required_insert_count == 0) { + *required_insert_count = 0; + return true; + } + + // |max_entries| is calculated by dividing an unsigned 64-bit integer by 32, + // precluding all calculations in this method from overflowing. + DCHECK_LE(max_entries, std::numeric_limits<uint64_t>::max() / 32); + + if (encoded_required_insert_count > 2 * max_entries) { + return false; + } + + *required_insert_count = encoded_required_insert_count - 1; + DCHECK_LT(*required_insert_count, std::numeric_limits<uint64_t>::max() / 16); + + uint64_t current_wrapped = total_number_of_inserts % (2 * max_entries); + DCHECK_LT(current_wrapped, std::numeric_limits<uint64_t>::max() / 16); + + if (current_wrapped >= *required_insert_count + max_entries) { + // Required Insert Count wrapped around 1 extra time. + *required_insert_count += 2 * max_entries; + } else if (current_wrapped + max_entries < *required_insert_count) { + // Decoder wrapped around 1 extra time. + current_wrapped += 2 * max_entries; + } + + if (*required_insert_count > + std::numeric_limits<uint64_t>::max() - total_number_of_inserts) { + return false; + } + + *required_insert_count += total_number_of_inserts; + + // Prevent underflow, also disallow invalid value 0 for Required Insert Count. + if (current_wrapped >= *required_insert_count) { + return false; + } + + *required_insert_count -= current_wrapped; + + return true; +} + +void QpackProgressiveDecoder::Decode(QuicStringPiece data) { + DCHECK(decoding_); + + if (data.empty() || error_detected_) { + return; + } + + // Decode prefix byte by byte until the first (and only) instruction is + // decoded. + while (!prefix_decoded_) { + prefix_decoder_->Decode(data.substr(0, 1)); + data = data.substr(1); + if (data.empty()) { + return; + } + } + + instruction_decoder_.Decode(data); +} + +void QpackProgressiveDecoder::EndHeaderBlock() { + DCHECK(decoding_); + decoding_ = false; + + if (error_detected_) { + return; + } + + if (!instruction_decoder_.AtInstructionBoundary()) { + OnError("Incomplete header block."); + return; + } + + if (!prefix_decoded_) { + OnError("Incomplete header data prefix."); + return; + } + + if (required_insert_count_ != required_insert_count_so_far_) { + OnError("Required Insert Count too large."); + return; + } + + decoder_stream_sender_->SendHeaderAcknowledgement(stream_id_); + handler_->OnDecodingCompleted(); +} + +bool QpackProgressiveDecoder::OnInstructionDecoded( + const QpackInstruction* instruction) { + if (instruction == QpackIndexedHeaderFieldInstruction()) { + return DoIndexedHeaderFieldInstruction(); + } + if (instruction == QpackIndexedHeaderFieldPostBaseInstruction()) { + return DoIndexedHeaderFieldPostBaseInstruction(); + } + if (instruction == QpackLiteralHeaderFieldNameReferenceInstruction()) { + return DoLiteralHeaderFieldNameReferenceInstruction(); + } + if (instruction == QpackLiteralHeaderFieldPostBaseInstruction()) { + return DoLiteralHeaderFieldPostBaseInstruction(); + } + if (instruction == QpackLiteralHeaderFieldInstruction()) { + return DoLiteralHeaderFieldInstruction(); + } + DCHECK_EQ(instruction, QpackPrefixInstruction()); + return DoPrefixInstruction(); +} + +void QpackProgressiveDecoder::OnError(QuicStringPiece error_message) { + DCHECK(!error_detected_); + + error_detected_ = true; + handler_->OnDecodingErrorDetected(error_message); +} + +bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { + if (!instruction_decoder_.s_bit()) { + uint64_t absolute_index; + if (!RequestStreamRelativeIndexToAbsoluteIndex( + instruction_decoder_.varint(), &absolute_index)) { + OnError("Invalid relative index."); + return false; + } + + if (absolute_index >= required_insert_count_) { + OnError("Absolute Index must be smaller than Required Insert Count."); + return false; + } + + DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max()); + required_insert_count_so_far_ = + std::max(required_insert_count_so_far_, absolute_index + 1); + + auto entry = + header_table_->LookupEntry(/* is_static = */ false, absolute_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), entry->value()); + return true; + } + + auto entry = header_table_->LookupEntry(/* is_static = */ true, + instruction_decoder_.varint()); + if (!entry) { + OnError("Static table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), entry->value()); + return true; +} + +bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { + uint64_t absolute_index; + if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), + &absolute_index)) { + OnError("Invalid post-base index."); + return false; + } + + if (absolute_index >= required_insert_count_) { + OnError("Absolute Index must be smaller than Required Insert Count."); + return false; + } + + DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max()); + required_insert_count_so_far_ = + std::max(required_insert_count_so_far_, absolute_index + 1); + + auto entry = + header_table_->LookupEntry(/* is_static = */ false, absolute_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), entry->value()); + return true; +} + +bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { + if (!instruction_decoder_.s_bit()) { + uint64_t absolute_index; + if (!RequestStreamRelativeIndexToAbsoluteIndex( + instruction_decoder_.varint(), &absolute_index)) { + OnError("Invalid relative index."); + return false; + } + + if (absolute_index >= required_insert_count_) { + OnError("Absolute Index must be smaller than Required Insert Count."); + return false; + } + + DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max()); + required_insert_count_so_far_ = + std::max(required_insert_count_so_far_, absolute_index + 1); + + auto entry = + header_table_->LookupEntry(/* is_static = */ false, absolute_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); + return true; + } + + auto entry = header_table_->LookupEntry(/* is_static = */ true, + instruction_decoder_.varint()); + if (!entry) { + OnError("Static table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); + return true; +} + +bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() { + uint64_t absolute_index; + if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), + &absolute_index)) { + OnError("Invalid post-base index."); + return false; + } + + if (absolute_index >= required_insert_count_) { + OnError("Absolute Index must be smaller than Required Insert Count."); + return false; + } + + DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max()); + required_insert_count_so_far_ = + std::max(required_insert_count_so_far_, absolute_index + 1); + + auto entry = + header_table_->LookupEntry(/* is_static = */ false, absolute_index); + if (!entry) { + OnError("Dynamic table entry not found."); + return false; + } + + handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); + return true; +} + +bool QpackProgressiveDecoder::DoLiteralHeaderFieldInstruction() { + handler_->OnHeaderDecoded(instruction_decoder_.name(), + instruction_decoder_.value()); + + return true; +} + +bool QpackProgressiveDecoder::DoPrefixInstruction() { + DCHECK(!prefix_decoded_); + + if (!DecodeRequiredInsertCount( + prefix_decoder_->varint(), header_table_->max_entries(), + header_table_->inserted_entry_count(), &required_insert_count_)) { + OnError("Error decoding Required Insert Count."); + return false; + } + + const bool sign = prefix_decoder_->s_bit(); + const uint64_t delta_base = prefix_decoder_->varint2(); + if (!DeltaBaseToBase(sign, delta_base, &base_)) { + OnError("Error calculating Base."); + return false; + } + + prefix_decoded_ = true; + + return true; +} + +bool QpackProgressiveDecoder::DeltaBaseToBase(bool sign, + uint64_t delta_base, + uint64_t* base) { + if (sign) { + if (delta_base == std::numeric_limits<uint64_t>::max() || + required_insert_count_ < delta_base + 1) { + return false; + } + *base = required_insert_count_ - delta_base - 1; + return true; + } + + if (delta_base > + std::numeric_limits<uint64_t>::max() - required_insert_count_) { + return false; + } + *base = required_insert_count_ + delta_base; + return true; +} + +bool QpackProgressiveDecoder::RequestStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t* absolute_index) const { + if (relative_index == std::numeric_limits<uint64_t>::max() || + relative_index + 1 > base_) { + return false; + } + + *absolute_index = base_ - 1 - relative_index; + return true; +} + +bool QpackProgressiveDecoder::PostBaseIndexToAbsoluteIndex( + uint64_t post_base_index, + uint64_t* absolute_index) const { + if (post_base_index >= std::numeric_limits<uint64_t>::max() - base_) { + return false; + } + + *absolute_index = base_ + post_base_index; + return true; +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_progressive_decoder.h b/quic/core/qpack/qpack_progressive_decoder.h new file mode 100644 index 0000000..7988861 --- /dev/null +++ b/quic/core/qpack/qpack_progressive_decoder.h
@@ -0,0 +1,140 @@ +// Copyright (c) 2018 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_QPACK_QPACK_PROGRESSIVE_DECODER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_ + +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QpackHeaderTable; + +// Class to decode a single header block. +class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder + : public QpackInstructionDecoder::Delegate { + public: + // Interface for receiving decoded header block from the decoder. + class QUIC_EXPORT_PRIVATE HeadersHandlerInterface { + public: + virtual ~HeadersHandlerInterface() {} + + // Called when a new header name-value pair is decoded. Multiple values for + // a given name will be emitted as multiple calls to OnHeader. + virtual void OnHeaderDecoded(QuicStringPiece name, + QuicStringPiece value) = 0; + + // Called when the header block is completely decoded. + // Indicates the total number of bytes in this block. + // The decoder will not access the handler after this call. + // Note that this method might not be called synchronously when the header + // block is received on the wire, in case decoding is blocked on receiving + // entries on the encoder stream. TODO(bnc): Implement blocked decoding. + virtual void OnDecodingCompleted() = 0; + + // Called when a decoding error has occurred. No other methods will be + // called afterwards. + virtual void OnDecodingErrorDetected(QuicStringPiece error_message) = 0; + }; + + QpackProgressiveDecoder() = delete; + QpackProgressiveDecoder(QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackDecoderStreamSender* decoder_stream_sender, + HeadersHandlerInterface* handler); + QpackProgressiveDecoder(const QpackProgressiveDecoder&) = delete; + QpackProgressiveDecoder& operator=(const QpackProgressiveDecoder&) = delete; + ~QpackProgressiveDecoder() override = default; + + // Calculate Required Insert Count from Encoded Required Insert Count, + // MaxEntries, and total number of dynamic table insertions according to + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#ric. + // Returns true on success, false on invalid input or overflow/underflow. + static bool DecodeRequiredInsertCount(uint64_t encoded_required_insert_count, + uint64_t max_entries, + uint64_t total_number_of_inserts, + uint64_t* required_insert_count); + + // Provide a data fragment to decode. + void Decode(QuicStringPiece data); + + // Signal that the entire header block has been received and passed in + // through Decode(). No methods must be called afterwards. + void EndHeaderBlock(); + + // QpackInstructionDecoder::Delegate implementation. + bool OnInstructionDecoded(const QpackInstruction* instruction) override; + void OnError(QuicStringPiece error_message) override; + + private: + bool DoIndexedHeaderFieldInstruction(); + bool DoIndexedHeaderFieldPostBaseInstruction(); + bool DoLiteralHeaderFieldNameReferenceInstruction(); + bool DoLiteralHeaderFieldPostBaseInstruction(); + bool DoLiteralHeaderFieldInstruction(); + bool DoPrefixInstruction(); + + // Calculates Base from |required_insert_count_|, which must be set before + // calling this method, and sign bit and Delta Base in the Header Data Prefix, + // which are passed in as arguments. Returns true on success, false on + // failure due to overflow/underflow. + bool DeltaBaseToBase(bool sign, uint64_t delta_base, uint64_t* base); + + // The request stream can use relative index (but different from the kind of + // relative index used on the encoder stream), and post-base index. + // These methods convert relative index and post-base index to absolute index + // (one based). They return true on success, or false if conversion fails due + // to overflow/underflow. On success, |*absolute_index| is guaranteed to be + // strictly less than std::numeric_limits<uint64_t>::max(). + bool RequestStreamRelativeIndexToAbsoluteIndex( + uint64_t relative_index, + uint64_t* absolute_index) const; + bool PostBaseIndexToAbsoluteIndex(uint64_t post_base_index, + uint64_t* absolute_index) const; + + const QuicStreamId stream_id_; + + // |prefix_decoder_| only decodes a handful of bytes then it can be + // destroyed to conserve memory. |instruction_decoder_|, on the other hand, + // is used until the entire header block is decoded. + std::unique_ptr<QpackInstructionDecoder> prefix_decoder_; + QpackInstructionDecoder instruction_decoder_; + + const QpackHeaderTable* const header_table_; + QpackDecoderStreamSender* const decoder_stream_sender_; + HeadersHandlerInterface* const handler_; + + // Required Insert Count and Base are decoded from the Header Data Prefix. + uint64_t required_insert_count_; + uint64_t base_; + + // Required Insert Count is one larger than the largest absolute index of all + // referenced dynamic table entries, or zero if no dynamic table entries are + // referenced. |required_insert_count_so_far_| starts out as zero and keeps + // track of the Required Insert Count based on entries decoded so far. + // After decoding is completed, it is compared to |required_insert_count_|. + uint64_t required_insert_count_so_far_; + + // False until prefix is fully read and decoded. + bool prefix_decoded_; + + // True until EndHeaderBlock() is called. + bool decoding_; + + // True if a decoding error has been detected. + bool error_detected_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
diff --git a/quic/core/qpack/qpack_progressive_decoder_test.cc b/quic/core/qpack/qpack_progressive_decoder_test.cc new file mode 100644 index 0000000..b306702 --- /dev/null +++ b/quic/core/qpack/qpack_progressive_decoder_test.cc
@@ -0,0 +1,124 @@ +// Copyright (c) 2018 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/qpack/qpack_progressive_decoder.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +// For testing valid decodings, the Encoded Required Insert Count is calculated +// from Required Insert Count, so that there is an expected value to compare +// the decoded value against, and so that intricate inequalities can be +// documented. +struct { + uint64_t required_insert_count; + uint64_t max_entries; + uint64_t total_number_of_inserts; +} kTestData[] = { + // Maximum dynamic table capacity is zero. + {0, 0, 0}, + // No dynamic entries in header. + {0, 100, 0}, + {0, 100, 500}, + // Required Insert Count has not wrapped around yet, no entries evicted. + {15, 100, 25}, + {20, 100, 10}, + // Required Insert Count has not wrapped around yet, some entries evicted. + {90, 100, 110}, + // Required Insert Count has wrapped around. + {234, 100, 180}, + // Required Insert Count has wrapped around many times. + {5678, 100, 5701}, + // Lowest and highest possible Required Insert Count values + // for given MaxEntries and total number of insertions. + {401, 100, 500}, + {600, 100, 500}}; + +uint64_t EncodeRequiredInsertCount(uint64_t required_insert_count, + uint64_t max_entries) { + if (required_insert_count == 0) { + return 0; + } + + return required_insert_count % (2 * max_entries) + 1; +} + +TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCount) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(kTestData); ++i) { + const uint64_t required_insert_count = kTestData[i].required_insert_count; + const uint64_t max_entries = kTestData[i].max_entries; + const uint64_t total_number_of_inserts = + kTestData[i].total_number_of_inserts; + + if (required_insert_count != 0) { + // Dynamic entries cannot be referenced if dynamic table capacity is zero. + ASSERT_LT(0u, max_entries) << i; + // Entry |total_number_of_inserts - 1 - max_entries| and earlier entries + // are evicted. Entry |required_insert_count - 1| is referenced. No + // evicted entry can be referenced. + ASSERT_LT(total_number_of_inserts, required_insert_count + max_entries) + << i; + // Entry |required_insert_count - 1 - max_entries| and earlier entries are + // evicted, entry |total_number_of_inserts - 1| is the last acknowledged + // entry. Every evicted entry must be acknowledged. + ASSERT_LE(required_insert_count, total_number_of_inserts + max_entries) + << i; + } + + uint64_t encoded_required_insert_count = + EncodeRequiredInsertCount(required_insert_count, max_entries); + + // Initialize to a value different from the expected output to confirm that + // DecodeRequiredInsertCount() modifies the value of + // |decoded_required_insert_count|. + uint64_t decoded_required_insert_count = required_insert_count + 1; + EXPECT_TRUE(QpackProgressiveDecoder::DecodeRequiredInsertCount( + encoded_required_insert_count, max_entries, total_number_of_inserts, + &decoded_required_insert_count)) + << i; + + EXPECT_EQ(decoded_required_insert_count, required_insert_count) << i; + } +} + +// Failures are tested with hardcoded values for encoded required insert count, +// to provide test coverage for values that would never be produced by a well +// behaved encoding function. +struct { + uint64_t encoded_required_insert_count; + uint64_t max_entries; + uint64_t total_number_of_inserts; +} kInvalidTestData[] = { + // Maximum dynamic table capacity is zero, yet header block + // claims to have a reference to a dynamic table entry. + {1, 0, 0}, + {9, 0, 0}, + // Examples from + // https://github.com/quicwg/base-drafts/issues/2112#issue-389626872. + {1, 10, 2}, + {18, 10, 2}, + // Encoded Required Insert Count value too small or too large + // for given MaxEntries and total number of insertions. + {400, 100, 500}, + {601, 100, 500}}; + +TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCountError) { + for (size_t i = 0; i < QUIC_ARRAYSIZE(kInvalidTestData); ++i) { + uint64_t decoded_required_insert_count = 0; + EXPECT_FALSE(QpackProgressiveDecoder::DecodeRequiredInsertCount( + kInvalidTestData[i].encoded_required_insert_count, + kInvalidTestData[i].max_entries, + kInvalidTestData[i].total_number_of_inserts, + &decoded_required_insert_count)) + << i; + } +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_progressive_encoder.cc b/quic/core/qpack/qpack_progressive_encoder.cc new file mode 100644 index 0000000..41433ff --- /dev/null +++ b/quic/core/qpack/qpack_progressive_encoder.cc
@@ -0,0 +1,138 @@ +// Copyright (c) 2018 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/qpack/qpack_progressive_encoder.h" + +#include "base/logging.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +QpackProgressiveEncoder::QpackProgressiveEncoder( + QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackEncoderStreamSender* encoder_stream_sender, + const spdy::SpdyHeaderBlock* header_list) + : stream_id_(stream_id), + header_table_(header_table), + encoder_stream_sender_(encoder_stream_sender), + header_list_(header_list), + header_list_iterator_(header_list_->begin()), + prefix_encoded_(false) { + // TODO(bnc): Use |stream_id_| for dynamic table entry management, and + // remove this dummy DCHECK. + DCHECK_LE(0u, stream_id_); + + DCHECK(header_table_); + DCHECK(encoder_stream_sender_); + DCHECK(header_list_); +} + +bool QpackProgressiveEncoder::HasNext() const { + return header_list_iterator_ != header_list_->end() || !prefix_encoded_; +} + +void QpackProgressiveEncoder::Next(size_t max_encoded_bytes, + QuicString* output) { + DCHECK_NE(0u, max_encoded_bytes); + DCHECK(HasNext()); + + // Since QpackInstructionEncoder::Next() does not indicate the number of bytes + // written, save the maximum new size of |*output|. + const size_t max_length = output->size() + max_encoded_bytes; + + DCHECK_LT(output->size(), max_length); + + if (!prefix_encoded_ && !instruction_encoder_.HasNext()) { + // TODO(bnc): Implement dynamic entries and set Required Insert Count and + // Delta Base accordingly. + instruction_encoder_.set_varint(0); + instruction_encoder_.set_varint2(0); + instruction_encoder_.set_s_bit(false); + + instruction_encoder_.Encode(QpackPrefixInstruction()); + + DCHECK(instruction_encoder_.HasNext()); + } + + do { + // Call QpackInstructionEncoder::Encode for |*header_list_iterator_| if it + // has not been called yet. + if (!instruction_encoder_.HasNext()) { + DCHECK(prefix_encoded_); + + // Even after |name| and |value| go out of scope, copies of these + // QuicStringPieces retained by QpackInstructionEncoder are still valid as + // long as |header_list_| is valid. + QuicStringPiece name = header_list_iterator_->first; + QuicStringPiece value = header_list_iterator_->second; + + // |is_static| and |index| are saved by QpackInstructionEncoder by value, + // there are no lifetime concerns. + bool is_static; + uint64_t index; + + auto match_type = + header_table_->FindHeaderField(name, value, &is_static, &index); + + switch (match_type) { + case QpackHeaderTable::MatchType::kNameAndValue: + DCHECK(is_static) << "Dynamic table entries not supported yet."; + + instruction_encoder_.set_s_bit(is_static); + instruction_encoder_.set_varint(index); + + instruction_encoder_.Encode(QpackIndexedHeaderFieldInstruction()); + + break; + case QpackHeaderTable::MatchType::kName: + DCHECK(is_static) << "Dynamic table entries not supported yet."; + + instruction_encoder_.set_s_bit(is_static); + instruction_encoder_.set_varint(index); + instruction_encoder_.set_value(value); + + instruction_encoder_.Encode( + QpackLiteralHeaderFieldNameReferenceInstruction()); + + break; + case QpackHeaderTable::MatchType::kNoMatch: + instruction_encoder_.set_name(name); + instruction_encoder_.set_value(value); + + instruction_encoder_.Encode(QpackLiteralHeaderFieldInstruction()); + + break; + } + } + + DCHECK(instruction_encoder_.HasNext()); + + instruction_encoder_.Next(max_length - output->size(), output); + + if (instruction_encoder_.HasNext()) { + // There was not enough room to completely encode current header field. + DCHECK_EQ(output->size(), max_length); + + return; + } + + // It is possible that the output buffer was just large enough for encoding + // the current header field, hence equality is allowed here. + DCHECK_LE(output->size(), max_length); + + if (prefix_encoded_) { + // Move on to the next header field. + ++header_list_iterator_; + } else { + // Mark prefix as encoded. + prefix_encoded_ = true; + } + } while (HasNext() && output->size() < max_length); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_progressive_encoder.h b/quic/core/qpack/qpack_progressive_encoder.h new file mode 100644 index 0000000..86cce55 --- /dev/null +++ b/quic/core/qpack/qpack_progressive_encoder.h
@@ -0,0 +1,57 @@ +// Copyright (c) 2018 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_QPACK_QPACK_PROGRESSIVE_ENCODER_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_ + +#include <cstddef> + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +namespace quic { + +class QpackHeaderTable; + +// An implementation of ProgressiveEncoder interface that encodes a single +// header block. +class QUIC_EXPORT_PRIVATE QpackProgressiveEncoder + : public spdy::HpackEncoder::ProgressiveEncoder { + public: + QpackProgressiveEncoder() = delete; + QpackProgressiveEncoder(QuicStreamId stream_id, + QpackHeaderTable* header_table, + QpackEncoderStreamSender* encoder_stream_sender, + const spdy::SpdyHeaderBlock* header_list); + QpackProgressiveEncoder(const QpackProgressiveEncoder&) = delete; + QpackProgressiveEncoder& operator=(const QpackProgressiveEncoder&) = delete; + ~QpackProgressiveEncoder() override = default; + + // Returns true iff more remains to encode. + bool HasNext() const override; + + // Encodes up to |max_encoded_bytes| octets, appending to |output|. + void Next(size_t max_encoded_bytes, QuicString* output) override; + + private: + const QuicStreamId stream_id_; + QpackInstructionEncoder instruction_encoder_; + const QpackHeaderTable* const header_table_; + QpackEncoderStreamSender* const encoder_stream_sender_; + const spdy::SpdyHeaderBlock* const header_list_; + + // Header field currently being encoded. + spdy::SpdyHeaderBlock::const_iterator header_list_iterator_; + + // False until prefix is fully encoded. + bool prefix_encoded_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
diff --git a/quic/core/qpack/qpack_round_trip_test.cc b/quic/core/qpack/qpack_round_trip_test.cc new file mode 100644 index 0000000..700ff85 --- /dev/null +++ b/quic/core/qpack/qpack_round_trip_test.cc
@@ -0,0 +1,137 @@ +// Copyright (c) 2018 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 <tuple> + +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +using ::testing::Combine; +using ::testing::Values; + +namespace quic { +namespace test { +namespace { + +class QpackRoundTripTest + : public QuicTestWithParam<std::tuple<FragmentMode, FragmentMode>> { + public: + QpackRoundTripTest() + : encoding_fragment_mode_(std::get<0>(GetParam())), + decoding_fragment_mode_(std::get<1>(GetParam())) {} + ~QpackRoundTripTest() override = default; + + spdy::SpdyHeaderBlock EncodeThenDecode( + const spdy::SpdyHeaderBlock& header_list) { + NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; + NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate; + QuicString encoded_header_block = QpackEncode( + &decoder_stream_error_delegate, &encoder_stream_sender_delegate, + FragmentModeToFragmentSizeGenerator(encoding_fragment_mode_), + &header_list); + + TestHeadersHandler handler; + NoopEncoderStreamErrorDelegate encoder_stream_error_delegate; + NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate; + QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate, + &handler, + FragmentModeToFragmentSizeGenerator(decoding_fragment_mode_), + encoded_header_block); + + EXPECT_TRUE(handler.decoding_completed()); + EXPECT_FALSE(handler.decoding_error_detected()); + + return handler.ReleaseHeaderList(); + } + + private: + const FragmentMode encoding_fragment_mode_; + const FragmentMode decoding_fragment_mode_; +}; + +INSTANTIATE_TEST_SUITE_P( + , + QpackRoundTripTest, + Combine(Values(FragmentMode::kSingleChunk, FragmentMode::kOctetByOctet), + Values(FragmentMode::kSingleChunk, FragmentMode::kOctetByOctet))); + +TEST_P(QpackRoundTripTest, Empty) { + spdy::SpdyHeaderBlock header_list; + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); +} + +TEST_P(QpackRoundTripTest, EmptyName) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = "bar"; + header_list[""] = "bar"; + + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); +} + +TEST_P(QpackRoundTripTest, EmptyValue) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = ""; + header_list[""] = ""; + + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); +} + +TEST_P(QpackRoundTripTest, MultipleWithLongEntries) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = "bar"; + header_list[":path"] = "/"; + header_list["foobaar"] = QuicString(127, 'Z'); + header_list[QuicString(1000, 'b')] = QuicString(1000, 'c'); + + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); +} + +TEST_P(QpackRoundTripTest, StaticTable) { + { + spdy::SpdyHeaderBlock header_list; + header_list[":method"] = "GET"; + header_list["accept-encoding"] = "gzip, deflate"; + header_list["cache-control"] = ""; + header_list["foo"] = "bar"; + header_list[":path"] = "/"; + + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); + } + { + spdy::SpdyHeaderBlock header_list; + header_list[":method"] = "POST"; + header_list["accept-encoding"] = "brotli"; + header_list["cache-control"] = "foo"; + header_list["foo"] = "bar"; + header_list[":path"] = "/"; + + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); + } + { + spdy::SpdyHeaderBlock header_list; + header_list[":method"] = "CONNECT"; + header_list["accept-encoding"] = ""; + header_list["foo"] = "bar"; + header_list[":path"] = "/"; + + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); + } +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_static_table.cc b/quic/core/qpack/qpack_static_table.cc new file mode 100644 index 0000000..f1986f7 --- /dev/null +++ b/quic/core/qpack/qpack_static_table.cc
@@ -0,0 +1,140 @@ +// Copyright (c) 2018 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/qpack/qpack_static_table.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +// The "constructor" for a QpackStaticEntry that computes the lengths at +// compile time. +#define STATIC_ENTRY(name, value) \ + { name, QUIC_ARRAYSIZE(name) - 1, value, QUIC_ARRAYSIZE(value) - 1 } + +const std::vector<QpackStaticEntry>& QpackStaticTableVector() { + static const auto* kQpackStaticTable = new std::vector<QpackStaticEntry>{ + STATIC_ENTRY(":authority", ""), // 0 + STATIC_ENTRY(":path", "/"), // 1 + STATIC_ENTRY("age", "0"), // 2 + STATIC_ENTRY("content-disposition", ""), // 3 + STATIC_ENTRY("content-length", "0"), // 4 + STATIC_ENTRY("cookie", ""), // 5 + STATIC_ENTRY("date", ""), // 6 + STATIC_ENTRY("etag", ""), // 7 + STATIC_ENTRY("if-modified-since", ""), // 8 + STATIC_ENTRY("if-none-match", ""), // 9 + STATIC_ENTRY("last-modified", ""), // 10 + STATIC_ENTRY("link", ""), // 11 + STATIC_ENTRY("location", ""), // 12 + STATIC_ENTRY("referer", ""), // 13 + STATIC_ENTRY("set-cookie", ""), // 14 + STATIC_ENTRY(":method", "CONNECT"), // 15 + STATIC_ENTRY(":method", "DELETE"), // 16 + STATIC_ENTRY(":method", "GET"), // 17 + STATIC_ENTRY(":method", "HEAD"), // 18 + STATIC_ENTRY(":method", "OPTIONS"), // 19 + STATIC_ENTRY(":method", "POST"), // 20 + STATIC_ENTRY(":method", "PUT"), // 21 + STATIC_ENTRY(":scheme", "http"), // 22 + STATIC_ENTRY(":scheme", "https"), // 23 + STATIC_ENTRY(":status", "103"), // 24 + STATIC_ENTRY(":status", "200"), // 25 + STATIC_ENTRY(":status", "304"), // 26 + STATIC_ENTRY(":status", "404"), // 27 + STATIC_ENTRY(":status", "503"), // 28 + STATIC_ENTRY("accept", "*/*"), // 29 + STATIC_ENTRY("accept", "application/dns-message"), // 30 + STATIC_ENTRY("accept-encoding", "gzip, deflate, br"), // 31 + STATIC_ENTRY("accept-ranges", "bytes"), // 32 + STATIC_ENTRY("access-control-allow-headers", "cache-control"), // 33 + STATIC_ENTRY("access-control-allow-headers", "content-type"), // 35 + STATIC_ENTRY("access-control-allow-origin", "*"), // 35 + STATIC_ENTRY("cache-control", "max-age=0"), // 36 + STATIC_ENTRY("cache-control", "max-age=2592000"), // 37 + STATIC_ENTRY("cache-control", "max-age=604800"), // 38 + STATIC_ENTRY("cache-control", "no-cache"), // 39 + STATIC_ENTRY("cache-control", "no-store"), // 40 + STATIC_ENTRY("cache-control", "public, max-age=31536000"), // 41 + STATIC_ENTRY("content-encoding", "br"), // 42 + STATIC_ENTRY("content-encoding", "gzip"), // 43 + STATIC_ENTRY("content-type", "application/dns-message"), // 44 + STATIC_ENTRY("content-type", "application/javascript"), // 45 + STATIC_ENTRY("content-type", "application/json"), // 46 + STATIC_ENTRY("content-type", "application/x-www-form-urlencoded"), // 47 + STATIC_ENTRY("content-type", "image/gif"), // 48 + STATIC_ENTRY("content-type", "image/jpeg"), // 49 + STATIC_ENTRY("content-type", "image/png"), // 50 + STATIC_ENTRY("content-type", "text/css"), // 51 + STATIC_ENTRY("content-type", "text/html; charset=utf-8"), // 52 + STATIC_ENTRY("content-type", "text/plain"), // 53 + STATIC_ENTRY("content-type", "text/plain;charset=utf-8"), // 54 + STATIC_ENTRY("range", "bytes=0-"), // 55 + STATIC_ENTRY("strict-transport-security", "max-age=31536000"), // 56 + STATIC_ENTRY("strict-transport-security", + "max-age=31536000; includesubdomains"), // 57 + STATIC_ENTRY("strict-transport-security", + "max-age=31536000; includesubdomains; preload"), // 58 + STATIC_ENTRY("vary", "accept-encoding"), // 59 + STATIC_ENTRY("vary", "origin"), // 60 + STATIC_ENTRY("x-content-type-options", "nosniff"), // 61 + STATIC_ENTRY("x-xss-protection", "1; mode=block"), // 62 + STATIC_ENTRY(":status", "100"), // 63 + STATIC_ENTRY(":status", "204"), // 64 + STATIC_ENTRY(":status", "206"), // 65 + STATIC_ENTRY(":status", "302"), // 66 + STATIC_ENTRY(":status", "400"), // 67 + STATIC_ENTRY(":status", "403"), // 68 + STATIC_ENTRY(":status", "421"), // 69 + STATIC_ENTRY(":status", "425"), // 70 + STATIC_ENTRY(":status", "500"), // 71 + STATIC_ENTRY("accept-language", ""), // 72 + STATIC_ENTRY("access-control-allow-credentials", "FALSE"), // 73 + STATIC_ENTRY("access-control-allow-credentials", "TRUE"), // 74 + STATIC_ENTRY("access-control-allow-headers", "*"), // 75 + STATIC_ENTRY("access-control-allow-methods", "get"), // 76 + STATIC_ENTRY("access-control-allow-methods", "get, post, options"), // 77 + STATIC_ENTRY("access-control-allow-methods", "options"), // 78 + STATIC_ENTRY("access-control-expose-headers", "content-length"), // 79 + STATIC_ENTRY("access-control-request-headers", "content-type"), // 80 + STATIC_ENTRY("access-control-request-method", "get"), // 81 + STATIC_ENTRY("access-control-request-method", "post"), // 82 + STATIC_ENTRY("alt-svc", "clear"), // 83 + STATIC_ENTRY("authorization", ""), // 84 + STATIC_ENTRY( + "content-security-policy", + "script-src 'none'; object-src 'none'; base-uri 'none'"), // 85 + STATIC_ENTRY("early-data", "1"), // 86 + STATIC_ENTRY("expect-ct", ""), // 87 + STATIC_ENTRY("forwarded", ""), // 88 + STATIC_ENTRY("if-range", ""), // 89 + STATIC_ENTRY("origin", ""), // 90 + STATIC_ENTRY("purpose", "prefetch"), // 91 + STATIC_ENTRY("server", ""), // 92 + STATIC_ENTRY("timing-allow-origin", "*"), // 93 + STATIC_ENTRY("upgrade-insecure-requests", "1"), // 94 + STATIC_ENTRY("user-agent", ""), // 95 + STATIC_ENTRY("x-forwarded-for", ""), // 96 + STATIC_ENTRY("x-frame-options", "deny"), // 97 + STATIC_ENTRY("x-frame-options", "sameorigin"), // 98 + }; + return *kQpackStaticTable; +} + +#undef STATIC_ENTRY + +const QpackStaticTable& ObtainQpackStaticTable() { + static const QpackStaticTable* const shared_static_table = []() { + auto* table = new QpackStaticTable(); + table->Initialize(QpackStaticTableVector().data(), + QpackStaticTableVector().size()); + CHECK(table->IsInitialized()); + return table; + }(); + return *shared_static_table; +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_static_table.h b/quic/core/qpack/qpack_static_table.h new file mode 100644 index 0000000..d8c2555 --- /dev/null +++ b/quic/core/qpack/qpack_static_table.h
@@ -0,0 +1,31 @@ +// Copyright (c) 2018 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_QPACK_QPACK_STATIC_TABLE_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_ + +#include <vector> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h" +#include "net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h" + +namespace quic { + +using QpackStaticEntry = spdy::HpackStaticEntry; +using QpackStaticTable = spdy::HpackStaticTable; + +// QPACK static table defined at +// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#static-table. +QUIC_EXPORT_PRIVATE const std::vector<QpackStaticEntry>& +QpackStaticTableVector(); + +// Returns a QpackStaticTable instance initialized with kQpackStaticTable. +// The instance is read-only, has static lifetime, and is safe to share amoung +// threads. This function is thread-safe. +QUIC_EXPORT_PRIVATE const QpackStaticTable& ObtainQpackStaticTable(); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_
diff --git a/quic/core/qpack/qpack_static_table_test.cc b/quic/core/qpack/qpack_static_table_test.cc new file mode 100644 index 0000000..50289f2 --- /dev/null +++ b/quic/core/qpack/qpack_static_table_test.cc
@@ -0,0 +1,53 @@ +// Copyright (c) 2018 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/qpack/qpack_static_table.h" + +#include <set> + +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +namespace test { + +namespace { + +// Check that an initialized instance has the right number of entries. +TEST(QpackStaticTableTest, Initialize) { + QpackStaticTable table; + EXPECT_FALSE(table.IsInitialized()); + + table.Initialize(QpackStaticTableVector().data(), + QpackStaticTableVector().size()); + EXPECT_TRUE(table.IsInitialized()); + + auto static_entries = table.GetStaticEntries(); + EXPECT_EQ(QpackStaticTableVector().size(), static_entries.size()); + + auto static_index = table.GetStaticIndex(); + EXPECT_EQ(QpackStaticTableVector().size(), static_index.size()); + + auto static_name_index = table.GetStaticNameIndex(); + std::set<QuicStringPiece> names; + for (auto entry : static_index) { + names.insert(entry->name()); + } + EXPECT_EQ(names.size(), static_name_index.size()); +} + +// Test that ObtainQpackStaticTable returns the same instance every time. +TEST(QpackStaticTableTest, IsSingleton) { + const QpackStaticTable* static_table_one = &ObtainQpackStaticTable(); + const QpackStaticTable* static_table_two = &ObtainQpackStaticTable(); + EXPECT_EQ(static_table_one, static_table_two); +} + +} // namespace + +} // namespace test + +} // namespace quic
diff --git a/quic/core/qpack/qpack_test_utils.cc b/quic/core/qpack/qpack_test_utils.cc new file mode 100644 index 0000000..2d4a72e --- /dev/null +++ b/quic/core/qpack/qpack_test_utils.cc
@@ -0,0 +1,23 @@ +// Copyright (c) 2018 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/qpack/qpack_test_utils.h" + +#include <limits> + +namespace quic { +namespace test { + +FragmentSizeGenerator FragmentModeToFragmentSizeGenerator( + FragmentMode fragment_mode) { + switch (fragment_mode) { + case FragmentMode::kSingleChunk: + return []() { return std::numeric_limits<size_t>::max(); }; + case FragmentMode::kOctetByOctet: + return []() { return 1; }; + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_test_utils.h b/quic/core/qpack/qpack_test_utils.h new file mode 100644 index 0000000..65bb5a2 --- /dev/null +++ b/quic/core/qpack/qpack_test_utils.h
@@ -0,0 +1,29 @@ +// Copyright (c) 2018 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_QPACK_QPACK_TEST_UTILS_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_ + +#include <cstddef> +#include <functional> + +namespace quic { +namespace test { + +// Called repeatedly to determine the size of each fragment when encoding or +// decoding. Must return a positive value. +using FragmentSizeGenerator = std::function<size_t()>; + +enum class FragmentMode { + kSingleChunk, + kOctetByOctet, +}; + +FragmentSizeGenerator FragmentModeToFragmentSizeGenerator( + FragmentMode fragment_mode); + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_
diff --git a/quic/core/quic_ack_listener_interface.cc b/quic/core/quic_ack_listener_interface.cc new file mode 100644 index 0000000..fc25a31 --- /dev/null +++ b/quic/core/quic_ack_listener_interface.cc
@@ -0,0 +1,11 @@ +// 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/quic_ack_listener_interface.h" + +namespace quic { + +QuicAckListenerInterface::~QuicAckListenerInterface() {} + +} // namespace quic
diff --git a/quic/core/quic_ack_listener_interface.h b/quic/core/quic_ack_listener_interface.h new file mode 100644 index 0000000..0a9c694 --- /dev/null +++ b/quic/core/quic_ack_listener_interface.h
@@ -0,0 +1,37 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_QUIC_ACK_LISTENER_INTERFACE_H_ +#define QUICHE_QUIC_CORE_QUIC_ACK_LISTENER_INTERFACE_H_ + +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" + +namespace quic { + +// Pure virtual class to listen for packet acknowledgements. +class QUIC_EXPORT_PRIVATE QuicAckListenerInterface + : public QuicReferenceCounted { + public: + QuicAckListenerInterface() {} + + // Called when a packet is acked. Called once per packet. + // |acked_bytes| is the number of data bytes acked. + virtual void OnPacketAcked(int acked_bytes, + QuicTime::Delta ack_delay_time) = 0; + + // Called when a packet is retransmitted. Called once per packet. + // |retransmitted_bytes| is the number of data bytes retransmitted. + virtual void OnPacketRetransmitted(int retransmitted_bytes) = 0; + + protected: + // Delegates are ref counted. + ~QuicAckListenerInterface() override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_ACK_LISTENER_INTERFACE_H_
diff --git a/quic/core/quic_alarm.cc b/quic/core/quic_alarm.cc new file mode 100644 index 0000000..e31c7aa --- /dev/null +++ b/quic/core/quic_alarm.cc
@@ -0,0 +1,73 @@ +// Copyright 2013 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/quic_alarm.h" + +namespace quic { + +QuicAlarm::QuicAlarm(QuicArenaScopedPtr<Delegate> delegate) + : delegate_(std::move(delegate)), deadline_(QuicTime::Zero()) {} + +QuicAlarm::~QuicAlarm() {} + +void QuicAlarm::Set(QuicTime new_deadline) { + DCHECK(!IsSet()); + DCHECK(new_deadline.IsInitialized()); + deadline_ = new_deadline; + SetImpl(); +} + +void QuicAlarm::Cancel() { + if (!IsSet()) { + // Don't try to cancel an alarm that hasn't been set. + return; + } + deadline_ = QuicTime::Zero(); + CancelImpl(); +} + +void QuicAlarm::Update(QuicTime new_deadline, QuicTime::Delta granularity) { + if (!new_deadline.IsInitialized()) { + Cancel(); + return; + } + if (std::abs((new_deadline - deadline_).ToMicroseconds()) < + granularity.ToMicroseconds()) { + return; + } + const bool was_set = IsSet(); + deadline_ = new_deadline; + if (was_set) { + UpdateImpl(); + } else { + SetImpl(); + } +} + +bool QuicAlarm::IsSet() const { + return deadline_.IsInitialized(); +} + +void QuicAlarm::Fire() { + if (!IsSet()) { + return; + } + + deadline_ = QuicTime::Zero(); + delegate_->OnAlarm(); +} + +void QuicAlarm::UpdateImpl() { + // CancelImpl and SetImpl take the new deadline by way of the deadline_ + // member, so save and restore deadline_ before canceling. + const QuicTime new_deadline = deadline_; + + deadline_ = QuicTime::Zero(); + CancelImpl(); + + deadline_ = new_deadline; + SetImpl(); +} + +} // namespace quic
diff --git a/quic/core/quic_alarm.h b/quic/core/quic_alarm.h new file mode 100644 index 0000000..d731e28 --- /dev/null +++ b/quic/core/quic_alarm.h
@@ -0,0 +1,87 @@ +// Copyright 2013 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_QUIC_ALARM_H_ +#define QUICHE_QUIC_CORE_QUIC_ALARM_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Abstract class which represents an alarm which will go off at a +// scheduled time, and execute the |OnAlarm| method of the delegate. +// An alarm may be cancelled, in which case it may or may not be +// removed from the underlying scheduling system, but in either case +// the task will not be executed. +class QUIC_EXPORT_PRIVATE QuicAlarm { + public: + class QUIC_EXPORT_PRIVATE Delegate { + public: + virtual ~Delegate() {} + + // Invoked when the alarm fires. + virtual void OnAlarm() = 0; + }; + + explicit QuicAlarm(QuicArenaScopedPtr<Delegate> delegate); + QuicAlarm(const QuicAlarm&) = delete; + QuicAlarm& operator=(const QuicAlarm&) = delete; + virtual ~QuicAlarm(); + + // Sets the alarm to fire at |deadline|. Must not be called while + // the alarm is set. To reschedule an alarm, call Cancel() first, + // then Set(). + void Set(QuicTime new_deadline); + + // Cancels the alarm. May be called repeatedly. Does not + // guarantee that the underlying scheduling system will remove + // the alarm's associated task, but guarantees that the + // delegates OnAlarm method will not be called. + void Cancel(); + + // Cancels and sets the alarm if the |deadline| is farther from the current + // deadline than |granularity|, and otherwise does nothing. If |deadline| is + // not initialized, the alarm is cancelled. + void Update(QuicTime new_deadline, QuicTime::Delta granularity); + + // Returns true if |deadline_| has been set to a non-zero time. + bool IsSet() const; + + QuicTime deadline() const { return deadline_; } + + protected: + // Subclasses implement this method to perform the platform-specific + // scheduling of the alarm. Is called from Set() or Fire(), after the + // deadline has been updated. + virtual void SetImpl() = 0; + + // Subclasses implement this method to perform the platform-specific + // cancelation of the alarm. + virtual void CancelImpl() = 0; + + // Subclasses implement this method to perform the platform-specific update of + // the alarm if there exists a more optimal implementation than calling + // CancelImpl() and SetImpl(). + virtual void UpdateImpl(); + + // Called by subclasses when the alarm fires. Invokes the + // delegates |OnAlarm| if a delegate is set, and if the deadline + // has been exceeded. Implementations which do not remove the + // alarm from the underlying scheduler on Cancel() may need to handle + // the situation where the task executes before the deadline has been + // reached, in which case they need to reschedule the task and must not + // call invoke this method. + void Fire(); + + private: + QuicArenaScopedPtr<Delegate> delegate_; + QuicTime deadline_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_ALARM_H_
diff --git a/quic/core/quic_alarm_factory.h b/quic/core/quic_alarm_factory.h new file mode 100644 index 0000000..0e0ce33 --- /dev/null +++ b/quic/core/quic_alarm_factory.h
@@ -0,0 +1,36 @@ +// Copyright (c) 2015 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_QUIC_ALARM_FACTORY_H_ +#define QUICHE_QUIC_CORE_QUIC_ALARM_FACTORY_H_ + +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Creates platform-specific alarms used throughout QUIC. +class QUIC_EXPORT_PRIVATE QuicAlarmFactory { + public: + virtual ~QuicAlarmFactory() {} + + // Creates a new platform-specific alarm which will be configured to notify + // |delegate| when the alarm fires. Returns an alarm allocated on the heap. + // Caller takes ownership of the new alarm, which will not yet be "set" to + // fire. + virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) = 0; + + // Creates a new platform-specific alarm which will be configured to notify + // |delegate| when the alarm fires. Caller takes ownership of the new alarm, + // which will not yet be "set" to fire. If |arena| is null, then the alarm + // will be created on the heap. Otherwise, it will be created in |arena|. + virtual QuicArenaScopedPtr<QuicAlarm> CreateAlarm( + QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, + QuicConnectionArena* arena) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_ALARM_FACTORY_H_
diff --git a/quic/core/quic_alarm_test.cc b/quic/core/quic_alarm_test.cc new file mode 100644 index 0000000..b2f4690 --- /dev/null +++ b/quic/core/quic_alarm_test.cc
@@ -0,0 +1,166 @@ +// Copyright 2013 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/quic_alarm.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +using testing::Invoke; + +namespace quic { +namespace test { +namespace { + +class MockDelegate : public QuicAlarm::Delegate { + public: + MOCK_METHOD0(OnAlarm, void()); +}; + +class DestructiveDelegate : public QuicAlarm::Delegate { + public: + DestructiveDelegate() : alarm_(nullptr) {} + + void set_alarm(QuicAlarm* alarm) { alarm_ = alarm; } + + void OnAlarm() override { + DCHECK(alarm_); + delete alarm_; + } + + private: + QuicAlarm* alarm_; +}; + +class TestAlarm : public QuicAlarm { + public: + explicit TestAlarm(QuicAlarm::Delegate* delegate) + : QuicAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)) {} + + bool scheduled() const { return scheduled_; } + + void FireAlarm() { + scheduled_ = false; + Fire(); + } + + protected: + void SetImpl() override { + DCHECK(deadline().IsInitialized()); + scheduled_ = true; + } + + void CancelImpl() override { + DCHECK(!deadline().IsInitialized()); + scheduled_ = false; + } + + private: + bool scheduled_; +}; + +class DestructiveAlarm : public QuicAlarm { + public: + explicit DestructiveAlarm(DestructiveDelegate* delegate) + : QuicAlarm(QuicArenaScopedPtr<DestructiveDelegate>(delegate)) {} + + void FireAlarm() { Fire(); } + + protected: + void SetImpl() override {} + + void CancelImpl() override {} +}; + +class QuicAlarmTest : public QuicTest { + public: + QuicAlarmTest() + : delegate_(new MockDelegate()), + alarm_(delegate_), + deadline_(QuicTime::Zero() + QuicTime::Delta::FromSeconds(7)), + deadline2_(QuicTime::Zero() + QuicTime::Delta::FromSeconds(14)), + new_deadline_(QuicTime::Zero()) {} + + void ResetAlarm() { alarm_.Set(new_deadline_); } + + MockDelegate* delegate_; // not owned + TestAlarm alarm_; + QuicTime deadline_; + QuicTime deadline2_; + QuicTime new_deadline_; +}; + +TEST_F(QuicAlarmTest, IsSet) { + EXPECT_FALSE(alarm_.IsSet()); +} + +TEST_F(QuicAlarmTest, Set) { + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + EXPECT_TRUE(alarm_.IsSet()); + EXPECT_TRUE(alarm_.scheduled()); + EXPECT_EQ(deadline, alarm_.deadline()); +} + +TEST_F(QuicAlarmTest, Cancel) { + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + alarm_.Cancel(); + EXPECT_FALSE(alarm_.IsSet()); + EXPECT_FALSE(alarm_.scheduled()); + EXPECT_EQ(QuicTime::Zero(), alarm_.deadline()); +} + +TEST_F(QuicAlarmTest, Update) { + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + QuicTime new_deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(8); + alarm_.Update(new_deadline, QuicTime::Delta::Zero()); + EXPECT_TRUE(alarm_.IsSet()); + EXPECT_TRUE(alarm_.scheduled()); + EXPECT_EQ(new_deadline, alarm_.deadline()); +} + +TEST_F(QuicAlarmTest, UpdateWithZero) { + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + alarm_.Update(QuicTime::Zero(), QuicTime::Delta::Zero()); + EXPECT_FALSE(alarm_.IsSet()); + EXPECT_FALSE(alarm_.scheduled()); + EXPECT_EQ(QuicTime::Zero(), alarm_.deadline()); +} + +TEST_F(QuicAlarmTest, Fire) { + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + EXPECT_CALL(*delegate_, OnAlarm()); + alarm_.FireAlarm(); + EXPECT_FALSE(alarm_.IsSet()); + EXPECT_FALSE(alarm_.scheduled()); + EXPECT_EQ(QuicTime::Zero(), alarm_.deadline()); +} + +TEST_F(QuicAlarmTest, FireAndResetViaSet) { + alarm_.Set(deadline_); + new_deadline_ = deadline2_; + EXPECT_CALL(*delegate_, OnAlarm()) + .WillOnce(Invoke(this, &QuicAlarmTest::ResetAlarm)); + alarm_.FireAlarm(); + EXPECT_TRUE(alarm_.IsSet()); + EXPECT_TRUE(alarm_.scheduled()); + EXPECT_EQ(deadline2_, alarm_.deadline()); +} + +TEST_F(QuicAlarmTest, FireDestroysAlarm) { + DestructiveDelegate* delegate(new DestructiveDelegate); + DestructiveAlarm* alarm = new DestructiveAlarm(delegate); + delegate->set_alarm(alarm); + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm->Set(deadline); + // This should not crash, even though it will destroy alarm. + alarm->FireAlarm(); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_arena_scoped_ptr.h b/quic/core/quic_arena_scoped_ptr.h new file mode 100644 index 0000000..b719185 --- /dev/null +++ b/quic/core/quic_arena_scoped_ptr.h
@@ -0,0 +1,209 @@ +// 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. + +// unique_ptr-style pointer that stores values that may be from an arena. Takes +// up the same storage as the platform's native pointer type. Takes ownership +// of the value it's constructed with; if holding a value in an arena, and the +// type has a non-trivial destructor, the arena must outlive the +// QuicArenaScopedPtr. Does not support array overloads. + +#ifndef QUICHE_QUIC_CORE_QUIC_ARENA_SCOPED_PTR_H_ +#define QUICHE_QUIC_CORE_QUIC_ARENA_SCOPED_PTR_H_ + +#include <cstdint> // for uintptr_t + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +template <typename T> +class QuicArenaScopedPtr { + static_assert(QUIC_ALIGN_OF(T*) > 1, + "QuicArenaScopedPtr can only store objects that are aligned to " + "greater than 1 byte."); + + public: + // Constructs an empty QuicArenaScopedPtr. + QuicArenaScopedPtr(); + + // Constructs a QuicArenaScopedPtr referencing the heap-allocated memory + // provided. + explicit QuicArenaScopedPtr(T* value); + + template <typename U> + QuicArenaScopedPtr(QuicArenaScopedPtr<U>&& other); // NOLINT + template <typename U> + QuicArenaScopedPtr& operator=(QuicArenaScopedPtr<U>&& other); + ~QuicArenaScopedPtr(); + + // Returns a pointer to the value. + T* get() const; + + // Returns a reference to the value. + T& operator*() const; + + // Returns a pointer to the value. + T* operator->() const; + + // Swaps the value of this pointer with |other|. + void swap(QuicArenaScopedPtr& other); + + // Resets the held value to |value|. + void reset(T* value = nullptr); + + // Returns true if |this| came from an arena. Primarily exposed for testing + // and assertions. + bool is_from_arena(); + + private: + // Friends with other derived types of QuicArenaScopedPtr, to support the + // derived-types case. + template <typename U> + friend class QuicArenaScopedPtr; + // Also befriend all known arenas, only to prevent misuse. + template <uint32_t ArenaSize> + friend class QuicOneBlockArena; + + // Tag to denote that a QuicArenaScopedPtr is being explicitly created by an + // arena. + enum class ConstructFrom { kHeap, kArena }; + + // Constructs a QuicArenaScopedPtr with the given representation. + QuicArenaScopedPtr(void* value, ConstructFrom from); + QuicArenaScopedPtr(const QuicArenaScopedPtr&) = delete; + QuicArenaScopedPtr& operator=(const QuicArenaScopedPtr&) = delete; + + // Low-order bits of value_ that determine if the pointer came from an arena. + static const uintptr_t kFromArenaMask = 0x1; + + // Every platform we care about has at least 4B aligned integers, so store the + // is_from_arena bit in the least significant bit. + void* value_; +}; + +template <typename T> +bool operator==(const QuicArenaScopedPtr<T>& left, + const QuicArenaScopedPtr<T>& right) { + return left.get() == right.get(); +} + +template <typename T> +bool operator!=(const QuicArenaScopedPtr<T>& left, + const QuicArenaScopedPtr<T>& right) { + return left.get() != right.get(); +} + +template <typename T> +bool operator==(std::nullptr_t, const QuicArenaScopedPtr<T>& right) { + return nullptr == right.get(); +} + +template <typename T> +bool operator!=(std::nullptr_t, const QuicArenaScopedPtr<T>& right) { + return nullptr != right.get(); +} + +template <typename T> +bool operator==(const QuicArenaScopedPtr<T>& left, std::nullptr_t) { + return left.get() == nullptr; +} + +template <typename T> +bool operator!=(const QuicArenaScopedPtr<T>& left, std::nullptr_t) { + return left.get() != nullptr; +} + +template <typename T> +QuicArenaScopedPtr<T>::QuicArenaScopedPtr() : value_(nullptr) {} + +template <typename T> +QuicArenaScopedPtr<T>::QuicArenaScopedPtr(T* value) + : QuicArenaScopedPtr(value, ConstructFrom::kHeap) {} + +template <typename T> +template <typename U> +QuicArenaScopedPtr<T>::QuicArenaScopedPtr(QuicArenaScopedPtr<U>&& other) + : value_(other.value_) { + static_assert( + std::is_base_of<T, U>::value || std::is_same<T, U>::value, + "Cannot construct QuicArenaScopedPtr; type is not derived or same."); + other.value_ = nullptr; +} + +template <typename T> +template <typename U> +QuicArenaScopedPtr<T>& QuicArenaScopedPtr<T>::operator=( + QuicArenaScopedPtr<U>&& other) { + static_assert( + std::is_base_of<T, U>::value || std::is_same<T, U>::value, + "Cannot assign QuicArenaScopedPtr; type is not derived or same."); + swap(other); + return *this; +} + +template <typename T> +QuicArenaScopedPtr<T>::~QuicArenaScopedPtr() { + reset(); +} + +template <typename T> +T* QuicArenaScopedPtr<T>::get() const { + return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(value_) & + ~kFromArenaMask); +} + +template <typename T> +T& QuicArenaScopedPtr<T>::operator*() const { + return *get(); +} + +template <typename T> +T* QuicArenaScopedPtr<T>::operator->() const { + return get(); +} + +template <typename T> +void QuicArenaScopedPtr<T>::swap(QuicArenaScopedPtr& other) { + using std::swap; + swap(value_, other.value_); +} + +template <typename T> +bool QuicArenaScopedPtr<T>::is_from_arena() { + return (reinterpret_cast<uintptr_t>(value_) & kFromArenaMask) != 0; +} + +template <typename T> +void QuicArenaScopedPtr<T>::reset(T* value) { + if (value_ != nullptr) { + if (is_from_arena()) { + // Manually invoke the destructor. + get()->~T(); + } else { + delete get(); + } + } + DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(value) & kFromArenaMask); + value_ = value; +} + +template <typename T> +QuicArenaScopedPtr<T>::QuicArenaScopedPtr(void* value, ConstructFrom from_arena) + : value_(value) { + DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(value_) & kFromArenaMask); + switch (from_arena) { + case ConstructFrom::kHeap: + break; + case ConstructFrom::kArena: + value_ = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(value_) | + QuicArenaScopedPtr<T>::kFromArenaMask); + break; + } +} + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_ARENA_SCOPED_PTR_H_
diff --git a/quic/core/quic_arena_scoped_ptr_test.cc b/quic/core/quic_arena_scoped_ptr_test.cc new file mode 100644 index 0000000..109fd37 --- /dev/null +++ b/quic/core/quic_arena_scoped_ptr_test.cc
@@ -0,0 +1,102 @@ +// 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/quic_arena_scoped_ptr.h" + +#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace { + +enum class TestParam { kFromHeap, kFromArena }; + +struct TestObject { + explicit TestObject(uintptr_t value) : value(value) { buffer.resize(1024); } + uintptr_t value; + + // Ensure that we have a non-trivial destructor that will leak memory if it's + // not called. + std::vector<char> buffer; +}; + +class QuicArenaScopedPtrParamTest : public QuicTestWithParam<TestParam> { + protected: + QuicArenaScopedPtr<TestObject> CreateObject(uintptr_t value) { + QuicArenaScopedPtr<TestObject> ptr; + switch (GetParam()) { + case TestParam::kFromHeap: + ptr = QuicArenaScopedPtr<TestObject>(new TestObject(value)); + CHECK(!ptr.is_from_arena()); + break; + case TestParam::kFromArena: + ptr = arena_.New<TestObject>(value); + CHECK(ptr.is_from_arena()); + break; + } + return ptr; + } + + private: + QuicOneBlockArena<1024> arena_; +}; + +INSTANTIATE_TEST_SUITE_P(QuicArenaScopedPtrParamTest, + QuicArenaScopedPtrParamTest, + testing::Values(TestParam::kFromHeap, + TestParam::kFromArena)); + +TEST_P(QuicArenaScopedPtrParamTest, NullObjects) { + QuicArenaScopedPtr<TestObject> def; + QuicArenaScopedPtr<TestObject> null(nullptr); + EXPECT_EQ(def, null); + EXPECT_EQ(def, nullptr); + EXPECT_EQ(null, nullptr); +} + +TEST_P(QuicArenaScopedPtrParamTest, FromArena) { + QuicOneBlockArena<1024> arena_; + EXPECT_TRUE(arena_.New<TestObject>(0).is_from_arena()); + EXPECT_FALSE( + QuicArenaScopedPtr<TestObject>(new TestObject(0)).is_from_arena()); +} + +TEST_P(QuicArenaScopedPtrParamTest, Assign) { + QuicArenaScopedPtr<TestObject> ptr = CreateObject(12345); + ptr = CreateObject(54321); + EXPECT_EQ(54321u, ptr->value); +} + +TEST_P(QuicArenaScopedPtrParamTest, MoveConstruct) { + QuicArenaScopedPtr<TestObject> ptr1 = CreateObject(12345); + QuicArenaScopedPtr<TestObject> ptr2(std::move(ptr1)); + EXPECT_EQ(nullptr, ptr1); + EXPECT_EQ(12345u, ptr2->value); +} + +TEST_P(QuicArenaScopedPtrParamTest, Accessors) { + QuicArenaScopedPtr<TestObject> ptr = CreateObject(12345); + EXPECT_EQ(12345u, (*ptr).value); + EXPECT_EQ(12345u, ptr->value); + // We explicitly want to test that get() returns a valid pointer to the data, + // but the call looks redundant. + EXPECT_EQ(12345u, ptr.get()->value); // NOLINT +} + +TEST_P(QuicArenaScopedPtrParamTest, Reset) { + QuicArenaScopedPtr<TestObject> ptr = CreateObject(12345); + ptr.reset(new TestObject(54321)); + EXPECT_EQ(54321u, ptr->value); +} + +TEST_P(QuicArenaScopedPtrParamTest, Swap) { + QuicArenaScopedPtr<TestObject> ptr1 = CreateObject(12345); + QuicArenaScopedPtr<TestObject> ptr2 = CreateObject(54321); + ptr1.swap(ptr2); + EXPECT_EQ(12345u, ptr2->value); + EXPECT_EQ(54321u, ptr1->value); +} + +} // namespace +} // namespace quic
diff --git a/quic/core/quic_bandwidth.cc b/quic/core/quic_bandwidth.cc new file mode 100644 index 0000000..cdcf003 --- /dev/null +++ b/quic/core/quic_bandwidth.cc
@@ -0,0 +1,40 @@ +// Copyright (c) 2012 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/quic_bandwidth.h" + +#include <cinttypes> + +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicString QuicBandwidth::ToDebugValue() const { + if (bits_per_second_ < 80000) { + return QuicStringPrintf("%" PRId64 " bits/s (%" PRId64 " bytes/s)", + bits_per_second_, bits_per_second_ / 8); + } + + double divisor; + char unit; + if (bits_per_second_ < 8 * 1000 * 1000) { + divisor = 1e3; + unit = 'k'; + } else if (bits_per_second_ < INT64_C(8) * 1000 * 1000 * 1000) { + divisor = 1e6; + unit = 'M'; + } else { + divisor = 1e9; + unit = 'G'; + } + + double bits_per_second_with_unit = bits_per_second_ / divisor; + double bytes_per_second_with_unit = bits_per_second_with_unit / 8; + return QuicStringPrintf("%.2f %cbits/s (%.2f %cbytes/s)", + bits_per_second_with_unit, unit, + bytes_per_second_with_unit, unit); +} + +} // namespace quic
diff --git a/quic/core/quic_bandwidth.h b/quic/core/quic_bandwidth.h new file mode 100644 index 0000000..be3c158 --- /dev/null +++ b/quic/core/quic_bandwidth.h
@@ -0,0 +1,151 @@ +// Copyright (c) 2012 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. + +// QuicBandwidth represents a bandwidth, stored in bits per second resolution. + +#ifndef QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_ +#define QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_ + +#include <cmath> +#include <cstdint> +#include <limits> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicBandwidth { + public: + // Creates a new QuicBandwidth with an internal value of 0. + static constexpr QuicBandwidth Zero() { return QuicBandwidth(0); } + + // Creates a new QuicBandwidth with an internal value of INT64_MAX. + static constexpr QuicBandwidth Infinite() { + return QuicBandwidth(std::numeric_limits<int64_t>::max()); + } + + // Create a new QuicBandwidth holding the bits per second. + static constexpr QuicBandwidth FromBitsPerSecond(int64_t bits_per_second) { + return QuicBandwidth(bits_per_second); + } + + // Create a new QuicBandwidth holding the kilo bits per second. + static constexpr QuicBandwidth FromKBitsPerSecond(int64_t k_bits_per_second) { + return QuicBandwidth(k_bits_per_second * 1000); + } + + // Create a new QuicBandwidth holding the bytes per second. + static constexpr QuicBandwidth FromBytesPerSecond(int64_t bytes_per_second) { + return QuicBandwidth(bytes_per_second * 8); + } + + // Create a new QuicBandwidth holding the kilo bytes per second. + static constexpr QuicBandwidth FromKBytesPerSecond( + int64_t k_bytes_per_second) { + return QuicBandwidth(k_bytes_per_second * 8000); + } + + // Create a new QuicBandwidth based on the bytes per the elapsed delta. + static inline QuicBandwidth FromBytesAndTimeDelta(QuicByteCount bytes, + QuicTime::Delta delta) { + return QuicBandwidth((bytes * kNumMicrosPerSecond) / + delta.ToMicroseconds() * 8); + } + + inline int64_t ToBitsPerSecond() const { return bits_per_second_; } + + inline int64_t ToKBitsPerSecond() const { return bits_per_second_ / 1000; } + + inline int64_t ToBytesPerSecond() const { return bits_per_second_ / 8; } + + inline int64_t ToKBytesPerSecond() const { return bits_per_second_ / 8000; } + + inline QuicByteCount ToBytesPerPeriod(QuicTime::Delta time_period) const { + return ToBytesPerSecond() * time_period.ToMicroseconds() / + kNumMicrosPerSecond; + } + + inline int64_t ToKBytesPerPeriod(QuicTime::Delta time_period) const { + return ToKBytesPerSecond() * time_period.ToMicroseconds() / + kNumMicrosPerSecond; + } + + inline bool IsZero() const { return bits_per_second_ == 0; } + + inline QuicTime::Delta TransferTime(QuicByteCount bytes) const { + if (bits_per_second_ == 0) { + return QuicTime::Delta::Zero(); + } + return QuicTime::Delta::FromMicroseconds(bytes * 8 * kNumMicrosPerSecond / + bits_per_second_); + } + + QuicString ToDebugValue() const; + + private: + explicit constexpr QuicBandwidth(int64_t bits_per_second) + : bits_per_second_(bits_per_second >= 0 ? bits_per_second : 0) {} + + int64_t bits_per_second_; + + friend QuicBandwidth operator+(QuicBandwidth lhs, QuicBandwidth rhs); + friend QuicBandwidth operator-(QuicBandwidth lhs, QuicBandwidth rhs); + friend QuicBandwidth operator*(QuicBandwidth lhs, float factor); +}; + +// Non-member relational operators for QuicBandwidth. +inline bool operator==(QuicBandwidth lhs, QuicBandwidth rhs) { + return lhs.ToBitsPerSecond() == rhs.ToBitsPerSecond(); +} +inline bool operator!=(QuicBandwidth lhs, QuicBandwidth rhs) { + return !(lhs == rhs); +} +inline bool operator<(QuicBandwidth lhs, QuicBandwidth rhs) { + return lhs.ToBitsPerSecond() < rhs.ToBitsPerSecond(); +} +inline bool operator>(QuicBandwidth lhs, QuicBandwidth rhs) { + return rhs < lhs; +} +inline bool operator<=(QuicBandwidth lhs, QuicBandwidth rhs) { + return !(rhs < lhs); +} +inline bool operator>=(QuicBandwidth lhs, QuicBandwidth rhs) { + return !(lhs < rhs); +} + +// Non-member arithmetic operators for QuicBandwidth. +inline QuicBandwidth operator+(QuicBandwidth lhs, QuicBandwidth rhs) { + return QuicBandwidth(lhs.bits_per_second_ + rhs.bits_per_second_); +} +inline QuicBandwidth operator-(QuicBandwidth lhs, QuicBandwidth rhs) { + return QuicBandwidth(lhs.bits_per_second_ - rhs.bits_per_second_); +} +inline QuicBandwidth operator*(QuicBandwidth lhs, float rhs) { + return QuicBandwidth( + static_cast<int64_t>(std::llround(lhs.bits_per_second_ * rhs))); +} +inline QuicBandwidth operator*(float lhs, QuicBandwidth rhs) { + return rhs * lhs; +} +inline QuicByteCount operator*(QuicBandwidth lhs, QuicTime::Delta rhs) { + return lhs.ToBytesPerPeriod(rhs); +} +inline QuicByteCount operator*(QuicTime::Delta lhs, QuicBandwidth rhs) { + return rhs * lhs; +} + +// Override stream output operator for gtest. +inline std::ostream& operator<<(std::ostream& output, + const QuicBandwidth bandwidth) { + output << bandwidth.ToDebugValue(); + return output; +} + +} // namespace quic +#endif // QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_
diff --git a/quic/core/quic_bandwidth_test.cc b/quic/core/quic_bandwidth_test.cc new file mode 100644 index 0000000..ed23dac --- /dev/null +++ b/quic/core/quic_bandwidth_test.cc
@@ -0,0 +1,122 @@ +// Copyright (c) 2012 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/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class QuicBandwidthTest : public QuicTest {}; + +TEST_F(QuicBandwidthTest, FromTo) { + EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(1), + QuicBandwidth::FromBitsPerSecond(1000)); + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1), + QuicBandwidth::FromBytesPerSecond(1000)); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(8000), + QuicBandwidth::FromBytesPerSecond(1000)); + EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(8), + QuicBandwidth::FromKBytesPerSecond(1)); + + EXPECT_EQ(0, QuicBandwidth::Zero().ToBitsPerSecond()); + EXPECT_EQ(0, QuicBandwidth::Zero().ToKBitsPerSecond()); + EXPECT_EQ(0, QuicBandwidth::Zero().ToBytesPerSecond()); + EXPECT_EQ(0, QuicBandwidth::Zero().ToKBytesPerSecond()); + + EXPECT_EQ(1, QuicBandwidth::FromBitsPerSecond(1000).ToKBitsPerSecond()); + EXPECT_EQ(1000, QuicBandwidth::FromKBitsPerSecond(1).ToBitsPerSecond()); + EXPECT_EQ(1, QuicBandwidth::FromBytesPerSecond(1000).ToKBytesPerSecond()); + EXPECT_EQ(1000, QuicBandwidth::FromKBytesPerSecond(1).ToBytesPerSecond()); +} + +TEST_F(QuicBandwidthTest, Add) { + QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1); + QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1); + + EXPECT_EQ(9000, (bandwidht_1 + bandwidht_2).ToBitsPerSecond()); + EXPECT_EQ(9000, (bandwidht_2 + bandwidht_1).ToBitsPerSecond()); +} + +TEST_F(QuicBandwidthTest, Subtract) { + QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1); + QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1); + + EXPECT_EQ(7000, (bandwidht_2 - bandwidht_1).ToBitsPerSecond()); +} + +TEST_F(QuicBandwidthTest, TimeDelta) { + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1000), + QuicBandwidth::FromBytesAndTimeDelta( + 1000, QuicTime::Delta::FromMilliseconds(1))); + + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(10), + QuicBandwidth::FromBytesAndTimeDelta( + 1000, QuicTime::Delta::FromMilliseconds(100))); +} + +TEST_F(QuicBandwidthTest, Scale) { + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(500), + QuicBandwidth::FromKBytesPerSecond(1000) * 0.5f); + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(750), + 0.75f * QuicBandwidth::FromKBytesPerSecond(1000)); + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1250), + QuicBandwidth::FromKBytesPerSecond(1000) * 1.25f); + + // Ensure we are rounding correctly within a 1bps level of precision. + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(5), + QuicBandwidth::FromBitsPerSecond(9) * 0.5f); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(2), + QuicBandwidth::FromBitsPerSecond(12) * 0.2f); +} + +TEST_F(QuicBandwidthTest, BytesPerPeriod) { + EXPECT_EQ(2000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(1))); + EXPECT_EQ(2u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(1))); + EXPECT_EQ(200000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(100))); + EXPECT_EQ(200u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod( + QuicTime::Delta::FromMilliseconds(100))); +} + +TEST_F(QuicBandwidthTest, TransferTime) { + EXPECT_EQ(QuicTime::Delta::FromSeconds(1), + QuicBandwidth::FromKBytesPerSecond(1).TransferTime(1000)); + EXPECT_EQ(QuicTime::Delta::Zero(), QuicBandwidth::Zero().TransferTime(1000)); +} + +TEST_F(QuicBandwidthTest, RelOps) { + const QuicBandwidth b1 = QuicBandwidth::FromKBitsPerSecond(1); + const QuicBandwidth b2 = QuicBandwidth::FromKBytesPerSecond(2); + EXPECT_EQ(b1, b1); + EXPECT_NE(b1, b2); + EXPECT_LT(b1, b2); + EXPECT_GT(b2, b1); + EXPECT_LE(b1, b1); + EXPECT_LE(b1, b2); + EXPECT_GE(b1, b1); + EXPECT_GE(b2, b1); +} + +TEST_F(QuicBandwidthTest, DebugValue) { + EXPECT_EQ("128 bits/s (16 bytes/s)", + QuicBandwidth::FromBytesPerSecond(16).ToDebugValue()); + EXPECT_EQ("4096 bits/s (512 bytes/s)", + QuicBandwidth::FromBytesPerSecond(512).ToDebugValue()); + + QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(1000 * 50); + EXPECT_EQ("400.00 kbits/s (50.00 kbytes/s)", bandwidth.ToDebugValue()); + + bandwidth = bandwidth * 1000; + EXPECT_EQ("400.00 Mbits/s (50.00 Mbytes/s)", bandwidth.ToDebugValue()); + + bandwidth = bandwidth * 1000; + EXPECT_EQ("400.00 Gbits/s (50.00 Gbytes/s)", bandwidth.ToDebugValue()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_blocked_writer_interface.h b/quic/core/quic_blocked_writer_interface.h new file mode 100644 index 0000000..8193b25 --- /dev/null +++ b/quic/core/quic_blocked_writer_interface.h
@@ -0,0 +1,29 @@ +// Copyright (c) 2012 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. + +// This is an interface for all objects that want to be notified that +// the underlying UDP socket is available for writing (not write blocked +// anymore). + +#ifndef QUICHE_QUIC_CORE_QUIC_BLOCKED_WRITER_INTERFACE_H_ +#define QUICHE_QUIC_CORE_QUIC_BLOCKED_WRITER_INTERFACE_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicBlockedWriterInterface { + public: + virtual ~QuicBlockedWriterInterface() {} + + // Called by the PacketWriter when the underlying socket becomes writable + // so that the BlockedWriter can go ahead and try writing. + virtual void OnBlockedWriterCanWrite() = 0; + + virtual bool IsWriterBlocked() const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_BLOCKED_WRITER_INTERFACE_H_
diff --git a/quic/core/quic_buffer_allocator.cc b/quic/core/quic_buffer_allocator.cc new file mode 100644 index 0000000..c380274 --- /dev/null +++ b/quic/core/quic_buffer_allocator.cc
@@ -0,0 +1,11 @@ +// Copyright (c) 2012 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/quic_buffer_allocator.h" + +namespace quic { + +QuicBufferAllocator::~QuicBufferAllocator() = default; + +} // namespace quic
diff --git a/quic/core/quic_buffer_allocator.h b/quic/core/quic_buffer_allocator.h new file mode 100644 index 0000000..10df369 --- /dev/null +++ b/quic/core/quic_buffer_allocator.h
@@ -0,0 +1,37 @@ +// Copyright (c) 2012 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_QUIC_BUFFER_ALLOCATOR_H_ +#define QUICHE_QUIC_CORE_QUIC_BUFFER_ALLOCATOR_H_ + +#include <stddef.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Abstract base class for classes which allocate and delete buffers. +class QUIC_EXPORT_PRIVATE QuicBufferAllocator { + public: + virtual ~QuicBufferAllocator(); + + // Returns or allocates a new buffer of |size|. Never returns null. + virtual char* New(size_t size) = 0; + + // Returns or allocates a new buffer of |size| if |flag_enable| is true. + // Otherwise, returns a buffer that is compatible with this class directly + // with operator new. Never returns null. + virtual char* New(size_t size, bool flag_enable) = 0; + + // Releases a buffer. + virtual void Delete(char* buffer) = 0; + + // Marks the allocator as being idle. Serves as a hint to notify the allocator + // that it should release any resources it's still holding on to. + virtual void MarkAllocatorIdle() {} +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_BUFFER_ALLOCATOR_H_
diff --git a/quic/core/quic_buffered_packet_store.cc b/quic/core/quic_buffered_packet_store.cc new file mode 100644 index 0000000..68ebac9 --- /dev/null +++ b/quic/core/quic_buffered_packet_store.cc
@@ -0,0 +1,236 @@ +// 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/quic_buffered_packet_store.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket; +typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList; +typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult; + +// Max number of connections this store can keep track. +static const size_t kDefaultMaxConnectionsInStore = 100; +// Up to half of the capacity can be used for storing non-CHLO packets. +static const size_t kMaxConnectionsWithoutCHLO = + kDefaultMaxConnectionsInStore / 2; + +namespace { + +// This alarm removes expired entries in map each time this alarm fires. +class ConnectionExpireAlarm : public QuicAlarm::Delegate { + public: + explicit ConnectionExpireAlarm(QuicBufferedPacketStore* store) + : connection_store_(store) {} + + void OnAlarm() override { connection_store_->OnExpirationTimeout(); } + + ConnectionExpireAlarm(const ConnectionExpireAlarm&) = delete; + ConnectionExpireAlarm& operator=(const ConnectionExpireAlarm&) = delete; + + private: + QuicBufferedPacketStore* connection_store_; +}; + +} // namespace + +BufferedPacket::BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet, + QuicSocketAddress self_address, + QuicSocketAddress peer_address) + : packet(std::move(packet)), + self_address(self_address), + peer_address(peer_address) {} + +BufferedPacket::BufferedPacket(BufferedPacket&& other) = default; + +BufferedPacket& BufferedPacket::operator=(BufferedPacket&& other) = default; + +BufferedPacket::~BufferedPacket() {} + +BufferedPacketList::BufferedPacketList() + : creation_time(QuicTime::Zero()), + ietf_quic(false), + version(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED) {} + +BufferedPacketList::BufferedPacketList(BufferedPacketList&& other) = default; + +BufferedPacketList& BufferedPacketList::operator=(BufferedPacketList&& other) = + default; + +BufferedPacketList::~BufferedPacketList() {} + +QuicBufferedPacketStore::QuicBufferedPacketStore( + VisitorInterface* visitor, + const QuicClock* clock, + QuicAlarmFactory* alarm_factory) + : connection_life_span_( + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)), + visitor_(visitor), + clock_(clock), + expiration_alarm_( + alarm_factory->CreateAlarm(new ConnectionExpireAlarm(this))) {} + +QuicBufferedPacketStore::~QuicBufferedPacketStore() {} + +EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket( + QuicConnectionId connection_id, + bool ietf_quic, + const QuicReceivedPacket& packet, + QuicSocketAddress self_address, + QuicSocketAddress peer_address, + bool is_chlo, + const QuicString& alpn, + const ParsedQuicVersion& version) { + QUIC_BUG_IF(!FLAGS_quic_allow_chlo_buffering) + << "Shouldn't buffer packets if disabled via flag."; + QUIC_BUG_IF(is_chlo && QuicContainsKey(connections_with_chlo_, connection_id)) + << "Shouldn't buffer duplicated CHLO on connection " << connection_id; + QUIC_BUG_IF(!is_chlo && !alpn.empty()) + << "Shouldn't have an ALPN defined for a non-CHLO packet."; + QUIC_BUG_IF(is_chlo && version.transport_version == QUIC_VERSION_UNSUPPORTED) + << "Should have version for CHLO packet."; + + if (!QuicContainsKey(undecryptable_packets_, connection_id) && + ShouldBufferPacket(is_chlo)) { + // Drop the packet if the upper limit of undecryptable packets has been + // reached or the whole capacity of the store has been reached. + return TOO_MANY_CONNECTIONS; + } else if (!QuicContainsKey(undecryptable_packets_, connection_id)) { + undecryptable_packets_.emplace( + std::make_pair(connection_id, BufferedPacketList())); + undecryptable_packets_.back().second.ietf_quic = ietf_quic; + undecryptable_packets_.back().second.version = version; + } + CHECK(QuicContainsKey(undecryptable_packets_, connection_id)); + BufferedPacketList& queue = + undecryptable_packets_.find(connection_id)->second; + + if (!is_chlo) { + // If current packet is not CHLO, it might not be buffered because store + // only buffers certain number of undecryptable packets per connection. + size_t num_non_chlo_packets = + QuicContainsKey(connections_with_chlo_, connection_id) + ? (queue.buffered_packets.size() - 1) + : queue.buffered_packets.size(); + if (num_non_chlo_packets >= kDefaultMaxUndecryptablePackets) { + // If there are kMaxBufferedPacketsPerConnection packets buffered up for + // this connection, drop the current packet. + return TOO_MANY_PACKETS; + } + } + + if (queue.buffered_packets.empty()) { + // If this is the first packet arrived on a new connection, initialize the + // creation time. + queue.creation_time = clock_->ApproximateNow(); + } + + BufferedPacket new_entry(std::unique_ptr<QuicReceivedPacket>(packet.Clone()), + self_address, peer_address); + if (is_chlo) { + // Add CHLO to the beginning of buffered packets so that it can be delivered + // first later. + queue.buffered_packets.push_front(std::move(new_entry)); + queue.alpn = alpn; + connections_with_chlo_[connection_id] = false; // Dummy value. + // Set the version of buffered packets of this connection on CHLO. + queue.version = version; + } else { + // Buffer non-CHLO packets in arrival order. + queue.buffered_packets.push_back(std::move(new_entry)); + } + MaybeSetExpirationAlarm(); + return SUCCESS; +} + +bool QuicBufferedPacketStore::HasBufferedPackets( + QuicConnectionId connection_id) const { + return QuicContainsKey(undecryptable_packets_, connection_id); +} + +bool QuicBufferedPacketStore::HasChlosBuffered() const { + return !connections_with_chlo_.empty(); +} + +BufferedPacketList QuicBufferedPacketStore::DeliverPackets( + QuicConnectionId connection_id) { + BufferedPacketList packets_to_deliver; + auto it = undecryptable_packets_.find(connection_id); + if (it != undecryptable_packets_.end()) { + packets_to_deliver = std::move(it->second); + undecryptable_packets_.erase(connection_id); + } + return packets_to_deliver; +} + +void QuicBufferedPacketStore::DiscardPackets(QuicConnectionId connection_id) { + undecryptable_packets_.erase(connection_id); + connections_with_chlo_.erase(connection_id); +} + +void QuicBufferedPacketStore::OnExpirationTimeout() { + QuicTime expiration_time = clock_->ApproximateNow() - connection_life_span_; + while (!undecryptable_packets_.empty()) { + auto& entry = undecryptable_packets_.front(); + if (entry.second.creation_time > expiration_time) { + break; + } + QuicConnectionId connection_id = entry.first; + visitor_->OnExpiredPackets(connection_id, std::move(entry.second)); + undecryptable_packets_.pop_front(); + connections_with_chlo_.erase(connection_id); + } + if (!undecryptable_packets_.empty()) { + MaybeSetExpirationAlarm(); + } +} + +void QuicBufferedPacketStore::MaybeSetExpirationAlarm() { + if (!expiration_alarm_->IsSet()) { + expiration_alarm_->Set(clock_->ApproximateNow() + connection_life_span_); + } +} + +bool QuicBufferedPacketStore::ShouldBufferPacket(bool is_chlo) { + bool is_store_full = + undecryptable_packets_.size() >= kDefaultMaxConnectionsInStore; + + if (is_chlo) { + return is_store_full; + } + + size_t num_connections_without_chlo = + undecryptable_packets_.size() - connections_with_chlo_.size(); + bool reach_non_chlo_limit = + num_connections_without_chlo >= kMaxConnectionsWithoutCHLO; + + return is_store_full || reach_non_chlo_limit; +} + +BufferedPacketList QuicBufferedPacketStore::DeliverPacketsForNextConnection( + QuicConnectionId* connection_id) { + if (connections_with_chlo_.empty()) { + // Returns empty list if no CHLO has been buffered. + return BufferedPacketList(); + } + *connection_id = connections_with_chlo_.front().first; + connections_with_chlo_.pop_front(); + + BufferedPacketList packets = DeliverPackets(*connection_id); + DCHECK(!packets.buffered_packets.empty()) + << "Try to deliver connectons without CHLO"; + return packets; +} + +bool QuicBufferedPacketStore::HasChloForConnection( + QuicConnectionId connection_id) { + return QuicContainsKey(connections_with_chlo_, connection_id); +} + +} // namespace quic
diff --git a/quic/core/quic_buffered_packet_store.h b/quic/core/quic_buffered_packet_store.h new file mode 100644 index 0000000..f2490ee --- /dev/null +++ b/quic/core/quic_buffered_packet_store.h
@@ -0,0 +1,178 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_QUIC_BUFFERED_PACKET_STORE_H_ +#define QUICHE_QUIC_CORE_QUIC_BUFFERED_PACKET_STORE_H_ + +#include <list> + +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace test { +class QuicBufferedPacketStorePeer; +} // namespace test + +// This class buffers packets for each connection until either +// 1) They are requested to be delivered via +// DeliverPacket()/DeliverPacketsForNextConnection(), or +// 2) They expire after exceeding their lifetime in the store. +// +// It can only buffer packets on certain number of connections. It has two pools +// of connections: connections with CHLO buffered and those without CHLO. The +// latter has its own upper limit along with the max number of connections this +// store can hold. The former pool can grow till this store is full. +class QUIC_EXPORT_PRIVATE QuicBufferedPacketStore { + public: + enum EnqueuePacketResult { + SUCCESS = 0, + TOO_MANY_PACKETS, // Too many packets stored up for a certain connection. + TOO_MANY_CONNECTIONS // Too many connections stored up in the store. + }; + + struct QUIC_EXPORT_PRIVATE BufferedPacket { + BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet, + QuicSocketAddress self_address, + QuicSocketAddress peer_address); + BufferedPacket(BufferedPacket&& other); + + BufferedPacket& operator=(BufferedPacket&& other); + + ~BufferedPacket(); + + std::unique_ptr<QuicReceivedPacket> packet; + QuicSocketAddress self_address; + QuicSocketAddress peer_address; + }; + + // A queue of BufferedPackets for a connection. + struct QUIC_EXPORT_PRIVATE BufferedPacketList { + BufferedPacketList(); + BufferedPacketList(BufferedPacketList&& other); + + BufferedPacketList& operator=(BufferedPacketList&& other); + + ~BufferedPacketList(); + + std::list<BufferedPacket> buffered_packets; + QuicTime creation_time; + // The alpn from the CHLO, if one was found. + QuicString alpn; + // Indicating whether this is an IETF QUIC connection. + bool ietf_quic; + // If buffered_packets contains the CHLO, it is the version of the CHLO. + // Otherwise, it is the version of the first packet in |buffered_packets|. + ParsedQuicVersion version; + }; + + typedef QuicLinkedHashMap<QuicConnectionId, + BufferedPacketList, + QuicConnectionIdHash> + BufferedPacketMap; + + class QUIC_EXPORT_PRIVATE VisitorInterface { + public: + virtual ~VisitorInterface() {} + + // Called for each expired connection when alarm fires. + virtual void OnExpiredPackets(QuicConnectionId connection_id, + BufferedPacketList early_arrived_packets) = 0; + }; + + QuicBufferedPacketStore(VisitorInterface* vistor, + const QuicClock* clock, + QuicAlarmFactory* alarm_factory); + + QuicBufferedPacketStore(const QuicBufferedPacketStore&) = delete; + + ~QuicBufferedPacketStore(); + + QuicBufferedPacketStore& operator=(const QuicBufferedPacketStore&) = delete; + + // Adds a copy of packet into packet queue for given connection. + // TODO(danzh): Consider to split this method to EnqueueChlo() and + // EnqueueDataPacket(). + EnqueuePacketResult EnqueuePacket(QuicConnectionId connection_id, + bool ietf_quic, + const QuicReceivedPacket& packet, + QuicSocketAddress self_address, + QuicSocketAddress peer_address, + bool is_chlo, + const QuicString& alpn, + const ParsedQuicVersion& version); + + // Returns true if there are any packets buffered for |connection_id|. + bool HasBufferedPackets(QuicConnectionId connection_id) const; + + // Returns the list of buffered packets for |connection_id| and removes them + // from the store. Returns an empty list if no early arrived packets for this + // connection are present. + BufferedPacketList DeliverPackets(QuicConnectionId connection_id); + + // Discards packets buffered for |connection_id|, if any. + void DiscardPackets(QuicConnectionId connection_id); + + // Examines how long packets have been buffered in the store for each + // connection. If they stay too long, removes them for new coming packets and + // calls |visitor_|'s OnPotentialConnectionExpire(). + // Resets the alarm at the end. + void OnExpirationTimeout(); + + // Delivers buffered packets for next connection with CHLO to open. + // Return connection id for next connection in |connection_id| + // and all buffered packets including CHLO. + // The returned list should at least has one packet(CHLO) if + // store does have any connection to open. If no connection in the store has + // received CHLO yet, empty list will be returned. + BufferedPacketList DeliverPacketsForNextConnection( + QuicConnectionId* connection_id); + + // Is given connection already buffered in the store? + bool HasChloForConnection(QuicConnectionId connection_id); + + // Is there any CHLO buffered in the store? + bool HasChlosBuffered() const; + + private: + friend class test::QuicBufferedPacketStorePeer; + + // Set expiration alarm if it hasn't been set. + void MaybeSetExpirationAlarm(); + + // Return true if add an extra packet will go beyond allowed max connection + // limit. The limit for non-CHLO packet and CHLO packet is different. + bool ShouldBufferPacket(bool is_chlo); + + // A map to store packet queues with creation time for each connection. + BufferedPacketMap undecryptable_packets_; + + // The max time the packets of a connection can be buffer in the store. + const QuicTime::Delta connection_life_span_; + + VisitorInterface* visitor_; // Unowned. + + const QuicClock* clock_; // Unowned. + + // This alarm fires every |connection_life_span_| to clean up + // packets staying in the store for too long. + std::unique_ptr<QuicAlarm> expiration_alarm_; + + // Keeps track of connection with CHLO buffered up already and the order they + // arrive. + QuicLinkedHashMap<QuicConnectionId, bool, QuicConnectionIdHash> + connections_with_chlo_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_BUFFERED_PACKET_STORE_H_
diff --git a/quic/core/quic_buffered_packet_store_test.cc b/quic/core/quic_buffered_packet_store_test.cc new file mode 100644 index 0000000..e214ff1 --- /dev/null +++ b/quic/core/quic_buffered_packet_store_test.cc
@@ -0,0 +1,441 @@ +// 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/quic_buffered_packet_store.h" + +#include <list> + +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { + +typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket; +typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult; + +static const size_t kDefaultMaxConnectionsInStore = 100; +static const size_t kMaxConnectionsWithoutCHLO = + kDefaultMaxConnectionsInStore / 2; + +namespace test { +namespace { + +typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket; +typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList; + +class QuicBufferedPacketStoreVisitor + : public QuicBufferedPacketStore::VisitorInterface { + public: + QuicBufferedPacketStoreVisitor() {} + + ~QuicBufferedPacketStoreVisitor() override {} + + void OnExpiredPackets(QuicConnectionId connection_id, + BufferedPacketList early_arrived_packets) override { + last_expired_packet_queue_ = std::move(early_arrived_packets); + } + + // The packets queue for most recently expirect connection. + BufferedPacketList last_expired_packet_queue_; +}; + +class QuicBufferedPacketStoreTest : public QuicTest { + public: + QuicBufferedPacketStoreTest() + : store_(&visitor_, &clock_, &alarm_factory_), + self_address_(QuicIpAddress::Any6(), 65535), + peer_address_(QuicIpAddress::Any6(), 65535), + packet_content_("some encrypted content"), + packet_time_(QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(42)), + packet_(packet_content_.data(), packet_content_.size(), packet_time_), + invalid_version_(UnsupportedQuicVersion()), + valid_version_(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44) {} + + protected: + QuicBufferedPacketStoreVisitor visitor_; + MockClock clock_; + MockAlarmFactory alarm_factory_; + QuicBufferedPacketStore store_; + QuicSocketAddress self_address_; + QuicSocketAddress peer_address_; + QuicString packet_content_; + QuicTime packet_time_; + QuicReceivedPacket packet_; + const ParsedQuicVersion invalid_version_; + const ParsedQuicVersion valid_version_; +}; + +TEST_F(QuicBufferedPacketStoreTest, SimpleEnqueueAndDeliverPacket) { + QuicConnectionId connection_id = TestConnectionId(1); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + EXPECT_TRUE(store_.HasBufferedPackets(connection_id)); + auto packets = store_.DeliverPackets(connection_id); + const std::list<BufferedPacket>& queue = packets.buffered_packets; + ASSERT_EQ(1u, queue.size()); + // The alpn should be ignored for non-chlo packets. + ASSERT_EQ("", packets.alpn); + // There is no valid version because CHLO has not arrived. + EXPECT_EQ(invalid_version_, packets.version); + // Check content of the only packet in the queue. + EXPECT_EQ(packet_content_, queue.front().packet->AsStringPiece()); + EXPECT_EQ(packet_time_, queue.front().packet->receipt_time()); + EXPECT_EQ(peer_address_, queue.front().peer_address); + EXPECT_EQ(self_address_, queue.front().self_address); + // No more packets on connection 1 should remain in the store. + EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty()); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); +} + +TEST_F(QuicBufferedPacketStoreTest, DifferentPacketAddressOnOneConnection) { + QuicSocketAddress addr_with_new_port(QuicIpAddress::Any4(), 256); + QuicConnectionId connection_id = TestConnectionId(1); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + addr_with_new_port, false, "", invalid_version_); + std::list<BufferedPacket> queue = + store_.DeliverPackets(connection_id).buffered_packets; + ASSERT_EQ(2u, queue.size()); + // The address migration path should be preserved. + EXPECT_EQ(peer_address_, queue.front().peer_address); + EXPECT_EQ(addr_with_new_port, queue.back().peer_address); +} + +TEST_F(QuicBufferedPacketStoreTest, + EnqueueAndDeliverMultiplePacketsOnMultipleConnections) { + size_t num_connections = 10; + for (uint64_t conn_id = 1; conn_id <= num_connections; ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + } + + // Deliver packets in reversed order. + for (uint64_t conn_id = num_connections; conn_id > 0; --conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + std::list<BufferedPacket> queue = + store_.DeliverPackets(connection_id).buffered_packets; + ASSERT_EQ(2u, queue.size()); + } +} + +TEST_F(QuicBufferedPacketStoreTest, + FailToBufferTooManyPacketsOnExistingConnection) { + // Tests that for one connection, only limited number of packets can be + // buffered. + size_t num_packets = kDefaultMaxUndecryptablePackets + 1; + QuicConnectionId connection_id = TestConnectionId(1); + // Arrived CHLO packet shouldn't affect how many non-CHLO pacekts store can + // keep. + EXPECT_EQ(QuicBufferedPacketStore::SUCCESS, + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, true, "", valid_version_)); + for (size_t i = 1; i <= num_packets; ++i) { + // Only first |kDefaultMaxUndecryptablePackets packets| will be buffered. + EnqueuePacketResult result = + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + if (i <= kDefaultMaxUndecryptablePackets) { + EXPECT_EQ(EnqueuePacketResult::SUCCESS, result); + } else { + EXPECT_EQ(EnqueuePacketResult::TOO_MANY_PACKETS, result); + } + } + + // Only first |kDefaultMaxUndecryptablePackets| non-CHLO packets and CHLO are + // buffered. + EXPECT_EQ(kDefaultMaxUndecryptablePackets + 1, + store_.DeliverPackets(connection_id).buffered_packets.size()); +} + +TEST_F(QuicBufferedPacketStoreTest, ReachNonChloConnectionUpperLimit) { + // Tests that store can only keep early arrived packets for limited number of + // connections. + const size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1; + for (uint64_t conn_id = 1; conn_id <= kNumConnections; ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + EnqueuePacketResult result = + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + if (conn_id <= kMaxConnectionsWithoutCHLO) { + EXPECT_EQ(EnqueuePacketResult::SUCCESS, result); + } else { + EXPECT_EQ(EnqueuePacketResult::TOO_MANY_CONNECTIONS, result); + } + } + // Store only keeps early arrived packets upto |kNumConnections| connections. + for (uint64_t conn_id = 1; conn_id <= kNumConnections; ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + std::list<BufferedPacket> queue = + store_.DeliverPackets(connection_id).buffered_packets; + if (conn_id <= kMaxConnectionsWithoutCHLO) { + EXPECT_EQ(1u, queue.size()); + } else { + EXPECT_EQ(0u, queue.size()); + } + } +} + +TEST_F(QuicBufferedPacketStoreTest, + FullStoreFailToBufferDataPacketOnNewConnection) { + // Send enough CHLOs so that store gets full before number of connections + // without CHLO reaches its upper limit. + size_t num_chlos = + kDefaultMaxConnectionsInStore - kMaxConnectionsWithoutCHLO + 1; + for (uint64_t conn_id = 1; conn_id <= num_chlos; ++conn_id) { + EXPECT_EQ(EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket(TestConnectionId(conn_id), false, packet_, + self_address_, peer_address_, true, "", + valid_version_)); + } + + // Send data packets on another |kMaxConnectionsWithoutCHLO| connections. + // Store should only be able to buffer till it's full. + for (uint64_t conn_id = num_chlos + 1; + conn_id <= (kDefaultMaxConnectionsInStore + 1); ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + EnqueuePacketResult result = + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, true, "", valid_version_); + if (conn_id <= kDefaultMaxConnectionsInStore) { + EXPECT_EQ(EnqueuePacketResult::SUCCESS, result); + } else { + EXPECT_EQ(EnqueuePacketResult::TOO_MANY_CONNECTIONS, result); + } + } +} + +TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) { + // Buffer data packets on different connections upto limit. + for (uint64_t conn_id = 1; conn_id <= kMaxConnectionsWithoutCHLO; ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + EXPECT_EQ(EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_)); + } + + // Buffer CHLOs on other connections till store is full. + for (size_t i = kMaxConnectionsWithoutCHLO + 1; + i <= kDefaultMaxConnectionsInStore + 1; ++i) { + QuicConnectionId connection_id = TestConnectionId(i); + EnqueuePacketResult rs = + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, true, "", valid_version_); + if (i <= kDefaultMaxConnectionsInStore) { + EXPECT_EQ(EnqueuePacketResult::SUCCESS, rs); + EXPECT_TRUE(store_.HasChloForConnection(connection_id)); + } else { + // Last CHLO can't be buffered because store is full. + EXPECT_EQ(EnqueuePacketResult::TOO_MANY_CONNECTIONS, rs); + EXPECT_FALSE(store_.HasChloForConnection(connection_id)); + } + } + + // But buffering a CHLO belonging to a connection already has data packet + // buffered in the store should success. This is the connection should be + // delivered at last. + EXPECT_EQ(EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket( + /*connection_id=*/TestConnectionId(1), false, packet_, + self_address_, peer_address_, true, "", valid_version_)); + EXPECT_TRUE(store_.HasChloForConnection( + /*connection_id=*/TestConnectionId(1))); + + QuicConnectionId delivered_conn_id; + for (size_t i = 0; + i < kDefaultMaxConnectionsInStore - kMaxConnectionsWithoutCHLO + 1; + ++i) { + if (i < kDefaultMaxConnectionsInStore - kMaxConnectionsWithoutCHLO) { + // Only CHLO is buffered. + EXPECT_EQ(1u, store_.DeliverPacketsForNextConnection(&delivered_conn_id) + .buffered_packets.size()); + EXPECT_EQ(TestConnectionId(i + kMaxConnectionsWithoutCHLO + 1), + delivered_conn_id); + } else { + EXPECT_EQ(2u, store_.DeliverPacketsForNextConnection(&delivered_conn_id) + .buffered_packets.size()); + EXPECT_EQ(TestConnectionId(1u), delivered_conn_id); + } + } + EXPECT_FALSE(store_.HasChlosBuffered()); +} + +// Tests that store expires long-staying connections appropriately for +// connections both with and without CHLOs. +TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery) { + QuicConnectionId connection_id = TestConnectionId(1); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + EXPECT_EQ(EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, true, "", valid_version_)); + QuicConnectionId connection_id2 = TestConnectionId(2); + EXPECT_EQ(EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket(connection_id2, false, packet_, self_address_, + peer_address_, false, "", invalid_version_)); + + // CHLO on connection 3 arrives 1ms later. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + QuicConnectionId connection_id3 = TestConnectionId(3); + // Use different client address to differetiate packets from different + // connections. + QuicSocketAddress another_client_address(QuicIpAddress::Any4(), 255); + store_.EnqueuePacket(connection_id3, false, packet_, self_address_, + another_client_address, true, "", valid_version_); + + // Advance clock to the time when connection 1 and 2 expires. + clock_.AdvanceTime( + QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() - + clock_.ApproximateNow()); + ASSERT_GE(clock_.ApproximateNow(), + QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline()); + // Fire alarm to remove long-staying connection 1 and 2 packets. + alarm_factory_.FireAlarm( + QuicBufferedPacketStorePeer::expiration_alarm(&store_)); + EXPECT_EQ(1u, visitor_.last_expired_packet_queue_.buffered_packets.size()); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id2)); + + // Try to deliver packets, but packet queue has been removed so no + // packets can be returned. + ASSERT_EQ(0u, store_.DeliverPackets(connection_id).buffered_packets.size()); + ASSERT_EQ(0u, store_.DeliverPackets(connection_id2).buffered_packets.size()); + QuicConnectionId delivered_conn_id; + auto queue = store_.DeliverPacketsForNextConnection(&delivered_conn_id) + .buffered_packets; + // Connection 3 is the next to be delivered as connection 1 already expired. + EXPECT_EQ(connection_id3, delivered_conn_id); + ASSERT_EQ(1u, queue.size()); + // Packets in connection 3 should use another peer address. + EXPECT_EQ(another_client_address, queue.front().peer_address); + + // Test the alarm is reset by enqueueing 2 packets for 4th connection and wait + // for them to expire. + QuicConnectionId connection_id4 = TestConnectionId(4); + store_.EnqueuePacket(connection_id4, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + store_.EnqueuePacket(connection_id4, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + clock_.AdvanceTime( + QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() - + clock_.ApproximateNow()); + alarm_factory_.FireAlarm( + QuicBufferedPacketStorePeer::expiration_alarm(&store_)); + // |last_expired_packet_queue_| should be updated. + EXPECT_EQ(2u, visitor_.last_expired_packet_queue_.buffered_packets.size()); +} + +TEST_F(QuicBufferedPacketStoreTest, SimpleDiscardPackets) { + QuicConnectionId connection_id = TestConnectionId(1); + + // Enqueue some packets + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + EXPECT_TRUE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasChlosBuffered()); + + // Dicard the packets + store_.DiscardPackets(connection_id); + + // No packets on connection 1 should remain in the store + EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty()); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasChlosBuffered()); + + // Check idempotency + store_.DiscardPackets(connection_id); + EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty()); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasChlosBuffered()); +} + +TEST_F(QuicBufferedPacketStoreTest, DiscardWithCHLOs) { + QuicConnectionId connection_id = TestConnectionId(1); + + // Enqueue some packets, which include a CHLO + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, true, "", valid_version_); + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + EXPECT_TRUE(store_.HasBufferedPackets(connection_id)); + EXPECT_TRUE(store_.HasChlosBuffered()); + + // Dicard the packets + store_.DiscardPackets(connection_id); + + // No packets on connection 1 should remain in the store + EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty()); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasChlosBuffered()); + + // Check idempotency + store_.DiscardPackets(connection_id); + EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty()); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasChlosBuffered()); +} + +TEST_F(QuicBufferedPacketStoreTest, MultipleDiscardPackets) { + QuicConnectionId connection_id_1 = TestConnectionId(1); + QuicConnectionId connection_id_2 = TestConnectionId(2); + + // Enqueue some packets for two connection IDs + store_.EnqueuePacket(connection_id_1, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + store_.EnqueuePacket(connection_id_1, false, packet_, self_address_, + peer_address_, false, "", invalid_version_); + store_.EnqueuePacket(connection_id_2, false, packet_, self_address_, + peer_address_, true, "h3", valid_version_); + EXPECT_TRUE(store_.HasBufferedPackets(connection_id_1)); + EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2)); + EXPECT_TRUE(store_.HasChlosBuffered()); + + // Discard the packets for connection 1 + store_.DiscardPackets(connection_id_1); + + // No packets on connection 1 should remain in the store + EXPECT_TRUE(store_.DeliverPackets(connection_id_1).buffered_packets.empty()); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id_1)); + EXPECT_TRUE(store_.HasChlosBuffered()); + + // Packets on connection 2 should remain + EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2)); + auto packets = store_.DeliverPackets(connection_id_2); + EXPECT_EQ(1u, packets.buffered_packets.size()); + EXPECT_EQ("h3", packets.alpn); + // Since connection_id_2's chlo arrives, verify version is set. + EXPECT_EQ(valid_version_, packets.version); + EXPECT_TRUE(store_.HasChlosBuffered()); + + // Discard the packets for connection 2 + store_.DiscardPackets(connection_id_2); + EXPECT_FALSE(store_.HasChlosBuffered()); +} + +TEST_F(QuicBufferedPacketStoreTest, DiscardPacketsEmpty) { + // Check that DiscardPackets on an unknown connection ID is safe and does + // nothing. + QuicConnectionId connection_id = TestConnectionId(11235); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasChlosBuffered()); + store_.DiscardPackets(connection_id); + EXPECT_FALSE(store_.HasBufferedPackets(connection_id)); + EXPECT_FALSE(store_.HasChlosBuffered()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc new file mode 100644 index 0000000..e53719c --- /dev/null +++ b/quic/core/quic_config.cc
@@ -0,0 +1,823 @@ +// Copyright (c) 2013 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/quic_config.h" + +#include <algorithm> + +#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/quic_socket_address_coder.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// Reads the value corresponding to |name_| from |msg| into |out|. If the +// |name_| is absent in |msg| and |presence| is set to OPTIONAL |out| is set +// to |default_value|. +QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg, + QuicTag tag, + QuicConfigPresence presence, + uint32_t default_value, + uint32_t* out, + QuicString* error_details) { + DCHECK(error_details != nullptr); + QuicErrorCode error = msg.GetUint32(tag, out); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence == PRESENCE_REQUIRED) { + *error_details = "Missing " + QuicTagToString(tag); + break; + } + error = QUIC_NO_ERROR; + *out = default_value; + break; + case QUIC_NO_ERROR: + break; + default: + *error_details = "Bad " + QuicTagToString(tag); + break; + } + return error; +} + +QuicConfigValue::QuicConfigValue(QuicTag tag, QuicConfigPresence presence) + : tag_(tag), presence_(presence) {} +QuicConfigValue::~QuicConfigValue() {} + +QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, + QuicConfigPresence presence) + : QuicConfigValue(tag, presence), negotiated_(false) {} +QuicNegotiableValue::~QuicNegotiableValue() {} + +QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, + QuicConfigPresence presence) + : QuicNegotiableValue(tag, presence), + max_value_(0), + default_value_(0), + negotiated_value_(0) {} +QuicNegotiableUint32::~QuicNegotiableUint32() {} + +void QuicNegotiableUint32::set(uint32_t max, uint32_t default_value) { + DCHECK_LE(default_value, max); + max_value_ = max; + default_value_ = default_value; +} + +uint32_t QuicNegotiableUint32::GetUint32() const { + if (negotiated()) { + return negotiated_value_; + } + return default_value_; +} + +// Returns the maximum value negotiable. +uint32_t QuicNegotiableUint32::GetMax() const { + return max_value_; +} + +void QuicNegotiableUint32::ToHandshakeMessage( + CryptoHandshakeMessage* out) const { + if (negotiated()) { + out->SetValue(tag_, negotiated_value_); + } else { + out->SetValue(tag_, max_value_); + } +} + +QuicErrorCode QuicNegotiableUint32::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) { + DCHECK(!negotiated()); + DCHECK(error_details != nullptr); + uint32_t value; + QuicErrorCode error = ReadUint32(peer_hello, tag_, presence_, default_value_, + &value, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + return ReceiveValue(value, hello_type, error_details); +} + +QuicErrorCode QuicNegotiableUint32::ReceiveValue(uint32_t value, + HelloType hello_type, + QuicString* error_details) { + if (hello_type == SERVER && value > max_value_) { + *error_details = "Invalid value received for " + QuicTagToString(tag_); + return QUIC_INVALID_NEGOTIATED_VALUE; + } + + set_negotiated(true); + negotiated_value_ = std::min(value, max_value_); + return QUIC_NO_ERROR; +} + +QuicFixedUint32::QuicFixedUint32(QuicTag tag, QuicConfigPresence presence) + : QuicConfigValue(tag, presence), + has_send_value_(false), + has_receive_value_(false) {} +QuicFixedUint32::~QuicFixedUint32() {} + +bool QuicFixedUint32::HasSendValue() const { + return has_send_value_; +} + +uint32_t QuicFixedUint32::GetSendValue() const { + QUIC_BUG_IF(!has_send_value_) + << "No send value to get for tag:" << QuicTagToString(tag_); + return send_value_; +} + +void QuicFixedUint32::SetSendValue(uint32_t value) { + has_send_value_ = true; + send_value_ = value; +} + +bool QuicFixedUint32::HasReceivedValue() const { + return has_receive_value_; +} + +uint32_t QuicFixedUint32::GetReceivedValue() const { + QUIC_BUG_IF(!has_receive_value_) + << "No receive value to get for tag:" << QuicTagToString(tag_); + return receive_value_; +} + +void QuicFixedUint32::SetReceivedValue(uint32_t value) { + has_receive_value_ = true; + receive_value_ = value; +} + +void QuicFixedUint32::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + if (has_send_value_) { + out->SetValue(tag_, send_value_); + } +} + +QuicErrorCode QuicFixedUint32::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) { + DCHECK(error_details != nullptr); + QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence_ == PRESENCE_OPTIONAL) { + return QUIC_NO_ERROR; + } + *error_details = "Missing " + QuicTagToString(tag_); + break; + case QUIC_NO_ERROR: + has_receive_value_ = true; + break; + default: + *error_details = "Bad " + QuicTagToString(tag_); + break; + } + return error; +} + +QuicFixedUint128::QuicFixedUint128(QuicTag tag, QuicConfigPresence presence) + : QuicConfigValue(tag, presence), + has_send_value_(false), + has_receive_value_(false) {} +QuicFixedUint128::~QuicFixedUint128() {} + +bool QuicFixedUint128::HasSendValue() const { + return has_send_value_; +} + +QuicUint128 QuicFixedUint128::GetSendValue() const { + QUIC_BUG_IF(!has_send_value_) + << "No send value to get for tag:" << QuicTagToString(tag_); + return send_value_; +} + +void QuicFixedUint128::SetSendValue(QuicUint128 value) { + has_send_value_ = true; + send_value_ = value; +} + +bool QuicFixedUint128::HasReceivedValue() const { + return has_receive_value_; +} + +QuicUint128 QuicFixedUint128::GetReceivedValue() const { + QUIC_BUG_IF(!has_receive_value_) + << "No receive value to get for tag:" << QuicTagToString(tag_); + return receive_value_; +} + +void QuicFixedUint128::SetReceivedValue(QuicUint128 value) { + has_receive_value_ = true; + receive_value_ = value; +} + +void QuicFixedUint128::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + if (has_send_value_) { + out->SetValue(tag_, send_value_); + } +} + +QuicErrorCode QuicFixedUint128::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) { + DCHECK(error_details != nullptr); + QuicErrorCode error = peer_hello.GetUint128(tag_, &receive_value_); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence_ == PRESENCE_OPTIONAL) { + return QUIC_NO_ERROR; + } + *error_details = "Missing " + QuicTagToString(tag_); + break; + case QUIC_NO_ERROR: + has_receive_value_ = true; + break; + default: + *error_details = "Bad " + QuicTagToString(tag_); + break; + } + return error; +} + +QuicFixedTagVector::QuicFixedTagVector(QuicTag name, + QuicConfigPresence presence) + : QuicConfigValue(name, presence), + has_send_values_(false), + has_receive_values_(false) {} + +QuicFixedTagVector::QuicFixedTagVector(const QuicFixedTagVector& other) = + default; + +QuicFixedTagVector::~QuicFixedTagVector() {} + +bool QuicFixedTagVector::HasSendValues() const { + return has_send_values_; +} + +QuicTagVector QuicFixedTagVector::GetSendValues() const { + QUIC_BUG_IF(!has_send_values_) + << "No send values to get for tag:" << QuicTagToString(tag_); + return send_values_; +} + +void QuicFixedTagVector::SetSendValues(const QuicTagVector& values) { + has_send_values_ = true; + send_values_ = values; +} + +bool QuicFixedTagVector::HasReceivedValues() const { + return has_receive_values_; +} + +QuicTagVector QuicFixedTagVector::GetReceivedValues() const { + QUIC_BUG_IF(!has_receive_values_) + << "No receive value to get for tag:" << QuicTagToString(tag_); + return receive_values_; +} + +void QuicFixedTagVector::SetReceivedValues(const QuicTagVector& values) { + has_receive_values_ = true; + receive_values_ = values; +} + +void QuicFixedTagVector::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + if (has_send_values_) { + out->SetVector(tag_, send_values_); + } +} + +QuicErrorCode QuicFixedTagVector::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) { + DCHECK(error_details != nullptr); + QuicTagVector values; + QuicErrorCode error = peer_hello.GetTaglist(tag_, &values); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence_ == PRESENCE_OPTIONAL) { + return QUIC_NO_ERROR; + } + *error_details = "Missing " + QuicTagToString(tag_); + break; + case QUIC_NO_ERROR: + QUIC_DVLOG(1) << "Received Connection Option tags from receiver."; + has_receive_values_ = true; + receive_values_.insert(receive_values_.end(), values.begin(), + values.end()); + break; + default: + *error_details = "Bad " + QuicTagToString(tag_); + break; + } + return error; +} + +QuicFixedSocketAddress::QuicFixedSocketAddress(QuicTag tag, + QuicConfigPresence presence) + : QuicConfigValue(tag, presence), + has_send_value_(false), + has_receive_value_(false) {} + +QuicFixedSocketAddress::~QuicFixedSocketAddress() {} + +bool QuicFixedSocketAddress::HasSendValue() const { + return has_send_value_; +} + +const QuicSocketAddress& QuicFixedSocketAddress::GetSendValue() const { + QUIC_BUG_IF(!has_send_value_) + << "No send value to get for tag:" << QuicTagToString(tag_); + return send_value_; +} + +void QuicFixedSocketAddress::SetSendValue(const QuicSocketAddress& value) { + has_send_value_ = true; + send_value_ = value; +} + +bool QuicFixedSocketAddress::HasReceivedValue() const { + return has_receive_value_; +} + +const QuicSocketAddress& QuicFixedSocketAddress::GetReceivedValue() const { + QUIC_BUG_IF(!has_receive_value_) + << "No receive value to get for tag:" << QuicTagToString(tag_); + return receive_value_; +} + +void QuicFixedSocketAddress::SetReceivedValue(const QuicSocketAddress& value) { + has_receive_value_ = true; + receive_value_ = value; +} + +void QuicFixedSocketAddress::ToHandshakeMessage( + CryptoHandshakeMessage* out) const { + if (has_send_value_) { + QuicSocketAddressCoder address_coder(send_value_); + out->SetStringPiece(tag_, address_coder.Encode()); + } +} + +QuicErrorCode QuicFixedSocketAddress::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) { + QuicStringPiece address; + if (!peer_hello.GetStringPiece(tag_, &address)) { + if (presence_ == PRESENCE_REQUIRED) { + *error_details = "Missing " + QuicTagToString(tag_); + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + } else { + QuicSocketAddressCoder address_coder; + if (address_coder.Decode(address.data(), address.length())) { + SetReceivedValue( + QuicSocketAddress(address_coder.ip(), address_coder.port())); + } + } + return QUIC_NO_ERROR; +} + +QuicConfig::QuicConfig() + : max_time_before_crypto_handshake_(QuicTime::Delta::Zero()), + max_idle_time_before_crypto_handshake_(QuicTime::Delta::Zero()), + max_undecryptable_packets_(0), + connection_options_(kCOPT, PRESENCE_OPTIONAL), + client_connection_options_(kCLOP, PRESENCE_OPTIONAL), + idle_network_timeout_seconds_(kICSL, PRESENCE_REQUIRED), + silent_close_(kSCLS, PRESENCE_OPTIONAL), + max_incoming_dynamic_streams_(kMIDS, PRESENCE_REQUIRED), + bytes_for_connection_id_(kTCID, PRESENCE_OPTIONAL), + initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL), + initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL), + initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL), + connection_migration_disabled_(kNCMR, PRESENCE_OPTIONAL), + alternate_server_address_(kASAD, PRESENCE_OPTIONAL), + support_max_header_list_size_(kSMHL, PRESENCE_OPTIONAL), + stateless_reset_token_(kSRST, PRESENCE_OPTIONAL) { + SetDefaults(); +} + +QuicConfig::QuicConfig(const QuicConfig& other) = default; + +QuicConfig::~QuicConfig() {} + +bool QuicConfig::SetInitialReceivedConnectionOptions( + const QuicTagVector& tags) { + if (HasReceivedConnectionOptions()) { + // If we have already received connection options (via handshake or due to + // a previous call), don't re-initialize. + return false; + } + connection_options_.SetReceivedValues(tags); + return true; +} + +void QuicConfig::SetConnectionOptionsToSend( + const QuicTagVector& connection_options) { + connection_options_.SetSendValues(connection_options); +} + +bool QuicConfig::HasReceivedConnectionOptions() const { + return connection_options_.HasReceivedValues(); +} + +QuicTagVector QuicConfig::ReceivedConnectionOptions() const { + return connection_options_.GetReceivedValues(); +} + +bool QuicConfig::HasSendConnectionOptions() const { + return connection_options_.HasSendValues(); +} + +QuicTagVector QuicConfig::SendConnectionOptions() const { + return connection_options_.GetSendValues(); +} + +bool QuicConfig::HasClientSentConnectionOption(QuicTag tag, + Perspective perspective) const { + if (perspective == Perspective::IS_SERVER) { + if (HasReceivedConnectionOptions() && + ContainsQuicTag(ReceivedConnectionOptions(), tag)) { + return true; + } + } else if (HasSendConnectionOptions() && + ContainsQuicTag(SendConnectionOptions(), tag)) { + return true; + } + return false; +} + +void QuicConfig::SetClientConnectionOptions( + const QuicTagVector& client_connection_options) { + client_connection_options_.SetSendValues(client_connection_options); +} + +bool QuicConfig::HasClientRequestedIndependentOption( + QuicTag tag, + Perspective perspective) const { + if (perspective == Perspective::IS_SERVER) { + return (HasReceivedConnectionOptions() && + ContainsQuicTag(ReceivedConnectionOptions(), tag)); + } + + return (client_connection_options_.HasSendValues() && + ContainsQuicTag(client_connection_options_.GetSendValues(), tag)); +} + +void QuicConfig::SetIdleNetworkTimeout( + QuicTime::Delta max_idle_network_timeout, + QuicTime::Delta default_idle_network_timeout) { + idle_network_timeout_seconds_.set( + static_cast<uint32_t>(max_idle_network_timeout.ToSeconds()), + static_cast<uint32_t>(default_idle_network_timeout.ToSeconds())); +} + +QuicTime::Delta QuicConfig::IdleNetworkTimeout() const { + return QuicTime::Delta::FromSeconds( + idle_network_timeout_seconds_.GetUint32()); +} + +// TODO(ianswett) Use this for silent close on mobile, or delete. +QUIC_UNUSED void QuicConfig::SetSilentClose(bool silent_close) { + silent_close_.set(silent_close ? 1 : 0, silent_close ? 1 : 0); +} + +bool QuicConfig::SilentClose() const { + return silent_close_.GetUint32() > 0; +} + +void QuicConfig::SetMaxIncomingDynamicStreamsToSend( + uint32_t max_incoming_dynamic_streams) { + max_incoming_dynamic_streams_.SetSendValue(max_incoming_dynamic_streams); +} + +uint32_t QuicConfig::GetMaxIncomingDynamicStreamsToSend() { + return max_incoming_dynamic_streams_.GetSendValue(); +} + +bool QuicConfig::HasReceivedMaxIncomingDynamicStreams() { + return max_incoming_dynamic_streams_.HasReceivedValue(); +} + +uint32_t QuicConfig::ReceivedMaxIncomingDynamicStreams() { + return max_incoming_dynamic_streams_.GetReceivedValue(); +} + +bool QuicConfig::HasSetBytesForConnectionIdToSend() const { + return bytes_for_connection_id_.HasSendValue(); +} + +void QuicConfig::SetBytesForConnectionIdToSend(uint32_t bytes) { + bytes_for_connection_id_.SetSendValue(bytes); +} + +bool QuicConfig::HasReceivedBytesForConnectionId() const { + return bytes_for_connection_id_.HasReceivedValue(); +} + +uint32_t QuicConfig::ReceivedBytesForConnectionId() const { + return bytes_for_connection_id_.GetReceivedValue(); +} + +void QuicConfig::SetInitialRoundTripTimeUsToSend(uint32_t rtt) { + initial_round_trip_time_us_.SetSendValue(rtt); +} + +bool QuicConfig::HasReceivedInitialRoundTripTimeUs() const { + return initial_round_trip_time_us_.HasReceivedValue(); +} + +uint32_t QuicConfig::ReceivedInitialRoundTripTimeUs() const { + return initial_round_trip_time_us_.GetReceivedValue(); +} + +bool QuicConfig::HasInitialRoundTripTimeUsToSend() const { + return initial_round_trip_time_us_.HasSendValue(); +} + +uint32_t QuicConfig::GetInitialRoundTripTimeUsToSend() const { + return initial_round_trip_time_us_.GetSendValue(); +} + +void QuicConfig::SetInitialStreamFlowControlWindowToSend( + uint32_t window_bytes) { + if (window_bytes < kMinimumFlowControlSendWindow) { + QUIC_BUG << "Initial stream flow control receive window (" << window_bytes + << ") cannot be set lower than default (" + << kMinimumFlowControlSendWindow << ")."; + window_bytes = kMinimumFlowControlSendWindow; + } + initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes); +} + +uint32_t QuicConfig::GetInitialStreamFlowControlWindowToSend() const { + return initial_stream_flow_control_window_bytes_.GetSendValue(); +} + +bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const { + return initial_stream_flow_control_window_bytes_.HasReceivedValue(); +} + +uint32_t QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const { + return initial_stream_flow_control_window_bytes_.GetReceivedValue(); +} + +void QuicConfig::SetInitialSessionFlowControlWindowToSend( + uint32_t window_bytes) { + if (window_bytes < kMinimumFlowControlSendWindow) { + QUIC_BUG << "Initial session flow control receive window (" << window_bytes + << ") cannot be set lower than default (" + << kMinimumFlowControlSendWindow << ")."; + window_bytes = kMinimumFlowControlSendWindow; + } + initial_session_flow_control_window_bytes_.SetSendValue(window_bytes); +} + +uint32_t QuicConfig::GetInitialSessionFlowControlWindowToSend() const { + return initial_session_flow_control_window_bytes_.GetSendValue(); +} + +bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const { + return initial_session_flow_control_window_bytes_.HasReceivedValue(); +} + +uint32_t QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const { + return initial_session_flow_control_window_bytes_.GetReceivedValue(); +} + +void QuicConfig::SetDisableConnectionMigration() { + connection_migration_disabled_.SetSendValue(1); +} + +bool QuicConfig::DisableConnectionMigration() const { + return connection_migration_disabled_.HasReceivedValue(); +} + +void QuicConfig::SetAlternateServerAddressToSend( + const QuicSocketAddress& alternate_server_address) { + alternate_server_address_.SetSendValue(alternate_server_address); +} + +bool QuicConfig::HasReceivedAlternateServerAddress() const { + return alternate_server_address_.HasReceivedValue(); +} + +const QuicSocketAddress& QuicConfig::ReceivedAlternateServerAddress() const { + return alternate_server_address_.GetReceivedValue(); +} + +void QuicConfig::SetSupportMaxHeaderListSize() { + support_max_header_list_size_.SetSendValue(1); +} + +bool QuicConfig::SupportMaxHeaderListSize() const { + return support_max_header_list_size_.HasReceivedValue(); +} + +void QuicConfig::SetStatelessResetTokenToSend( + QuicUint128 stateless_reset_token) { + stateless_reset_token_.SetSendValue(stateless_reset_token); +} + +bool QuicConfig::HasReceivedStatelessResetToken() const { + return stateless_reset_token_.HasReceivedValue(); +} + +QuicUint128 QuicConfig::ReceivedStatelessResetToken() const { + return stateless_reset_token_.GetReceivedValue(); +} + +bool QuicConfig::negotiated() const { + // TODO(ianswett): Add the negotiated parameters once and iterate over all + // of them in negotiated, ToHandshakeMessage, and ProcessPeerHello. + return idle_network_timeout_seconds_.negotiated(); +} + +void QuicConfig::SetCreateSessionTagIndicators(QuicTagVector tags) { + create_session_tag_indicators_ = std::move(tags); +} + +const QuicTagVector& QuicConfig::create_session_tag_indicators() const { + return create_session_tag_indicators_; +} + +void QuicConfig::SetDefaults() { + idle_network_timeout_seconds_.set(kMaximumIdleTimeoutSecs, + kDefaultIdleTimeoutSecs); + silent_close_.set(1, 0); + SetMaxIncomingDynamicStreamsToSend(kDefaultMaxStreamsPerConnection); + max_time_before_crypto_handshake_ = + QuicTime::Delta::FromSeconds(kMaxTimeForCryptoHandshakeSecs); + max_idle_time_before_crypto_handshake_ = + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs); + max_undecryptable_packets_ = kDefaultMaxUndecryptablePackets; + + SetInitialStreamFlowControlWindowToSend(kMinimumFlowControlSendWindow); + SetInitialSessionFlowControlWindowToSend(kMinimumFlowControlSendWindow); + SetSupportMaxHeaderListSize(); +} + +void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + idle_network_timeout_seconds_.ToHandshakeMessage(out); + silent_close_.ToHandshakeMessage(out); + max_incoming_dynamic_streams_.ToHandshakeMessage(out); + bytes_for_connection_id_.ToHandshakeMessage(out); + initial_round_trip_time_us_.ToHandshakeMessage(out); + initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out); + initial_session_flow_control_window_bytes_.ToHandshakeMessage(out); + connection_migration_disabled_.ToHandshakeMessage(out); + connection_options_.ToHandshakeMessage(out); + alternate_server_address_.ToHandshakeMessage(out); + support_max_header_list_size_.ToHandshakeMessage(out); + stateless_reset_token_.ToHandshakeMessage(out); +} + +QuicErrorCode QuicConfig::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) { + DCHECK(error_details != nullptr); + + QuicErrorCode error = QUIC_NO_ERROR; + if (error == QUIC_NO_ERROR) { + error = idle_network_timeout_seconds_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { + error = + silent_close_.ProcessPeerHello(peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { + error = max_incoming_dynamic_streams_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { + error = bytes_for_connection_id_.ProcessPeerHello(peer_hello, hello_type, + error_details); + } + if (error == QUIC_NO_ERROR) { + error = initial_round_trip_time_us_.ProcessPeerHello(peer_hello, hello_type, + error_details); + } + if (error == QUIC_NO_ERROR) { + error = initial_stream_flow_control_window_bytes_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { + error = initial_session_flow_control_window_bytes_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { + error = connection_migration_disabled_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { + error = connection_options_.ProcessPeerHello(peer_hello, hello_type, + error_details); + } + if (error == QUIC_NO_ERROR) { + error = alternate_server_address_.ProcessPeerHello(peer_hello, hello_type, + error_details); + } + if (error == QUIC_NO_ERROR) { + error = support_max_header_list_size_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } + if (error == QUIC_NO_ERROR) { + error = stateless_reset_token_.ProcessPeerHello(peer_hello, hello_type, + error_details); + } + return error; +} + +bool QuicConfig::FillTransportParameters(TransportParameters* params) const { + params->initial_max_stream_data = + initial_stream_flow_control_window_bytes_.GetSendValue(); + params->initial_max_data = + initial_session_flow_control_window_bytes_.GetSendValue(); + + uint32_t idle_timeout = idle_network_timeout_seconds_.GetUint32(); + if (idle_timeout > std::numeric_limits<uint16_t>::max()) { + QUIC_BUG << "idle network timeout set too large"; + return false; + } + params->idle_timeout = idle_timeout; + + uint32_t initial_max_streams = max_incoming_dynamic_streams_.GetSendValue(); + if (initial_max_streams > std::numeric_limits<uint16_t>::max()) { + QUIC_BUG << "max incoming streams set too large"; + return false; + } + params->initial_max_bidi_streams.present = true; + params->initial_max_bidi_streams.value = initial_max_streams; + + if (!params->google_quic_params) { + params->google_quic_params = QuicMakeUnique<CryptoHandshakeMessage>(); + } + silent_close_.ToHandshakeMessage(params->google_quic_params.get()); + initial_round_trip_time_us_.ToHandshakeMessage( + params->google_quic_params.get()); + connection_options_.ToHandshakeMessage(params->google_quic_params.get()); + return true; +} + +QuicErrorCode QuicConfig::ProcessTransportParameters( + const TransportParameters& params, + HelloType hello_type, + QuicString* error_details) { + QuicErrorCode error = idle_network_timeout_seconds_.ReceiveValue( + params.idle_timeout, hello_type, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + const CryptoHandshakeMessage* peer_params = params.google_quic_params.get(); + if (!peer_params) { + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + error = + silent_close_.ProcessPeerHello(*peer_params, hello_type, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + error = initial_round_trip_time_us_.ProcessPeerHello(*peer_params, hello_type, + error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + error = connection_options_.ProcessPeerHello(*peer_params, hello_type, + error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + initial_stream_flow_control_window_bytes_.SetReceivedValue( + params.initial_max_stream_data); + initial_session_flow_control_window_bytes_.SetReceivedValue( + params.initial_max_data); + if (params.initial_max_bidi_streams.present) { + max_incoming_dynamic_streams_.SetReceivedValue( + params.initial_max_bidi_streams.value); + } else { + // An absent value for initial_max_bidi_streams is treated as a value of 0. + max_incoming_dynamic_streams_.SetReceivedValue(0); + } + + return QUIC_NO_ERROR; +} + +} // namespace quic
diff --git a/quic/core/quic_config.h b/quic/core/quic_config.h new file mode 100644 index 0000000..affc7b9 --- /dev/null +++ b/quic/core/quic_config.h
@@ -0,0 +1,491 @@ +// Copyright (c) 2013 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_QUIC_CONFIG_H_ +#define QUICHE_QUIC_CORE_QUIC_CONFIG_H_ + +#include <cstddef> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +namespace test { +class QuicConfigPeer; +} // namespace test + +class CryptoHandshakeMessage; + +// Describes whether or not a given QuicTag is required or optional in the +// handshake message. +enum QuicConfigPresence { + // This negotiable value can be absent from the handshake message. Default + // value is selected as the negotiated value in such a case. + PRESENCE_OPTIONAL, + // This negotiable value is required in the handshake message otherwise the + // Process*Hello function returns an error. + PRESENCE_REQUIRED, +}; + +// Whether the CryptoHandshakeMessage is from the client or server. +enum HelloType { + CLIENT, + SERVER, +}; + +// An abstract base class that stores a value that can be sent in CHLO/SHLO +// message. These values can be OPTIONAL or REQUIRED, depending on |presence_|. +class QUIC_EXPORT_PRIVATE QuicConfigValue { + public: + QuicConfigValue(QuicTag tag, QuicConfigPresence presence); + virtual ~QuicConfigValue(); + + // Serialises tag name and value(s) to |out|. + virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const = 0; + + // Selects a mutually acceptable value from those offered in |peer_hello| + // and those defined in the subclass. + virtual QuicErrorCode ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) = 0; + + protected: + const QuicTag tag_; + const QuicConfigPresence presence_; +}; + +class QUIC_EXPORT_PRIVATE QuicNegotiableValue : public QuicConfigValue { + public: + QuicNegotiableValue(QuicTag tag, QuicConfigPresence presence); + ~QuicNegotiableValue() override; + + bool negotiated() const { return negotiated_; } + + protected: + void set_negotiated(bool negotiated) { negotiated_ = negotiated; } + + private: + bool negotiated_; +}; + +class QUIC_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { + // TODO(fayang): some negotiated values use uint32 as bool (e.g., silent + // close). Consider adding a QuicNegotiableBool type. + public: + // Default and max values default to 0. + QuicNegotiableUint32(QuicTag name, QuicConfigPresence presence); + ~QuicNegotiableUint32() override; + + // Sets the maximum possible value that can be achieved after negotiation and + // also the default values to be assumed if PRESENCE_OPTIONAL and the *HLO msg + // doesn't contain a value corresponding to |name_|. |max| is serialised via + // ToHandshakeMessage call if |negotiated_| is false. + void set(uint32_t max, uint32_t default_value); + + // Returns the value negotiated if |negotiated_| is true, otherwise returns + // default_value_ (used to set default values before negotiation finishes). + uint32_t GetUint32() const; + + // Returns the maximum value negotiable. + uint32_t GetMax() const; + + // Serialises |name_| and value to |out|. If |negotiated_| is true then + // |negotiated_value_| is serialised, otherwise |max_value_| is serialised. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const override; + + // Processes the corresponding value from |peer_hello| and if present calls + // ReceiveValue with it. If the corresponding value is missing and + // PRESENCE_OPTIONAL then |negotiated_value_| is set to |default_value_|. + QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) override; + + // Takes a value |value| parsed from a handshake message (whether a TLS + // ClientHello/ServerHello or a CryptoHandshakeMessage) whose sender was + // |hello_type|, and sets |negotiated_value_| to the minimum of |value| and + // |max_value_|. On success this function returns QUIC_NO_ERROR; if there is + // an error, details are put in |*error_details|. + QuicErrorCode ReceiveValue(uint32_t value, + HelloType hello_type, + QuicString* error_details); + + private: + uint32_t max_value_; + uint32_t default_value_; + uint32_t negotiated_value_; +}; + +// Stores uint32_t from CHLO or SHLO messages that are not negotiated. +class QUIC_EXPORT_PRIVATE QuicFixedUint32 : public QuicConfigValue { + public: + QuicFixedUint32(QuicTag name, QuicConfigPresence presence); + ~QuicFixedUint32() override; + + bool HasSendValue() const; + + uint32_t GetSendValue() const; + + void SetSendValue(uint32_t value); + + bool HasReceivedValue() const; + + uint32_t GetReceivedValue() const; + + void SetReceivedValue(uint32_t value); + + // If has_send_value is true, serialises |tag_| and |send_value_| to |out|. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const override; + + // Sets |value_| to the corresponding value from |peer_hello_| if it exists. + QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) override; + + private: + uint32_t send_value_; + bool has_send_value_; + uint32_t receive_value_; + bool has_receive_value_; +}; + +// Stores uint128 from CHLO or SHLO messages that are not negotiated. +class QUIC_EXPORT_PRIVATE QuicFixedUint128 : public QuicConfigValue { + public: + QuicFixedUint128(QuicTag tag, QuicConfigPresence presence); + ~QuicFixedUint128() override; + + bool HasSendValue() const; + + QuicUint128 GetSendValue() const; + + void SetSendValue(QuicUint128 value); + + bool HasReceivedValue() const; + + QuicUint128 GetReceivedValue() const; + + void SetReceivedValue(QuicUint128 value); + + // If has_send_value is true, serialises |tag_| and |send_value_| to |out|. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const override; + + // Sets |value_| to the corresponding value from |peer_hello_| if it exists. + QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) override; + + private: + QuicUint128 send_value_; + bool has_send_value_; + QuicUint128 receive_value_; + bool has_receive_value_; +}; + +// Stores tag from CHLO or SHLO messages that are not negotiated. +class QUIC_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue { + public: + QuicFixedTagVector(QuicTag name, QuicConfigPresence presence); + QuicFixedTagVector(const QuicFixedTagVector& other); + ~QuicFixedTagVector() override; + + bool HasSendValues() const; + + QuicTagVector GetSendValues() const; + + void SetSendValues(const QuicTagVector& values); + + bool HasReceivedValues() const; + + QuicTagVector GetReceivedValues() const; + + void SetReceivedValues(const QuicTagVector& values); + + // If has_send_value is true, serialises |tag_vector_| and |send_value_| to + // |out|. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const override; + + // Sets |receive_values_| to the corresponding value from |client_hello_| if + // it exists. + QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) override; + + private: + QuicTagVector send_values_; + bool has_send_values_; + QuicTagVector receive_values_; + bool has_receive_values_; +}; + +// Stores QuicSocketAddress from CHLO or SHLO messages that are not negotiated. +class QUIC_EXPORT_PRIVATE QuicFixedSocketAddress : public QuicConfigValue { + public: + QuicFixedSocketAddress(QuicTag tag, QuicConfigPresence presence); + ~QuicFixedSocketAddress() override; + + bool HasSendValue() const; + + const QuicSocketAddress& GetSendValue() const; + + void SetSendValue(const QuicSocketAddress& value); + + bool HasReceivedValue() const; + + const QuicSocketAddress& GetReceivedValue() const; + + void SetReceivedValue(const QuicSocketAddress& value); + + void ToHandshakeMessage(CryptoHandshakeMessage* out) const override; + + QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details) override; + + private: + QuicSocketAddress send_value_; + bool has_send_value_; + QuicSocketAddress receive_value_; + bool has_receive_value_; +}; + +// QuicConfig contains non-crypto configuration options that are negotiated in +// the crypto handshake. +class QUIC_EXPORT_PRIVATE QuicConfig { + public: + QuicConfig(); + QuicConfig(const QuicConfig& other); + ~QuicConfig(); + + void SetConnectionOptionsToSend(const QuicTagVector& connection_options); + + bool HasReceivedConnectionOptions() const; + + // Sets initial received connection options. All received connection options + // will be initialized with these fields. Initial received options may only be + // set once per config, prior to the setting of any other options. If options + // have already been set (either by previous calls or via handshake), this + // function does nothing and returns false. + bool SetInitialReceivedConnectionOptions(const QuicTagVector& tags); + + QuicTagVector ReceivedConnectionOptions() const; + + bool HasSendConnectionOptions() const; + + QuicTagVector SendConnectionOptions() const; + + // Returns true if the client is sending or the server has received a + // connection option. + // TODO(ianswett): Rename to HasClientRequestedSharedOption + bool HasClientSentConnectionOption(QuicTag tag, + Perspective perspective) const; + + void SetClientConnectionOptions( + const QuicTagVector& client_connection_options); + + // Returns true if the client has requested the specified connection option. + // Checks the client connection options if the |perspective| is client and + // connection options if the |perspective| is the server. + bool HasClientRequestedIndependentOption(QuicTag tag, + Perspective perspective) const; + + void SetIdleNetworkTimeout(QuicTime::Delta max_idle_network_timeout, + QuicTime::Delta default_idle_network_timeout); + + QuicTime::Delta IdleNetworkTimeout() const; + + void SetSilentClose(bool silent_close); + + bool SilentClose() const; + + void SetMaxIncomingDynamicStreamsToSend( + uint32_t max_incoming_dynamic_streams); + + uint32_t GetMaxIncomingDynamicStreamsToSend(); + + bool HasReceivedMaxIncomingDynamicStreams(); + + uint32_t ReceivedMaxIncomingDynamicStreams(); + + void set_max_time_before_crypto_handshake( + QuicTime::Delta max_time_before_crypto_handshake) { + max_time_before_crypto_handshake_ = max_time_before_crypto_handshake; + } + + QuicTime::Delta max_time_before_crypto_handshake() const { + return max_time_before_crypto_handshake_; + } + + void set_max_idle_time_before_crypto_handshake( + QuicTime::Delta max_idle_time_before_crypto_handshake) { + max_idle_time_before_crypto_handshake_ = + max_idle_time_before_crypto_handshake; + } + + QuicTime::Delta max_idle_time_before_crypto_handshake() const { + return max_idle_time_before_crypto_handshake_; + } + + QuicNegotiableUint32 idle_network_timeout_seconds() const { + return idle_network_timeout_seconds_; + } + + void set_max_undecryptable_packets(size_t max_undecryptable_packets) { + max_undecryptable_packets_ = max_undecryptable_packets; + } + + size_t max_undecryptable_packets() const { + return max_undecryptable_packets_; + } + + bool HasSetBytesForConnectionIdToSend() const; + + // Sets the peer's connection id length, in bytes. + void SetBytesForConnectionIdToSend(uint32_t bytes); + + bool HasReceivedBytesForConnectionId() const; + + uint32_t ReceivedBytesForConnectionId() const; + + // Sets an estimated initial round trip time in us. + void SetInitialRoundTripTimeUsToSend(uint32_t rtt_us); + + bool HasReceivedInitialRoundTripTimeUs() const; + + uint32_t ReceivedInitialRoundTripTimeUs() const; + + bool HasInitialRoundTripTimeUsToSend() const; + + uint32_t GetInitialRoundTripTimeUsToSend() const; + + // Sets an initial stream flow control window size to transmit to the peer. + void SetInitialStreamFlowControlWindowToSend(uint32_t window_bytes); + + uint32_t GetInitialStreamFlowControlWindowToSend() const; + + bool HasReceivedInitialStreamFlowControlWindowBytes() const; + + uint32_t ReceivedInitialStreamFlowControlWindowBytes() const; + + // Sets an initial session flow control window size to transmit to the peer. + void SetInitialSessionFlowControlWindowToSend(uint32_t window_bytes); + + uint32_t GetInitialSessionFlowControlWindowToSend() const; + + bool HasReceivedInitialSessionFlowControlWindowBytes() const; + + uint32_t ReceivedInitialSessionFlowControlWindowBytes() const; + + void SetDisableConnectionMigration(); + + bool DisableConnectionMigration() const; + + void SetAlternateServerAddressToSend( + const QuicSocketAddress& alternate_server_address); + + bool HasReceivedAlternateServerAddress() const; + + const QuicSocketAddress& ReceivedAlternateServerAddress() const; + + void SetSupportMaxHeaderListSize(); + + bool SupportMaxHeaderListSize() const; + + void SetStatelessResetTokenToSend(QuicUint128 stateless_reset_token); + + bool HasReceivedStatelessResetToken() const; + + QuicUint128 ReceivedStatelessResetToken() const; + + bool negotiated() const; + + void SetCreateSessionTagIndicators(QuicTagVector tags); + + const QuicTagVector& create_session_tag_indicators() const; + + // ToHandshakeMessage serialises the settings in this object as a series of + // tags /value pairs and adds them to |out|. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + + // Calls ProcessPeerHello on each negotiable parameter. On failure returns + // the corresponding QuicErrorCode and sets detailed error in |error_details|. + QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + QuicString* error_details); + + // FillTransportParameters writes the values to send for ICSL, MIDS, CFCW, and + // SFCW to |*params|, returning true if the values could be written and false + // if something prevents them from being written (e.g. a value is too large). + bool FillTransportParameters(TransportParameters* params) const; + + // ProcessTransportParameters reads from |params| which was received from a + // peer operating as a |hello_type|. It processes values for ICSL, MIDS, CFCW, + // and SFCW and sets the corresponding members of this QuicConfig. On failure, + // it returns a QuicErrorCode and puts a detailed error in |*error_details|. + QuicErrorCode ProcessTransportParameters(const TransportParameters& params, + HelloType hello_type, + QuicString* error_details); + + private: + friend class test::QuicConfigPeer; + + // SetDefaults sets the members to sensible, default values. + void SetDefaults(); + + // Configurations options that are not negotiated. + // Maximum time the session can be alive before crypto handshake is finished. + QuicTime::Delta max_time_before_crypto_handshake_; + // Maximum idle time before the crypto handshake has completed. + QuicTime::Delta max_idle_time_before_crypto_handshake_; + // Maximum number of undecryptable packets stored before CHLO/SHLO. + size_t max_undecryptable_packets_; + + // Connection options which affect the server side. May also affect the + // client side in cases when identical behavior is desirable. + QuicFixedTagVector connection_options_; + // Connection options which only affect the client side. + QuicFixedTagVector client_connection_options_; + // Idle network timeout in seconds. + QuicNegotiableUint32 idle_network_timeout_seconds_; + // Whether to use silent close. Defaults to 0 (false) and is otherwise true. + QuicNegotiableUint32 silent_close_; + // Maximum number of incoming dynamic streams that the connection can support. + QuicFixedUint32 max_incoming_dynamic_streams_; + // The number of bytes required for the connection ID. + QuicFixedUint32 bytes_for_connection_id_; + // Initial round trip time estimate in microseconds. + QuicFixedUint32 initial_round_trip_time_us_; + + // Initial stream flow control receive window in bytes. + QuicFixedUint32 initial_stream_flow_control_window_bytes_; + // Initial session flow control receive window in bytes. + QuicFixedUint32 initial_session_flow_control_window_bytes_; + + // Whether tell peer not to attempt connection migration. + QuicFixedUint32 connection_migration_disabled_; + + // An alternate server address the client could connect to. + QuicFixedSocketAddress alternate_server_address_; + + // Whether support HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE SETTINGS frame. + QuicFixedUint32 support_max_header_list_size_; + + // Stateless reset token used in IETF public reset packet. + QuicFixedUint128 stateless_reset_token_; + + // List of QuicTags whose presence immediately causes the session to + // be created. This allows for CHLOs that are larger than a single + // packet to be processed. + QuicTagVector create_session_tag_indicators_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CONFIG_H_
diff --git a/quic/core/quic_config_test.cc b/quic/core/quic_config_test.cc new file mode 100644 index 0000000..59ad0ef --- /dev/null +++ b/quic/core/quic_config_test.cc
@@ -0,0 +1,287 @@ +// Copyright (c) 2013 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/quic_config.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/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +class QuicConfigTest : public QuicTest { + protected: + QuicConfig config_; +}; + +TEST_F(QuicConfigTest, ToHandshakeMessage) { + config_.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + config_.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + config_.SetIdleNetworkTimeout(QuicTime::Delta::FromSeconds(5), + QuicTime::Delta::FromSeconds(2)); + CryptoHandshakeMessage msg; + config_.ToHandshakeMessage(&msg); + + uint32_t value; + QuicErrorCode error = msg.GetUint32(kICSL, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(5u, value); + + error = msg.GetUint32(kSFCW, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(kInitialStreamFlowControlWindowForTest, value); + + error = msg.GetUint32(kCFCW, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value); +} + +TEST_F(QuicConfigTest, ProcessClientHello) { + QuicConfig client_config; + QuicTagVector cgst; + cgst.push_back(kQBIC); + client_config.SetIdleNetworkTimeout( + QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs), + QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs)); + client_config.SetInitialRoundTripTimeUsToSend(10 * kNumMicrosPerMilli); + client_config.SetInitialStreamFlowControlWindowToSend( + 2 * kInitialStreamFlowControlWindowForTest); + client_config.SetInitialSessionFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + QuicTagVector copt; + copt.push_back(kTBBR); + client_config.SetConnectionOptionsToSend(copt); + CryptoHandshakeMessage msg; + client_config.ToHandshakeMessage(&msg); + + QuicString error_details; + QuicTagVector initial_received_options; + initial_received_options.push_back(kIW50); + EXPECT_TRUE( + config_.SetInitialReceivedConnectionOptions(initial_received_options)); + EXPECT_FALSE( + config_.SetInitialReceivedConnectionOptions(initial_received_options)) + << "You can only set initial options once."; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_FALSE( + config_.SetInitialReceivedConnectionOptions(initial_received_options)) + << "You cannot set initial options after the hello."; + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs), + config_.IdleNetworkTimeout()); + EXPECT_EQ(10 * kNumMicrosPerMilli, config_.ReceivedInitialRoundTripTimeUs()); + EXPECT_TRUE(config_.HasReceivedConnectionOptions()); + EXPECT_EQ(2u, config_.ReceivedConnectionOptions().size()); + EXPECT_EQ(config_.ReceivedConnectionOptions()[0], kIW50); + EXPECT_EQ(config_.ReceivedConnectionOptions()[1], kTBBR); + EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(), + 2 * kInitialStreamFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), + 2 * kInitialSessionFlowControlWindowForTest); +} + +TEST_F(QuicConfigTest, ProcessServerHello) { + QuicIpAddress host; + host.FromString("127.0.3.1"); + const QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234); + const QuicUint128 kTestResetToken = MakeQuicUint128(0, 10111100001); + QuicConfig server_config; + QuicTagVector cgst; + cgst.push_back(kQBIC); + server_config.SetIdleNetworkTimeout( + QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2), + QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2)); + server_config.SetInitialRoundTripTimeUsToSend(10 * kNumMicrosPerMilli); + server_config.SetInitialStreamFlowControlWindowToSend( + 2 * kInitialStreamFlowControlWindowForTest); + server_config.SetInitialSessionFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + server_config.SetAlternateServerAddressToSend(kTestServerAddress); + server_config.SetStatelessResetTokenToSend(kTestResetToken); + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2), + config_.IdleNetworkTimeout()); + EXPECT_EQ(10 * kNumMicrosPerMilli, config_.ReceivedInitialRoundTripTimeUs()); + EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(), + 2 * kInitialStreamFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), + 2 * kInitialSessionFlowControlWindowForTest); + EXPECT_TRUE(config_.HasReceivedAlternateServerAddress()); + EXPECT_EQ(kTestServerAddress, config_.ReceivedAlternateServerAddress()); + EXPECT_TRUE(config_.HasReceivedStatelessResetToken()); + EXPECT_EQ(kTestResetToken, config_.ReceivedStatelessResetToken()); +} + +TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) { + CryptoHandshakeMessage msg; + msg.SetValue(kICSL, 1); + + // Set all REQUIRED tags. + msg.SetValue(kICSL, 1); + msg.SetValue(kMIDS, 1); + + // No error, as rest are optional. + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); +} + +TEST_F(QuicConfigTest, MissingOptionalValuesInSHLO) { + CryptoHandshakeMessage msg; + + // Set all REQUIRED tags. + msg.SetValue(kICSL, 1); + msg.SetValue(kMIDS, 1); + + // No error, as rest are optional. + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); +} + +TEST_F(QuicConfigTest, MissingValueInCHLO) { + // Server receives CHLO with missing kICSL. + CryptoHandshakeMessage msg; + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error); +} + +TEST_F(QuicConfigTest, MissingValueInSHLO) { + // Client receives SHLO with missing kICSL. + CryptoHandshakeMessage msg; + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error); +} + +TEST_F(QuicConfigTest, OutOfBoundSHLO) { + QuicConfig server_config; + server_config.SetIdleNetworkTimeout( + QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs), + QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs)); + + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); + EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error); +} + +TEST_F(QuicConfigTest, InvalidFlowControlWindow) { + // QuicConfig should not accept an invalid flow control window to send to the + // peer: the receive window must be at least the default of 16 Kb. + QuicConfig config; + const uint64_t kInvalidWindow = kMinimumFlowControlSendWindow - 1; + EXPECT_QUIC_BUG( + config.SetInitialStreamFlowControlWindowToSend(kInvalidWindow), + "Initial stream flow control receive window"); + + EXPECT_EQ(kMinimumFlowControlSendWindow, + config.GetInitialStreamFlowControlWindowToSend()); +} + +TEST_F(QuicConfigTest, HasClientSentConnectionOption) { + QuicConfig client_config; + QuicTagVector copt; + copt.push_back(kTBBR); + client_config.SetConnectionOptionsToSend(copt); + EXPECT_TRUE(client_config.HasClientSentConnectionOption( + kTBBR, Perspective::IS_CLIENT)); + + CryptoHandshakeMessage msg; + client_config.ToHandshakeMessage(&msg); + + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + + EXPECT_TRUE(config_.HasReceivedConnectionOptions()); + EXPECT_EQ(1u, config_.ReceivedConnectionOptions().size()); + EXPECT_TRUE( + config_.HasClientSentConnectionOption(kTBBR, Perspective::IS_SERVER)); +} + +TEST_F(QuicConfigTest, DontSendClientConnectionOptions) { + QuicConfig client_config; + QuicTagVector copt; + copt.push_back(kTBBR); + client_config.SetClientConnectionOptions(copt); + + CryptoHandshakeMessage msg; + client_config.ToHandshakeMessage(&msg); + + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + + EXPECT_FALSE(config_.HasReceivedConnectionOptions()); +} + +TEST_F(QuicConfigTest, HasClientRequestedIndependentOption) { + QuicConfig client_config; + QuicTagVector client_opt; + client_opt.push_back(kRENO); + QuicTagVector copt; + copt.push_back(kTBBR); + client_config.SetClientConnectionOptions(client_opt); + client_config.SetConnectionOptionsToSend(copt); + EXPECT_TRUE(client_config.HasClientSentConnectionOption( + kTBBR, Perspective::IS_CLIENT)); + EXPECT_TRUE(client_config.HasClientRequestedIndependentOption( + kRENO, Perspective::IS_CLIENT)); + EXPECT_FALSE(client_config.HasClientRequestedIndependentOption( + kTBBR, Perspective::IS_CLIENT)); + + CryptoHandshakeMessage msg; + client_config.ToHandshakeMessage(&msg); + + QuicString error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + + EXPECT_TRUE(config_.HasReceivedConnectionOptions()); + EXPECT_EQ(1u, config_.ReceivedConnectionOptions().size()); + EXPECT_FALSE(config_.HasClientRequestedIndependentOption( + kRENO, Perspective::IS_SERVER)); + EXPECT_TRUE(config_.HasClientRequestedIndependentOption( + kTBBR, Perspective::IS_SERVER)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc new file mode 100644 index 0000000..070191b --- /dev/null +++ b/quic/core/quic_connection.cc
@@ -0,0 +1,3815 @@ +// Copyright (c) 2012 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/quic_connection.h" + +#include <string.h> +#include <sys/types.h> + +#include <algorithm> +#include <iterator> +#include <limits> +#include <memory> +#include <set> +#include <utility> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.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/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h" +#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +class QuicDecrypter; +class QuicEncrypter; + +namespace { + +// Maximum number of consecutive sent nonretransmittable packets. +const QuicPacketCount kMaxConsecutiveNonRetransmittablePackets = 19; + +// Maximum number of retransmittable packets received before sending an ack. +const QuicPacketCount kDefaultRetransmittablePacketsBeforeAck = 2; +// Minimum number of packets received before ack decimation is enabled. +// This intends to avoid the beginning of slow start, when CWNDs may be +// rapidly increasing. +const QuicPacketCount kMinReceivedBeforeAckDecimation = 100; +// Wait for up to 10 retransmittable packets before sending an ack. +const QuicPacketCount kMaxRetransmittablePacketsBeforeAck = 10; +// One quarter RTT delay when doing ack decimation. +const float kAckDecimationDelay = 0.25; +// One eighth RTT delay when doing ack decimation. +const float kShortAckDecimationDelay = 0.125; + +// The minimum release time into future in ms. +const int kMinReleaseTimeIntoFutureMs = 1; + +bool Near(QuicPacketNumber a, QuicPacketNumber b) { + QuicPacketCount delta = (a > b) ? a - b : b - a; + return delta <= kMaxPacketGap; +} + +// An alarm that is scheduled to send an ack if a timeout occurs. +class AckAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit AckAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + AckAlarmDelegate(const AckAlarmDelegate&) = delete; + AckAlarmDelegate& operator=(const AckAlarmDelegate&) = delete; + + void OnAlarm() override { + DCHECK(connection_->ack_frame_updated()); + QuicConnection::ScopedPacketFlusher flusher(connection_, + QuicConnection::SEND_ACK); + if (connection_->packet_generator().deprecate_ack_bundling_mode()) { + DCHECK(!connection_->GetUpdatedAckFrame().ack_frame->packets.Empty()); + connection_->SendAck(); + } + } + + private: + QuicConnection* connection_; +}; + +// This alarm will be scheduled any time a data-bearing packet is sent out. +// When the alarm goes off, the connection checks to see if the oldest packets +// have been acked, and retransmit them if they have not. +class RetransmissionAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit RetransmissionAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + RetransmissionAlarmDelegate(const RetransmissionAlarmDelegate&) = delete; + RetransmissionAlarmDelegate& operator=(const RetransmissionAlarmDelegate&) = + delete; + + void OnAlarm() override { connection_->OnRetransmissionTimeout(); } + + private: + QuicConnection* connection_; +}; + +// An alarm that is scheduled when the SentPacketManager requires a delay +// before sending packets and fires when the packet may be sent. +class SendAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit SendAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + SendAlarmDelegate(const SendAlarmDelegate&) = delete; + SendAlarmDelegate& operator=(const SendAlarmDelegate&) = delete; + + void OnAlarm() override { connection_->WriteAndBundleAcksIfNotBlocked(); } + + private: + QuicConnection* connection_; +}; + +class PathDegradingAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit PathDegradingAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + PathDegradingAlarmDelegate(const PathDegradingAlarmDelegate&) = delete; + PathDegradingAlarmDelegate& operator=(const PathDegradingAlarmDelegate&) = + delete; + + void OnAlarm() override { connection_->OnPathDegradingTimeout(); } + + private: + QuicConnection* connection_; +}; + +class TimeoutAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit TimeoutAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + TimeoutAlarmDelegate(const TimeoutAlarmDelegate&) = delete; + TimeoutAlarmDelegate& operator=(const TimeoutAlarmDelegate&) = delete; + + void OnAlarm() override { connection_->CheckForTimeout(); } + + private: + QuicConnection* connection_; +}; + +class PingAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit PingAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + PingAlarmDelegate(const PingAlarmDelegate&) = delete; + PingAlarmDelegate& operator=(const PingAlarmDelegate&) = delete; + + void OnAlarm() override { connection_->OnPingTimeout(); } + + private: + QuicConnection* connection_; +}; + +class MtuDiscoveryAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit MtuDiscoveryAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + MtuDiscoveryAlarmDelegate(const MtuDiscoveryAlarmDelegate&) = delete; + MtuDiscoveryAlarmDelegate& operator=(const MtuDiscoveryAlarmDelegate&) = + delete; + + void OnAlarm() override { connection_->DiscoverMtu(); } + + private: + QuicConnection* connection_; +}; + +class RetransmittableOnWireAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit RetransmittableOnWireAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + RetransmittableOnWireAlarmDelegate( + const RetransmittableOnWireAlarmDelegate&) = delete; + RetransmittableOnWireAlarmDelegate& operator=( + const RetransmittableOnWireAlarmDelegate&) = delete; + + void OnAlarm() override { connection_->OnPingTimeout(); } + + private: + QuicConnection* connection_; +}; + +class ProcessUndecryptablePacketsAlarmDelegate : public QuicAlarm::Delegate { + public: + explicit ProcessUndecryptablePacketsAlarmDelegate(QuicConnection* connection) + : connection_(connection) {} + ProcessUndecryptablePacketsAlarmDelegate( + const ProcessUndecryptablePacketsAlarmDelegate&) = delete; + ProcessUndecryptablePacketsAlarmDelegate& operator=( + const ProcessUndecryptablePacketsAlarmDelegate&) = delete; + + void OnAlarm() override { connection_->MaybeProcessUndecryptablePackets(); } + + private: + QuicConnection* connection_; +}; + +} // namespace + +#define ENDPOINT \ + (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") + +QuicConnection::QuicConnection( + QuicConnectionId connection_id, + QuicSocketAddress initial_peer_address, + QuicConnectionHelperInterface* helper, + QuicAlarmFactory* alarm_factory, + QuicPacketWriter* writer, + bool owns_writer, + Perspective perspective, + const ParsedQuicVersionVector& supported_versions) + : framer_(supported_versions, + helper->GetClock()->ApproximateNow(), + perspective, + connection_id.length()), + current_packet_content_(NO_FRAMES_RECEIVED), + is_current_packet_connectivity_probing_(false), + current_effective_peer_migration_type_(NO_CHANGE), + helper_(helper), + alarm_factory_(alarm_factory), + per_packet_options_(nullptr), + writer_(writer), + owns_writer_(owns_writer), + encryption_level_(ENCRYPTION_NONE), + clock_(helper->GetClock()), + random_generator_(helper->GetRandomGenerator()), + connection_id_(connection_id), + peer_address_(initial_peer_address), + direct_peer_address_(initial_peer_address), + active_effective_peer_migration_type_(NO_CHANGE), + last_packet_decrypted_(false), + last_size_(0), + current_packet_data_(nullptr), + last_decrypted_packet_level_(ENCRYPTION_NONE), + should_last_packet_instigate_acks_(false), + was_last_packet_missing_(false), + max_undecryptable_packets_(0), + max_tracked_packets_(kMaxTrackedPackets), + pending_version_negotiation_packet_(false), + send_ietf_version_negotiation_packet_(false), + save_crypto_packets_as_termination_packets_(false), + idle_timeout_connection_close_behavior_( + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET), + close_connection_after_five_rtos_(false), + received_packet_manager_(&stats_), + ack_queued_(false), + num_retransmittable_packets_received_since_last_ack_sent_(0), + num_packets_received_since_last_ack_sent_(0), + stop_waiting_count_(0), + ack_mode_(GetQuicReloadableFlag(quic_enable_ack_decimation) + ? ACK_DECIMATION + : TCP_ACKING), + ack_decimation_delay_(kAckDecimationDelay), + unlimited_ack_decimation_(false), + fast_ack_after_quiescence_(false), + pending_retransmission_alarm_(false), + defer_send_in_response_to_packets_(false), + ping_timeout_(QuicTime::Delta::FromSeconds(kPingTimeoutSecs)), + retransmittable_on_wire_timeout_(QuicTime::Delta::Infinite()), + arena_(), + ack_alarm_(alarm_factory_->CreateAlarm(arena_.New<AckAlarmDelegate>(this), + &arena_)), + retransmission_alarm_(alarm_factory_->CreateAlarm( + arena_.New<RetransmissionAlarmDelegate>(this), + &arena_)), + send_alarm_( + alarm_factory_->CreateAlarm(arena_.New<SendAlarmDelegate>(this), + &arena_)), + timeout_alarm_( + alarm_factory_->CreateAlarm(arena_.New<TimeoutAlarmDelegate>(this), + &arena_)), + ping_alarm_( + alarm_factory_->CreateAlarm(arena_.New<PingAlarmDelegate>(this), + &arena_)), + mtu_discovery_alarm_(alarm_factory_->CreateAlarm( + arena_.New<MtuDiscoveryAlarmDelegate>(this), + &arena_)), + path_degrading_alarm_(alarm_factory_->CreateAlarm( + arena_.New<PathDegradingAlarmDelegate>(this), + &arena_)), + process_undecryptable_packets_alarm_(alarm_factory_->CreateAlarm( + arena_.New<ProcessUndecryptablePacketsAlarmDelegate>(this), + &arena_)), + visitor_(nullptr), + debug_visitor_(nullptr), + packet_generator_(connection_id_, &framer_, random_generator_, this), + idle_network_timeout_(QuicTime::Delta::Infinite()), + handshake_timeout_(QuicTime::Delta::Infinite()), + time_of_first_packet_sent_after_receiving_( + GetQuicReloadableFlag( + quic_fix_time_of_first_packet_sent_after_receiving) + ? QuicTime::Zero() + : clock_->ApproximateNow()), + time_of_last_received_packet_(clock_->ApproximateNow()), + time_of_previous_received_packet_(QuicTime::Zero()), + sent_packet_manager_( + perspective, + clock_, + &stats_, + GetQuicReloadableFlag(quic_default_to_bbr) ? kBBR : kCubicBytes, + kNack), + version_negotiation_state_(START_NEGOTIATION), + perspective_(perspective), + connected_(true), + can_truncate_connection_ids_(perspective == Perspective::IS_SERVER), + mtu_discovery_target_(0), + mtu_probe_count_(0), + packets_between_mtu_probes_(kPacketsBetweenMtuProbesBase), + next_mtu_probe_at_(kPacketsBetweenMtuProbesBase), + largest_received_packet_size_(0), + write_error_occurred_(false), + no_stop_waiting_frames_(transport_version() > QUIC_VERSION_43), + consecutive_num_packets_with_no_retransmittable_frames_(0), + max_consecutive_num_packets_with_no_retransmittable_frames_( + kMaxConsecutiveNonRetransmittablePackets), + min_received_before_ack_decimation_(kMinReceivedBeforeAckDecimation), + ack_frequency_before_ack_decimation_( + kDefaultRetransmittablePacketsBeforeAck), + fill_up_link_during_probing_(false), + probing_retransmission_pending_(false), + stateless_reset_token_received_(false), + received_stateless_reset_token_(0), + last_control_frame_id_(kInvalidControlFrameId), + is_path_degrading_(false), + processing_ack_frame_(false), + supports_release_time_(false), + release_time_into_future_(QuicTime::Delta::Zero()), + no_version_negotiation_(supported_versions.size() == 1), + fix_termination_packets_( + GetQuicReloadableFlag(quic_fix_termination_packets)), + send_ack_when_on_can_write_(false) { + if (ack_mode_ == ACK_DECIMATION) { + QUIC_RELOADABLE_FLAG_COUNT(quic_enable_ack_decimation); + } + if (perspective_ == Perspective::IS_SERVER && + supported_versions.size() == 1) { + QUIC_RESTART_FLAG_COUNT(quic_no_server_conn_ver_negotiation2); + } + if (packet_generator_.deprecate_ack_bundling_mode()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_deprecate_ack_bundling_mode); + } + if (received_packet_manager_.decide_when_to_send_acks()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_rpm_decides_when_to_send_acks); + } + QUIC_DLOG(INFO) << ENDPOINT + << "Created connection with connection_id: " << connection_id + << " and version: " + << QuicVersionToString(transport_version()); + + QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(connection_id, + transport_version())) + << "QuicConnection: attempted to use connection ID " << connection_id + << " which is invalid with version " + << QuicVersionToString(transport_version()); + + framer_.set_visitor(this); + stats_.connection_creation_time = clock_->ApproximateNow(); + // TODO(ianswett): Supply the NetworkChangeVisitor as a constructor argument + // and make it required non-null, because it's always used. + sent_packet_manager_.SetNetworkChangeVisitor(this); + if (GetQuicRestartFlag(quic_offload_pacing_to_usps2)) { + sent_packet_manager_.SetPacingAlarmGranularity(QuicTime::Delta::Zero()); + release_time_into_future_ = + QuicTime::Delta::FromMilliseconds(kMinReleaseTimeIntoFutureMs); + } + // Allow the packet writer to potentially reduce the packet size to a value + // even smaller than kDefaultMaxPacketSize. + SetMaxPacketLength(perspective_ == Perspective::IS_SERVER + ? kDefaultServerMaxPacketSize + : kDefaultMaxPacketSize); + received_packet_manager_.set_max_ack_ranges(255); + MaybeEnableSessionDecidesWhatToWrite(); + DCHECK(!GetQuicRestartFlag(quic_no_server_conn_ver_negotiation2) || + perspective_ == Perspective::IS_CLIENT || + supported_versions.size() == 1); +} + +QuicConnection::~QuicConnection() { + if (owns_writer_) { + delete writer_; + } + ClearQueuedPackets(); +} + +void QuicConnection::ClearQueuedPackets() { + for (auto it = queued_packets_.begin(); it != queued_packets_.end(); ++it) { + // Delete the buffer before calling ClearSerializedPacket, which sets + // encrypted_buffer to nullptr. + delete[] it->encrypted_buffer; + ClearSerializedPacket(&(*it)); + } + queued_packets_.clear(); +} + +void QuicConnection::SetFromConfig(const QuicConfig& config) { + if (config.negotiated()) { + // Handshake complete, set handshake timeout to Infinite. + SetNetworkTimeouts(QuicTime::Delta::Infinite(), + config.IdleNetworkTimeout()); + if (config.SilentClose()) { + idle_timeout_connection_close_behavior_ = + ConnectionCloseBehavior::SILENT_CLOSE; + } + } else { + SetNetworkTimeouts(config.max_time_before_crypto_handshake(), + config.max_idle_time_before_crypto_handshake()); + } + + sent_packet_manager_.SetFromConfig(config); + if (config.HasReceivedBytesForConnectionId() && + can_truncate_connection_ids_) { + packet_generator_.SetConnectionIdLength( + config.ReceivedBytesForConnectionId()); + } + max_undecryptable_packets_ = config.max_undecryptable_packets(); + + if (config.HasClientSentConnectionOption(kMTUH, perspective_)) { + SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh); + } + if (config.HasClientSentConnectionOption(kMTUL, perspective_)) { + SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeLow); + } + if (debug_visitor_ != nullptr) { + debug_visitor_->OnSetFromConfig(config); + } + if (received_packet_manager_.decide_when_to_send_acks()) { + received_packet_manager_.SetFromConfig(config, perspective_); + } else { + if (GetQuicReloadableFlag(quic_enable_ack_decimation) && + config.HasClientSentConnectionOption(kACD0, perspective_)) { + ack_mode_ = TCP_ACKING; + } + if (config.HasClientSentConnectionOption(kACKD, perspective_)) { + ack_mode_ = ACK_DECIMATION; + } + if (config.HasClientSentConnectionOption(kAKD2, perspective_)) { + ack_mode_ = ACK_DECIMATION_WITH_REORDERING; + } + if (config.HasClientSentConnectionOption(kAKD3, perspective_)) { + ack_mode_ = ACK_DECIMATION; + ack_decimation_delay_ = kShortAckDecimationDelay; + } + if (config.HasClientSentConnectionOption(kAKD4, perspective_)) { + ack_mode_ = ACK_DECIMATION_WITH_REORDERING; + ack_decimation_delay_ = kShortAckDecimationDelay; + } + if (config.HasClientSentConnectionOption(kAKDU, perspective_)) { + unlimited_ack_decimation_ = true; + } + if (config.HasClientSentConnectionOption(kACKQ, perspective_)) { + fast_ack_after_quiescence_ = true; + } + } + if (config.HasClientSentConnectionOption(k5RTO, perspective_)) { + close_connection_after_five_rtos_ = true; + } + if (config.HasClientSentConnectionOption(kNSTP, perspective_)) { + no_stop_waiting_frames_ = true; + } + if (config.HasReceivedStatelessResetToken()) { + stateless_reset_token_received_ = true; + received_stateless_reset_token_ = config.ReceivedStatelessResetToken(); + } + if (GetQuicReloadableFlag(quic_send_timestamps) && + config.HasClientSentConnectionOption(kSTMP, perspective_)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_send_timestamps); + framer_.set_process_timestamps(true); + received_packet_manager_.set_save_timestamps(true); + } + + supports_release_time_ = + writer_ != nullptr && writer_->SupportsReleaseTime() && + !config.HasClientSentConnectionOption(kNPCO, perspective_); + + if (supports_release_time_) { + UpdateReleaseTimeIntoFuture(); + } +} + +void QuicConnection::OnSendConnectionState( + const CachedNetworkParameters& cached_network_params) { + if (debug_visitor_ != nullptr) { + debug_visitor_->OnSendConnectionState(cached_network_params); + } +} + +void QuicConnection::OnReceiveConnectionState( + const CachedNetworkParameters& cached_network_params) { + if (debug_visitor_ != nullptr) { + debug_visitor_->OnReceiveConnectionState(cached_network_params); + } +} + +void QuicConnection::ResumeConnectionState( + const CachedNetworkParameters& cached_network_params, + bool max_bandwidth_resumption) { + sent_packet_manager_.ResumeConnectionState(cached_network_params, + max_bandwidth_resumption); +} + +void QuicConnection::SetMaxPacingRate(QuicBandwidth max_pacing_rate) { + sent_packet_manager_.SetMaxPacingRate(max_pacing_rate); +} + +void QuicConnection::AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + sent_packet_manager_.AdjustNetworkParameters(bandwidth, rtt); +} + +QuicBandwidth QuicConnection::MaxPacingRate() const { + return sent_packet_manager_.MaxPacingRate(); +} + +bool QuicConnection::SelectMutualVersion( + const ParsedQuicVersionVector& available_versions) { + // Try to find the highest mutual version by iterating over supported + // versions, starting with the highest, and breaking out of the loop once we + // find a matching version in the provided available_versions vector. + const ParsedQuicVersionVector& supported_versions = + framer_.supported_versions(); + for (size_t i = 0; i < supported_versions.size(); ++i) { + const ParsedQuicVersion& version = supported_versions[i]; + if (QuicContainsValue(available_versions, version)) { + framer_.set_version(version); + return true; + } + } + + return false; +} + +void QuicConnection::OnError(QuicFramer* framer) { + // Packets that we can not or have not decrypted are dropped. + // TODO(rch): add stats to measure this. + if (!connected_ || last_packet_decrypted_ == false) { + return; + } + CloseConnection(framer->error(), framer->detailed_error(), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +void QuicConnection::OnPacket() { + last_packet_decrypted_ = false; +} + +void QuicConnection::OnPublicResetPacket(const QuicPublicResetPacket& packet) { + // Check that any public reset packet with a different connection ID that was + // routed to this QuicConnection has been redirected before control reaches + // here. (Check for a bug regression.) + DCHECK_EQ(connection_id_, packet.connection_id); + DCHECK_EQ(perspective_, Perspective::IS_CLIENT); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPublicResetPacket(packet); + } + QuicString error_details = "Received public reset."; + if (perspective_ == Perspective::IS_CLIENT && !packet.endpoint_id.empty()) { + QuicStrAppend(&error_details, " From ", packet.endpoint_id, "."); + } + QUIC_DLOG(INFO) << ENDPOINT << error_details; + QUIC_CODE_COUNT(quic_tear_down_local_connection_on_public_reset); + TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details, + ConnectionCloseSource::FROM_PEER); +} + +bool QuicConnection::OnProtocolVersionMismatch( + ParsedQuicVersion received_version, + PacketHeaderFormat form) { + QUIC_DLOG(INFO) << ENDPOINT << "Received packet with mismatched version " + << ParsedQuicVersionToString(received_version); + if (perspective_ == Perspective::IS_CLIENT) { + const QuicString error_details = "Protocol version mismatch."; + QUIC_BUG << ENDPOINT << error_details; + TearDownLocalConnectionState(QUIC_INTERNAL_ERROR, error_details, + ConnectionCloseSource::FROM_SELF); + return false; + } + if (no_version_negotiation_) { + // Drop old packets that were sent by the client before the version was + // negotiated. + return false; + } + DCHECK_NE(version(), received_version); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnProtocolVersionMismatch(received_version); + } + + switch (version_negotiation_state_) { + case START_NEGOTIATION: + if (!framer_.IsSupportedVersion(received_version)) { + SendVersionNegotiationPacket(form != GOOGLE_QUIC_PACKET); + version_negotiation_state_ = NEGOTIATION_IN_PROGRESS; + return false; + } + break; + + case NEGOTIATION_IN_PROGRESS: + if (!framer_.IsSupportedVersion(received_version)) { + SendVersionNegotiationPacket(form != GOOGLE_QUIC_PACKET); + return false; + } + break; + + case NEGOTIATED_VERSION: + // Might be old packets that were sent by the client before the version + // was negotiated. Drop these. + return false; + + default: + DCHECK(false); + } + + // Store the new version. + framer_.set_version(received_version); + framer_.InferPacketHeaderTypeFromVersion(); + + version_negotiation_state_ = NEGOTIATED_VERSION; + visitor_->OnSuccessfulVersionNegotiation(received_version); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnSuccessfulVersionNegotiation(received_version); + } + QUIC_DLOG(INFO) << ENDPOINT << "version negotiated " + << ParsedQuicVersionToString(received_version); + + MaybeEnableSessionDecidesWhatToWrite(); + no_stop_waiting_frames_ = + received_version.transport_version > QUIC_VERSION_43; + + // TODO(satyamshekhar): Store the packet number of this packet and close the + // connection if we ever received a packet with incorrect version and whose + // packet number is greater. + return true; +} + +// Handles version negotiation for client connection. +void QuicConnection::OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) { + // Check that any public reset packet with a different connection ID that was + // routed to this QuicConnection has been redirected before control reaches + // here. (Check for a bug regression.) + DCHECK_EQ(connection_id_, packet.connection_id); + if (perspective_ == Perspective::IS_SERVER) { + const QuicString error_details = + "Server receieved version negotiation packet."; + QUIC_BUG << error_details; + QUIC_CODE_COUNT(quic_tear_down_local_connection_on_version_negotiation); + TearDownLocalConnectionState(QUIC_INTERNAL_ERROR, error_details, + ConnectionCloseSource::FROM_SELF); + return; + } + if (debug_visitor_ != nullptr) { + debug_visitor_->OnVersionNegotiationPacket(packet); + } + + if (version_negotiation_state_ != START_NEGOTIATION) { + // Possibly a duplicate version negotiation packet. + return; + } + + if (QuicContainsValue(packet.versions, version())) { + const QuicString error_details = + "Server already supports client's version and should have accepted the " + "connection."; + QUIC_DLOG(WARNING) << error_details; + TearDownLocalConnectionState(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, + error_details, + ConnectionCloseSource::FROM_SELF); + return; + } + + server_supported_versions_ = packet.versions; + + if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_no_client_conn_ver_negotiation); + CloseConnection( + QUIC_INVALID_VERSION, + QuicStrCat( + "Client may support one of the versions in the server's list, but " + "it's going to close the connection anyway. Supported versions: {", + ParsedQuicVersionVectorToString(framer_.supported_versions()), + "}, peer supported versions: {", + ParsedQuicVersionVectorToString(packet.versions), "}"), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + if (!SelectMutualVersion(packet.versions)) { + CloseConnection( + QUIC_INVALID_VERSION, + QuicStrCat( + "No common version found. Supported versions: {", + ParsedQuicVersionVectorToString(framer_.supported_versions()), + "}, peer supported versions: {", + ParsedQuicVersionVectorToString(packet.versions), "}"), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + QUIC_DLOG(INFO) << ENDPOINT << "Negotiated version: " + << QuicVersionToString(transport_version()); + no_stop_waiting_frames_ = transport_version() > QUIC_VERSION_43; + version_negotiation_state_ = NEGOTIATION_IN_PROGRESS; + RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); +} + +bool QuicConnection::OnUnauthenticatedPublicHeader( + const QuicPacketHeader& header) { + if (header.destination_connection_id == connection_id_) { + return true; + } + + ++stats_.packets_dropped; + QUIC_DLOG(INFO) << ENDPOINT + << "Ignoring packet from unexpected ConnectionId: " + << header.destination_connection_id << " instead of " + << connection_id_; + if (debug_visitor_ != nullptr) { + debug_visitor_->OnIncorrectConnectionId(header.destination_connection_id); + } + // If this is a server, the dispatcher routes each packet to the + // QuicConnection responsible for the packet's connection ID. So if control + // arrives here and this is a server, the dispatcher must be malfunctioning. + DCHECK_NE(Perspective::IS_SERVER, perspective_); + return false; +} + +bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { + if (debug_visitor_ != nullptr) { + debug_visitor_->OnUnauthenticatedHeader(header); + } + + // Check that any public reset packet with a different connection ID that was + // routed to this QuicConnection has been redirected before control reaches + // here. + DCHECK_EQ(connection_id_, header.destination_connection_id); + + if (!packet_generator_.IsPendingPacketEmpty()) { + // Incoming packets may change a queued ACK frame. + const QuicString error_details = + "Pending frames must be serialized before incoming packets are " + "processed."; + QUIC_BUG << error_details << ", received header: " << header; + CloseConnection(QUIC_INTERNAL_ERROR, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + // If this packet has already been seen, or the sender has told us that it + // will not be retransmitted, then stop processing the packet. + if (!received_packet_manager_.IsAwaitingPacket(header.packet_number)) { + if (framer_.IsIetfStatelessResetPacket(header)) { + QuicIetfStatelessResetPacket packet( + header, header.possible_stateless_reset_token); + OnAuthenticatedIetfStatelessResetPacket(packet); + return false; + } + QUIC_DLOG(INFO) << ENDPOINT << "Packet " << header.packet_number + << " no longer being waited for. Discarding."; + if (debug_visitor_ != nullptr) { + debug_visitor_->OnDuplicatePacket(header.packet_number); + } + ++stats_.packets_dropped; + return false; + } + + if (version_negotiation_state_ != NEGOTIATED_VERSION && + perspective_ == Perspective::IS_SERVER) { + if (!header.version_flag) { + // Packets should have the version flag till version negotiation is + // done. + QuicString error_details = + QuicStrCat(ENDPOINT, "Packet ", header.packet_number.ToUint64(), + " without version flag before version negotiated."); + QUIC_DLOG(WARNING) << error_details; + CloseConnection(QUIC_INVALID_VERSION, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } else { + DCHECK_EQ(header.version, version()); + version_negotiation_state_ = NEGOTIATED_VERSION; + framer_.InferPacketHeaderTypeFromVersion(); + visitor_->OnSuccessfulVersionNegotiation(version()); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnSuccessfulVersionNegotiation(version()); + } + } + DCHECK_EQ(NEGOTIATED_VERSION, version_negotiation_state_); + } + + return true; +} + +void QuicConnection::OnDecryptedPacket(EncryptionLevel level) { + last_decrypted_packet_level_ = level; + last_packet_decrypted_ = true; + + // Once the server receives a forward secure packet, the handshake is + // confirmed. + if (level == ENCRYPTION_FORWARD_SECURE && + perspective_ == Perspective::IS_SERVER) { + sent_packet_manager_.SetHandshakeConfirmed(); + if (sent_packet_manager_.unacked_packets().use_uber_loss_algorithm()) { + // This may have changed the retransmission timer, so re-arm it. + SetRetransmissionAlarm(); + } + } +} + +QuicSocketAddress QuicConnection::GetEffectivePeerAddressFromCurrentPacket() + const { + // By default, the connection is not proxied, and the effective peer address + // is the packet's source address, i.e. the direct peer address. + return last_packet_source_address_; +} + +bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPacketHeader(header); + } + + // Will be decremented below if we fall through to return true. + ++stats_.packets_dropped; + + if (!ProcessValidatedPacket(header)) { + return false; + } + + // Initialize the current packet content state. + current_packet_content_ = NO_FRAMES_RECEIVED; + is_current_packet_connectivity_probing_ = false; + current_effective_peer_migration_type_ = NO_CHANGE; + + if (perspective_ == Perspective::IS_CLIENT) { + if (!received_packet_manager_.GetLargestObserved().IsInitialized() || + header.packet_number > received_packet_manager_.GetLargestObserved()) { + // Update peer_address_ and effective_peer_address_ immediately for + // client connections. + direct_peer_address_ = last_packet_source_address_; + effective_peer_address_ = GetEffectivePeerAddressFromCurrentPacket(); + } + } else { + // At server, remember the address change type of effective_peer_address + // in current_effective_peer_migration_type_. But this variable alone + // doesn't necessarily starts a migration. A migration will be started + // later, once the current packet is confirmed to meet the following + // conditions: + // 1) current_effective_peer_migration_type_ is not NO_CHANGE. + // 2) The current packet is not a connectivity probing. + // 3) The current packet is not reordered, i.e. its packet number is the + // largest of this connection so far. + // Once the above conditions are confirmed, a new migration will start + // even if there is an active migration underway. + current_effective_peer_migration_type_ = + QuicUtils::DetermineAddressChangeType( + effective_peer_address_, + GetEffectivePeerAddressFromCurrentPacket()); + + QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE) + << ENDPOINT << "Effective peer's ip:port changed from " + << effective_peer_address_.ToString() << " to " + << GetEffectivePeerAddressFromCurrentPacket().ToString() + << ", active_effective_peer_migration_type is " + << active_effective_peer_migration_type_; + } + + --stats_.packets_dropped; + QUIC_DVLOG(1) << ENDPOINT << "Received packet header: " << header; + last_header_ = header; + // An ack will be sent if a missing retransmittable packet was received; + was_last_packet_missing_ = + received_packet_manager_.IsMissing(last_header_.packet_number); + + // Record packet receipt to populate ack info before processing stream + // frames, since the processing may result in sending a bundled ack. + received_packet_manager_.RecordPacketReceived(last_header_, + time_of_last_received_packet_); + DCHECK(connected_); + return true; +} + +bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { + DCHECK(connected_); + + // Since a stream frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnStreamFrame(frame); + } + if (frame.stream_id != QuicUtils::GetCryptoStreamId(transport_version()) && + last_decrypted_packet_level_ == ENCRYPTION_NONE) { + if (MaybeConsiderAsMemoryCorruption(frame)) { + CloseConnection(QUIC_MAYBE_CORRUPTED_MEMORY, + "Received crypto frame on non crypto stream.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + QUIC_PEER_BUG << ENDPOINT + << "Received an unencrypted data frame: closing connection" + << " packet_number:" << last_header_.packet_number + << " stream_id:" << frame.stream_id << " received_packets:" + << received_packet_manager_.ack_frame(); + CloseConnection(QUIC_UNENCRYPTED_STREAM_DATA, + "Unencrypted stream data seen.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + visitor_->OnStreamFrame(frame); + stats_.stream_bytes_received += frame.data_length; + should_last_packet_instigate_acks_ = true; + return connected_; +} + +bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) { + DCHECK(connected_); + + // Since a CRYPTO frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + visitor_->OnCryptoFrame(frame); + should_last_packet_instigate_acks_ = true; + return connected_; +} + +bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked, + QuicTime::Delta ack_delay_time) { + DCHECK(connected_); + + if (processing_ack_frame_) { + CloseConnection(QUIC_INVALID_ACK_DATA, + "Received a new ack while processing an ack frame.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + // Since an ack frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + QUIC_DVLOG(1) << ENDPOINT + << "OnAckFrameStart, largest_acked: " << largest_acked; + + if (largest_seen_packet_with_ack_.IsInitialized() && + last_header_.packet_number <= largest_seen_packet_with_ack_) { + QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; + return true; + } + + if (!sent_packet_manager_.GetLargestSentPacket().IsInitialized() || + largest_acked > sent_packet_manager_.GetLargestSentPacket()) { + QUIC_DLOG(WARNING) << ENDPOINT + << "Peer's observed unsent packet:" << largest_acked + << " vs " << sent_packet_manager_.GetLargestSentPacket(); + // We got an ack for data we have not sent. + CloseConnection(QUIC_INVALID_ACK_DATA, "Largest observed too high.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + if (!sent_packet_manager_.GetLargestObserved().IsInitialized() || + largest_acked > sent_packet_manager_.GetLargestObserved()) { + visitor_->OnForwardProgressConfirmed(); + } else if (largest_acked < sent_packet_manager_.GetLargestObserved()) { + QUIC_LOG(INFO) << ENDPOINT << "Peer's largest_observed packet decreased:" + << largest_acked << " vs " + << sent_packet_manager_.GetLargestObserved() + << " packet_number:" << last_header_.packet_number + << " largest seen with ack:" << largest_seen_packet_with_ack_ + << " connection_id: " << connection_id_; + // A new ack has a diminished largest_observed value. + // If this was an old packet, we wouldn't even have checked. + CloseConnection(QUIC_INVALID_ACK_DATA, "Largest observed too low.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + processing_ack_frame_ = true; + sent_packet_manager_.OnAckFrameStart(largest_acked, ack_delay_time, + time_of_last_received_packet_); + return true; +} + +bool QuicConnection::OnAckRange(QuicPacketNumber start, QuicPacketNumber end) { + DCHECK(connected_); + QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")"; + + if (largest_seen_packet_with_ack_.IsInitialized() && + last_header_.packet_number <= largest_seen_packet_with_ack_) { + QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; + return true; + } + + sent_packet_manager_.OnAckRange(start, end); + return true; +} + +bool QuicConnection::OnAckTimestamp(QuicPacketNumber packet_number, + QuicTime timestamp) { + DCHECK(connected_); + QUIC_DVLOG(1) << ENDPOINT << "OnAckTimestamp: [" << packet_number << ", " + << timestamp.ToDebuggingValue() << ")"; + + if (largest_seen_packet_with_ack_.IsInitialized() && + last_header_.packet_number <= largest_seen_packet_with_ack_) { + QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; + return true; + } + + sent_packet_manager_.OnAckTimestamp(packet_number, timestamp); + return true; +} + +bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) { + DCHECK(connected_); + QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start; + + if (largest_seen_packet_with_ack_.IsInitialized() && + last_header_.packet_number <= largest_seen_packet_with_ack_) { + QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; + return true; + } + bool acked_new_packet = + sent_packet_manager_.OnAckFrameEnd(time_of_last_received_packet_); + // Cancel the send alarm because new packets likely have been acked, which + // may change the congestion window and/or pacing rate. Canceling the alarm + // causes CanWrite to recalculate the next send time. + if (send_alarm_->IsSet()) { + send_alarm_->Cancel(); + } + if (supports_release_time_) { + // Update pace time into future because smoothed RTT is likely updated. + UpdateReleaseTimeIntoFuture(); + } + largest_seen_packet_with_ack_ = last_header_.packet_number; + // If the incoming ack's packets set expresses missing packets: peer is still + // waiting for a packet lower than a packet that we are no longer planning to + // send. + // If the incoming ack's packets set expresses received packets: peer is still + // acking packets which we never care about. + // Send an ack to raise the high water mark. + PostProcessAfterAckFrame(GetLeastUnacked() > start, acked_new_packet); + processing_ack_frame_ = false; + + return connected_; +} + +bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) { + DCHECK(connected_); + + // Since a stop waiting frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (no_stop_waiting_frames_) { + return true; + } + if (largest_seen_packet_with_stop_waiting_.IsInitialized() && + last_header_.packet_number <= largest_seen_packet_with_stop_waiting_) { + QUIC_DLOG(INFO) << ENDPOINT + << "Received an old stop waiting frame: ignoring"; + return true; + } + + const char* error = ValidateStopWaitingFrame(frame); + if (error != nullptr) { + CloseConnection(QUIC_INVALID_STOP_WAITING_DATA, error, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnStopWaitingFrame(frame); + } + + largest_seen_packet_with_stop_waiting_ = last_header_.packet_number; + received_packet_manager_.DontWaitForPacketsBefore(frame.least_unacked); + return connected_; +} + +bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) { + DCHECK(connected_); + UpdatePacketContent(SECOND_FRAME_IS_PADDING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPaddingFrame(frame); + } + return true; +} + +bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) { + DCHECK(connected_); + UpdatePacketContent(FIRST_FRAME_IS_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPingFrame(frame); + } + should_last_packet_instigate_acks_ = true; + return true; +} + +const char* QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { + if (LargestAcked(incoming_ack) > packet_generator_.packet_number()) { + QUIC_DLOG(WARNING) << ENDPOINT << "Peer's observed unsent packet:" + << LargestAcked(incoming_ack) << " vs " + << packet_generator_.packet_number(); + // We got an error for data we have not sent. Error out. + return "Largest observed too high."; + } + + if (LargestAcked(incoming_ack) < sent_packet_manager_.GetLargestObserved()) { + QUIC_LOG(INFO) << ENDPOINT << "Peer's largest_observed packet decreased:" + << LargestAcked(incoming_ack) << " vs " + << sent_packet_manager_.GetLargestObserved() + << " packet_number:" << last_header_.packet_number + << " largest seen with ack:" << largest_seen_packet_with_ack_ + << " connection_id: " << connection_id_; + // A new ack has a diminished largest_observed value. Error out. + // If this was an old packet, we wouldn't even have checked. + return "Largest observed too low."; + } + + if (!incoming_ack.packets.Empty() && + incoming_ack.packets.Max() != LargestAcked(incoming_ack)) { + QUIC_BUG << ENDPOINT + << "Peer last received packet: " << incoming_ack.packets.Max() + << " which is not equal to largest observed: " + << incoming_ack.largest_acked; + return "Last received packet not equal to largest observed."; + } + + return nullptr; +} + +const char* QuicConnection::ValidateStopWaitingFrame( + const QuicStopWaitingFrame& stop_waiting) { + if (received_packet_manager_.peer_least_packet_awaiting_ack() + .IsInitialized() && + stop_waiting.least_unacked < + received_packet_manager_.peer_least_packet_awaiting_ack()) { + QUIC_DLOG(ERROR) + << ENDPOINT + << "Peer's sent low least_unacked: " << stop_waiting.least_unacked + << " vs " << received_packet_manager_.peer_least_packet_awaiting_ack(); + // We never process old ack frames, so this number should only increase. + return "Least unacked too small."; + } + + if (stop_waiting.least_unacked > last_header_.packet_number) { + QUIC_DLOG(ERROR) << ENDPOINT + << "Peer sent least_unacked:" << stop_waiting.least_unacked + << " greater than the enclosing packet number:" + << last_header_.packet_number; + return "Least unacked too large."; + } + + return nullptr; +} + +bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { + DCHECK(connected_); + + // Since a reset stream frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnRstStreamFrame(frame); + } + QUIC_DLOG(INFO) << ENDPOINT + << "RST_STREAM_FRAME received for stream: " << frame.stream_id + << " with error: " + << QuicRstStreamErrorCodeToString(frame.error_code); + visitor_->OnRstStream(frame); + should_last_packet_instigate_acks_ = true; + return connected_; +} + +bool QuicConnection::OnApplicationCloseFrame( + const QuicApplicationCloseFrame& frame) { + // TODO(fkastenholz): Need to figure out what the right thing is to do with + // this when we get one. Most likely, the correct action is to mimic the + // OnConnectionCloseFrame actions, with possibly an indication to the + // application of the ApplicationClose information. + return true; +} + +bool QuicConnection::OnStopSendingFrame(const QuicStopSendingFrame& frame) { + DCHECK(connected_); + + // Since a reset stream frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnStopSendingFrame(frame); + } + + QUIC_DLOG(INFO) << ENDPOINT << "STOP_SENDING frame received for stream: " + << frame.stream_id + << " with error: " << frame.application_error_code; + + visitor_->OnStopSendingFrame(frame); + return connected_; +} + +bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) { + // Save the path challenge's payload, for later use in generating the + // response. + received_path_challenge_payloads_.push_back(frame.data_buffer); + + // For VERSION 99 we define a "Padded PATH CHALLENGE" to be the same thing + // as a PADDED PING -- it will start a connectivity check and prevent + // connection migration. Insofar as the connectivity check and connection + // migration are concerned, logically the PATH CHALLENGE is the same as the + // PING, so as a stopgap, tell the FSM that determines whether we have a + // Padded PING or not that we received a PING. + UpdatePacketContent(FIRST_FRAME_IS_PING); + should_last_packet_instigate_acks_ = true; + return true; +} + +bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) { + should_last_packet_instigate_acks_ = true; + if (!transmitted_connectivity_probe_payload_ || + *transmitted_connectivity_probe_payload_ != frame.data_buffer) { + // Is not for the probe we sent, ignore it. + return true; + } + // Have received the matching PATH RESPONSE, saved payload no longer valid. + transmitted_connectivity_probe_payload_ = nullptr; + UpdatePacketContent(FIRST_FRAME_IS_PING); + return true; +} + +bool QuicConnection::OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) { + DCHECK(connected_); + + // Since a connection close frame was received, this is not a connectivity + // probe. A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnConnectionCloseFrame(frame); + } + QUIC_DLOG(INFO) << ENDPOINT << "Received ConnectionClose for connection: " + << connection_id() + << ", with error: " << QuicErrorCodeToString(frame.error_code) + << " (" << frame.error_details << ")"; + if (frame.error_code == QUIC_BAD_MULTIPATH_FLAG) { + QUIC_LOG_FIRST_N(ERROR, 10) << "Unexpected QUIC_BAD_MULTIPATH_FLAG error." + << " last_received_header: " << last_header_ + << " encryption_level: " << encryption_level_; + } + TearDownLocalConnectionState(frame.error_code, frame.error_details, + ConnectionCloseSource::FROM_PEER); + return connected_; +} + +bool QuicConnection::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) { + return visitor_->OnMaxStreamIdFrame(frame); +} + +bool QuicConnection::OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& frame) { + return visitor_->OnStreamIdBlockedFrame(frame); +} + +bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { + DCHECK(connected_); + + // Since a go away frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnGoAwayFrame(frame); + } + QUIC_DLOG(INFO) << ENDPOINT << "GOAWAY_FRAME received with last good stream: " + << frame.last_good_stream_id + << " and error: " << QuicErrorCodeToString(frame.error_code) + << " and reason: " << frame.reason_phrase; + + visitor_->OnGoAway(frame); + should_last_packet_instigate_acks_ = true; + return connected_; +} + +bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { + DCHECK(connected_); + + // Since a window update frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnWindowUpdateFrame(frame, time_of_last_received_packet_); + } + QUIC_DLOG(INFO) << ENDPOINT << "WINDOW_UPDATE_FRAME received for stream: " + << frame.stream_id + << " with byte offset: " << frame.byte_offset; + visitor_->OnWindowUpdateFrame(frame); + should_last_packet_instigate_acks_ = true; + return connected_; +} + +bool QuicConnection::OnNewConnectionIdFrame( + const QuicNewConnectionIdFrame& frame) { + return true; +} + +bool QuicConnection::OnRetireConnectionIdFrame( + const QuicRetireConnectionIdFrame& frame) { + return true; +} + +bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) { + return true; +} + +bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) { + DCHECK(connected_); + + // Since a message frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnMessageFrame(frame); + } + visitor_->OnMessageReceived( + QuicStringPiece(frame.data, frame.message_length)); + should_last_packet_instigate_acks_ = true; + return connected_; +} + +bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { + DCHECK(connected_); + + // Since a blocked frame was received, this is not a connectivity probe. + // A probe only contains a PING and full padding. + UpdatePacketContent(NOT_PADDED_PING); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnBlockedFrame(frame); + } + QUIC_DLOG(INFO) << ENDPOINT + << "BLOCKED_FRAME received for stream: " << frame.stream_id; + visitor_->OnBlockedFrame(frame); + stats_.blocked_frames_received++; + should_last_packet_instigate_acks_ = true; + return connected_; +} + +void QuicConnection::OnPacketComplete() { + // Don't do anything if this packet closed the connection. + if (!connected_) { + ClearLastFrames(); + return; + } + + if (IsCurrentPacketConnectivityProbing()) { + ++stats_.num_connectivity_probing_received; + } + + QUIC_DVLOG(1) << ENDPOINT << "Got packet " << last_header_.packet_number + << " for " << last_header_.destination_connection_id; + + QUIC_DLOG_IF(INFO, current_packet_content_ == SECOND_FRAME_IS_PADDING) + << ENDPOINT << "Received a padded PING packet. is_probing: " + << IsCurrentPacketConnectivityProbing(); + + if (perspective_ == Perspective::IS_CLIENT) { + QUIC_DVLOG(1) << ENDPOINT + << "Received a speculative connectivity probing packet for " + << last_header_.destination_connection_id + << " from ip:port: " << last_packet_source_address_.ToString() + << " to ip:port: " + << last_packet_destination_address_.ToString(); + // TODO(zhongyi): change the method name. + visitor_->OnConnectivityProbeReceived(last_packet_destination_address_, + last_packet_source_address_); + } else if (IsCurrentPacketConnectivityProbing()) { + // This node is not a client (is a server) AND the received packet was + // connectivity-probing, send an appropriate response. + QUIC_DVLOG(1) << ENDPOINT << "Received a connectivity probing packet for " + << last_header_.destination_connection_id + << " from ip:port: " << last_packet_source_address_.ToString() + << " to ip:port: " + << last_packet_destination_address_.ToString(); + visitor_->OnConnectivityProbeReceived(last_packet_destination_address_, + last_packet_source_address_); + } else { + // This node is not a client (is a server) AND the received packet was + // NOT connectivity-probing. If the packet had PATH CHALLENGES, send + // appropriate RESPONSE. Then deal with possible peer migration. + if (transport_version() == QUIC_VERSION_99 && + !received_path_challenge_payloads_.empty()) { + // If a PATH CHALLENGE was in a "Padded PING (or PATH CHALLENGE)" + // then it is taken care of above. This handles the case where a PATH + // CHALLENGE appeared someplace else (eg, the peer randomly added a PATH + // CHALLENGE frame to some other packet. + // There was at least one PATH CHALLENGE in the received packet, + // Generate the required PATH RESPONSE. + SendGenericPathProbePacket(nullptr, last_packet_source_address_, + /* is_response= */ true); + } + + if (last_header_.packet_number == + received_packet_manager_.GetLargestObserved()) { + direct_peer_address_ = last_packet_source_address_; + if (current_effective_peer_migration_type_ != NO_CHANGE) { + StartEffectivePeerMigration(current_effective_peer_migration_type_); + } + } + } + + current_effective_peer_migration_type_ = NO_CHANGE; + + // An ack will be sent if a missing retransmittable packet was received; + const bool was_missing = + should_last_packet_instigate_acks_ && was_last_packet_missing_; + + if (received_packet_manager_.decide_when_to_send_acks()) { + received_packet_manager_.MaybeUpdateAckTimeout( + should_last_packet_instigate_acks_, last_header_.packet_number, + time_of_last_received_packet_, clock_->ApproximateNow(), + sent_packet_manager_.GetRttStats(), + sent_packet_manager_.delayed_ack_time()); + } else if (ack_frame_updated()) { + // It's possible the ack frame was sent along with response data, so it + // no longer needs to be sent. + MaybeQueueAck(was_missing); + } + + ClearLastFrames(); + CloseIfTooManyOutstandingSentPackets(); +} + +bool QuicConnection::IsValidStatelessResetToken(QuicUint128 token) const { + return stateless_reset_token_received_ && + token == received_stateless_reset_token_; +} + +void QuicConnection::OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) { + // TODO(fayang): Add OnAuthenticatedIetfStatelessResetPacket to + // debug_visitor_. + const QuicString error_details = "Received stateless reset."; + QUIC_CODE_COUNT(quic_tear_down_local_connection_on_stateless_reset); + TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details, + ConnectionCloseSource::FROM_PEER); +} + +void QuicConnection::MaybeQueueAck(bool was_missing) { + DCHECK(!received_packet_manager_.decide_when_to_send_acks()); + ++num_packets_received_since_last_ack_sent_; + // Determine whether the newly received packet was missing before recording + // the received packet. + if (was_missing) { + // Only ack immediately if an ACK frame was sent with a larger + // largest acked than the newly received packet number. + const QuicPacketNumber largest_sent_largest_acked = + sent_packet_manager_.unacked_packets().largest_sent_largest_acked(); + if (largest_sent_largest_acked.IsInitialized() && + last_header_.packet_number < largest_sent_largest_acked) { + if (packet_generator_.deprecate_ack_bundling_mode()) { + MaybeSetAckAlarmTo(clock_->ApproximateNow()); + } else { + ack_queued_ = true; + } + } + } + + if (should_last_packet_instigate_acks_ && !ack_queued_) { + ++num_retransmittable_packets_received_since_last_ack_sent_; + if (ack_mode_ != TCP_ACKING && + last_header_.packet_number >= + received_packet_manager_.PeerFirstSendingPacketNumber() + + min_received_before_ack_decimation_) { + // Ack up to 10 packets at once unless ack decimation is unlimited. + if (!unlimited_ack_decimation_ && + num_retransmittable_packets_received_since_last_ack_sent_ >= + kMaxRetransmittablePacketsBeforeAck) { + if (packet_generator_.deprecate_ack_bundling_mode()) { + MaybeSetAckAlarmTo(clock_->ApproximateNow()); + } else { + ack_queued_ = true; + } + } else if (ShouldSetAckAlarm()) { + // Wait for the minimum of the ack decimation delay or the delayed ack + // time before sending an ack. + QuicTime::Delta ack_delay = + std::min(sent_packet_manager_.delayed_ack_time(), + sent_packet_manager_.GetRttStats()->min_rtt() * + ack_decimation_delay_); + const QuicTime approximate_now = clock_->ApproximateNow(); + if (fast_ack_after_quiescence_ && + (approximate_now - time_of_previous_received_packet_) > + sent_packet_manager_.GetRttStats()->SmoothedOrInitialRtt()) { + // Ack the first packet out of queiscence faster, because QUIC does + // not pace the first few packets and commonly these may be handshake + // or TLP packets, which we'd like to acknowledge quickly. + ack_delay = QuicTime::Delta::FromMilliseconds(1); + } + ack_alarm_->Set(approximate_now + ack_delay); + } + } else { + // Ack with a timer or every 2 packets by default. + if (num_retransmittable_packets_received_since_last_ack_sent_ >= + ack_frequency_before_ack_decimation_) { + if (packet_generator_.deprecate_ack_bundling_mode()) { + MaybeSetAckAlarmTo(clock_->ApproximateNow()); + } else { + ack_queued_ = true; + } + } else if (ShouldSetAckAlarm()) { + const QuicTime approximate_now = clock_->ApproximateNow(); + if (fast_ack_after_quiescence_ && + (approximate_now - time_of_previous_received_packet_) > + sent_packet_manager_.GetRttStats()->SmoothedOrInitialRtt()) { + // Ack the first packet out of queiscence faster, because QUIC does + // not pace the first few packets and commonly these may be handshake + // or TLP packets, which we'd like to acknowledge quickly. + ack_alarm_->Set(approximate_now + + QuicTime::Delta::FromMilliseconds(1)); + } else { + ack_alarm_->Set(approximate_now + + sent_packet_manager_.delayed_ack_time()); + } + } + } + + // If there are new missing packets to report, send an ack immediately. + if (received_packet_manager_.HasNewMissingPackets()) { + if (ack_mode_ == ACK_DECIMATION_WITH_REORDERING) { + // Wait the minimum of an eighth min_rtt and the existing ack time. + QuicTime ack_time = + clock_->ApproximateNow() + + 0.125 * sent_packet_manager_.GetRttStats()->min_rtt(); + if (ShouldSetAckAlarm() || ack_alarm_->deadline() > ack_time) { + ack_alarm_->Update(ack_time, QuicTime::Delta::Zero()); + } + } else { + if (packet_generator_.deprecate_ack_bundling_mode()) { + MaybeSetAckAlarmTo(clock_->ApproximateNow()); + } else { + ack_queued_ = true; + } + } + } + + if (fast_ack_after_quiescence_) { + time_of_previous_received_packet_ = time_of_last_received_packet_; + } + } + + if (ack_queued_) { + ack_alarm_->Cancel(); + } +} + +void QuicConnection::ClearLastFrames() { + should_last_packet_instigate_acks_ = false; +} + +void QuicConnection::CloseIfTooManyOutstandingSentPackets() { + // This occurs if we don't discard old packets we've seen fast enough. It's + // possible largest observed is less than leaset unacked. + if (sent_packet_manager_.GetLargestObserved().IsInitialized() && + sent_packet_manager_.GetLargestObserved() > + sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_) { + CloseConnection( + QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, + QuicStrCat("More than ", max_tracked_packets_, + " outstanding, least_unacked: ", + sent_packet_manager_.GetLeastUnacked().ToUint64()), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } +} + +const QuicFrame QuicConnection::GetUpdatedAckFrame() { + return received_packet_manager_.GetUpdatedAckFrame(clock_->ApproximateNow()); +} + +void QuicConnection::PopulateStopWaitingFrame( + QuicStopWaitingFrame* stop_waiting) { + stop_waiting->least_unacked = GetLeastUnacked(); +} + +QuicPacketNumber QuicConnection::GetLeastUnacked() const { + return sent_packet_manager_.GetLeastUnacked(); +} + +bool QuicConnection::HandleWriteBlocked() { + if (!writer_->IsWriteBlocked()) { + return false; + } + + visitor_->OnWriteBlocked(); + return true; +} + +void QuicConnection::MaybeSendInResponseToPacket() { + if (!connected_) { + return; + } + + // If the writer is blocked, don't attempt to send packets now or in the send + // alarm. When the writer unblocks, OnCanWrite() will be called for this + // connection to send. + if (HandleWriteBlocked()) { + return; + } + + // Now that we have received an ack, we might be able to send packets which + // are queued locally, or drain streams which are blocked. + if (defer_send_in_response_to_packets_) { + send_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero()); + } else { + WriteAndBundleAcksIfNotBlocked(); + } +} + +void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic) { + pending_version_negotiation_packet_ = true; + send_ietf_version_negotiation_packet_ = ietf_quic; + + if (HandleWriteBlocked()) { + return; + } + + QUIC_DLOG(INFO) << ENDPOINT << "Sending version negotiation packet: {" + << ParsedQuicVersionVectorToString( + framer_.supported_versions()) + << "}, ietf_quic: " << ietf_quic; + std::unique_ptr<QuicEncryptedPacket> version_packet( + packet_generator_.SerializeVersionNegotiationPacket( + ietf_quic, framer_.supported_versions())); + WriteResult result = writer_->WritePacket( + version_packet->data(), version_packet->length(), self_address().host(), + peer_address(), per_packet_options_); + + if (IsWriteError(result.status)) { + OnWriteError(result.error_code); + return; + } + if (IsWriteBlockedStatus(result.status)) { + visitor_->OnWriteBlocked(); + if (result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) { + pending_version_negotiation_packet_ = false; + } + return; + } + + pending_version_negotiation_packet_ = false; +} + +size_t QuicConnection::SendCryptoData(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset) { + if (write_length == 0) { + QUIC_BUG << "Attempt to send empty crypto frame"; + return 0; + } + + ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING); + return packet_generator_.ConsumeCryptoData(level, write_length, offset); +} + +QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state) { + if (state == NO_FIN && write_length == 0) { + QUIC_BUG << "Attempt to send empty stream frame"; + return QuicConsumedData(0, false); + } + + // Opportunistically bundle an ack with every outgoing packet. + // Particularly, we want to bundle with handshake packets since we don't know + // which decrypter will be used on an ack packet following a handshake + // packet (a handshake packet from client to server could result in a REJ or a + // SHLO from the server, leading to two different decrypters at the server.) + ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING); + return packet_generator_.ConsumeData(id, write_length, offset, state); +} + +bool QuicConnection::SendControlFrame(const QuicFrame& frame) { + if (!CanWrite(HAS_RETRANSMITTABLE_DATA) && frame.type != PING_FRAME) { + QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame; + // Do not check congestion window for ping. + return false; + } + ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING); + packet_generator_.AddControlFrame(frame); + if (frame.type == PING_FRAME) { + // Flush PING frame immediately. + packet_generator_.FlushAllQueuedFrames(); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPingSent(); + } + } + if (frame.type == BLOCKED_FRAME) { + stats_.blocked_frames_sent++; + } + return true; +} + +void QuicConnection::OnStreamReset(QuicStreamId id, + QuicRstStreamErrorCode error) { + if (error == QUIC_STREAM_NO_ERROR) { + // All data for streams which are reset with QUIC_STREAM_NO_ERROR must + // be received by the peer. + return; + } + // Flush stream frames of reset stream. + if (packet_generator_.HasPendingStreamFramesOfStream(id)) { + ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING); + packet_generator_.FlushAllQueuedFrames(); + } + + sent_packet_manager_.CancelRetransmissionsForStream(id); + // Remove all queued packets which only contain data for the reset stream. + // TODO(fayang): consider removing this because it should be rarely executed. + auto packet_iterator = queued_packets_.begin(); + while (packet_iterator != queued_packets_.end()) { + QuicFrames* retransmittable_frames = + &packet_iterator->retransmittable_frames; + if (retransmittable_frames->empty()) { + ++packet_iterator; + continue; + } + // NOTE THAT RemoveFramesForStream removes only STREAM frames + // for the specified stream. + RemoveFramesForStream(retransmittable_frames, id); + if (!retransmittable_frames->empty()) { + ++packet_iterator; + continue; + } + delete[] packet_iterator->encrypted_buffer; + ClearSerializedPacket(&(*packet_iterator)); + packet_iterator = queued_packets_.erase(packet_iterator); + } + // TODO(ianswett): Consider checking for 3 RTOs when the last stream is + // cancelled as well. +} + +const QuicConnectionStats& QuicConnection::GetStats() { + const RttStats* rtt_stats = sent_packet_manager_.GetRttStats(); + + // Update rtt and estimated bandwidth. + QuicTime::Delta min_rtt = rtt_stats->min_rtt(); + if (min_rtt.IsZero()) { + // If min RTT has not been set, use initial RTT instead. + min_rtt = rtt_stats->initial_rtt(); + } + stats_.min_rtt_us = min_rtt.ToMicroseconds(); + + QuicTime::Delta srtt = rtt_stats->SmoothedOrInitialRtt(); + stats_.srtt_us = srtt.ToMicroseconds(); + + stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate(); + stats_.max_packet_size = packet_generator_.GetCurrentMaxPacketLength(); + stats_.max_received_packet_size = largest_received_packet_size_; + return stats_; +} + +void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) { + QueueCoalescedPacket(packet); +} + +void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet) { + if (!connected_) { + return; + } + QUIC_BUG_IF(current_packet_data_ != nullptr) + << "ProcessUdpPacket must not be called while processing a packet."; + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPacketReceived(self_address, peer_address, packet); + } + last_size_ = packet.length(); + current_packet_data_ = packet.data(); + + last_packet_destination_address_ = self_address; + last_packet_source_address_ = peer_address; + if (!self_address_.IsInitialized()) { + self_address_ = last_packet_destination_address_; + } + + if (!direct_peer_address_.IsInitialized()) { + direct_peer_address_ = last_packet_source_address_; + } + + if (!effective_peer_address_.IsInitialized()) { + const QuicSocketAddress effective_peer_addr = + GetEffectivePeerAddressFromCurrentPacket(); + + // effective_peer_address_ must be initialized at the beginning of the + // first packet processed(here). If effective_peer_addr is uninitialized, + // just set effective_peer_address_ to the direct peer address. + effective_peer_address_ = effective_peer_addr.IsInitialized() + ? effective_peer_addr + : direct_peer_address_; + } + + stats_.bytes_received += packet.length(); + ++stats_.packets_received; + + // Ensure the time coming from the packet reader is within 2 minutes of now. + if (std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) > + 2 * 60) { + QUIC_BUG << "Packet receipt time:" + << packet.receipt_time().ToDebuggingValue() + << " too far from current time:" + << clock_->ApproximateNow().ToDebuggingValue(); + } + time_of_last_received_packet_ = packet.receipt_time(); + QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: " + << time_of_last_received_packet_.ToDebuggingValue(); + + ScopedPacketFlusher flusher(this, NO_ACK); + if (!framer_.ProcessPacket(packet)) { + // If we are unable to decrypt this packet, it might be + // because the CHLO or SHLO packet was lost. + if (framer_.error() == QUIC_DECRYPTION_FAILURE) { + if (encryption_level_ != ENCRYPTION_FORWARD_SECURE && + undecryptable_packets_.size() < max_undecryptable_packets_) { + QueueUndecryptablePacket(packet); + } else if (debug_visitor_ != nullptr) { + debug_visitor_->OnUndecryptablePacket(); + } + } + QUIC_DVLOG(1) << ENDPOINT + << "Unable to process packet. Last packet processed: " + << last_header_.packet_number; + current_packet_data_ = nullptr; + is_current_packet_connectivity_probing_ = false; + + MaybeProcessCoalescedPackets(); + return; + } + + ++stats_.packets_processed; + + QUIC_DLOG_IF(INFO, active_effective_peer_migration_type_ != NO_CHANGE) + << "sent_packet_manager_.GetLargestObserved() = " + << sent_packet_manager_.GetLargestObserved() + << ", highest_packet_sent_before_effective_peer_migration_ = " + << highest_packet_sent_before_effective_peer_migration_; + if (active_effective_peer_migration_type_ != NO_CHANGE && + sent_packet_manager_.GetLargestObserved().IsInitialized() && + (!highest_packet_sent_before_effective_peer_migration_.IsInitialized() || + sent_packet_manager_.GetLargestObserved() > + highest_packet_sent_before_effective_peer_migration_)) { + if (perspective_ == Perspective::IS_SERVER) { + OnEffectivePeerMigrationValidated(); + } + } + + MaybeProcessCoalescedPackets(); + MaybeProcessUndecryptablePackets(); + MaybeSendInResponseToPacket(); + SetPingAlarm(); + current_packet_data_ = nullptr; + is_current_packet_connectivity_probing_ = false; +} + +void QuicConnection::OnBlockedWriterCanWrite() { + writer_->SetWritable(); + OnCanWrite(); +} + +void QuicConnection::OnCanWrite() { + DCHECK(!writer_->IsWriteBlocked()); + + // Add a flusher to ensure the connection is marked app-limited. + ScopedPacketFlusher flusher(this, NO_ACK); + + WriteQueuedPackets(); + if (received_packet_manager_.decide_when_to_send_acks()) { + const QuicTime ack_timeout = received_packet_manager_.ack_timeout(); + if (ack_timeout.IsInitialized() && + ack_timeout <= clock_->ApproximateNow()) { + // Send an ACK now because either 1) we were write blocked when we last + // tried to send an ACK, or 2) both ack alarm and send alarm were set to + // go off together. + SendAck(); + } + } else if (send_ack_when_on_can_write_) { + // Send an ACK now because either 1) we were write blocked when we last + // tried to send an ACK, or 2) both ack alarm and send alarm were set to go + // off together. + DCHECK(packet_generator_.deprecate_ack_bundling_mode()); + SendAck(); + } + if (!session_decides_what_to_write()) { + WritePendingRetransmissions(); + } + + WriteNewData(); +} + +void QuicConnection::WriteNewData() { + // Sending queued packets may have caused the socket to become write blocked, + // or the congestion manager to prohibit sending. If we've sent everything + // we had queued and we're still not blocked, let the visitor know it can + // write more. + if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { + return; + } + + { + ScopedPacketFlusher flusher(this, SEND_ACK_IF_QUEUED); + visitor_->OnCanWrite(); + } + + // After the visitor writes, it may have caused the socket to become write + // blocked or the congestion manager to prohibit sending, so check again. + if (visitor_->WillingAndAbleToWrite() && !send_alarm_->IsSet() && + CanWrite(HAS_RETRANSMITTABLE_DATA)) { + // We're not write blocked, but some stream didn't write out all of its + // bytes. Register for 'immediate' resumption so we'll keep writing after + // other connections and events have had a chance to use the thread. + send_alarm_->Set(clock_->ApproximateNow()); + } +} + +void QuicConnection::WriteIfNotBlocked() { + if (!HandleWriteBlocked()) { + OnCanWrite(); + } +} + +void QuicConnection::WriteAndBundleAcksIfNotBlocked() { + if (!HandleWriteBlocked()) { + ScopedPacketFlusher flusher(this, SEND_ACK_IF_QUEUED); + WriteIfNotBlocked(); + } +} + +bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { + if (perspective_ == Perspective::IS_SERVER && self_address_.IsInitialized() && + last_packet_destination_address_.IsInitialized() && + self_address_ != last_packet_destination_address_) { + // Allow change between pure IPv4 and equivalent mapped IPv4 address. + if (self_address_.port() != last_packet_destination_address_.port() || + self_address_.host().Normalized() != + last_packet_destination_address_.host().Normalized()) { + if (!visitor_->AllowSelfAddressChange()) { + CloseConnection( + QUIC_ERROR_MIGRATING_ADDRESS, + "Self address migration is not supported at the server.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + } + self_address_ = last_packet_destination_address_; + } + + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + QUIC_RESTART_FLAG_COUNT_N(quic_enable_accept_random_ipn, 2, 2); + // Configured to accept any packet number in range 1...0x7fffffff + // as initial packet number. + if (last_header_.packet_number.IsInitialized()) { + // The last packet's number is not 0. Ensure that this packet + // is reasonably close to where it should be. + if (!Near(header.packet_number, last_header_.packet_number)) { + QUIC_DLOG(INFO) << ENDPOINT << "Packet " << header.packet_number + << " out of bounds. Discarding"; + CloseConnection(QUIC_INVALID_PACKET_HEADER, + "Packet number out of bounds.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + } else { + // The "last packet's number" is 0, meaning that this packet is the first + // one received. Ensure it is in range 1..MaxRandomInitialPacketNumber(), + // inclusive. + if ((header.packet_number > MaxRandomInitialPacketNumber())) { + // packet number is bad. + QUIC_DLOG(INFO) << ENDPOINT << "Initial packet " << header.packet_number + << " out of bounds. Discarding"; + CloseConnection(QUIC_INVALID_PACKET_HEADER, + "Initial packet number out of bounds.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + } + } else { // if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) + // Count those that would have been accepted if FLAGS..random_ipn + // were true -- to detect/diagnose potential issues prior to + // enabling the flag. + if (header.packet_number > + received_packet_manager_.PeerFirstSendingPacketNumber() && + header.packet_number <= MaxRandomInitialPacketNumber()) { + QUIC_CODE_COUNT_N(had_possibly_random_ipn, 2, 2); + } + bool out_of_bound = + last_header_.packet_number.IsInitialized() + ? !Near(header.packet_number, last_header_.packet_number) + : header.packet_number >= + (received_packet_manager_.PeerFirstSendingPacketNumber() + + kMaxPacketGap); + if (out_of_bound) { + QUIC_DLOG(INFO) << ENDPOINT << "Packet " << header.packet_number + << " out of bounds. Discarding"; + QuicStringPiece packet_data = GetCurrentPacket(); + const size_t kMaxPacketLengthInErrorDetails = 64; + CloseConnection( + QUIC_INVALID_PACKET_HEADER, + QuicStrCat("Packet number out of bounds. ", + last_header_.packet_number.IsInitialized() + ? QuicStrCat("last_pkn=", + last_header_.packet_number.ToUint64()) + : "first received packet", + ", current_pkn=", header.packet_number.ToUint64(), + ", current_pkt_len=", packet_data.length(), + ", current_hdr=", + QuicTextUtils::HexEncode( + packet_data.length() > kMaxPacketLengthInErrorDetails + ? QuicStringPiece(packet_data.data(), + kMaxPacketLengthInErrorDetails) + : packet_data)), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + } + + if (version_negotiation_state_ != NEGOTIATED_VERSION) { + if (perspective_ == Perspective::IS_CLIENT) { + DCHECK(!header.version_flag || header.form != GOOGLE_QUIC_PACKET); + if (framer_.transport_version() <= QUIC_VERSION_43) { + // If the client gets a packet without the version flag from the server + // it should stop sending version since the version negotiation is done. + // IETF QUIC stops sending version once encryption level switches to + // forward secure. + packet_generator_.StopSendingVersion(); + } + version_negotiation_state_ = NEGOTIATED_VERSION; + visitor_->OnSuccessfulVersionNegotiation(version()); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnSuccessfulVersionNegotiation(version()); + } + } + } + + if (last_size_ > largest_received_packet_size_) { + largest_received_packet_size_ = last_size_; + } + + if (perspective_ == Perspective::IS_SERVER && + encryption_level_ == ENCRYPTION_NONE && + last_size_ > packet_generator_.GetCurrentMaxPacketLength()) { + SetMaxPacketLength(last_size_); + } + return true; +} + +void QuicConnection::WriteQueuedPackets() { + DCHECK(!writer_->IsWriteBlocked()); + + if (pending_version_negotiation_packet_) { + SendVersionNegotiationPacket(send_ietf_version_negotiation_packet_); + } + + QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsBeforeWrite", + queued_packets_.size(), 1, 1000, 50, ""); + while (!queued_packets_.empty()) { + // WritePacket() can potentially clear all queued packets, so we need to + // save the first queued packet to a local variable before calling it. + SerializedPacket packet(std::move(queued_packets_.front())); + queued_packets_.pop_front(); + + const bool write_result = WritePacket(&packet); + + if (connected_ && !write_result) { + // Write failed but connection is open, re-insert |packet| into the + // front of the queue, it will be retried later. + queued_packets_.emplace_front(std::move(packet)); + break; + } + + delete[] packet.encrypted_buffer; + ClearSerializedPacket(&packet); + if (!connected_) { + DCHECK(queued_packets_.empty()) << "Queued packets should have been " + "cleared while closing connection"; + break; + } + + // Continue to send the next packet in queue. + } +} + +void QuicConnection::WritePendingRetransmissions() { + DCHECK(!session_decides_what_to_write()); + // Keep writing as long as there's a pending retransmission which can be + // written. + while (sent_packet_manager_.HasPendingRetransmissions() && + CanWrite(HAS_RETRANSMITTABLE_DATA)) { + const QuicPendingRetransmission pending = + sent_packet_manager_.NextPendingRetransmission(); + + // Re-packetize the frames with a new packet number for retransmission. + // Retransmitted packets use the same packet number length as the + // original. + // Flush the packet generator before making a new packet. + // TODO(ianswett): Implement ReserializeAllFrames as a separate path that + // does not require the creator to be flushed. + // TODO(fayang): FlushAllQueuedFrames should only be called once, and should + // be moved outside of the loop. Also, CanWrite is not checked after the + // generator is flushed. + { + ScopedPacketFlusher flusher(this, NO_ACK); + packet_generator_.FlushAllQueuedFrames(); + } + DCHECK(!packet_generator_.HasQueuedFrames()); + char buffer[kMaxPacketSize]; + packet_generator_.ReserializeAllFrames(pending, buffer, kMaxPacketSize); + } +} + +void QuicConnection::SendProbingRetransmissions() { + while (sent_packet_manager_.GetSendAlgorithm()->ShouldSendProbingPacket() && + CanWrite(HAS_RETRANSMITTABLE_DATA)) { + const bool can_retransmit = + sent_packet_manager_.MaybeRetransmitOldestPacket( + PROBING_RETRANSMISSION); + if (!can_retransmit) { + QUIC_DVLOG(1) + << "Cannot send probing retransmissions: nothing to retransmit."; + break; + } + + if (!session_decides_what_to_write()) { + DCHECK(sent_packet_manager_.HasPendingRetransmissions()); + WritePendingRetransmissions(); + } + } +} + +void QuicConnection::RetransmitUnackedPackets( + TransmissionType retransmission_type) { + sent_packet_manager_.RetransmitUnackedPackets(retransmission_type); + + WriteIfNotBlocked(); +} + +void QuicConnection::NeuterUnencryptedPackets() { + sent_packet_manager_.NeuterUnencryptedPackets(); + // This may have changed the retransmission timer, so re-arm it. + SetRetransmissionAlarm(); +} + +bool QuicConnection::ShouldGeneratePacket( + HasRetransmittableData retransmittable, + IsHandshake handshake) { + // We should serialize handshake packets immediately to ensure that they + // end up sent at the right encryption level. + if (handshake == IS_HANDSHAKE) { + return true; + } + + return CanWrite(retransmittable); +} + +const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() { + DCHECK(packet_generator_.deprecate_ack_bundling_mode()); + QuicFrames frames; + bool has_pending_ack = false; + if (received_packet_manager_.decide_when_to_send_acks()) { + has_pending_ack = received_packet_manager_.ack_timeout().IsInitialized(); + } else { + has_pending_ack = ack_alarm_->IsSet(); + } + if (!has_pending_ack && stop_waiting_count_ <= 1) { + // No need to send an ACK. + return frames; + } + ResetAckStates(); + + QUIC_DVLOG(1) << ENDPOINT << "Bundle an ACK opportunistically"; + frames.push_back(GetUpdatedAckFrame()); + if (!no_stop_waiting_frames_) { + QuicStopWaitingFrame stop_waiting; + PopulateStopWaitingFrame(&stop_waiting); + frames.push_back(QuicFrame(stop_waiting)); + } + return frames; +} + +bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) { + if (!connected_) { + return false; + } + + if (session_decides_what_to_write() && + sent_packet_manager_.pending_timer_transmission_count() > 0) { + // Force sending the retransmissions for HANDSHAKE, TLP, RTO, PROBING cases. + return true; + } + + if (HandleWriteBlocked()) { + return false; + } + + // Allow acks to be sent immediately. + if (retransmittable == NO_RETRANSMITTABLE_DATA) { + return true; + } + // If the send alarm is set, wait for it to fire. + if (send_alarm_->IsSet()) { + return false; + } + + QuicTime now = clock_->Now(); + QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(now); + if (delay.IsInfinite()) { + send_alarm_->Cancel(); + return false; + } + + // Scheduler requires a delay. + if (!delay.IsZero()) { + if (delay <= release_time_into_future_) { + // Required delay is within pace time into future, send now. + return true; + } + // Cannot send packet now because delay is too far in the future. + send_alarm_->Update(now + delay, QuicTime::Delta::FromMilliseconds(1)); + QUIC_DVLOG(1) << ENDPOINT << "Delaying sending " << delay.ToMilliseconds() + << "ms"; + return false; + } + return true; +} + +bool QuicConnection::WritePacket(SerializedPacket* packet) { + if (ShouldDiscardPacket(*packet)) { + ++stats_.packets_discarded; + return true; + } + if (sent_packet_manager_.GetLargestSentPacket().IsInitialized() && + packet->packet_number < sent_packet_manager_.GetLargestSentPacket()) { + QUIC_BUG << "Attempt to write packet:" << packet->packet_number + << " after:" << sent_packet_manager_.GetLargestSentPacket(); + QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsAtOutOfOrder", + queued_packets_.size(), 1, 1000, 50, ""); + CloseConnection(QUIC_INTERNAL_ERROR, "Packet written out of order.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return true; + } + // Termination packets are encrypted and saved, so don't exit early. + const bool is_termination_packet = IsTerminationPacket(*packet); + if (HandleWriteBlocked() && !is_termination_packet) { + return false; + } + + QuicPacketNumber packet_number = packet->packet_number; + + QuicPacketLength encrypted_length = packet->encrypted_length; + // Termination packets are eventually owned by TimeWaitListManager. + // Others are deleted at the end of this call. + if (is_termination_packet) { + if (termination_packets_ == nullptr) { + termination_packets_.reset( + new std::vector<std::unique_ptr<QuicEncryptedPacket>>); + } + // Copy the buffer so it's owned in the future. + char* buffer_copy = CopyBuffer(*packet); + termination_packets_->emplace_back( + new QuicEncryptedPacket(buffer_copy, encrypted_length, true)); + // This assures we won't try to write *forced* packets when blocked. + // Return true to stop processing. + if (HandleWriteBlocked()) { + return true; + } + } + + DCHECK_LE(encrypted_length, kMaxPacketSize); + DCHECK_LE(encrypted_length, packet_generator_.GetCurrentMaxPacketLength()); + QUIC_DVLOG(1) << ENDPOINT << "Sending packet " << packet_number << " : " + << (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA + ? "data bearing " + : " ack only ") + << ", encryption level: " + << QuicUtils::EncryptionLevelToString(packet->encryption_level) + << ", encrypted length:" << encrypted_length; + QUIC_DVLOG(2) << ENDPOINT << "packet(" << packet_number << "): " << std::endl + << QuicTextUtils::HexDump(QuicStringPiece( + packet->encrypted_buffer, encrypted_length)); + + // Measure the RTT from before the write begins to avoid underestimating the + // min_rtt_, especially in cases where the thread blocks or gets swapped out + // during the WritePacket below. + QuicTime packet_send_time = clock_->Now(); + if (supports_release_time_ && per_packet_options_ != nullptr) { + QuicTime next_release_time = sent_packet_manager_.GetNextReleaseTime(); + QuicTime::Delta release_time_delay = QuicTime::Delta::Zero(); + QuicTime now = packet_send_time; + if (next_release_time > now) { + release_time_delay = next_release_time - now; + // Set packet_send_time to the future to make the RTT estimation accurate. + packet_send_time = next_release_time; + } + per_packet_options_->release_time_delay = release_time_delay; + } + WriteResult result = writer_->WritePacket( + packet->encrypted_buffer, encrypted_length, self_address().host(), + peer_address(), per_packet_options_); + + QUIC_HISTOGRAM_ENUM( + "QuicConnection.WritePacketStatus", result.status, + WRITE_STATUS_NUM_VALUES, + "Status code returned by writer_->WritePacket() in QuicConnection."); + + if (IsWriteBlockedStatus(result.status)) { + // Ensure the writer is still write blocked, otherwise QUIC may continue + // trying to write when it will not be able to. + DCHECK(writer_->IsWriteBlocked()); + visitor_->OnWriteBlocked(); + // If the socket buffers the data, then the packet should not + // be queued and sent again, which would result in an unnecessary + // duplicate packet being sent. The helper must call OnCanWrite + // when the write completes, and OnWriteError if an error occurs. + if (result.status != WRITE_STATUS_BLOCKED_DATA_BUFFERED) { + return false; + } + } + + // In some cases, an MTU probe can cause EMSGSIZE. This indicates that the + // MTU discovery is permanently unsuccessful. + if (IsMsgTooBig(result) && packet->retransmittable_frames.empty() && + packet->encrypted_length > long_term_mtu_) { + mtu_discovery_target_ = 0; + mtu_discovery_alarm_->Cancel(); + // The write failed, but the writer is not blocked, so return true. + return true; + } + + if (IsWriteError(result.status)) { + OnWriteError(result.error_code); + QUIC_LOG_FIRST_N(ERROR, 10) + << ENDPOINT << "failed writing " << encrypted_length + << " bytes from host " << self_address().host().ToString() + << " to address " << peer_address().ToString() << " with error code " + << result.error_code; + return false; + } + + if (debug_visitor_ != nullptr) { + // Pass the write result to the visitor. + debug_visitor_->OnPacketSent(*packet, packet->original_packet_number, + packet->transmission_type, packet_send_time); + } + if (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA) { + if (!is_path_degrading_ && !path_degrading_alarm_->IsSet()) { + // This is the first retransmittable packet on the working path. + // Start the path degrading alarm to detect new path degrading. + SetPathDegradingAlarm(); + } + + if (GetQuicReloadableFlag( + quic_fix_time_of_first_packet_sent_after_receiving)) { + // Update |time_of_first_packet_sent_after_receiving_| if this is the + // first packet sent after the last packet was received. If it were + // updated on every sent packet, then sending into a black hole might + // never timeout. + if (time_of_first_packet_sent_after_receiving_ < + time_of_last_received_packet_) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_fix_time_of_first_packet_sent_after_receiving); + time_of_first_packet_sent_after_receiving_ = packet_send_time; + } + } else { + // Only adjust the last sent time (for the purpose of tracking the idle + // timeout) if this is the first retransmittable packet sent after a + // packet is received. If it were updated on every sent packet, then + // sending into a black hole might never timeout. + if (time_of_first_packet_sent_after_receiving_ <= + time_of_last_received_packet_) { + time_of_first_packet_sent_after_receiving_ = packet_send_time; + } + } + } + + MaybeSetMtuAlarm(packet_number); + QUIC_DVLOG(1) << ENDPOINT << "time we began writing last sent packet: " + << packet_send_time.ToDebuggingValue(); + + bool reset_retransmission_alarm = sent_packet_manager_.OnPacketSent( + packet, packet->original_packet_number, packet_send_time, + packet->transmission_type, IsRetransmittable(*packet)); + + if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) { + SetRetransmissionAlarm(); + } + SetPingAlarm(); + + // The packet number length must be updated after OnPacketSent, because it + // may change the packet number length in packet. + packet_generator_.UpdatePacketNumberLength( + sent_packet_manager_.GetLeastUnacked(), + sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length())); + + stats_.bytes_sent += result.bytes_written; + ++stats_.packets_sent; + if (packet->transmission_type != NOT_RETRANSMISSION) { + stats_.bytes_retransmitted += result.bytes_written; + ++stats_.packets_retransmitted; + } + + return true; +} + +void QuicConnection::FlushPackets() { + if (!connected_) { + return; + } + + if (!writer_->IsBatchMode()) { + return; + } + + if (HandleWriteBlocked()) { + QUIC_DLOG(INFO) << ENDPOINT << "FlushPackets called while blocked."; + return; + } + + WriteResult result = writer_->Flush(); + + if (HandleWriteBlocked()) { + DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status) + << "Unexpected flush result:" << result; + QUIC_DLOG(INFO) << ENDPOINT << "Write blocked in FlushPackets."; + return; + } + + if (IsWriteError(result.status)) { + OnWriteError(result.error_code); + } +} + +bool QuicConnection::IsMsgTooBig(const WriteResult& result) { + return (result.status == WRITE_STATUS_MSG_TOO_BIG) || + (IsWriteError(result.status) && result.error_code == QUIC_EMSGSIZE); +} + +bool QuicConnection::ShouldDiscardPacket(const SerializedPacket& packet) { + if (!connected_) { + QUIC_DLOG(INFO) << ENDPOINT + << "Not sending packet as connection is disconnected."; + return true; + } + + QuicPacketNumber packet_number = packet.packet_number; + if (encryption_level_ == ENCRYPTION_FORWARD_SECURE && + packet.encryption_level == ENCRYPTION_NONE) { + // Drop packets that are NULL encrypted since the peer won't accept them + // anymore. + QUIC_DLOG(INFO) << ENDPOINT + << "Dropping NULL encrypted packet: " << packet_number + << " since the connection is forward secure."; + return true; + } + + return false; +} + +void QuicConnection::OnWriteError(int error_code) { + if (write_error_occurred_) { + // A write error already occurred. The connection is being closed. + return; + } + write_error_occurred_ = true; + + const QuicString error_details = QuicStrCat( + "Write failed with error: ", error_code, " (", strerror(error_code), ")"); + QUIC_LOG_FIRST_N(ERROR, 2) << ENDPOINT << error_details; + switch (error_code) { + case QUIC_EMSGSIZE: + CloseConnection( + QUIC_PACKET_WRITE_ERROR, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK); + break; + default: + // We can't send an error as the socket is presumably borked. + if (transport_version() > QUIC_VERSION_43) { + QUIC_CODE_COUNT(quic_tear_down_local_connection_on_write_error_ietf); + } else { + QUIC_CODE_COUNT( + quic_tear_down_local_connection_on_write_error_non_ietf); + } + TearDownLocalConnectionState(QUIC_PACKET_WRITE_ERROR, error_details, + ConnectionCloseSource::FROM_SELF); + } +} + +char* QuicConnection::GetPacketBuffer() { + return writer_->GetNextWriteLocation(self_address().host(), peer_address()); +} + +void QuicConnection::OnSerializedPacket(SerializedPacket* serialized_packet) { + if (serialized_packet->encrypted_buffer == nullptr) { + // We failed to serialize the packet, so close the connection. + // TearDownLocalConnectionState does not send close packet, so no infinite + // loop here. + // TODO(ianswett): This is actually an internal error, not an + // encryption failure. + if (transport_version() > QUIC_VERSION_43) { + QUIC_CODE_COUNT( + quic_tear_down_local_connection_on_serialized_packet_ietf); + } else { + QUIC_CODE_COUNT( + quic_tear_down_local_connection_on_serialized_packet_non_ietf); + } + TearDownLocalConnectionState( + QUIC_ENCRYPTION_FAILURE, + "Serialized packet does not have an encrypted buffer.", + ConnectionCloseSource::FROM_SELF); + return; + } + + if (serialized_packet->retransmittable_frames.empty() && + !serialized_packet->original_packet_number.IsInitialized()) { + // Increment consecutive_num_packets_with_no_retransmittable_frames_ if + // this packet is a new transmission with no retransmittable frames. + ++consecutive_num_packets_with_no_retransmittable_frames_; + } else { + consecutive_num_packets_with_no_retransmittable_frames_ = 0; + } + SendOrQueuePacket(serialized_packet); +} + +void QuicConnection::OnUnrecoverableError(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) { + // The packet creator or generator encountered an unrecoverable error: tear + // down local connection state immediately. + if (transport_version() > QUIC_VERSION_43) { + QUIC_CODE_COUNT( + quic_tear_down_local_connection_on_unrecoverable_error_ietf); + } else { + QUIC_CODE_COUNT( + quic_tear_down_local_connection_on_unrecoverable_error_non_ietf); + } + TearDownLocalConnectionState(error, error_details, source); +} + +void QuicConnection::OnCongestionChange() { + visitor_->OnCongestionWindowChange(clock_->ApproximateNow()); + + // Uses the connection's smoothed RTT. If zero, uses initial_rtt. + QuicTime::Delta rtt = sent_packet_manager_.GetRttStats()->smoothed_rtt(); + if (rtt.IsZero()) { + rtt = sent_packet_manager_.GetRttStats()->initial_rtt(); + } + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnRttChanged(rtt); + } +} + +void QuicConnection::OnPathMtuIncreased(QuicPacketLength packet_size) { + if (packet_size > max_packet_length()) { + SetMaxPacketLength(packet_size); + } +} + +void QuicConnection::OnHandshakeComplete() { + sent_packet_manager_.SetHandshakeConfirmed(); + if (sent_packet_manager_.unacked_packets().use_uber_loss_algorithm()) { + // This may have changed the retransmission timer, so re-arm it. + SetRetransmissionAlarm(); + } + // The client should immediately ack the SHLO to confirm the handshake is + // complete with the server. + if (perspective_ == Perspective::IS_CLIENT && !ack_queued_ && + ack_frame_updated()) { + ack_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero()); + } +} + +void QuicConnection::SendOrQueuePacket(SerializedPacket* packet) { + // The caller of this function is responsible for checking CanWrite(). + if (packet->encrypted_buffer == nullptr) { + QUIC_BUG << "packet.encrypted_buffer == nullptr in to SendOrQueuePacket"; + return; + } + // If there are already queued packets, queue this one immediately to ensure + // it's written in sequence number order. + if (!queued_packets_.empty() || !WritePacket(packet)) { + // Take ownership of the underlying encrypted packet. + packet->encrypted_buffer = CopyBuffer(*packet); + queued_packets_.push_back(*packet); + packet->retransmittable_frames.clear(); + } + + ClearSerializedPacket(packet); +} + +void QuicConnection::OnPingTimeout() { + if (!retransmission_alarm_->IsSet()) { + visitor_->SendPing(); + } +} + +void QuicConnection::SendAck() { + if (!received_packet_manager_.decide_when_to_send_acks()) { + // When received_packet_manager decides when to send ack, delaying + // ResetAckStates until ACK is successfully flushed. + ResetAckStates(); + } + + if (packet_generator_.deprecate_ack_bundling_mode()) { + QUIC_DVLOG(1) << ENDPOINT << "Sending an ACK proactively"; + QuicFrames frames; + frames.push_back(GetUpdatedAckFrame()); + if (!no_stop_waiting_frames_) { + QuicStopWaitingFrame stop_waiting; + PopulateStopWaitingFrame(&stop_waiting); + frames.push_back(QuicFrame(stop_waiting)); + } + if (received_packet_manager_.decide_when_to_send_acks()) { + if (!packet_generator_.FlushAckFrame(frames)) { + return; + } + ResetAckStates(); + } else { + send_ack_when_on_can_write_ = !packet_generator_.FlushAckFrame(frames); + } + } else { + packet_generator_.SetShouldSendAck(!no_stop_waiting_frames_); + } + if (consecutive_num_packets_with_no_retransmittable_frames_ < + max_consecutive_num_packets_with_no_retransmittable_frames_) { + return; + } + consecutive_num_packets_with_no_retransmittable_frames_ = 0; + if (packet_generator_.HasRetransmittableFrames() || + visitor_->WillingAndAbleToWrite()) { + // There are pending retransmittable frames. + return; + } + + visitor_->OnAckNeedsRetransmittableFrame(); +} + +void QuicConnection::OnPathDegradingTimeout() { + is_path_degrading_ = true; + visitor_->OnPathDegrading(); +} + +void QuicConnection::OnRetransmissionTimeout() { + DCHECK(!sent_packet_manager_.unacked_packets().empty()); + if (close_connection_after_five_rtos_ && + sent_packet_manager_.GetConsecutiveRtoCount() >= 4) { + // Close on the 5th consecutive RTO, so after 4 previous RTOs have occurred. + CloseConnection(QUIC_TOO_MANY_RTOS, "5 consecutive retransmission timeouts", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + sent_packet_manager_.OnRetransmissionTimeout(); + WriteIfNotBlocked(); + + // A write failure can result in the connection being closed, don't attempt to + // write further packets, or to set alarms. + if (!connected_) { + return; + } + + // In the TLP case, the SentPacketManager gives the connection the opportunity + // to send new data before retransmitting. + if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) { + // Send the pending retransmission now that it's been queued. + WriteIfNotBlocked(); + } + + // Ensure the retransmission alarm is always set if there are unacked packets + // and nothing waiting to be sent. + // This happens if the loss algorithm invokes a timer based loss, but the + // packet doesn't need to be retransmitted. + if (!HasQueuedData() && !retransmission_alarm_->IsSet()) { + SetRetransmissionAlarm(); + } +} + +void QuicConnection::SetEncrypter(EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter) { + packet_generator_.SetEncrypter(level, std::move(encrypter)); +} + +void QuicConnection::SetDiversificationNonce( + const DiversificationNonce& nonce) { + DCHECK_EQ(Perspective::IS_SERVER, perspective_); + packet_generator_.SetDiversificationNonce(nonce); +} + +void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) { + if (level != encryption_level_ && packet_generator_.HasQueuedFrames()) { + // Flush all queued frames when encryption level changes. + ScopedPacketFlusher flusher(this, NO_ACK); + packet_generator_.FlushAllQueuedFrames(); + } + encryption_level_ = level; + packet_generator_.set_encryption_level(level); +} + +void QuicConnection::SetDecrypter(EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter) { + framer_.SetDecrypter(level, std::move(decrypter)); + + if (!undecryptable_packets_.empty() && + !process_undecryptable_packets_alarm_->IsSet()) { + process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow()); + } +} + +void QuicConnection::SetAlternativeDecrypter( + EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter, + bool latch_once_used) { + framer_.SetAlternativeDecrypter(level, std::move(decrypter), latch_once_used); + + if (!undecryptable_packets_.empty() && + !process_undecryptable_packets_alarm_->IsSet()) { + process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow()); + } +} + +const QuicDecrypter* QuicConnection::decrypter() const { + return framer_.decrypter(); +} + +const QuicDecrypter* QuicConnection::alternative_decrypter() const { + return framer_.alternative_decrypter(); +} + +void QuicConnection::QueueUndecryptablePacket( + const QuicEncryptedPacket& packet) { + QUIC_DVLOG(1) << ENDPOINT << "Queueing undecryptable packet."; + undecryptable_packets_.push_back(packet.Clone()); +} + +void QuicConnection::MaybeProcessUndecryptablePackets() { + process_undecryptable_packets_alarm_->Cancel(); + + if (undecryptable_packets_.empty() || encryption_level_ == ENCRYPTION_NONE) { + return; + } + + while (connected_ && !undecryptable_packets_.empty()) { + // Making sure there is no pending frames when processing next undecrypted + // packet because the queued ack frame may change. + packet_generator_.FlushAllQueuedFrames(); + if (!connected_) { + return; + } + QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet"; + QuicEncryptedPacket* packet = undecryptable_packets_.front().get(); + if (!framer_.ProcessPacket(*packet) && + framer_.error() == QUIC_DECRYPTION_FAILURE) { + QUIC_DVLOG(1) << ENDPOINT << "Unable to process undecryptable packet..."; + break; + } + QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!"; + ++stats_.packets_processed; + undecryptable_packets_.pop_front(); + } + + // Once forward secure encryption is in use, there will be no + // new keys installed and hence any undecryptable packets will + // never be able to be decrypted. + if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) { + if (debug_visitor_ != nullptr) { + // TODO(rtenneti): perhaps more efficient to pass the number of + // undecryptable packets as the argument to OnUndecryptablePacket so that + // we just need to call OnUndecryptablePacket once? + for (size_t i = 0; i < undecryptable_packets_.size(); ++i) { + debug_visitor_->OnUndecryptablePacket(); + } + } + undecryptable_packets_.clear(); + } +} + +void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) { + QUIC_DVLOG(1) << ENDPOINT << "Queueing coalesced packet."; + coalesced_packets_.push_back(packet.Clone()); +} + +void QuicConnection::MaybeProcessCoalescedPackets() { + bool processed = false; + for (const auto& packet : coalesced_packets_) { + if (!connected_) { + return; + } + + // } + // while (connected_ && !coalesced_packets_.empty()) { + QUIC_DVLOG(1) << ENDPOINT << "Processing coalesced packet"; + // QuicEncryptedPacket* packet = coalesced_packets_.front().get(); + if (framer_.ProcessPacket(*packet)) { + processed = true; + } else { + // If we are unable to decrypt this packet, it might be + // because the CHLO or SHLO packet was lost. + if (framer_.error() == QUIC_DECRYPTION_FAILURE) { + if (encryption_level_ != ENCRYPTION_FORWARD_SECURE && + undecryptable_packets_.size() < max_undecryptable_packets_) { + QueueUndecryptablePacket(*packet); + } else if (debug_visitor_ != nullptr) { + debug_visitor_->OnUndecryptablePacket(); + } + } + } + // coalesced_packets_.pop_front(); + } + coalesced_packets_.clear(); + if (processed) { + MaybeProcessUndecryptablePackets(); + } +} + +void QuicConnection::CloseConnection( + QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseBehavior connection_close_behavior) { + DCHECK(!error_details.empty()); + if (!connected_) { + QUIC_DLOG(INFO) << "Connection is already closed."; + return; + } + + QUIC_DLOG(INFO) << ENDPOINT << "Closing connection: " << connection_id() + << ", with error: " << QuicErrorCodeToString(error) << " (" + << error << "), and details: " << error_details; + + if (connection_close_behavior == + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET) { + SendConnectionClosePacket(error, error_details, SEND_ACK); + } else if (connection_close_behavior == + ConnectionCloseBehavior:: + SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK) { + SendConnectionClosePacket(error, error_details, NO_ACK); + } + + ConnectionCloseSource source = ConnectionCloseSource::FROM_SELF; + if (perspective_ == Perspective::IS_CLIENT && + error == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { + // Regard stateless rejected connection as closed by server. + source = ConnectionCloseSource::FROM_PEER; + } + TearDownLocalConnectionState(error, error_details, source); +} + +void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, + const QuicString& details, + AckBundling ack_mode) { + QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet."; + if (fix_termination_packets_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_termination_packets); + SetDefaultEncryptionLevel(GetConnectionCloseEncryptionLevel()); + } + ClearQueuedPackets(); + ScopedPacketFlusher flusher(this, ack_mode); + if (packet_generator_.deprecate_ack_bundling_mode() && ack_mode == SEND_ACK && + !GetUpdatedAckFrame().ack_frame->packets.Empty()) { + SendAck(); + } + QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(); + frame->error_code = error; + frame->error_details = details; + packet_generator_.AddControlFrame(QuicFrame(frame)); + packet_generator_.FlushAllQueuedFrames(); +} + +void QuicConnection::TearDownLocalConnectionState( + QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) { + if (!connected_) { + QUIC_DLOG(INFO) << "Connection is already closed."; + return; + } + + // If we are using a batch writer, flush packets queued in it, if any. + FlushPackets(); + connected_ = false; + DCHECK(visitor_ != nullptr); + visitor_->OnConnectionClosed(error, error_details, source); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnConnectionClosed(error, error_details, source); + } + // Cancel the alarms so they don't trigger any action now that the + // connection is closed. + CancelAllAlarms(); +} + +void QuicConnection::CancelAllAlarms() { + QUIC_DVLOG(1) << "Cancelling all QuicConnection alarms."; + + ack_alarm_->Cancel(); + ping_alarm_->Cancel(); + retransmission_alarm_->Cancel(); + send_alarm_->Cancel(); + timeout_alarm_->Cancel(); + mtu_discovery_alarm_->Cancel(); + path_degrading_alarm_->Cancel(); +} + +QuicByteCount QuicConnection::max_packet_length() const { + return packet_generator_.GetCurrentMaxPacketLength(); +} + +void QuicConnection::SetMaxPacketLength(QuicByteCount length) { + long_term_mtu_ = length; + packet_generator_.SetMaxPacketLength(GetLimitedMaxPacketSize(length)); +} + +bool QuicConnection::HasQueuedData() const { + return pending_version_negotiation_packet_ || !queued_packets_.empty() || + packet_generator_.HasQueuedFrames(); +} + +void QuicConnection::EnableSavingCryptoPackets() { + save_crypto_packets_as_termination_packets_ = true; +} + +bool QuicConnection::CanWriteStreamData() { + // Don't write stream data if there are negotiation or queued data packets + // to send. Otherwise, continue and bundle as many frames as possible. + if (pending_version_negotiation_packet_ || !queued_packets_.empty()) { + return false; + } + + IsHandshake pending_handshake = + visitor_->HasPendingHandshake() ? IS_HANDSHAKE : NOT_HANDSHAKE; + // Sending queued packets may have caused the socket to become write blocked, + // or the congestion manager to prohibit sending. If we've sent everything + // we had queued and we're still not blocked, let the visitor know it can + // write more. + return ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, pending_handshake); +} + +void QuicConnection::SetNetworkTimeouts(QuicTime::Delta handshake_timeout, + QuicTime::Delta idle_timeout) { + QUIC_BUG_IF(idle_timeout > handshake_timeout) + << "idle_timeout:" << idle_timeout.ToMilliseconds() + << " handshake_timeout:" << handshake_timeout.ToMilliseconds(); + // Adjust the idle timeout on client and server to prevent clients from + // sending requests to servers which have already closed the connection. + if (perspective_ == Perspective::IS_SERVER) { + idle_timeout = idle_timeout + QuicTime::Delta::FromSeconds(3); + } else if (idle_timeout > QuicTime::Delta::FromSeconds(1)) { + idle_timeout = idle_timeout - QuicTime::Delta::FromSeconds(1); + } + handshake_timeout_ = handshake_timeout; + idle_network_timeout_ = idle_timeout; + + SetTimeoutAlarm(); +} + +void QuicConnection::CheckForTimeout() { + QuicTime now = clock_->ApproximateNow(); + QuicTime time_of_last_packet = + std::max(time_of_last_received_packet_, + time_of_first_packet_sent_after_receiving_); + + // |delta| can be < 0 as |now| is approximate time but |time_of_last_packet| + // is accurate time. However, this should not change the behavior of + // timeout handling. + QuicTime::Delta idle_duration = now - time_of_last_packet; + QUIC_DVLOG(1) << ENDPOINT << "last packet " + << time_of_last_packet.ToDebuggingValue() + << " now:" << now.ToDebuggingValue() + << " idle_duration:" << idle_duration.ToMicroseconds() + << " idle_network_timeout: " + << idle_network_timeout_.ToMicroseconds(); + if (idle_duration >= idle_network_timeout_) { + const QuicString error_details = "No recent network activity."; + QUIC_DVLOG(1) << ENDPOINT << error_details; + if ((sent_packet_manager_.GetConsecutiveTlpCount() > 0 || + sent_packet_manager_.GetConsecutiveRtoCount() > 0 || + visitor_->ShouldKeepConnectionAlive())) { + CloseConnection(QUIC_NETWORK_IDLE_TIMEOUT, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } else { + CloseConnection(QUIC_NETWORK_IDLE_TIMEOUT, error_details, + idle_timeout_connection_close_behavior_); + } + return; + } + + if (!handshake_timeout_.IsInfinite()) { + QuicTime::Delta connected_duration = now - stats_.connection_creation_time; + QUIC_DVLOG(1) << ENDPOINT + << "connection time: " << connected_duration.ToMicroseconds() + << " handshake timeout: " + << handshake_timeout_.ToMicroseconds(); + if (connected_duration >= handshake_timeout_) { + const QuicString error_details = "Handshake timeout expired."; + QUIC_DVLOG(1) << ENDPOINT << error_details; + CloseConnection(QUIC_HANDSHAKE_TIMEOUT, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + } + + SetTimeoutAlarm(); +} + +void QuicConnection::SetTimeoutAlarm() { + QuicTime time_of_last_packet = + std::max(time_of_last_received_packet_, + time_of_first_packet_sent_after_receiving_); + + QuicTime deadline = time_of_last_packet + idle_network_timeout_; + if (!handshake_timeout_.IsInfinite()) { + deadline = std::min(deadline, + stats_.connection_creation_time + handshake_timeout_); + } + + timeout_alarm_->Update(deadline, QuicTime::Delta::Zero()); +} + +void QuicConnection::SetPingAlarm() { + if (perspective_ == Perspective::IS_SERVER) { + // Only clients send pings. + return; + } + if (!visitor_->ShouldKeepConnectionAlive()) { + ping_alarm_->Cancel(); + // Don't send a ping unless there are open streams. + return; + } + if (retransmittable_on_wire_timeout_.IsInfinite() || + sent_packet_manager_.HasInFlightPackets()) { + // Extend the ping alarm. + ping_alarm_->Update(clock_->ApproximateNow() + ping_timeout_, + QuicTime::Delta::FromSeconds(1)); + return; + } + DCHECK_LT(retransmittable_on_wire_timeout_, ping_timeout_); + // If it's already set to an earlier time, then don't update it. + if (ping_alarm_->IsSet() && + ping_alarm_->deadline() < + clock_->ApproximateNow() + retransmittable_on_wire_timeout_) { + return; + } + // Use a shorter timeout if there are open streams, but nothing on the wire. + ping_alarm_->Update( + clock_->ApproximateNow() + retransmittable_on_wire_timeout_, + QuicTime::Delta::FromMilliseconds(1)); +} + +void QuicConnection::SetRetransmissionAlarm() { + if (packet_generator_.PacketFlusherAttached()) { + pending_retransmission_alarm_ = true; + return; + } + QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime(); + retransmission_alarm_->Update(retransmission_time, + QuicTime::Delta::FromMilliseconds(1)); +} + +void QuicConnection::SetPathDegradingAlarm() { + if (perspective_ == Perspective::IS_SERVER) { + return; + } + const QuicTime::Delta delay = sent_packet_manager_.GetPathDegradingDelay(); + path_degrading_alarm_->Update(clock_->ApproximateNow() + delay, + QuicTime::Delta::FromMilliseconds(1)); +} + +void QuicConnection::MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number) { + // Do not set the alarm if the target size is less than the current size. + // This covers the case when |mtu_discovery_target_| is at its default value, + // zero. + if (mtu_discovery_target_ <= max_packet_length()) { + return; + } + + if (mtu_probe_count_ >= kMtuDiscoveryAttempts) { + return; + } + + if (mtu_discovery_alarm_->IsSet()) { + return; + } + + if (sent_packet_number >= next_mtu_probe_at_) { + // Use an alarm to send the MTU probe to ensure that no ScopedPacketFlushers + // are active. + mtu_discovery_alarm_->Set(clock_->ApproximateNow()); + } +} + +void QuicConnection::MaybeSetAckAlarmTo(QuicTime time) { + DCHECK(packet_generator_.deprecate_ack_bundling_mode()); + if (!ack_alarm_->IsSet() || ack_alarm_->deadline() > time) { + ack_alarm_->Update(time, QuicTime::Delta::Zero()); + } +} + +QuicConnection::ScopedPacketFlusher::ScopedPacketFlusher( + QuicConnection* connection, + AckBundling ack_mode) + : connection_(connection), + flush_and_set_pending_retransmission_alarm_on_delete_(false) { + if (connection_ == nullptr) { + return; + } + + if (!connection_->packet_generator_.PacketFlusherAttached()) { + flush_and_set_pending_retransmission_alarm_on_delete_ = true; + connection->packet_generator_.AttachPacketFlusher(); + } + if (connection_->packet_generator_.deprecate_ack_bundling_mode()) { + return; + } + + // If caller wants us to include an ack, check the delayed-ack timer to see if + // there's ack info to be sent. + if (ShouldSendAck(ack_mode)) { + if (!connection_->GetUpdatedAckFrame().ack_frame->packets.Empty()) { + QUIC_DVLOG(1) << "Bundling ack with outgoing packet."; + connection_->SendAck(); + } + } +} + +bool QuicConnection::ScopedPacketFlusher::ShouldSendAck( + AckBundling ack_mode) const { + DCHECK(!connection_->packet_generator_.deprecate_ack_bundling_mode()); + // If the ack alarm is set, make sure the ack has been updated. + DCHECK(!connection_->ack_alarm_->IsSet() || connection_->ack_frame_updated()) + << "ack_mode:" << ack_mode; + switch (ack_mode) { + case SEND_ACK: + return true; + case SEND_ACK_IF_QUEUED: + return connection_->ack_queued(); + case SEND_ACK_IF_PENDING: + return connection_->ack_alarm_->IsSet() || + connection_->stop_waiting_count_ > 1; + case NO_ACK: + return false; + default: + QUIC_BUG << "Unsupported ack_mode."; + return true; + } +} + +QuicConnection::ScopedPacketFlusher::~ScopedPacketFlusher() { + if (connection_ == nullptr) { + return; + } + + if (flush_and_set_pending_retransmission_alarm_on_delete_) { + if (connection_->packet_generator_.deprecate_ack_bundling_mode()) { + if (connection_->received_packet_manager_.decide_when_to_send_acks()) { + const QuicTime ack_timeout = + connection_->received_packet_manager_.ack_timeout(); + if (ack_timeout.IsInitialized()) { + if (ack_timeout <= connection_->clock_->ApproximateNow() && + !connection_->CanWrite(NO_RETRANSMITTABLE_DATA)) { + // Cancel ACK alarm if connection is write blocked, and ACK will be + // sent when connection gets unblocked. + connection_->ack_alarm_->Cancel(); + } else { + connection_->MaybeSetAckAlarmTo(ack_timeout); + } + } + } + if (connection_->ack_alarm_->IsSet() && + connection_->ack_alarm_->deadline() <= + connection_->clock_->ApproximateNow()) { + // An ACK needs to be sent right now. This ACK did not get bundled + // because either there was no data to write or packets were marked as + // received after frames were queued in the generator. + if (connection_->send_alarm_->IsSet() && + connection_->send_alarm_->deadline() <= + connection_->clock_->ApproximateNow()) { + // If send alarm will go off soon, let send alarm send the ACK. + connection_->ack_alarm_->Cancel(); + if (!connection_->received_packet_manager_ + .decide_when_to_send_acks()) { + connection_->send_ack_when_on_can_write_ = true; + } + } else { + connection_->SendAck(); + } + } + } + connection_->packet_generator_.Flush(); + connection_->FlushPackets(); + if (connection_->session_decides_what_to_write()) { + // Reset transmission type. + connection_->SetTransmissionType(NOT_RETRANSMISSION); + } + + // Once all transmissions are done, check if there is any outstanding data + // to send and notify the congestion controller if not. + // + // Note that this means that the application limited check will happen as + // soon as the last flusher gets destroyed, which is typically after a + // single stream write is finished. This means that if all the data from a + // single write goes through the connection, the application-limited signal + // will fire even if the caller does a write operation immediately after. + // There are two important approaches to remedy this situation: + // (1) Instantiate ScopedPacketFlusher before performing multiple subsequent + // writes, thus deferring this check until all writes are done. + // (2) Write data in chunks sufficiently large so that they cause the + // connection to be limited by the congestion control. Typically, this + // would mean writing chunks larger than the product of the current + // pacing rate and the pacer granularity. So, for instance, if the + // pacing rate of the connection is 1 Gbps, and the pacer granularity is + // 1 ms, the caller should send at least 125k bytes in order to not + // be marked as application-limited. + connection_->CheckIfApplicationLimited(); + + if (connection_->pending_retransmission_alarm_) { + connection_->SetRetransmissionAlarm(); + connection_->pending_retransmission_alarm_ = false; + } + } + DCHECK_EQ(flush_and_set_pending_retransmission_alarm_on_delete_, + !connection_->packet_generator_.PacketFlusherAttached()); +} + +HasRetransmittableData QuicConnection::IsRetransmittable( + const SerializedPacket& packet) { + // Retransmitted packets retransmittable frames are owned by the unacked + // packet map, but are not present in the serialized packet. + if (packet.transmission_type != NOT_RETRANSMISSION || + !packet.retransmittable_frames.empty()) { + return HAS_RETRANSMITTABLE_DATA; + } else { + return NO_RETRANSMITTABLE_DATA; + } +} + +bool QuicConnection::IsTerminationPacket(const SerializedPacket& packet) { + if (packet.retransmittable_frames.empty()) { + return false; + } + for (const QuicFrame& frame : packet.retransmittable_frames) { + if (frame.type == CONNECTION_CLOSE_FRAME) { + return true; + } + if (save_crypto_packets_as_termination_packets_ && + QuicUtils::IsHandshakeFrame(frame, transport_version())) { + return true; + } + } + return false; +} + +void QuicConnection::SetMtuDiscoveryTarget(QuicByteCount target) { + mtu_discovery_target_ = GetLimitedMaxPacketSize(target); +} + +QuicByteCount QuicConnection::GetLimitedMaxPacketSize( + QuicByteCount suggested_max_packet_size) { + if (!peer_address_.IsInitialized()) { + QUIC_BUG << "Attempted to use a connection without a valid peer address"; + return suggested_max_packet_size; + } + + const QuicByteCount writer_limit = writer_->GetMaxPacketSize(peer_address()); + + QuicByteCount max_packet_size = suggested_max_packet_size; + if (max_packet_size > writer_limit) { + max_packet_size = writer_limit; + } + if (max_packet_size > kMaxPacketSize) { + max_packet_size = kMaxPacketSize; + } + return max_packet_size; +} + +void QuicConnection::SendMtuDiscoveryPacket(QuicByteCount target_mtu) { + // Currently, this limit is ensured by the caller. + DCHECK_EQ(target_mtu, GetLimitedMaxPacketSize(target_mtu)); + + // Send the probe. + packet_generator_.GenerateMtuDiscoveryPacket(target_mtu); +} + +// TODO(zhongyi): change this method to generate a connectivity probing packet +// and let the caller to call writer to write the packet and handle write +// status. +bool QuicConnection::SendConnectivityProbingPacket( + QuicPacketWriter* probing_writer, + const QuicSocketAddress& peer_address) { + return SendGenericPathProbePacket(probing_writer, peer_address, + /* is_response= */ false); +} + +void QuicConnection::SendConnectivityProbingResponsePacket( + const QuicSocketAddress& peer_address) { + SendGenericPathProbePacket(nullptr, peer_address, + /* is_response= */ true); +} + +bool QuicConnection::SendGenericPathProbePacket( + QuicPacketWriter* probing_writer, + const QuicSocketAddress& peer_address, + bool is_response) { + DCHECK(peer_address.IsInitialized()); + if (!connected_) { + QUIC_BUG << "Not sending connectivity probing packet as connection is " + << "disconnected."; + return false; + } + if (perspective_ == Perspective::IS_SERVER && probing_writer == nullptr) { + // Server can use default packet writer to write packet. + probing_writer = writer_; + } + DCHECK(probing_writer); + + if (probing_writer->IsWriteBlocked()) { + QUIC_DLOG(INFO) + << ENDPOINT + << "Writer blocked when sending connectivity probing packet."; + if (probing_writer == writer_) { + // Visitor should not be write blocked if the probing writer is not the + // default packet writer. + visitor_->OnWriteBlocked(); + } + return true; + } + + QUIC_DLOG(INFO) << ENDPOINT + << "Sending path probe packet for connection_id = " + << connection_id_; + + OwningSerializedPacketPointer probing_packet; + if (transport_version() != QUIC_VERSION_99) { + // Non-IETF QUIC, generate a padded ping regardless of whether this is a + // request or a response. + probing_packet = packet_generator_.SerializeConnectivityProbingPacket(); + } else { + if (is_response) { + // Respond using IETF QUIC PATH_RESPONSE frame + if (IsCurrentPacketConnectivityProbing()) { + // Pad the response if the request was a google connectivity probe + // (padded). + probing_packet = + packet_generator_.SerializePathResponseConnectivityProbingPacket( + received_path_challenge_payloads_, /* is_padded = */ true); + received_path_challenge_payloads_.clear(); + } else { + // Do not pad the response if the path challenge was not a google + // connectivity probe. + probing_packet = + packet_generator_.SerializePathResponseConnectivityProbingPacket( + received_path_challenge_payloads_, + /* is_padded = */ false); + received_path_challenge_payloads_.clear(); + } + } else { + // Request using IETF QUIC PATH_CHALLENGE frame + transmitted_connectivity_probe_payload_ = + QuicMakeUnique<QuicPathFrameBuffer>(); + probing_packet = + packet_generator_.SerializePathChallengeConnectivityProbingPacket( + transmitted_connectivity_probe_payload_.get()); + if (!probing_packet) { + transmitted_connectivity_probe_payload_ = nullptr; + } + } + } + + DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA); + + const QuicTime packet_send_time = clock_->Now(); + WriteResult result = probing_writer->WritePacket( + probing_packet->encrypted_buffer, probing_packet->encrypted_length, + self_address().host(), peer_address, per_packet_options_); + + // If using a batch writer and the probing packet is buffered, flush it. + if (probing_writer->IsBatchMode() && result.status == WRITE_STATUS_OK && + result.bytes_written == 0) { + result = probing_writer->Flush(); + } + + if (IsWriteError(result.status)) { + // Write error for any connectivity probe should not affect the connection + // as it is sent on a different path. + QUIC_DLOG(INFO) << ENDPOINT << "Write probing packet failed with error = " + << result.error_code; + return false; + } + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPacketSent( + *probing_packet, probing_packet->original_packet_number, + probing_packet->transmission_type, packet_send_time); + } + + // Call OnPacketSent regardless of the write result. + sent_packet_manager_.OnPacketSent( + probing_packet.get(), probing_packet->original_packet_number, + packet_send_time, probing_packet->transmission_type, + NO_RETRANSMITTABLE_DATA); + + if (IsWriteBlockedStatus(result.status)) { + if (probing_writer == writer_) { + // Visitor should not be write blocked if the probing writer is not the + // default packet writer. + visitor_->OnWriteBlocked(); + } + if (result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) { + QUIC_DLOG(INFO) << ENDPOINT << "Write probing packet blocked"; + } + } + + return true; +} + +void QuicConnection::DiscoverMtu() { + DCHECK(!mtu_discovery_alarm_->IsSet()); + + // Check if the MTU has been already increased. + if (mtu_discovery_target_ <= max_packet_length()) { + return; + } + + // Calculate the packet number of the next probe *before* sending the current + // one. Otherwise, when SendMtuDiscoveryPacket() is called, + // MaybeSetMtuAlarm() will not realize that the probe has been just sent, and + // will reschedule this probe again. + packets_between_mtu_probes_ *= 2; + next_mtu_probe_at_ = sent_packet_manager_.GetLargestSentPacket() + + packets_between_mtu_probes_ + 1; + ++mtu_probe_count_; + + QUIC_DVLOG(2) << "Sending a path MTU discovery packet #" << mtu_probe_count_; + SendMtuDiscoveryPacket(mtu_discovery_target_); + + DCHECK(!mtu_discovery_alarm_->IsSet()); +} + +void QuicConnection::OnEffectivePeerMigrationValidated() { + if (active_effective_peer_migration_type_ == NO_CHANGE) { + QUIC_BUG << "No migration underway."; + return; + } + highest_packet_sent_before_effective_peer_migration_.Clear(); + active_effective_peer_migration_type_ = NO_CHANGE; +} + +void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { + // TODO(fayang): Currently, all peer address change type are allowed. Need to + // add a method ShouldAllowPeerAddressChange(PeerAddressChangeType type) to + // determine whether |type| is allowed. + if (type == NO_CHANGE) { + QUIC_BUG << "EffectivePeerMigration started without address change."; + return; + } + QUIC_DLOG(INFO) << ENDPOINT << "Effective peer's ip:port changed from " + << effective_peer_address_.ToString() << " to " + << GetEffectivePeerAddressFromCurrentPacket().ToString() + << ", address change type is " << type + << ", migrating connection."; + + highest_packet_sent_before_effective_peer_migration_ = + sent_packet_manager_.GetLargestSentPacket(); + effective_peer_address_ = GetEffectivePeerAddressFromCurrentPacket(); + active_effective_peer_migration_type_ = type; + + // TODO(wub): Move these calls to OnEffectivePeerMigrationValidated. + OnConnectionMigration(type); +} + +void QuicConnection::OnConnectionMigration(AddressChangeType addr_change_type) { + visitor_->OnConnectionMigration(addr_change_type); + sent_packet_manager_.OnConnectionMigration(addr_change_type); +} + +bool QuicConnection::IsCurrentPacketConnectivityProbing() const { + return is_current_packet_connectivity_probing_; +} + +bool QuicConnection::ack_frame_updated() const { + return received_packet_manager_.ack_frame_updated(); +} + +QuicStringPiece QuicConnection::GetCurrentPacket() { + if (current_packet_data_ == nullptr) { + return QuicStringPiece(); + } + return QuicStringPiece(current_packet_data_, last_size_); +} + +bool QuicConnection::MaybeConsiderAsMemoryCorruption( + const QuicStreamFrame& frame) { + if (frame.stream_id == QuicUtils::GetCryptoStreamId(transport_version()) || + last_decrypted_packet_level_ != ENCRYPTION_NONE) { + return false; + } + + if (perspective_ == Perspective::IS_SERVER && + frame.data_length >= sizeof(kCHLO) && + strncmp(frame.data_buffer, reinterpret_cast<const char*>(&kCHLO), + sizeof(kCHLO)) == 0) { + return true; + } + + if (perspective_ == Perspective::IS_CLIENT && + frame.data_length >= sizeof(kREJ) && + strncmp(frame.data_buffer, reinterpret_cast<const char*>(&kREJ), + sizeof(kREJ)) == 0) { + return true; + } + + return false; +} + +void QuicConnection::MaybeSendProbingRetransmissions() { + DCHECK(fill_up_link_during_probing_); + + // Don't send probing retransmissions until the handshake has completed. + if (!sent_packet_manager_.handshake_confirmed() || + sent_packet_manager().HasUnackedCryptoPackets()) { + return; + } + + if (probing_retransmission_pending_) { + QUIC_BUG << "MaybeSendProbingRetransmissions is called while another call " + "to it is already in progress"; + return; + } + + probing_retransmission_pending_ = true; + SendProbingRetransmissions(); + probing_retransmission_pending_ = false; +} + +void QuicConnection::CheckIfApplicationLimited() { + if (session_decides_what_to_write() && probing_retransmission_pending_) { + return; + } + + bool application_limited = + queued_packets_.empty() && + !sent_packet_manager_.HasPendingRetransmissions() && + !visitor_->WillingAndAbleToWrite(); + + if (!application_limited) { + return; + } + + if (fill_up_link_during_probing_) { + MaybeSendProbingRetransmissions(); + if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { + return; + } + } + + sent_packet_manager_.OnApplicationLimited(); +} + +void QuicConnection::UpdatePacketContent(PacketContent type) { + if (current_packet_content_ == NOT_PADDED_PING) { + // We have already learned the current packet is not a connectivity + // probing packet. Peer migration should have already been started earlier + // if needed. + return; + } + + if (type == NO_FRAMES_RECEIVED) { + return; + } + + if (type == FIRST_FRAME_IS_PING) { + if (current_packet_content_ == NO_FRAMES_RECEIVED) { + current_packet_content_ = FIRST_FRAME_IS_PING; + return; + } + } + + // In Google QUIC we look for a packet with just a PING and PADDING. + // For IETF QUIC, the packet must consist of just a PATH_CHALLENGE frame, + // followed by PADDING. If the condition is met, mark things as + // connectivity-probing, causing later processing to generate the correct + // response. + if (type == SECOND_FRAME_IS_PADDING && + current_packet_content_ == FIRST_FRAME_IS_PING) { + current_packet_content_ = SECOND_FRAME_IS_PADDING; + if (perspective_ == Perspective::IS_SERVER) { + is_current_packet_connectivity_probing_ = + current_effective_peer_migration_type_ != NO_CHANGE; + } else { + is_current_packet_connectivity_probing_ = + (last_packet_source_address_ != peer_address_) || + (last_packet_destination_address_ != self_address_); + } + return; + } + + current_packet_content_ = NOT_PADDED_PING; + if (received_packet_manager_.GetLargestObserved().IsInitialized() && + last_header_.packet_number == + received_packet_manager_.GetLargestObserved()) { + direct_peer_address_ = last_packet_source_address_; + if (current_effective_peer_migration_type_ != NO_CHANGE) { + // Start effective peer migration immediately when the current packet is + // confirmed not a connectivity probing packet. + StartEffectivePeerMigration(current_effective_peer_migration_type_); + } + } + current_effective_peer_migration_type_ = NO_CHANGE; +} + +void QuicConnection::MaybeEnableSessionDecidesWhatToWrite() { + // Only enable session decides what to write code path for version 42+, + // because it needs the receiver to allow receiving overlapping stream data. + const bool enable_session_decides_what_to_write = + transport_version() > QUIC_VERSION_39; + sent_packet_manager_.SetSessionDecideWhatToWrite( + enable_session_decides_what_to_write); + packet_generator_.SetCanSetTransmissionType( + enable_session_decides_what_to_write); +} + +void QuicConnection::PostProcessAfterAckFrame(bool send_stop_waiting, + bool acked_new_packet) { + if (no_stop_waiting_frames_) { + received_packet_manager_.DontWaitForPacketsBefore( + sent_packet_manager_.largest_packet_peer_knows_is_acked()); + } + // Always reset the retransmission alarm when an ack comes in, since we now + // have a better estimate of the current rtt than when it was set. + SetRetransmissionAlarm(); + MaybeSetPathDegradingAlarm(acked_new_packet); + + // TODO(ianswett): Only increment stop_waiting_count_ if StopWaiting frames + // are sent. + if (send_stop_waiting) { + ++stop_waiting_count_; + } else { + stop_waiting_count_ = 0; + } +} + +void QuicConnection::MaybeSetPathDegradingAlarm(bool acked_new_packet) { + if (!sent_packet_manager_.HasInFlightPackets()) { + // There are no retransmittable packets on the wire, so it's impossible to + // say if the connection has degraded. + path_degrading_alarm_->Cancel(); + } else if (acked_new_packet) { + // A previously-unacked packet has been acked, which means forward progress + // has been made. Unset |is_path_degrading| if the path was considered as + // degrading previously. Set/update the path degrading alarm. + is_path_degrading_ = false; + SetPathDegradingAlarm(); + } +} + +void QuicConnection::SetSessionNotifier( + SessionNotifierInterface* session_notifier) { + sent_packet_manager_.SetSessionNotifier(session_notifier); +} + +void QuicConnection::SetDataProducer( + QuicStreamFrameDataProducer* data_producer) { + framer_.set_data_producer(data_producer); +} + +void QuicConnection::SetTransmissionType(TransmissionType type) { + packet_generator_.SetTransmissionType(type); +} + +bool QuicConnection::session_decides_what_to_write() const { + return sent_packet_manager_.session_decides_what_to_write(); +} + +void QuicConnection::UpdateReleaseTimeIntoFuture() { + DCHECK(supports_release_time_); + + release_time_into_future_ = std::max( + QuicTime::Delta::FromMilliseconds(kMinReleaseTimeIntoFutureMs), + std::min( + QuicTime::Delta::FromMilliseconds( + GetQuicFlag(FLAGS_quic_max_pace_time_into_future_ms)), + sent_packet_manager_.GetRttStats()->SmoothedOrInitialRtt() * + GetQuicFlag(FLAGS_quic_pace_time_into_future_srtt_fraction))); +} + +void QuicConnection::ResetAckStates() { + ack_alarm_->Cancel(); + ack_queued_ = false; + stop_waiting_count_ = 0; + num_retransmittable_packets_received_since_last_ack_sent_ = 0; + num_packets_received_since_last_ack_sent_ = 0; + if (received_packet_manager_.decide_when_to_send_acks()) { + received_packet_manager_.ResetAckStates(); + } +} + +MessageStatus QuicConnection::SendMessage(QuicMessageId message_id, + QuicMemSliceSpan message) { + if (transport_version() <= QUIC_VERSION_44) { + QUIC_BUG << "MESSAGE frame is not supported for version " + << transport_version(); + return MESSAGE_STATUS_UNSUPPORTED; + } + if (message.total_length() > GetLargestMessagePayload()) { + return MESSAGE_STATUS_TOO_LARGE; + } + if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { + return MESSAGE_STATUS_BLOCKED; + } + ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING); + return packet_generator_.AddMessageFrame(message_id, message); +} + +QuicPacketLength QuicConnection::GetLargestMessagePayload() const { + return packet_generator_.GetLargestMessagePayload(); +} + +bool QuicConnection::ShouldSetAckAlarm() const { + DCHECK(ack_frame_updated()); + if (ack_alarm_->IsSet()) { + // ACK alarm has been set. + return false; + } + if (GetQuicReloadableFlag(quic_fix_spurious_ack_alarm) && + packet_generator_.should_send_ack()) { + // If the generator is already configured to send an ACK, then there is no + // need to schedule the ACK alarm. The updated ACK information will be sent + // when the generator flushes. + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_spurious_ack_alarm); + return false; + } + return true; +} + +EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const { + DCHECK(fix_termination_packets_); + if (perspective_ == Perspective::IS_CLIENT) { + return encryption_level_; + } + if (sent_packet_manager_.handshake_confirmed()) { + // A forward secure packet has been received. + QUIC_BUG_IF(encryption_level_ != ENCRYPTION_FORWARD_SECURE); + return ENCRYPTION_FORWARD_SECURE; + } + if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_ZERO_RTT)) { + if (encryption_level_ != ENCRYPTION_ZERO_RTT) { + if (transport_version() > QUIC_VERSION_43) { + QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close_ietf); + } else { + QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close); + } + } + return ENCRYPTION_ZERO_RTT; + } + return ENCRYPTION_NONE; +} + +size_t QuicConnection::min_received_before_ack_decimation() const { + if (received_packet_manager_.decide_when_to_send_acks()) { + return received_packet_manager_.min_received_before_ack_decimation(); + } + return min_received_before_ack_decimation_; +} + +void QuicConnection::set_min_received_before_ack_decimation(size_t new_value) { + if (received_packet_manager_.decide_when_to_send_acks()) { + received_packet_manager_.set_min_received_before_ack_decimation(new_value); + } else { + min_received_before_ack_decimation_ = new_value; + } +} + +size_t QuicConnection::ack_frequency_before_ack_decimation() const { + if (received_packet_manager_.decide_when_to_send_acks()) { + return received_packet_manager_.ack_frequency_before_ack_decimation(); + } + return ack_frequency_before_ack_decimation_; +} + +void QuicConnection::set_ack_frequency_before_ack_decimation(size_t new_value) { + DCHECK_GT(new_value, 0u); + if (received_packet_manager_.decide_when_to_send_acks()) { + received_packet_manager_.set_ack_frequency_before_ack_decimation(new_value); + } else { + ack_frequency_before_ack_decimation_ = new_value; + } +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h new file mode 100644 index 0000000..985e44c --- /dev/null +++ b/quic/core/quic_connection.h
@@ -0,0 +1,1465 @@ +// Copyright (c) 2012 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. + +// The entity that handles framing writes for a Quic client or server. +// Each QuicSession will have a connection associated with it. +// +// On the server side, the Dispatcher handles the raw reads, and hands off +// packets via ProcessUdpPacket for framing and processing. +// +// On the client side, the Connection handles the raw reads, as well as the +// processing. +// +// Note: this class is not thread-safe. + +#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_H_ +#define QUICHE_QUIC_CORE_QUIC_CONNECTION_H_ + +#include <cstddef> +#include <cstdint> +#include <list> +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.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/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_framer.h" +#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_received_packet_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicClock; +class QuicConfig; +class QuicConnection; +class QuicRandom; + +namespace test { +class QuicConnectionPeer; +} // namespace test + +// The initial number of packets between MTU probes. After each attempt the +// number is doubled. +const QuicPacketCount kPacketsBetweenMtuProbesBase = 100; + +// The number of MTU probes that get sent before giving up. +const size_t kMtuDiscoveryAttempts = 3; + +// Ensure that exponential back-off does not result in an integer overflow. +// The number of packets can be potentially capped, but that is not useful at +// current kMtuDiscoveryAttempts value, and hence is not implemented at present. +static_assert(kMtuDiscoveryAttempts + 8 < 8 * sizeof(QuicPacketNumber), + "The number of MTU discovery attempts is too high"); +static_assert(kPacketsBetweenMtuProbesBase < (1 << 8), + "The initial number of packets between MTU probes is too high"); + +// The incresed packet size targeted when doing path MTU discovery. +const QuicByteCount kMtuDiscoveryTargetPacketSizeHigh = 1450; +const QuicByteCount kMtuDiscoveryTargetPacketSizeLow = 1430; + +static_assert(kMtuDiscoveryTargetPacketSizeLow <= kMaxPacketSize, + "MTU discovery target is too large"); +static_assert(kMtuDiscoveryTargetPacketSizeHigh <= kMaxPacketSize, + "MTU discovery target is too large"); + +static_assert(kMtuDiscoveryTargetPacketSizeLow > kDefaultMaxPacketSize, + "MTU discovery target does not exceed the default packet size"); +static_assert(kMtuDiscoveryTargetPacketSizeHigh > kDefaultMaxPacketSize, + "MTU discovery target does not exceed the default packet size"); + +// Class that receives callbacks from the connection when frames are received +// and when other interesting events happen. +class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { + public: + virtual ~QuicConnectionVisitorInterface() {} + + // A simple visitor interface for dealing with a data frame. + virtual void OnStreamFrame(const QuicStreamFrame& frame) = 0; + + // Called when a CRYPTO frame containing handshake data is received. + virtual void OnCryptoFrame(const QuicCryptoFrame& frame) = 0; + + // The session should process the WINDOW_UPDATE frame, adjusting both stream + // and connection level flow control windows. + virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0; + + // A BLOCKED frame indicates the peer is flow control blocked + // on a specified stream. + virtual void OnBlockedFrame(const QuicBlockedFrame& frame) = 0; + + // Called when the stream is reset by the peer. + virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0; + + // Called when the connection is going away according to the peer. + virtual void OnGoAway(const QuicGoAwayFrame& frame) = 0; + + // Called when |message| has been received. + virtual void OnMessageReceived(QuicStringPiece message) = 0; + + // Called when a MAX_STREAM_ID frame has been received from the peer. + virtual bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) = 0; + + // Called when a STREAM_ID_BLOCKED frame has been received from the peer. + virtual bool OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& frame) = 0; + + // Called when the connection is closed either locally by the framer, or + // remotely by the peer. + virtual void OnConnectionClosed(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) = 0; + + // Called when the connection failed to write because the socket was blocked. + virtual void OnWriteBlocked() = 0; + + // Called once a specific QUIC version is agreed by both endpoints. + virtual void OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) = 0; + + // Called when a connectivity probe has been received by the connection. + virtual void OnConnectivityProbeReceived( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address) = 0; + + // Called when a blocked socket becomes writable. + virtual void OnCanWrite() = 0; + + // Called when the connection experiences a change in congestion window. + virtual void OnCongestionWindowChange(QuicTime now) = 0; + + // Called when the connection receives a packet from a migrated client. + virtual void OnConnectionMigration(AddressChangeType type) = 0; + + // Called when the peer seems unreachable over the current path. + virtual void OnPathDegrading() = 0; + + // Called when the connection sends ack after + // max_consecutive_num_packets_with_no_retransmittable_frames_ consecutive not + // retransmittable packets sent. To instigate an ack from peer, a + // retransmittable frame needs to be added. + virtual void OnAckNeedsRetransmittableFrame() = 0; + + // Called when a ping needs to be sent. + virtual void SendPing() = 0; + + // Called to ask if the visitor wants to schedule write resumption as it both + // has pending data to write, and is able to write (e.g. based on flow control + // limits). + // Writes may be pending because they were write-blocked, congestion-throttled + // or yielded to other connections. + virtual bool WillingAndAbleToWrite() const = 0; + + // Called to ask if any handshake messages are pending in this visitor. + virtual bool HasPendingHandshake() const = 0; + + // Called to ask if the connection should be kept alive and prevented + // from timing out, for example if there are outstanding application + // transactions expecting a response. + virtual bool ShouldKeepConnectionAlive() const = 0; + + // Called when a self address change is observed. Returns true if self address + // change is allowed. + virtual bool AllowSelfAddressChange() const = 0; + + // Called when an ACK is received with a larger |largest_acked| than + // previously observed. + virtual void OnForwardProgressConfirmed() = 0; + + // Called when a STOP_SENDING frame has been received. + virtual bool OnStopSendingFrame(const QuicStopSendingFrame& frame) = 0; +}; + +// Interface which gets callbacks from the QuicConnection at interesting +// points. Implementations must not mutate the state of the connection +// as a result of these callbacks. +class QUIC_EXPORT_PRIVATE QuicConnectionDebugVisitor + : public QuicSentPacketManager::DebugDelegate { + public: + ~QuicConnectionDebugVisitor() override {} + + // Called when a packet has been sent. + virtual void OnPacketSent(const SerializedPacket& serialized_packet, + QuicPacketNumber original_packet_number, + TransmissionType transmission_type, + QuicTime sent_time) {} + + // Called when a PING frame has been sent. + virtual void OnPingSent() {} + + // Called when a packet has been received, but before it is + // validated or parsed. + virtual void OnPacketReceived(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicEncryptedPacket& packet) {} + + // Called when the unauthenticated portion of the header has been parsed. + virtual void OnUnauthenticatedHeader(const QuicPacketHeader& header) {} + + // Called when a packet is received with a connection id that does not + // match the ID of this connection. + virtual void OnIncorrectConnectionId(QuicConnectionId connection_id) {} + + // Called when an undecryptable packet has been received. + virtual void OnUndecryptablePacket() {} + + // Called when a duplicate packet has been received. + virtual void OnDuplicatePacket(QuicPacketNumber packet_number) {} + + // Called when the protocol version on the received packet doensn't match + // current protocol version of the connection. + virtual void OnProtocolVersionMismatch(ParsedQuicVersion version) {} + + // Called when the complete header of a packet has been parsed. + virtual void OnPacketHeader(const QuicPacketHeader& header) {} + + // Called when a StreamFrame has been parsed. + virtual void OnStreamFrame(const QuicStreamFrame& frame) {} + + // Called when a StopWaitingFrame has been parsed. + virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {} + + // Called when a QuicPaddingFrame has been parsed. + virtual void OnPaddingFrame(const QuicPaddingFrame& frame) {} + + // Called when a Ping has been parsed. + virtual void OnPingFrame(const QuicPingFrame& frame) {} + + // Called when a GoAway has been parsed. + virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {} + + // Called when a RstStreamFrame has been parsed. + virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) {} + + // Called when a ConnectionCloseFrame has been parsed. + virtual void OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) {} + + // Called when an ApplicationCloseFrame has been parsed. + virtual void OnApplicationCloseFrame(const QuicApplicationCloseFrame& frame) { + } + + // Called when a WindowUpdate has been parsed. + virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + const QuicTime& receive_time) {} + + // Called when a BlockedFrame has been parsed. + virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {} + + // Called when a MessageFrame has been parsed. + virtual void OnMessageFrame(const QuicMessageFrame& frame) {} + + // Called when a public reset packet has been received. + virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {} + + // Called when a version negotiation packet has been received. + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) {} + + // Called when the connection is closed. + virtual void OnConnectionClosed(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) {} + + // Called when the version negotiation is successful. + virtual void OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) {} + + // Called when a CachedNetworkParameters is sent to the client. + virtual void OnSendConnectionState( + const CachedNetworkParameters& cached_network_params) {} + + // Called when a CachedNetworkParameters are received from the client. + virtual void OnReceiveConnectionState( + const CachedNetworkParameters& cached_network_params) {} + + // Called when the connection parameters are set from the supplied + // |config|. + virtual void OnSetFromConfig(const QuicConfig& config) {} + + // Called when RTT may have changed, including when an RTT is read from + // the config. + virtual void OnRttChanged(QuicTime::Delta rtt) const {} + + // Called when a StopSendingFrame has been parsed. + virtual void OnStopSendingFrame(const QuicStopSendingFrame& frame) {} +}; + +class QUIC_EXPORT_PRIVATE QuicConnectionHelperInterface { + public: + virtual ~QuicConnectionHelperInterface() {} + + // Returns a QuicClock to be used for all time related functions. + virtual const QuicClock* GetClock() const = 0; + + // Returns a QuicRandom to be used for all random number related functions. + virtual QuicRandom* GetRandomGenerator() = 0; + + // Returns a QuicBufferAllocator to be used for stream send buffers. + virtual QuicBufferAllocator* GetStreamSendBufferAllocator() = 0; +}; + +class QUIC_EXPORT_PRIVATE QuicConnection + : public QuicFramerVisitorInterface, + public QuicBlockedWriterInterface, + public QuicPacketGenerator::DelegateInterface, + public QuicSentPacketManager::NetworkChangeVisitor { + public: + // TODO(fayang): Remove this enum when deprecating + // quic_deprecate_ack_bundling_mode. + enum AckBundling { + // Send an ack if it's already queued in the connection. + SEND_ACK_IF_QUEUED, + // Always send an ack. + SEND_ACK, + // Bundle an ack with outgoing data. + SEND_ACK_IF_PENDING, + // Do not send ack. + NO_ACK, + }; + + // Constructs a new QuicConnection for |connection_id| and + // |initial_peer_address| using |writer| to write packets. |owns_writer| + // specifies whether the connection takes ownership of |writer|. |helper| must + // outlive this connection. + QuicConnection(QuicConnectionId connection_id, + QuicSocketAddress initial_peer_address, + QuicConnectionHelperInterface* helper, + QuicAlarmFactory* alarm_factory, + QuicPacketWriter* writer, + bool owns_writer, + Perspective perspective, + const ParsedQuicVersionVector& supported_versions); + QuicConnection(const QuicConnection&) = delete; + QuicConnection& operator=(const QuicConnection&) = delete; + ~QuicConnection() override; + + // Sets connection parameters from the supplied |config|. + void SetFromConfig(const QuicConfig& config); + + // Called by the session when sending connection state to the client. + virtual void OnSendConnectionState( + const CachedNetworkParameters& cached_network_params); + + // Called by the session when receiving connection state from the client. + virtual void OnReceiveConnectionState( + const CachedNetworkParameters& cached_network_params); + + // Called by the Session when the client has provided CachedNetworkParameters. + virtual void ResumeConnectionState( + const CachedNetworkParameters& cached_network_params, + bool max_bandwidth_resumption); + + // Called by the Session when a max pacing rate for the connection is needed. + virtual void SetMaxPacingRate(QuicBandwidth max_pacing_rate); + + // Allows the client to adjust network parameters based on external + // information. + void AdjustNetworkParameters(QuicBandwidth bandwidth, QuicTime::Delta rtt); + + // Returns the max pacing rate for the connection. + virtual QuicBandwidth MaxPacingRate() const; + + // Sends crypto handshake messages of length |write_length| to the peer in as + // few packets as possible. Returns the number of bytes consumed from the + // data. + virtual size_t SendCryptoData(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset); + + // Send the data of length |write_length| to the peer in as few packets as + // possible. Returns the number of bytes consumed from data, and a boolean + // indicating if the fin bit was consumed. This does not indicate the data + // has been sent on the wire: it may have been turned into a packet and queued + // if the socket was unexpectedly blocked. + virtual QuicConsumedData SendStreamData(QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state); + + // Send |frame| to the peer. Returns true if frame is consumed, false + // otherwise. + virtual bool SendControlFrame(const QuicFrame& frame); + + // Called when stream |id| is reset because of |error|. + virtual void OnStreamReset(QuicStreamId id, QuicRstStreamErrorCode error); + + // Closes the connection. + // |connection_close_behavior| determines whether or not a connection close + // packet is sent to the peer. + virtual void CloseConnection( + QuicErrorCode error, + const QuicString& details, + ConnectionCloseBehavior connection_close_behavior); + + // Returns statistics tracked for this connection. + const QuicConnectionStats& GetStats(); + + // Processes an incoming UDP packet (consisting of a QuicEncryptedPacket) from + // the peer. + // In a client, the packet may be "stray" and have a different connection ID + // than that of this connection. + virtual void ProcessUdpPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet); + + // QuicBlockedWriterInterface + // Called when the underlying connection becomes writable to allow queued + // writes to happen. + void OnBlockedWriterCanWrite() override; + + bool IsWriterBlocked() const override { + return writer_ != nullptr && writer_->IsWriteBlocked(); + } + + // Called when the caller thinks it's worth a try to write. + virtual void OnCanWrite(); + + // Called when an error occurs while attempting to write a packet to the + // network. + void OnWriteError(int error_code); + + // Whether |result| represents a MSG TOO BIG write error. + bool IsMsgTooBig(const WriteResult& result); + + // If the socket is not blocked, writes queued packets. + void WriteIfNotBlocked(); + + // If the socket is not blocked, writes queued packets and bundles any pending + // ACKs. + void WriteAndBundleAcksIfNotBlocked(); + + // Set the packet writer. + void SetQuicPacketWriter(QuicPacketWriter* writer, bool owns_writer) { + DCHECK(writer != nullptr); + if (writer_ != nullptr && owns_writer_) { + delete writer_; + } + writer_ = writer; + owns_writer_ = owns_writer; + } + + // Set self address. + void SetSelfAddress(QuicSocketAddress address) { self_address_ = address; } + + // The version of the protocol this connection is using. + QuicTransportVersion transport_version() const { + return framer_.transport_version(); + } + + ParsedQuicVersion version() const { return framer_.version(); } + + // The versions of the protocol that this connection supports. + const ParsedQuicVersionVector& supported_versions() const { + return framer_.supported_versions(); + } + + // From QuicFramerVisitorInterface + void OnError(QuicFramer* framer) override; + bool OnProtocolVersionMismatch(ParsedQuicVersion received_version, + PacketHeaderFormat form) override; + void OnPacket() override; + void OnPublicResetPacket(const QuicPublicResetPacket& packet) override; + void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) 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; + 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 OnPaddingFrame(const QuicPaddingFrame& frame) override; + bool OnPingFrame(const QuicPingFrame& frame) override; + bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override; + bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override; + bool OnApplicationCloseFrame(const QuicApplicationCloseFrame& 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 OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override; + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override; + bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override; + bool OnBlockedFrame(const QuicBlockedFrame& frame) override; + bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override; + bool OnRetireConnectionIdFrame( + const QuicRetireConnectionIdFrame& frame) override; + bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override; + bool OnMessageFrame(const QuicMessageFrame& frame) override; + void OnPacketComplete() override; + bool IsValidStatelessResetToken(QuicUint128 token) const override; + void OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) override; + + // QuicConnectionCloseDelegateInterface + void OnUnrecoverableError(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) override; + + // QuicPacketGenerator::DelegateInterface + bool ShouldGeneratePacket(HasRetransmittableData retransmittable, + IsHandshake handshake) override; + const QuicFrames MaybeBundleAckOpportunistically() override; + const QuicFrame GetUpdatedAckFrame() override; + void PopulateStopWaitingFrame(QuicStopWaitingFrame* stop_waiting) override; + + // QuicPacketCreator::DelegateInterface + char* GetPacketBuffer() override; + void OnSerializedPacket(SerializedPacket* packet) override; + + // QuicSentPacketManager::NetworkChangeVisitor + void OnCongestionChange() override; + void OnPathMtuIncreased(QuicPacketLength packet_size) override; + + // Called by the crypto stream when the handshake completes. In the server's + // case this is when the SHLO has been ACKed. Clients call this on receipt of + // the SHLO. + void OnHandshakeComplete(); + + // Accessors + void set_visitor(QuicConnectionVisitorInterface* visitor) { + visitor_ = visitor; + } + void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) { + debug_visitor_ = debug_visitor; + sent_packet_manager_.SetDebugDelegate(debug_visitor); + } + // Used in Chromium, but not internally. + // Must only be called before ping_alarm_ is set. + void set_ping_timeout(QuicTime::Delta ping_timeout) { + DCHECK(!ping_alarm_->IsSet()); + ping_timeout_ = ping_timeout; + } + const QuicTime::Delta ping_timeout() { return ping_timeout_; } + // Used in Chromium, but not internally. + // Sets a timeout for the ping alarm when there is no retransmittable data + // in flight, allowing for a more aggressive ping alarm in that case. + void set_retransmittable_on_wire_timeout( + QuicTime::Delta retransmittable_on_wire_timeout) { + DCHECK(!ping_alarm_->IsSet()); + retransmittable_on_wire_timeout_ = retransmittable_on_wire_timeout; + } + const QuicTime::Delta retransmittable_on_wire_timeout() { + return retransmittable_on_wire_timeout_; + } + // Used in Chromium, but not internally. + void set_creator_debug_delegate(QuicPacketCreator::DebugDelegate* visitor) { + packet_generator_.set_debug_delegate(visitor); + } + const QuicSocketAddress& self_address() const { return self_address_; } + const QuicSocketAddress& peer_address() const { return direct_peer_address_; } + const QuicSocketAddress& effective_peer_address() const { + return effective_peer_address_; + } + QuicConnectionId connection_id() const { return connection_id_; } + const QuicClock* clock() const { return clock_; } + QuicRandom* random_generator() const { return random_generator_; } + QuicByteCount max_packet_length() const; + void SetMaxPacketLength(QuicByteCount length); + + size_t mtu_probe_count() const { return mtu_probe_count_; } + + bool connected() const { return connected_; } + + // Must only be called on client connections. + const ParsedQuicVersionVector& server_supported_versions() const { + DCHECK_EQ(Perspective::IS_CLIENT, perspective_); + return server_supported_versions_; + } + + // Testing only. + size_t NumQueuedPackets() const { return queued_packets_.size(); } + + // Once called, any sent crypto packets to be saved as the + // termination packet, for use with stateless rejections. + void EnableSavingCryptoPackets(); + + // Returns true if the underlying UDP socket is writable, there is + // no queued data and the connection is not congestion-control + // blocked. + bool CanWriteStreamData(); + + // Returns true if the connection has queued packets or frames. + bool HasQueuedData() const; + + // Sets the handshake and idle state connection timeouts. + void SetNetworkTimeouts(QuicTime::Delta handshake_timeout, + QuicTime::Delta idle_timeout); + + // If the connection has timed out, this will close the connection. + // Otherwise, it will reschedule the timeout alarm. + void CheckForTimeout(); + + // Called when the ping alarm fires. Causes a ping frame to be sent only + // if the retransmission alarm is not running. + void OnPingTimeout(); + + // Sets up a packet with an QuicAckFrame and sends it out. + void SendAck(); + + // Called when the path degrading alarm fires. + void OnPathDegradingTimeout(); + + // Called when an RTO fires. Resets the retransmission alarm if there are + // remaining unacked packets. + void OnRetransmissionTimeout(); + + // Retransmits all unacked packets with retransmittable frames if + // |retransmission_type| is ALL_UNACKED_PACKETS, otherwise retransmits only + // initially encrypted packets. Used when the negotiated protocol version is + // different from what was initially assumed and when the initial encryption + // changes. + void RetransmitUnackedPackets(TransmissionType retransmission_type); + + // Calls |sent_packet_manager_|'s NeuterUnencryptedPackets. Used when the + // connection becomes forward secure and hasn't received acks for all packets. + void NeuterUnencryptedPackets(); + + // Changes the encrypter used for level |level| to |encrypter|. + void SetEncrypter(EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter); + + // SetNonceForPublicHeader sets the nonce that will be transmitted in the + // header of each packet encrypted at the initial encryption level decrypted. + // This should only be called on the server side. + void SetDiversificationNonce(const DiversificationNonce& nonce); + + // SetDefaultEncryptionLevel sets the encryption level that will be applied + // to new packets. + void SetDefaultEncryptionLevel(EncryptionLevel level); + + // SetDecrypter sets the primary decrypter, replacing any that already exists. + // If an alternative decrypter is in place then the function DCHECKs. This is + // intended for cases where one knows that future packets will be using the + // new decrypter and the previous decrypter is now obsolete. |level| indicates + // the encryption level of the new decrypter. + void SetDecrypter(EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter); + + // SetAlternativeDecrypter sets a decrypter that may be used to decrypt + // future packets. |level| indicates the encryption level of the decrypter. If + // |latch_once_used| is true, then the first time that the decrypter is + // successful it will replace the primary decrypter. Otherwise both + // decrypters will remain active and the primary decrypter will be the one + // last used. + void SetAlternativeDecrypter(EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter, + bool latch_once_used); + + const QuicDecrypter* decrypter() const; + const QuicDecrypter* alternative_decrypter() const; + + Perspective perspective() const { return perspective_; } + + // Allow easy overriding of truncated connection IDs. + void set_can_truncate_connection_ids(bool can) { + can_truncate_connection_ids_ = can; + } + + // Returns the underlying sent packet manager. + const QuicSentPacketManager& sent_packet_manager() const { + return sent_packet_manager_; + } + + // Returns the underlying sent packet manager. + QuicSentPacketManager& sent_packet_manager() { return sent_packet_manager_; } + + bool CanWrite(HasRetransmittableData retransmittable); + + // When the flusher is out of scope, only the outermost flusher will cause a + // flush of the connection and set the retransmission alarm if there is one + // pending. In addition, this flusher can be configured to ensure that an ACK + // frame is included in the first packet created, if there's new ack + // information to be sent. + class QUIC_EXPORT_PRIVATE ScopedPacketFlusher { + public: + // Setting |include_ack| to true ensures that an ACK frame is + // opportunistically bundled with the first outgoing packet. + // TODO(fayang): Remove |ack_mode| when deprecating + // quic_deprecate_ack_bundling_mode. + ScopedPacketFlusher(QuicConnection* connection, AckBundling ack_mode); + ~ScopedPacketFlusher(); + + private: + bool ShouldSendAck(AckBundling ack_mode) const; + + QuicConnection* connection_; + // If true, when this flusher goes out of scope, flush connection and set + // retransmission alarm if there is one pending. + bool flush_and_set_pending_retransmission_alarm_on_delete_; + }; + + QuicPacketWriter* writer() { return writer_; } + const QuicPacketWriter* writer() const { return writer_; } + + // Sends an MTU discovery packet of size |target_mtu|. If the packet is + // acknowledged by the peer, the maximum packet size will be increased to + // |target_mtu|. + void SendMtuDiscoveryPacket(QuicByteCount target_mtu); + + // Sends a connectivity probing packet to |peer_address| with + // |probing_writer|. If |probing_writer| is nullptr, will use default + // packet writer to write the packet. Returns true if subsequent packets can + // be written to the probing writer. If connection is V99, a padded IETF QUIC + // PATH_CHALLENGE packet is transmitted; if not V99, a Google QUIC padded PING + // packet is transmitted. + virtual bool SendConnectivityProbingPacket( + QuicPacketWriter* probing_writer, + const QuicSocketAddress& peer_address); + + // Sends response to a connectivity probe. Sends either a Padded Ping + // or an IETF PATH_RESPONSE based on the version of the connection. + // Is the counterpart to SendConnectivityProbingPacket(). + virtual void SendConnectivityProbingResponsePacket( + const QuicSocketAddress& peer_address); + + // Sends an MTU discovery packet of size |mtu_discovery_target_| and updates + // the MTU discovery alarm. + void DiscoverMtu(); + + // Sets the session notifier on the SentPacketManager. + void SetSessionNotifier(SessionNotifierInterface* session_notifier); + + // Set data producer in framer. + void SetDataProducer(QuicStreamFrameDataProducer* data_producer); + + // Set transmission type of next sending packets. + void SetTransmissionType(TransmissionType type); + + // Tries to send |message| and returns the message status. + virtual MessageStatus SendMessage(QuicMessageId message_id, + QuicMemSliceSpan message); + + // Returns the largest payload that will fit into a single MESSAGE frame. + QuicPacketLength GetLargestMessagePayload() const; + + // Return the id of the cipher of the primary decrypter of the framer. + uint32_t cipher_id() const { return framer_.decrypter()->cipher_id(); } + + std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets() { + return termination_packets_.get(); + } + + bool ack_queued() const { return ack_queued_; } + + bool ack_frame_updated() const; + + QuicConnectionHelperInterface* helper() { return helper_; } + QuicAlarmFactory* alarm_factory() { return alarm_factory_; } + + QuicStringPiece GetCurrentPacket(); + + const QuicFramer& framer() const { return framer_; } + + const QuicPacketGenerator& packet_generator() const { + return packet_generator_; + } + + const QuicReceivedPacketManager& received_packet_manager() const { + return received_packet_manager_; + } + + EncryptionLevel encryption_level() const { return encryption_level_; } + EncryptionLevel last_decrypted_level() const { + return last_decrypted_packet_level_; + } + + const QuicSocketAddress& last_packet_source_address() const { + return last_packet_source_address_; + } + + bool fill_up_link_during_probing() const { + return fill_up_link_during_probing_; + } + void set_fill_up_link_during_probing(bool new_value) { + fill_up_link_during_probing_ = new_value; + } + + // This setting may be changed during the crypto handshake in order to + // enable/disable padding of different packets in the crypto handshake. + // + // This setting should never be set to false in public facing endpoints. It + // can only be set to false if there is some other mechanism of preventing + // amplification attacks, such as ICE (plus its a non-standard quic). + void set_fully_pad_crypto_hadshake_packets(bool new_value) { + packet_generator_.set_fully_pad_crypto_hadshake_packets(new_value); + } + + bool fully_pad_during_crypto_handshake() const { + return packet_generator_.fully_pad_crypto_handshake_packets(); + } + + size_t min_received_before_ack_decimation() const; + void set_min_received_before_ack_decimation(size_t new_value); + + size_t ack_frequency_before_ack_decimation() const; + void set_ack_frequency_before_ack_decimation(size_t new_value); + + // If |defer| is true, configures the connection to defer sending packets in + // response to an ACK to the SendAlarm. If |defer| is false, packets may be + // sent immediately after receiving an ACK. + void set_defer_send_in_response_to_packets(bool defer) { + defer_send_in_response_to_packets_ = defer; + } + + bool session_decides_what_to_write() const; + + void SetRetransmittableOnWireAlarm(); + + // Sets the current per-packet options for the connection. The QuicConnection + // does not take ownership of |options|; |options| must live for as long as + // the QuicConnection is in use. + void set_per_packet_options(PerPacketOptions* options) { + per_packet_options_ = options; + } + + bool IsPathDegrading() const { return is_path_degrading_; } + + // Attempts to process any queued undecryptable packets. + void MaybeProcessUndecryptablePackets(); + + // Queue a coalesced packet. + void QueueCoalescedPacket(const QuicEncryptedPacket& packet); + + // Process previously queued coalesced packets. + void MaybeProcessCoalescedPackets(); + + enum PacketContent : uint8_t { + NO_FRAMES_RECEIVED, + // TODO(fkastenholz): Change name when we get rid of padded ping/ + // pre-version-99. + // Also PATH CHALLENGE and PATH RESPONSE. + FIRST_FRAME_IS_PING, + SECOND_FRAME_IS_PADDING, + NOT_PADDED_PING, // Set if the packet is not {PING, PADDING}. + }; + + // Whether the handshake is confirmed from this connection's perspective. + bool IsHandshakeConfirmed() const { + return sent_packet_manager_.handshake_confirmed(); + } + + protected: + // Calls cancel() on all the alarms owned by this connection. + void CancelAllAlarms(); + + // Send a packet to the peer, and takes ownership of the packet if the packet + // cannot be written immediately. + virtual void SendOrQueuePacket(SerializedPacket* packet); + + // Called after a packet is received from a new effective peer address and is + // decrypted. Starts validation of effective peer's address change. Calls + // OnConnectionMigration as soon as the address changed. + void StartEffectivePeerMigration(AddressChangeType type); + + // Called when a effective peer address migration is validated. + virtual void OnEffectivePeerMigrationValidated(); + + // Get the effective peer address from the packet being processed. For proxied + // connections, effective peer address is the address of the endpoint behind + // the proxy. For non-proxied connections, effective peer address is the same + // as peer address. + // + // Notes for implementations in subclasses: + // - If the connection is not proxied, the overridden method should use the + // base implementation: + // + // return QuicConnection::GetEffectivePeerAddressFromCurrentPacket(); + // + // - If the connection is proxied, the overridden method may return either of + // the following: + // a) The address of the endpoint behind the proxy. The address is used to + // drive effective peer migration. + // b) An uninitialized address, meaning the effective peer address does not + // change. + virtual QuicSocketAddress GetEffectivePeerAddressFromCurrentPacket() const; + + // Selects and updates the version of the protocol being used by selecting a + // version from |available_versions| which is also supported. Returns true if + // such a version exists, false otherwise. + bool SelectMutualVersion(const ParsedQuicVersionVector& available_versions); + + // Returns the current per-packet options for the connection. + PerPacketOptions* per_packet_options() { return per_packet_options_; } + + AddressChangeType active_effective_peer_migration_type() const { + return active_effective_peer_migration_type_; + } + + // Sends the connection close packet to the peer. |ack_mode| determines + // whether ack frame will be bundled with the connection close packet. + // TODO(fayang): change |ack_mode| to bool |force_sending_ack| when + // deprecating quic_deprecate_ack_bundling_mode. + virtual void SendConnectionClosePacket(QuicErrorCode error, + const QuicString& details, + AckBundling ack_mode); + + // Returns true if the packet should be discarded and not sent. + virtual bool ShouldDiscardPacket(const SerializedPacket& packet); + + // Retransmits packets continuously until blocked by the congestion control. + // If there are no packets to retransmit, does not do anything. + void SendProbingRetransmissions(); + + // Decides whether to send probing retransmissions, and does so if required. + void MaybeSendProbingRetransmissions(); + + // Notify various components(SendPacketManager, Session etc.) that this + // connection has been migrated. + virtual void OnConnectionMigration(AddressChangeType addr_change_type); + + // Return whether the packet being processed is a connectivity probing. + // A packet is a connectivity probing if it is a padded ping packet with self + // and/or peer address changes. + bool IsCurrentPacketConnectivityProbing() const; + + // Return true iff the writer is blocked, if blocked, call + // visitor_->OnWriteBlocked() to add the connection into the write blocked + // list. + bool HandleWriteBlocked(); + + private: + friend class test::QuicConnectionPeer; + + typedef std::list<SerializedPacket> QueuedPacketList; + + // Notifies the visitor of the close and marks the connection as disconnected. + // Does not send a connection close frame to the peer. + void TearDownLocalConnectionState(QuicErrorCode error, + const QuicString& details, + ConnectionCloseSource source); + + // Writes the given packet to socket, encrypted with packet's + // encryption_level. Returns true on successful write, and false if the writer + // was blocked and the write needs to be tried again. Notifies the + // SentPacketManager when the write is successful and sets + // retransmittable frames to nullptr. + // Saves the connection close packet for later transmission, even if the + // writer is write blocked. + bool WritePacket(SerializedPacket* packet); + + // Flush packets buffered in the writer, if any. + void FlushPackets(); + + // Make sure an ack we got from our peer is sane. + // Returns nullptr for valid acks or an error string if it was invalid. + const char* ValidateAckFrame(const QuicAckFrame& incoming_ack); + + // Make sure a stop waiting we got from our peer is sane. + // Returns nullptr if the frame is valid or an error string if it was invalid. + const char* ValidateStopWaitingFrame( + const QuicStopWaitingFrame& stop_waiting); + + // Sends a version negotiation packet to the peer. + void SendVersionNegotiationPacket(bool ietf_quic); + + // Clears any accumulated frames from the last received packet. + void ClearLastFrames(); + + // Deletes and clears any queued packets. + void ClearQueuedPackets(); + + // Closes the connection if the sent packet manager is tracking too many + // outstanding packets. + void CloseIfTooManyOutstandingSentPackets(); + + // Writes as many queued packets as possible. The connection must not be + // blocked when this is called. + void WriteQueuedPackets(); + + // Writes as many pending retransmissions as possible. + void WritePendingRetransmissions(); + + // Writes new data if congestion control allows. + void WriteNewData(); + + // Queues |packet| in the hopes that it can be decrypted in the + // future, when a new key is installed. + void QueueUndecryptablePacket(const QuicEncryptedPacket& packet); + + // Sends any packets which are a response to the last packet, including both + // acks and pending writes if an ack opened the congestion window. + void MaybeSendInResponseToPacket(); + + // Queue an ack or set the ack alarm if needed. |was_missing| is true if + // the most recently received packet was formerly missing. + void MaybeQueueAck(bool was_missing); + + // Gets the least unacked packet number, which is the next packet number to be + // sent if there are no outstanding packets. + QuicPacketNumber GetLeastUnacked() const; + + // Sets the timeout alarm to the appropriate value, if any. + void SetTimeoutAlarm(); + + // Sets the ping alarm to the appropriate value, if any. + void SetPingAlarm(); + + // Sets the retransmission alarm based on SentPacketManager. + void SetRetransmissionAlarm(); + + // Sets the path degrading alarm. + void SetPathDegradingAlarm(); + + // Sets the MTU discovery alarm if necessary. + // |sent_packet_number| is the recently sent packet number. + void MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number); + + // Sets ack alarm to |time| if ack alarm is not set or the deadline > time. + void MaybeSetAckAlarmTo(QuicTime time); + + HasRetransmittableData IsRetransmittable(const SerializedPacket& packet); + bool IsTerminationPacket(const SerializedPacket& packet); + + // Set the size of the packet we are targeting while doing path MTU discovery. + void SetMtuDiscoveryTarget(QuicByteCount target); + + // Returns |suggested_max_packet_size| clamped to any limits set by the + // underlying writer, connection, or protocol. + QuicByteCount GetLimitedMaxPacketSize( + QuicByteCount suggested_max_packet_size); + + // Do any work which logically would be done in OnPacket but can not be + // safely done until the packet is validated. Returns true if packet can be + // handled, false otherwise. + bool ProcessValidatedPacket(const QuicPacketHeader& header); + + // Consider receiving crypto frame on non crypto stream as memory corruption. + bool MaybeConsiderAsMemoryCorruption(const QuicStreamFrame& frame); + + // Check if the connection has no outstanding data to send and notify + // congestion controller if it is the case. + void CheckIfApplicationLimited(); + + // Sets |current_packet_content_| to |type| if applicable. And + // starts effective peer migration if current packet is confirmed not a + // connectivity probe and |current_effective_peer_migration_type_| indicates + // effective peer address change. + void UpdatePacketContent(PacketContent type); + + // Enables session decide what to write based on version and flags. + void MaybeEnableSessionDecidesWhatToWrite(); + + // Called when last received ack frame has been processed. + // |send_stop_waiting| indicates whether a stop waiting needs to be sent. + // |acked_new_packet| is true if a previously-unacked packet was acked. + void PostProcessAfterAckFrame(bool send_stop_waiting, bool acked_new_packet); + + // Called when an ACK is received to set the path degrading alarm or + // retransmittable on wire alarm. + void MaybeSetPathDegradingAlarm(bool acked_new_packet); + + // Updates the release time into the future. + void UpdateReleaseTimeIntoFuture(); + + // Sends generic path probe packet to the peer. If we are not IETF QUIC, will + // always send a padded ping, regardless of whether this is a request or + // response. If version 99/ietf quic, will send a PATH_RESPONSE if + // |is_response| is true, a PATH_CHALLENGE if not. + bool SendGenericPathProbePacket(QuicPacketWriter* probing_writer, + const QuicSocketAddress& peer_address, + bool is_response); + + // Called when an ACK is about to send. Resets ACK related internal states, + // e.g., cancels ack_alarm_, resets + // num_retransmittable_packets_received_since_last_ack_sent_ etc. + void ResetAckStates(); + + // Returns true if ack alarm is not set and there is no pending ack in the + // generator. + bool ShouldSetAckAlarm() const; + + // Returns the encryption level the connection close packet should be sent at, + // which is the highest encryption level that peer can guarantee to process. + EncryptionLevel GetConnectionCloseEncryptionLevel() const; + + QuicFramer framer_; + + // Contents received in the current packet, especially used to identify + // whether the current packet is a padded PING packet. + PacketContent current_packet_content_; + // Set to true as soon as the packet currently being processed has been + // detected as a connectivity probing. + // Always false outside the context of ProcessUdpPacket(). + bool is_current_packet_connectivity_probing_; + + // Caches the current effective peer migration type if a effective peer + // migration might be initiated. As soon as the current packet is confirmed + // not a connectivity probe, effective peer migration will start. + AddressChangeType current_effective_peer_migration_type_; + QuicConnectionHelperInterface* helper_; // Not owned. + QuicAlarmFactory* alarm_factory_; // Not owned. + PerPacketOptions* per_packet_options_; // Not owned. + QuicPacketWriter* writer_; // Owned or not depending on |owns_writer_|. + bool owns_writer_; + // Encryption level for new packets. Should only be changed via + // SetDefaultEncryptionLevel(). + EncryptionLevel encryption_level_; + const QuicClock* clock_; + QuicRandom* random_generator_; + + const QuicConnectionId connection_id_; + // Address on the last successfully processed packet received from the + // direct peer. + QuicSocketAddress self_address_; + QuicSocketAddress peer_address_; + + QuicSocketAddress direct_peer_address_; + // Address of the endpoint behind the proxy if the connection is proxied. + // Otherwise it is the same as |peer_address_|. + // NOTE: Currently |effective_peer_address_| and |peer_address_| are always + // the same(the address of the direct peer), but soon we'll change + // |effective_peer_address_| to be the address of the endpoint behind the + // proxy if the connection is proxied. + QuicSocketAddress effective_peer_address_; + + // Records change type when the effective peer initiates migration to a new + // address. Reset to NO_CHANGE after effective peer migration is validated. + AddressChangeType active_effective_peer_migration_type_; + + // Records highest sent packet number when effective peer migration is + // started. + QuicPacketNumber highest_packet_sent_before_effective_peer_migration_; + + // True if the last packet has gotten far enough in the framer to be + // decrypted. + bool last_packet_decrypted_; + QuicByteCount last_size_; // Size of the last received packet. + // TODO(rch): remove this when b/27221014 is fixed. + const char* current_packet_data_; // UDP payload of packet currently being + // parsed or nullptr. + EncryptionLevel last_decrypted_packet_level_; + QuicPacketHeader last_header_; + bool should_last_packet_instigate_acks_; + // Whether the most recent packet was missing before it was received. + // TODO(fayang): Remove was_last_packet_missing_ when deprecating + // quic_rpm_decides_when_to_send_acks. + bool was_last_packet_missing_; + + // Track some peer state so we can do less bookkeeping + // Largest sequence sent by the peer which had an ack frame (latest ack info). + QuicPacketNumber largest_seen_packet_with_ack_; + + // Largest packet number sent by the peer which had a stop waiting frame. + QuicPacketNumber largest_seen_packet_with_stop_waiting_; + + // Collection of packets which were received before encryption was + // established, but which could not be decrypted. We buffer these on + // the assumption that they could not be processed because they were + // sent with the INITIAL encryption and the CHLO message was lost. + QuicDeque<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_; + + // Collection of coalesced packets which were received while processing + // the current packet. + QuicDeque<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_; + + // Maximum number of undecryptable packets the connection will store. + size_t max_undecryptable_packets_; + + // Maximum number of tracked packets. + QuicPacketCount max_tracked_packets_; + + // When the version negotiation packet could not be sent because the socket + // was not writable, this is set to true. + bool pending_version_negotiation_packet_; + // Used when pending_version_negotiation_packet_ is true. + bool send_ietf_version_negotiation_packet_; + + // When packets could not be sent because the socket was not writable, + // they are added to this list. All corresponding frames are in + // unacked_packets_ if they are to be retransmitted. Packets encrypted_buffer + // fields are owned by the QueuedPacketList, in order to ensure they outlast + // the original scope of the SerializedPacket. + QueuedPacketList queued_packets_; + + // If true, then crypto packets will be saved as termination packets. + bool save_crypto_packets_as_termination_packets_; + + // Contains the connection close packets if the connection has been closed. + std::unique_ptr<std::vector<std::unique_ptr<QuicEncryptedPacket>>> + termination_packets_; + + // Determines whether or not a connection close packet is sent to the peer + // after idle timeout due to lack of network activity. + // This is particularly important on mobile, where waking up the radio is + // undesirable. + ConnectionCloseBehavior idle_timeout_connection_close_behavior_; + + // When true, close the QUIC connection after 5 RTOs. Due to the min rto of + // 200ms, this is over 5 seconds. + bool close_connection_after_five_rtos_; + + QuicReceivedPacketManager received_packet_manager_; + + // Indicates whether an ack should be sent the next time we try to write. + // TODO(fayang): Remove ack_queued_ when deprecating + // quic_deprecate_ack_bundling_mode. + bool ack_queued_; + // How many retransmittable packets have arrived without sending an ack. + // TODO(fayang): Remove + // num_retransmittable_packets_received_since_last_ack_sent_ when deprecating + // quic_rpm_decides_when_to_send_acks. + QuicPacketCount num_retransmittable_packets_received_since_last_ack_sent_; + // How many consecutive packets have arrived without sending an ack. + QuicPacketCount num_packets_received_since_last_ack_sent_; + // Indicates how many consecutive times an ack has arrived which indicates + // the peer needs to stop waiting for some packets. + int stop_waiting_count_; + // TODO(fayang): Remove ack_mode_, ack_decimation_delay_, + // unlimited_ack_decimation_, fast_ack_after_quiescence_ when deprecating + // quic_rpm_decides_when_to_send_acks. + // Indicates the current ack mode, defaults to acking every 2 packets. + AckMode ack_mode_; + // The max delay in fraction of min_rtt to use when sending decimated acks. + float ack_decimation_delay_; + // When true, removes ack decimation's max number of packets(10) before + // sending an ack. + bool unlimited_ack_decimation_; + // When true, use a 1ms delayed ack timer if it's been an SRTT since a packet + // was received. + bool fast_ack_after_quiescence_; + + // Indicates the retransmission alarm needs to be set. + bool pending_retransmission_alarm_; + + // If true, defer sending data in response to received packets to the + // SendAlarm. + bool defer_send_in_response_to_packets_; + + // The timeout for PING. + QuicTime::Delta ping_timeout_; + + // Timeout for how long the wire can have no retransmittable packets. + QuicTime::Delta retransmittable_on_wire_timeout_; + + // Arena to store class implementations within the QuicConnection. + QuicConnectionArena arena_; + + // An alarm that fires when an ACK should be sent to the peer. + QuicArenaScopedPtr<QuicAlarm> ack_alarm_; + // An alarm that fires when a packet needs to be retransmitted. + QuicArenaScopedPtr<QuicAlarm> retransmission_alarm_; + // An alarm that is scheduled when the SentPacketManager requires a delay + // before sending packets and fires when the packet may be sent. + QuicArenaScopedPtr<QuicAlarm> send_alarm_; + // An alarm that is scheduled when the connection can still write and there + // may be more data to send. + // An alarm that fires when the connection may have timed out. + QuicArenaScopedPtr<QuicAlarm> timeout_alarm_; + // An alarm that fires when a ping should be sent. + QuicArenaScopedPtr<QuicAlarm> ping_alarm_; + // An alarm that fires when an MTU probe should be sent. + QuicArenaScopedPtr<QuicAlarm> mtu_discovery_alarm_; + // An alarm that fires when this connection is considered degrading. + QuicArenaScopedPtr<QuicAlarm> path_degrading_alarm_; + // An alarm that fires to process undecryptable packets when new decyrption + // keys are available. + QuicArenaScopedPtr<QuicAlarm> process_undecryptable_packets_alarm_; + // Neither visitor is owned by this class. + QuicConnectionVisitorInterface* visitor_; + QuicConnectionDebugVisitor* debug_visitor_; + + QuicPacketGenerator packet_generator_; + + // Network idle time before this connection is closed. + QuicTime::Delta idle_network_timeout_; + // The connection will wait this long for the handshake to complete. + QuicTime::Delta handshake_timeout_; + + // Statistics for this session. + QuicConnectionStats stats_; + + // Timestamps used for timeouts. + // The time of the first retransmittable packet that was sent after the most + // recently received packet. + QuicTime time_of_first_packet_sent_after_receiving_; + // The time that a packet is received for this connection. Initialized to + // connection creation time. + // This is used for timeouts, and does not indicate the packet was processed. + QuicTime time_of_last_received_packet_; + + // The time the previous ack-instigating packet was received and processed. + // TODO(fayang): Remove time_of_previous_received_packet_ when deprecating + // quic_rpm_decides_when_to_send_acks. + QuicTime time_of_previous_received_packet_; + + // Sent packet manager which tracks the status of packets sent by this + // connection and contains the send and receive algorithms to determine when + // to send packets. + QuicSentPacketManager sent_packet_manager_; + + // The state of connection in version negotiation finite state machine. + enum QuicVersionNegotiationState { + START_NEGOTIATION = 0, + // Server-side this implies we've sent a version negotiation packet and are + // waiting on the client to select a compatible version. Client-side this + // implies we've gotten a version negotiation packet, are retransmitting the + // initial packets with a supported version and are waiting for our first + // packet from the server. + NEGOTIATION_IN_PROGRESS, + // This indicates this endpoint has received a packet from the peer with a + // version this endpoint supports. Version negotiation is complete, and the + // version number will no longer be sent with future packets. + NEGOTIATED_VERSION + }; + QuicVersionNegotiationState version_negotiation_state_; + + // Tracks if the connection was created by the server or the client. + Perspective perspective_; + + // True by default. False if we've received or sent an explicit connection + // close. + bool connected_; + + // Destination address of the last received packet. + QuicSocketAddress last_packet_destination_address_; + + // Source address of the last received packet. + QuicSocketAddress last_packet_source_address_; + + // Set to false if the connection should not send truncated connection IDs to + // the peer, even if the peer supports it. + bool can_truncate_connection_ids_; + + // If non-empty this contains the set of versions received in a + // version negotiation packet. + ParsedQuicVersionVector server_supported_versions_; + + // The size of the packet we are targeting while doing path MTU discovery. + QuicByteCount mtu_discovery_target_; + + // The number of MTU probes already sent. + size_t mtu_probe_count_; + + // The number of packets between MTU probes. + QuicPacketCount packets_between_mtu_probes_; + + // The packet number of the packet after which the next MTU probe will be + // sent. + QuicPacketNumber next_mtu_probe_at_; + + // The value of the MTU regularly used by the connection. This is different + // from the value returned by max_packet_size(), as max_packet_size() returns + // the value of the MTU as currently used by the serializer, so if + // serialization of an MTU probe is in progress, those two values will be + // different. + QuicByteCount long_term_mtu_; + + // The size of the largest packet received from peer. + QuicByteCount largest_received_packet_size_; + + // Indicates whether a write error is encountered currently. This is used to + // avoid infinite write errors. + bool write_error_occurred_; + + // Indicates not to send or process stop waiting frames. + bool no_stop_waiting_frames_; + + // Consecutive number of sent packets which have no retransmittable frames. + size_t consecutive_num_packets_with_no_retransmittable_frames_; + + // After this many packets sent without retransmittable frames, an artificial + // retransmittable frame(a WINDOW_UPDATE) will be created to solicit an ack + // from the peer. Default to kMaxConsecutiveNonRetransmittablePackets. + size_t max_consecutive_num_packets_with_no_retransmittable_frames_; + + // Ack decimation will start happening after this many packets are received. + // TODO(fayang): Remove min_received_before_ack_decimation_ when deprecating + // quic_rpm_decides_when_to_send_acks. + size_t min_received_before_ack_decimation_; + + // Before ack decimation starts (if enabled), we ack every n-th packet. + // TODO(fayang): Remove ack_frequency_before_ack_decimation_ when deprecating + // quic_rpm_decides_when_to_send_acks. + size_t ack_frequency_before_ack_decimation_; + + // If true, the connection will fill up the pipe with extra data whenever the + // congestion controller needs it in order to make a bandwidth estimate. This + // is useful if the application pesistently underutilizes the link, but still + // relies on having a reasonable bandwidth estimate from the connection, e.g. + // for real time applications. + bool fill_up_link_during_probing_; + + // If true, the probing retransmission will not be started again. This is + // used to safeguard against an accidental tail recursion in probing + // retransmission code. + bool probing_retransmission_pending_; + + // Indicates whether a stateless reset token has been received from peer. + bool stateless_reset_token_received_; + // Stores received stateless reset token from peer. Used to verify whether a + // packet is a stateless reset packet. + QuicUint128 received_stateless_reset_token_; + + // Id of latest sent control frame. 0 if no control frame has been sent. + QuicControlFrameId last_control_frame_id_; + + // True if the peer is unreachable on the current path. + bool is_path_degrading_; + + // True if an ack frame is being processed. + bool processing_ack_frame_; + + // True if the writer supports release timestamp. + bool supports_release_time_; + + // Time this connection can release packets into the future. + QuicTime::Delta release_time_into_future_; + + // Indicates whether server connection does version negotiation. Server + // connection does not support version negotiation if a single version is + // provided in constructor. + const bool no_version_negotiation_; + + // Payload of most recently transmitted QUIC_VERSION_99 connectivity + // probe packet (the PATH_CHALLENGE payload). This implementation transmits + // only one PATH_CHALLENGE per connectivity probe, so only one + // QuicPathFrameBuffer is needed. + std::unique_ptr<QuicPathFrameBuffer> transmitted_connectivity_probe_payload_; + + // Payloads that were received in the most recent probe. This needs to be a + // Deque because the peer might no be using this implementation, and others + // might send a packet with more than one PATH_CHALLENGE, so all need to be + // saved and responded to. + QuicDeque<QuicPathFrameBuffer> received_path_challenge_payloads_; + + // Latched value of quic_fix_termination_packets. + const bool fix_termination_packets_; + + // Indicates whether an ACK needs to be sent in OnCanWrite(). Only used when + // deprecate_ack_bundling_mode is true. + // TODO(fayang): Remove this when ACK sending logic is moved to received + // packet manager, and an ACK timeout would be used to record when an ACK + // needs to be sent. + bool send_ack_when_on_can_write_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_H_
diff --git a/quic/core/quic_connection_close_delegate_interface.h b/quic/core/quic_connection_close_delegate_interface.h new file mode 100644 index 0000000..c9faab8 --- /dev/null +++ b/quic/core/quic_connection_close_delegate_interface.h
@@ -0,0 +1,28 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_CLOSE_DELEGATE_INTERFACE_H_ +#define QUICHE_QUIC_CORE_QUIC_CONNECTION_CLOSE_DELEGATE_INTERFACE_H_ + +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// Pure virtual class to close connection on unrecoverable errors. +class QUIC_EXPORT_PRIVATE QuicConnectionCloseDelegateInterface { + public: + virtual ~QuicConnectionCloseDelegateInterface() {} + + // Called when an unrecoverable error is encountered. + virtual void OnUnrecoverableError(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_CLOSE_DELEGATE_INTERFACE_H_
diff --git a/quic/core/quic_connection_id.cc b/quic/core/quic_connection_id.cc new file mode 100644 index 0000000..25c5b1f --- /dev/null +++ b/quic/core/quic_connection_id.cc
@@ -0,0 +1,106 @@ +// Copyright 2018 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/quic_connection_id.h" + +#include <cstdint> +#include <cstring> +#include <iomanip> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +QuicConnectionId::QuicConnectionId() : length_(0) {} + +QuicConnectionId::QuicConnectionId(const char* data, uint8_t length) { + if (length > kQuicMaxConnectionIdLength) { + QUIC_BUG << "Attempted to create connection ID of length " << length; + length = kQuicMaxConnectionIdLength; + } + length_ = length; + if (length_ > 0) { + memcpy(data_, data, length_); + } +} + +QuicConnectionId::~QuicConnectionId() {} + +const char* QuicConnectionId::data() const { + return data_; +} + +char* QuicConnectionId::mutable_data() { + return data_; +} + +uint8_t QuicConnectionId::length() const { + return length_; +} + +void QuicConnectionId::set_length(uint8_t length) { + length_ = length; +} + +bool QuicConnectionId::IsEmpty() const { + return length_ == 0; +} + +size_t QuicConnectionId::Hash() const { + uint64_t data_bytes[3] = {0, 0, 0}; + static_assert(sizeof(data_bytes) >= sizeof(data_), "sizeof(data_) changed"); + memcpy(data_bytes, data_, length_); + // This Hash function is designed to return the same value as the host byte + // order representation when the connection ID length is 64 bits. + return QuicEndian::NetToHost64(kQuicDefaultConnectionIdLength ^ length_ ^ + data_bytes[0] ^ data_bytes[1] ^ data_bytes[2]); +} + +QuicString QuicConnectionId::ToString() const { + if (IsEmpty()) { + return QuicString("0"); + } + return QuicTextUtils::HexEncode(data_, length_); +} + +std::ostream& operator<<(std::ostream& os, const QuicConnectionId& v) { + os << v.ToString(); + return os; +} + +bool QuicConnectionId::operator==(const QuicConnectionId& v) const { + return length_ == v.length_ && memcmp(data_, v.data_, length_) == 0; +} + +bool QuicConnectionId::operator!=(const QuicConnectionId& v) const { + return !(v == *this); +} + +bool QuicConnectionId::operator<(const QuicConnectionId& v) const { + if (length_ < v.length_) { + return true; + } + if (length_ > v.length_) { + return false; + } + return memcmp(data_, v.data_, length_) < 0; +} + +QuicConnectionId EmptyQuicConnectionId() { + return QuicConnectionId(); +} + +static_assert(kQuicDefaultConnectionIdLength == sizeof(uint64_t), + "kQuicDefaultConnectionIdLength changed"); +static_assert(kQuicDefaultConnectionIdLength == PACKET_8BYTE_CONNECTION_ID, + "kQuicDefaultConnectionIdLength changed"); + +} // namespace quic
diff --git a/quic/core/quic_connection_id.h b/quic/core/quic_connection_id.h new file mode 100644 index 0000000..5a78acd --- /dev/null +++ b/quic/core/quic_connection_id.h
@@ -0,0 +1,98 @@ +// Copyright 2018 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_QUIC_CONNECTION_ID_H_ +#define QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +enum QuicConnectionIdLength { + PACKET_0BYTE_CONNECTION_ID = 0, + PACKET_8BYTE_CONNECTION_ID = 8, +}; + +// This is a property of QUIC headers, it indicates whether the connection ID +// should actually be sent over the wire (or was sent on received packets). +enum QuicConnectionIdIncluded : uint8_t { + CONNECTION_ID_PRESENT = 1, + CONNECTION_ID_ABSENT = 2, +}; + +// Connection IDs can be 0-18 bytes per IETF specifications. +const uint8_t kQuicMaxConnectionIdLength = 18; + +// kQuicDefaultConnectionIdLength is the only supported length for QUIC +// versions < v99, and is the default picked for all versions. +const uint8_t kQuicDefaultConnectionIdLength = 8; + +class QUIC_EXPORT_PRIVATE QuicConnectionId { + public: + // Creates a connection ID of length zero. + QuicConnectionId(); + + // Creates a connection ID from network order bytes. + QuicConnectionId(const char* data, uint8_t length); + + ~QuicConnectionId(); + + // Returns the length of the connection ID, in bytes. + uint8_t length() const; + + // Sets the length of the connection ID, in bytes. + void set_length(uint8_t length); + + // Returns a pointer to the connection ID bytes, in network byte order. + const char* data() const; + + // Returns a mutable pointer to the connection ID bytes, + // in network byte order. + char* mutable_data(); + + // Returns whether the connection ID has length zero. + bool IsEmpty() const; + + // Hash() is required to use connection IDs as keys in hash tables. + size_t Hash() const; + + // Generates an ASCII string that represents + // the contents of the connection ID, or "0" if it is empty. + QuicString ToString() const; + + // operator<< allows easily logging connection IDs. + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicConnectionId& v); + + bool operator==(const QuicConnectionId& v) const; + bool operator!=(const QuicConnectionId& v) const; + // operator< is required to use connection IDs as keys in hash tables. + bool operator<(const QuicConnectionId& v) const; + + private: + // The connection ID is represented in network byte order + // in the first |length_| bytes of |data_|. + char data_[kQuicMaxConnectionIdLength]; + uint8_t length_; +}; + +// Creates a connection ID of length zero, unless the restart flag +// quic_connection_ids_network_byte_order is false in which case +// it returns an 8-byte all-zeroes connection ID. +QUIC_EXPORT_PRIVATE QuicConnectionId EmptyQuicConnectionId(); + +// QuicConnectionIdHash can be passed as hash argument to hash tables. +class QuicConnectionIdHash { + public: + size_t operator()(QuicConnectionId const& connection_id) const noexcept { + return connection_id.Hash(); + } +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
diff --git a/quic/core/quic_connection_id_test.cc b/quic/core/quic_connection_id_test.cc new file mode 100644 index 0000000..1c7c761 --- /dev/null +++ b/quic/core/quic_connection_id_test.cc
@@ -0,0 +1,97 @@ +// Copyright 2018 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/quic_connection_id.h" + +#include <cstdint> +#include <cstring> + +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { + +namespace { + +class QuicConnectionIdTest : public QuicTest {}; + +TEST_F(QuicConnectionIdTest, Empty) { + QuicConnectionId connection_id_empty = EmptyQuicConnectionId(); + EXPECT_TRUE(connection_id_empty.IsEmpty()); +} + +TEST_F(QuicConnectionIdTest, DefaultIsEmpty) { + QuicConnectionId connection_id_empty = QuicConnectionId(); + EXPECT_TRUE(connection_id_empty.IsEmpty()); +} + +TEST_F(QuicConnectionIdTest, NotEmpty) { + QuicConnectionId connection_id = test::TestConnectionId(1); + EXPECT_FALSE(connection_id.IsEmpty()); +} + +TEST_F(QuicConnectionIdTest, ZeroIsNotEmpty) { + QuicConnectionId connection_id = test::TestConnectionId(0); + EXPECT_FALSE(connection_id.IsEmpty()); +} + +TEST_F(QuicConnectionIdTest, Data) { + char connection_id_data[kQuicDefaultConnectionIdLength]; + memset(connection_id_data, 0x42, sizeof(connection_id_data)); + QuicConnectionId connection_id1 = + QuicConnectionId(connection_id_data, sizeof(connection_id_data)); + QuicConnectionId connection_id2 = + QuicConnectionId(connection_id_data, sizeof(connection_id_data)); + EXPECT_EQ(connection_id1, connection_id2); + EXPECT_EQ(connection_id1.length(), kQuicDefaultConnectionIdLength); + EXPECT_EQ(connection_id1.data(), connection_id1.mutable_data()); + EXPECT_EQ(0, memcmp(connection_id1.data(), connection_id2.data(), + sizeof(connection_id_data))); + EXPECT_EQ(0, memcmp(connection_id1.data(), connection_id_data, + sizeof(connection_id_data))); + connection_id2.mutable_data()[0] = 0x33; + EXPECT_NE(connection_id1, connection_id2); + static const uint8_t kNewLength = 4; + connection_id2.set_length(kNewLength); + EXPECT_EQ(kNewLength, connection_id2.length()); +} + +TEST_F(QuicConnectionIdTest, DoubleConvert) { + QuicConnectionId connection_id64_1 = test::TestConnectionId(1); + QuicConnectionId connection_id64_2 = test::TestConnectionId(42); + QuicConnectionId connection_id64_3 = + test::TestConnectionId(UINT64_C(0xfedcba9876543210)); + EXPECT_EQ(connection_id64_1, + test::TestConnectionId( + test::TestConnectionIdToUInt64(connection_id64_1))); + EXPECT_EQ(connection_id64_2, + test::TestConnectionId( + test::TestConnectionIdToUInt64(connection_id64_2))); + EXPECT_EQ(connection_id64_3, + test::TestConnectionId( + test::TestConnectionIdToUInt64(connection_id64_3))); + EXPECT_NE(connection_id64_1, connection_id64_2); + EXPECT_NE(connection_id64_1, connection_id64_3); + EXPECT_NE(connection_id64_2, connection_id64_3); +} + +TEST_F(QuicConnectionIdTest, Hash) { + QuicConnectionId connection_id64_1 = test::TestConnectionId(1); + QuicConnectionId connection_id64_1b = test::TestConnectionId(1); + QuicConnectionId connection_id64_2 = test::TestConnectionId(42); + QuicConnectionId connection_id64_3 = + test::TestConnectionId(UINT64_C(0xfedcba9876543210)); + EXPECT_EQ(connection_id64_1.Hash(), connection_id64_1b.Hash()); + EXPECT_NE(connection_id64_1.Hash(), connection_id64_2.Hash()); + EXPECT_NE(connection_id64_1.Hash(), connection_id64_3.Hash()); + EXPECT_NE(connection_id64_2.Hash(), connection_id64_3.Hash()); +} + +} // namespace + +} // namespace quic
diff --git a/quic/core/quic_connection_stats.cc b/quic/core/quic_connection_stats.cc new file mode 100644 index 0000000..5dea2ea --- /dev/null +++ b/quic/core/quic_connection_stats.cc
@@ -0,0 +1,92 @@ +// Copyright 2013 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/quic_connection_stats.h" + +namespace quic { + +QuicConnectionStats::QuicConnectionStats() + : bytes_sent(0), + packets_sent(0), + stream_bytes_sent(0), + packets_discarded(0), + bytes_received(0), + packets_received(0), + packets_processed(0), + stream_bytes_received(0), + bytes_retransmitted(0), + packets_retransmitted(0), + bytes_spuriously_retransmitted(0), + packets_spuriously_retransmitted(0), + packets_lost(0), + slowstart_packets_sent(0), + slowstart_packets_lost(0), + slowstart_bytes_lost(0), + packets_dropped(0), + crypto_retransmit_count(0), + loss_timeout_count(0), + tlp_count(0), + rto_count(0), + min_rtt_us(0), + srtt_us(0), + max_packet_size(0), + max_received_packet_size(0), + estimated_bandwidth(QuicBandwidth::Zero()), + packets_reordered(0), + max_sequence_reordering(0), + max_time_reordering_us(0), + tcp_loss_events(0), + connection_creation_time(QuicTime::Zero()), + blocked_frames_received(0), + blocked_frames_sent(0), + num_connectivity_probing_received(0) {} + +QuicConnectionStats::QuicConnectionStats(const QuicConnectionStats& other) = + default; + +QuicConnectionStats::~QuicConnectionStats() {} + +std::ostream& operator<<(std::ostream& os, const QuicConnectionStats& s) { + os << "{ bytes_sent: " << s.bytes_sent; + os << " packets_sent: " << s.packets_sent; + os << " stream_bytes_sent: " << s.stream_bytes_sent; + os << " packets_discarded: " << s.packets_discarded; + os << " bytes_received: " << s.bytes_received; + os << " packets_received: " << s.packets_received; + os << " packets_processed: " << s.packets_processed; + os << " stream_bytes_received: " << s.stream_bytes_received; + os << " bytes_retransmitted: " << s.bytes_retransmitted; + os << " packets_retransmitted: " << s.packets_retransmitted; + os << " bytes_spuriously_retransmitted: " << s.bytes_spuriously_retransmitted; + os << " packets_spuriously_retransmitted: " + << s.packets_spuriously_retransmitted; + os << " packets_lost: " << s.packets_lost; + os << " slowstart_packets_sent: " << s.slowstart_packets_sent; + os << " slowstart_packets_lost: " << s.slowstart_packets_lost; + os << " slowstart_bytes_lost: " << s.slowstart_bytes_lost; + os << " packets_dropped: " << s.packets_dropped; + os << " crypto_retransmit_count: " << s.crypto_retransmit_count; + os << " loss_timeout_count: " << s.loss_timeout_count; + os << " tlp_count: " << s.tlp_count; + os << " rto_count: " << s.rto_count; + os << " min_rtt_us: " << s.min_rtt_us; + os << " srtt_us: " << s.srtt_us; + os << " max_packet_size: " << s.max_packet_size; + os << " max_received_packet_size: " << s.max_received_packet_size; + os << " estimated_bandwidth: " << s.estimated_bandwidth; + os << " packets_reordered: " << s.packets_reordered; + os << " max_sequence_reordering: " << s.max_sequence_reordering; + os << " max_time_reordering_us: " << s.max_time_reordering_us; + os << " tcp_loss_events: " << s.tcp_loss_events; + os << " connection_creation_time: " + << s.connection_creation_time.ToDebuggingValue(); + os << " blocked_frames_received: " << s.blocked_frames_received; + os << " blocked_frames_sent: " << s.blocked_frames_sent; + os << " num_connectivity_probing_received: " + << s.num_connectivity_probing_received << " }"; + + return os; +} + +} // namespace quic
diff --git a/quic/core/quic_connection_stats.h b/quic/core/quic_connection_stats.h new file mode 100644 index 0000000..8123fb0 --- /dev/null +++ b/quic/core/quic_connection_stats.h
@@ -0,0 +1,97 @@ +// Copyright 2013 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_QUIC_CONNECTION_STATS_H_ +#define QUICHE_QUIC_CORE_QUIC_CONNECTION_STATS_H_ + +#include <cstdint> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { +// Structure to hold stats for a QuicConnection. +struct QUIC_EXPORT_PRIVATE QuicConnectionStats { + QuicConnectionStats(); + QuicConnectionStats(const QuicConnectionStats& other); + ~QuicConnectionStats(); + + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicConnectionStats& s); + + QuicByteCount bytes_sent; // Includes retransmissions. + QuicPacketCount packets_sent; + // Non-retransmitted bytes sent in a stream frame. + QuicByteCount stream_bytes_sent; + // Packets serialized and discarded before sending. + QuicPacketCount packets_discarded; + + // These include version negotiation and public reset packets, which do not + // have packet numbers or frame data. + QuicByteCount bytes_received; // Includes duplicate data for a stream. + // Includes packets which were not processable. + QuicPacketCount packets_received; + // Excludes packets which were not processable. + QuicPacketCount packets_processed; + QuicByteCount stream_bytes_received; // Bytes received in a stream frame. + + QuicByteCount bytes_retransmitted; + QuicPacketCount packets_retransmitted; + + QuicByteCount bytes_spuriously_retransmitted; + QuicPacketCount packets_spuriously_retransmitted; + // Number of packets abandoned as lost by the loss detection algorithm. + QuicPacketCount packets_lost; + + // Number of packets sent in slow start. + QuicPacketCount slowstart_packets_sent; + // Number of packets lost exiting slow start. + QuicPacketCount slowstart_packets_lost; + // Number of bytes lost exiting slow start. + QuicByteCount slowstart_bytes_lost; + + QuicPacketCount packets_dropped; // Duplicate or less than least unacked. + size_t crypto_retransmit_count; + // Count of times the loss detection alarm fired. At least one packet should + // be lost when the alarm fires. + size_t loss_timeout_count; + size_t tlp_count; + size_t rto_count; // Count of times the rto timer fired. + + int64_t min_rtt_us; // Minimum RTT in microseconds. + int64_t srtt_us; // Smoothed RTT in microseconds. + QuicByteCount max_packet_size; + QuicByteCount max_received_packet_size; + QuicBandwidth estimated_bandwidth; + + // Reordering stats for received packets. + // Number of packets received out of packet number order. + QuicPacketCount packets_reordered; + // Maximum reordering observed in packet number space. + QuicPacketCount max_sequence_reordering; + // Maximum reordering observed in microseconds + int64_t max_time_reordering_us; + + // The following stats are used only in TcpCubicSender. + // The number of loss events from TCP's perspective. Each loss event includes + // one or more lost packets. + uint32_t tcp_loss_events; + + // Creation time, as reported by the QuicClock. + QuicTime connection_creation_time; + + uint64_t blocked_frames_received; + uint64_t blocked_frames_sent; + + // Number of connectivity probing packets received by this connection. + uint64_t num_connectivity_probing_received; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_STATS_H_
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc new file mode 100644 index 0000000..62056a0 --- /dev/null +++ b/quic/core/quic_connection_test.cc
@@ -0,0 +1,7826 @@ +// Copyright (c) 2012 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/quic_connection.h" + +#include <errno.h> +#include <memory> +#include <ostream> +#include <utility> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.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_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h" + +using testing::_; +using testing::AnyNumber; +using testing::AtLeast; +using testing::DoAll; +using testing::Exactly; +using testing::Ge; +using testing::IgnoreResult; +using testing::InSequence; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::Lt; +using testing::Ref; +using testing::Return; +using testing::SaveArg; +using testing::SetArgPointee; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +const char data1[] = "foo"; +const char data2[] = "bar"; + +const bool kHasStopWaiting = true; + +const int kDefaultRetransmissionTimeMs = 500; + +const QuicSocketAddress kPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), + /*port=*/12345); +const QuicSocketAddress kSelfAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), + /*port=*/443); + +Perspective InvertPerspective(Perspective perspective) { + return perspective == Perspective::IS_CLIENT ? Perspective::IS_SERVER + : Perspective::IS_CLIENT; +} + +QuicStreamId GetNthClientInitiatedStreamId(int n, + QuicTransportVersion version) { + return QuicUtils::GetHeadersStreamId(version) + n * 2; +} + +// TaggingEncrypter appends kTagSize bytes of |tag| to the end of each message. +class TaggingEncrypter : public QuicEncrypter { + public: + explicit TaggingEncrypter(uint8_t tag) : tag_(tag) {} + TaggingEncrypter(const TaggingEncrypter&) = delete; + TaggingEncrypter& operator=(const TaggingEncrypter&) = delete; + + ~TaggingEncrypter() override {} + + // QuicEncrypter interface. + bool SetKey(QuicStringPiece key) override { return true; } + + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; } + + bool SetIV(QuicStringPiece iv) override { return true; } + + bool EncryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + char* output, + size_t* output_length, + size_t max_output_length) override { + const size_t len = plaintext.size() + kTagSize; + if (max_output_length < len) { + return false; + } + // Memmove is safe for inplace encryption. + memmove(output, plaintext.data(), plaintext.size()); + output += plaintext.size(); + memset(output, tag_, kTagSize); + *output_length = len; + return true; + } + + size_t GetKeySize() const override { return 0; } + size_t GetNoncePrefixSize() const override { return 0; } + size_t GetIVSize() const override { return 0; } + + size_t GetMaxPlaintextSize(size_t ciphertext_size) const override { + return ciphertext_size - kTagSize; + } + + size_t GetCiphertextSize(size_t plaintext_size) const override { + return plaintext_size + kTagSize; + } + + QuicStringPiece GetKey() const override { return QuicStringPiece(); } + + QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); } + + private: + enum { + kTagSize = 12, + }; + + const uint8_t tag_; +}; + +// TaggingDecrypter ensures that the final kTagSize bytes of the message all +// have the same value and then removes them. +class TaggingDecrypter : public QuicDecrypter { + public: + ~TaggingDecrypter() override {} + + // QuicDecrypter interface + bool SetKey(QuicStringPiece key) override { return true; } + + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; } + + bool SetIV(QuicStringPiece iv) override { return true; } + + bool SetPreliminaryKey(QuicStringPiece key) override { + QUIC_BUG << "should not be called"; + return false; + } + + bool SetDiversificationNonce(const DiversificationNonce& key) override { + return true; + } + + bool DecryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) override { + if (ciphertext.size() < kTagSize) { + return false; + } + if (!CheckTag(ciphertext, GetTag(ciphertext))) { + return false; + } + *output_length = ciphertext.size() - kTagSize; + memcpy(output, ciphertext.data(), *output_length); + return true; + } + + size_t GetKeySize() const override { return 0; } + size_t GetIVSize() const override { return 0; } + QuicStringPiece GetKey() const override { return QuicStringPiece(); } + QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); } + // Use a distinct value starting with 0xFFFFFF, which is never used by TLS. + uint32_t cipher_id() const override { return 0xFFFFFFF0; } + + protected: + virtual uint8_t GetTag(QuicStringPiece ciphertext) { + return ciphertext.data()[ciphertext.size() - 1]; + } + + private: + enum { + kTagSize = 12, + }; + + bool CheckTag(QuicStringPiece ciphertext, uint8_t tag) { + for (size_t i = ciphertext.size() - kTagSize; i < ciphertext.size(); i++) { + if (ciphertext.data()[i] != tag) { + return false; + } + } + + return true; + } +}; + +// StringTaggingDecrypter ensures that the final kTagSize bytes of the message +// match the expected value. +class StrictTaggingDecrypter : public TaggingDecrypter { + public: + explicit StrictTaggingDecrypter(uint8_t tag) : tag_(tag) {} + ~StrictTaggingDecrypter() override {} + + // TaggingQuicDecrypter + uint8_t GetTag(QuicStringPiece ciphertext) override { return tag_; } + + // Use a distinct value starting with 0xFFFFFF, which is never used by TLS. + uint32_t cipher_id() const override { return 0xFFFFFFF1; } + + private: + const uint8_t tag_; +}; + +class TestConnectionHelper : public QuicConnectionHelperInterface { + public: + TestConnectionHelper(MockClock* clock, MockRandom* random_generator) + : clock_(clock), random_generator_(random_generator) { + clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } + TestConnectionHelper(const TestConnectionHelper&) = delete; + TestConnectionHelper& operator=(const TestConnectionHelper&) = delete; + + // QuicConnectionHelperInterface + const QuicClock* GetClock() const override { return clock_; } + + QuicRandom* GetRandomGenerator() override { return random_generator_; } + + QuicBufferAllocator* GetStreamSendBufferAllocator() override { + return &buffer_allocator_; + } + + private: + MockClock* clock_; + MockRandom* random_generator_; + SimpleBufferAllocator buffer_allocator_; +}; + +class TestAlarmFactory : public QuicAlarmFactory { + public: + class TestAlarm : public QuicAlarm { + public: + explicit TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate) + : QuicAlarm(std::move(delegate)) {} + + void SetImpl() override {} + void CancelImpl() override {} + using QuicAlarm::Fire; + }; + + TestAlarmFactory() {} + TestAlarmFactory(const TestAlarmFactory&) = delete; + TestAlarmFactory& operator=(const TestAlarmFactory&) = delete; + + QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override { + return new TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)); + } + + QuicArenaScopedPtr<QuicAlarm> CreateAlarm( + QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, + QuicConnectionArena* arena) override { + return arena->New<TestAlarm>(std::move(delegate)); + } +}; + +class TestPacketWriter : public QuicPacketWriter { + public: + TestPacketWriter(ParsedQuicVersion version, MockClock* clock) + : version_(version), + framer_(SupportedVersions(version_), Perspective::IS_SERVER), + last_packet_size_(0), + write_blocked_(false), + write_should_fail_(false), + block_on_next_flush_(false), + block_on_next_write_(false), + next_packet_too_large_(false), + always_get_packet_too_large_(false), + is_write_blocked_data_buffered_(false), + is_batch_mode_(false), + final_bytes_of_last_packet_(0), + final_bytes_of_previous_packet_(0), + use_tagging_decrypter_(false), + packets_write_attempts_(0), + clock_(clock), + write_pause_time_delta_(QuicTime::Delta::Zero()), + max_packet_size_(kMaxPacketSize), + supports_release_time_(false) {} + TestPacketWriter(const TestPacketWriter&) = delete; + TestPacketWriter& operator=(const TestPacketWriter&) = delete; + + // QuicPacketWriter interface + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) override { + QuicEncryptedPacket packet(buffer, buf_len); + ++packets_write_attempts_; + + if (packet.length() >= sizeof(final_bytes_of_last_packet_)) { + final_bytes_of_previous_packet_ = final_bytes_of_last_packet_; + memcpy(&final_bytes_of_last_packet_, packet.data() + packet.length() - 4, + sizeof(final_bytes_of_last_packet_)); + } + + if (use_tagging_decrypter_) { + framer_.framer()->SetDecrypter(ENCRYPTION_NONE, + QuicMakeUnique<TaggingDecrypter>()); + } + EXPECT_TRUE(framer_.ProcessPacket(packet)); + if (block_on_next_write_) { + write_blocked_ = true; + block_on_next_write_ = false; + } + if (next_packet_too_large_) { + next_packet_too_large_ = false; + return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE); + } + if (always_get_packet_too_large_) { + return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE); + } + if (IsWriteBlocked()) { + return WriteResult(is_write_blocked_data_buffered_ + ? WRITE_STATUS_BLOCKED_DATA_BUFFERED + : WRITE_STATUS_BLOCKED, + 0); + } + + if (ShouldWriteFail()) { + return WriteResult(WRITE_STATUS_ERROR, 0); + } + + last_packet_size_ = packet.length(); + last_packet_header_ = framer_.header(); + + if (!write_pause_time_delta_.IsZero()) { + clock_->AdvanceTime(write_pause_time_delta_); + } + return WriteResult(WRITE_STATUS_OK, last_packet_size_); + } + + bool ShouldWriteFail() { return write_should_fail_; } + + bool IsWriteBlocked() const override { return write_blocked_; } + + void SetWriteBlocked() { write_blocked_ = true; } + + void SetWritable() override { write_blocked_ = false; } + + void SetShouldWriteFail() { write_should_fail_ = true; } + + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& /*peer_address*/) const override { + return max_packet_size_; + } + + bool SupportsReleaseTime() const { return supports_release_time_; } + + bool IsBatchMode() const override { return is_batch_mode_; } + + char* GetNextWriteLocation(const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override { + return nullptr; + } + + WriteResult Flush() override { + if (block_on_next_flush_) { + block_on_next_flush_ = false; + SetWriteBlocked(); + return WriteResult(WRITE_STATUS_BLOCKED, /*errno*/ -1); + } + return WriteResult(WRITE_STATUS_OK, 0); + } + + void BlockOnNextFlush() { block_on_next_flush_ = true; } + + void BlockOnNextWrite() { block_on_next_write_ = true; } + + void SimulateNextPacketTooLarge() { next_packet_too_large_ = true; } + + void AlwaysGetPacketTooLarge() { always_get_packet_too_large_ = true; } + + // Sets the amount of time that the writer should before the actual write. + void SetWritePauseTimeDelta(QuicTime::Delta delta) { + write_pause_time_delta_ = delta; + } + + void SetBatchMode(bool new_value) { is_batch_mode_ = new_value; } + + const QuicPacketHeader& header() { return framer_.header(); } + + size_t frame_count() const { return framer_.num_frames(); } + + const std::vector<QuicAckFrame>& ack_frames() const { + return framer_.ack_frames(); + } + + const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const { + return framer_.stop_waiting_frames(); + } + + const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const { + return framer_.connection_close_frames(); + } + + const std::vector<QuicRstStreamFrame>& rst_stream_frames() const { + return framer_.rst_stream_frames(); + } + + const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const { + return framer_.stream_frames(); + } + + const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const { + return framer_.crypto_frames(); + } + + const std::vector<QuicPingFrame>& ping_frames() const { + return framer_.ping_frames(); + } + + const std::vector<QuicMessageFrame>& message_frames() const { + return framer_.message_frames(); + } + + const std::vector<QuicWindowUpdateFrame>& window_update_frames() const { + return framer_.window_update_frames(); + } + + const std::vector<QuicPaddingFrame>& padding_frames() const { + return framer_.padding_frames(); + } + + const std::vector<QuicPathChallengeFrame>& path_challenge_frames() const { + return framer_.path_challenge_frames(); + } + + const std::vector<QuicPathResponseFrame>& path_response_frames() const { + return framer_.path_response_frames(); + } + + size_t last_packet_size() { return last_packet_size_; } + + const QuicPacketHeader& last_packet_header() const { + return last_packet_header_; + } + + const QuicVersionNegotiationPacket* version_negotiation_packet() { + return framer_.version_negotiation_packet(); + } + + void set_is_write_blocked_data_buffered(bool buffered) { + is_write_blocked_data_buffered_ = buffered; + } + + void set_perspective(Perspective perspective) { + // We invert perspective here, because the framer needs to parse packets + // we send. + QuicFramerPeer::SetPerspective(framer_.framer(), + InvertPerspective(perspective)); + } + + // final_bytes_of_last_packet_ returns the last four bytes of the previous + // packet as a little-endian, uint32_t. This is intended to be used with a + // TaggingEncrypter so that tests can determine which encrypter was used for + // a given packet. + uint32_t final_bytes_of_last_packet() { return final_bytes_of_last_packet_; } + + // Returns the final bytes of the second to last packet. + uint32_t final_bytes_of_previous_packet() { + return final_bytes_of_previous_packet_; + } + + void use_tagging_decrypter() { use_tagging_decrypter_ = true; } + + uint32_t packets_write_attempts() { return packets_write_attempts_; } + + void Reset() { framer_.Reset(); } + + void SetSupportedVersions(const ParsedQuicVersionVector& versions) { + framer_.SetSupportedVersions(versions); + } + + void set_max_packet_size(QuicByteCount max_packet_size) { + max_packet_size_ = max_packet_size; + } + + void set_supports_release_time(bool supports_release_time) { + supports_release_time_ = supports_release_time; + } + + SimpleQuicFramer* framer() { return &framer_; } + + private: + ParsedQuicVersion version_; + SimpleQuicFramer framer_; + size_t last_packet_size_; + QuicPacketHeader last_packet_header_; + bool write_blocked_; + bool write_should_fail_; + bool block_on_next_flush_; + bool block_on_next_write_; + bool next_packet_too_large_; + bool always_get_packet_too_large_; + bool is_write_blocked_data_buffered_; + bool is_batch_mode_; + uint32_t final_bytes_of_last_packet_; + uint32_t final_bytes_of_previous_packet_; + bool use_tagging_decrypter_; + uint32_t packets_write_attempts_; + MockClock* clock_; + // If non-zero, the clock will pause during WritePacket for this amount of + // time. + QuicTime::Delta write_pause_time_delta_; + QuicByteCount max_packet_size_; + bool supports_release_time_; +}; + +class TestConnection : public QuicConnection { + public: + TestConnection(QuicConnectionId connection_id, + QuicSocketAddress address, + TestConnectionHelper* helper, + TestAlarmFactory* alarm_factory, + TestPacketWriter* writer, + Perspective perspective, + ParsedQuicVersion version) + : QuicConnection(connection_id, + address, + helper, + alarm_factory, + writer, + /* owns_writer= */ false, + perspective, + SupportedVersions(version)), + notifier_(nullptr) { + writer->set_perspective(perspective); + SetEncrypter(ENCRYPTION_FORWARD_SECURE, + QuicMakeUnique<NullEncrypter>(perspective)); + SetDataProducer(&producer_); + } + TestConnection(const TestConnection&) = delete; + TestConnection& operator=(const TestConnection&) = delete; + + void SendAck() { QuicConnectionPeer::SendAck(this); } + + void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { + QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); + } + + void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) { + QuicConnectionPeer::SetLossAlgorithm(this, loss_algorithm); + } + + void SendPacket(EncryptionLevel level, + uint64_t packet_number, + std::unique_ptr<QuicPacket> packet, + HasRetransmittableData retransmittable, + bool has_ack, + bool has_pending_frames) { + char buffer[kMaxPacketSize]; + size_t encrypted_length = + QuicConnectionPeer::GetFramer(this)->EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(packet_number), *packet, buffer, + kMaxPacketSize); + SerializedPacket serialized_packet( + QuicPacketNumber(packet_number), PACKET_4BYTE_PACKET_NUMBER, buffer, + encrypted_length, has_ack, has_pending_frames); + if (retransmittable == HAS_RETRANSMITTABLE_DATA) { + serialized_packet.retransmittable_frames.push_back( + QuicFrame(QuicStreamFrame())); + } + OnSerializedPacket(&serialized_packet); + } + + QuicConsumedData SaveAndSendStreamData(QuicStreamId id, + const struct iovec* iov, + int iov_count, + size_t total_length, + QuicStreamOffset offset, + StreamSendingState state) { + ScopedPacketFlusher flusher(this, NO_ACK); + producer_.SaveStreamData(id, iov, iov_count, 0u, total_length); + if (notifier_ != nullptr) { + return notifier_->WriteOrBufferData(id, total_length, state); + } + return QuicConnection::SendStreamData(id, total_length, offset, state); + } + + QuicConsumedData SendStreamDataWithString(QuicStreamId id, + QuicStringPiece data, + QuicStreamOffset offset, + StreamSendingState state) { + ScopedPacketFlusher flusher(this, NO_ACK); + if (id != QuicUtils::GetCryptoStreamId(transport_version()) && + this->encryption_level() == ENCRYPTION_NONE) { + this->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + } + struct iovec iov; + MakeIOVector(data, &iov); + return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state); + } + + QuicConsumedData SendStreamData3() { + return SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, transport_version()), "food", 0, + NO_FIN); + } + + QuicConsumedData SendStreamData5() { + return SendStreamDataWithString( + GetNthClientInitiatedStreamId(2, transport_version()), "food2", 0, + NO_FIN); + } + + // Ensures the connection can write stream data before writing. + QuicConsumedData EnsureWritableAndSendStreamData5() { + EXPECT_TRUE(CanWriteStreamData()); + return SendStreamData5(); + } + + // The crypto stream has special semantics so that it is not blocked by a + // congestion window limitation, and also so that it gets put into a separate + // packet (so that it is easier to reason about a crypto frame not being + // split needlessly across packet boundaries). As a result, we have separate + // tests for some cases for this stream. + QuicConsumedData SendCryptoStreamData() { + QuicStreamOffset offset = 0; + QuicStringPiece data("chlo"); + if (transport_version() < QUIC_VERSION_47) { + return SendStreamDataWithString( + QuicUtils::GetCryptoStreamId(transport_version()), data, offset, + NO_FIN); + } + producer_.SaveCryptoData(ENCRYPTION_NONE, offset, data); + size_t bytes_written; + if (notifier_) { + bytes_written = + notifier_->WriteCryptoData(ENCRYPTION_NONE, data.length(), offset); + } else { + bytes_written = QuicConnection::SendCryptoData(ENCRYPTION_NONE, + data.length(), offset); + } + return QuicConsumedData(bytes_written, /*fin_consumed*/ false); + } + + void set_version(ParsedQuicVersion version) { + QuicConnectionPeer::GetFramer(this)->set_version(version); + } + + void SetSupportedVersions(const ParsedQuicVersionVector& versions) { + QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions); + QuicConnectionPeer::SetNoVersionNegotiation(this, versions.size() == 1); + writer()->SetSupportedVersions(versions); + } + + void set_perspective(Perspective perspective) { + writer()->set_perspective(perspective); + QuicConnectionPeer::SetPerspective(this, perspective); + } + + // Enable path MTU discovery. Assumes that the test is performed from the + // client perspective and the higher value of MTU target is used. + void EnablePathMtuDiscovery(MockSendAlgorithm* send_algorithm) { + ASSERT_EQ(Perspective::IS_CLIENT, perspective()); + + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kMTUH); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm, SetFromConfig(_, _)); + SetFromConfig(config); + + // Normally, the pacing would be disabled in the test, but calling + // SetFromConfig enables it. Set nearly-infinite bandwidth to make the + // pacing algorithm work. + EXPECT_CALL(*send_algorithm, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Infinite())); + } + + TestAlarmFactory::TestAlarm* GetAckAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetAckAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetPingAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetPingAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetRetransmissionAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetRetransmissionAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetSendAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetSendAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetTimeoutAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetTimeoutAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetMtuDiscoveryAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetMtuDiscoveryAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetPathDegradingAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetPathDegradingAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetProcessUndecryptablePacketsAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(this)); + } + + void SetMaxTailLossProbes(size_t max_tail_loss_probes) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes( + QuicConnectionPeer::GetSentPacketManager(this), max_tail_loss_probes); + } + + QuicByteCount GetBytesInFlight() { + return QuicSentPacketManagerPeer::GetBytesInFlight( + QuicConnectionPeer::GetSentPacketManager(this)); + } + + void set_notifier(SimpleSessionNotifier* notifier) { notifier_ = notifier; } + + void ReturnEffectivePeerAddressForNextPacket(const QuicSocketAddress& addr) { + next_effective_peer_addr_ = QuicMakeUnique<QuicSocketAddress>(addr); + } + + using QuicConnection::active_effective_peer_migration_type; + using QuicConnection::IsCurrentPacketConnectivityProbing; + using QuicConnection::SelectMutualVersion; + using QuicConnection::SendProbingRetransmissions; + using QuicConnection::set_defer_send_in_response_to_packets; + + protected: + QuicSocketAddress GetEffectivePeerAddressFromCurrentPacket() const override { + if (next_effective_peer_addr_) { + return *std::move(next_effective_peer_addr_); + } + return QuicConnection::GetEffectivePeerAddressFromCurrentPacket(); + } + + private: + TestPacketWriter* writer() { + return static_cast<TestPacketWriter*>(QuicConnection::writer()); + } + + SimpleDataProducer producer_; + + SimpleSessionNotifier* notifier_; + + std::unique_ptr<QuicSocketAddress> next_effective_peer_addr_; +}; + +enum class AckResponse { kDefer, kImmediate }; + +// Run tests with combinations of {ParsedQuicVersion, AckResponse}. +struct TestParams { + TestParams(ParsedQuicVersion version, + AckResponse ack_response, + bool no_stop_waiting) + : version(version), + ack_response(ack_response), + no_stop_waiting(no_stop_waiting) {} + + friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { + os << "{ client_version: " << ParsedQuicVersionToString(p.version) + << " ack_response: " + << (p.ack_response == AckResponse::kDefer ? "defer" : "immediate") + << " no_stop_waiting: " << p.no_stop_waiting << " }"; + return os; + } + + ParsedQuicVersion version; + AckResponse ack_response; + bool no_stop_waiting; +}; + +// Constructs various test permutations. +std::vector<TestParams> GetTestParams() { + QuicFlagSaver flags; + SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true); + std::vector<TestParams> params; + ParsedQuicVersionVector all_supported_versions = AllSupportedVersions(); + for (size_t i = 0; i < all_supported_versions.size(); ++i) { + for (AckResponse ack_response : + {AckResponse::kDefer, AckResponse::kImmediate}) { + for (bool no_stop_waiting : {true, false}) { + // After version 43, never use STOP_WAITING. + if (all_supported_versions[i].transport_version <= QUIC_VERSION_43 || + no_stop_waiting) { + params.push_back(TestParams(all_supported_versions[i], ack_response, + no_stop_waiting)); + } + } + } + } + return params; +} + +class QuicConnectionTest : public QuicTestWithParam<TestParams> { + protected: + QuicConnectionTest() + : connection_id_(TestConnectionId()), + framer_(SupportedVersions(version()), + QuicTime::Zero(), + Perspective::IS_CLIENT, + connection_id_.length()), + send_algorithm_(new StrictMock<MockSendAlgorithm>), + loss_algorithm_(new MockLossAlgorithm()), + helper_(new TestConnectionHelper(&clock_, &random_generator_)), + alarm_factory_(new TestAlarmFactory()), + peer_framer_(SupportedVersions(version()), + QuicTime::Zero(), + Perspective::IS_SERVER, + connection_id_.length()), + peer_creator_(connection_id_, + &peer_framer_, + /*delegate=*/nullptr), + writer_(new TestPacketWriter(version(), &clock_)), + connection_(connection_id_, + kPeerAddress, + helper_.get(), + alarm_factory_.get(), + writer_.get(), + Perspective::IS_CLIENT, + version()), + creator_(QuicConnectionPeer::GetPacketCreator(&connection_)), + generator_(QuicConnectionPeer::GetPacketGenerator(&connection_)), + manager_(QuicConnectionPeer::GetSentPacketManager(&connection_)), + frame1_(QuicUtils::GetCryptoStreamId(version().transport_version), + false, + 0, + QuicStringPiece(data1)), + frame2_(QuicUtils::GetCryptoStreamId(version().transport_version), + false, + 3, + QuicStringPiece(data2)), + packet_number_length_(PACKET_4BYTE_PACKET_NUMBER), + connection_id_included_(CONNECTION_ID_PRESENT), + notifier_(&connection_) { + SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true); + connection_.set_defer_send_in_response_to_packets(GetParam().ack_response == + AckResponse::kDefer); + QuicFramerPeer::SetLastSerializedConnectionId( + QuicConnectionPeer::GetFramer(&connection_), connection_id_); + if (version().transport_version > QUIC_VERSION_43) { + EXPECT_TRUE(QuicConnectionPeer::GetNoStopWaitingFrames(&connection_)); + } else { + QuicConnectionPeer::SetNoStopWaitingFrames(&connection_, + GetParam().no_stop_waiting); + } + connection_.set_visitor(&visitor_); + if (connection_.session_decides_what_to_write()) { + connection_.SetSessionNotifier(¬ifier_); + connection_.set_notifier(¬ifier_); + } + connection_.SetSendAlgorithm(send_algorithm_); + connection_.SetLossAlgorithm(loss_algorithm_.get()); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(kDefaultTCPMSS)); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, HasReliableBandwidthEstimate()) + .Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()) + .Times(AnyNumber()) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber()); + EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber()); + if (connection_.session_decides_what_to_write()) { + EXPECT_CALL(visitor_, OnCanWrite()) + .WillRepeatedly( + Invoke(¬ifier_, &SimpleSessionNotifier::OnCanWrite)); + } else { + EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber()); + } + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(AnyNumber()); + + EXPECT_CALL(*loss_algorithm_, GetLossTimeout()) + .WillRepeatedly(Return(QuicTime::Zero())); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .Times(AnyNumber()); + } + + QuicConnectionTest(const QuicConnectionTest&) = delete; + QuicConnectionTest& operator=(const QuicConnectionTest&) = delete; + + ParsedQuicVersion version() { return GetParam().version; } + + QuicAckFrame* outgoing_ack() { + QuicFrame ack_frame = QuicConnectionPeer::GetUpdatedAckFrame(&connection_); + ack_ = *ack_frame.ack_frame; + return &ack_; + } + + QuicStopWaitingFrame* stop_waiting() { + QuicConnectionPeer::PopulateStopWaitingFrame(&connection_, &stop_waiting_); + return &stop_waiting_; + } + + QuicPacketNumber least_unacked() { + if (writer_->stop_waiting_frames().empty()) { + return QuicPacketNumber(); + } + return writer_->stop_waiting_frames()[0].least_unacked; + } + + void use_tagging_decrypter() { writer_->use_tagging_decrypter(); } + + void ProcessPacket(uint64_t number) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(number); + if (connection_.GetSendAlarm()->IsSet()) { + connection_.GetSendAlarm()->Fire(); + } + } + + void ProcessReceivedPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet) { + connection_.ProcessUdpPacket(self_address, peer_address, packet); + if (connection_.GetSendAlarm()->IsSet()) { + connection_.GetSendAlarm()->Fire(); + } + } + + void ProcessFramePacket(QuicFrame frame) { + ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); + } + + void ProcessFramePacketWithAddresses(QuicFrame frame, + QuicSocketAddress self_address, + QuicSocketAddress peer_address) { + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + QuicPacketCreatorPeer::SetSendVersionInPacket( + &peer_creator_, connection_.perspective() == Perspective::IS_SERVER); + if (QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) > + ENCRYPTION_NONE) { + // Set peer_framer_'s corresponding encrypter. + peer_creator_.SetEncrypter( + QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), + QuicMakeUnique<NullEncrypter>(peer_framer_.perspective())); + } + + char buffer[kMaxPacketSize]; + SerializedPacket serialized_packet = + QuicPacketCreatorPeer::SerializeAllFrames(&peer_creator_, frames, + buffer, kMaxPacketSize); + connection_.ProcessUdpPacket( + self_address, peer_address, + QuicReceivedPacket(serialized_packet.encrypted_buffer, + serialized_packet.encrypted_length, clock_.Now())); + if (connection_.GetSendAlarm()->IsSet()) { + connection_.GetSendAlarm()->Fire(); + } + } + + // Bypassing the packet creator is unrealistic, but allows us to process + // packets the QuicPacketCreator won't allow us to create. + void ForceProcessFramePacket(QuicFrame frame) { + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + QuicPacketCreatorPeer::SetSendVersionInPacket( + &peer_creator_, connection_.perspective() == Perspective::IS_SERVER); + QuicPacketHeader header; + QuicPacketCreatorPeer::FillPacketHeader(&peer_creator_, &header); + char encrypted_buffer[kMaxPacketSize]; + size_t length = peer_framer_.BuildDataPacket( + header, frames, encrypted_buffer, kMaxPacketSize, ENCRYPTION_NONE); + DCHECK_GT(length, 0u); + + const size_t encrypted_length = peer_framer_.EncryptInPlace( + ENCRYPTION_NONE, header.packet_number, + GetStartOfEncryptedData(peer_framer_.version().transport_version, + header), + length, kMaxPacketSize, encrypted_buffer); + DCHECK_GT(encrypted_length, 0u); + + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(encrypted_buffer, encrypted_length, clock_.Now())); + } + + size_t ProcessFramePacketAtLevel(uint64_t number, + QuicFrame frame, + EncryptionLevel level) { + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + header.packet_number_length = packet_number_length_; + header.destination_connection_id_included = connection_id_included_; + if (peer_framer_.transport_version() > QUIC_VERSION_43 && + peer_framer_.perspective() == Perspective::IS_SERVER) { + header.destination_connection_id_included = CONNECTION_ID_ABSENT; + } + header.packet_number = QuicPacketNumber(number); + QuicFrames frames; + frames.push_back(frame); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + + char buffer[kMaxPacketSize]; + size_t encrypted_length = framer_.EncryptPayload( + level, QuicPacketNumber(number), *packet, buffer, kMaxPacketSize); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); + return encrypted_length; + } + + size_t ProcessDataPacket(uint64_t number) { + return ProcessDataPacketAtLevel(number, false, ENCRYPTION_NONE); + } + + size_t ProcessDataPacket(QuicPacketNumber packet_number) { + return ProcessDataPacketAtLevel(packet_number, false, ENCRYPTION_NONE); + } + + size_t ProcessDataPacketAtLevel(QuicPacketNumber packet_number, + bool has_stop_waiting, + EncryptionLevel level) { + return ProcessDataPacketAtLevel(packet_number.ToUint64(), has_stop_waiting, + level); + } + + size_t ProcessDataPacketAtLevel(uint64_t number, + bool has_stop_waiting, + EncryptionLevel level) { + std::unique_ptr<QuicPacket> packet( + ConstructDataPacket(number, has_stop_waiting)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + level, QuicPacketNumber(number), *packet, buffer, kMaxPacketSize); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); + if (connection_.GetSendAlarm()->IsSet()) { + connection_.GetSendAlarm()->Fire(); + } + return encrypted_length; + } + + void ProcessClosePacket(uint64_t number) { + std::unique_ptr<QuicPacket> packet(ConstructClosePacket(number)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = + peer_framer_.EncryptPayload(ENCRYPTION_NONE, QuicPacketNumber(number), + *packet, buffer, kMaxPacketSize); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); + } + + QuicByteCount SendStreamDataToPeer(QuicStreamId id, + QuicStringPiece data, + QuicStreamOffset offset, + StreamSendingState state, + QuicPacketNumber* last_packet) { + QuicByteCount packet_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<3>(&packet_size)); + connection_.SendStreamDataWithString(id, data, offset, state); + if (last_packet != nullptr) { + *last_packet = creator_->packet_number(); + } + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .Times(AnyNumber()); + return packet_size; + } + + void SendAckPacketToPeer() { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + { + QuicConnection::ScopedPacketFlusher flusher(&connection_, + QuicConnection::NO_ACK); + connection_.SendAck(); + } + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .Times(AnyNumber()); + } + + void SendRstStream(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) { + if (connection_.session_decides_what_to_write()) { + notifier_.WriteOrBufferRstStream(id, error, bytes_written); + connection_.OnStreamReset(id, error); + return; + } + std::unique_ptr<QuicRstStreamFrame> rst_stream = + QuicMakeUnique<QuicRstStreamFrame>(1, id, error, bytes_written); + if (connection_.SendControlFrame(QuicFrame(rst_stream.get()))) { + rst_stream.release(); + } + connection_.OnStreamReset(id, error); + } + + void ProcessAckPacket(uint64_t packet_number, QuicAckFrame* frame) { + if (packet_number > 1) { + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, packet_number - 1); + } else { + QuicPacketCreatorPeer::ClearPacketNumber(&peer_creator_); + } + ProcessFramePacket(QuicFrame(frame)); + } + + void ProcessAckPacket(QuicAckFrame* frame) { + ProcessFramePacket(QuicFrame(frame)); + } + + void ProcessStopWaitingPacket(QuicStopWaitingFrame frame) { + ProcessFramePacket(QuicFrame(frame)); + } + + size_t ProcessStopWaitingPacketAtLevel(uint64_t number, + QuicStopWaitingFrame frame, + EncryptionLevel level) { + return ProcessFramePacketAtLevel(number, QuicFrame(frame), + ENCRYPTION_ZERO_RTT); + } + + void ProcessGoAwayPacket(QuicGoAwayFrame* frame) { + ProcessFramePacket(QuicFrame(frame)); + } + + bool IsMissing(uint64_t number) { + return IsAwaitingPacket(*outgoing_ack(), QuicPacketNumber(number), + QuicPacketNumber()); + } + + std::unique_ptr<QuicPacket> ConstructPacket(const QuicPacketHeader& header, + const QuicFrames& frames) { + auto packet = BuildUnsizedDataPacket(&peer_framer_, header, frames); + EXPECT_NE(nullptr, packet.get()); + return packet; + } + + std::unique_ptr<QuicPacket> ConstructDataPacket(uint64_t number, + bool has_stop_waiting) { + QuicPacketHeader header; + // Set connection_id to peer's in memory representation as this data packet + // is created by peer_framer. + header.destination_connection_id = connection_id_; + header.packet_number_length = packet_number_length_; + header.destination_connection_id_included = connection_id_included_; + if (peer_framer_.transport_version() > QUIC_VERSION_43 && + peer_framer_.perspective() == Perspective::IS_SERVER) { + header.destination_connection_id_included = CONNECTION_ID_ABSENT; + } + header.packet_number = QuicPacketNumber(number); + + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + if (has_stop_waiting) { + frames.push_back(QuicFrame(stop_waiting_)); + } + return ConstructPacket(header, frames); + } + + OwningSerializedPacketPointer ConstructProbingPacket() { + if (version().transport_version == QUIC_VERSION_99) { + QuicPathFrameBuffer payload = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; + return QuicPacketCreatorPeer:: + SerializePathChallengeConnectivityProbingPacket(&peer_creator_, + &payload); + } + return QuicPacketCreatorPeer::SerializeConnectivityProbingPacket( + &peer_creator_); + } + + std::unique_ptr<QuicPacket> ConstructClosePacket(uint64_t number) { + QuicPacketHeader header; + // Set connection_id to peer's in memory representation as this connection + // close packet is created by peer_framer. + header.destination_connection_id = connection_id_; + header.packet_number = QuicPacketNumber(number); + if (peer_framer_.transport_version() > QUIC_VERSION_43 && + peer_framer_.perspective() == Perspective::IS_SERVER) { + header.destination_connection_id_included = CONNECTION_ID_ABSENT; + } + + QuicConnectionCloseFrame qccf; + qccf.error_code = QUIC_PEER_GOING_AWAY; + + QuicFrames frames; + frames.push_back(QuicFrame(&qccf)); + return ConstructPacket(header, frames); + } + + QuicTime::Delta DefaultRetransmissionTime() { + return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); + } + + QuicTime::Delta DefaultDelayedAckTime() { + return QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + } + + const QuicStopWaitingFrame InitStopWaitingFrame(uint64_t least_unacked) { + QuicStopWaitingFrame frame; + frame.least_unacked = QuicPacketNumber(least_unacked); + return frame; + } + + // Construct a ack_frame that acks all packet numbers between 1 and + // |largest_acked|, except |missing|. + // REQUIRES: 1 <= |missing| < |largest_acked| + QuicAckFrame ConstructAckFrame(uint64_t largest_acked, uint64_t missing) { + return ConstructAckFrame(QuicPacketNumber(largest_acked), + QuicPacketNumber(missing)); + } + + QuicAckFrame ConstructAckFrame(QuicPacketNumber largest_acked, + QuicPacketNumber missing) { + if (missing == QuicPacketNumber(1)) { + return InitAckFrame({{missing + 1, largest_acked + 1}}); + } + return InitAckFrame( + {{QuicPacketNumber(1), missing}, {missing + 1, largest_acked + 1}}); + } + + // Undo nacking a packet within the frame. + void AckPacket(QuicPacketNumber arrived, QuicAckFrame* frame) { + EXPECT_FALSE(frame->packets.Contains(arrived)); + frame->packets.Add(arrived); + } + + void TriggerConnectionClose() { + // Send an erroneous packet to close the connection. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, _, + ConnectionCloseSource::FROM_SELF)); + // Call ProcessDataPacket rather than ProcessPacket, as we should not get a + // packet call to the visitor. + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + ProcessDataPacket(MaxRandomInitialPacketNumber() + 6000); + } else { + ProcessDataPacket(6000); + } + + EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == + nullptr); + } + + void BlockOnNextWrite() { + writer_->BlockOnNextWrite(); + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); + } + + void SimulateNextPacketTooLarge() { writer_->SimulateNextPacketTooLarge(); } + + void AlwaysGetPacketTooLarge() { writer_->AlwaysGetPacketTooLarge(); } + + void SetWritePauseTimeDelta(QuicTime::Delta delta) { + writer_->SetWritePauseTimeDelta(delta); + } + + void CongestionBlockWrites() { + EXPECT_CALL(*send_algorithm_, CanSend(_)) + .WillRepeatedly(testing::Return(false)); + } + + void CongestionUnblockWrites() { + EXPECT_CALL(*send_algorithm_, CanSend(_)) + .WillRepeatedly(testing::Return(true)); + } + + void set_perspective(Perspective perspective) { + connection_.set_perspective(perspective); + if (perspective == Perspective::IS_SERVER) { + connection_.set_can_truncate_connection_ids(true); + } + QuicFramerPeer::SetPerspective(&peer_framer_, + InvertPerspective(perspective)); + } + + void set_packets_between_probes_base( + const QuicPacketCount packets_between_probes_base) { + QuicConnectionPeer::SetPacketsBetweenMtuProbes(&connection_, + packets_between_probes_base); + QuicConnectionPeer::SetNextMtuProbeAt( + &connection_, QuicPacketNumber(packets_between_probes_base)); + } + + bool IsDefaultTestConfiguration() { + TestParams p = GetParam(); + return p.ack_response == AckResponse::kImmediate && + p.version == AllSupportedVersions()[0] && p.no_stop_waiting; + } + + QuicConnectionId connection_id_; + QuicFramer framer_; + + MockSendAlgorithm* send_algorithm_; + std::unique_ptr<MockLossAlgorithm> loss_algorithm_; + MockClock clock_; + MockRandom random_generator_; + SimpleBufferAllocator buffer_allocator_; + std::unique_ptr<TestConnectionHelper> helper_; + std::unique_ptr<TestAlarmFactory> alarm_factory_; + QuicFramer peer_framer_; + QuicPacketCreator peer_creator_; + std::unique_ptr<TestPacketWriter> writer_; + TestConnection connection_; + QuicPacketCreator* creator_; + QuicPacketGenerator* generator_; + QuicSentPacketManager* manager_; + StrictMock<MockQuicConnectionVisitor> visitor_; + + QuicStreamFrame frame1_; + QuicStreamFrame frame2_; + QuicAckFrame ack_; + QuicStopWaitingFrame stop_waiting_; + QuicPacketNumberLength packet_number_length_; + QuicConnectionIdIncluded connection_id_included_; + + SimpleSessionNotifier notifier_; +}; + +// Run all end to end tests with all supported versions. +INSTANTIATE_TEST_SUITE_P(SupportedVersion, + QuicConnectionTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QuicConnectionTest, SelfAddressChangeAtClient) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + EXPECT_TRUE(connection_.connected()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + // Cause change in self_address. + QuicIpAddress host; + host.FromString("1.1.1.1"); + QuicSocketAddress self_address(host, 123); + EXPECT_CALL(visitor_, OnStreamFrame(_)); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address, + kPeerAddress); + EXPECT_TRUE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, SelfAddressChangeAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + EXPECT_TRUE(connection_.connected()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + // Cause change in self_address. + QuicIpAddress host; + host.FromString("1.1.1.1"); + QuicSocketAddress self_address(host, 123); + EXPECT_CALL(visitor_, AllowSelfAddressChange()).WillOnce(Return(false)); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_ERROR_MIGRATING_ADDRESS, _, _)); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address, + kPeerAddress); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, AllowSelfAddressChangeToMappedIpv4AddressAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + EXPECT_TRUE(connection_.connected()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3); + QuicIpAddress host; + host.FromString("1.1.1.1"); + QuicSocketAddress self_address1(host, 443); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address1, + kPeerAddress); + // Cause self_address change to mapped Ipv4 address. + QuicIpAddress host2; + host2.FromString( + QuicStrCat("::ffff:", connection_.self_address().host().ToString())); + QuicSocketAddress self_address2(host2, connection_.self_address().port()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address2, + kPeerAddress); + EXPECT_TRUE(connection_.connected()); + // self_address change back to Ipv4 address. + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address1, + kPeerAddress); + EXPECT_TRUE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, ClientAddressChangeAndPacketReordered) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5); + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), + /*port=*/23456); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kNewPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); + + // Decrease packet number to simulate out-of-order packets. + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4); + // This is an old packet, do not migrate. + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, PeerAddressChangeAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Process another packet with a different peer address on server side will + // start connection migration. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kNewPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is different from direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + const QuicSocketAddress kEffectivePeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/43210); + connection_.ReturnEffectivePeerAddressForNextPacket(kEffectivePeerAddress); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kEffectivePeerAddress, connection_.effective_peer_address()); + + // Process another packet with the same direct peer address and different + // effective peer address on server side will start connection migration. + const QuicSocketAddress kNewEffectivePeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/54321); + connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress); + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); + + // Process another packet with a different direct peer address and the same + // effective peer address on server side will not start connection migration. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress); + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + // ack_frame is used to complete the migration started by the last packet, we + // need to make sure a new migration does not start after the previous one is + // completed. + QuicAckFrame ack_frame = InitAckFrame(1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); + ProcessFramePacketWithAddresses(QuicFrame(&ack_frame), kSelfAddress, + kNewPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); + + // Process another packet with different direct peer address and different + // effective peer address on server side will start connection migration. + const QuicSocketAddress kNewerEffectivePeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/65432); + const QuicSocketAddress kFinalPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/34567); + connection_.ReturnEffectivePeerAddressForNextPacket( + kNewerEffectivePeerAddress); + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kFinalPeerAddress); + EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewerEffectivePeerAddress, connection_.effective_peer_address()); + EXPECT_EQ(PORT_CHANGE, connection_.active_effective_peer_migration_type()); + + // While the previous migration is ongoing, process another packet with the + // same direct peer address and different effective peer address on server + // side will start a new connection migration. + const QuicSocketAddress kNewestEffectivePeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/65430); + connection_.ReturnEffectivePeerAddressForNextPacket( + kNewestEffectivePeerAddress); + EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); + EXPECT_CALL(*send_algorithm_, OnConnectionMigration()).Times(1); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kFinalPeerAddress); + EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewestEffectivePeerAddress, connection_.effective_peer_address()); + EXPECT_EQ(IPV6_TO_IPV4_CHANGE, + connection_.active_effective_peer_migration_type()); +} + +TEST_P(QuicConnectionTest, ReceivePaddedPingAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0); + + // Process a padded PING or PATH CHALLENGE packet with no peer address change + // on server side will be ignored. + OwningSerializedPacketPointer probing_packet; + if (version().transport_version == QUIC_VERSION_99) { + QuicPathFrameBuffer payload = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; + probing_packet = + QuicPacketCreatorPeer::SerializePathChallengeConnectivityProbingPacket( + &peer_creator_, &payload); + } else { + probing_packet = QuicPacketCreatorPeer::SerializeConnectivityProbingPacket( + &peer_creator_); + } + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + + uint64_t num_probing_received = + connection_.GetStats().num_connectivity_probing_received; + ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received); + + EXPECT_EQ(num_probing_received, + connection_.GetStats().num_connectivity_probing_received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, WriteOutOfOrderQueuedPackets) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (!IsDefaultTestConfiguration()) { + return; + } + + set_perspective(Perspective::IS_CLIENT); + + BlockOnNextWrite(); + + QuicStreamId stream_id = 2; + connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN); + + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + writer_->SetWritable(); + connection_.SendConnectivityProbingPacket(writer_.get(), + connection_.peer_address()); + + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INTERNAL_ERROR, + "Packet written out of order.", + ConnectionCloseSource::FROM_SELF)); + EXPECT_QUIC_BUG(connection_.OnCanWrite(), + "Attempt to write packet:1 after:2"); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, DiscardQueuedPacketsAfterConnectionClose) { + // Regression test for b/74073386. + { + InSequence seq; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1); + } + + set_perspective(Perspective::IS_CLIENT); + + writer_->SimulateNextPacketTooLarge(); + + // This packet write should fail, which should cause the connection to close + // after sending a connection close packet, then the failed packet should be + // queued. + connection_.SendStreamDataWithString(/*id=*/2, "foo", 0, NO_FIN); + + EXPECT_FALSE(connection_.connected()); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + EXPECT_EQ(0u, connection_.GetStats().packets_discarded); + connection_.OnCanWrite(); + EXPECT_EQ(1u, connection_.GetStats().packets_discarded); +} + +TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + + // Process a padded PING packet from a new peer address on server side + // is effectively receiving a connectivity probing. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + + OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + + uint64_t num_probing_received = + connection_.GetStats().num_connectivity_probing_received; + ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); + + EXPECT_EQ(num_probing_received + 1, + connection_.GetStats().num_connectivity_probing_received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Process another packet with the old peer address on server side will not + // start peer migration. + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, ReceiveReorderedConnectivityProbingAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5); + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Decrease packet number to simulate out-of-order packets. + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4); + + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + + // Process a padded PING packet from a new peer address on server side + // is effectively receiving a connectivity probing, even if a newer packet has + // been received before this one. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + + OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + + uint64_t num_probing_received = + connection_.GetStats().num_connectivity_probing_received; + ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); + + EXPECT_EQ(num_probing_received + 1, + connection_.GetStats().num_connectivity_probing_received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + + // Process a padded PING packet from a new peer address on server side + // is effectively receiving a connectivity probing. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + + OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Process another non-probing packet with the new peer address on server + // side will start peer migration. + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); + + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kNewPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, ReceivePaddedPingAtClient) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_CLIENT); + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Client takes all padded PING packet as speculative connectivity + // probing packet, and reports to visitor. + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + + OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + uint64_t num_probing_received = + connection_.GetStats().num_connectivity_probing_received; + ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received); + + EXPECT_EQ(num_probing_received, + connection_.GetStats().num_connectivity_probing_received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtClient) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_CLIENT); + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Process a padded PING packet with a different self address on client side + // is effectively receiving a connectivity probing. + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1); + + const QuicSocketAddress kNewSelfAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + + OwningSerializedPacketPointer probing_packet = ConstructProbingPacket(); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + uint64_t num_probing_received = + connection_.GetStats().num_connectivity_probing_received; + ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received); + + EXPECT_EQ(num_probing_received + 1, + connection_.GetStats().num_connectivity_probing_received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_CLIENT); + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Process another packet with a different peer address on client side will + // only update peer address. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kNewPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); +} + +TEST_P(QuicConnectionTest, MaxPacketSize) { + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + EXPECT_EQ(1350u, connection_.max_packet_length()); +} + +TEST_P(QuicConnectionTest, SmallerServerMaxPacketSize) { + TestConnection connection(TestConnectionId(), kPeerAddress, helper_.get(), + alarm_factory_.get(), writer_.get(), + Perspective::IS_SERVER, version()); + EXPECT_EQ(Perspective::IS_SERVER, connection.perspective()); + EXPECT_EQ(1000u, connection.max_packet_length()); +} + +TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSize) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + set_perspective(Perspective::IS_SERVER); + connection_.SetMaxPacketLength(1000); + + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + header.version_flag = true; + header.packet_number = QuicPacketNumber(1); + + if (QuicVersionHasLongHeaderLengths( + peer_framer_.version().transport_version)) { + header.long_packet_type = INITIAL; + header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + + QuicFrames frames; + QuicPaddingFrame padding; + frames.push_back(QuicFrame(frame1_)); + frames.push_back(QuicFrame(padding)); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize); + EXPECT_EQ(kMaxPacketSize, encrypted_length); + + framer_.set_version(version()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); + + EXPECT_EQ(kMaxPacketSize, connection_.max_packet_length()); +} + +TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSizeWhileWriterLimited) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + const QuicByteCount lower_max_packet_size = 1240; + writer_->set_max_packet_size(lower_max_packet_size); + set_perspective(Perspective::IS_SERVER); + connection_.SetMaxPacketLength(1000); + EXPECT_EQ(1000u, connection_.max_packet_length()); + + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + header.version_flag = true; + header.packet_number = QuicPacketNumber(1); + + if (QuicVersionHasLongHeaderLengths( + peer_framer_.version().transport_version)) { + header.long_packet_type = INITIAL; + header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + + QuicFrames frames; + QuicPaddingFrame padding; + frames.push_back(QuicFrame(frame1_)); + frames.push_back(QuicFrame(padding)); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize); + EXPECT_EQ(kMaxPacketSize, encrypted_length); + + framer_.set_version(version()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); + + // Here, the limit imposed by the writer is lower than the size of the packet + // received, so the writer max packet size is used. + EXPECT_EQ(lower_max_packet_size, connection_.max_packet_length()); +} + +TEST_P(QuicConnectionTest, LimitMaxPacketSizeByWriter) { + const QuicByteCount lower_max_packet_size = 1240; + writer_->set_max_packet_size(lower_max_packet_size); + + static_assert(lower_max_packet_size < kDefaultMaxPacketSize, + "Default maximum packet size is too low"); + connection_.SetMaxPacketLength(kDefaultMaxPacketSize); + + EXPECT_EQ(lower_max_packet_size, connection_.max_packet_length()); +} + +TEST_P(QuicConnectionTest, LimitMaxPacketSizeByWriterForNewConnection) { + const QuicConnectionId connection_id = TestConnectionId(17); + const QuicByteCount lower_max_packet_size = 1240; + writer_->set_max_packet_size(lower_max_packet_size); + TestConnection connection(connection_id, kPeerAddress, helper_.get(), + alarm_factory_.get(), writer_.get(), + Perspective::IS_CLIENT, version()); + EXPECT_EQ(Perspective::IS_CLIENT, connection.perspective()); + EXPECT_EQ(lower_max_packet_size, connection.max_packet_length()); +} + +TEST_P(QuicConnectionTest, PacketsInOrder) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + ProcessPacket(1); + EXPECT_EQ(QuicPacketNumber(1u), LargestAcked(*outgoing_ack())); + EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); + + ProcessPacket(2); + EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(*outgoing_ack())); + EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); + + ProcessPacket(3); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); +} + +TEST_P(QuicConnectionTest, PacketsOutOfOrder) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + ProcessPacket(3); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(2); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_FALSE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(1); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_FALSE(IsMissing(2)); + EXPECT_FALSE(IsMissing(1)); +} + +TEST_P(QuicConnectionTest, DuplicatePacket) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + ProcessPacket(3); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + // Send packet 3 again, but do not set the expectation that + // the visitor OnStreamFrame() will be called. + ProcessDataPacket(3); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); +} + +TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + ProcessPacket(3); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(2); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack())); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(5); + EXPECT_EQ(QuicPacketNumber(5u), LargestAcked(*outgoing_ack())); + EXPECT_TRUE(IsMissing(1)); + EXPECT_TRUE(IsMissing(4)); + + // Pretend at this point the client has gotten acks for 2 and 3 and 1 is a + // packet the peer will not retransmit. It indicates this by sending 'least + // awaiting' is 4. The connection should then realize 1 will not be + // retransmitted, and will remove it from the missing list. + QuicAckFrame frame = InitAckFrame(1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); + ProcessAckPacket(6, &frame); + + // Force an ack to be sent. + SendAckPacketToPeer(); + EXPECT_TRUE(IsMissing(4)); +} + +TEST_P(QuicConnectionTest, RejectPacketTooFarOut) { + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, _, + ConnectionCloseSource::FROM_SELF)); + + // Call ProcessDataPacket rather than ProcessPacket, as we should not get a + // packet call to the visitor. + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + ProcessDataPacket(MaxRandomInitialPacketNumber() + 6000); + } else { + ProcessDataPacket(6000); + } + EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == + nullptr); +} + +TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (!IsDefaultTestConfiguration()) { + return; + } + + // Process an unencrypted packet from the non-crypto stream. + frame1_.stream_id = 3; + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_QUIC_PEER_BUG(ProcessDataPacket(1), ""); + EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == + nullptr); + const std::vector<QuicConnectionCloseFrame>& connection_close_frames = + writer_->connection_close_frames(); + EXPECT_EQ(1u, connection_close_frames.size()); + EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA, + connection_close_frames[0].error_code); +} + +TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + ProcessPacket(3); + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + // Should not cause an ack. + EXPECT_EQ(0u, writer_->packets_write_attempts()); + } else { + // Should ack immediately since we have missing packets. + EXPECT_EQ(1u, writer_->packets_write_attempts()); + } + + ProcessPacket(2); + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + // Should ack immediately, since this fills the last hole. + EXPECT_EQ(1u, writer_->packets_write_attempts()); + } else { + // Should ack immediately since we have missing packets. + EXPECT_EQ(2u, writer_->packets_write_attempts()); + } + + ProcessPacket(1); + // Should ack immediately, since this fills the last hole. + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_EQ(2u, writer_->packets_write_attempts()); + } else { + EXPECT_EQ(3u, writer_->packets_write_attempts()); + } + + ProcessPacket(4); + // Should not cause an ack. + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_EQ(2u, writer_->packets_write_attempts()); + } else { + EXPECT_EQ(3u, writer_->packets_write_attempts()); + } +} + +TEST_P(QuicConnectionTest, OutOfOrderAckReceiptCausesNoAck) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); + SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr); + EXPECT_EQ(2u, writer_->packets_write_attempts()); + + QuicAckFrame ack1 = InitAckFrame(1); + QuicAckFrame ack2 = InitAckFrame(2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(2, &ack2); + // Should ack immediately since we have missing packets. + EXPECT_EQ(2u, writer_->packets_write_attempts()); + + ProcessAckPacket(1, &ack1); + // Should not ack an ack filling a missing packet. + EXPECT_EQ(2u, writer_->packets_write_attempts()); +} + +TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + QuicPacketNumber original, second; + + QuicByteCount packet_size = + SendStreamDataToPeer(3, "foo", 0, NO_FIN, &original); // 1st packet. + SendStreamDataToPeer(3, "bar", 3, NO_FIN, &second); // 2nd packet. + + QuicAckFrame frame = InitAckFrame({{second, second + 1}}); + // First nack triggers early retransmit. + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(original, kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicPacketNumber retransmission; + // Packet 1 is short header for IETF QUIC because the encryption level + // switched to ENCRYPTION_FORWARD_SECURE in SendStreamDataToPeer. + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, _, _, + GetParam().version.transport_version > QUIC_VERSION_43 + ? packet_size + : packet_size - kQuicVersionSize, + _)) + .WillOnce(SaveArg<2>(&retransmission)); + + ProcessAckPacket(&frame); + + QuicAckFrame frame2 = ConstructAckFrame(retransmission, original); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + ProcessAckPacket(&frame2); + + // Now if the peer sends an ack which still reports the retransmitted packet + // as missing, that will bundle an ack with data after two acks in a row + // indicate the high water mark needs to be raised. + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)); + connection_.SendStreamDataWithString(3, "foo", 6, NO_FIN); + // No ack sent. + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + + // No more packet loss for the rest of the test. + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .Times(AnyNumber()); + ProcessAckPacket(&frame2); + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)); + connection_.SendStreamDataWithString(3, "foo", 9, NO_FIN); + // Ack bundled. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(2u, writer_->frame_count()); + } else { + EXPECT_EQ(3u, writer_->frame_count()); + } + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_FALSE(writer_->ack_frames().empty()); + + // But an ack with no missing packets will not send an ack. + AckPacket(original, &frame2); + ProcessAckPacket(&frame2); + ProcessAckPacket(&frame2); +} + +TEST_P(QuicConnectionTest, AckSentEveryNthPacket) { + connection_.set_ack_frequency_before_ack_decimation(3); + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(39); + + // Expect 13 acks, every 3rd packet. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(13); + // Receives packets 1 - 39. + for (size_t i = 1; i <= 39; ++i) { + ProcessDataPacket(i); + } +} + +TEST_P(QuicConnectionTest, AckDecimationReducesAcks) { + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING); + + // Start ack decimation from 10th packet. + connection_.set_min_received_before_ack_decimation(10); + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(30); + + // Expect 6 acks: 5 acks between packets 1-10, and ack at 20. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6); + // Receives packets 1 - 29. + for (size_t i = 1; i <= 29; ++i) { + ProcessDataPacket(i); + } + + // We now receive the 30th packet, and so we send an ack. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + ProcessDataPacket(30); +} + +TEST_P(QuicConnectionTest, AckNeedsRetransmittableFrames) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(99); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(19); + // Receives packets 1 - 39. + for (size_t i = 1; i <= 39; ++i) { + ProcessDataPacket(i); + } + // Receiving Packet 40 causes 20th ack to send. Session is informed and adds + // WINDOW_UPDATE. + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()) + .WillOnce(Invoke([this]() { + connection_.SendControlFrame( + QuicFrame(new QuicWindowUpdateFrame(1, 0, 0))); + })); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + EXPECT_EQ(0u, writer_->window_update_frames().size()); + ProcessDataPacket(40); + EXPECT_EQ(1u, writer_->window_update_frames().size()); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(9); + // Receives packets 41 - 59. + for (size_t i = 41; i <= 59; ++i) { + ProcessDataPacket(i); + } + // Send a packet containing stream frame. + SendStreamDataToPeer(1, "bar", 0, NO_FIN, nullptr); + + // Session will not be informed until receiving another 20 packets. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(19); + for (size_t i = 60; i <= 98; ++i) { + ProcessDataPacket(i); + EXPECT_EQ(0u, writer_->window_update_frames().size()); + } + // Session does not add a retransmittable frame. + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()) + .WillOnce(Invoke([this]() { + connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); + })); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + EXPECT_EQ(0u, writer_->ping_frames().size()); + ProcessDataPacket(99); + EXPECT_EQ(0u, writer_->window_update_frames().size()); + // A ping frame will be added. + EXPECT_EQ(1u, writer_->ping_frames().size()); +} + +TEST_P(QuicConnectionTest, LeastUnackedLower) { + if (GetParam().version.transport_version > QUIC_VERSION_43) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); + SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr); + SendStreamDataToPeer(1, "eep", 6, NO_FIN, nullptr); + + // Start out saying the least unacked is 2. + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5); + ProcessStopWaitingPacket(InitStopWaitingFrame(2)); + + // Change it to 1, but lower the packet number to fake out-of-order packets. + // This should be fine. + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 1); + // The scheduler will not process out of order acks, but all packet processing + // causes the connection to try to write. + if (!GetParam().no_stop_waiting) { + EXPECT_CALL(visitor_, OnCanWrite()); + } + ProcessStopWaitingPacket(InitStopWaitingFrame(1)); + + // Now claim it's one, but set the ordering so it was sent "after" the first + // one. This should cause a connection error. + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 7); + if (!GetParam().no_stop_waiting) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, _, + ConnectionCloseSource::FROM_SELF)); + } + ProcessStopWaitingPacket(InitStopWaitingFrame(1)); +} + +TEST_P(QuicConnectionTest, TooManySentPackets) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + QuicPacketCount max_tracked_packets = 50; + QuicConnectionPeer::SetMaxTrackedPackets(&connection_, max_tracked_packets); + + const int num_packets = max_tracked_packets + 5; + + for (int i = 0; i < num_packets; ++i) { + SendStreamDataToPeer(1, "foo", 3 * i, NO_FIN, nullptr); + } + + // Ack packet 1, which leaves more than the limit outstanding. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(visitor_, + OnConnectionClosed(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, _, + ConnectionCloseSource::FROM_SELF)); + + // Nack the first packet and ack the rest, leaving a huge gap. + QuicAckFrame frame1 = ConstructAckFrame(num_packets, 1); + ProcessAckPacket(&frame1); +} + +TEST_P(QuicConnectionTest, LargestObservedLower) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); + SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr); + SendStreamDataToPeer(1, "eep", 6, NO_FIN, nullptr); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + + // Start out saying the largest observed is 2. + QuicAckFrame frame1 = InitAckFrame(1); + QuicAckFrame frame2 = InitAckFrame(2); + ProcessAckPacket(&frame2); + + // Now change it to 1, and it should cause a connection error. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(visitor_, OnCanWrite()).Times(0); + ProcessAckPacket(&frame1); +} + +TEST_P(QuicConnectionTest, AckUnsentData) { + // Ack a packet which has not been sent. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + QuicAckFrame frame = InitAckFrame(1); + EXPECT_CALL(visitor_, OnCanWrite()).Times(0); + ProcessAckPacket(&frame); +} + +TEST_P(QuicConnectionTest, BasicSending) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(1); + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2); + QuicPacketNumber last_packet; + SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); // Packet 1 + EXPECT_EQ(QuicPacketNumber(1u), last_packet); + SendAckPacketToPeer(); // Packet 2 + + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(1u), least_unacked()); + } + + SendAckPacketToPeer(); // Packet 3 + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(1u), least_unacked()); + } + + SendStreamDataToPeer(1, "bar", 3, NO_FIN, &last_packet); // Packet 4 + EXPECT_EQ(QuicPacketNumber(4u), last_packet); + SendAckPacketToPeer(); // Packet 5 + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(1u), least_unacked()); + } + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + + // Peer acks up to packet 3. + QuicAckFrame frame = InitAckFrame(3); + ProcessAckPacket(&frame); + SendAckPacketToPeer(); // Packet 6 + + // As soon as we've acked one, we skip ack packets 2 and 3 and note lack of + // ack for 4. + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(4u), least_unacked()); + } + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + + // Peer acks up to packet 4, the last packet. + QuicAckFrame frame2 = InitAckFrame(6); + ProcessAckPacket(&frame2); // Acks don't instigate acks. + + // Verify that we did not send an ack. + EXPECT_EQ(QuicPacketNumber(6u), writer_->header().packet_number); + + // So the last ack has not changed. + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(4u), least_unacked()); + } + + // If we force an ack, we shouldn't change our retransmit state. + SendAckPacketToPeer(); // Packet 7 + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(7u), least_unacked()); + } + + // But if we send more data it should. + SendStreamDataToPeer(1, "eep", 6, NO_FIN, &last_packet); // Packet 8 + EXPECT_EQ(QuicPacketNumber(8u), last_packet); + SendAckPacketToPeer(); // Packet 9 + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(7u), least_unacked()); + } +} + +// QuicConnection should record the packet sent-time prior to sending the +// packet. +TEST_P(QuicConnectionTest, RecordSentTimeBeforePacketSent) { + // We're using a MockClock for the tests, so we have complete control over the + // time. + // Our recorded timestamp for the last packet sent time will be passed in to + // the send_algorithm. Make sure that it is set to the correct value. + QuicTime actual_recorded_send_time = QuicTime::Zero(); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<0>(&actual_recorded_send_time)); + + // First send without any pause and check the result. + QuicTime expected_recorded_send_time = clock_.Now(); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + EXPECT_EQ(expected_recorded_send_time, actual_recorded_send_time) + << "Expected time = " << expected_recorded_send_time.ToDebuggingValue() + << ". Actual time = " << actual_recorded_send_time.ToDebuggingValue(); + + // Now pause during the write, and check the results. + actual_recorded_send_time = QuicTime::Zero(); + const QuicTime::Delta write_pause_time_delta = + QuicTime::Delta::FromMilliseconds(5000); + SetWritePauseTimeDelta(write_pause_time_delta); + expected_recorded_send_time = clock_.Now(); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<0>(&actual_recorded_send_time)); + connection_.SendStreamDataWithString(2, "baz", 0, NO_FIN); + EXPECT_EQ(expected_recorded_send_time, actual_recorded_send_time) + << "Expected time = " << expected_recorded_send_time.ToDebuggingValue() + << ". Actual time = " << actual_recorded_send_time.ToDebuggingValue(); +} + +TEST_P(QuicConnectionTest, FramePacking) { + // Send two stream frames in 1 packet by queueing them. + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + { + QuicConnection::ScopedPacketFlusher flusher(&connection_, + QuicConnection::SEND_ACK); + connection_.SendStreamData3(); + connection_.SendStreamData5(); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + } + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure it's an ack and two stream frames from + // two different streams. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } + + EXPECT_TRUE(writer_->ack_frames().empty()); + + ASSERT_EQ(2u, writer_->stream_frames().size()); + EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); + EXPECT_EQ(GetNthClientInitiatedStreamId(2, connection_.transport_version()), + writer_->stream_frames()[1]->stream_id); +} + +TEST_P(QuicConnectionTest, FramePackingNonCryptoThenCrypto) { + // Send two stream frames (one non-crypto, then one crypto) in 2 packets by + // queueing them. + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + QuicConnection::ScopedPacketFlusher flusher(&connection_, + QuicConnection::SEND_ACK); + connection_.SendStreamData3(); + connection_.SendCryptoStreamData(); + } + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure it's the crypto stream frame. + EXPECT_EQ(2u, writer_->frame_count()); + ASSERT_EQ(1u, writer_->padding_frames().size()); + if (connection_.transport_version() < QUIC_VERSION_47) { + ASSERT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); + } else { + EXPECT_EQ(1u, writer_->crypto_frames().size()); + } +} + +TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) { + // Send two stream frames (one crypto, then one non-crypto) in 2 packets by + // queueing them. + { + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + QuicConnection::ScopedPacketFlusher flusher(&connection_, + QuicConnection::SEND_ACK); + connection_.SendCryptoStreamData(); + connection_.SendStreamData3(); + } + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure it's the stream frame from stream 3. + EXPECT_EQ(1u, writer_->frame_count()); + ASSERT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); +} + +TEST_P(QuicConnectionTest, FramePackingAckResponse) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + // Process a data packet to queue up a pending ack. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(1); + QuicPacketNumber last_packet; + SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); + // Verify ack is bundled with outging packet. + EXPECT_FALSE(writer_->ack_frames().empty()); + + EXPECT_CALL(visitor_, OnCanWrite()) + .WillOnce(DoAll(IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendStreamData3)), + IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendStreamData5)))); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + + // Process a data packet to cause the visitor's OnCanWrite to be invoked. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(2); + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure it's an ack and two stream frames from + // two different streams. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(4u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + ASSERT_EQ(2u, writer_->stream_frames().size()); + EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); + EXPECT_EQ(GetNthClientInitiatedStreamId(2, connection_.transport_version()), + writer_->stream_frames()[1]->stream_id); +} + +TEST_P(QuicConnectionTest, FramePackingSendv) { + // Send data in 1 packet by writing multiple blocks in a single iovector + // using writev. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + + char data[] = "ABCDEF"; + struct iovec iov[2]; + iov[0].iov_base = data; + iov[0].iov_len = 4; + iov[1].iov_base = data + 4; + iov[1].iov_len = 2; + connection_.SaveAndSendStreamData( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), iov, 2, 6, + 0, NO_FIN); + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure multiple iovector blocks have + // been packed into a single stream frame from one stream. + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(1u, writer_->padding_frames().size()); + QuicStreamFrame* frame = writer_->stream_frames()[0].get(); + EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()), + frame->stream_id); + EXPECT_EQ("ABCDEF", QuicStringPiece(frame->data_buffer, frame->data_length)); +} + +TEST_P(QuicConnectionTest, FramePackingSendvQueued) { + // Try to send two stream frames in 1 packet by using writev. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + + BlockOnNextWrite(); + char data[] = "ABCDEF"; + struct iovec iov[2]; + iov[0].iov_base = data; + iov[0].iov_len = 4; + iov[1].iov_base = data + 4; + iov[1].iov_len = 2; + connection_.SaveAndSendStreamData( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), iov, 2, 6, + 0, NO_FIN); + + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + EXPECT_TRUE(connection_.HasQueuedData()); + + // Unblock the writes and actually send. + writer_->SetWritable(); + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + + // Parse the last packet and ensure it's one stream frame from one stream. + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(1u, writer_->padding_frames().size()); + EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); +} + +TEST_P(QuicConnectionTest, SendingZeroBytes) { + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Send a zero byte write with a fin using writev. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.SaveAndSendStreamData( + QuicUtils::GetHeadersStreamId(connection_.transport_version()), nullptr, + 0, 0, 0, FIN); + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure it's one stream frame from one stream. + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); + EXPECT_TRUE(writer_->stream_frames()[0]->fin); +} + +TEST_P(QuicConnectionTest, LargeSendWithPendingAck) { + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Set the ack alarm by processing a ping frame. + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Processs a PING frame. + ProcessFramePacket(QuicFrame(QuicPingFrame())); + // Ensure that this has caused the ACK alarm to be set. + QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); + EXPECT_TRUE(ack_alarm->IsSet()); + + // Send data and ensure the ack is bundled. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(8); + size_t len = 10000; + std::unique_ptr<char[]> data_array(new char[len]); + memset(data_array.get(), '?', len); + struct iovec iov; + iov.iov_base = data_array.get(); + iov.iov_len = len; + QuicConsumedData consumed = connection_.SaveAndSendStreamData( + QuicUtils::GetHeadersStreamId(connection_.transport_version()), &iov, 1, + len, 0, FIN); + EXPECT_EQ(len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure it's one stream frame with a fin. + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); + EXPECT_TRUE(writer_->stream_frames()[0]->fin); + // Ensure the ack alarm was cancelled when the ack was sent. + EXPECT_FALSE(ack_alarm->IsSet()); +} + +TEST_P(QuicConnectionTest, OnCanWrite) { + // Visitor's OnCanWrite will send data, but will have more pending writes. + EXPECT_CALL(visitor_, OnCanWrite()) + .WillOnce(DoAll(IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendStreamData3)), + IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendStreamData5)))); + { + InSequence seq; + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillOnce(Return(true)); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()) + .WillRepeatedly(Return(false)); + } + + EXPECT_CALL(*send_algorithm_, CanSend(_)) + .WillRepeatedly(testing::Return(true)); + + connection_.OnCanWrite(); + + // Parse the last packet and ensure it's the two stream frames from + // two different streams. + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_EQ(2u, writer_->stream_frames().size()); + EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()), + writer_->stream_frames()[0]->stream_id); + EXPECT_EQ(GetNthClientInitiatedStreamId(2, connection_.transport_version()), + writer_->stream_frames()[1]->stream_id); +} + +TEST_P(QuicConnectionTest, RetransmitOnNack) { + QuicPacketNumber last_packet; + QuicByteCount second_packet_size; + SendStreamDataToPeer(3, "foo", 0, NO_FIN, &last_packet); // Packet 1 + second_packet_size = + SendStreamDataToPeer(3, "foos", 3, NO_FIN, &last_packet); // Packet 2 + SendStreamDataToPeer(3, "fooos", 7, NO_FIN, &last_packet); // Packet 3 + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Don't lose a packet on an ack, and nothing is retransmitted. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame ack_one = InitAckFrame(1); + ProcessAckPacket(&ack_one); + + // Lose a packet and ensure it triggers retransmission. + QuicAckFrame nack_two = ConstructAckFrame(3, 2); + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(2), kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + EXPECT_FALSE(QuicPacketCreatorPeer::SendVersionInPacket(creator_)); + ProcessAckPacket(&nack_two); +} + +TEST_P(QuicConnectionTest, DoNotSendQueuedPacketForResetStream) { + // Block the connection to queue the packet. + BlockOnNextWrite(); + + QuicStreamId stream_id = 2; + connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN); + + // Now that there is a queued packet, reset the stream. + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3); + + // Unblock the connection and verify that only the RST_STREAM is sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + writer_->SetWritable(); + connection_.OnCanWrite(); + if (!connection_.session_decides_what_to_write()) { + // OnCanWrite will cause RST_STREAM be sent again. + connection_.SendControlFrame(QuicFrame(new QuicRstStreamFrame( + 1, stream_id, QUIC_ERROR_PROCESSING_STREAM, 14))); + } + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->rst_stream_frames().size()); +} + +TEST_P(QuicConnectionTest, SendQueuedPacketForQuicRstStreamNoError) { + // Block the connection to queue the packet. + BlockOnNextWrite(); + + QuicStreamId stream_id = 2; + connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN); + + // Now that there is a queued packet, reset the stream. + SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 3); + + // Unblock the connection and verify that the RST_STREAM is sent and the data + // packet is sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(2)); + writer_->SetWritable(); + connection_.OnCanWrite(); + if (!connection_.session_decides_what_to_write()) { + // OnCanWrite will cause RST_STREAM be sent again. + connection_.SendControlFrame(QuicFrame( + new QuicRstStreamFrame(1, stream_id, QUIC_STREAM_NO_ERROR, 14))); + } + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->rst_stream_frames().size()); +} + +TEST_P(QuicConnectionTest, DoNotRetransmitForResetStreamOnNack) { + QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "fooos", 7, NO_FIN, &last_packet); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 12); + + // Lose a packet and ensure it does not trigger retransmission. + QuicAckFrame nack_two = ConstructAckFrame(last_packet, last_packet - 1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + ProcessAckPacket(&nack_two); +} + +TEST_P(QuicConnectionTest, RetransmitForQuicRstStreamNoErrorOnNack) { + QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "fooos", 7, NO_FIN, &last_packet); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 12); + + // Lose a packet, ensure it triggers retransmission. + QuicAckFrame nack_two = ConstructAckFrame(last_packet, last_packet - 1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(last_packet - 1, kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(1)); + ProcessAckPacket(&nack_two); +} + +TEST_P(QuicConnectionTest, DoNotRetransmitForResetStreamOnRTO) { + QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3); + + // Fire the RTO and verify that the RST_STREAM is resent, not stream data. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + clock_.AdvanceTime(DefaultRetransmissionTime()); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->rst_stream_frames().size()); + EXPECT_EQ(stream_id, writer_->rst_stream_frames().front().stream_id); +} + +// Ensure that if the only data in flight is non-retransmittable, the +// retransmission alarm is not set. +TEST_P(QuicConnectionTest, CancelRetransmissionAlarmAfterResetStream) { + QuicStreamId stream_id = 2; + QuicPacketNumber last_data_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_data_packet); + + // Cancel the stream. + const QuicPacketNumber rst_packet = last_data_packet + 1; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, rst_packet, _, _)).Times(1); + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3); + + // Ack the RST_STREAM frame (since it's retransmittable), but not the data + // packet, which is no longer retransmittable since the stream was cancelled. + QuicAckFrame nack_stream_data = + ConstructAckFrame(rst_packet, last_data_packet); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + ProcessAckPacket(&nack_stream_data); + + // Ensure that the data is still in flight, but the retransmission alarm is no + // longer set. + EXPECT_GT(QuicSentPacketManagerPeer::GetBytesInFlight(manager_), 0u); + if (GetQuicReloadableFlag(quic_optimize_inflight_check)) { + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + // Firing the alarm should remove all bytes_in_flight. + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetBytesInFlight(manager_)); + } + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, RetransmitForQuicRstStreamNoErrorOnRTO) { + connection_.SetMaxTailLossProbes(0); + + QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 3); + + // Fire the RTO and verify that the RST_STREAM is resent, the stream data + // is sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(2)); + clock_.AdvanceTime(DefaultRetransmissionTime()); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(1u, writer_->frame_count()); + ASSERT_EQ(1u, writer_->rst_stream_frames().size()); + EXPECT_EQ(stream_id, writer_->rst_stream_frames().front().stream_id); +} + +TEST_P(QuicConnectionTest, DoNotSendPendingRetransmissionForResetStream) { + QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet); + BlockOnNextWrite(); + connection_.SendStreamDataWithString(stream_id, "fooos", 7, NO_FIN); + + // Lose a packet which will trigger a pending retransmission. + QuicAckFrame ack = ConstructAckFrame(last_packet, last_packet - 1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + ProcessAckPacket(&ack); + + SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 12); + + // Unblock the connection and verify that the RST_STREAM is sent but not the + // second data packet nor a retransmit. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + writer_->SetWritable(); + connection_.OnCanWrite(); + if (!connection_.session_decides_what_to_write()) { + // OnCanWrite will cause this RST_STREAM_FRAME be sent again. + connection_.SendControlFrame(QuicFrame(new QuicRstStreamFrame( + 1, stream_id, QUIC_ERROR_PROCESSING_STREAM, 14))); + } + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->rst_stream_frames().size()); + EXPECT_EQ(stream_id, writer_->rst_stream_frames().front().stream_id); +} + +TEST_P(QuicConnectionTest, SendPendingRetransmissionForQuicRstStreamNoError) { + QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet); + BlockOnNextWrite(); + connection_.SendStreamDataWithString(stream_id, "fooos", 7, NO_FIN); + + // Lose a packet which will trigger a pending retransmission. + QuicAckFrame ack = ConstructAckFrame(last_packet, last_packet - 1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(last_packet - 1, kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + ProcessAckPacket(&ack); + + SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 12); + + // Unblock the connection and verify that the RST_STREAM is sent and the + // second data packet or a retransmit is sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(2)); + writer_->SetWritable(); + connection_.OnCanWrite(); + // The RST_STREAM_FRAME is sent after queued packets and pending + // retransmission. + connection_.SendControlFrame(QuicFrame( + new QuicRstStreamFrame(1, stream_id, QUIC_STREAM_NO_ERROR, 14))); + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->rst_stream_frames().size()); +} + +TEST_P(QuicConnectionTest, RetransmitAckedPacket) { + QuicPacketNumber last_packet; + SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); // Packet 1 + SendStreamDataToPeer(1, "foos", 3, NO_FIN, &last_packet); // Packet 2 + SendStreamDataToPeer(1, "fooos", 7, NO_FIN, &last_packet); // Packet 3 + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Instigate a loss with an ack. + QuicAckFrame nack_two = ConstructAckFrame(3, 2); + // The first nack should trigger a fast retransmission, but we'll be + // write blocked, so the packet will be queued. + BlockOnNextWrite(); + + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(2), kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&nack_two); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Now, ack the previous transmission. + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(false, _, _, _, _)); + QuicAckFrame ack_all = InitAckFrame(3); + ProcessAckPacket(&ack_all); + + // Unblock the socket and attempt to send the queued packets. We will always + // send the retransmission. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(4), _, _)) + .Times(1); + + writer_->SetWritable(); + connection_.OnCanWrite(); + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + // We do not store retransmittable frames of this retransmission. + EXPECT_FALSE(QuicConnectionPeer::HasRetransmittableFrames(&connection_, 4)); +} + +TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + QuicPacketNumber original, second; + + QuicByteCount packet_size = + SendStreamDataToPeer(3, "foo", 0, NO_FIN, &original); // 1st packet. + SendStreamDataToPeer(3, "bar", 3, NO_FIN, &second); // 2nd packet. + + QuicAckFrame frame = InitAckFrame({{second, second + 1}}); + // The first nack should retransmit the largest observed packet. + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(original, kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + // Packet 1 is short header for IETF QUIC because the encryption level + // switched to ENCRYPTION_FORWARD_SECURE in SendStreamDataToPeer. + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, _, _, + GetParam().version.transport_version > QUIC_VERSION_43 + ? packet_size + : packet_size - kQuicVersionSize, + _)); + ProcessAckPacket(&frame); +} + +TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) { + connection_.SetMaxTailLossProbes(0); + + for (int i = 0; i < 10; ++i) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendStreamDataWithString(3, "foo", i * 3, NO_FIN); + } + + // Block the writer and ensure they're queued. + BlockOnNextWrite(); + clock_.AdvanceTime(DefaultRetransmissionTime()); + // Only one packet should be retransmitted. + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_TRUE(connection_.HasQueuedData()); + + // Unblock the writer. + writer_->SetWritable(); + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds( + 2 * DefaultRetransmissionTime().ToMicroseconds())); + // Retransmit already retransmitted packets event though the packet number + // greater than the largest observed. + if (connection_.session_decides_what_to_write()) { + // 2 RTOs + 1 TLP. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3); + } else { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + } + connection_.GetRetransmissionAlarm()->Fire(); + connection_.OnCanWrite(); +} + +TEST_P(QuicConnectionTest, WriteBlockedBufferedThenSent) { + BlockOnNextWrite(); + writer_->set_is_write_blocked_data_buffered(true); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + writer_->SetWritable(); + connection_.OnCanWrite(); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, WriteBlockedThenSent) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + BlockOnNextWrite(); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // The second packet should also be queued, in order to ensure packets are + // never sent out of order. + writer_->SetWritable(); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + EXPECT_EQ(2u, connection_.NumQueuedPackets()); + + // Now both are sent in order when we unblock. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + connection_.OnCanWrite(); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, RetransmitWriteBlockedAckedOriginalThenSent) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + BlockOnNextWrite(); + writer_->set_is_write_blocked_data_buffered(true); + // Simulate the retransmission alarm firing. + clock_.AdvanceTime(DefaultRetransmissionTime()); + connection_.GetRetransmissionAlarm()->Fire(); + + // Ack the sent packet before the callback returns, which happens in + // rare circumstances with write blocked sockets. + QuicAckFrame ack = InitAckFrame(1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&ack); + + writer_->SetWritable(); + connection_.OnCanWrite(); + // There is now a pending packet, but with no retransmittable frames. + if (GetQuicReloadableFlag(quic_optimize_inflight_check)) { + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + // Firing the alarm should remove all bytes_in_flight. + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetBytesInFlight(manager_)); + } + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(QuicConnectionPeer::HasRetransmittableFrames(&connection_, 2)); +} + +TEST_P(QuicConnectionTest, AlarmsWhenWriteBlocked) { + // Block the connection. + BlockOnNextWrite(); + connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + EXPECT_TRUE(writer_->IsWriteBlocked()); + + // Set the send alarm. Fire the alarm and ensure it doesn't attempt to write. + connection_.GetSendAlarm()->Set(clock_.ApproximateNow()); + connection_.GetSendAlarm()->Fire(); + EXPECT_TRUE(writer_->IsWriteBlocked()); + EXPECT_EQ(1u, writer_->packets_write_attempts()); +} + +TEST_P(QuicConnectionTest, NoSendAlarmAfterProcessPacketWhenWriteBlocked) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Block the connection. + BlockOnNextWrite(); + connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + EXPECT_TRUE(writer_->IsWriteBlocked()); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + // Process packet number 1. Can not call ProcessPacket or ProcessDataPacket + // here, because they will fire the alarm after QuicConnection::ProcessPacket + // is returned. + const uint64_t received_packet_num = 1; + const bool has_stop_waiting = false; + const EncryptionLevel level = ENCRYPTION_NONE; + std::unique_ptr<QuicPacket> packet( + ConstructDataPacket(received_packet_num, has_stop_waiting)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = + peer_framer_.EncryptPayload(level, QuicPacketNumber(received_packet_num), + *packet, buffer, kMaxPacketSize); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); + + EXPECT_TRUE(writer_->IsWriteBlocked()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, AddToWriteBlockedListIfWriterBlockedWhenProcessing) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); + + // Simulate the case where a shared writer gets blocked by another connection. + writer_->SetWriteBlocked(); + + // Process an ACK, make sure the connection calls visitor_->OnWriteBlocked(). + QuicAckFrame ack1 = InitAckFrame(1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1); + ProcessAckPacket(1, &ack1); +} + +TEST_P(QuicConnectionTest, DoNotAddToWriteBlockedListAfterDisconnect) { + writer_->SetBatchMode(true); + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _, + ConnectionCloseSource::FROM_SELF)); + + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0); + + { + QuicConnection::ScopedPacketFlusher flusher(&connection_, + QuicConnection::NO_ACK); + connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason", + ConnectionCloseBehavior::SILENT_CLOSE); + + EXPECT_FALSE(connection_.connected()); + writer_->SetWriteBlocked(); + } +} + +TEST_P(QuicConnectionTest, AddToWriteBlockedListIfBlockedOnFlushPackets) { + writer_->SetBatchMode(true); + writer_->BlockOnNextFlush(); + + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1); + { + QuicConnection::ScopedPacketFlusher flusher(&connection_, + QuicConnection::NO_ACK); + // flusher's destructor will call connection_.FlushPackets, which should add + // the connection to the write blocked list. + } +} + +TEST_P(QuicConnectionTest, NoLimitPacketsPerNack) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + int offset = 0; + // Send packets 1 to 15. + for (int i = 0; i < 15; ++i) { + SendStreamDataToPeer(1, "foo", offset, NO_FIN, nullptr); + offset += 3; + } + + // Ack 15, nack 1-14. + + QuicAckFrame nack = + InitAckFrame({{QuicPacketNumber(15), QuicPacketNumber(16)}}); + + // 14 packets have been NACK'd and lost. + LostPacketVector lost_packets; + for (int i = 1; i < 15; ++i) { + lost_packets.push_back(LostPacket(QuicPacketNumber(i), kMaxPacketSize)); + } + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + if (connection_.session_decides_what_to_write()) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + } else { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(14); + } + ProcessAckPacket(&nack); +} + +// Test sending multiple acks from the connection to the session. +TEST_P(QuicConnectionTest, MultipleAcks) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(1); + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2); + QuicPacketNumber last_packet; + SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); // Packet 1 + EXPECT_EQ(QuicPacketNumber(1u), last_packet); + SendStreamDataToPeer(3, "foo", 0, NO_FIN, &last_packet); // Packet 2 + EXPECT_EQ(QuicPacketNumber(2u), last_packet); + SendAckPacketToPeer(); // Packet 3 + SendStreamDataToPeer(5, "foo", 0, NO_FIN, &last_packet); // Packet 4 + EXPECT_EQ(QuicPacketNumber(4u), last_packet); + SendStreamDataToPeer(1, "foo", 3, NO_FIN, &last_packet); // Packet 5 + EXPECT_EQ(QuicPacketNumber(5u), last_packet); + SendStreamDataToPeer(3, "foo", 3, NO_FIN, &last_packet); // Packet 6 + EXPECT_EQ(QuicPacketNumber(6u), last_packet); + + // Client will ack packets 1, 2, [!3], 4, 5. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame1 = ConstructAckFrame(5, 3); + ProcessAckPacket(&frame1); + + // Now the client implicitly acks 3, and explicitly acks 6. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame2 = InitAckFrame(6); + ProcessAckPacket(&frame2); +} + +TEST_P(QuicConnectionTest, DontLatchUnackedPacket) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(1); + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2); + SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); // Packet 1; + // From now on, we send acks, so the send algorithm won't mark them pending. + SendAckPacketToPeer(); // Packet 2 + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = InitAckFrame(1); + ProcessAckPacket(&frame); + + // Verify that our internal state has least-unacked as 2, because we're still + // waiting for a potential ack for 2. + + EXPECT_EQ(QuicPacketNumber(2u), stop_waiting()->least_unacked); + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + frame = InitAckFrame(2); + ProcessAckPacket(&frame); + EXPECT_EQ(QuicPacketNumber(3u), stop_waiting()->least_unacked); + + // When we send an ack, we make sure our least-unacked makes sense. In this + // case since we're not waiting on an ack for 2 and all packets are acked, we + // set it to 3. + SendAckPacketToPeer(); // Packet 3 + // Least_unacked remains at 3 until another ack is received. + EXPECT_EQ(QuicPacketNumber(3u), stop_waiting()->least_unacked); + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + // Check that the outgoing ack had its packet number as least_unacked. + EXPECT_EQ(QuicPacketNumber(3u), least_unacked()); + } + + // Ack the ack, which updates the rtt and raises the least unacked. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + frame = InitAckFrame(3); + ProcessAckPacket(&frame); + + SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr); // Packet 4 + EXPECT_EQ(QuicPacketNumber(4u), stop_waiting()->least_unacked); + SendAckPacketToPeer(); // Packet 5 + if (GetParam().no_stop_waiting) { + // Expect no stop waiting frame is sent. + EXPECT_FALSE(least_unacked().IsInitialized()); + } else { + EXPECT_EQ(QuicPacketNumber(4u), least_unacked()); + } + + // Send two data packets at the end, and ensure if the last one is acked, + // the least unacked is raised above the ack packets. + SendStreamDataToPeer(1, "bar", 6, NO_FIN, nullptr); // Packet 6 + SendStreamDataToPeer(1, "bar", 9, NO_FIN, nullptr); // Packet 7 + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + frame = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(5)}, + {QuicPacketNumber(7), QuicPacketNumber(8)}}); + ProcessAckPacket(&frame); + + EXPECT_EQ(QuicPacketNumber(6u), stop_waiting()->least_unacked); +} + +TEST_P(QuicConnectionTest, TLP) { + connection_.SetMaxTailLossProbes(1); + + SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr); + EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked); + QuicTime retransmission_time = + connection_.GetRetransmissionAlarm()->deadline(); + EXPECT_NE(QuicTime::Zero(), retransmission_time); + + EXPECT_EQ(QuicPacketNumber(1u), writer_->header().packet_number); + // Simulate the retransmission alarm firing and sending a tlp, + // so send algorithm's OnRetransmissionTimeout is not called. + clock_.AdvanceTime(retransmission_time - clock_.Now()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(QuicPacketNumber(2u), writer_->header().packet_number); + // We do not raise the high water mark yet. + EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked); +} + +TEST_P(QuicConnectionTest, RTO) { + connection_.SetMaxTailLossProbes(0); + + QuicTime default_retransmission_time = + clock_.ApproximateNow() + DefaultRetransmissionTime(); + SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr); + EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked); + + EXPECT_EQ(QuicPacketNumber(1u), writer_->header().packet_number); + EXPECT_EQ(default_retransmission_time, + connection_.GetRetransmissionAlarm()->deadline()); + // Simulate the retransmission alarm firing. + clock_.AdvanceTime(DefaultRetransmissionTime()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(QuicPacketNumber(2u), writer_->header().packet_number); + // We do not raise the high water mark yet. + EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked); +} + +TEST_P(QuicConnectionTest, RetransmitWithSameEncryptionLevel) { + use_tagging_decrypter(); + + // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at + // the end of the packet. We can test this to check which encrypter was used. + connection_.SetEncrypter(ENCRYPTION_NONE, + QuicMakeUnique<TaggingEncrypter>(0x01)); + SendStreamDataToPeer( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0, + NO_FIN, nullptr); + EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet()); + + connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr); + EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); + + { + InSequence s; + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, QuicPacketNumber(3), _, _)); + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, QuicPacketNumber(4), _, _)); + } + + // Manually mark both packets for retransmission. + connection_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); + + // Packet should have been sent with ENCRYPTION_NONE. + EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet()); + + // Packet should have been sent with ENCRYPTION_ZERO_RTT. + EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); +} + +TEST_P(QuicConnectionTest, SendHandshakeMessages) { + use_tagging_decrypter(); + // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at + // the end of the packet. We can test this to check which encrypter was used. + connection_.SetEncrypter(ENCRYPTION_NONE, + QuicMakeUnique<TaggingEncrypter>(0x01)); + + // Attempt to send a handshake message and have the socket block. + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + BlockOnNextWrite(); + connection_.SendStreamDataWithString( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0, + NO_FIN); + // The packet should be serialized, but not queued. + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Switch to the new encrypter. + connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + + // Now become writeable and flush the packets. + writer_->SetWritable(); + EXPECT_CALL(visitor_, OnCanWrite()); + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + + // Verify that the handshake packet went out at the null encryption. + EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet()); +} + +TEST_P(QuicConnectionTest, + DropRetransmitsForNullEncryptedPacketAfterForwardSecure) { + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_NONE, + QuicMakeUnique<TaggingEncrypter>(0x01)); + QuicPacketNumber packet_number; + connection_.SendCryptoStreamData(); + + // Simulate the retransmission alarm firing and the socket blocking. + BlockOnNextWrite(); + clock_.AdvanceTime(DefaultRetransmissionTime()); + connection_.GetRetransmissionAlarm()->Fire(); + + // Go forward secure. + connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + QuicMakeUnique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + notifier_.NeuterUnencryptedData(); + connection_.NeuterUnencryptedPackets(); + + EXPECT_EQ(QuicTime::Zero(), connection_.GetRetransmissionAlarm()->deadline()); + // Unblock the socket and ensure that no packets are sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + writer_->SetWritable(); + connection_.OnCanWrite(); +} + +TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_NONE, + QuicMakeUnique<TaggingEncrypter>(0x01)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE); + + SendStreamDataToPeer( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0, + NO_FIN, nullptr); + + connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + + SendStreamDataToPeer(2, "bar", 0, NO_FIN, nullptr); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + + connection_.RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION); +} + +TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) { + // SetFromConfig is always called after construction from InitializeSession. + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + use_tagging_decrypter(); + + const uint8_t tag = 0x07; + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + + // Process an encrypted packet which can not yet be decrypted which should + // result in the packet being buffered. + ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Transition to the new encryption state and process another encrypted packet + // which should result in the original packet being processed. + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(2); + ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Finally, process a third packet and note that we do not reprocess the + // buffered packet. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); +} + +TEST_P(QuicConnectionTest, TestRetransmitOrder) { + connection_.SetMaxTailLossProbes(0); + + QuicByteCount first_packet_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<3>(&first_packet_size)); + + connection_.SendStreamDataWithString(3, "first_packet", 0, NO_FIN); + QuicByteCount second_packet_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<3>(&second_packet_size)); + connection_.SendStreamDataWithString(3, "second_packet", 12, NO_FIN); + EXPECT_NE(first_packet_size, second_packet_size); + // Advance the clock by huge time to make sure packets will be retransmitted. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); + { + InSequence s; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, first_packet_size, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, second_packet_size, _)); + } + connection_.GetRetransmissionAlarm()->Fire(); + + // Advance again and expect the packets to be sent again in the same order. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(20)); + { + InSequence s; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, first_packet_size, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, second_packet_size, _)); + } + connection_.GetRetransmissionAlarm()->Fire(); +} + +TEST_P(QuicConnectionTest, Buffer100NonDecryptablePacketsThenKeyChange) { + // SetFromConfig is always called after construction from InitializeSession. + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + config.set_max_undecryptable_packets(100); + connection_.SetFromConfig(config); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + use_tagging_decrypter(); + + const uint8_t tag = 0x07; + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + + // Process an encrypted packet which can not yet be decrypted which should + // result in the packet being buffered. + for (uint64_t i = 1; i <= 100; ++i) { + ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + + // Transition to the new encryption state and process another encrypted packet + // which should result in the original packets being processed. + EXPECT_FALSE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet()); + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet()); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(100); + connection_.GetProcessUndecryptablePacketsAlarm()->Fire(); + + // Finally, process a third packet and note that we do not reprocess the + // buffered packet. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(102, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); +} + +TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) { + BlockOnNextWrite(); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + // Make sure that RTO is not started when the packet is queued. + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Test that RTO is started once we write to the socket. + writer_->SetWritable(); + connection_.OnCanWrite(); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) { + connection_.SetMaxTailLossProbes(0); + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + connection_.SendStreamDataWithString(2, "foo", 0, NO_FIN); + connection_.SendStreamDataWithString(3, "bar", 0, NO_FIN); + QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm(); + EXPECT_TRUE(retransmission_alarm->IsSet()); + EXPECT_EQ(clock_.Now() + DefaultRetransmissionTime(), + retransmission_alarm->deadline()); + + // Advance the time right before the RTO, then receive an ack for the first + // packet to delay the RTO. + clock_.AdvanceTime(DefaultRetransmissionTime()); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame ack = InitAckFrame(1); + ProcessAckPacket(&ack); + // Now we have an RTT sample of DefaultRetransmissionTime(500ms), + // so the RTO has increased to 2 * SRTT. + EXPECT_TRUE(retransmission_alarm->IsSet()); + EXPECT_EQ(retransmission_alarm->deadline(), + clock_.Now() + 2 * DefaultRetransmissionTime()); + + // Move forward past the original RTO and ensure the RTO is still pending. + clock_.AdvanceTime(2 * DefaultRetransmissionTime()); + + // Ensure the second packet gets retransmitted when it finally fires. + EXPECT_TRUE(retransmission_alarm->IsSet()); + EXPECT_EQ(retransmission_alarm->deadline(), clock_.ApproximateNow()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + // Manually cancel the alarm to simulate a real test. + connection_.GetRetransmissionAlarm()->Fire(); + + // The new retransmitted packet number should set the RTO to a larger value + // than previously. + EXPECT_TRUE(retransmission_alarm->IsSet()); + QuicTime next_rto_time = retransmission_alarm->deadline(); + QuicTime expected_rto_time = + connection_.sent_packet_manager().GetRetransmissionTime(); + EXPECT_EQ(next_rto_time, expected_rto_time); +} + +TEST_P(QuicConnectionTest, TestQueued) { + connection_.SetMaxTailLossProbes(0); + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + BlockOnNextWrite(); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Unblock the writes and actually send. + writer_->SetWritable(); + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_P(QuicConnectionTest, InitialTimeout) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber()); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + + // SetFromConfig sets the initial timeouts before negotiation. + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + // Subtract a second from the idle timeout on the client side. + QuicTime default_timeout = + clock_.ApproximateNow() + + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + // Simulate the timeout alarm firing. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1)); + connection_.GetTimeoutAlarm()->Fire(); + + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); + + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, IdleTimeoutAfterFirstSentPacket) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber()); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + QuicTime initial_ddl = + clock_.ApproximateNow() + + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + EXPECT_EQ(initial_ddl, connection_.GetTimeoutAlarm()->deadline()); + EXPECT_TRUE(connection_.connected()); + + // Advance the time and send the first packet to the peer. + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20)); + QuicPacketNumber last_packet; + SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); + EXPECT_EQ(QuicPacketNumber(1u), last_packet); + // This will be the updated deadline for the connection to idle time out. + QuicTime new_ddl = clock_.ApproximateNow() + + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + + // Simulate the timeout alarm firing, the connection should not be closed as + // a new packet has been sent. + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0); + QuicTime::Delta delay = initial_ddl - clock_.ApproximateNow(); + clock_.AdvanceTime(delay); + connection_.GetTimeoutAlarm()->Fire(); + // Verify the timeout alarm deadline is updated. + EXPECT_TRUE(connection_.connected()); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_EQ(new_ddl, connection_.GetTimeoutAlarm()->deadline()); + + // Simulate the timeout alarm firing again, the connection now should be + // closed. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + clock_.AdvanceTime(new_ddl - clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); + + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, IdleTimeoutAfterSendTwoPackets) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber()); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + QuicTime initial_ddl = + clock_.ApproximateNow() + + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + EXPECT_EQ(initial_ddl, connection_.GetTimeoutAlarm()->deadline()); + EXPECT_TRUE(connection_.connected()); + + // Immediately send the first packet, this is a rare case but test code will + // hit this issue often as MockClock used for tests doesn't move with code + // execution until manually adjusted. + QuicPacketNumber last_packet; + SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); + EXPECT_EQ(QuicPacketNumber(1u), last_packet); + + // Advance the time and send the second packet to the peer. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); + SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); + EXPECT_EQ(QuicPacketNumber(2u), last_packet); + + if (GetQuicReloadableFlag( + quic_fix_time_of_first_packet_sent_after_receiving)) { + // Simulate the timeout alarm firing, the connection will be closed. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + clock_.AdvanceTime(initial_ddl - clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + } else { + // Simulate the timeout alarm firing, the connection will not be closed. + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0); + clock_.AdvanceTime(initial_ddl - clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + + // Advance another 20ms, and fire the alarm again. The connection will be + // closed. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); + connection_.GetTimeoutAlarm()->Fire(); + } + + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); + + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, HandshakeTimeout) { + // Use a shorter handshake timeout than idle timeout for this test. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5); + connection_.SetNetworkTimeouts(timeout, timeout); + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber()); + + QuicTime handshake_timeout = + clock_.ApproximateNow() + timeout - QuicTime::Delta::FromSeconds(1); + EXPECT_EQ(handshake_timeout, connection_.GetTimeoutAlarm()->deadline()); + EXPECT_TRUE(connection_.connected()); + + // Send and ack new data 3 seconds later to lengthen the idle timeout. + SendStreamDataToPeer( + QuicUtils::GetHeadersStreamId(connection_.transport_version()), "GET /", + 0, FIN, nullptr); + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(3)); + QuicAckFrame frame = InitAckFrame(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&frame); + + // Fire early to verify it wouldn't timeout yet. + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + + clock_.AdvanceTime(timeout - QuicTime::Delta::FromSeconds(2)); + + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_HANDSHAKE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + // Simulate the timeout alarm firing. + connection_.GetTimeoutAlarm()->Fire(); + + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); + + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, PingAfterSend) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + + // Advance to 5ms, and send a packet to the peer, which will set + // the ping alarm. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + SendStreamDataToPeer( + QuicUtils::GetHeadersStreamId(connection_.transport_version()), "GET /", + 0, FIN, nullptr); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(15), + connection_.GetPingAlarm()->deadline()); + + // Now recevie an ACK of the previous packet, which will move the + // ping alarm forward. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + QuicAckFrame frame = InitAckFrame(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + // The ping timer is set slightly less than 15 seconds in the future, because + // of the 1s ping timer alarm granularity. + EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(15) - + QuicTime::Delta::FromMilliseconds(5), + connection_.GetPingAlarm()->deadline()); + + writer_->Reset(); + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15)); + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { + connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); + })); + connection_.GetPingAlarm()->Fire(); + EXPECT_EQ(1u, writer_->frame_count()); + ASSERT_EQ(1u, writer_->ping_frames().size()); + writer_->Reset(); + + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(false)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + SendAckPacketToPeer(); + + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, ReducedPingTimeout) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + + // Use a reduced ping timeout for this connection. + connection_.set_ping_timeout(QuicTime::Delta::FromSeconds(10)); + + // Advance to 5ms, and send a packet to the peer, which will set + // the ping alarm. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + SendStreamDataToPeer( + QuicUtils::GetHeadersStreamId(connection_.transport_version()), "GET /", + 0, FIN, nullptr); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(10), + connection_.GetPingAlarm()->deadline()); + + // Now recevie an ACK of the previous packet, which will move the + // ping alarm forward. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + QuicAckFrame frame = InitAckFrame(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + // The ping timer is set slightly less than 10 seconds in the future, because + // of the 1s ping timer alarm granularity. + EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(10) - + QuicTime::Delta::FromMilliseconds(5), + connection_.GetPingAlarm()->deadline()); + + writer_->Reset(); + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { + connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); + })); + connection_.GetPingAlarm()->Fire(); + EXPECT_EQ(1u, writer_->frame_count()); + ASSERT_EQ(1u, writer_->ping_frames().size()); + writer_->Reset(); + + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(false)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + SendAckPacketToPeer(); + + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); +} + +// Tests whether sending an MTU discovery packet to peer successfully causes the +// maximum packet size to increase. +TEST_P(QuicConnectionTest, SendMtuDiscoveryPacket) { + EXPECT_TRUE(connection_.connected()); + + // Send an MTU probe. + const size_t new_mtu = kDefaultMaxPacketSize + 100; + QuicByteCount mtu_probe_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<3>(&mtu_probe_size)); + connection_.SendMtuDiscoveryPacket(new_mtu); + EXPECT_EQ(new_mtu, mtu_probe_size); + EXPECT_EQ(QuicPacketNumber(1u), creator_->packet_number()); + + // Send more than MTU worth of data. No acknowledgement was received so far, + // so the MTU should be at its old value. + const QuicString data(kDefaultMaxPacketSize + 1, '.'); + QuicByteCount size_before_mtu_change; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .Times(2) + .WillOnce(SaveArg<3>(&size_before_mtu_change)) + .WillOnce(Return()); + connection_.SendStreamDataWithString(3, data, 0, FIN); + EXPECT_EQ(QuicPacketNumber(3u), creator_->packet_number()); + EXPECT_EQ(kDefaultMaxPacketSize, size_before_mtu_change); + + // Acknowledge all packets so far. + QuicAckFrame probe_ack = InitAckFrame(3); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&probe_ack); + EXPECT_EQ(new_mtu, connection_.max_packet_length()); + + // Send the same data again. Check that it fits into a single packet now. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendStreamDataWithString(3, data, 0, FIN); + EXPECT_EQ(QuicPacketNumber(4u), creator_->packet_number()); +} + +// Tests whether MTU discovery does not happen when it is not explicitly enabled +// by the connection options. +TEST_P(QuicConnectionTest, MtuDiscoveryDisabled) { + EXPECT_TRUE(connection_.connected()); + + const QuicPacketCount packets_between_probes_base = 10; + set_packets_between_probes_base(packets_between_probes_base); + + const QuicPacketCount number_of_packets = packets_between_probes_base * 2; + for (QuicPacketCount i = 0; i < number_of_packets; i++) { + SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + EXPECT_EQ(0u, connection_.mtu_probe_count()); + } +} + +// Tests whether MTU discovery works when the probe gets acknowledged on the +// first try. +TEST_P(QuicConnectionTest, MtuDiscoveryEnabled) { + EXPECT_TRUE(connection_.connected()); + + connection_.EnablePathMtuDiscovery(send_algorithm_); + + const QuicPacketCount packets_between_probes_base = 5; + set_packets_between_probes_base(packets_between_probes_base); + + // Send enough packets so that the next one triggers path MTU discovery. + for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) { + SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + // Trigger the probe. + SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN, + nullptr); + ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + QuicByteCount probe_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<3>(&probe_size)); + connection_.GetMtuDiscoveryAlarm()->Fire(); + EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, probe_size); + + const QuicPacketNumber probe_packet_number = + FirstSendingPacketNumber() + packets_between_probes_base; + ASSERT_EQ(probe_packet_number, creator_->packet_number()); + + // Acknowledge all packets sent so far. + QuicAckFrame probe_ack = InitAckFrame(probe_packet_number); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&probe_ack); + EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, connection_.max_packet_length()); + EXPECT_EQ(0u, connection_.GetBytesInFlight()); + + // Send more packets, and ensure that none of them sets the alarm. + for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) { + SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN, + nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + EXPECT_EQ(1u, connection_.mtu_probe_count()); +} + +// Tests whether MTU discovery works correctly when the probes never get +// acknowledged. +TEST_P(QuicConnectionTest, MtuDiscoveryFailed) { + EXPECT_TRUE(connection_.connected()); + + connection_.EnablePathMtuDiscovery(send_algorithm_); + + const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100); + + EXPECT_EQ(kPacketsBetweenMtuProbesBase, + QuicConnectionPeer::GetPacketsBetweenMtuProbes(&connection_)); + // Lower the number of probes between packets in order to make the test go + // much faster. + const QuicPacketCount packets_between_probes_base = 5; + set_packets_between_probes_base(packets_between_probes_base); + + // This tests sends more packets than strictly necessary to make sure that if + // the connection was to send more discovery packets than needed, those would + // get caught as well. + const QuicPacketCount number_of_packets = + packets_between_probes_base * (1 << (kMtuDiscoveryAttempts + 1)); + std::vector<QuicPacketNumber> mtu_discovery_packets; + // Called by the first ack. + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + // Called on many acks. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)) + .Times(AnyNumber()); + for (QuicPacketCount i = 0; i < number_of_packets; i++) { + SendStreamDataToPeer(3, "!", i, NO_FIN, nullptr); + clock_.AdvanceTime(rtt); + + // Receive an ACK, which marks all data packets as received, and all MTU + // discovery packets as missing. + + QuicAckFrame ack; + + if (!mtu_discovery_packets.empty()) { + QuicPacketNumber min_packet = *min_element(mtu_discovery_packets.begin(), + mtu_discovery_packets.end()); + QuicPacketNumber max_packet = *max_element(mtu_discovery_packets.begin(), + mtu_discovery_packets.end()); + ack.packets.AddRange(QuicPacketNumber(1), min_packet); + ack.packets.AddRange(QuicPacketNumber(max_packet + 1), + creator_->packet_number() + 1); + ack.largest_acked = creator_->packet_number(); + + } else { + ack.packets.AddRange(QuicPacketNumber(1), creator_->packet_number() + 1); + ack.largest_acked = creator_->packet_number(); + } + + ProcessAckPacket(&ack); + + // Trigger MTU probe if it would be scheduled now. + if (!connection_.GetMtuDiscoveryAlarm()->IsSet()) { + continue; + } + + // Fire the alarm. The alarm should cause a packet to be sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.GetMtuDiscoveryAlarm()->Fire(); + // Record the packet number of the MTU discovery packet in order to + // mark it as NACK'd. + mtu_discovery_packets.push_back(creator_->packet_number()); + } + + // Ensure the number of packets between probes grows exponentially by checking + // it against the closed-form expression for the packet number. + ASSERT_EQ(kMtuDiscoveryAttempts, mtu_discovery_packets.size()); + for (uint64_t i = 0; i < kMtuDiscoveryAttempts; i++) { + // 2^0 + 2^1 + 2^2 + ... + 2^n = 2^(n + 1) - 1 + const QuicPacketCount packets_between_probes = + packets_between_probes_base * ((1 << (i + 1)) - 1); + EXPECT_EQ(QuicPacketNumber(packets_between_probes + (i + 1)), + mtu_discovery_packets[i]); + } + + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + EXPECT_EQ(kDefaultMaxPacketSize, connection_.max_packet_length()); + EXPECT_EQ(kMtuDiscoveryAttempts, connection_.mtu_probe_count()); +} + +// Tests whether MTU discovery works when the writer has a limit on how large a +// packet can be. +TEST_P(QuicConnectionTest, MtuDiscoveryWriterLimited) { + EXPECT_TRUE(connection_.connected()); + + const QuicByteCount mtu_limit = kMtuDiscoveryTargetPacketSizeHigh - 1; + writer_->set_max_packet_size(mtu_limit); + connection_.EnablePathMtuDiscovery(send_algorithm_); + + const QuicPacketCount packets_between_probes_base = 5; + set_packets_between_probes_base(packets_between_probes_base); + + // Send enough packets so that the next one triggers path MTU discovery. + for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) { + SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + // Trigger the probe. + SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN, + nullptr); + ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + QuicByteCount probe_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(SaveArg<3>(&probe_size)); + connection_.GetMtuDiscoveryAlarm()->Fire(); + EXPECT_EQ(mtu_limit, probe_size); + + const QuicPacketNumber probe_sequence_number = + FirstSendingPacketNumber() + packets_between_probes_base; + ASSERT_EQ(probe_sequence_number, creator_->packet_number()); + + // Acknowledge all packets sent so far. + QuicAckFrame probe_ack = InitAckFrame(probe_sequence_number); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&probe_ack); + EXPECT_EQ(mtu_limit, connection_.max_packet_length()); + EXPECT_EQ(0u, connection_.GetBytesInFlight()); + + // Send more packets, and ensure that none of them sets the alarm. + for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) { + SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN, + nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + EXPECT_EQ(1u, connection_.mtu_probe_count()); +} + +// Tests whether MTU discovery works when the writer returns an error despite +// advertising higher packet length. +TEST_P(QuicConnectionTest, MtuDiscoveryWriterFailed) { + EXPECT_TRUE(connection_.connected()); + + const QuicByteCount mtu_limit = kMtuDiscoveryTargetPacketSizeHigh - 1; + const QuicByteCount initial_mtu = connection_.max_packet_length(); + EXPECT_LT(initial_mtu, mtu_limit); + writer_->set_max_packet_size(mtu_limit); + connection_.EnablePathMtuDiscovery(send_algorithm_); + + const QuicPacketCount packets_between_probes_base = 5; + set_packets_between_probes_base(packets_between_probes_base); + + // Send enough packets so that the next one triggers path MTU discovery. + for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) { + SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + // Trigger the probe. + SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN, + nullptr); + ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + writer_->SimulateNextPacketTooLarge(); + connection_.GetMtuDiscoveryAlarm()->Fire(); + ASSERT_TRUE(connection_.connected()); + + // Send more data. + QuicPacketNumber probe_number = creator_->packet_number(); + QuicPacketCount extra_packets = packets_between_probes_base * 3; + for (QuicPacketCount i = 0; i < extra_packets; i++) { + connection_.EnsureWritableAndSendStreamData5(); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + // Acknowledge all packets sent so far, except for the lost probe. + QuicAckFrame probe_ack = + ConstructAckFrame(creator_->packet_number(), probe_number); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&probe_ack); + EXPECT_EQ(initial_mtu, connection_.max_packet_length()); + + // Send more packets, and ensure that none of them sets the alarm. + for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) { + connection_.EnsureWritableAndSendStreamData5(); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + EXPECT_EQ(initial_mtu, connection_.max_packet_length()); + EXPECT_EQ(1u, connection_.mtu_probe_count()); +} + +TEST_P(QuicConnectionTest, NoMtuDiscoveryAfterConnectionClosed) { + EXPECT_TRUE(connection_.connected()); + + connection_.EnablePathMtuDiscovery(send_algorithm_); + + const QuicPacketCount packets_between_probes_base = 10; + set_packets_between_probes_base(packets_between_probes_base); + + // Send enough packets so that the next one triggers path MTU discovery. + for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) { + SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN, + nullptr); + EXPECT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)); + connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason", + ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, TimeoutAfterSend) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_)); + + const QuicTime::Delta initial_idle_timeout = + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5); + QuicTime default_timeout = clock_.ApproximateNow() + initial_idle_timeout; + + // When we send a packet, the timeout will change to 5ms + + // kInitialIdleTimeoutSecs. + clock_.AdvanceTime(five_ms); + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // Now send more data. This will not move the timeout because + // no data has been received since the previous write. + clock_.AdvanceTime(five_ms); + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 3, FIN, nullptr); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // The original alarm will fire. We should not time out because we had a + // network event at t=5ms. The alarm will reregister. + clock_.AdvanceTime(initial_idle_timeout - five_ms - five_ms); + EXPECT_EQ(default_timeout, clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + EXPECT_EQ(default_timeout + five_ms, + connection_.GetTimeoutAlarm()->deadline()); + + // This time, we should time out. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + clock_.AdvanceTime(five_ms); + EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, TimeoutAfterRetransmission) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_)); + + const QuicTime start_time = clock_.Now(); + const QuicTime::Delta initial_idle_timeout = + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + QuicTime default_timeout = clock_.Now() + initial_idle_timeout; + + connection_.SetMaxTailLossProbes(0); + const QuicTime default_retransmission_time = + start_time + DefaultRetransmissionTime(); + + ASSERT_LT(default_retransmission_time, default_timeout); + + // When we send a packet, the timeout will change to 5 ms + + // kInitialIdleTimeoutSecs (but it will not reschedule the alarm). + const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5); + const QuicTime send_time = start_time + five_ms; + clock_.AdvanceTime(five_ms); + ASSERT_EQ(send_time, clock_.Now()); + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // Move forward 5 ms and receive a packet, which will move the timeout + // forward 5 ms more (but will not reschedule the alarm). + const QuicTime receive_time = send_time + five_ms; + clock_.AdvanceTime(receive_time - clock_.Now()); + ASSERT_EQ(receive_time, clock_.Now()); + ProcessPacket(1); + + // Now move forward to the retransmission time and retransmit the + // packet, which should move the timeout forward again (but will not + // reschedule the alarm). + EXPECT_EQ(default_retransmission_time + five_ms, + connection_.GetRetransmissionAlarm()->deadline()); + // Simulate the retransmission alarm firing. + const QuicTime rto_time = send_time + DefaultRetransmissionTime(); + const QuicTime final_timeout = rto_time + initial_idle_timeout; + clock_.AdvanceTime(rto_time - clock_.Now()); + ASSERT_EQ(rto_time, clock_.Now()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + + // Advance to the original timeout and fire the alarm. The connection should + // timeout, and the alarm should be registered based on the time of the + // retransmission. + clock_.AdvanceTime(default_timeout - clock_.Now()); + ASSERT_EQ(default_timeout.ToDebuggingValue(), + clock_.Now().ToDebuggingValue()); + EXPECT_EQ(default_timeout, clock_.Now()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + ASSERT_EQ(final_timeout.ToDebuggingValue(), + connection_.GetTimeoutAlarm()->deadline().ToDebuggingValue()); + + // This time, we should time out. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + clock_.AdvanceTime(final_timeout - clock_.Now()); + EXPECT_EQ(connection_.GetTimeoutAlarm()->deadline(), clock_.Now()); + EXPECT_EQ(final_timeout, clock_.Now()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, NewTimeoutAfterSendSilentClose) { + // Same test as above, but complete a handshake which enables silent close, + // causing no connection close packet to be sent. + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + + // Create a handshake message that also enables silent close. + CryptoHandshakeMessage msg; + QuicString error_details; + QuicConfig client_config; + client_config.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + client_config.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + client_config.SetIdleNetworkTimeout( + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs), + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs)); + client_config.ToHandshakeMessage(&msg); + const QuicErrorCode error = + config.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + + connection_.SetFromConfig(config); + EXPECT_TRUE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_)); + + const QuicTime::Delta default_idle_timeout = + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs - 1); + const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5); + QuicTime default_timeout = clock_.ApproximateNow() + default_idle_timeout; + + // When we send a packet, the timeout will change to 5ms + + // kInitialIdleTimeoutSecs. + clock_.AdvanceTime(five_ms); + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // Now send more data. This will not move the timeout because + // no data has been received since the previous write. + clock_.AdvanceTime(five_ms); + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 3, FIN, nullptr); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // The original alarm will fire. We should not time out because we had a + // network event at t=5ms. The alarm will reregister. + clock_.AdvanceTime(default_idle_timeout - five_ms - five_ms); + EXPECT_EQ(default_timeout, clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + EXPECT_EQ(default_timeout + five_ms, + connection_.GetTimeoutAlarm()->deadline()); + + // This time, we should time out. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + clock_.AdvanceTime(five_ms); + EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseAndTLP) { + // Same test as above, but complete a handshake which enables silent close, + // but sending TLPs causes the connection close to be sent. + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + + // Create a handshake message that also enables silent close. + CryptoHandshakeMessage msg; + QuicString error_details; + QuicConfig client_config; + client_config.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + client_config.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + client_config.SetIdleNetworkTimeout( + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs), + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs)); + client_config.ToHandshakeMessage(&msg); + const QuicErrorCode error = + config.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + + connection_.SetFromConfig(config); + EXPECT_TRUE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_)); + + const QuicTime::Delta default_idle_timeout = + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs - 1); + const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5); + QuicTime default_timeout = clock_.ApproximateNow() + default_idle_timeout; + + // When we send a packet, the timeout will change to 5ms + + // kInitialIdleTimeoutSecs. + clock_.AdvanceTime(five_ms); + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // Retransmit the packet via tail loss probe. + clock_.AdvanceTime(connection_.GetRetransmissionAlarm()->deadline() - + clock_.Now()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + + // This time, we should time out and send a connection close due to the TLP. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + clock_.AdvanceTime(connection_.GetTimeoutAlarm()->deadline() - + clock_.ApproximateNow() + five_ms); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseWithOpenStreams) { + // Same test as above, but complete a handshake which enables silent close, + // but having open streams causes the connection close to be sent. + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + + // Create a handshake message that also enables silent close. + CryptoHandshakeMessage msg; + QuicString error_details; + QuicConfig client_config; + client_config.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + client_config.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + client_config.SetIdleNetworkTimeout( + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs), + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs)); + client_config.ToHandshakeMessage(&msg); + const QuicErrorCode error = + config.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + + connection_.SetFromConfig(config); + EXPECT_TRUE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_)); + + const QuicTime::Delta default_idle_timeout = + QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs - 1); + const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5); + QuicTime default_timeout = clock_.ApproximateNow() + default_idle_timeout; + + // When we send a packet, the timeout will change to 5ms + + // kInitialIdleTimeoutSecs. + clock_.AdvanceTime(five_ms); + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // Indicate streams are still open. + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + + // This time, we should time out and send a connection close due to the TLP. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + clock_.AdvanceTime(connection_.GetTimeoutAlarm()->deadline() - + clock_.ApproximateNow() + five_ms); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, TimeoutAfterReceive) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_)); + + const QuicTime::Delta initial_idle_timeout = + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5); + QuicTime default_timeout = clock_.ApproximateNow() + initial_idle_timeout; + + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, NO_FIN); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 3, NO_FIN); + + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + clock_.AdvanceTime(five_ms); + + // When we receive a packet, the timeout will change to 5ms + + // kInitialIdleTimeoutSecs. + QuicAckFrame ack = InitAckFrame(2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&ack); + + // The original alarm will fire. We should not time out because we had a + // network event at t=5ms. The alarm will reregister. + clock_.AdvanceTime(initial_idle_timeout - five_ms); + EXPECT_EQ(default_timeout, clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_TRUE(connection_.connected()); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_EQ(default_timeout + five_ms, + connection_.GetTimeoutAlarm()->deadline()); + + // This time, we should time out. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + clock_.AdvanceTime(five_ms); + EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, TimeoutAfterReceiveNotSendWhenUnacked) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_)); + + const QuicTime::Delta initial_idle_timeout = + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1); + connection_.SetNetworkTimeouts( + QuicTime::Delta::Infinite(), + initial_idle_timeout + QuicTime::Delta::FromSeconds(1)); + const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5); + QuicTime default_timeout = clock_.ApproximateNow() + initial_idle_timeout; + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, NO_FIN); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 3, NO_FIN); + + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + clock_.AdvanceTime(five_ms); + + // When we receive a packet, the timeout will change to 5ms + + // kInitialIdleTimeoutSecs. + QuicAckFrame ack = InitAckFrame(2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&ack); + + // The original alarm will fire. We should not time out because we had a + // network event at t=5ms. The alarm will reregister. + clock_.AdvanceTime(initial_idle_timeout - five_ms); + EXPECT_EQ(default_timeout, clock_.ApproximateNow()); + connection_.GetTimeoutAlarm()->Fire(); + EXPECT_TRUE(connection_.connected()); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_EQ(default_timeout + five_ms, + connection_.GetTimeoutAlarm()->deadline()); + + // Now, send packets while advancing the time and verify that the connection + // eventually times out. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber()); + for (int i = 0; i < 100 && connection_.connected(); ++i) { + QUIC_LOG(INFO) << "sending data packet"; + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), + "foo", 0, NO_FIN); + connection_.GetTimeoutAlarm()->Fire(); + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } + EXPECT_FALSE(connection_.connected()); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, TimeoutAfter5ClientRTOs) { + connection_.SetMaxTailLossProbes(2); + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k5RTO); + config.SetConnectionOptionsToSend(connection_options); + connection_.SetFromConfig(config); + + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + + // Fire the retransmission alarm 6 times, twice for TLP and 4 times for RTO. + for (int i = 0; i < 6; ++i) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_TRUE(connection_.connected()); + } + + EXPECT_EQ(2u, connection_.sent_packet_manager().GetConsecutiveTlpCount()); + EXPECT_EQ(4u, connection_.sent_packet_manager().GetConsecutiveRtoCount()); + // This time, we should time out. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_TOO_MANY_RTOS, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, SendScheduler) { + // Test that if we send a packet without delay, it is not queued. + QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting); + QuicPacketCreatorPeer::SetPacketNumber(creator_, 1); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet), + HAS_RETRANSMITTABLE_DATA, false, false); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_P(QuicConnectionTest, FailToSendFirstPacket) { + // Test that the connection does not crash when it fails to send the first + // packet at which point self_address_ might be uninitialized. + QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT); + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1); + std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting); + QuicPacketCreatorPeer::SetPacketNumber(creator_, 1); + writer_->SetShouldWriteFail(); + connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet), + HAS_RETRANSMITTABLE_DATA, false, false); +} + +TEST_P(QuicConnectionTest, SendSchedulerEAGAIN) { + QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting); + QuicPacketCreatorPeer::SetPacketNumber(creator_, 1); + BlockOnNextWrite(); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _)) + .Times(0); + connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet), + HAS_RETRANSMITTABLE_DATA, false, false); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { + // All packets carry version info till version is negotiated. + size_t payload_length; + size_t length = GetPacketLengthForOneStream( + connection_.version().transport_version, kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), + QuicPacketCreatorPeer::GetLengthLength(creator_), &payload_length); + connection_.SetMaxPacketLength(length); + + // Queue the first packet. + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(testing::Return(false)); + const QuicString payload(payload_length, 'a'); + EXPECT_EQ(0u, connection_.SendStreamDataWithString(3, payload, 0, NO_FIN) + .bytes_consumed); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_P(QuicConnectionTest, LoopThroughSendingPackets) { + // All packets carry version info till version is negotiated. + size_t payload_length; + + // Number of packets this test generates. The goal is to have + // kPacketCount packets, each the same size (overhead and payload). + // The payload will vary depending on the overhead (which in turn + // varies per the QUIC packet encoding rules). + const int kPacketCount = 7; + + // Get the basic packet size. This assumes, among other things, a + // stream offset of 0. + size_t length = GetPacketLengthForOneStream( + connection_.version().transport_version, kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), + QuicPacketCreatorPeer::GetLengthLength(creator_), &payload_length); + // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining + // packet length. The size of the offset field in a stream frame is + // 0 for offset 0, and 2 for non-zero offsets up through 16K (for + // versions other than 99) and 1 for non-zero offsets through 16K + // for version 99. Increase the length by 1 or 2, as apporpriate, so + // that subsequent packets containing subsequent stream frames with + // non-zero offsets will fit within the packet length. + if (connection_.version().transport_version == QUIC_VERSION_99) { + length = length + 1; + } else { + length = length + 2; + } + + connection_.SetMaxPacketLength(length); + + // Queue the first packet. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .Times(kPacketCount); + + size_t total_payload_length = payload_length * kPacketCount; + // The first frame of the stream is at offset 0. When the offset is + // 0, it is not included in the stream frame. Increase the total + // payload so that the "missing" offset byte in the first packet is + // occupied by a payload byte. The net result is that each of the N + // packets of the test will contain a single stream frame, each of + // which will be the same size (overhead + data). + if (connection_.version().transport_version == QUIC_VERSION_99) { + // Version 99 encodes the offset in 1 byte for the scope of this test. + total_payload_length = total_payload_length + 1; + } else { + // Versions other than 99 encode the offset in 2 bytes for the + // scope of this test. + total_payload_length = total_payload_length + 2; + } + const QuicString payload(total_payload_length, 'a'); + + EXPECT_EQ(payload.size(), + connection_ + .SendStreamDataWithString(QuicUtils::GetCryptoStreamId( + connection_.transport_version()), + payload, 0, NO_FIN) + .bytes_consumed); +} + +TEST_P(QuicConnectionTest, LoopThroughSendingPacketsWithTruncation) { + set_perspective(Perspective::IS_SERVER); + if (GetParam().version.transport_version <= QUIC_VERSION_43) { + // For IETF QUIC, encryption level will be switched to FORWARD_SECURE in + // SendStreamDataWithString. + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + } + // Set up a larger payload than will fit in one packet. + const QuicString payload(connection_.max_packet_length(), 'a'); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber()); + + // Now send some packets with no truncation. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + EXPECT_EQ(payload.size(), + connection_.SendStreamDataWithString(3, payload, 0, NO_FIN) + .bytes_consumed); + // Track the size of the second packet here. The overhead will be the largest + // we see in this test, due to the non-truncated connection id. + size_t non_truncated_packet_size = writer_->last_packet_size(); + + // Change to a 0 byte connection id. + QuicConfig config; + QuicConfigPeer::SetReceivedBytesForConnectionId(&config, 0); + connection_.SetFromConfig(config); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + EXPECT_EQ(payload.size(), + connection_.SendStreamDataWithString(3, payload, 1350, NO_FIN) + .bytes_consumed); + if (connection_.transport_version() > QUIC_VERSION_43) { + // Short header packets sent from server omit connection ID already, and + // stream offset size increases from 0 to 2. + EXPECT_EQ(non_truncated_packet_size, writer_->last_packet_size() - 2); + } else { + // Just like above, we save 8 bytes on payload, and 8 on truncation. -2 + // because stream offset size is 2 instead of 0. + EXPECT_EQ(non_truncated_packet_size, + writer_->last_packet_size() + 8 * 2 - 2); + } +} + +TEST_P(QuicConnectionTest, SendDelayedAck) { + QuicTime ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime(); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + // Simulate delayed ack alarm firing. + connection_.GetAckAlarm()->Fire(); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { + QuicConnectionPeer::SetFastAckAfterQuiescence(&connection_, true); + + // The beginning of the connection counts as quiescence. + QuicTime ack_time = + clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + // Simulate delayed ack alarm firing. + connection_.GetAckAlarm()->Fire(); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // Process another packet immedately after sending the ack and expect the + // ack alarm to be set delayed ack time in the future. + ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime(); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + // Simulate delayed ack alarm firing. + connection_.GetAckAlarm()->Fire(); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // Wait 1 second and enesure the ack alarm is set to 1ms in the future. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckDecimation) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // The 10th received packet causes an ack to be sent. + for (int i = 0; i < 9; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION); + QuicConnectionPeer::SetFastAckAfterQuiescence(&connection_, true); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + // The beginning of the connection counts as quiescence. + QuicTime ack_time = + clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + // Simulate delayed ack alarm firing. + connection_.GetAckAlarm()->Fire(); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // Process another packet immedately after sending the ack and expect the + // ack alarm to be set delayed ack time in the future. + ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime(); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + // Simulate delayed ack alarm firing. + connection_.GetAckAlarm()->Fire(); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // Wait 1 second and enesure the ack alarm is set to 1ms in the future. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // Process enough packets to get into ack decimation behavior. + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 4; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(4 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // The 10th received packet causes an ack to be sent. + for (int i = 0; i < 9; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // Wait 1 second and enesure the ack alarm is set to 1ms in the future. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kACKD); + // No limit on the number of packets received before sending an ack. + connection_options.push_back(kAKDU); + config.SetConnectionOptionsToSend(connection_options); + connection_.SetFromConfig(config); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // 18 packets will not cause an ack to be sent. 19 will because when + // stop waiting frames are in use, we ack every 20 packets no matter what. + for (int i = 0; i < 18; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + } + // The delayed ack timer should still be set to the expected deadline. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION); + QuicConnectionPeer::SetAckDecimationDelay(&connection_, 0.125); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/8, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 8); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // The 10th received packet causes an ack to be sent. + for (int i = 0; i < 9; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // Receive one packet out of order and then the rest in order. + // The loop leaves a one packet gap between acks sent to simulate some loss. + for (int j = 0; j < 3; ++j) { + // Process packet 10 first and ensure the alarm is one eighth min_rtt. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9 + (j * 11), + !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // The 10th received packet causes an ack to be sent. + writer_->Reset(); + for (int i = 0; i < 9; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + // The ACK shouldn't be sent until the 10th packet is processed. + EXPECT_TRUE(writer_->ack_frames().empty()); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + i + (j * 11), + !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + } +} + +TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // Process packet 10 first and ensure the alarm is one eighth min_rtt. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // The 10th received packet causes an ack to be sent. + for (int i = 0; i < 8; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // The next packet received in order will cause an immediate ack, + // because it fills a hole. + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING); + QuicConnectionPeer::SetAckDecimationDelay(&connection_, 0.125); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/8, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 8); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // Process packet 10 first and ensure the alarm is one eighth min_rtt. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // The 10th received packet causes an ack to be sent. + for (int i = 0; i < 8; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, + SendDelayedAckDecimationWithLargeReorderingEighthRtt) { + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING); + QuicConnectionPeer::SetAckDecimationDelay(&connection_, 0.125); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/8, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 8); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<StrictTaggingDecrypter>(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + QuicMakeUnique<TaggingEncrypter>(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // Process packet 10 first and ensure the alarm is one eighth min_rtt. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // The 10th received packet causes an ack to be sent. + for (int i = 0; i < 8; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // The next packet received in order will cause an immediate ack, + // because it fills a hole. + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting, + ENCRYPTION_ZERO_RTT); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckOnHandshakeConfirmed) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessPacket(1); + // Check that ack is sent and that delayed ack alarm is set. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + QuicTime ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime(); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // Completing the handshake as the server does nothing. + QuicConnectionPeer::SetPerspective(&connection_, Perspective::IS_SERVER); + connection_.OnHandshakeComplete(); + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // Complete the handshake as the client decreases the delayed ack time to 0ms. + QuicConnectionPeer::SetPerspective(&connection_, Perspective::IS_CLIENT); + connection_.OnHandshakeComplete(); + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow(), connection_.GetAckAlarm()->deadline()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckOnSecondPacket) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessPacket(1); + ProcessPacket(2); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, NoAckOnOldNacks) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + // Drop one packet, triggering a sequence of acks. + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + } else { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + } + ProcessPacket(2); + size_t frames_per_ack = GetParam().no_stop_waiting ? 1 : 2; + if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); + writer_->Reset(); + } + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + ProcessPacket(3); + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); + writer_->Reset(); + + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + } else { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + } + ProcessPacket(4); + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_EQ(0u, writer_->frame_count()); + } else { + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); + writer_->Reset(); + } + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + ProcessPacket(5); + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); + writer_->Reset(); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + // Now only set the timer on the 6th packet, instead of sending another ack. + ProcessPacket(6); + EXPECT_EQ(0u, writer_->frame_count()); + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessPacket(1); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, NO_FIN); + // Check that ack is bundled with outgoing data and that delayed ack + // alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessPacket(1); + connection_.SendStreamDataWithString( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0, + NO_FIN); + // Check that ack is bundled with outgoing crypto data. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(4u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, BlockAndBufferOnFirstCHLOPacketOfTwo) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessPacket(1); + BlockOnNextWrite(); + writer_->set_is_write_blocked_data_buffered(true); + connection_.SendStreamDataWithString( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0, + NO_FIN); + EXPECT_TRUE(writer_->IsWriteBlocked()); + EXPECT_FALSE(connection_.HasQueuedData()); + connection_.SendStreamDataWithString( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), "bar", 3, + NO_FIN); + EXPECT_TRUE(writer_->IsWriteBlocked()); + EXPECT_TRUE(connection_.HasQueuedData()); +} + +TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnCanWrite()) + .WillOnce(IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendCryptoStreamData))); + // Process a packet from the crypto stream, which is frame1_'s default. + // Receiving the CHLO as packet 2 first will cause the connection to + // immediately send an ack, due to the packet gap. + ProcessPacket(2); + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(4u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + if (connection_.transport_version() < QUIC_VERSION_47) { + EXPECT_EQ(1u, writer_->stream_frames().size()); + } else { + EXPECT_EQ(1u, writer_->crypto_frames().size()); + } + EXPECT_EQ(1u, writer_->padding_frames().size()); + ASSERT_FALSE(writer_->ack_frames().empty()); + EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front())); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, BundleAckForSecondCHLOTwoPacketReject) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + + // Process two packets from the crypto stream, which is frame1_'s default, + // simulating a 2 packet reject. + { + ProcessPacket(1); + // Send the new CHLO when the REJ is processed. + EXPECT_CALL(visitor_, OnStreamFrame(_)) + .WillOnce(IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendCryptoStreamData))); + ProcessDataPacket(2); + } + // Check that ack is sent and that delayed ack alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(4u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + if (connection_.transport_version() < QUIC_VERSION_47) { + EXPECT_EQ(1u, writer_->stream_frames().size()); + } else { + EXPECT_EQ(1u, writer_->crypto_frames().size()); + } + EXPECT_EQ(1u, writer_->padding_frames().size()); + ASSERT_FALSE(writer_->ack_frames().empty()); + EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front())); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, NO_FIN); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 3, NO_FIN); + // Ack the second packet, which will retransmit the first packet. + QuicAckFrame ack = ConstructAckFrame(2, 1); + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&ack); + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + writer_->Reset(); + + // Now ack the retransmission, which will both raise the high water mark + // and see if there is more data to send. + ack = ConstructAckFrame(3, 1); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(&ack); + + // Check that no packet is sent and the ack alarm isn't set. + EXPECT_EQ(0u, writer_->frame_count()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + writer_->Reset(); + + // Send the same ack, but send both data and an ack together. + ack = ConstructAckFrame(3, 1); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(visitor_, OnCanWrite()) + .WillOnce(IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::EnsureWritableAndSendStreamData5))); + ProcessAckPacket(&ack); + + // Check that ack is bundled with outgoing data and the delayed ack + // alarm is reset. + if (GetParam().no_stop_waiting) { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_TRUE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(writer_->ack_frames().front())); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, NoAckSentForClose) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessPacket(1); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _, + ConnectionCloseSource::FROM_PEER)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + ProcessClosePacket(2); +} + +TEST_P(QuicConnectionTest, SendWhenDisconnected) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _, + ConnectionCloseSource::FROM_SELF)); + connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason", + ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_FALSE(connection_.connected()); + EXPECT_FALSE(connection_.CanWriteStreamData()); + std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) + .Times(0); + connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet), + HAS_RETRANSMITTABLE_DATA, false, false); +} + +TEST_P(QuicConnectionTest, SendConnectivityProbingWhenDisconnected) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (!IsDefaultTestConfiguration()) { + return; + } + + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _, + ConnectionCloseSource::FROM_SELF)); + connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason", + ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_FALSE(connection_.connected()); + EXPECT_FALSE(connection_.CanWriteStreamData()); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) + .Times(0); + + EXPECT_QUIC_BUG(connection_.SendConnectivityProbingPacket( + writer_.get(), connection_.peer_address()), + "Not sending connectivity probing packet as connection is " + "disconnected."); +} + +TEST_P(QuicConnectionTest, WriteBlockedAfterClientSendsConnectivityProbe) { + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + TestPacketWriter probing_writer(version(), &clock_); + // Block next write so that sending connectivity probe will encounter a + // blocked write when send a connectivity probe to the peer. + probing_writer.BlockOnNextWrite(); + // Connection will not be marked as write blocked as connectivity probe only + // affects the probing_writer which is not the default. + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) + .Times(1); + connection_.SendConnectivityProbingPacket(&probing_writer, + connection_.peer_address()); +} + +TEST_P(QuicConnectionTest, WriterBlockedAfterServerSendsConnectivityProbe) { + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + // Block next write so that sending connectivity probe will encounter a + // blocked write when send a connectivity probe to the peer. + writer_->BlockOnNextWrite(); + // Connection will be marked as write blocked as server uses the default + // writer to send connectivity probes. + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) + .Times(1); + connection_.SendConnectivityProbingPacket(writer_.get(), + connection_.peer_address()); +} + +TEST_P(QuicConnectionTest, WriterErrorWhenClientSendsConnectivityProbe) { + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + TestPacketWriter probing_writer(version(), &clock_); + probing_writer.SetShouldWriteFail(); + + // Connection should not be closed if a connectivity probe is failed to be + // sent. + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) + .Times(0); + connection_.SendConnectivityProbingPacket(&probing_writer, + connection_.peer_address()); +} + +TEST_P(QuicConnectionTest, WriterErrorWhenServerSendsConnectivityProbe) { + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + writer_->SetShouldWriteFail(); + // Connection should not be closed if a connectivity probe is failed to be + // sent. + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) + .Times(0); + connection_.SendConnectivityProbingPacket(writer_.get(), + connection_.peer_address()); +} + +TEST_P(QuicConnectionTest, PublicReset) { + if (GetParam().version.transport_version > QUIC_VERSION_43) { + return; + } + QuicPublicResetPacket header; + // Public reset packet in only built by server. + header.connection_id = connection_id_; + std::unique_ptr<QuicEncryptedPacket> packet( + framer_.BuildPublicResetPacket(header)); + std::unique_ptr<QuicReceivedPacket> received( + ConstructReceivedPacket(*packet, QuicTime::Zero())); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PUBLIC_RESET, _, + ConnectionCloseSource::FROM_PEER)); + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received); +} + +TEST_P(QuicConnectionTest, IetfStatelessReset) { + if (GetParam().version.transport_version <= QUIC_VERSION_43) { + return; + } + const QuicUint128 kTestStatelessResetToken = 1010101; + QuicConfig config; + QuicConfigPeer::SetReceivedStatelessResetToken(&config, + kTestStatelessResetToken); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + std::unique_ptr<QuicEncryptedPacket> packet( + QuicFramer::BuildIetfStatelessResetPacket(connection_id_, + kTestStatelessResetToken)); + std::unique_ptr<QuicReceivedPacket> received( + ConstructReceivedPacket(*packet, QuicTime::Zero())); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PUBLIC_RESET, _, + ConnectionCloseSource::FROM_PEER)); + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received); +} + +TEST_P(QuicConnectionTest, GoAway) { + if (GetParam().version.transport_version == QUIC_VERSION_99) { + // GoAway is not available in version 99. + return; + } + + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + QuicGoAwayFrame goaway; + goaway.last_good_stream_id = 1; + goaway.error_code = QUIC_PEER_GOING_AWAY; + goaway.reason_phrase = "Going away."; + EXPECT_CALL(visitor_, OnGoAway(_)); + ProcessGoAwayPacket(&goaway); +} + +TEST_P(QuicConnectionTest, WindowUpdate) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + QuicWindowUpdateFrame window_update; + window_update.stream_id = 3; + window_update.byte_offset = 1234; + EXPECT_CALL(visitor_, OnWindowUpdateFrame(_)); + ProcessFramePacket(QuicFrame(&window_update)); +} + +TEST_P(QuicConnectionTest, Blocked) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + QuicBlockedFrame blocked; + blocked.stream_id = 3; + EXPECT_CALL(visitor_, OnBlockedFrame(_)); + ProcessFramePacket(QuicFrame(&blocked)); + EXPECT_EQ(1u, connection_.GetStats().blocked_frames_received); + EXPECT_EQ(0u, connection_.GetStats().blocked_frames_sent); +} + +TEST_P(QuicConnectionTest, ZeroBytePacket) { + // Don't close the connection for zero byte packets. + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0); + QuicReceivedPacket encrypted(nullptr, 0, QuicTime::Zero()); + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, encrypted); +} + +TEST_P(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) { + if (GetParam().version.transport_version > QUIC_VERSION_43) { + return; + } + // Set the packet number of the ack packet to be least unacked (4). + QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 3); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessStopWaitingPacket(InitStopWaitingFrame(4)); + EXPECT_FALSE(outgoing_ack()->packets.Empty()); +} + +TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacket) { + // Turn off QUIC_VERSION_99. + SetQuicReloadableFlag(quic_enable_version_99, false); + connection_.SetSupportedVersions(CurrentSupportedVersions()); + set_perspective(Perspective::IS_SERVER); + if (GetParam().version.transport_version > QUIC_VERSION_43) { + peer_framer_.set_version_for_tests( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99)); + } else { + peer_framer_.set_version_for_tests(UnsupportedQuicVersion()); + } + + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + header.version_flag = true; + header.packet_number = QuicPacketNumber(12); + + if (QuicVersionHasLongHeaderLengths( + peer_framer_.version().transport_version)) { + header.long_packet_type = INITIAL; + header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize); + + framer_.set_version(version()); + // Writer's framer's perspective is client, so that it needs to have the right + // version to process either IETF or GQUIC version negotiation packet. + writer_->SetSupportedVersions({version()}); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); + EXPECT_TRUE(writer_->version_negotiation_packet() != nullptr); + + ParsedQuicVersionVector supported_versions = CurrentSupportedVersions(); + ASSERT_EQ(supported_versions.size(), + writer_->version_negotiation_packet()->versions.size()); + + // We expect all versions in supported_versions to be + // included in the packet. + for (size_t i = 0; i < supported_versions.size(); ++i) { + EXPECT_EQ(supported_versions[i], + writer_->version_negotiation_packet()->versions[i]); + } +} + +TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) { + // Turn off QUIC_VERSION_99. + SetQuicReloadableFlag(quic_enable_version_99, false); + connection_.SetSupportedVersions(CurrentSupportedVersions()); + set_perspective(Perspective::IS_SERVER); + if (GetParam().version.transport_version > QUIC_VERSION_43) { + peer_framer_.set_version_for_tests( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99)); + } else { + peer_framer_.set_version_for_tests(UnsupportedQuicVersion()); + } + + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + header.version_flag = true; + header.packet_number = QuicPacketNumber(12); + + if (QuicVersionHasLongHeaderLengths( + peer_framer_.version().transport_version)) { + header.long_packet_type = INITIAL; + header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize); + + framer_.set_version(version()); + BlockOnNextWrite(); + // Writer's framer's perspective is client, so that it needs to have the right + // version to process either IETF or GQUIC version negotiation packet. + writer_->SetSupportedVersions({version()}); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); + EXPECT_EQ(0u, writer_->last_packet_size()); + EXPECT_TRUE(connection_.HasQueuedData()); + + writer_->SetWritable(); + connection_.OnCanWrite(); + EXPECT_TRUE(writer_->version_negotiation_packet() != nullptr); + + ParsedQuicVersionVector supported_versions = CurrentSupportedVersions(); + ASSERT_EQ(supported_versions.size(), + writer_->version_negotiation_packet()->versions.size()); + + // We expect all versions in supported_versions to be + // included in the packet. + for (size_t i = 0; i < supported_versions.size(); ++i) { + EXPECT_EQ(supported_versions[i], + writer_->version_negotiation_packet()->versions[i]); + } +} + +TEST_P(QuicConnectionTest, + ServerSendsVersionNegotiationPacketSocketBlockedDataBuffered) { + // Turn off QUIC_VERSION_99. + SetQuicReloadableFlag(quic_enable_version_99, false); + connection_.SetSupportedVersions(CurrentSupportedVersions()); + set_perspective(Perspective::IS_SERVER); + if (GetParam().version.transport_version > QUIC_VERSION_43) { + peer_framer_.set_version_for_tests( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99)); + } else { + peer_framer_.set_version_for_tests(UnsupportedQuicVersion()); + } + + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + header.version_flag = true; + header.packet_number = QuicPacketNumber(12); + + if (QuicVersionHasLongHeaderLengths( + peer_framer_.version().transport_version)) { + header.long_packet_type = INITIAL; + header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + char buffer[kMaxPacketSize]; + size_t encryped_length = framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize); + + framer_.set_version(version()); + set_perspective(Perspective::IS_SERVER); + BlockOnNextWrite(); + writer_->set_is_write_blocked_data_buffered(true); + // Writer's framer's perspective is client, so that it needs to have the right + // version to process either IETF or GQUIC version negotiation packet. + writer_->SetSupportedVersions({version()}); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encryped_length, QuicTime::Zero(), false)); + EXPECT_EQ(0u, writer_->last_packet_size()); + EXPECT_FALSE(connection_.HasQueuedData()); +} + +TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) { + // Start out with some unsupported version. + QuicConnectionPeer::GetFramer(&connection_) + ->set_version_for_tests(ParsedQuicVersion( + PROTOCOL_UNSUPPORTED, + GetParam().version.transport_version == QUIC_VERSION_99 + ? QUIC_VERSION_99 + : QUIC_VERSION_UNSUPPORTED)); + + // Send a version negotiation packet. + std::unique_ptr<QuicEncryptedPacket> encrypted( + peer_framer_.BuildVersionNegotiationPacket( + connection_id_, connection_.transport_version() > QUIC_VERSION_43, + AllSupportedVersions())); + std::unique_ptr<QuicReceivedPacket> received( + ConstructReceivedPacket(*encrypted, QuicTime::Zero())); + if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) { + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_VERSION, _, + ConnectionCloseSource::FROM_SELF)); + } + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received); + if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) { + EXPECT_FALSE(connection_.connected()); + return; + } + + // Now force another packet. The connection should transition into + // NEGOTIATED_VERSION state and tell the packet creator to StopSendingVersion. + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + header.destination_connection_id_included = CONNECTION_ID_ABSENT; + header.packet_number = QuicPacketNumber(12); + header.version_flag = false; + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize); + ASSERT_NE(0u, encrypted_length); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); + if (GetParam().version.transport_version > QUIC_VERSION_43) { + // IETF QUIC stops sending version when switch to FORWARD_SECURE. + EXPECT_NE(ENCRYPTION_FORWARD_SECURE, connection_.encryption_level()); + ASSERT_TRUE(QuicPacketCreatorPeer::SendVersionInPacket(creator_)); + } else { + ASSERT_FALSE(QuicPacketCreatorPeer::SendVersionInPacket(creator_)); + } +} + +TEST_P(QuicConnectionTest, BadVersionNegotiation) { + // Send a version negotiation packet with the version the client started with. + // It should be rejected. + EXPECT_CALL(visitor_, + OnConnectionClosed(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, _, + ConnectionCloseSource::FROM_SELF)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + framer_.BuildVersionNegotiationPacket( + connection_id_, connection_.transport_version() > QUIC_VERSION_43, + AllSupportedVersions())); + std::unique_ptr<QuicReceivedPacket> received( + ConstructReceivedPacket(*encrypted, QuicTime::Zero())); + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received); +} + +TEST_P(QuicConnectionTest, CheckSendStats) { + connection_.SetMaxTailLossProbes(0); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.SendStreamDataWithString(3, "first", 0, NO_FIN); + size_t first_packet_size = writer_->last_packet_size(); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + connection_.SendStreamDataWithString(5, "second", 0, NO_FIN); + size_t second_packet_size = writer_->last_packet_size(); + + // 2 retransmissions due to rto, 1 due to explicit nack. + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3); + + // Retransmit due to RTO. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); + connection_.GetRetransmissionAlarm()->Fire(); + + // Retransmit due to explicit nacks. + QuicAckFrame nack_three = + InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}, + {QuicPacketNumber(4), QuicPacketNumber(5)}}); + + LostPacketVector lost_packets; + lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize)); + lost_packets.push_back(LostPacket(QuicPacketNumber(3), kMaxPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .WillOnce(SetArgPointee<5>(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + if (!connection_.session_decides_what_to_write()) { + EXPECT_CALL(visitor_, OnCanWrite()); + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessAckPacket(&nack_three); + + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()) + .WillOnce(Return(QuicBandwidth::Zero())); + + const QuicConnectionStats& stats = connection_.GetStats(); + // For IETF QUIC, version is not included as the encryption level switches to + // FORWARD_SECURE in SendStreamDataWithString. + size_t save_on_version = + GetParam().version.transport_version > QUIC_VERSION_43 ? 0 + : kQuicVersionSize; + EXPECT_EQ(3 * first_packet_size + 2 * second_packet_size - save_on_version, + stats.bytes_sent); + EXPECT_EQ(5u, stats.packets_sent); + EXPECT_EQ(2 * first_packet_size + second_packet_size - save_on_version, + stats.bytes_retransmitted); + EXPECT_EQ(3u, stats.packets_retransmitted); + EXPECT_EQ(1u, stats.rto_count); + EXPECT_EQ(kDefaultMaxPacketSize, stats.max_packet_size); +} + +TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { + // Construct a packet with stream frame and connection close frame. + QuicPacketHeader header; + header.destination_connection_id = connection_id_; + if (peer_framer_.transport_version() > QUIC_VERSION_43) { + header.destination_connection_id_included = CONNECTION_ID_ABSENT; + } + header.packet_number = QuicPacketNumber(1); + header.version_flag = false; + + QuicConnectionCloseFrame qccf; + qccf.error_code = QUIC_PEER_GOING_AWAY; + + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + frames.push_back(QuicFrame(&qccf)); + std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); + EXPECT_TRUE(nullptr != packet); + char buffer[kMaxPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(1), *packet, buffer, kMaxPacketSize); + + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _, + ConnectionCloseSource::FROM_PEER)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); +} + +TEST_P(QuicConnectionTest, SelectMutualVersion) { + connection_.SetSupportedVersions(AllSupportedVersions()); + // Set the connection to speak the lowest quic version. + connection_.set_version(QuicVersionMin()); + EXPECT_EQ(QuicVersionMin(), connection_.version()); + + // Pass in available versions which includes a higher mutually supported + // version. The higher mutually supported version should be selected. + ParsedQuicVersionVector supported_versions = AllSupportedVersions(); + EXPECT_TRUE(connection_.SelectMutualVersion(supported_versions)); + EXPECT_EQ(QuicVersionMax(), connection_.version()); + + // Expect that the lowest version is selected. + // Ensure the lowest supported version is less than the max, unless they're + // the same. + ParsedQuicVersionVector lowest_version_vector; + lowest_version_vector.push_back(QuicVersionMin()); + EXPECT_TRUE(connection_.SelectMutualVersion(lowest_version_vector)); + EXPECT_EQ(QuicVersionMin(), connection_.version()); + + // Shouldn't be able to find a mutually supported version. + ParsedQuicVersionVector unsupported_version; + unsupported_version.push_back(UnsupportedQuicVersion()); + EXPECT_FALSE(connection_.SelectMutualVersion(unsupported_version)); +} + +TEST_P(QuicConnectionTest, ConnectionCloseWhenWritable) { + EXPECT_FALSE(writer_->IsWriteBlocked()); + + // Send a packet. + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + + TriggerConnectionClose(); + EXPECT_EQ(2u, writer_->packets_write_attempts()); +} + +TEST_P(QuicConnectionTest, ConnectionCloseGettingWriteBlocked) { + BlockOnNextWrite(); + TriggerConnectionClose(); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + EXPECT_TRUE(writer_->IsWriteBlocked()); +} + +TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) { + BlockOnNextWrite(); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + EXPECT_TRUE(writer_->IsWriteBlocked()); + TriggerConnectionClose(); + EXPECT_EQ(1u, writer_->packets_write_attempts()); +} + +TEST_P(QuicConnectionTest, OnPacketSentDebugVisitor) { + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1); + connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); + + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1); + connection_.SendConnectivityProbingPacket(writer_.get(), + connection_.peer_address()); +} + +TEST_P(QuicConnectionTest, OnPacketHeaderDebugVisitor) { + QuicPacketHeader header; + header.packet_number = QuicPacketNumber(1); + if (GetParam().version.transport_version > QUIC_VERSION_43) { + header.form = IETF_QUIC_LONG_HEADER_PACKET; + } + + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + EXPECT_CALL(debug_visitor, OnPacketHeader(Ref(header))).Times(1); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)).Times(1); + EXPECT_CALL(debug_visitor, OnSuccessfulVersionNegotiation(_)).Times(1); + connection_.OnPacketHeader(header); +} + +TEST_P(QuicConnectionTest, Pacing) { + TestConnection server(connection_id_, kSelfAddress, helper_.get(), + alarm_factory_.get(), writer_.get(), + Perspective::IS_SERVER, version()); + TestConnection client(connection_id_, kPeerAddress, helper_.get(), + alarm_factory_.get(), writer_.get(), + Perspective::IS_CLIENT, version()); + EXPECT_FALSE(QuicSentPacketManagerPeer::UsingPacing( + static_cast<const QuicSentPacketManager*>( + &client.sent_packet_manager()))); + EXPECT_FALSE(QuicSentPacketManagerPeer::UsingPacing( + static_cast<const QuicSentPacketManager*>( + &server.sent_packet_manager()))); +} + +TEST_P(QuicConnectionTest, WindowUpdateInstigateAcks) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Send a WINDOW_UPDATE frame. + QuicWindowUpdateFrame window_update; + window_update.stream_id = 3; + window_update.byte_offset = 1234; + EXPECT_CALL(visitor_, OnWindowUpdateFrame(_)); + ProcessFramePacket(QuicFrame(&window_update)); + + // Ensure that this has caused the ACK alarm to be set. + QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); + EXPECT_TRUE(ack_alarm->IsSet()); +} + +TEST_P(QuicConnectionTest, BlockedFrameInstigateAcks) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Send a BLOCKED frame. + QuicBlockedFrame blocked; + blocked.stream_id = 3; + EXPECT_CALL(visitor_, OnBlockedFrame(_)); + ProcessFramePacket(QuicFrame(&blocked)); + + // Ensure that this has caused the ACK alarm to be set. + QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); + EXPECT_TRUE(ack_alarm->IsSet()); +} + +TEST_P(QuicConnectionTest, ReevaluateTimeUntilSendOnAck) { + // Enable pacing. + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + connection_.SetFromConfig(config); + + // Send two packets. One packet is not sufficient because if it gets acked, + // there will be no packets in flight after that and the pacer will always + // allow the next packet in that situation. + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, NO_FIN); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "bar", + 3, NO_FIN); + connection_.OnCanWrite(); + + // Schedule the next packet for a few milliseconds in future. + QuicSentPacketManagerPeer::DisablePacerBursts(manager_); + QuicTime scheduled_pacing_time = + clock_.Now() + QuicTime::Delta::FromMilliseconds(5); + QuicSentPacketManagerPeer::SetNextPacedPacketTime(manager_, + scheduled_pacing_time); + + // Send a packet and have it be blocked by congestion control. + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(false)); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "baz", + 6, NO_FIN); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + + // Process an ack and the send alarm will be set to the new 5ms delay. + QuicAckFrame ack = InitAckFrame(1); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + ProcessAckPacket(&ack); + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_TRUE(connection_.GetSendAlarm()->IsSet()); + EXPECT_EQ(scheduled_pacing_time, connection_.GetSendAlarm()->deadline()); + writer_->Reset(); +} + +TEST_P(QuicConnectionTest, SendAcksImmediately) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(1); + CongestionBlockWrites(); + SendAckPacketToPeer(); +} + +TEST_P(QuicConnectionTest, SendPingImmediately) { + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + + CongestionBlockWrites(); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1); + EXPECT_CALL(debug_visitor, OnPingSent()).Times(1); + connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); + EXPECT_FALSE(connection_.HasQueuedData()); +} + +TEST_P(QuicConnectionTest, SendBlockedImmediately) { + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1); + EXPECT_EQ(0u, connection_.GetStats().blocked_frames_sent); + connection_.SendControlFrame(QuicFrame(new QuicBlockedFrame(1, 3))); + EXPECT_EQ(1u, connection_.GetStats().blocked_frames_sent); + EXPECT_FALSE(connection_.HasQueuedData()); +} + +TEST_P(QuicConnectionTest, SendingUnencryptedStreamDataFails) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (!IsDefaultTestConfiguration()) { + return; + } + + EXPECT_CALL(visitor_, + OnConnectionClosed(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA, + _, ConnectionCloseSource::FROM_SELF)); + struct iovec iov; + MakeIOVector("", &iov); + EXPECT_QUIC_BUG(connection_.SaveAndSendStreamData(3, &iov, 1, 0, 0, FIN), + "Cannot send stream data without encryption."); + EXPECT_FALSE(connection_.connected()); +} + +TEST_P(QuicConnectionTest, SetRetransmissionAlarmForCryptoPacket) { + EXPECT_TRUE(connection_.connected()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendCryptoStreamData(); + + // Verify retransmission timer is correctly set after crypto packet has been + // sent. + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + QuicTime retransmission_time = + QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetRetransmissionTime(); + EXPECT_NE(retransmission_time, clock_.ApproximateNow()); + EXPECT_EQ(retransmission_time, + connection_.GetRetransmissionAlarm()->deadline()); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.GetRetransmissionAlarm()->Fire(); +} + +TEST_P(QuicConnectionTest, PathDegradingAlarmForCryptoPacket) { + EXPECT_TRUE(connection_.connected()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.IsPathDegrading()); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendCryptoStreamData(); + + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.IsPathDegrading()); + QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + + // Fire the path degrading alarm, path degrading signal should be sent to + // the visitor. + EXPECT_CALL(visitor_, OnPathDegrading()); + clock_.AdvanceTime(delay); + connection_.GetPathDegradingAlarm()->Fire(); + EXPECT_TRUE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); +} + +// Includes regression test for https://b.corp.google.com/issues/69979024. +TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) { + EXPECT_TRUE(connection_.connected()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.IsPathDegrading()); + + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + + for (int i = 0; i < 2; ++i) { + // Send a packet. Now there's a retransmittable packet on the wire, so the + // path degrading alarm should be set. + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), data, + offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + // Check the deadline of the path degrading alarm. + QuicTime::Delta delay = + QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + + // Send a second packet. The path degrading alarm's deadline should remain + // the same. + // Regression test for https://b.corp.google.com/issues/69979024. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline(); + connection_.SendStreamDataWithString( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), data, + offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + + // Now receive an ACK of the first packet. This should advance the path + // degrading alarm's deadline since forward progress has been made. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + if (i == 0) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + } + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = InitAckFrame( + {{QuicPacketNumber(1u + 2u * i), QuicPacketNumber(2u + 2u * i)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + // Check the deadline of the path degrading alarm. + delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + + if (i == 0) { + // Now receive an ACK of the second packet. Since there are no more + // retransmittable packets on the wire, this should cancel the path + // degrading alarm. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); + ProcessAckPacket(&frame); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + } else { + // Advance time to the path degrading alarm's deadline and simulate + // firing the alarm. + clock_.AdvanceTime(delay); + EXPECT_CALL(visitor_, OnPathDegrading()); + connection_.GetPathDegradingAlarm()->Fire(); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + } + } + EXPECT_TRUE(connection_.IsPathDegrading()); +} + +TEST_P(QuicConnectionTest, RetransmittableOnWireSetsPingAlarm) { + const QuicTime::Delta retransmittable_on_wire_timeout = + QuicTime::Delta::FromMilliseconds(50); + connection_.set_retransmittable_on_wire_timeout( + retransmittable_on_wire_timeout); + + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + + // Send a packet. + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + // Now there's a retransmittable packet on the wire, so the path degrading + // alarm should be set. + // The retransmittable-on-wire alarm should not be set. + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + ASSERT_TRUE(connection_.sent_packet_manager().HasInFlightPackets()); + // The ping alarm is set for the ping timeout, not the shorter + // retransmittable_on_wire_timeout. + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + QuicTime::Delta ping_delay = QuicTime::Delta::FromSeconds(kPingTimeoutSecs); + EXPECT_EQ((clock_.ApproximateNow() + ping_delay), + connection_.GetPingAlarm()->deadline()); + + // Now receive an ACK of the packet. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}}); + ProcessAckPacket(&frame); + // No more retransmittable packets on the wire, so the path degrading alarm + // should be cancelled, and the ping alarm should be set to the + // retransmittable_on_wire_timeout. + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout, + connection_.GetPingAlarm()->deadline()); + + // Simulate firing the ping alarm and sending a PING. + clock_.AdvanceTime(retransmittable_on_wire_timeout); + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { + connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); + })); + connection_.GetPingAlarm()->Fire(); + + // Now there's a retransmittable packet (PING) on the wire, so the path + // degrading alarm should be set. + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); +} + +// This test verifies that the connection marks path as degrading and does not +// spin timer to detect path degrading when a new packet is sent on the +// degraded path. +TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) { + EXPECT_TRUE(connection_.connected()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.IsPathDegrading()); + + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + + // Send the first packet. Now there's a retransmittable packet on the wire, so + // the path degrading alarm should be set. + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + // Check the deadline of the path degrading alarm. + QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + + // Send a second packet. The path degrading alarm's deadline should remain + // the same. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline(); + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + + // Now receive an ACK of the first packet. This should advance the path + // degrading alarm's deadline since forward progress has been made. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + // Check the deadline of the path degrading alarm. + delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + + // Advance time to the path degrading alarm's deadline and simulate + // firing the path degrading alarm. This path will be considered as + // degrading. + clock_.AdvanceTime(delay); + EXPECT_CALL(visitor_, OnPathDegrading()).Times(1); + connection_.GetPathDegradingAlarm()->Fire(); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.IsPathDegrading()); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + // Send a third packet. The path degrading alarm is no longer set but path + // should still be marked as degrading. + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.IsPathDegrading()); +} + +// This test verifies that the connection unmarks path as degrarding and spins +// the timer to detect future path degrading when forward progress is made +// after path has been marked degrading. +TEST_P(QuicConnectionTest, UnmarkPathDegradingOnForwardProgress) { + EXPECT_TRUE(connection_.connected()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.IsPathDegrading()); + + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + + // Send the first packet. Now there's a retransmittable packet on the wire, so + // the path degrading alarm should be set. + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + // Check the deadline of the path degrading alarm. + QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + + // Send a second packet. The path degrading alarm's deadline should remain + // the same. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline(); + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + + // Now receive an ACK of the first packet. This should advance the path + // degrading alarm's deadline since forward progress has been made. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + // Check the deadline of the path degrading alarm. + delay = QuicConnectionPeer::GetSentPacketManager(&connection_) + ->GetPathDegradingDelay(); + EXPECT_EQ(clock_.ApproximateNow() + delay, + connection_.GetPathDegradingAlarm()->deadline()); + + // Advance time to the path degrading alarm's deadline and simulate + // firing the alarm. + clock_.AdvanceTime(delay); + EXPECT_CALL(visitor_, OnPathDegrading()).Times(1); + connection_.GetPathDegradingAlarm()->Fire(); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.IsPathDegrading()); + + // Send a third packet. The path degrading alarm is no longer set but path + // should still be marked as degrading. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.IsPathDegrading()); + + // Now receive an ACK of the second packet. This should unmark the path as + // degrading. And will set a timer to detect new path degrading. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); + ProcessAckPacket(&frame); + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, NoPathDegradingOnServer) { + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + + // Send data. + const char data[] = "data"; + connection_.SendStreamDataWithString(1, data, 0, NO_FIN); + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + + // Ack data. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}}); + ProcessAckPacket(&frame); + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, NoPathDegradingAfterSendingAck) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacket(1); + SendAckPacketToPeer(); + EXPECT_FALSE(connection_.sent_packet_manager().unacked_packets().empty()); + EXPECT_FALSE(connection_.sent_packet_manager().HasInFlightPackets()); + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, MultipleCallsToCloseConnection) { + // Verifies that multiple calls to CloseConnection do not + // result in multiple attempts to close the connection - it will be marked as + // disconnected after the first call. + EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1); + connection_.CloseConnection(QUIC_NO_ERROR, "no reason", + ConnectionCloseBehavior::SILENT_CLOSE); + connection_.CloseConnection(QUIC_NO_ERROR, "no reason", + ConnectionCloseBehavior::SILENT_CLOSE); +} + +TEST_P(QuicConnectionTest, ServerReceivesChloOnNonCryptoStream) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + CryptoHandshakeMessage message; + CryptoFramer framer; + message.set_tag(kCHLO); + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + frame1_.stream_id = 10; + frame1_.data_buffer = data->data(); + frame1_.data_length = data->length(); + + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_MAYBE_CORRUPTED_MEMORY, _, + ConnectionCloseSource::FROM_SELF)); + ForceProcessFramePacket(QuicFrame(frame1_)); +} + +TEST_P(QuicConnectionTest, ClientReceivesRejOnNonCryptoStream) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + CryptoHandshakeMessage message; + CryptoFramer framer; + message.set_tag(kREJ); + std::unique_ptr<QuicData> data(framer.ConstructHandshakeMessage(message)); + frame1_.stream_id = 10; + frame1_.data_buffer = data->data(); + frame1_.data_length = data->length(); + + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_MAYBE_CORRUPTED_MEMORY, _, + ConnectionCloseSource::FROM_SELF)); + ForceProcessFramePacket(QuicFrame(frame1_)); +} + +TEST_P(QuicConnectionTest, CloseConnectionOnPacketTooLarge) { + SimulateNextPacketTooLarge(); + // A connection close packet is sent + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _, + ConnectionCloseSource::FROM_SELF)) + .Times(1); + connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); +} + +TEST_P(QuicConnectionTest, AlwaysGetPacketTooLarge) { + // Test even we always get packet too large, we do not infinitely try to send + // close packet. + AlwaysGetPacketTooLarge(); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _, + ConnectionCloseSource::FROM_SELF)) + .Times(1); + connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); +} + +// Verify that if connection has no outstanding data, it notifies the send +// algorithm after the write. +TEST_P(QuicConnectionTest, SendDataAndBecomeApplicationLimited) { + EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(1); + { + InSequence seq; + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()) + .WillRepeatedly(Return(false)); + } + + connection_.SendStreamData3(); +} + +// Verify that the connection does not become app-limited if there is +// outstanding data to send after the write. +TEST_P(QuicConnectionTest, NotBecomeApplicationLimitedIfMoreDataAvailable) { + EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(0); + { + InSequence seq; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true)); + } + + connection_.SendStreamData3(); +} + +// Verify that the connection does not become app-limited after blocked write +// even if there is outstanding data to send after the write. +TEST_P(QuicConnectionTest, NotBecomeApplicationLimitedDueToWriteBlock) { + EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(0); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true)); + BlockOnNextWrite(); + + connection_.SendStreamData3(); + + // Now unblock the writer, become congestion control blocked, + // and ensure we become app-limited after writing. + writer_->SetWritable(); + CongestionBlockWrites(); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(false)); + { + InSequence seq; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(1); + } + connection_.OnCanWrite(); +} + +// Test the mode in which the link is filled up with probing retransmissions if +// the connection becomes application-limited. +TEST_P(QuicConnectionTest, SendDataWhenApplicationLimited) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, ShouldSendProbingPacket()) + .WillRepeatedly(Return(true)); + { + InSequence seq; + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()) + .WillRepeatedly(Return(false)); + } + // Fix congestion window to be 20,000 bytes. + EXPECT_CALL(*send_algorithm_, CanSend(Ge(20000u))) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*send_algorithm_, CanSend(Lt(20000u))) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(0); + ASSERT_EQ(0u, connection_.GetStats().packets_sent); + connection_.set_fill_up_link_during_probing(true); + connection_.OnHandshakeComplete(); + connection_.SendStreamData3(); + + // We expect a lot of packets from a 20 kbyte window. + EXPECT_GT(connection_.GetStats().packets_sent, 10u); + // Ensure that the packets are padded. + QuicByteCount average_packet_size = + connection_.GetStats().bytes_sent / connection_.GetStats().packets_sent; + EXPECT_GT(average_packet_size, 1000u); + + // Acknowledge all packets sent, except for the last one. + QuicAckFrame ack = InitAckFrame( + connection_.sent_packet_manager().GetLargestSentPacket() - 1); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + + // Ensure that since we no longer have retransmittable bytes in flight, this + // will not cause any responses to be sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(1); + ProcessAckPacket(&ack); +} + +TEST_P(QuicConnectionTest, DonotForceSendingAckOnPacketTooLarge) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + // Send an ack by simulating delayed ack alarm firing. + ProcessPacket(1); + QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); + EXPECT_TRUE(ack_alarm->IsSet()); + connection_.GetAckAlarm()->Fire(); + // Simulate data packet causes write error. + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _, _)); + SimulateNextPacketTooLarge(); + connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_FALSE(writer_->connection_close_frames().empty()); + // Ack frame is not bundled in connection close packet. + EXPECT_TRUE(writer_->ack_frames().empty()); +} + +TEST_P(QuicConnectionTest, CloseConnectionForStatelessReject) { + QuicString error_details("stateless reject"); + EXPECT_CALL(visitor_, OnConnectionClosed( + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, + error_details, ConnectionCloseSource::FROM_PEER)); + connection_.set_perspective(Perspective::IS_CLIENT); + connection_.CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, + error_details, + ConnectionCloseBehavior::SILENT_CLOSE); +} + +// Regression test for b/63620844. +TEST_P(QuicConnectionTest, FailedToWriteHandshakePacket) { + SimulateNextPacketTooLarge(); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _, + ConnectionCloseSource::FROM_SELF)) + .Times(1); + connection_.SendCryptoStreamData(); +} + +TEST_P(QuicConnectionTest, MaxPacingRate) { + EXPECT_EQ(0, connection_.MaxPacingRate().ToBytesPerSecond()); + connection_.SetMaxPacingRate(QuicBandwidth::FromBytesPerSecond(100)); + EXPECT_EQ(100, connection_.MaxPacingRate().ToBytesPerSecond()); +} + +TEST_P(QuicConnectionTest, ClientAlwaysSendConnectionId) { + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + EXPECT_EQ(CONNECTION_ID_PRESENT, + writer_->last_packet_header().destination_connection_id_included); + + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + QuicConfigPeer::SetReceivedBytesForConnectionId(&config, 0); + connection_.SetFromConfig(config); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendStreamDataWithString(3, "bar", 3, NO_FIN); + // Verify connection id is still sent in the packet. + EXPECT_EQ(CONNECTION_ID_PRESENT, + writer_->last_packet_header().destination_connection_id_included); +} + +TEST_P(QuicConnectionTest, SendProbingRetransmissions) { + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + + const QuicStreamId stream_id = 2; + QuicPacketNumber last_packet; + SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "bar", 3, NO_FIN, &last_packet); + SendStreamDataToPeer(stream_id, "test", 6, NO_FIN, &last_packet); + + const QuicByteCount old_bytes_in_flight = + connection_.sent_packet_manager().GetBytesInFlight(); + + // Allow 9 probing retransmissions to be sent. + { + InSequence seq; + EXPECT_CALL(*send_algorithm_, CanSend(_)) + .Times(9 * 2) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false)); + } + // Expect them retransmitted in cyclic order (foo, bar, test, foo, bar...). + QuicPacketCount sent_count = 0; + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)) + .WillRepeatedly(Invoke([this, &sent_count](const SerializedPacket&, + QuicPacketNumber, + TransmissionType, QuicTime) { + ASSERT_EQ(1u, writer_->stream_frames().size()); + // Identify the frames by stream offset (0, 3, 6, 0, 3...). + EXPECT_EQ(3 * (sent_count % 3), writer_->stream_frames()[0]->offset); + sent_count++; + })); + EXPECT_CALL(*send_algorithm_, ShouldSendProbingPacket()) + .WillRepeatedly(Return(true)); + + connection_.SendProbingRetransmissions(); + + // Ensure that the in-flight has increased. + const QuicByteCount new_bytes_in_flight = + connection_.sent_packet_manager().GetBytesInFlight(); + EXPECT_GT(new_bytes_in_flight, old_bytes_in_flight); +} + +// Ensure that SendProbingRetransmissions() does not retransmit anything when +// there are no outstanding packets. +TEST_P(QuicConnectionTest, + SendProbingRetransmissionsFailsWhenNothingToRetransmit) { + ASSERT_TRUE(connection_.sent_packet_manager().unacked_packets().empty()); + + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, ShouldSendProbingPacket()) + .WillRepeatedly(Return(true)); + + connection_.SendProbingRetransmissions(); +} + +TEST_P(QuicConnectionTest, PingAfterLastRetransmittablePacketAcked) { + const QuicTime::Delta retransmittable_on_wire_timeout = + QuicTime::Delta::FromMilliseconds(50); + connection_.set_retransmittable_on_wire_timeout( + retransmittable_on_wire_timeout); + + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + + // Advance 5ms, send a retransmittable packet to the peer. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.sent_packet_manager().HasInFlightPackets()); + // The ping alarm is set for the ping timeout, not the shorter + // retransmittable_on_wire_timeout. + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + QuicTime::Delta ping_delay = QuicTime::Delta::FromSeconds(kPingTimeoutSecs); + EXPECT_EQ((clock_.ApproximateNow() + ping_delay), + connection_.GetPingAlarm()->deadline()); + + // Advance 5ms, send a second retransmittable packet to the peer. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + + // Now receive an ACK of the first packet. This should not set the + // retransmittable-on-wire alarm since packet 2 is still on the wire. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.sent_packet_manager().HasInFlightPackets()); + // The ping alarm is set for the ping timeout, not the shorter + // retransmittable_on_wire_timeout. + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + // The ping alarm has a 1 second granularity, and the clock has been advanced + // 10ms since it was originally set. + EXPECT_EQ((clock_.ApproximateNow() + ping_delay - + QuicTime::Delta::FromMilliseconds(10)), + connection_.GetPingAlarm()->deadline()); + + // Now receive an ACK of the second packet. This should set the + // retransmittable-on-wire alarm now that no retransmittable packets are on + // the wire. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout, + connection_.GetPingAlarm()->deadline()); + + // Now receive a duplicate ACK of the second packet. This should not update + // the ping alarm. + QuicTime prev_deadline = connection_.GetPingAlarm()->deadline(); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(prev_deadline, connection_.GetPingAlarm()->deadline()); + + // Now receive a non-ACK packet. This should not update the ping alarm. + prev_deadline = connection_.GetPingAlarm()->deadline(); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + ProcessPacket(4); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(prev_deadline, connection_.GetPingAlarm()->deadline()); + + // Simulate the alarm firing and check that a PING is sent. + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { + connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); + })); + connection_.GetPingAlarm()->Fire(); + if (GetParam().no_stop_waiting) { + EXPECT_EQ(2u, writer_->frame_count()); + } else { + EXPECT_EQ(3u, writer_->frame_count()); + } + ASSERT_EQ(1u, writer_->ping_frames().size()); +} + +TEST_P(QuicConnectionTest, NoPingIfRetransmittablePacketSent) { + const QuicTime::Delta retransmittable_on_wire_timeout = + QuicTime::Delta::FromMilliseconds(50); + connection_.set_retransmittable_on_wire_timeout( + retransmittable_on_wire_timeout); + + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + + // Advance 5ms, send a retransmittable packet to the peer. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.sent_packet_manager().HasInFlightPackets()); + // The ping alarm is set for the ping timeout, not the shorter + // retransmittable_on_wire_timeout. + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + QuicTime::Delta ping_delay = QuicTime::Delta::FromSeconds(kPingTimeoutSecs); + EXPECT_EQ((clock_.ApproximateNow() + ping_delay), + connection_.GetPingAlarm()->deadline()); + + // Now receive an ACK of the first packet. This should set the + // retransmittable-on-wire alarm now that no retransmittable packets are on + // the wire. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout, + connection_.GetPingAlarm()->deadline()); + + // Before the alarm fires, send another retransmittable packet. This should + // cancel the retransmittable-on-wire alarm since now there's a + // retransmittable packet on the wire. + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + + // Now receive an ACK of the second packet. This should set the + // retransmittable-on-wire alarm now that no retransmittable packets are on + // the wire. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout, + connection_.GetPingAlarm()->deadline()); + + // Simulate the alarm firing and check that a PING is sent. + writer_->Reset(); + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { + connection_.SendControlFrame(QuicFrame(QuicPingFrame(1))); + })); + connection_.GetPingAlarm()->Fire(); + if (GetParam().no_stop_waiting) { + EXPECT_EQ(2u, writer_->frame_count()); + } else { + EXPECT_EQ(3u, writer_->frame_count()); + } + ASSERT_EQ(1u, writer_->ping_frames().size()); +} + +TEST_P(QuicConnectionTest, OnForwardProgressConfirmed) { + EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(Exactly(0)); + EXPECT_TRUE(connection_.connected()); + + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + + // Send two packets. + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + + // Ack packet 1. This increases the largest_acked to 1, so + // OnForwardProgressConfirmed() should be called + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(visitor_, OnForwardProgressConfirmed()); + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}}); + ProcessAckPacket(&frame); + + // Ack packet 1 again. largest_acked remains at 1, so + // OnForwardProgressConfirmed() should not be called. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + frame = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}}); + ProcessAckPacket(&frame); + + // Ack packet 2. This increases the largest_acked to 2, so + // OnForwardProgressConfirmed() should be called. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(visitor_, OnForwardProgressConfirmed()); + frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); + ProcessAckPacket(&frame); +} + +TEST_P(QuicConnectionTest, ValidStatelessResetToken) { + const QuicUint128 kTestToken = 1010101; + const QuicUint128 kWrongTestToken = 1010100; + QuicConfig config; + // No token has been received. + EXPECT_FALSE(connection_.IsValidStatelessResetToken(kTestToken)); + + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(2); + // Token is different from received token. + QuicConfigPeer::SetReceivedStatelessResetToken(&config, kTestToken); + connection_.SetFromConfig(config); + EXPECT_FALSE(connection_.IsValidStatelessResetToken(kWrongTestToken)); + + QuicConfigPeer::SetReceivedStatelessResetToken(&config, kTestToken); + connection_.SetFromConfig(config); + EXPECT_TRUE(connection_.IsValidStatelessResetToken(kTestToken)); +} + +TEST_P(QuicConnectionTest, WriteBlockedWithInvalidAck) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _, _)); + + BlockOnNextWrite(); + connection_.SendStreamDataWithString(5, "foo", 0, FIN); + // This causes connection to be closed because packet 1 has not been sent yet. + QuicAckFrame frame = InitAckFrame(1); + ProcessAckPacket(1, &frame); +} + +TEST_P(QuicConnectionTest, SendMessage) { + if (connection_.transport_version() <= QUIC_VERSION_44) { + return; + } + QuicString message(connection_.GetLargestMessagePayload() * 2, 'a'); + QuicStringPiece message_data(message); + QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); + { + QuicConnection::ScopedPacketFlusher flusher(&connection_, + QuicConnection::SEND_ACK); + connection_.SendStreamData3(); + // Send a message which cannot fit into current open packet, and 2 packets + // get sent, one contains stream frame, and the other only contains the + // message frame. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + EXPECT_EQ( + MESSAGE_STATUS_SUCCESS, + connection_.SendMessage( + 1, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), + QuicStringPiece(message_data.data(), + connection_.GetLargestMessagePayload()), + &storage))); + } + // Fail to send a message if connection is congestion control blocked. + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false)); + EXPECT_EQ( + MESSAGE_STATUS_BLOCKED, + connection_.SendMessage( + 2, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), + "message", &storage))); + + // Always fail to send a message which cannot fit into one packet. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + EXPECT_EQ( + MESSAGE_STATUS_TOO_LARGE, + connection_.SendMessage( + 3, + MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), + QuicStringPiece(message_data.data(), + connection_.GetLargestMessagePayload() + 1), + &storage))); +} + +// Test to check that the path challenge/path response logic works +// correctly. This test is only for version-99 +TEST_P(QuicConnectionTest, PathChallengeResponse) { + if (connection_.version().transport_version != QUIC_VERSION_99) { + return; + } + // First check if we can probe from server to client and back + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + + // Create and send the probe request (PATH_CHALLENGE frame). + // SendConnectivityProbingPacket ends up calling + // TestPacketWriter::WritePacket() which in turns receives and parses the + // packet by calling framer_.ProcessPacket() -- which in turn calls + // SimpleQuicFramer::OnPathChallengeFrame(). SimpleQuicFramer saves + // the packet in writer_->path_challenge_frames() + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendConnectivityProbingPacket(writer_.get(), + connection_.peer_address()); + // Save the random contents of the challenge for later comparison to the + // response. + QuicPathFrameBuffer challenge_data = + writer_->path_challenge_frames().front().data_buffer; + + // Normally, QuicConnection::OnPathChallengeFrame and OnPaddingFrame would be + // called and it will perform actions to ensure that the rest of the protocol + // is performed (specifically, call UpdatePacketContent to say that this is a + // path challenge so that when QuicConnection::OnPacketComplete is called + // (again, out of the framer), the response is generated). Simulate those + // calls so that the right internal state is set up for generating + // the response. + EXPECT_TRUE(connection_.OnPathChallengeFrame( + writer_->path_challenge_frames().front())); + EXPECT_TRUE(connection_.OnPaddingFrame(writer_->padding_frames().front())); + // Cause the response to be created and sent. Result is that the response + // should be stashed in writer's path_response_frames. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendConnectivityProbingResponsePacket(connection_.peer_address()); + + // The final check is to ensure that the random data in the response matches + // the random data from the challenge. + EXPECT_EQ(0, memcmp(&challenge_data, + &(writer_->path_response_frames().front().data_buffer), + sizeof(challenge_data))); +} + +// Regression test for b/110259444 +TEST_P(QuicConnectionTest, DoNotScheduleSpuriousAckAlarm) { + SetQuicReloadableFlag(quic_fix_spurious_ack_alarm, true); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); + writer_->SetWriteBlocked(); + + ProcessPacket(1); + QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); + // Verify ack alarm is set. + EXPECT_TRUE(ack_alarm->IsSet()); + // Fire the ack alarm, verify no packet is sent because the writer is blocked. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + connection_.GetAckAlarm()->Fire(); + + writer_->SetWritable(); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + ProcessPacket(2); + // Verify ack alarm is not set. + EXPECT_FALSE(ack_alarm->IsSet()); +} + +TEST_P(QuicConnectionTest, DisablePacingOffloadConnectionOptions) { + EXPECT_FALSE(QuicConnectionPeer::SupportsReleaseTime(&connection_)); + writer_->set_supports_release_time(true); + QuicConfig config; + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + EXPECT_TRUE(QuicConnectionPeer::SupportsReleaseTime(&connection_)); + + QuicTagVector connection_options; + connection_options.push_back(kNPCO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + // Verify pacing offload is disabled. + EXPECT_FALSE(QuicConnectionPeer::SupportsReleaseTime(&connection_)); +} + +// Regression test for b/110259444 +// Get a path response without having issued a path challenge... +TEST_P(QuicConnectionTest, OrphanPathResponse) { + QuicPathFrameBuffer data = {{0, 1, 2, 3, 4, 5, 6, 7}}; + + QuicPathResponseFrame frame(99, data); + EXPECT_TRUE(connection_.OnPathResponseFrame(frame)); + // If PATH_RESPONSE was accepted (payload matches the payload saved + // in QuicConnection::transmitted_connectivity_probe_payload_) then + // current_packet_content_ would be set to FIRST_FRAME_IS_PING. + // Since this PATH_RESPONSE does not match, current_packet_content_ + // must not be FIRST_FRAME_IS_PING. + EXPECT_NE(QuicConnection::FIRST_FRAME_IS_PING, + QuicConnectionPeer::GetCurrentPacketContent(&connection_)); +} + +// Regression test for b/120791670 +TEST_P(QuicConnectionTest, StopProcessingGQuicPacketInIetfQuicConnection) { + // This test mimics a problematic scenario where an IETF QUIC connection + // receives a Google QUIC packet and continue processing it using Google QUIC + // wire format. + if (version().transport_version <= QUIC_VERSION_43) { + return; + } + set_perspective(Perspective::IS_SERVER); + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u, + QuicStringPiece()); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress, + kPeerAddress); + + // Let connection process a Google QUIC packet. + peer_framer_.set_version_for_tests( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43)); + std::unique_ptr<QuicPacket> packet(ConstructDataPacket(2, !kHasStopWaiting)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + ENCRYPTION_NONE, QuicPacketNumber(2), *packet, buffer, kMaxPacketSize); + // Make sure no stream frame is processed. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(0); + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); + + EXPECT_EQ(2u, connection_.GetStats().packets_received); + EXPECT_EQ(1u, connection_.GetStats().packets_processed); +} + +TEST_P(QuicConnectionTest, AcceptPacketNumberZero) { + if (!GetQuicRestartFlag(quic_uint64max_uninitialized_pn) || + version().transport_version != QUIC_VERSION_99) { + return; + } + // Set first_sending_packet_number to be 0 to allow successfully processing + // acks which ack packet number 0. + QuicFramerPeer::SetFirstSendingPacketNumber(writer_->framer()->framer(), 0); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + ProcessPacket(0); + EXPECT_EQ(QuicPacketNumber(0), LargestAcked(*outgoing_ack())); + EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); + + ProcessPacket(1); + EXPECT_EQ(QuicPacketNumber(1), LargestAcked(*outgoing_ack())); + EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); + + ProcessPacket(2); + EXPECT_EQ(QuicPacketNumber(2), LargestAcked(*outgoing_ack())); + EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_constants.cc b/quic/core/quic_constants.cc new file mode 100644 index 0000000..8e46f88 --- /dev/null +++ b/quic/core/quic_constants.cc
@@ -0,0 +1,25 @@ +// 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/quic_constants.h" + +namespace quic { + +const char* const kFinalOffsetHeaderKey = ":final-offset"; + +const char* const kEPIDGoogleFrontEnd = "GFE"; +const char* const kEPIDGoogleFrontEnd0 = "GFE0"; + +QuicPacketNumber MaxRandomInitialPacketNumber() { + static const QuicPacketNumber kMaxRandomInitialPacketNumber = + QuicPacketNumber(0x7fffffff); + return kMaxRandomInitialPacketNumber; +} + +QuicPacketNumber FirstSendingPacketNumber() { + static const QuicPacketNumber kFirstSendingPacketNumber = QuicPacketNumber(1); + return kFirstSendingPacketNumber; +} + +} // namespace quic
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h new file mode 100644 index 0000000..5ae88b5 --- /dev/null +++ b/quic/core/quic_constants.h
@@ -0,0 +1,234 @@ +// Copyright (c) 2012 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_QUIC_CONSTANTS_H_ +#define QUICHE_QUIC_CORE_QUIC_CONSTANTS_H_ + +#include <stddef.h> + +#include <cstdint> +#include <limits> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +// Definitions of constant values used throughout the QUIC code. + +namespace quic { + +// Simple time constants. +const uint64_t kNumSecondsPerMinute = 60; +const uint64_t kNumSecondsPerHour = kNumSecondsPerMinute * 60; +const uint64_t kNumSecondsPerWeek = kNumSecondsPerHour * 24 * 7; +const uint64_t kNumMicrosPerMilli = 1000; +const uint64_t kNumMicrosPerSecond = 1000 * 1000; + +// Default number of connections for N-connection emulation. +const uint32_t kDefaultNumConnections = 2; +// Default initial maximum size in bytes of a QUIC packet. +const QuicByteCount kDefaultMaxPacketSize = 1350; +// Default initial maximum size in bytes of a QUIC packet for servers. +const QuicByteCount kDefaultServerMaxPacketSize = 1000; +// Maximum transmission unit on Ethernet. +const QuicByteCount kEthernetMTU = 1500; +// The maximum packet size of any QUIC packet, based on ethernet's max size, +// minus the IP and UDP headers. IPv6 has a 40 byte header, UDP adds an +// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's +// max packet size is 1500 bytes, 1500 - 48 = 1452. +const QuicByteCount kMaxPacketSize = 1452; +// The maximum packet size of any QUIC packet over IPv4. +// 1500(Ethernet) - 20(IPv4 header) - 8(UDP header) = 1472. +const QuicByteCount kMaxV4PacketSize = 1472; +// ETH_MAX_MTU - MAX(sizeof(iphdr), sizeof(ip6_hdr)) - sizeof(udphdr). +const QuicByteCount kMaxGsoPacketSize = 65535 - 40 - 8; +// Default maximum packet size used in the Linux TCP implementation. +// Used in QUIC for congestion window computations in bytes. +const QuicByteCount kDefaultTCPMSS = 1460; +const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS; +// The minimum size of a packet which can elicit a version negotiation packet, +// as per section 8.1 of the QUIC spec. +const QuicByteCount kMinPacketSizeForVersionNegotiation = 1200; + +// We match SPDY's use of 32 (since we'd compete with SPDY). +const QuicPacketCount kInitialCongestionWindow = 32; + +// Minimum size of initial flow control window, for both stream and session. +const uint32_t kMinimumFlowControlSendWindow = 16 * 1024; // 16 KB + +// Maximum flow control receive window limits for connection and stream. +const QuicByteCount kStreamReceiveWindowLimit = 16 * 1024 * 1024; // 16 MB +const QuicByteCount kSessionReceiveWindowLimit = 24 * 1024 * 1024; // 24 MB + +// Default limit on the size of uncompressed headers. +const QuicByteCount kDefaultMaxUncompressedHeaderSize = 16 * 1024; // 16 KB + +// Minimum size of the CWND, in packets, when doing bandwidth resumption. +const QuicPacketCount kMinCongestionWindowForBandwidthResumption = 10; + +// Maximum number of tracked packets. +const QuicPacketCount kMaxTrackedPackets = 10000; + +// Default size of the socket receive buffer in bytes. +const QuicByteCount kDefaultSocketReceiveBuffer = 1024 * 1024; + +// Don't allow a client to suggest an RTT shorter than 10ms. +const uint32_t kMinInitialRoundTripTimeUs = 10 * kNumMicrosPerMilli; + +// Don't allow a client to suggest an RTT longer than 15 seconds. +const uint32_t kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond; + +// Maximum number of open streams per connection. +const size_t kDefaultMaxStreamsPerConnection = 100; + +// Number of bytes reserved for public flags in the packet header. +const size_t kPublicFlagsSize = 1; +// Number of bytes reserved for version number in the packet header. +const size_t kQuicVersionSize = 4; + +// Signifies that the QuicPacket will contain version of the protocol. +const bool kIncludeVersion = true; +// Signifies that the QuicPacket will include a diversification nonce. +const bool kIncludeDiversificationNonce = true; + +// Header key used to identify final offset on data stream when sending HTTP/2 +// trailing headers over QUIC. +QUIC_EXPORT_PRIVATE extern const char* const kFinalOffsetHeaderKey; + +// Default maximum delayed ack time, in ms. +// Uses a 25ms delayed ack timer. Helps with better signaling +// in low-bandwidth (< ~384 kbps), where an ack is sent per packet. +const int64_t kDefaultDelayedAckTimeMs = 25; + +// Minimum tail loss probe time in ms. +static const int64_t kMinTailLossProbeTimeoutMs = 10; + +// The timeout before the handshake succeeds. +const int64_t kInitialIdleTimeoutSecs = 5; +// The default idle timeout. +const int64_t kDefaultIdleTimeoutSecs = 30; +// The maximum idle timeout that can be negotiated. +const int64_t kMaximumIdleTimeoutSecs = 60 * 10; // 10 minutes. +// The default timeout for a connection until the crypto handshake succeeds. +const int64_t kMaxTimeForCryptoHandshakeSecs = 10; // 10 secs. + +// Default limit on the number of undecryptable packets the connection buffers +// before the CHLO/SHLO arrive. +const size_t kDefaultMaxUndecryptablePackets = 10; + +// Default ping timeout. +const int64_t kPingTimeoutSecs = 15; // 15 secs. + +// Minimum number of RTTs between Server Config Updates (SCUP) sent to client. +const int kMinIntervalBetweenServerConfigUpdatesRTTs = 10; + +// Minimum time between Server Config Updates (SCUP) sent to client. +const int kMinIntervalBetweenServerConfigUpdatesMs = 1000; + +// Minimum number of packets between Server Config Updates (SCUP). +const int kMinPacketsBetweenServerConfigUpdates = 100; + +// The number of open streams that a server will accept is set to be slightly +// larger than the negotiated limit. Immediately closing the connection if the +// client opens slightly too many streams is not ideal: the client may have sent +// a FIN that was lost, and simultaneously opened a new stream. The number of +// streams a server accepts is a fixed increment over the negotiated limit, or a +// percentage increase, whichever is larger. +const float kMaxStreamsMultiplier = 1.1f; +const int kMaxStreamsMinimumIncrement = 10; + +// Available streams are ones with IDs less than the highest stream that has +// been opened which have neither been opened or reset. The limit on the number +// of available streams is 10 times the limit on the number of open streams. +const int kMaxAvailableStreamsMultiplier = 10; + +// Track the number of promises that are not yet claimed by a +// corresponding get. This must be smaller than +// kMaxAvailableStreamsMultiplier, because RST on a promised stream my +// create available streams entries. +const int kMaxPromisedStreamsMultiplier = kMaxAvailableStreamsMultiplier - 1; + +// TCP RFC calls for 1 second RTO however Linux differs from this default and +// define the minimum RTO to 200ms, we will use the same until we have data to +// support a higher or lower value. +static const int64_t kMinRetransmissionTimeMs = 200; +// The delayed ack time must not be greater than half the min RTO. +static_assert(kDefaultDelayedAckTimeMs <= kMinRetransmissionTimeMs / 2, + "Delayed ack time must be less than or equal half the MinRTO"); + +// We define an unsigned 16-bit floating point value, inspired by IEEE floats +// (http://en.wikipedia.org/wiki/Half_precision_floating-point_format), +// with 5-bit exponent (bias 1), 11-bit mantissa (effective 12 with hidden +// bit) and denormals, but without signs, transfinites or fractions. Wire format +// 16 bits (little-endian byte order) are split into exponent (high 5) and +// mantissa (low 11) and decoded as: +// uint64_t value; +// if (exponent == 0) value = mantissa; +// else value = (mantissa | 1 << 11) << (exponent - 1) +const int kUFloat16ExponentBits = 5; +const int kUFloat16MaxExponent = (1 << kUFloat16ExponentBits) - 2; // 30 +const int kUFloat16MantissaBits = 16 - kUFloat16ExponentBits; // 11 +const int kUFloat16MantissaEffectiveBits = kUFloat16MantissaBits + 1; // 12 +const uint64_t kUFloat16MaxValue = // 0x3FFC0000000 + ((UINT64_C(1) << kUFloat16MantissaEffectiveBits) - 1) + << kUFloat16MaxExponent; + +// kDiversificationNonceSize is the size, in bytes, of the nonce that a server +// may set in the packet header to ensure that its INITIAL keys are not +// duplicated. +const size_t kDiversificationNonceSize = 32; + +// The largest gap in packets we'll accept without closing the connection. +// This will likely have to be tuned. +const QuicPacketCount kMaxPacketGap = 5000; + +// The maximum number of random padding bytes to add. +const QuicByteCount kMaxNumRandomPaddingBytes = 256; + +// The size of stream send buffer data slice size in bytes. A data slice is +// piece of stream data stored in contiguous memory, and a stream frame can +// contain data from multiple data slices. +const QuicByteCount kQuicStreamSendBufferSliceSize = 4 * 1024; + +// For When using Random Initial Packet Numbers, they can start +// anyplace in the range 1...((2^31)-1) or 0x7fffffff +QUIC_EXPORT_PRIVATE QuicPacketNumber MaxRandomInitialPacketNumber(); + +// Used to represent an invalid or no control frame id. +const QuicControlFrameId kInvalidControlFrameId = 0; + +// The max length a stream can have. +const QuicByteCount kMaxStreamLength = (UINT64_C(1) << 62) - 1; + +// The max value that can be encoded using IETF Var Ints. +const uint64_t kMaxIetfVarInt = UINT64_C(0x3fffffffffffffff); + +// The maximum stream id value that is supported - (2^32)-1 +// TODO(fkastenholz): Should update this to 64 bits for IETF Quic. +const QuicStreamId kMaxQuicStreamId = 0xffffffff; + +// Number of bytes reserved for packet header type. +const size_t kPacketHeaderTypeSize = 1; + +// Number of bytes reserved for connection ID length. +const size_t kConnectionIdLengthSize = 1; + +// Minimum length of random bytes in IETF stateless reset packet. +const size_t kMinRandomBytesLengthInStatelessReset = 24; + +// Maximum length allowed for the token in a NEW_TOKEN frame. +const size_t kMaxNewTokenTokenLength = 0xffff; + +// Packet number of first sending packet of a connection. Please note, this +// cannot be used as first received packet because peer can choose its starting +// packet number. +QUIC_EXPORT_PRIVATE QuicPacketNumber FirstSendingPacketNumber(); + +// Used by clients to tell if a public reset is sent from a Google frontend. +QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd; +QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd0; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CONSTANTS_H_
diff --git a/quic/core/quic_control_frame_manager.cc b/quic/core/quic_control_frame_manager.cc new file mode 100644 index 0000000..8d4ba53 --- /dev/null +++ b/quic/core/quic_control_frame_manager.cc
@@ -0,0 +1,311 @@ +// Copyright (c) 2017 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/quic_control_frame_manager.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicControlFrameManager::QuicControlFrameManager(QuicSession* session) + : last_control_frame_id_(kInvalidControlFrameId), + least_unacked_(1), + least_unsent_(1), + session_(session) {} + +QuicControlFrameManager::~QuicControlFrameManager() { + while (!control_frames_.empty()) { + DeleteFrame(&control_frames_.front()); + control_frames_.pop_front(); + } +} + +void QuicControlFrameManager::WriteOrBufferQuicFrame(QuicFrame frame) { + const bool had_buffered_frames = HasBufferedFrames(); + control_frames_.emplace_back(frame); + if (had_buffered_frames) { + return; + } + WriteBufferedFrames(); +} + +void QuicControlFrameManager::WriteOrBufferRstStream( + QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) { + QUIC_DVLOG(1) << "Writing RST_STREAM_FRAME"; + WriteOrBufferQuicFrame((QuicFrame(new QuicRstStreamFrame( + ++last_control_frame_id_, id, error, bytes_written)))); +} + +void QuicControlFrameManager::WriteOrBufferGoAway( + QuicErrorCode error, + QuicStreamId last_good_stream_id, + const QuicString& reason) { + QUIC_DVLOG(1) << "Writing GOAWAY_FRAME"; + WriteOrBufferQuicFrame(QuicFrame(new QuicGoAwayFrame( + ++last_control_frame_id_, error, last_good_stream_id, reason))); +} + +void QuicControlFrameManager::WriteOrBufferWindowUpdate( + QuicStreamId id, + QuicStreamOffset byte_offset) { + QUIC_DVLOG(1) << "Writing WINDOW_UPDATE_FRAME"; + WriteOrBufferQuicFrame(QuicFrame( + new QuicWindowUpdateFrame(++last_control_frame_id_, id, byte_offset))); +} + +void QuicControlFrameManager::WriteOrBufferBlocked(QuicStreamId id) { + QUIC_DVLOG(1) << "Writing BLOCKED_FRAME"; + WriteOrBufferQuicFrame( + QuicFrame(new QuicBlockedFrame(++last_control_frame_id_, id))); +} + +void QuicControlFrameManager::WriteOrBufferStreamIdBlocked(QuicStreamId id) { + QUIC_DVLOG(1) << "Writing STREAM_ID_BLOCKED Frame"; + QUIC_CODE_COUNT(stream_id_blocked_transmits); + WriteOrBufferQuicFrame( + QuicFrame(QuicStreamIdBlockedFrame(++last_control_frame_id_, id))); +} + +void QuicControlFrameManager::WriteOrBufferMaxStreamId(QuicStreamId id) { + QUIC_DVLOG(1) << "Writing MAX_STREAM_ID Frame"; + QUIC_CODE_COUNT(max_stream_id_transmits); + WriteOrBufferQuicFrame( + QuicFrame(QuicMaxStreamIdFrame(++last_control_frame_id_, id))); +} + +void QuicControlFrameManager::WriteOrBufferStopSending(uint16_t code, + QuicStreamId stream_id) { + QUIC_DVLOG(1) << "Writing STOP_SENDING_FRAME"; + WriteOrBufferQuicFrame(QuicFrame( + new QuicStopSendingFrame(++last_control_frame_id_, stream_id, code))); +} + +void QuicControlFrameManager::WritePing() { + QUIC_DVLOG(1) << "Writing PING_FRAME"; + if (HasBufferedFrames()) { + // Do not send ping if there is buffered frames. + QUIC_LOG(WARNING) + << "Try to send PING when there is buffered control frames."; + return; + } + control_frames_.emplace_back( + QuicFrame(QuicPingFrame(++last_control_frame_id_))); + WriteBufferedFrames(); +} + +void QuicControlFrameManager::OnControlFrameSent(const QuicFrame& frame) { + QuicControlFrameId id = GetControlFrameId(frame); + if (id == kInvalidControlFrameId) { + QUIC_BUG + << "Send or retransmit a control frame with invalid control frame id"; + return; + } + if (frame.type == WINDOW_UPDATE_FRAME) { + QuicStreamId stream_id = frame.window_update_frame->stream_id; + if (QuicContainsKey(window_update_frames_, stream_id) && + id > window_update_frames_[stream_id]) { + // Consider the older window update of the same stream as acked. + OnControlFrameIdAcked(window_update_frames_[stream_id]); + } + window_update_frames_[stream_id] = id; + } + if (QuicContainsKey(pending_retransmissions_, id)) { + // This is retransmitted control frame. + pending_retransmissions_.erase(id); + return; + } + if (id > least_unsent_) { + QUIC_BUG << "Try to send control frames out of order, id: " << id + << " least_unsent: " << least_unsent_; + session_->connection()->CloseConnection( + QUIC_INTERNAL_ERROR, "Try to send control frames out of order", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + ++least_unsent_; +} + +bool QuicControlFrameManager::OnControlFrameAcked(const QuicFrame& frame) { + QuicControlFrameId id = GetControlFrameId(frame); + if (!OnControlFrameIdAcked(id)) { + return false; + } + if (frame.type == WINDOW_UPDATE_FRAME) { + QuicStreamId stream_id = frame.window_update_frame->stream_id; + if (QuicContainsKey(window_update_frames_, stream_id) && + window_update_frames_[stream_id] == id) { + window_update_frames_.erase(stream_id); + } + } + return true; +} + +void QuicControlFrameManager::OnControlFrameLost(const QuicFrame& frame) { + QuicControlFrameId id = GetControlFrameId(frame); + if (id == kInvalidControlFrameId) { + // Frame does not have a valid control frame ID, ignore it. + return; + } + if (id >= least_unsent_) { + QUIC_BUG << "Try to mark unsent control frame as lost"; + session_->connection()->CloseConnection( + QUIC_INTERNAL_ERROR, "Try to mark unsent control frame as lost", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + if (id < least_unacked_ || + GetControlFrameId(control_frames_.at(id - least_unacked_)) == + kInvalidControlFrameId) { + // This frame has already been acked. + return; + } + if (!QuicContainsKey(pending_retransmissions_, id)) { + pending_retransmissions_[id] = true; + } +} + +bool QuicControlFrameManager::IsControlFrameOutstanding( + const QuicFrame& frame) const { + QuicControlFrameId id = GetControlFrameId(frame); + if (id == kInvalidControlFrameId) { + // Frame without a control frame ID should not be retransmitted. + return false; + } + // Consider this frame is outstanding if it does not get acked. + return id < least_unacked_ + control_frames_.size() && id >= least_unacked_ && + GetControlFrameId(control_frames_.at(id - least_unacked_)) != + kInvalidControlFrameId; +} + +bool QuicControlFrameManager::HasPendingRetransmission() const { + return !pending_retransmissions_.empty(); +} + +bool QuicControlFrameManager::WillingToWrite() const { + return HasPendingRetransmission() || HasBufferedFrames(); +} + +QuicFrame QuicControlFrameManager::NextPendingRetransmission() const { + QUIC_BUG_IF(pending_retransmissions_.empty()) + << "Unexpected call to NextPendingRetransmission() with empty pending " + << "retransmission list."; + QuicControlFrameId id = pending_retransmissions_.begin()->first; + return control_frames_.at(id - least_unacked_); +} + +void QuicControlFrameManager::OnCanWrite() { + if (HasPendingRetransmission()) { + // Exit early to allow streams to write pending retransmissions if any. + WritePendingRetransmission(); + return; + } + WriteBufferedFrames(); +} + +bool QuicControlFrameManager::RetransmitControlFrame(const QuicFrame& frame) { + QuicControlFrameId id = GetControlFrameId(frame); + if (id == kInvalidControlFrameId) { + // Frame does not have a valid control frame ID, ignore it. Returns true + // to allow writing following frames. + return true; + } + if (id >= least_unsent_) { + QUIC_BUG << "Try to retransmit unsent control frame"; + session_->connection()->CloseConnection( + QUIC_INTERNAL_ERROR, "Try to retransmit unsent control frame", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + if (id < least_unacked_ || + GetControlFrameId(control_frames_.at(id - least_unacked_)) == + kInvalidControlFrameId) { + // This frame has already been acked. + return true; + } + QuicFrame copy = CopyRetransmittableControlFrame(frame); + QUIC_DVLOG(1) << "control frame manager is forced to retransmit frame: " + << frame; + if (session_->WriteControlFrame(copy)) { + return true; + } + DeleteFrame(©); + return false; +} + +void QuicControlFrameManager::WriteBufferedFrames() { + while (HasBufferedFrames()) { + if (session_->session_decides_what_to_write()) { + session_->SetTransmissionType(NOT_RETRANSMISSION); + } + QuicFrame frame_to_send = + control_frames_.at(least_unsent_ - least_unacked_); + QuicFrame copy = CopyRetransmittableControlFrame(frame_to_send); + if (!session_->WriteControlFrame(copy)) { + // Connection is write blocked. + DeleteFrame(©); + break; + } + OnControlFrameSent(frame_to_send); + } +} + +void QuicControlFrameManager::WritePendingRetransmission() { + while (HasPendingRetransmission()) { + QuicFrame pending = NextPendingRetransmission(); + QuicFrame copy = CopyRetransmittableControlFrame(pending); + if (!session_->WriteControlFrame(copy)) { + // Connection is write blocked. + DeleteFrame(©); + break; + } + OnControlFrameSent(pending); + } +} + +bool QuicControlFrameManager::OnControlFrameIdAcked(QuicControlFrameId id) { + if (id == kInvalidControlFrameId) { + // Frame does not have a valid control frame ID, ignore it. + return false; + } + if (id >= least_unsent_) { + QUIC_BUG << "Try to ack unsent control frame"; + session_->connection()->CloseConnection( + QUIC_INTERNAL_ERROR, "Try to ack unsent control frame", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + if (id < least_unacked_ || + GetControlFrameId(control_frames_.at(id - least_unacked_)) == + kInvalidControlFrameId) { + // This frame has already been acked. + return false; + } + + // Set control frame ID of acked frames to 0. + SetControlFrameId(kInvalidControlFrameId, + &control_frames_.at(id - least_unacked_)); + // Remove acked control frames from pending retransmissions. + pending_retransmissions_.erase(id); + // Clean up control frames queue and increment least_unacked_. + while (!control_frames_.empty() && + GetControlFrameId(control_frames_.front()) == kInvalidControlFrameId) { + DeleteFrame(&control_frames_.front()); + control_frames_.pop_front(); + ++least_unacked_; + } + return true; +} + +bool QuicControlFrameManager::HasBufferedFrames() const { + return least_unsent_ < least_unacked_ + control_frames_.size(); +} + +} // namespace quic
diff --git a/quic/core/quic_control_frame_manager.h b/quic/core/quic_control_frame_manager.h new file mode 100644 index 0000000..618f3f9 --- /dev/null +++ b/quic/core/quic_control_frame_manager.h
@@ -0,0 +1,152 @@ +// Copyright (c) 2017 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_QUIC_CONTROL_FRAME_MANAGER_H_ +#define QUICHE_QUIC_CORE_QUIC_CONTROL_FRAME_MANAGER_H_ + +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class QuicSession; + +namespace test { +class QuicControlFrameManagerPeer; +} // namespace test + +// Control frame manager contains a list of sent control frames with valid +// control frame IDs. Control frames without valid control frame IDs include: +// (1) non-retransmittable frames (e.g., ACK_FRAME, PADDING_FRAME, +// STOP_WAITING_FRAME, etc.), (2) CONNECTION_CLOSE and IETF Quic +// APPLICATION_CLOSE frames. +// New control frames are added to the tail of the list when they are added to +// the generator. Control frames are removed from the head of the list when they +// get acked. Control frame manager also keeps track of lost control frames +// which need to be retransmitted. +class QUIC_EXPORT_PRIVATE QuicControlFrameManager { + public: + explicit QuicControlFrameManager(QuicSession* session); + QuicControlFrameManager(const QuicControlFrameManager& other) = delete; + QuicControlFrameManager(QuicControlFrameManager&& other) = delete; + ~QuicControlFrameManager(); + + // Tries to send a WINDOW_UPDATE_FRAME. Buffers the frame if it cannot be sent + // immediately. + void WriteOrBufferRstStream(QuicControlFrameId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written); + + // Tries to send a GOAWAY_FRAME. Buffers the frame if it cannot be sent + // immediately. + void WriteOrBufferGoAway(QuicErrorCode error, + QuicStreamId last_good_stream_id, + const QuicString& reason); + + // Tries to send a WINDOW_UPDATE_FRAME. Buffers the frame if it cannot be sent + // immediately. + void WriteOrBufferWindowUpdate(QuicStreamId id, QuicStreamOffset byte_offset); + + // Tries to send a BLOCKED_FRAME. Buffers the frame if it cannot be sent + // immediately. + void WriteOrBufferBlocked(QuicStreamId id); + + // Tries to send a STREAM_ID_BLOCKED Frame. Buffers the frame if it cannot be + // sent immediately. + void WriteOrBufferStreamIdBlocked(QuicStreamId id); + + // Tries to send a MAX_STREAM_ID Frame. Buffers the frame if it cannot be sent + // immediately. + void WriteOrBufferMaxStreamId(QuicStreamId id); + + // Tries to send an IETF-QUIC STOP_SENDING frame. The frame is buffered if it + // can not be sent immediately. + void WriteOrBufferStopSending(uint16_t code, QuicStreamId stream_id); + + // Sends a PING_FRAME. Do not send PING if there is buffered frames. + void WritePing(); + + // Called when |frame| gets acked. Returns true if |frame| gets acked for the + // first time, return false otherwise. + bool OnControlFrameAcked(const QuicFrame& frame); + + // Called when |frame| is considered as lost. + void OnControlFrameLost(const QuicFrame& frame); + + // Called by the session when the connection becomes writable. + void OnCanWrite(); + + // Retransmit |frame| if it is still outstanding. Returns false if the frame + // does not get retransmitted because the connection is blocked. Otherwise, + // returns true. + bool RetransmitControlFrame(const QuicFrame& frame); + + // Returns true if |frame| is outstanding and waiting to be acked. Returns + // false otherwise. + bool IsControlFrameOutstanding(const QuicFrame& frame) const; + + // Returns true if there is any lost control frames waiting to be + // retransmitted. + bool HasPendingRetransmission() const; + + // Returns true if there are any lost or new control frames waiting to be + // sent. + bool WillingToWrite() const; + + private: + friend class test::QuicControlFrameManagerPeer; + + // Tries to write buffered control frames to the peer. + void WriteBufferedFrames(); + + // Called when |frame| is sent for the first time or gets retransmitted. + void OnControlFrameSent(const QuicFrame& frame); + + // Writes pending retransmissions if any. + void WritePendingRetransmission(); + + // Called when frame with |id| gets acked. Returns true if |id| gets acked for + // the first time, return false otherwise. + bool OnControlFrameIdAcked(QuicControlFrameId id); + + // Retrieves the next pending retransmission. This must only be called when + // there are pending retransmissions. + QuicFrame NextPendingRetransmission() const; + + // Returns true if there are buffered frames waiting to be sent for the first + // time. + bool HasBufferedFrames() const; + + // Writes or buffers a control frame. Frame is buffered if there already + // are frames waiting to be sent. If no others waiting, will try to send the + // frame. + void WriteOrBufferQuicFrame(QuicFrame frame); + + QuicDeque<QuicFrame> control_frames_; + + // Id of latest saved control frame. 0 if no control frame has been saved. + QuicControlFrameId last_control_frame_id_; + + // The control frame at the 0th index of control_frames_. + QuicControlFrameId least_unacked_; + + // ID of the least unsent control frame. + QuicControlFrameId least_unsent_; + + // TODO(fayang): switch to linked_hash_set when chromium supports it. The bool + // is not used here. + // Lost control frames waiting to be retransmitted. + QuicLinkedHashMap<QuicControlFrameId, bool> pending_retransmissions_; + + // Pointer to the owning QuicSession object. + QuicSession* session_; + + // Last sent window update frame for each stream. + QuicSmallMap<QuicStreamId, QuicControlFrameId, 10> window_update_frames_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CONTROL_FRAME_MANAGER_H_
diff --git a/quic/core/quic_control_frame_manager_test.cc b/quic/core/quic_control_frame_manager_test.cc new file mode 100644 index 0000000..ccddadd --- /dev/null +++ b/quic/core/quic_control_frame_manager_test.cc
@@ -0,0 +1,306 @@ +// Copyright (c) 2017 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/quic_control_frame_manager.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::InSequence; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { + +class QuicControlFrameManagerPeer { + public: + static size_t QueueSize(QuicControlFrameManager* manager) { + return manager->control_frames_.size(); + } +}; + +namespace { + +const QuicStreamId kTestStreamId = 5; +const QuicStreamId kTestStopSendingCode = 321; + +class QuicControlFrameManagerTest : public QuicTest { + public: + bool ClearControlFrame(const QuicFrame& frame) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + bool SaveControlFrame(const QuicFrame& frame) { + frame_ = frame; + return true; + } + + protected: + // Pre-fills the control frame queue with the following frames: + // ID Type + // 1 RST_STREAM + // 2 GO_AWAY + // 3 WINDOW_UPDATE + // 4 BLOCKED + // 5 STOP_SENDING + // This is verified. The tests then perform manipulations on these. + void Initialize() { + connection_ = new MockQuicConnection(&helper_, &alarm_factory_, + Perspective::IS_SERVER); + session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_); + manager_ = QuicMakeUnique<QuicControlFrameManager>(session_.get()); + EXPECT_EQ(0u, QuicControlFrameManagerPeer::QueueSize(manager_.get())); + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_FALSE(manager_->WillingToWrite()); + + EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false)); + manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0); + manager_->WriteOrBufferGoAway(QUIC_PEER_GOING_AWAY, kTestStreamId, + "Going away."); + manager_->WriteOrBufferWindowUpdate(kTestStreamId, 100); + manager_->WriteOrBufferBlocked(kTestStreamId); + manager_->WriteOrBufferStopSending(kTestStopSendingCode, kTestStreamId); + number_of_frames_ = 5u; + ping_frame_id_ = 6u; + EXPECT_EQ(number_of_frames_, + QuicControlFrameManagerPeer::QueueSize(manager_.get())); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&rst_stream_))); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&goaway_))); + EXPECT_TRUE( + manager_->IsControlFrameOutstanding(QuicFrame(&window_update_))); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&blocked_))); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&stop_sending_))); + EXPECT_FALSE(manager_->IsControlFrameOutstanding( + QuicFrame(QuicPingFrame(ping_frame_id_)))); + + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_TRUE(manager_->WillingToWrite()); + } + + QuicRstStreamFrame rst_stream_ = {1, kTestStreamId, QUIC_STREAM_CANCELLED, 0}; + QuicGoAwayFrame goaway_ = {2, QUIC_PEER_GOING_AWAY, kTestStreamId, + "Going away."}; + QuicWindowUpdateFrame window_update_ = {3, kTestStreamId, 100}; + QuicBlockedFrame blocked_ = {4, kTestStreamId}; + QuicStopSendingFrame stop_sending_ = {5, kTestStreamId, kTestStopSendingCode}; + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* connection_; + std::unique_ptr<StrictMock<MockQuicSession>> session_; + std::unique_ptr<QuicControlFrameManager> manager_; + QuicFrame frame_; + size_t number_of_frames_; + int ping_frame_id_; +}; + +TEST_F(QuicControlFrameManagerTest, OnControlFrameAcked) { + Initialize(); + InSequence s; + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(3) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false)); + // Send control frames 1, 2, 3. + manager_->OnCanWrite(); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&rst_stream_))); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&goaway_))); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&window_update_))); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&blocked_))); + EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&stop_sending_))); + + EXPECT_FALSE(manager_->IsControlFrameOutstanding( + QuicFrame(QuicPingFrame(ping_frame_id_)))); + EXPECT_TRUE(manager_->OnControlFrameAcked(QuicFrame(&window_update_))); + EXPECT_FALSE(manager_->IsControlFrameOutstanding(QuicFrame(&window_update_))); + EXPECT_EQ(number_of_frames_, + QuicControlFrameManagerPeer::QueueSize(manager_.get())); + + EXPECT_TRUE(manager_->OnControlFrameAcked(QuicFrame(&goaway_))); + EXPECT_FALSE(manager_->IsControlFrameOutstanding(QuicFrame(&goaway_))); + EXPECT_EQ(number_of_frames_, + QuicControlFrameManagerPeer::QueueSize(manager_.get())); + EXPECT_TRUE(manager_->OnControlFrameAcked(QuicFrame(&rst_stream_))); + EXPECT_FALSE(manager_->IsControlFrameOutstanding(QuicFrame(&rst_stream_))); + // Only after the first frame in the queue is acked do the frames get + // removed ... now see that the length has been reduced by 3. + EXPECT_EQ(number_of_frames_ - 3u, + QuicControlFrameManagerPeer::QueueSize(manager_.get())); + // Duplicate ack. + EXPECT_FALSE(manager_->OnControlFrameAcked(QuicFrame(&goaway_))); + + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_TRUE(manager_->WillingToWrite()); + + // Send control frames 4, 5. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + manager_->WritePing(); + EXPECT_FALSE(manager_->WillingToWrite()); +} + +TEST_F(QuicControlFrameManagerTest, OnControlFrameLost) { + Initialize(); + InSequence s; + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(3) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false)); + // Send control frames 1, 2, 3. + manager_->OnCanWrite(); + + // Lost control frames 1, 2, 3. + manager_->OnControlFrameLost(QuicFrame(&rst_stream_)); + manager_->OnControlFrameLost(QuicFrame(&goaway_)); + manager_->OnControlFrameLost(QuicFrame(&window_update_)); + EXPECT_TRUE(manager_->HasPendingRetransmission()); + + // Ack control frame 2. + manager_->OnControlFrameAcked(QuicFrame(&goaway_)); + + // Retransmit control frames 1, 3. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_TRUE(manager_->WillingToWrite()); + + // Send control frames 4, 5, and 6. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(number_of_frames_ - 2u) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + manager_->WritePing(); + EXPECT_FALSE(manager_->WillingToWrite()); +} + +TEST_F(QuicControlFrameManagerTest, RetransmitControlFrame) { + Initialize(); + InSequence s; + // Send control frames 1, 2, 3, 4. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(number_of_frames_) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + + // Ack control frame 2. + manager_->OnControlFrameAcked(QuicFrame(&goaway_)); + // Do not retransmit an acked frame. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + EXPECT_TRUE(manager_->RetransmitControlFrame(QuicFrame(&goaway_))); + + // Retransmit control frame 3. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + EXPECT_TRUE(manager_->RetransmitControlFrame(QuicFrame(&window_update_))); + + // Retransmit control frame 4, and connection is write blocked. + EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false)); + EXPECT_FALSE(manager_->RetransmitControlFrame(QuicFrame(&window_update_))); +} + +TEST_F(QuicControlFrameManagerTest, DonotSendPingWithBufferedFrames) { + Initialize(); + InSequence s; + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false)); + // Send control frame 1. + manager_->OnCanWrite(); + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_TRUE(manager_->WillingToWrite()); + + // Send PING when there is buffered frames. + manager_->WritePing(); + // Verify only the buffered frames are sent. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(number_of_frames_ - 1) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_FALSE(manager_->WillingToWrite()); +} + +TEST_F(QuicControlFrameManagerTest, DonotRetransmitOldWindowUpdates) { + Initialize(); + // Send two more window updates of the same stream. + manager_->WriteOrBufferWindowUpdate(kTestStreamId, 200); + QuicWindowUpdateFrame window_update2(number_of_frames_ + 1, kTestStreamId, + 200); + + manager_->WriteOrBufferWindowUpdate(kTestStreamId, 300); + QuicWindowUpdateFrame window_update3(number_of_frames_ + 2, kTestStreamId, + 300); + InSequence s; + // Flush all buffered control frames. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + + // Mark all 3 window updates as lost. + manager_->OnControlFrameLost(QuicFrame(&window_update_)); + manager_->OnControlFrameLost(QuicFrame(&window_update2)); + manager_->OnControlFrameLost(QuicFrame(&window_update3)); + EXPECT_TRUE(manager_->HasPendingRetransmission()); + EXPECT_TRUE(manager_->WillingToWrite()); + + // Verify only the latest window update gets retransmitted. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(this, &QuicControlFrameManagerTest::SaveControlFrame)); + manager_->OnCanWrite(); + EXPECT_EQ(number_of_frames_ + 2u, + frame_.window_update_frame->control_frame_id); + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_FALSE(manager_->WillingToWrite()); + DeleteFrame(&frame_); +} + +TEST_F(QuicControlFrameManagerTest, RetransmitWindowUpdateOfDifferentStreams) { + Initialize(); + // Send two more window updates of different streams. + manager_->WriteOrBufferWindowUpdate(kTestStreamId + 2, 200); + QuicWindowUpdateFrame window_update2(5, kTestStreamId + 2, 200); + + manager_->WriteOrBufferWindowUpdate(kTestStreamId + 4, 300); + QuicWindowUpdateFrame window_update3(6, kTestStreamId + 4, 300); + InSequence s; + // Flush all buffered control frames. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + + // Mark all 3 window updates as lost. + manager_->OnControlFrameLost(QuicFrame(&window_update_)); + manager_->OnControlFrameLost(QuicFrame(&window_update2)); + manager_->OnControlFrameLost(QuicFrame(&window_update3)); + EXPECT_TRUE(manager_->HasPendingRetransmission()); + EXPECT_TRUE(manager_->WillingToWrite()); + + // Verify all 3 window updates get retransmitted. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(3) + .WillRepeatedly( + Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame)); + manager_->OnCanWrite(); + EXPECT_FALSE(manager_->HasPendingRetransmission()); + EXPECT_FALSE(manager_->WillingToWrite()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_crypto_client_handshaker.cc b/quic/core/quic_crypto_client_handshaker.cc new file mode 100644 index 0000000..6647061 --- /dev/null +++ b/quic/core/quic_crypto_client_handshaker.cc
@@ -0,0 +1,696 @@ +// Copyright (c) 2012 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/quic_crypto_client_handshaker.h" + +#include <memory> + +#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/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl:: + ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent) + : parent_(parent) {} + +QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl:: + ~ChannelIDSourceCallbackImpl() {} + +void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Run( + std::unique_ptr<ChannelIDKey>* channel_id_key) { + if (parent_ == nullptr) { + return; + } + + parent_->channel_id_key_ = std::move(*channel_id_key); + parent_->channel_id_source_callback_run_ = true; + parent_->channel_id_source_callback_ = nullptr; + parent_->DoHandshakeLoop(nullptr); + + // The ChannelIDSource owns this object and will delete it when this method + // returns. +} + +void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Cancel() { + parent_ = nullptr; +} + +QuicCryptoClientHandshaker::ProofVerifierCallbackImpl:: + ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent) + : parent_(parent) {} + +QuicCryptoClientHandshaker::ProofVerifierCallbackImpl:: + ~ProofVerifierCallbackImpl() {} + +void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Run( + bool ok, + const QuicString& error_details, + std::unique_ptr<ProofVerifyDetails>* details) { + if (parent_ == nullptr) { + return; + } + + parent_->verify_ok_ = ok; + parent_->verify_error_details_ = error_details; + parent_->verify_details_ = std::move(*details); + parent_->proof_verify_callback_ = nullptr; + parent_->DoHandshakeLoop(nullptr); + + // The ProofVerifier owns this object and will delete it when this method + // returns. +} + +void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Cancel() { + parent_ = nullptr; +} + +QuicCryptoClientHandshaker::QuicCryptoClientHandshaker( + const QuicServerId& server_id, + QuicCryptoClientStream* stream, + QuicSession* session, + std::unique_ptr<ProofVerifyContext> verify_context, + QuicCryptoClientConfig* crypto_config, + QuicCryptoClientStream::ProofHandler* proof_handler) + : QuicCryptoHandshaker(stream, session), + stream_(stream), + session_(session), + next_state_(STATE_IDLE), + num_client_hellos_(0), + crypto_config_(crypto_config), + server_id_(server_id), + generation_counter_(0), + channel_id_sent_(false), + channel_id_source_callback_run_(false), + channel_id_source_callback_(nullptr), + verify_context_(std::move(verify_context)), + proof_verify_callback_(nullptr), + proof_handler_(proof_handler), + verify_ok_(false), + stateless_reject_received_(false), + proof_verify_start_time_(QuicTime::Zero()), + num_scup_messages_received_(0), + encryption_established_(false), + handshake_confirmed_(false), + crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {} + +QuicCryptoClientHandshaker::~QuicCryptoClientHandshaker() { + if (channel_id_source_callback_) { + channel_id_source_callback_->Cancel(); + } + if (proof_verify_callback_) { + proof_verify_callback_->Cancel(); + } +} + +void QuicCryptoClientHandshaker::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QuicCryptoHandshaker::OnHandshakeMessage(message); + if (message.tag() == kSCUP) { + if (!handshake_confirmed()) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE, + "Early SCUP disallowed"); + return; + } + + // |message| is an update from the server, so we treat it differently from a + // handshake message. + HandleServerConfigUpdateMessage(message); + num_scup_messages_received_++; + return; + } + + // Do not process handshake messages after the handshake is confirmed. + if (handshake_confirmed()) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, + "Unexpected handshake message"); + return; + } + + DoHandshakeLoop(&message); +} + +bool QuicCryptoClientHandshaker::CryptoConnect() { + next_state_ = STATE_INITIALIZE; + DoHandshakeLoop(nullptr); + return session()->connection()->connected(); +} + +int QuicCryptoClientHandshaker::num_sent_client_hellos() const { + return num_client_hellos_; +} + +int QuicCryptoClientHandshaker::num_scup_messages_received() const { + return num_scup_messages_received_; +} + +bool QuicCryptoClientHandshaker::WasChannelIDSent() const { + return channel_id_sent_; +} + +bool QuicCryptoClientHandshaker::WasChannelIDSourceCallbackRun() const { + return channel_id_source_callback_run_; +} + +QuicString QuicCryptoClientHandshaker::chlo_hash() const { + return chlo_hash_; +} + +bool QuicCryptoClientHandshaker::encryption_established() const { + return encryption_established_; +} + +bool QuicCryptoClientHandshaker::handshake_confirmed() const { + return handshake_confirmed_; +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoClientHandshaker::crypto_negotiated_params() const { + return *crypto_negotiated_params_; +} + +CryptoMessageParser* QuicCryptoClientHandshaker::crypto_message_parser() { + return QuicCryptoHandshaker::crypto_message_parser(); +} + +void QuicCryptoClientHandshaker::HandleServerConfigUpdateMessage( + const CryptoHandshakeMessage& server_config_update) { + DCHECK(server_config_update.tag() == kSCUP); + QuicString error_details; + QuicCryptoClientConfig::CachedState* cached = + crypto_config_->LookupOrCreate(server_id_); + QuicErrorCode error = crypto_config_->ProcessServerConfigUpdate( + server_config_update, session()->connection()->clock()->WallNow(), + session()->connection()->transport_version(), chlo_hash_, cached, + crypto_negotiated_params_, &error_details); + + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails( + error, "Server config update invalid: " + error_details); + return; + } + + DCHECK(handshake_confirmed()); + if (proof_verify_callback_) { + proof_verify_callback_->Cancel(); + } + next_state_ = STATE_INITIALIZE_SCUP; + DoHandshakeLoop(nullptr); +} + +void QuicCryptoClientHandshaker::DoHandshakeLoop( + const CryptoHandshakeMessage* in) { + QuicCryptoClientConfig::CachedState* cached = + crypto_config_->LookupOrCreate(server_id_); + + QuicAsyncStatus rv = QUIC_SUCCESS; + do { + CHECK_NE(STATE_NONE, next_state_); + const State state = next_state_; + next_state_ = STATE_IDLE; + rv = QUIC_SUCCESS; + switch (state) { + case STATE_INITIALIZE: + DoInitialize(cached); + break; + case STATE_SEND_CHLO: + DoSendCHLO(cached); + return; // return waiting to hear from server. + case STATE_RECV_REJ: + DoReceiveREJ(in, cached); + break; + case STATE_VERIFY_PROOF: + rv = DoVerifyProof(cached); + break; + case STATE_VERIFY_PROOF_COMPLETE: + DoVerifyProofComplete(cached); + break; + case STATE_GET_CHANNEL_ID: + rv = DoGetChannelID(cached); + break; + case STATE_GET_CHANNEL_ID_COMPLETE: + DoGetChannelIDComplete(); + break; + case STATE_RECV_SHLO: + DoReceiveSHLO(in, cached); + break; + case STATE_IDLE: + // This means that the peer sent us a message that we weren't expecting. + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Handshake in idle state"); + return; + case STATE_INITIALIZE_SCUP: + DoInitializeServerConfigUpdate(cached); + break; + case STATE_NONE: + QUIC_NOTREACHED(); + return; // We are done. + } + } while (rv != QUIC_PENDING && next_state_ != STATE_NONE); +} + +void QuicCryptoClientHandshaker::DoInitialize( + QuicCryptoClientConfig::CachedState* cached) { + if (!cached->IsEmpty() && !cached->signature().empty()) { + // Note that we verify the proof even if the cached proof is valid. + // This allows us to respond to CA trust changes or certificate + // expiration because it may have been a while since we last verified + // the proof. + DCHECK(crypto_config_->proof_verifier()); + // Track proof verification time when cached server config is used. + proof_verify_start_time_ = session()->connection()->clock()->Now(); + chlo_hash_ = cached->chlo_hash(); + // If the cached state needs to be verified, do it now. + next_state_ = STATE_VERIFY_PROOF; + } else { + next_state_ = STATE_GET_CHANNEL_ID; + } +} + +void QuicCryptoClientHandshaker::DoSendCHLO( + QuicCryptoClientConfig::CachedState* cached) { + if (stateless_reject_received_) { + // If we've gotten to this point, we've sent at least one hello + // and received a stateless reject in response. We cannot + // continue to send hellos because the server has abandoned state + // for this connection. Abandon further handshakes. + next_state_ = STATE_NONE; + if (session()->connection()->connected()) { + session()->connection()->CloseConnection( + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject received", + ConnectionCloseBehavior::SILENT_CLOSE); + } + return; + } + + // Send the client hello in plaintext. + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE); + encryption_established_ = false; + if (num_client_hellos_ > QuicCryptoClientStream::kMaxClientHellos) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_TOO_MANY_REJECTS, + QuicStrCat("More than ", QuicCryptoClientStream::kMaxClientHellos, + " rejects")); + return; + } + num_client_hellos_++; + + CryptoHandshakeMessage out; + DCHECK(session() != nullptr); + DCHECK(session()->config() != nullptr); + // Send all the options, regardless of whether we're sending an + // inchoate or subsequent hello. + session()->config()->ToHandshakeMessage(&out); + + if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { + crypto_config_->FillInchoateClientHello( + server_id_, session()->supported_versions().front(), cached, + session()->connection()->random_generator(), + /* demand_x509_proof= */ true, crypto_negotiated_params_, &out); + // Pad the inchoate client hello to fill up a packet. + const QuicByteCount kFramingOverhead = 50; // A rough estimate. + const QuicByteCount max_packet_size = + session()->connection()->max_packet_length(); + if (max_packet_size <= kFramingOverhead) { + QUIC_DLOG(DFATAL) << "max_packet_length (" << max_packet_size + << ") has no room for framing overhead."; + stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "max_packet_size too smalll"); + return; + } + if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) { + QUIC_DLOG(DFATAL) << "Client hello won't fit in a single packet."; + stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "CHLO too large"); + return; + } + next_state_ = STATE_RECV_REJ; + CryptoUtils::HashHandshakeMessage(out, &chlo_hash_, Perspective::IS_CLIENT); + session()->connection()->set_fully_pad_crypto_hadshake_packets( + crypto_config_->pad_inchoate_hello()); + SendHandshakeMessage(out); + return; + } + + // If the server nonce is empty, copy over the server nonce from a previous + // SREJ, if there is one. + if (GetQuicReloadableFlag(enable_quic_stateless_reject_support) && + crypto_negotiated_params_->server_nonce.empty() && + cached->has_server_nonce()) { + crypto_negotiated_params_->server_nonce = cached->GetNextServerNonce(); + DCHECK(!crypto_negotiated_params_->server_nonce.empty()); + } + + QuicString error_details; + QuicErrorCode error = crypto_config_->FillClientHello( + server_id_, session()->connection()->connection_id(), + session()->supported_versions().front(), cached, + session()->connection()->clock()->WallNow(), + session()->connection()->random_generator(), channel_id_key_.get(), + crypto_negotiated_params_, &out, &error_details); + if (error != QUIC_NO_ERROR) { + // Flush the cached config so that, if it's bad, the server has a + // chance to send us another in the future. + cached->InvalidateServerConfig(); + stream_->CloseConnectionWithDetails(error, error_details); + return; + } + CryptoUtils::HashHandshakeMessage(out, &chlo_hash_, Perspective::IS_CLIENT); + channel_id_sent_ = (channel_id_key_ != nullptr); + if (cached->proof_verify_details()) { + proof_handler_->OnProofVerifyDetailsAvailable( + *cached->proof_verify_details()); + } + next_state_ = STATE_RECV_SHLO; + session()->connection()->set_fully_pad_crypto_hadshake_packets( + crypto_config_->pad_full_hello()); + SendHandshakeMessage(out); + // Be prepared to decrypt with the new server write key. + session()->connection()->SetAlternativeDecrypter( + ENCRYPTION_ZERO_RTT, + std::move(crypto_negotiated_params_->initial_crypters.decrypter), + true /* latch once used */); + // Send subsequent packets under encryption on the assumption that the + // server will accept the handshake. + session()->connection()->SetEncrypter( + ENCRYPTION_ZERO_RTT, + std::move(crypto_negotiated_params_->initial_crypters.encrypter)); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + + // TODO(ianswett): Merge ENCRYPTION_REESTABLISHED and + // ENCRYPTION_FIRST_ESTABLSIHED + encryption_established_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_REESTABLISHED); +} + +void QuicCryptoClientHandshaker::DoReceiveREJ( + const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached) { + // We sent a dummy CHLO because we didn't have enough information to + // perform a handshake, or we sent a full hello that the server + // rejected. Here we hope to have a REJ that contains the information + // that we need. + if ((in->tag() != kREJ) && (in->tag() != kSREJ)) { + next_state_ = STATE_NONE; + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected REJ"); + return; + } + + QuicTagVector reject_reasons; + static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync"); + if (in->GetTaglist(kRREJ, &reject_reasons) == QUIC_NO_ERROR) { + uint32_t packed_error = 0; + for (size_t i = 0; i < reject_reasons.size(); ++i) { + // HANDSHAKE_OK is 0 and don't report that as error. + if (reject_reasons[i] == HANDSHAKE_OK || reject_reasons[i] >= 32) { + continue; + } + HandshakeFailureReason reason = + static_cast<HandshakeFailureReason>(reject_reasons[i]); + packed_error |= 1 << (reason - 1); + } + DVLOG(1) << "Reasons for rejection: " << packed_error; + if (num_client_hellos_ == QuicCryptoClientStream::kMaxClientHellos) { + QuicClientSparseHistogram("QuicClientHelloRejectReasons.TooMany", + packed_error); + } + QuicClientSparseHistogram("QuicClientHelloRejectReasons.Secure", + packed_error); + } + + // Receipt of a REJ message means that the server received the CHLO + // so we can cancel and retransmissions. + session()->NeuterUnencryptedData(); + + stateless_reject_received_ = in->tag() == kSREJ; + QuicString error_details; + QuicErrorCode error = crypto_config_->ProcessRejection( + *in, session()->connection()->clock()->WallNow(), + session()->connection()->transport_version(), chlo_hash_, cached, + crypto_negotiated_params_, &error_details); + + if (error != QUIC_NO_ERROR) { + next_state_ = STATE_NONE; + stream_->CloseConnectionWithDetails(error, error_details); + return; + } + if (!cached->proof_valid()) { + if (!cached->signature().empty()) { + // Note that we only verify the proof if the cached proof is not + // valid. If the cached proof is valid here, someone else must have + // just added the server config to the cache and verified the proof, + // so we can assume no CA trust changes or certificate expiration + // has happened since then. + next_state_ = STATE_VERIFY_PROOF; + return; + } + } + next_state_ = STATE_GET_CHANNEL_ID; +} + +QuicAsyncStatus QuicCryptoClientHandshaker::DoVerifyProof( + QuicCryptoClientConfig::CachedState* cached) { + ProofVerifier* verifier = crypto_config_->proof_verifier(); + DCHECK(verifier); + next_state_ = STATE_VERIFY_PROOF_COMPLETE; + generation_counter_ = cached->generation_counter(); + + ProofVerifierCallbackImpl* proof_verify_callback = + new ProofVerifierCallbackImpl(this); + + verify_ok_ = false; + + QuicAsyncStatus status = verifier->VerifyProof( + server_id_.host(), server_id_.port(), cached->server_config(), + session()->connection()->transport_version(), chlo_hash_, cached->certs(), + cached->cert_sct(), cached->signature(), verify_context_.get(), + &verify_error_details_, &verify_details_, + std::unique_ptr<ProofVerifierCallback>(proof_verify_callback)); + + switch (status) { + case QUIC_PENDING: + proof_verify_callback_ = proof_verify_callback; + QUIC_DVLOG(1) << "Doing VerifyProof"; + break; + case QUIC_FAILURE: + break; + case QUIC_SUCCESS: + verify_ok_ = true; + break; + } + return status; +} + +void QuicCryptoClientHandshaker::DoVerifyProofComplete( + QuicCryptoClientConfig::CachedState* cached) { + if (proof_verify_start_time_.IsInitialized()) { + QUIC_CLIENT_HISTOGRAM_TIMES( + "QuicSession.VerifyProofTime.CachedServerConfig", + (session()->connection()->clock()->Now() - proof_verify_start_time_), + QuicTime::Delta::FromMilliseconds(1), QuicTime::Delta::FromSeconds(10), + 50, ""); + } + if (!verify_ok_) { + if (verify_details_) { + proof_handler_->OnProofVerifyDetailsAvailable(*verify_details_); + } + if (num_client_hellos_ == 0) { + cached->Clear(); + next_state_ = STATE_INITIALIZE; + return; + } + next_state_ = STATE_NONE; + QUIC_CLIENT_HISTOGRAM_BOOL("QuicVerifyProofFailed.HandshakeConfirmed", + handshake_confirmed(), ""); + stream_->CloseConnectionWithDetails( + QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_); + return; + } + + // Check if generation_counter has changed between STATE_VERIFY_PROOF and + // STATE_VERIFY_PROOF_COMPLETE state changes. + if (generation_counter_ != cached->generation_counter()) { + next_state_ = STATE_VERIFY_PROOF; + } else { + SetCachedProofValid(cached); + cached->SetProofVerifyDetails(verify_details_.release()); + if (!handshake_confirmed()) { + next_state_ = STATE_GET_CHANNEL_ID; + } else { + // TODO: Enable Expect-Staple. https://crbug.com/631101 + next_state_ = STATE_NONE; + } + } +} + +QuicAsyncStatus QuicCryptoClientHandshaker::DoGetChannelID( + QuicCryptoClientConfig::CachedState* cached) { + next_state_ = STATE_GET_CHANNEL_ID_COMPLETE; + channel_id_key_.reset(); + if (!RequiresChannelID(cached)) { + next_state_ = STATE_SEND_CHLO; + return QUIC_SUCCESS; + } + + ChannelIDSourceCallbackImpl* channel_id_source_callback = + new ChannelIDSourceCallbackImpl(this); + QuicAsyncStatus status = crypto_config_->channel_id_source()->GetChannelIDKey( + server_id_.host(), &channel_id_key_, channel_id_source_callback); + + switch (status) { + case QUIC_PENDING: + channel_id_source_callback_ = channel_id_source_callback; + QUIC_DVLOG(1) << "Looking up channel ID"; + break; + case QUIC_FAILURE: + next_state_ = STATE_NONE; + delete channel_id_source_callback; + stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, + "Channel ID lookup failed"); + break; + case QUIC_SUCCESS: + delete channel_id_source_callback; + break; + } + return status; +} + +void QuicCryptoClientHandshaker::DoGetChannelIDComplete() { + if (!channel_id_key_.get()) { + next_state_ = STATE_NONE; + stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, + "Channel ID lookup failed"); + return; + } + next_state_ = STATE_SEND_CHLO; +} + +void QuicCryptoClientHandshaker::DoReceiveSHLO( + const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached) { + next_state_ = STATE_NONE; + // We sent a CHLO that we expected to be accepted and now we're + // hoping for a SHLO from the server to confirm that. First check + // to see whether the response was a reject, and if so, move on to + // the reject-processing state. + if ((in->tag() == kREJ) || (in->tag() == kSREJ)) { + // alternative_decrypter will be nullptr if the original alternative + // decrypter latched and became the primary decrypter. That happens + // if we received a message encrypted with the INITIAL key. + if (session()->connection()->alternative_decrypter() == nullptr) { + // The rejection was sent encrypted! + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, "encrypted REJ message"); + return; + } + next_state_ = STATE_RECV_REJ; + return; + } + + if (in->tag() != kSHLO) { + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected SHLO or REJ"); + return; + } + + // alternative_decrypter will be nullptr if the original alternative + // decrypter latched and became the primary decrypter. That happens + // if we received a message encrypted with the INITIAL key. + if (session()->connection()->alternative_decrypter() != nullptr) { + // The server hello was sent without encryption. + stream_->CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, + "unencrypted SHLO message"); + return; + } + + QuicString error_details; + QuicErrorCode error = crypto_config_->ProcessServerHello( + *in, session()->connection()->connection_id(), + session()->connection()->version(), + session()->connection()->server_supported_versions(), cached, + crypto_negotiated_params_, &error_details); + + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails( + error, "Server hello invalid: " + error_details); + return; + } + error = session()->config()->ProcessPeerHello(*in, SERVER, &error_details); + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails( + error, "Server hello invalid: " + error_details); + return; + } + session()->OnConfigNegotiated(); + + CrypterPair* crypters = &crypto_negotiated_params_->forward_secure_crypters; + // TODO(agl): we don't currently latch this decrypter because the idea + // has been floated that the server shouldn't send packets encrypted + // with the FORWARD_SECURE key until it receives a FORWARD_SECURE + // packet from the client. + session()->connection()->SetAlternativeDecrypter( + ENCRYPTION_FORWARD_SECURE, std::move(crypters->decrypter), + false /* don't latch */); + session()->connection()->SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::move(crypters->encrypter)); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + session()->connection()->OnHandshakeComplete(); +} + +void QuicCryptoClientHandshaker::DoInitializeServerConfigUpdate( + QuicCryptoClientConfig::CachedState* cached) { + bool update_ignored = false; + if (!cached->IsEmpty() && !cached->signature().empty()) { + // Note that we verify the proof even if the cached proof is valid. + DCHECK(crypto_config_->proof_verifier()); + next_state_ = STATE_VERIFY_PROOF; + } else { + update_ignored = true; + next_state_ = STATE_NONE; + } + QUIC_CLIENT_HISTOGRAM_COUNTS("QuicNumServerConfig.UpdateMessagesIgnored", + update_ignored, 1, 1000000, 50, ""); +} + +void QuicCryptoClientHandshaker::SetCachedProofValid( + QuicCryptoClientConfig::CachedState* cached) { + cached->SetProofValid(); + proof_handler_->OnProofValid(*cached); +} + +bool QuicCryptoClientHandshaker::RequiresChannelID( + QuicCryptoClientConfig::CachedState* cached) { + if (server_id_.privacy_mode_enabled() || + !crypto_config_->channel_id_source()) { + return false; + } + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + if (!scfg) { // scfg may be null then we send an inchoate CHLO. + return false; + } + QuicTagVector their_proof_demands; + if (scfg->GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) { + return false; + } + for (const QuicTag tag : their_proof_demands) { + if (tag == kCHID) { + return true; + } + } + return false; +} + +} // namespace quic
diff --git a/quic/core/quic_crypto_client_handshaker.h b/quic/core/quic_crypto_client_handshaker.h new file mode 100644 index 0000000..5836ea2 --- /dev/null +++ b/quic/core/quic_crypto_client_handshaker.h
@@ -0,0 +1,236 @@ +// Copyright (c) 2012 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_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_ +#define QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_ + +#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// An implementation of QuicCryptoClientStream::HandshakerDelegate which uses +// QUIC crypto as the crypto handshake protocol. +class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker + : public QuicCryptoClientStream::HandshakerDelegate, + public QuicCryptoHandshaker { + public: + QuicCryptoClientHandshaker( + const QuicServerId& server_id, + QuicCryptoClientStream* stream, + QuicSession* session, + std::unique_ptr<ProofVerifyContext> verify_context, + QuicCryptoClientConfig* crypto_config, + QuicCryptoClientStream::ProofHandler* proof_handler); + QuicCryptoClientHandshaker(const QuicCryptoClientHandshaker&) = delete; + QuicCryptoClientHandshaker& operator=(const QuicCryptoClientHandshaker&) = + delete; + + ~QuicCryptoClientHandshaker() override; + + // From QuicCryptoClientStream::HandshakerDelegate + bool CryptoConnect() override; + int num_sent_client_hellos() const override; + int num_scup_messages_received() const override; + bool WasChannelIDSent() const override; + bool WasChannelIDSourceCallbackRun() const override; + QuicString chlo_hash() const override; + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + + // From QuicCryptoHandshaker + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; + + protected: + // Returns the QuicSession that this stream belongs to. + QuicSession* session() const { return session_; } + + // Send either InchoateClientHello or ClientHello message to the server. + void DoSendCHLO(QuicCryptoClientConfig::CachedState* cached); + + private: + // ChannelIDSourceCallbackImpl is passed as the callback method to + // GetChannelIDKey. The ChannelIDSource calls this class with the result of + // channel ID lookup when lookup is performed asynchronously. + class ChannelIDSourceCallbackImpl : public ChannelIDSourceCallback { + public: + explicit ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent); + ~ChannelIDSourceCallbackImpl() override; + + // ChannelIDSourceCallback interface. + void Run(std::unique_ptr<ChannelIDKey>* channel_id_key) override; + + // Cancel causes any future callbacks to be ignored. It must be called on + // the same thread as the callback will be made on. + void Cancel(); + + private: + QuicCryptoClientHandshaker* parent_; + }; + + // ProofVerifierCallbackImpl is passed as the callback method to VerifyProof. + // The ProofVerifier calls this class with the result of proof verification + // when verification is performed asynchronously. + class ProofVerifierCallbackImpl : public ProofVerifierCallback { + public: + explicit ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent); + ~ProofVerifierCallbackImpl() override; + + // ProofVerifierCallback interface. + void Run(bool ok, + const QuicString& error_details, + std::unique_ptr<ProofVerifyDetails>* details) override; + + // Cancel causes any future callbacks to be ignored. It must be called on + // the same thread as the callback will be made on. + void Cancel(); + + private: + QuicCryptoClientHandshaker* parent_; + }; + + enum State { + STATE_IDLE, + STATE_INITIALIZE, + STATE_SEND_CHLO, + STATE_RECV_REJ, + STATE_VERIFY_PROOF, + STATE_VERIFY_PROOF_COMPLETE, + STATE_GET_CHANNEL_ID, + STATE_GET_CHANNEL_ID_COMPLETE, + STATE_RECV_SHLO, + STATE_INITIALIZE_SCUP, + STATE_NONE, + }; + + // Handles new server config and optional source-address token provided by the + // server during a connection. + void HandleServerConfigUpdateMessage( + const CryptoHandshakeMessage& server_config_update); + + // DoHandshakeLoop performs a step of the handshake state machine. Note that + // |in| may be nullptr if the call did not result from a received message. + void DoHandshakeLoop(const CryptoHandshakeMessage* in); + + // Start the handshake process. + void DoInitialize(QuicCryptoClientConfig::CachedState* cached); + + // Process REJ message from the server. + void DoReceiveREJ(const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached); + + // Start the proof verification process. Returns the QuicAsyncStatus returned + // by the ProofVerifier's VerifyProof. + QuicAsyncStatus DoVerifyProof(QuicCryptoClientConfig::CachedState* cached); + + // If proof is valid then it sets the proof as valid (which persists the + // server config). If not, it closes the connection. + void DoVerifyProofComplete(QuicCryptoClientConfig::CachedState* cached); + + // Start the look up of Channel ID process. Returns either QUIC_SUCCESS if + // RequiresChannelID returns false or QuicAsyncStatus returned by + // GetChannelIDKey. + QuicAsyncStatus DoGetChannelID(QuicCryptoClientConfig::CachedState* cached); + + // If there is no channel ID, then close the connection otherwise transtion to + // STATE_SEND_CHLO state. + void DoGetChannelIDComplete(); + + // Process SHLO message from the server. + void DoReceiveSHLO(const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached); + + // Start the proof verification if |server_id_| is https and |cached| has + // signature. + void DoInitializeServerConfigUpdate( + QuicCryptoClientConfig::CachedState* cached); + + // Called to set the proof of |cached| valid. Also invokes the session's + // OnProofValid() method. + void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached); + + // Returns true if the server crypto config in |cached| requires a ChannelID + // and the client config settings also allow sending a ChannelID. + bool RequiresChannelID(QuicCryptoClientConfig::CachedState* cached); + + QuicCryptoClientStream* stream_; + + QuicSession* session_; + + State next_state_; + // num_client_hellos_ contains the number of client hello messages that this + // connection has sent. + int num_client_hellos_; + + QuicCryptoClientConfig* const crypto_config_; + + // SHA-256 hash of the most recently sent CHLO. + QuicString chlo_hash_; + + // Server's (hostname, port, is_https, privacy_mode) tuple. + const QuicServerId server_id_; + + // Generation counter from QuicCryptoClientConfig's CachedState. + uint64_t generation_counter_; + + // True if a channel ID was sent. + bool channel_id_sent_; + + // True if channel_id_source_callback_ was run. + bool channel_id_source_callback_run_; + + // channel_id_source_callback_ contains the callback object that we passed + // to an asynchronous channel ID lookup. The ChannelIDSource owns this + // object. + ChannelIDSourceCallbackImpl* channel_id_source_callback_; + + // These members are used to store the result of an asynchronous channel ID + // lookup. These members must not be used after + // STATE_GET_CHANNEL_ID_COMPLETE. + std::unique_ptr<ChannelIDKey> channel_id_key_; + + // verify_context_ contains the context object that we pass to asynchronous + // proof verifications. + std::unique_ptr<ProofVerifyContext> verify_context_; + + // proof_verify_callback_ contains the callback object that we passed to an + // asynchronous proof verification. The ProofVerifier owns this object. + ProofVerifierCallbackImpl* proof_verify_callback_; + // proof_handler_ contains the callback object used by a quic client + // for proof verification. It is not owned by this class. + QuicCryptoClientStream::ProofHandler* proof_handler_; + + // These members are used to store the result of an asynchronous proof + // verification. These members must not be used after + // STATE_VERIFY_PROOF_COMPLETE. + bool verify_ok_; + QuicString verify_error_details_; + std::unique_ptr<ProofVerifyDetails> verify_details_; + + // True if the server responded to a previous CHLO with a stateless + // reject. Used for book-keeping between the STATE_RECV_REJ, + // STATE_VERIFY_PROOF*, and subsequent STATE_SEND_CHLO state. + bool stateless_reject_received_; + + QuicTime proof_verify_start_time_; + + int num_scup_messages_received_; + + bool encryption_established_; + bool handshake_confirmed_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> + crypto_negotiated_params_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_
diff --git a/quic/core/quic_crypto_client_handshaker_test.cc b/quic/core/quic_crypto_client_handshaker_test.cc new file mode 100644 index 0000000..e21faf3 --- /dev/null +++ b/quic/core/quic_crypto_client_handshaker_test.cc
@@ -0,0 +1,213 @@ +// Copyright (c) 2012 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/quic_crypto_client_handshaker.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace { + +using ::testing::Test; + +class TestProofHandler : public QuicCryptoClientStream::ProofHandler { + public: + ~TestProofHandler() override {} + void OnProofValid( + const QuicCryptoClientConfig::CachedState& cached) override {} + void OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) override {} +}; + +class InsecureProofVerifier : public ProofVerifier { + public: + InsecureProofVerifier() {} + ~InsecureProofVerifier() override {} + + // ProofVerifier override. + QuicAsyncStatus VerifyProof( + const QuicString& hostname, + const uint16_t port, + const QuicString& server_config, + QuicTransportVersion transport_version, + QuicStringPiece chlo_hash, + const std::vector<QuicString>& certs, + const QuicString& cert_sct, + const QuicString& signature, + const ProofVerifyContext* context, + QuicString* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) override { + return QUIC_SUCCESS; + } + + QuicAsyncStatus VerifyCertChain( + const QuicString& hostname, + const std::vector<QuicString>& certs, + const ProofVerifyContext* context, + QuicString* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) override { + return QUIC_SUCCESS; + } + + std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override { + return nullptr; + } +}; + +class DummyProofSource : public ProofSource { + public: + DummyProofSource() {} + ~DummyProofSource() override {} + + // ProofSource override. + void GetProof(const QuicSocketAddress& server_address, + const QuicString& hostname, + const QuicString& server_config, + QuicTransportVersion transport_version, + QuicStringPiece chlo_hash, + std::unique_ptr<Callback> callback) override { + QuicReferenceCountedPointer<ProofSource::Chain> chain = + GetCertChain(server_address, hostname); + QuicCryptoProof proof; + proof.signature = "Dummy signature"; + proof.leaf_cert_scts = "Dummy timestamp"; + callback->Run(true, chain, proof, nullptr /* details */); + } + + QuicReferenceCountedPointer<Chain> GetCertChain( + const QuicSocketAddress& server_address, + const QuicString& hostname) override { + std::vector<QuicString> certs; + certs.push_back("Dummy cert"); + return QuicReferenceCountedPointer<ProofSource::Chain>( + new ProofSource::Chain(certs)); + } + + void ComputeTlsSignature( + const QuicSocketAddress& server_address, + const QuicString& hostname, + uint16_t signature_algorithm, + QuicStringPiece in, + std::unique_ptr<SignatureCallback> callback) override { + callback->Run(true, "Dummy signature"); + } +}; + +class Handshaker : public QuicCryptoClientHandshaker { + public: + Handshaker(const QuicServerId& server_id, + QuicCryptoClientStream* stream, + QuicSession* session, + std::unique_ptr<ProofVerifyContext> verify_context, + QuicCryptoClientConfig* crypto_config, + QuicCryptoClientStream::ProofHandler* proof_handler) + : QuicCryptoClientHandshaker(server_id, + stream, + session, + std::move(verify_context), + crypto_config, + proof_handler) {} + + void DoSendCHLOTest(QuicCryptoClientConfig::CachedState* cached) { + QuicCryptoClientHandshaker::DoSendCHLO(cached); + } +}; + +class QuicCryptoClientHandshakerTest : public Test { + protected: + QuicCryptoClientHandshakerTest() + : proof_handler_(), + helper_(), + alarm_factory_(), + server_id_("host", 123), + connection_(new test::MockQuicConnection(&helper_, + &alarm_factory_, + Perspective::IS_CLIENT)), + session_(connection_, false), + crypto_client_config_(QuicMakeUnique<InsecureProofVerifier>(), + quic::TlsClientHandshaker::CreateSslCtx()), + client_stream_(new QuicCryptoClientStream(server_id_, + &session_, + nullptr, + &crypto_client_config_, + &proof_handler_)), + handshaker_(server_id_, + client_stream_, + &session_, + nullptr, + &crypto_client_config_, + &proof_handler_), + state_() { + // Session takes the ownership of the client stream! (but handshaker also + // takes a reference to it, but doesn't take the ownership). + session_.SetCryptoStream(client_stream_); + session_.Initialize(); + } + + void InitializeServerParametersToEnableFullHello() { + QuicCryptoServerConfig::ConfigOptions options; + std::unique_ptr<QuicServerConfigProtobuf> config = + QuicCryptoServerConfig::GenerateConfig(helper_.GetRandomGenerator(), + helper_.GetClock(), options); + state_.Initialize( + config->config(), "sourcetoken", std::vector<QuicString>{"Dummy cert"}, + "", "chlo_hash", "signature", helper_.GetClock()->WallNow(), + helper_.GetClock()->WallNow().Add(QuicTime::Delta::FromSeconds(30))); + + state_.SetProofValid(); + } + + TestProofHandler proof_handler_; + test::MockQuicConnectionHelper helper_; + test::MockAlarmFactory alarm_factory_; + QuicServerId server_id_; + // Session takes the ownership of the connection. + test::MockQuicConnection* connection_; + test::MockQuicSession session_; + QuicCryptoClientConfig crypto_client_config_; + QuicCryptoClientStream* client_stream_; + Handshaker handshaker_; + QuicCryptoClientConfig::CachedState state_; +}; + +TEST_F(QuicCryptoClientHandshakerTest, TestSendFullPaddingInInchoateHello) { + handshaker_.DoSendCHLOTest(&state_); + + EXPECT_TRUE(connection_->fully_pad_during_crypto_handshake()); +} + +TEST_F(QuicCryptoClientHandshakerTest, TestDisabledPaddingInInchoateHello) { + crypto_client_config_.set_pad_inchoate_hello(false); + handshaker_.DoSendCHLOTest(&state_); + EXPECT_FALSE(connection_->fully_pad_during_crypto_handshake()); +} + +TEST_F(QuicCryptoClientHandshakerTest, + TestPaddingInFullHelloEvenIfInchoateDisabled) { + // Disable inchoate, but full hello should still be padded. + crypto_client_config_.set_pad_inchoate_hello(false); + + InitializeServerParametersToEnableFullHello(); + + handshaker_.DoSendCHLOTest(&state_); + EXPECT_TRUE(connection_->fully_pad_during_crypto_handshake()); +} + +TEST_F(QuicCryptoClientHandshakerTest, TestNoPaddingInFullHelloWhenDisabled) { + crypto_client_config_.set_pad_full_hello(false); + + InitializeServerParametersToEnableFullHello(); + + handshaker_.DoSendCHLOTest(&state_); + EXPECT_FALSE(connection_->fully_pad_during_crypto_handshake()); +} + +} // namespace +} // namespace quic
diff --git a/quic/core/quic_crypto_client_stream.cc b/quic/core/quic_crypto_client_stream.cc new file mode 100644 index 0000000..66bb9e4 --- /dev/null +++ b/quic/core/quic_crypto_client_stream.cc
@@ -0,0 +1,99 @@ +// Copyright (c) 2012 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/quic_crypto_client_stream.h" + +#include <memory> + +#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/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +const int QuicCryptoClientStream::kMaxClientHellos; + +QuicCryptoClientStreamBase::QuicCryptoClientStreamBase(QuicSession* session) + : QuicCryptoStream(session) {} + +QuicCryptoClientStream::QuicCryptoClientStream( + const QuicServerId& server_id, + QuicSession* session, + std::unique_ptr<ProofVerifyContext> verify_context, + QuicCryptoClientConfig* crypto_config, + ProofHandler* proof_handler) + : QuicCryptoClientStreamBase(session) { + DCHECK_EQ(Perspective::IS_CLIENT, session->connection()->perspective()); + switch (session->connection()->version().handshake_protocol) { + case PROTOCOL_QUIC_CRYPTO: + handshaker_ = QuicMakeUnique<QuicCryptoClientHandshaker>( + server_id, this, session, std::move(verify_context), crypto_config, + proof_handler); + break; + case PROTOCOL_TLS1_3: + handshaker_ = QuicMakeUnique<TlsClientHandshaker>( + this, session, server_id, crypto_config->proof_verifier(), + crypto_config->ssl_ctx(), std::move(verify_context), + crypto_config->user_agent_id()); + break; + case PROTOCOL_UNSUPPORTED: + QUIC_BUG << "Attempting to create QuicCryptoClientStream for unknown " + "handshake protocol"; + } +} + +QuicCryptoClientStream::~QuicCryptoClientStream() {} + +bool QuicCryptoClientStream::CryptoConnect() { + return handshaker_->CryptoConnect(); +} + +int QuicCryptoClientStream::num_sent_client_hellos() const { + return handshaker_->num_sent_client_hellos(); +} + +int QuicCryptoClientStream::num_scup_messages_received() const { + return handshaker_->num_scup_messages_received(); +} + +bool QuicCryptoClientStream::encryption_established() const { + return handshaker_->encryption_established(); +} + +bool QuicCryptoClientStream::handshake_confirmed() const { + return handshaker_->handshake_confirmed(); +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoClientStream::crypto_negotiated_params() const { + return handshaker_->crypto_negotiated_params(); +} + +CryptoMessageParser* QuicCryptoClientStream::crypto_message_parser() { + return handshaker_->crypto_message_parser(); +} + +bool QuicCryptoClientStream::WasChannelIDSent() const { + return handshaker_->WasChannelIDSent(); +} + +bool QuicCryptoClientStream::WasChannelIDSourceCallbackRun() const { + return handshaker_->WasChannelIDSourceCallbackRun(); +} + +QuicString QuicCryptoClientStream::chlo_hash() const { + return handshaker_->chlo_hash(); +} + +} // namespace quic
diff --git a/quic/core/quic_crypto_client_stream.h b/quic/core/quic_crypto_client_stream.h new file mode 100644 index 0000000..da149fe --- /dev/null +++ b/quic/core/quic_crypto_client_stream.h
@@ -0,0 +1,172 @@ +// Copyright (c) 2012 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_QUIC_CRYPTO_CLIENT_STREAM_H_ +#define QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_STREAM_H_ + +#include <cstdint> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicCryptoClientStreamBase : public QuicCryptoStream { + public: + explicit QuicCryptoClientStreamBase(QuicSession* session); + + ~QuicCryptoClientStreamBase() override {} + + // Performs a crypto handshake with the server. Returns true if the connection + // is still connected. + virtual bool CryptoConnect() = 0; + + // num_sent_client_hellos returns the number of client hello messages that + // have been sent. If the handshake has completed then this is one greater + // than the number of round-trips needed for the handshake. + virtual int num_sent_client_hellos() const = 0; + + // The number of server config update messages received by the + // client. Does not count update messages that were received prior + // to handshake confirmation. + virtual int num_scup_messages_received() const = 0; +}; + +class QUIC_EXPORT_PRIVATE QuicCryptoClientStream + : public QuicCryptoClientStreamBase { + public: + // kMaxClientHellos is the maximum number of times that we'll send a client + // hello. The value 3 accounts for: + // * One failure due to an incorrect or missing source-address token. + // * One failure due the server's certificate chain being unavailible and + // the server being unwilling to send it without a valid source-address + // token. + static const int kMaxClientHellos = 3; + + // QuicCryptoClientStream creates a HandshakerDelegate at construction time + // based on the QuicTransportVersion of the connection. Different + // HandshakerDelegates provide implementations of different crypto handshake + // protocols. Currently QUIC crypto is the only protocol implemented; a future + // HandshakerDelegate will use TLS as the handshake protocol. + // QuicCryptoClientStream delegates all of its public methods to its + // HandshakerDelegate. + // + // This setup of the crypto stream delegating its implementation to the + // handshaker results in the handshaker reading and writing bytes on the + // crypto stream, instead of the handshaker passing the stream bytes to send. + class QUIC_EXPORT_PRIVATE HandshakerDelegate { + public: + virtual ~HandshakerDelegate() {} + + // Performs a crypto handshake with the server. Returns true if the + // connection is still connected. + virtual bool CryptoConnect() = 0; + + // num_sent_client_hellos returns the number of client hello messages that + // have been sent. If the handshake has completed then this is one greater + // than the number of round-trips needed for the handshake. + virtual int num_sent_client_hellos() const = 0; + + // The number of server config update messages received by the + // client. Does not count update messages that were received prior + // to handshake confirmation. + virtual int num_scup_messages_received() const = 0; + + // Returns true if a channel ID was sent on this connection. + virtual bool WasChannelIDSent() const = 0; + + // Returns true if our ChannelIDSourceCallback was run, which implies the + // ChannelIDSource operated asynchronously. Intended for testing. + virtual bool WasChannelIDSourceCallbackRun() const = 0; + + virtual QuicString chlo_hash() const = 0; + + // Returns true once any encrypter (initial/0RTT or final/1RTT) has been set + // for the connection. + virtual bool encryption_established() const = 0; + + // Returns true once the crypto handshake has completed. + virtual bool handshake_confirmed() const = 0; + + // Returns the parameters negotiated in the crypto handshake. + virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const = 0; + + // Used by QuicCryptoStream to parse data received on this stream. + virtual CryptoMessageParser* crypto_message_parser() = 0; + }; + + // ProofHandler is an interface that handles callbacks from the crypto + // stream when the client has proof verification details of the server. + class QUIC_EXPORT_PRIVATE ProofHandler { + public: + virtual ~ProofHandler() {} + + // Called when the proof in |cached| is marked valid. If this is a secure + // QUIC session, then this will happen only after the proof verifier + // completes. + virtual void OnProofValid( + const QuicCryptoClientConfig::CachedState& cached) = 0; + + // Called when proof verification details become available, either because + // proof verification is complete, or when cached details are used. This + // will only be called for secure QUIC connections. + virtual void OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) = 0; + }; + + QuicCryptoClientStream(const QuicServerId& server_id, + QuicSession* session, + std::unique_ptr<ProofVerifyContext> verify_context, + QuicCryptoClientConfig* crypto_config, + ProofHandler* proof_handler); + QuicCryptoClientStream(const QuicCryptoClientStream&) = delete; + QuicCryptoClientStream& operator=(const QuicCryptoClientStream&) = delete; + + ~QuicCryptoClientStream() override; + + // From QuicCryptoClientStreamBase + bool CryptoConnect() override; + int num_sent_client_hellos() const override; + + int num_scup_messages_received() const override; + + // From QuicCryptoStream + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + + // Returns true if a channel ID was sent on this connection. + bool WasChannelIDSent() const; + + // Returns true if our ChannelIDSourceCallback was run, which implies the + // ChannelIDSource operated asynchronously. Intended for testing. + bool WasChannelIDSourceCallbackRun() const; + + QuicString chlo_hash() const; + + protected: + void set_handshaker(std::unique_ptr<HandshakerDelegate> handshaker) { + handshaker_ = std::move(handshaker); + } + + private: + std::unique_ptr<HandshakerDelegate> handshaker_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_STREAM_H_
diff --git a/quic/core/quic_crypto_client_stream_test.cc b/quic/core/quic_crypto_client_stream_test.cc new file mode 100644 index 0000000..d9b344a --- /dev/null +++ b/quic/core/quic_crypto_client_stream_test.cc
@@ -0,0 +1,474 @@ +// Copyright (c) 2012 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/quic_crypto_client_stream.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.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_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h" + +using testing::_; + +namespace quic { +namespace test { +namespace { + +const char kServerHostname[] = "test.example.com"; +const uint16_t kServerPort = 443; + +class QuicCryptoClientStreamTest : public QuicTest { + public: + QuicCryptoClientStreamTest() + : supported_versions_(AllSupportedVersions()), + server_id_(kServerHostname, kServerPort, false), + crypto_config_(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()) { + CreateConnection(); + } + + void CreateConnection() { + connection_ = + new PacketSavingConnection(&client_helper_, &alarm_factory_, + Perspective::IS_CLIENT, supported_versions_); + // Advance the time, because timers do not like uninitialized times. + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + + session_ = QuicMakeUnique<TestQuicSpdyClientSession>( + connection_, DefaultQuicConfig(), supported_versions_, server_id_, + &crypto_config_); + } + + void CompleteCryptoHandshake() { + if (stream()->handshake_protocol() != PROTOCOL_TLS1_3) { + EXPECT_CALL(*session_, OnProofValid(testing::_)); + } + EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_)) + .Times(testing::AnyNumber()); + stream()->CryptoConnect(); + QuicConfig config; + crypto_test_utils::HandshakeWithFakeServer(&config, &server_helper_, + &alarm_factory_, connection_, + stream(), server_options_); + } + + QuicCryptoClientStream* stream() { + return session_->GetMutableCryptoStream(); + } + + MockQuicConnectionHelper server_helper_; + MockQuicConnectionHelper client_helper_; + MockAlarmFactory alarm_factory_; + PacketSavingConnection* connection_; + ParsedQuicVersionVector supported_versions_; + std::unique_ptr<TestQuicSpdyClientSession> session_; + QuicServerId server_id_; + CryptoHandshakeMessage message_; + QuicCryptoClientConfig crypto_config_; + crypto_test_utils::FakeServerOptions server_options_; +}; + +TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { + EXPECT_FALSE(stream()->encryption_established()); + EXPECT_FALSE(stream()->handshake_confirmed()); +} + +TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { + CompleteCryptoHandshake(); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->handshake_confirmed()); +} + +TEST_F(QuicCryptoClientStreamTest, ConnectedAfterTlsHandshake) { + FLAGS_quic_supports_tls_handshake = true; + supported_versions_.clear(); + for (QuicTransportVersion transport_version : + AllSupportedTransportVersions()) { + supported_versions_.push_back( + ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version)); + } + CreateConnection(); + CompleteCryptoHandshake(); + EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->handshake_confirmed()); +} + +TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { + CompleteCryptoHandshake(); + + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, _, _)); + message_.set_tag(kCHLO); + crypto_test_utils::SendHandshakeMessageToStream(stream(), message_, + Perspective::IS_CLIENT); +} + +TEST_F(QuicCryptoClientStreamTest, BadMessageType) { + stream()->CryptoConnect(); + + message_.set_tag(kCHLO); + + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected REJ", _)); + crypto_test_utils::SendHandshakeMessageToStream(stream(), message_, + Perspective::IS_CLIENT); +} + +TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { + CompleteCryptoHandshake(); + + const QuicConfig* config = session_->config(); + EXPECT_EQ(kMaximumIdleTimeoutSecs, config->IdleNetworkTimeout().ToSeconds()); + + const QuicCryptoNegotiatedParameters& crypto_params( + stream()->crypto_negotiated_params()); + EXPECT_EQ(crypto_config_.aead[0], crypto_params.aead); + EXPECT_EQ(crypto_config_.kexs[0], crypto_params.key_exchange); +} + +TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) { + // Seed the config with a cached server config. + CompleteCryptoHandshake(); + + // Recreate connection with the new config. + CreateConnection(); + + // Advance time 5 years to ensure that we pass the expiry time of the cached + // server config. + connection_->AdvanceTime( + QuicTime::Delta::FromSeconds(60 * 60 * 24 * 365 * 5)); + + EXPECT_CALL(*session_, OnProofValid(testing::_)); + stream()->CryptoConnect(); + // Check that a client hello was sent. + ASSERT_EQ(1u, connection_->encrypted_packets_.size()); + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); +} + +TEST_F(QuicCryptoClientStreamTest, ClockSkew) { + // Test that if the client's clock is skewed with respect to the server, + // the handshake succeeds. In the past, the client would get the server + // config, notice that it had already expired and then close the connection. + + // Advance time 5 years to ensure that we pass the expiry time in the server + // config, but the TTL is used instead. + connection_->AdvanceTime( + QuicTime::Delta::FromSeconds(60 * 60 * 24 * 365 * 5)); + + // The handshakes completes! + CompleteCryptoHandshake(); +} + +TEST_F(QuicCryptoClientStreamTest, InvalidCachedServerConfig) { + // Seed the config with a cached server config. + CompleteCryptoHandshake(); + + // Recreate connection with the new config. + CreateConnection(); + + QuicCryptoClientConfig::CachedState* state = + crypto_config_.LookupOrCreate(server_id_); + + std::vector<QuicString> certs = state->certs(); + QuicString cert_sct = state->cert_sct(); + QuicString signature = state->signature(); + QuicString chlo_hash = state->chlo_hash(); + state->SetProof(certs, cert_sct, chlo_hash, signature + signature); + + EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_)) + .Times(testing::AnyNumber()); + stream()->CryptoConnect(); + // Check that a client hello was sent. + ASSERT_EQ(1u, connection_->encrypted_packets_.size()); +} + +TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdate) { + // Test that the crypto client stream can receive server config updates after + // the connection has been established. + CompleteCryptoHandshake(); + + QuicCryptoClientConfig::CachedState* state = + crypto_config_.LookupOrCreate(server_id_); + + // Ensure cached STK is different to what we send in the handshake. + EXPECT_NE("xstk", state->source_address_token()); + + // Initialize using {...} syntax to avoid trailing \0 if converting from + // string. + unsigned char stk[] = {'x', 's', 't', 'k'}; + + // Minimum SCFG that passes config validation checks. + unsigned char scfg[] = {// SCFG + 0x53, 0x43, 0x46, 0x47, + // num entries + 0x01, 0x00, + // padding + 0x00, 0x00, + // EXPY + 0x45, 0x58, 0x50, 0x59, + // EXPY end offset + 0x08, 0x00, 0x00, 0x00, + // Value + '1', '2', '3', '4', '5', '6', '7', '8'}; + + CryptoHandshakeMessage server_config_update; + server_config_update.set_tag(kSCUP); + server_config_update.SetValue(kSourceAddressTokenTag, stk); + server_config_update.SetValue(kSCFG, scfg); + const uint64_t expiry_seconds = 60 * 60 * 24 * 2; + server_config_update.SetValue(kSTTL, expiry_seconds); + + crypto_test_utils::SendHandshakeMessageToStream( + stream(), server_config_update, Perspective::IS_SERVER); + + // Make sure that the STK and SCFG are cached correctly. + EXPECT_EQ("xstk", state->source_address_token()); + + const QuicString& cached_scfg = state->server_config(); + test::CompareCharArraysWithHexError( + "scfg", cached_scfg.data(), cached_scfg.length(), + reinterpret_cast<char*>(scfg), QUIC_ARRAYSIZE(scfg)); + + QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(stream()); + EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); +} + +TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateWithCert) { + // Test that the crypto client stream can receive and use server config + // updates with certificates after the connection has been established. + CompleteCryptoHandshake(); + + // Build a server config update message with certificates + QuicCryptoServerConfig crypto_config( + QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), + crypto_test_utils::ProofSourceForTesting(), KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()); + crypto_test_utils::FakeServerOptions options; + crypto_test_utils::SetupCryptoServerConfigForTest( + connection_->clock(), QuicRandom::GetInstance(), &crypto_config, options); + SourceAddressTokens tokens; + QuicCompressedCertsCache cache(1); + CachedNetworkParameters network_params; + CryptoHandshakeMessage server_config_update; + + class Callback : public BuildServerConfigUpdateMessageResultCallback { + public: + Callback(bool* ok, CryptoHandshakeMessage* message) + : ok_(ok), message_(message) {} + void Run(bool ok, const CryptoHandshakeMessage& message) override { + *ok_ = ok; + *message_ = message; + } + + private: + bool* ok_; + CryptoHandshakeMessage* message_; + }; + + // Note: relies on the callback being invoked synchronously + bool ok = false; + crypto_config.BuildServerConfigUpdateMessage( + session_->connection()->transport_version(), stream()->chlo_hash(), + tokens, QuicSocketAddress(QuicIpAddress::Loopback6(), 1234), + QuicIpAddress::Loopback6(), connection_->clock(), + QuicRandom::GetInstance(), &cache, stream()->crypto_negotiated_params(), + &network_params, + std::unique_ptr<BuildServerConfigUpdateMessageResultCallback>( + new Callback(&ok, &server_config_update))); + EXPECT_TRUE(ok); + + EXPECT_CALL(*session_, OnProofValid(testing::_)); + crypto_test_utils::SendHandshakeMessageToStream( + stream(), server_config_update, Perspective::IS_SERVER); + + // Recreate connection with the new config and verify a 0-RTT attempt. + CreateConnection(); + + EXPECT_CALL(*connection_, OnCanWrite()); + EXPECT_CALL(*session_, OnProofValid(testing::_)); + EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_)) + .Times(testing::AnyNumber()); + stream()->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); +} + +TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateBeforeHandshake) { + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE, _, _)); + CryptoHandshakeMessage server_config_update; + server_config_update.set_tag(kSCUP); + crypto_test_utils::SendHandshakeMessageToStream( + stream(), server_config_update, Perspective::IS_SERVER); +} + +TEST_F(QuicCryptoClientStreamTest, NoChannelID) { + crypto_config_.SetChannelIDSource(nullptr); + + CompleteCryptoHandshake(); + EXPECT_FALSE(stream()->WasChannelIDSent()); + EXPECT_FALSE(stream()->WasChannelIDSourceCallbackRun()); +} + +TEST_F(QuicCryptoClientStreamTest, PreferredVersion) { + // This mimics the case where client receives version negotiation packet, such + // that, the preferred version is different from the packets' version. + connection_ = new PacketSavingConnection( + &client_helper_, &alarm_factory_, Perspective::IS_CLIENT, + ParsedVersionOfIndex(supported_versions_, 1)); + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + + session_ = QuicMakeUnique<TestQuicSpdyClientSession>( + connection_, DefaultQuicConfig(), supported_versions_, server_id_, + &crypto_config_); + CompleteCryptoHandshake(); + // 2 CHLOs are sent. + ASSERT_EQ(2u, session_->sent_crypto_handshake_messages().size()); + // Verify preferred version is the highest version that session supports, and + // is different from connection's version. + QuicVersionLabel client_version_label; + EXPECT_EQ(QUIC_NO_ERROR, + session_->sent_crypto_handshake_messages()[0].GetVersionLabel( + kVER, &client_version_label)); + EXPECT_EQ(CreateQuicVersionLabel(supported_versions_[0]), + client_version_label); + EXPECT_EQ(QUIC_NO_ERROR, + session_->sent_crypto_handshake_messages()[1].GetVersionLabel( + kVER, &client_version_label)); + EXPECT_EQ(CreateQuicVersionLabel(supported_versions_[0]), + client_version_label); + EXPECT_NE(CreateQuicVersionLabel(connection_->version()), + client_version_label); +} + +class QuicCryptoClientStreamStatelessTest : public QuicTest { + public: + QuicCryptoClientStreamStatelessTest() + : client_crypto_config_(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()), + server_crypto_config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance(), + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + server_compressed_certs_cache_( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), + server_id_(kServerHostname, kServerPort, false) { + TestQuicSpdyClientSession* client_session = nullptr; + CreateClientSessionForTest(server_id_, + /* supports_stateless_rejects= */ true, + QuicTime::Delta::FromSeconds(100000), + AllSupportedVersions(), &helper_, + &alarm_factory_, &client_crypto_config_, + &client_connection_, &client_session); + CHECK(client_session); + client_session_.reset(client_session); + } + + QuicCryptoServerStream* server_stream() { + return server_session_->GetMutableCryptoStream(); + } + + void AdvanceHandshakeWithFakeServer() { + client_session_->GetMutableCryptoStream()->CryptoConnect(); + EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) + .Times(testing::AnyNumber()); + EXPECT_CALL(*server_session_->helper(), GenerateConnectionIdForReject(_, _)) + .Times(testing::AnyNumber()); + crypto_test_utils::AdvanceHandshake( + client_connection_, client_session_->GetMutableCryptoStream(), 0, + server_connection_, server_stream(), 0); + } + + // Initializes the server_stream_ for stateless rejects. + void InitializeFakeStatelessRejectServer() { + TestQuicSpdyServerSession* server_session = nullptr; + CreateServerSessionForTest( + server_id_, QuicTime::Delta::FromSeconds(100000), + ParsedVersionOfIndex(AllSupportedVersions(), 0), &helper_, + &alarm_factory_, &server_crypto_config_, + &server_compressed_certs_cache_, &server_connection_, &server_session); + CHECK(server_session); + server_session_.reset(server_session); + server_session_->OnSuccessfulVersionNegotiation(AllSupportedVersions()[0]); + crypto_test_utils::FakeServerOptions options; + crypto_test_utils::SetupCryptoServerConfigForTest( + server_connection_->clock(), server_connection_->random_generator(), + &server_crypto_config_, options); + SetQuicReloadableFlag(enable_quic_stateless_reject_support, true); + } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + + // Client crypto stream state + PacketSavingConnection* client_connection_; + std::unique_ptr<TestQuicSpdyClientSession> client_session_; + QuicCryptoClientConfig client_crypto_config_; + + // Server crypto stream state + PacketSavingConnection* server_connection_; + std::unique_ptr<TestQuicSpdyServerSession> server_session_; + QuicCryptoServerConfig server_crypto_config_; + QuicCompressedCertsCache server_compressed_certs_cache_; + QuicServerId server_id_; +}; + +TEST_F(QuicCryptoClientStreamStatelessTest, StatelessReject) { + SetQuicReloadableFlag(enable_quic_stateless_reject_support, true); + + QuicCryptoClientConfig::CachedState* client_state = + client_crypto_config_.LookupOrCreate(server_id_); + + EXPECT_FALSE(client_state->has_server_designated_connection_id()); + EXPECT_CALL(*client_session_, OnProofValid(testing::_)); + + InitializeFakeStatelessRejectServer(); + EXPECT_CALL(*client_connection_, + CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _)); + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _)); + AdvanceHandshakeWithFakeServer(); + + EXPECT_EQ(1, server_stream()->NumHandshakeMessages()); + EXPECT_EQ(0, server_stream()->NumHandshakeMessagesWithServerNonces()); + + EXPECT_FALSE(client_session_->IsEncryptionEstablished()); + EXPECT_FALSE(client_session_->IsCryptoHandshakeConfirmed()); + // Even though the handshake was not complete, the cached client_state is + // complete, and can be used for a subsequent successful handshake. + EXPECT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0))); + + ASSERT_TRUE(client_state->has_server_nonce()); + ASSERT_FALSE(client_state->GetNextServerNonce().empty()); + ASSERT_TRUE(client_state->has_server_designated_connection_id()); + QuicConnectionId server_designated_id = + client_state->GetNextServerDesignatedConnectionId(); + QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId( + server_session_->connection()->random_generator()); + EXPECT_EQ(expected_id, server_designated_id); + EXPECT_FALSE(client_state->has_server_designated_connection_id()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_crypto_handshaker.cc b/quic/core/quic_crypto_handshaker.cc new file mode 100644 index 0000000..fa0f78a --- /dev/null +++ b/quic/core/quic_crypto_handshaker.cc
@@ -0,0 +1,49 @@ +// Copyright (c) 2012 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/quic_crypto_handshaker.h" + +#include "net/third_party/quiche/src/quic/core/quic_session.h" + +namespace quic { + +#define ENDPOINT \ + (session()->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") + +QuicCryptoHandshaker::QuicCryptoHandshaker(QuicCryptoStream* stream, + QuicSession* session) + : stream_(stream), session_(session), last_sent_handshake_message_tag_(0) { + crypto_framer_.set_visitor(this); +} + +QuicCryptoHandshaker::~QuicCryptoHandshaker() {} + +void QuicCryptoHandshaker::SendHandshakeMessage( + const CryptoHandshakeMessage& message) { + QUIC_DVLOG(1) << ENDPOINT << "Sending " << message.DebugString(); + session()->NeuterUnencryptedData(); + session()->OnCryptoHandshakeMessageSent(message); + last_sent_handshake_message_tag_ = message.tag(); + const QuicData& data = message.GetSerialized(); + stream_->WriteCryptoData(session_->connection()->encryption_level(), + data.AsStringPiece()); +} + +void QuicCryptoHandshaker::OnError(CryptoFramer* framer) { + QUIC_DLOG(WARNING) << "Error processing crypto data: " + << QuicErrorCodeToString(framer->error()); +} + +void QuicCryptoHandshaker::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QUIC_DVLOG(1) << ENDPOINT << "Received " << message.DebugString(); + session()->OnCryptoHandshakeMessageReceived(message); +} + +CryptoMessageParser* QuicCryptoHandshaker::crypto_message_parser() { + return &crypto_framer_; +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_crypto_handshaker.h b/quic/core/quic_crypto_handshaker.h new file mode 100644 index 0000000..231acfc --- /dev/null +++ b/quic/core/quic_crypto_handshaker.h
@@ -0,0 +1,50 @@ +// Copyright (c) 2012 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_QUIC_CRYPTO_HANDSHAKER_H_ +#define QUICHE_QUIC_CORE_QUIC_CRYPTO_HANDSHAKER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicCryptoHandshaker + : public CryptoFramerVisitorInterface { + public: + QuicCryptoHandshaker(QuicCryptoStream* stream, QuicSession* session); + QuicCryptoHandshaker(const QuicCryptoHandshaker&) = delete; + QuicCryptoHandshaker& operator=(const QuicCryptoHandshaker&) = delete; + + ~QuicCryptoHandshaker() override; + + // Sends |message| to the peer. + // TODO(wtc): return a success/failure status. + void SendHandshakeMessage(const CryptoHandshakeMessage& message); + + void OnError(CryptoFramer* framer) override; + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; + + CryptoMessageParser* crypto_message_parser(); + + protected: + QuicTag last_sent_handshake_message_tag() const { + return last_sent_handshake_message_tag_; + } + + private: + QuicSession* session() { return session_; } + + QuicCryptoStream* stream_; + QuicSession* session_; + + CryptoFramer crypto_framer_; + + // Records last sent crypto handshake message tag. + QuicTag last_sent_handshake_message_tag_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_HANDSHAKER_H_
diff --git a/quic/core/quic_crypto_server_handshaker.cc b/quic/core/quic_crypto_server_handshaker.cc new file mode 100644 index 0000000..66f3e12 --- /dev/null +++ b/quic/core/quic_crypto_server_handshaker.cc
@@ -0,0 +1,471 @@ +// Copyright (c) 2012 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/quic_crypto_server_handshaker.h" + +#include <memory> + +#include "third_party/boringssl/src/include/openssl/sha.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +class QuicCryptoServerHandshaker::ProcessClientHelloCallback + : public ProcessClientHelloResultCallback { + public: + ProcessClientHelloCallback( + QuicCryptoServerHandshaker* parent, + const QuicReferenceCountedPointer< + ValidateClientHelloResultCallback::Result>& result) + : parent_(parent), result_(result) {} + + void Run( + QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> proof_source_details) override { + if (parent_ == nullptr) { + return; + } + + parent_->FinishProcessingHandshakeMessageAfterProcessClientHello( + *result_, error, error_details, std::move(message), + std::move(diversification_nonce), std::move(proof_source_details)); + } + + void Cancel() { parent_ = nullptr; } + + private: + QuicCryptoServerHandshaker* parent_; + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result_; +}; + +QuicCryptoServerHandshaker::QuicCryptoServerHandshaker( + const QuicCryptoServerConfig* crypto_config, + QuicCryptoServerStream* stream, + QuicCompressedCertsCache* compressed_certs_cache, + QuicSession* session, + QuicCryptoServerStream::Helper* helper) + : QuicCryptoHandshaker(stream, session), + stream_(stream), + session_(session), + crypto_config_(crypto_config), + compressed_certs_cache_(compressed_certs_cache), + signed_config_(new QuicSignedServerConfig), + helper_(helper), + num_handshake_messages_(0), + num_handshake_messages_with_server_nonces_(0), + send_server_config_update_cb_(nullptr), + num_server_config_update_messages_sent_(0), + zero_rtt_attempted_(false), + chlo_packet_size_(0), + validate_client_hello_cb_(nullptr), + process_client_hello_cb_(nullptr), + encryption_established_(false), + handshake_confirmed_(false), + crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {} + +QuicCryptoServerHandshaker::~QuicCryptoServerHandshaker() { + CancelOutstandingCallbacks(); +} + +void QuicCryptoServerHandshaker::CancelOutstandingCallbacks() { + // Detach from the validation callback. Calling this multiple times is safe. + if (validate_client_hello_cb_ != nullptr) { + validate_client_hello_cb_->Cancel(); + validate_client_hello_cb_ = nullptr; + } + if (send_server_config_update_cb_ != nullptr) { + send_server_config_update_cb_->Cancel(); + send_server_config_update_cb_ = nullptr; + } + if (process_client_hello_cb_ != nullptr) { + process_client_hello_cb_->Cancel(); + process_client_hello_cb_ = nullptr; + } +} + +void QuicCryptoServerHandshaker::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QuicCryptoHandshaker::OnHandshakeMessage(message); + ++num_handshake_messages_; + chlo_packet_size_ = session()->connection()->GetCurrentPacket().length(); + + // Do not process handshake messages after the handshake is confirmed. + if (handshake_confirmed_) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, + "Unexpected handshake message from client"); + return; + } + + if (message.tag() != kCHLO) { + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Handshake packet not CHLO"); + return; + } + + if (validate_client_hello_cb_ != nullptr || + process_client_hello_cb_ != nullptr) { + // Already processing some other handshake message. The protocol + // does not allow for clients to send multiple handshake messages + // before the server has a chance to respond. + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO, + "Unexpected handshake message while processing CHLO"); + return; + } + + CryptoUtils::HashHandshakeMessage(message, &chlo_hash_, + Perspective::IS_SERVER); + + std::unique_ptr<ValidateCallback> cb(new ValidateCallback(this)); + DCHECK(validate_client_hello_cb_ == nullptr); + DCHECK(process_client_hello_cb_ == nullptr); + validate_client_hello_cb_ = cb.get(); + crypto_config_->ValidateClientHello( + message, GetClientAddress().host(), + session()->connection()->self_address(), transport_version(), + session()->connection()->clock(), signed_config_, std::move(cb)); +} + +void QuicCryptoServerHandshaker::FinishProcessingHandshakeMessage( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> details) { + const CryptoHandshakeMessage& message = result->client_hello; + + // Clear the callback that got us here. + DCHECK(validate_client_hello_cb_ != nullptr); + DCHECK(process_client_hello_cb_ == nullptr); + validate_client_hello_cb_ = nullptr; + + if (stream_->UseStatelessRejectsIfPeerSupported()) { + stream_->SetPeerSupportsStatelessRejects( + QuicCryptoServerStreamBase::DoesPeerSupportStatelessRejects(message)); + } + + std::unique_ptr<ProcessClientHelloCallback> cb( + new ProcessClientHelloCallback(this, result)); + process_client_hello_cb_ = cb.get(); + ProcessClientHello(result, std::move(details), std::move(cb)); +} + +void QuicCryptoServerHandshaker:: + FinishProcessingHandshakeMessageAfterProcessClientHello( + const ValidateClientHelloResultCallback::Result& result, + QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> reply, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> proof_source_details) { + // Clear the callback that got us here. + DCHECK(process_client_hello_cb_ != nullptr); + DCHECK(validate_client_hello_cb_ == nullptr); + process_client_hello_cb_ = nullptr; + + const CryptoHandshakeMessage& message = result.client_hello; + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails(error, error_details); + return; + } + + if (reply->tag() != kSHLO) { + if (reply->tag() == kSREJ) { + DCHECK(stream_->UseStatelessRejectsIfPeerSupported()); + DCHECK(stream_->PeerSupportsStatelessRejects()); + // Before sending the SREJ, cause the connection to save crypto packets + // so that they can be added to the time wait list manager and + // retransmitted. + session()->connection()->EnableSavingCryptoPackets(); + } + session()->connection()->set_fully_pad_crypto_hadshake_packets( + crypto_config_->pad_rej()); + SendHandshakeMessage(*reply); + + if (reply->tag() == kSREJ) { + DCHECK(stream_->UseStatelessRejectsIfPeerSupported()); + DCHECK(stream_->PeerSupportsStatelessRejects()); + DCHECK(!handshake_confirmed()); + QUIC_DLOG(INFO) << "Closing connection " + << session()->connection()->connection_id() + << " because of a stateless reject."; + session()->connection()->CloseConnection( + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject", + ConnectionCloseBehavior::SILENT_CLOSE); + } + return; + } + + // If we are returning a SHLO then we accepted the handshake. Now + // process the negotiated configuration options as part of the + // session config. + QuicConfig* config = session()->config(); + OverrideQuicConfigDefaults(config); + QuicString process_error_details; + const QuicErrorCode process_error = + config->ProcessPeerHello(message, CLIENT, &process_error_details); + if (process_error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails(process_error, process_error_details); + return; + } + + session()->OnConfigNegotiated(); + + config->ToHandshakeMessage(reply.get()); + + // Receiving a full CHLO implies the client is prepared to decrypt with + // the new server write key. We can start to encrypt with the new server + // write key. + // + // NOTE: the SHLO will be encrypted with the new server write key. + session()->connection()->SetEncrypter( + ENCRYPTION_ZERO_RTT, + std::move(crypto_negotiated_params_->initial_crypters.encrypter)); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + // Set the decrypter immediately so that we no longer accept unencrypted + // packets. + session()->connection()->SetDecrypter( + ENCRYPTION_ZERO_RTT, + std::move(crypto_negotiated_params_->initial_crypters.decrypter)); + session()->connection()->SetDiversificationNonce(*diversification_nonce); + + session()->connection()->set_fully_pad_crypto_hadshake_packets( + crypto_config_->pad_shlo()); + SendHandshakeMessage(*reply); + + session()->connection()->SetEncrypter( + ENCRYPTION_FORWARD_SECURE, + std::move(crypto_negotiated_params_->forward_secure_crypters.encrypter)); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + + session()->connection()->SetAlternativeDecrypter( + ENCRYPTION_FORWARD_SECURE, + std::move(crypto_negotiated_params_->forward_secure_crypters.decrypter), + false /* don't latch */); + + encryption_established_ = true; + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); +} + +void QuicCryptoServerHandshaker::SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) { + if (!handshake_confirmed_) { + return; + } + + if (send_server_config_update_cb_ != nullptr) { + QUIC_DVLOG(1) + << "Skipped server config update since one is already in progress"; + return; + } + + std::unique_ptr<SendServerConfigUpdateCallback> cb( + new SendServerConfigUpdateCallback(this)); + send_server_config_update_cb_ = cb.get(); + + crypto_config_->BuildServerConfigUpdateMessage( + session()->connection()->transport_version(), chlo_hash_, + previous_source_address_tokens_, session()->connection()->self_address(), + GetClientAddress().host(), session()->connection()->clock(), + session()->connection()->random_generator(), compressed_certs_cache_, + *crypto_negotiated_params_, cached_network_params, std::move(cb)); +} + +QuicCryptoServerHandshaker::SendServerConfigUpdateCallback:: + SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent) + : parent_(parent) {} + +void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Cancel() { + parent_ = nullptr; +} + +// From BuildServerConfigUpdateMessageResultCallback +void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Run( + bool ok, + const CryptoHandshakeMessage& message) { + if (parent_ == nullptr) { + return; + } + parent_->FinishSendServerConfigUpdate(ok, message); +} + +void QuicCryptoServerHandshaker::FinishSendServerConfigUpdate( + bool ok, + const CryptoHandshakeMessage& message) { + // Clear the callback that got us here. + DCHECK(send_server_config_update_cb_ != nullptr); + send_server_config_update_cb_ = nullptr; + + if (!ok) { + QUIC_DVLOG(1) << "Server: Failed to build server config update (SCUP)!"; + return; + } + + QUIC_DVLOG(1) << "Server: Sending server config update: " + << message.DebugString(); + if (transport_version() < QUIC_VERSION_47) { + const QuicData& data = message.GetSerialized(); + stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()), + false, nullptr); + } else { + SendHandshakeMessage(message); + } + + ++num_server_config_update_messages_sent_; +} + +uint8_t QuicCryptoServerHandshaker::NumHandshakeMessages() const { + return num_handshake_messages_; +} + +uint8_t QuicCryptoServerHandshaker::NumHandshakeMessagesWithServerNonces() + const { + return num_handshake_messages_with_server_nonces_; +} + +int QuicCryptoServerHandshaker::NumServerConfigUpdateMessagesSent() const { + return num_server_config_update_messages_sent_; +} + +const CachedNetworkParameters* +QuicCryptoServerHandshaker::PreviousCachedNetworkParams() const { + return previous_cached_network_params_.get(); +} + +bool QuicCryptoServerHandshaker::ZeroRttAttempted() const { + return zero_rtt_attempted_; +} + +void QuicCryptoServerHandshaker::SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) { + previous_cached_network_params_.reset( + new CachedNetworkParameters(cached_network_params)); +} + +bool QuicCryptoServerHandshaker::ShouldSendExpectCTHeader() const { + return signed_config_->proof.send_expect_ct_header; +} + +bool QuicCryptoServerHandshaker::GetBase64SHA256ClientChannelID( + QuicString* output) const { + if (!encryption_established() || + crypto_negotiated_params_->channel_id.empty()) { + return false; + } + + const QuicString& channel_id(crypto_negotiated_params_->channel_id); + uint8_t digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast<const uint8_t*>(channel_id.data()), channel_id.size(), + digest); + + QuicTextUtils::Base64Encode(digest, QUIC_ARRAYSIZE(digest), output); + return true; +} + +bool QuicCryptoServerHandshaker::encryption_established() const { + return encryption_established_; +} + +bool QuicCryptoServerHandshaker::handshake_confirmed() const { + return handshake_confirmed_; +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoServerHandshaker::crypto_negotiated_params() const { + return *crypto_negotiated_params_; +} + +CryptoMessageParser* QuicCryptoServerHandshaker::crypto_message_parser() { + return QuicCryptoHandshaker::crypto_message_parser(); +} + +void QuicCryptoServerHandshaker::ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> proof_source_details, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) { + const CryptoHandshakeMessage& message = result->client_hello; + QuicString error_details; + if (!helper_->CanAcceptClientHello( + message, GetClientAddress(), session()->connection()->peer_address(), + session()->connection()->self_address(), &error_details)) { + done_cb->Run(QUIC_HANDSHAKE_FAILED, error_details, nullptr, nullptr, + nullptr); + return; + } + if (!result->info.server_nonce.empty()) { + ++num_handshake_messages_with_server_nonces_; + } + + if (num_handshake_messages_ == 1) { + // Client attempts zero RTT handshake by sending a non-inchoate CHLO. + QuicStringPiece public_value; + zero_rtt_attempted_ = message.GetStringPiece(kPUBS, &public_value); + } + + // Store the bandwidth estimate from the client. + if (result->cached_network_params.bandwidth_estimate_bytes_per_second() > 0) { + previous_cached_network_params_.reset( + new CachedNetworkParameters(result->cached_network_params)); + } + previous_source_address_tokens_ = result->info.source_address_tokens; + + const bool use_stateless_rejects_in_crypto_config = + stream_->UseStatelessRejectsIfPeerSupported() && + stream_->PeerSupportsStatelessRejects(); + QuicConnection* connection = session()->connection(); + const QuicConnectionId server_designated_connection_id = + GenerateConnectionIdForReject(use_stateless_rejects_in_crypto_config); + crypto_config_->ProcessClientHello( + result, /*reject_only=*/false, connection->connection_id(), + connection->self_address(), GetClientAddress(), connection->version(), + session()->supported_versions(), use_stateless_rejects_in_crypto_config, + server_designated_connection_id, connection->clock(), + connection->random_generator(), compressed_certs_cache_, + crypto_negotiated_params_, signed_config_, + QuicCryptoStream::CryptoMessageFramingOverhead( + transport_version(), connection->connection_id()), + chlo_packet_size_, std::move(done_cb)); +} + +void QuicCryptoServerHandshaker::OverrideQuicConfigDefaults( + QuicConfig* config) {} + +QuicCryptoServerHandshaker::ValidateCallback::ValidateCallback( + QuicCryptoServerHandshaker* parent) + : parent_(parent) {} + +void QuicCryptoServerHandshaker::ValidateCallback::Cancel() { + parent_ = nullptr; +} + +void QuicCryptoServerHandshaker::ValidateCallback::Run( + QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> details) { + if (parent_ != nullptr) { + parent_->FinishProcessingHandshakeMessage(std::move(result), + std::move(details)); + } +} + +QuicConnectionId QuicCryptoServerHandshaker::GenerateConnectionIdForReject( + bool use_stateless_rejects) { + if (!use_stateless_rejects) { + return EmptyQuicConnectionId(); + } + return helper_->GenerateConnectionIdForReject( + transport_version(), session()->connection()->connection_id()); +} + +const QuicSocketAddress QuicCryptoServerHandshaker::GetClientAddress() { + return session()->connection()->peer_address(); +} + +} // namespace quic
diff --git a/quic/core/quic_crypto_server_handshaker.h b/quic/core/quic_crypto_server_handshaker.h new file mode 100644 index 0000000..a1ec99f --- /dev/null +++ b/quic/core/quic_crypto_server_handshaker.h
@@ -0,0 +1,238 @@ +// Copyright (c) 2012 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_QUIC_CRYPTO_SERVER_HANDSHAKER_H_ +#define QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_ + +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/proto/source_address_token.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace test { +class QuicCryptoServerStreamPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE QuicCryptoServerHandshaker + : public QuicCryptoServerStream::HandshakerDelegate, + public QuicCryptoHandshaker { + public: + // |crypto_config| must outlive the stream. + // |session| must outlive the stream. + // |helper| must outlive the stream. + QuicCryptoServerHandshaker(const QuicCryptoServerConfig* crypto_config, + QuicCryptoServerStream* stream, + QuicCompressedCertsCache* compressed_certs_cache, + QuicSession* session, + QuicCryptoServerStream::Helper* helper); + QuicCryptoServerHandshaker(const QuicCryptoServerHandshaker&) = delete; + QuicCryptoServerHandshaker& operator=(const QuicCryptoServerHandshaker&) = + delete; + + ~QuicCryptoServerHandshaker() override; + + // From HandshakerDelegate + void CancelOutstandingCallbacks() override; + bool GetBase64SHA256ClientChannelID(QuicString* output) const override; + void SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) override; + uint8_t NumHandshakeMessages() const override; + uint8_t NumHandshakeMessagesWithServerNonces() const override; + int NumServerConfigUpdateMessagesSent() const override; + const CachedNetworkParameters* PreviousCachedNetworkParams() const override; + bool ZeroRttAttempted() const override; + void SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) override; + bool ShouldSendExpectCTHeader() const override; + + // From QuicCryptoStream + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + + // From QuicCryptoHandshaker + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; + + protected: + virtual void ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> proof_source_details, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb); + + // Hook that allows the server to set QuicConfig defaults just + // before going through the parameter negotiation step. + virtual void OverrideQuicConfigDefaults(QuicConfig* config); + + // Returns client address used to generate and validate source address token. + virtual const QuicSocketAddress GetClientAddress(); + + // Returns the QuicSession that this stream belongs to. + QuicSession* session() const { return session_; } + + void set_encryption_established(bool encryption_established) { + encryption_established_ = encryption_established; + } + + void set_handshake_confirmed(bool handshake_confirmed) { + handshake_confirmed_ = handshake_confirmed; + } + + private: + friend class test::QuicCryptoServerStreamPeer; + + class ValidateCallback : public ValidateClientHelloResultCallback { + public: + explicit ValidateCallback(QuicCryptoServerHandshaker* parent); + ValidateCallback(const ValidateCallback&) = delete; + ValidateCallback& operator=(const ValidateCallback&) = delete; + // To allow the parent to detach itself from the callback before deletion. + void Cancel(); + + // From ValidateClientHelloResultCallback + void Run(QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> details) override; + + private: + QuicCryptoServerHandshaker* parent_; + }; + + class SendServerConfigUpdateCallback + : public BuildServerConfigUpdateMessageResultCallback { + public: + explicit SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent); + SendServerConfigUpdateCallback(const SendServerConfigUpdateCallback&) = + delete; + void operator=(const SendServerConfigUpdateCallback&) = delete; + + // To allow the parent to detach itself from the callback before deletion. + void Cancel(); + + // From BuildServerConfigUpdateMessageResultCallback + void Run(bool ok, const CryptoHandshakeMessage& message) override; + + private: + QuicCryptoServerHandshaker* parent_; + }; + + // Invoked by ValidateCallback::RunImpl once initial validation of + // the client hello is complete. Finishes processing of the client + // hello message and handles handshake success/failure. + void FinishProcessingHandshakeMessage( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> details); + + class ProcessClientHelloCallback; + friend class ProcessClientHelloCallback; + + // Portion of FinishProcessingHandshakeMessage which executes after + // ProcessClientHello has been called. + void FinishProcessingHandshakeMessageAfterProcessClientHello( + const ValidateClientHelloResultCallback::Result& result, + QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> reply, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> proof_source_details); + + // Invoked by SendServerConfigUpdateCallback::RunImpl once the proof has been + // received. |ok| indicates whether or not the proof was successfully + // acquired, and |message| holds the partially-constructed message from + // SendServerConfigUpdate. + void FinishSendServerConfigUpdate(bool ok, + const CryptoHandshakeMessage& message); + + // Returns a new ConnectionId to be used for statelessly rejected connections + // if |use_stateless_rejects| is true. Returns 0 otherwise. + QuicConnectionId GenerateConnectionIdForReject(bool use_stateless_rejects); + + // Returns the QuicTransportVersion of the connection. + QuicTransportVersion transport_version() const { + return session_->connection()->transport_version(); + } + + QuicCryptoServerStream* stream_; + + QuicSession* session_; + + // crypto_config_ contains crypto parameters for the handshake. + const QuicCryptoServerConfig* crypto_config_; + + // compressed_certs_cache_ contains a set of most recently compressed certs. + // Owned by QuicDispatcher. + QuicCompressedCertsCache* compressed_certs_cache_; + + // Server's certificate chain and signature of the server config, as provided + // by ProofSource::GetProof. + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + + // Hash of the last received CHLO message which can be used for generating + // server config update messages. + QuicString chlo_hash_; + + // Pointer to the helper for this crypto stream. Must outlive this stream. + QuicCryptoServerStream::Helper* helper_; + + // Number of handshake messages received by this stream. + uint8_t num_handshake_messages_; + + // Number of handshake messages received by this stream that contain + // server nonces (indicating that this is a non-zero-RTT handshake + // attempt). + uint8_t num_handshake_messages_with_server_nonces_; + + // Pointer to the active callback that will receive the result of + // BuildServerConfigUpdateMessage and forward it to + // FinishSendServerConfigUpdate. nullptr if no update message is currently + // being built. + SendServerConfigUpdateCallback* send_server_config_update_cb_; + + // Number of server config update (SCUP) messages sent by this stream. + int num_server_config_update_messages_sent_; + + // If the client provides CachedNetworkParameters in the STK in the CHLO, then + // store here, and send back in future STKs if we have no better bandwidth + // estimate to send. + std::unique_ptr<CachedNetworkParameters> previous_cached_network_params_; + + // Contains any source address tokens which were present in the CHLO. + SourceAddressTokens previous_source_address_tokens_; + + // True if client attempts 0-rtt handshake (which can succeed or fail). If + // stateless rejects are used, this variable will be false for the stateless + // rejected connection and true for subsequent connections. + bool zero_rtt_attempted_; + + // Size of the packet containing the most recently received CHLO. + QuicByteCount chlo_packet_size_; + + // Pointer to the active callback that will receive the result of the client + // hello validation request and forward it to FinishProcessingHandshakeMessage + // for processing. nullptr if no handshake message is being validated. Note + // that this field is mutually exclusive with process_client_hello_cb_. + ValidateCallback* validate_client_hello_cb_; + + // Pointer to the active callback which will receive the results of + // ProcessClientHello and forward it to + // FinishProcessingHandshakeMessageAfterProcessClientHello. Note that this + // field is mutually exclusive with validate_client_hello_cb_. + ProcessClientHelloCallback* process_client_hello_cb_; + + bool encryption_established_; + bool handshake_confirmed_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> + crypto_negotiated_params_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_
diff --git a/quic/core/quic_crypto_server_stream.cc b/quic/core/quic_crypto_server_stream.cc new file mode 100644 index 0000000..aa75c66 --- /dev/null +++ b/quic/core/quic_crypto_server_stream.cc
@@ -0,0 +1,174 @@ +// Copyright (c) 2012 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/quic_crypto_server_stream.h" + +#include <memory> + +#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_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +QuicCryptoServerStreamBase::QuicCryptoServerStreamBase(QuicSession* session) + : QuicCryptoStream(session) {} + +// TODO(jokulik): Once stateless rejects support is inherent in the version +// number, this function will likely go away entirely. +// static +bool QuicCryptoServerStreamBase::DoesPeerSupportStatelessRejects( + const CryptoHandshakeMessage& message) { + QuicTagVector received_tags; + QuicErrorCode error = message.GetTaglist(kCOPT, &received_tags); + if (error != QUIC_NO_ERROR) { + return false; + } + for (const QuicTag tag : received_tags) { + if (tag == kSREJ) { + return true; + } + } + return false; +} + +QuicCryptoServerStream::QuicCryptoServerStream( + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + bool use_stateless_rejects_if_peer_supported, + QuicSession* session, + Helper* helper) + : QuicCryptoServerStreamBase(session), + use_stateless_rejects_if_peer_supported_( + use_stateless_rejects_if_peer_supported), + peer_supports_stateless_rejects_(false), + crypto_config_(crypto_config), + compressed_certs_cache_(compressed_certs_cache), + helper_(helper) { + DCHECK_EQ(Perspective::IS_SERVER, session->connection()->perspective()); +} + +QuicCryptoServerStream::~QuicCryptoServerStream() {} + +void QuicCryptoServerStream::CancelOutstandingCallbacks() { + if (handshaker()) { + handshaker()->CancelOutstandingCallbacks(); + } +} + +bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID( + QuicString* output) const { + return handshaker()->GetBase64SHA256ClientChannelID(output); +} + +void QuicCryptoServerStream::SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) { + handshaker()->SendServerConfigUpdate(cached_network_params); +} + +uint8_t QuicCryptoServerStream::NumHandshakeMessages() const { + return handshaker()->NumHandshakeMessages(); +} + +uint8_t QuicCryptoServerStream::NumHandshakeMessagesWithServerNonces() const { + return handshaker()->NumHandshakeMessagesWithServerNonces(); +} + +int QuicCryptoServerStream::NumServerConfigUpdateMessagesSent() const { + return handshaker()->NumServerConfigUpdateMessagesSent(); +} + +const CachedNetworkParameters* +QuicCryptoServerStream::PreviousCachedNetworkParams() const { + return handshaker()->PreviousCachedNetworkParams(); +} + +bool QuicCryptoServerStream::UseStatelessRejectsIfPeerSupported() const { + return use_stateless_rejects_if_peer_supported_; +} + +bool QuicCryptoServerStream::PeerSupportsStatelessRejects() const { + return peer_supports_stateless_rejects_; +} + +bool QuicCryptoServerStream::ZeroRttAttempted() const { + return handshaker()->ZeroRttAttempted(); +} + +void QuicCryptoServerStream::SetPeerSupportsStatelessRejects( + bool peer_supports_stateless_rejects) { + peer_supports_stateless_rejects_ = peer_supports_stateless_rejects; +} + +void QuicCryptoServerStream::SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) { + handshaker()->SetPreviousCachedNetworkParams(cached_network_params); +} + +bool QuicCryptoServerStream::ShouldSendExpectCTHeader() const { + return handshaker()->ShouldSendExpectCTHeader(); +} + +bool QuicCryptoServerStream::encryption_established() const { + if (!handshaker()) { + return false; + } + return handshaker()->encryption_established(); +} + +bool QuicCryptoServerStream::handshake_confirmed() const { + if (!handshaker()) { + return false; + } + return handshaker()->handshake_confirmed(); +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoServerStream::crypto_negotiated_params() const { + return handshaker()->crypto_negotiated_params(); +} + +CryptoMessageParser* QuicCryptoServerStream::crypto_message_parser() { + return handshaker()->crypto_message_parser(); +} + +void QuicCryptoServerStream::OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) { + DCHECK_EQ(version, session()->connection()->version()); + CHECK(!handshaker_); + switch (session()->connection()->version().handshake_protocol) { + case PROTOCOL_QUIC_CRYPTO: + handshaker_ = QuicMakeUnique<QuicCryptoServerHandshaker>( + crypto_config_, this, compressed_certs_cache_, session(), helper_); + break; + case PROTOCOL_TLS1_3: + handshaker_ = QuicMakeUnique<TlsServerHandshaker>( + this, session(), crypto_config_->ssl_ctx(), + crypto_config_->proof_source()); + break; + case PROTOCOL_UNSUPPORTED: + QUIC_BUG << "Attempting to create QuicCryptoServerStream for unknown " + "handshake protocol"; + } +} + +QuicCryptoServerStream::HandshakerDelegate* QuicCryptoServerStream::handshaker() + const { + return handshaker_.get(); +} + +} // namespace quic
diff --git a/quic/core/quic_crypto_server_stream.h b/quic/core/quic_crypto_server_stream.h new file mode 100644 index 0000000..53d4b38 --- /dev/null +++ b/quic/core/quic_crypto_server_stream.h
@@ -0,0 +1,225 @@ +// Copyright (c) 2012 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_QUIC_CRYPTO_SERVER_STREAM_H_ +#define QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_STREAM_H_ + +#include <cstdint> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class CachedNetworkParameters; +class CryptoHandshakeMessage; +class QuicCryptoServerConfig; +class QuicCryptoServerStreamBase; + +// TODO(alyssar) see what can be moved out of QuicCryptoServerStream with +// various code and test refactoring. +class QUIC_EXPORT_PRIVATE QuicCryptoServerStreamBase : public QuicCryptoStream { + public: + explicit QuicCryptoServerStreamBase(QuicSession* session); + + ~QuicCryptoServerStreamBase() override {} + + // Cancel any outstanding callbacks, such as asynchronous validation of client + // hello. + virtual void CancelOutstandingCallbacks() = 0; + + // GetBase64SHA256ClientChannelID sets |*output| to the base64 encoded, + // SHA-256 hash of the client's ChannelID key and returns true, if the client + // presented a ChannelID. Otherwise it returns false. + virtual bool GetBase64SHA256ClientChannelID(QuicString* output) const = 0; + + virtual int NumServerConfigUpdateMessagesSent() const = 0; + + // Sends the latest server config and source-address token to the client. + virtual void SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) = 0; + + // These are all accessors and setters to their respective counters. + virtual uint8_t NumHandshakeMessages() const = 0; + virtual uint8_t NumHandshakeMessagesWithServerNonces() const = 0; + virtual bool UseStatelessRejectsIfPeerSupported() const = 0; + virtual bool PeerSupportsStatelessRejects() const = 0; + virtual bool ZeroRttAttempted() const = 0; + virtual void SetPeerSupportsStatelessRejects(bool set) = 0; + virtual const CachedNetworkParameters* PreviousCachedNetworkParams() + const = 0; + virtual void SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) = 0; + + // Checks the options on the handshake-message to see whether the + // peer supports stateless-rejects. + static bool DoesPeerSupportStatelessRejects( + const CryptoHandshakeMessage& message); +}; + +class QUIC_EXPORT_PRIVATE QuicCryptoServerStream + : public QuicCryptoServerStreamBase { + public: + // QuicCryptoServerStream creates a HandshakerDelegate at construction time + // based on the QuicTransportVersion of the connection. Different + // HandshakerDelegates provide implementations of different crypto handshake + // protocols. Currently QUIC crypto is the only protocol implemented; a future + // HandshakerDelegate will use TLS as the handshake protocol. + // QuicCryptoServerStream delegates all of its public methods to its + // HandshakerDelegate. + // + // This setup of the crypto stream delegating its implementation to the + // handshaker results in the handshaker reading and writing bytes on the + // crypto stream, instead of the handshake rpassing the stream bytes to send. + class QUIC_EXPORT_PRIVATE HandshakerDelegate { + public: + virtual ~HandshakerDelegate() {} + + // Cancel any outstanding callbacks, such as asynchronous validation of + // client hello. + virtual void CancelOutstandingCallbacks() = 0; + + // GetBase64SHA256ClientChannelID sets |*output| to the base64 encoded, + // SHA-256 hash of the client's ChannelID key and returns true, if the + // client presented a ChannelID. Otherwise it returns false. + virtual bool GetBase64SHA256ClientChannelID(QuicString* output) const = 0; + + // Sends the latest server config and source-address token to the client. + virtual void SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) = 0; + + // These are all accessors and setters to their respective counters. + virtual uint8_t NumHandshakeMessages() const = 0; + virtual uint8_t NumHandshakeMessagesWithServerNonces() const = 0; + virtual int NumServerConfigUpdateMessagesSent() const = 0; + virtual const CachedNetworkParameters* PreviousCachedNetworkParams() + const = 0; + virtual bool ZeroRttAttempted() const = 0; + virtual void SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) = 0; + + // NOTE: Indicating that the Expect-CT header should be sent here presents a + // layering violation to some extent. The Expect-CT header only applies to + // HTTP connections, while this class can be used for non-HTTP applications. + // However, it is exposed here because that is the only place where the + // configuration for the certificate used in the connection is accessible. + virtual bool ShouldSendExpectCTHeader() const = 0; + + // Returns true once any encrypter (initial/0RTT or final/1RTT) has been set + // for the connection. + virtual bool encryption_established() const = 0; + + // Returns true once the crypto handshake has completed. + virtual bool handshake_confirmed() const = 0; + + // Returns the parameters negotiated in the crypto handshake. + virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const = 0; + + // Used by QuicCryptoStream to parse data received on this stream. + virtual CryptoMessageParser* crypto_message_parser() = 0; + }; + + class Helper { + public: + virtual ~Helper() {} + + // Given the current connection_id, generates a new ConnectionId to + // be returned with a stateless reject. + virtual QuicConnectionId GenerateConnectionIdForReject( + QuicTransportVersion version, + QuicConnectionId connection_id) const = 0; + + // Returns true if |message|, which was received on |self_address| is + // acceptable according to the visitor's policy. Otherwise, returns false + // and populates |error_details|. + virtual bool CanAcceptClientHello(const CryptoHandshakeMessage& message, + const QuicSocketAddress& client_address, + const QuicSocketAddress& peer_address, + const QuicSocketAddress& self_address, + QuicString* error_details) const = 0; + }; + + // |crypto_config| must outlive the stream. + // |session| must outlive the stream. + // |helper| must outlive the stream. + QuicCryptoServerStream(const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + bool use_stateless_rejects_if_peer_supported, + QuicSession* session, + Helper* helper); + QuicCryptoServerStream(const QuicCryptoServerStream&) = delete; + QuicCryptoServerStream& operator=(const QuicCryptoServerStream&) = delete; + + ~QuicCryptoServerStream() override; + + // From QuicCryptoServerStreamBase + void CancelOutstandingCallbacks() override; + bool GetBase64SHA256ClientChannelID(QuicString* output) const override; + void SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) override; + uint8_t NumHandshakeMessages() const override; + uint8_t NumHandshakeMessagesWithServerNonces() const override; + int NumServerConfigUpdateMessagesSent() const override; + const CachedNetworkParameters* PreviousCachedNetworkParams() const override; + bool UseStatelessRejectsIfPeerSupported() const override; + bool PeerSupportsStatelessRejects() const override; + bool ZeroRttAttempted() const override; + void SetPeerSupportsStatelessRejects( + bool peer_supports_stateless_rejects) override; + void SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) override; + + // NOTE: Indicating that the Expect-CT header should be sent here presents + // a layering violation to some extent. The Expect-CT header only applies to + // HTTP connections, while this class can be used for non-HTTP applications. + // However, it is exposed here because that is the only place where the + // configuration for the certificate used in the connection is accessible. + bool ShouldSendExpectCTHeader() const; + + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + void OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) override; + + protected: + // Provided so that subclasses can provide their own handshaker. + virtual HandshakerDelegate* handshaker() const; + + private: + std::unique_ptr<HandshakerDelegate> handshaker_; + + // If true, the server should use stateless rejects, so long as the + // client supports them, as indicated by + // peer_supports_stateless_rejects_. + bool use_stateless_rejects_if_peer_supported_; + + // Set to true, once the server has received information from the + // client that it supports stateless reject. + // TODO(jokulik): Remove once client stateless reject support + // becomes the default. + bool peer_supports_stateless_rejects_; + + // Arguments from QuicCryptoServerStream constructor that might need to be + // passed to the HandshakerDelegate constructor in its late construction. + const QuicCryptoServerConfig* crypto_config_; + QuicCompressedCertsCache* compressed_certs_cache_; + Helper* helper_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_STREAM_H_
diff --git a/quic/core/quic_crypto_server_stream_test.cc b/quic/core/quic_crypto_server_stream_test.cc new file mode 100644 index 0000000..660c588 --- /dev/null +++ b/quic/core/quic_crypto_server_stream_test.cc
@@ -0,0 +1,573 @@ +// Copyright (c) 2012 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/quic_crypto_server_stream.h" + +#include <map> +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.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_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.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/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/failing_proof_source.h" +#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +class QuicConnection; +class QuicStream; +} // namespace quic + +using testing::_; +using testing::NiceMock; + +namespace quic { +namespace test { + +class QuicCryptoServerStreamPeer { + public: + static bool DoesPeerSupportStatelessRejects( + const CryptoHandshakeMessage& message) { + return QuicCryptoServerStream::DoesPeerSupportStatelessRejects(message); + } +}; + +namespace { + +const char kServerHostname[] = "test.example.com"; +const uint16_t kServerPort = 443; + +class QuicCryptoServerStreamTest : public QuicTestWithParam<bool> { + public: + QuicCryptoServerStreamTest() + : QuicCryptoServerStreamTest(crypto_test_utils::ProofSourceForTesting()) { + } + + explicit QuicCryptoServerStreamTest(std::unique_ptr<ProofSource> proof_source) + : server_crypto_config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance(), + std::move(proof_source), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + server_compressed_certs_cache_( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), + server_id_(kServerHostname, kServerPort, false), + client_crypto_config_(crypto_test_utils::ProofVerifierForTesting(), + TlsClientHandshaker::CreateSslCtx()) { + SetQuicReloadableFlag(enable_quic_stateless_reject_support, false); + } + + void Initialize() { InitializeServer(); } + + ~QuicCryptoServerStreamTest() override { + // Ensure that anything that might reference |helpers_| is destroyed before + // |helpers_| is destroyed. + server_session_.reset(); + client_session_.reset(); + helpers_.clear(); + alarm_factories_.clear(); + } + + // Initializes the crypto server stream state for testing. May be + // called multiple times. + void InitializeServer() { + TestQuicSpdyServerSession* server_session = nullptr; + helpers_.push_back(QuicMakeUnique<NiceMock<MockQuicConnectionHelper>>()); + alarm_factories_.push_back(QuicMakeUnique<MockAlarmFactory>()); + CreateServerSessionForTest( + server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_, + helpers_.back().get(), alarm_factories_.back().get(), + &server_crypto_config_, &server_compressed_certs_cache_, + &server_connection_, &server_session); + CHECK(server_session); + server_session_.reset(server_session); + EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) + .Times(testing::AnyNumber()); + EXPECT_CALL(*server_session_->helper(), GenerateConnectionIdForReject(_, _)) + .Times(testing::AnyNumber()); + crypto_test_utils::FakeServerOptions options; + options.token_binding_params = QuicTagVector{kTB10}; + crypto_test_utils::SetupCryptoServerConfigForTest( + server_connection_->clock(), server_connection_->random_generator(), + &server_crypto_config_, options); + server_session_->GetMutableCryptoStream()->OnSuccessfulVersionNegotiation( + supported_versions_.front()); + } + + QuicCryptoServerStream* server_stream() { + return server_session_->GetMutableCryptoStream(); + } + + QuicCryptoClientStream* client_stream() { + return client_session_->GetMutableCryptoStream(); + } + + // Initializes a fake client, and all its associated state, for + // testing. May be called multiple times. + void InitializeFakeClient(bool supports_stateless_rejects) { + TestQuicSpdyClientSession* client_session = nullptr; + helpers_.push_back(QuicMakeUnique<NiceMock<MockQuicConnectionHelper>>()); + alarm_factories_.push_back(QuicMakeUnique<MockAlarmFactory>()); + CreateClientSessionForTest( + server_id_, supports_stateless_rejects, + QuicTime::Delta::FromSeconds(100000), supported_versions_, + helpers_.back().get(), alarm_factories_.back().get(), + &client_crypto_config_, &client_connection_, &client_session); + CHECK(client_session); + client_session_.reset(client_session); + } + + int CompleteCryptoHandshake() { + CHECK(server_connection_); + CHECK(server_session_ != nullptr); + + return crypto_test_utils::HandshakeWithFakeClient( + helpers_.back().get(), alarm_factories_.back().get(), + server_connection_, server_stream(), server_id_, client_options_); + } + + // Performs a single round of handshake message-exchange between the + // client and server. + void AdvanceHandshakeWithFakeClient() { + CHECK(server_connection_); + CHECK(client_session_ != nullptr); + + EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_)) + .Times(testing::AnyNumber()); + EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber()); + EXPECT_CALL(*server_connection_, OnCanWrite()).Times(testing::AnyNumber()); + client_stream()->CryptoConnect(); + crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 0, + server_connection_, server_stream(), 0); + } + + protected: + // Every connection gets its own MockQuicConnectionHelper and + // MockAlarmFactory, tracked separately from the server and client state so + // their lifetimes persist through the whole test. + std::vector<std::unique_ptr<MockQuicConnectionHelper>> helpers_; + std::vector<std::unique_ptr<MockAlarmFactory>> alarm_factories_; + + // Server state. + PacketSavingConnection* server_connection_; + std::unique_ptr<TestQuicSpdyServerSession> server_session_; + QuicCryptoServerConfig server_crypto_config_; + QuicCompressedCertsCache server_compressed_certs_cache_; + QuicServerId server_id_; + + // Client state. + PacketSavingConnection* client_connection_; + QuicCryptoClientConfig client_crypto_config_; + std::unique_ptr<TestQuicSpdyClientSession> client_session_; + + CryptoHandshakeMessage message_; + crypto_test_utils::FakeClientOptions client_options_; + + // Which QUIC versions the client and server support. + ParsedQuicVersionVector supported_versions_ = AllSupportedVersions(); +}; + +INSTANTIATE_TEST_SUITE_P(Tests, QuicCryptoServerStreamTest, testing::Bool()); + +TEST_P(QuicCryptoServerStreamTest, NotInitiallyConected) { + Initialize(); + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); +} + +TEST_P(QuicCryptoServerStreamTest, NotInitiallySendingStatelessRejects) { + Initialize(); + EXPECT_FALSE(server_stream()->UseStatelessRejectsIfPeerSupported()); + EXPECT_FALSE(server_stream()->PeerSupportsStatelessRejects()); +} + +TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { + // CompleteCryptoHandshake returns the number of client hellos sent. This + // test should send: + // * One to get a source-address token and certificates. + // * One to complete the handshake. + Initialize(); + EXPECT_EQ(2, CompleteCryptoHandshake()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); +} + +TEST_P(QuicCryptoServerStreamTest, ConnectedAfterTlsHandshake) { + FLAGS_quic_supports_tls_handshake = true; + client_options_.only_tls_versions = true; + supported_versions_.clear(); + for (QuicTransportVersion transport_version : + AllSupportedTransportVersions()) { + supported_versions_.push_back( + ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version)); + } + Initialize(); + CompleteCryptoHandshake(); + EXPECT_EQ(PROTOCOL_TLS1_3, server_stream()->handshake_protocol()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); +} + +TEST_P(QuicCryptoServerStreamTest, ForwardSecureAfterCHLO) { + Initialize(); + InitializeFakeClient(/* supports_stateless_rejects= */ false); + + // Do a first handshake in order to prime the client config with the server's + // information. + AdvanceHandshakeWithFakeClient(); + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); + + // Now do another handshake, with the blocking SHLO connection option. + InitializeServer(); + InitializeFakeClient(/* supports_stateless_rejects= */ false); + + AdvanceHandshakeWithFakeClient(); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, + server_session_->connection()->encryption_level()); +} + +TEST_P(QuicCryptoServerStreamTest, StatelessRejectAfterCHLO) { + SetQuicReloadableFlag(enable_quic_stateless_reject_support, true); + Initialize(); + + InitializeFakeClient(/* supports_stateless_rejects= */ true); + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _)); + EXPECT_CALL(*client_connection_, + CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _)); + AdvanceHandshakeWithFakeClient(); + + // Check the server to make the sure the handshake did not succeed. + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); + + // Check the client state to make sure that it received a server-designated + // connection id. + QuicCryptoClientConfig::CachedState* client_state = + client_crypto_config_.LookupOrCreate(server_id_); + + ASSERT_TRUE(client_state->has_server_nonce()); + ASSERT_FALSE(client_state->GetNextServerNonce().empty()); + ASSERT_FALSE(client_state->has_server_nonce()); + + ASSERT_TRUE(client_state->has_server_designated_connection_id()); + const QuicConnectionId server_designated_connection_id = + client_state->GetNextServerDesignatedConnectionId(); + const QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId( + server_connection_->random_generator()); + EXPECT_EQ(expected_id, server_designated_connection_id); + EXPECT_FALSE(client_state->has_server_designated_connection_id()); + ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0))); +} + +TEST_P(QuicCryptoServerStreamTest, ConnectedAfterStatelessHandshake) { + SetQuicReloadableFlag(enable_quic_stateless_reject_support, true); + Initialize(); + + InitializeFakeClient(/* supports_stateless_rejects= */ true); + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _)); + EXPECT_CALL(*client_connection_, + CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _)); + AdvanceHandshakeWithFakeClient(); + + // On the first round, encryption will not be established. + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); + EXPECT_EQ(1, server_stream()->NumHandshakeMessages()); + EXPECT_EQ(0, server_stream()->NumHandshakeMessagesWithServerNonces()); + + // Now check the client state. + QuicCryptoClientConfig::CachedState* client_state = + client_crypto_config_.LookupOrCreate(server_id_); + + ASSERT_TRUE(client_state->has_server_designated_connection_id()); + const QuicConnectionId server_designated_connection_id = + client_state->GetNextServerDesignatedConnectionId(); + const QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId( + server_connection_->random_generator()); + EXPECT_EQ(expected_id, server_designated_connection_id); + EXPECT_FALSE(client_state->has_server_designated_connection_id()); + ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0))); + + // Now create new client and server streams with the existing config + // and try the handshake again (0-RTT handshake). + InitializeServer(); + + InitializeFakeClient(/* supports_stateless_rejects= */ true); + // In the stateless case, the second handshake contains a server-nonce, so the + // AsyncStrikeRegisterVerification() case will still succeed (unlike a 0-RTT + // handshake). + AdvanceHandshakeWithFakeClient(); + + // On the second round, encryption will be established. + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); + EXPECT_EQ(1, server_stream()->NumHandshakeMessages()); + EXPECT_EQ(1, server_stream()->NumHandshakeMessagesWithServerNonces()); +} + +TEST_P(QuicCryptoServerStreamTest, NoStatelessRejectIfNoClientSupport) { + SetQuicReloadableFlag(enable_quic_stateless_reject_support, true); + Initialize(); + + // The server is configured to use stateless rejects, but the client does not + // support it. + InitializeFakeClient(/* supports_stateless_rejects= */ false); + AdvanceHandshakeWithFakeClient(); + + // Check the server to make the sure the handshake did not succeed. + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); + + // Check the client state to make sure that it did not receive a + // server-designated connection id. + QuicCryptoClientConfig::CachedState* client_state = + client_crypto_config_.LookupOrCreate(server_id_); + + ASSERT_FALSE(client_state->has_server_designated_connection_id()); + ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0))); +} + +TEST_P(QuicCryptoServerStreamTest, ZeroRTT) { + Initialize(); + InitializeFakeClient(/* supports_stateless_rejects= */ false); + + // Do a first handshake in order to prime the client config with the server's + // information. + AdvanceHandshakeWithFakeClient(); + EXPECT_FALSE(server_stream()->ZeroRttAttempted()); + + // Now do another handshake, hopefully in 0-RTT. + QUIC_LOG(INFO) << "Resetting for 0-RTT handshake attempt"; + InitializeFakeClient(/* supports_stateless_rejects= */ false); + InitializeServer(); + + EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_)) + .Times(testing::AnyNumber()); + EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber()); + client_stream()->CryptoConnect(); + + EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_)) + .Times(testing::AnyNumber()); + EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber()); + crypto_test_utils::CommunicateHandshakeMessages( + client_connection_, client_stream(), server_connection_, server_stream()); + + EXPECT_EQ(1, client_stream()->num_sent_client_hellos()); + EXPECT_TRUE(server_stream()->ZeroRttAttempted()); +} + +TEST_P(QuicCryptoServerStreamTest, FailByPolicy) { + Initialize(); + InitializeFakeClient(/* supports_stateless_rejects= */ false); + + EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) + .WillOnce(testing::Return(false)); + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); + + AdvanceHandshakeWithFakeClient(); +} + +TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) { + Initialize(); + CompleteCryptoHandshake(); + EXPECT_CALL( + *server_connection_, + CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, _, _)); + message_.set_tag(kCHLO); + crypto_test_utils::SendHandshakeMessageToStream(server_stream(), message_, + Perspective::IS_CLIENT); +} + +TEST_P(QuicCryptoServerStreamTest, BadMessageType) { + Initialize(); + + message_.set_tag(kSHLO); + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, _, _)); + crypto_test_utils::SendHandshakeMessageToStream(server_stream(), message_, + Perspective::IS_SERVER); +} + +TEST_P(QuicCryptoServerStreamTest, ChannelID) { + Initialize(); + + client_options_.channel_id_enabled = true; + client_options_.channel_id_source_async = false; + // CompleteCryptoHandshake verifies + // server_stream()->crypto_negotiated_params().channel_id is correct. + EXPECT_EQ(2, CompleteCryptoHandshake()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); +} + +TEST_P(QuicCryptoServerStreamTest, ChannelIDAsync) { + Initialize(); + + client_options_.channel_id_enabled = true; + client_options_.channel_id_source_async = true; + // CompleteCryptoHandshake verifies + // server_stream()->crypto_negotiated_params().channel_id is correct. + EXPECT_EQ(2, CompleteCryptoHandshake()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); +} + +TEST_P(QuicCryptoServerStreamTest, OnlySendSCUPAfterHandshakeComplete) { + // An attempt to send a SCUP before completing handshake should fail. + Initialize(); + + server_stream()->SendServerConfigUpdate(nullptr); + EXPECT_EQ(0, server_stream()->NumServerConfigUpdateMessagesSent()); +} + +TEST_P(QuicCryptoServerStreamTest, SendSCUPAfterHandshakeComplete) { + Initialize(); + + InitializeFakeClient(/* supports_stateless_rejects= */ false); + + // Do a first handshake in order to prime the client config with the server's + // information. + AdvanceHandshakeWithFakeClient(); + + // Now do another handshake, with the blocking SHLO connection option. + InitializeServer(); + InitializeFakeClient(/* supports_stateless_rejects= */ false); + AdvanceHandshakeWithFakeClient(); + + // Send a SCUP message and ensure that the client was able to verify it. + EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0); + server_stream()->SendServerConfigUpdate(nullptr); + crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 1, + server_connection_, server_stream(), 1); + + EXPECT_EQ(1, server_stream()->NumServerConfigUpdateMessagesSent()); + EXPECT_EQ(1, client_stream()->num_scup_messages_received()); +} + +TEST_P(QuicCryptoServerStreamTest, DoesPeerSupportStatelessRejects) { + Initialize(); + + QuicConfig stateless_reject_config = DefaultQuicConfigStatelessRejects(); + stateless_reject_config.ToHandshakeMessage(&message_); + EXPECT_TRUE( + QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_)); + + message_.Clear(); + QuicConfig stateful_reject_config = DefaultQuicConfig(); + stateful_reject_config.ToHandshakeMessage(&message_); + EXPECT_FALSE( + QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_)); +} + +class QuicCryptoServerStreamTestWithFailingProofSource + : public QuicCryptoServerStreamTest { + public: + QuicCryptoServerStreamTestWithFailingProofSource() + : QuicCryptoServerStreamTest( + std::unique_ptr<FailingProofSource>(new FailingProofSource)) {} +}; + +INSTANTIATE_TEST_SUITE_P(MoreTests, + QuicCryptoServerStreamTestWithFailingProofSource, + testing::Bool()); + +TEST_P(QuicCryptoServerStreamTestWithFailingProofSource, Test) { + Initialize(); + InitializeFakeClient(/* supports_stateless_rejects= */ false); + + EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) + .WillOnce(testing::Return(true)); + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_HANDSHAKE_FAILED, "Failed to get proof", _)); + // Regression test for b/31521252, in which a crash would happen here. + AdvanceHandshakeWithFakeClient(); + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); +} + +class QuicCryptoServerStreamTestWithFakeProofSource + : public QuicCryptoServerStreamTest { + public: + QuicCryptoServerStreamTestWithFakeProofSource() + : QuicCryptoServerStreamTest( + std::unique_ptr<FakeProofSource>(new FakeProofSource)), + crypto_config_peer_(&server_crypto_config_) {} + + FakeProofSource* GetFakeProofSource() const { + return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource()); + } + + protected: + QuicCryptoServerConfigPeer crypto_config_peer_; +}; + +INSTANTIATE_TEST_SUITE_P(YetMoreTests, + QuicCryptoServerStreamTestWithFakeProofSource, + testing::Bool()); + +// Regression test for b/35422225, in which multiple CHLOs arriving on the same +// connection in close succession could cause a crash, especially when the use +// of Mentat signing meant that it took a while for each CHLO to be processed. +TEST_P(QuicCryptoServerStreamTestWithFakeProofSource, MultipleChlo) { + Initialize(); + GetFakeProofSource()->Activate(); + EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) + .WillOnce(testing::Return(true)); + + // Create a minimal CHLO + MockClock clock; + QuicTransportVersion version = AllSupportedTransportVersions().front(); + CryptoHandshakeMessage chlo = crypto_test_utils::GenerateDefaultInchoateCHLO( + &clock, version, &server_crypto_config_); + + // Send in the CHLO, and check that a callback is now pending in the + // ProofSource. + crypto_test_utils::SendHandshakeMessageToStream(server_stream(), chlo, + Perspective::IS_CLIENT); + EXPECT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Send in a second CHLO while processing of the first is still pending. + // Verify that the server closes the connection rather than crashing. Note + // that the crash is a use-after-free, so it may only show up consistently in + // ASAN tests. + EXPECT_CALL( + *server_connection_, + CloseConnection(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO, + "Unexpected handshake message while processing CHLO", _)); + crypto_test_utils::SendHandshakeMessageToStream(server_stream(), chlo, + Perspective::IS_CLIENT); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_crypto_stream.cc b/quic/core/quic_crypto_stream.cc new file mode 100644 index 0000000..aef431d --- /dev/null +++ b/quic/core/quic_crypto_stream.cc
@@ -0,0 +1,418 @@ +// Copyright (c) 2012 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/quic_crypto_stream.h" + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +#define ENDPOINT \ + (session()->perspective() == Perspective::IS_SERVER ? "Server: " \ + : "Client:" \ + " ") + +QuicCryptoStream::QuicCryptoStream(QuicSession* session) + : QuicStream(QuicUtils::GetCryptoStreamId( + session->connection()->transport_version()), + session, + /*is_static=*/true, + BIDIRECTIONAL), + substreams_{{this, ENCRYPTION_NONE}, + {this, ENCRYPTION_ZERO_RTT}, + {this, ENCRYPTION_FORWARD_SECURE}} { + // The crypto stream is exempt from connection level flow control. + DisableConnectionFlowControlForThisStream(); +} + +QuicCryptoStream::~QuicCryptoStream() {} + +// static +QuicByteCount QuicCryptoStream::CryptoMessageFramingOverhead( + QuicTransportVersion version, + QuicConnectionId connection_id) { + DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id, version)); + return QuicPacketCreator::StreamFramePacketOverhead( + version, static_cast<QuicConnectionIdLength>(connection_id.length()), + PACKET_0BYTE_CONNECTION_ID, + /*include_version=*/true, + /*include_diversification_nonce=*/true, + version > QUIC_VERSION_43 ? PACKET_4BYTE_PACKET_NUMBER + : PACKET_1BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_1, VARIABLE_LENGTH_INTEGER_LENGTH_2, + /*offset=*/0); +} + +void QuicCryptoStream::OnCryptoFrame(const QuicCryptoFrame& frame) { + QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47) + << "Versions less than 47 shouldn't receive CRYPTO frames"; + EncryptionLevel level = session()->connection()->last_decrypted_level(); + substreams_[level].sequencer.OnCryptoFrame(frame); +} + +void QuicCryptoStream::OnStreamFrame(const QuicStreamFrame& frame) { + if (session()->connection()->transport_version() >= QUIC_VERSION_47) { + QUIC_PEER_BUG + << "Crypto data received in stream frame instead of crypto frame"; + CloseConnectionWithDetails(QUIC_INVALID_STREAM_DATA, + "Unexpected stream frame"); + } + QuicStream::OnStreamFrame(frame); +} + +void QuicCryptoStream::OnDataAvailable() { + EncryptionLevel level = session()->connection()->last_decrypted_level(); + if (session()->connection()->transport_version() < QUIC_VERSION_47) { + // Versions less than 47 only support QUIC crypto, which ignores the + // EncryptionLevel passed into CryptoMessageParser::ProcessInput (and + // OnDataAvailableInSequencer). + OnDataAvailableInSequencer(sequencer(), level); + return; + } + OnDataAvailableInSequencer(&substreams_[level].sequencer, level); +} + +void QuicCryptoStream::OnDataAvailableInSequencer( + QuicStreamSequencer* sequencer, + EncryptionLevel level) { + struct iovec iov; + while (sequencer->GetReadableRegion(&iov)) { + QuicStringPiece data(static_cast<char*>(iov.iov_base), iov.iov_len); + if (!crypto_message_parser()->ProcessInput(data, level)) { + CloseConnectionWithDetails(crypto_message_parser()->error(), + crypto_message_parser()->error_detail()); + return; + } + sequencer->MarkConsumed(iov.iov_len); + if (handshake_confirmed() && + crypto_message_parser()->InputBytesRemaining() == 0) { + // If the handshake is complete and the current message has been fully + // processed then no more handshake messages are likely to arrive soon + // so release the memory in the stream sequencer. + sequencer->ReleaseBufferIfEmpty(); + } + } +} + +bool QuicCryptoStream::ExportKeyingMaterial(QuicStringPiece label, + QuicStringPiece context, + size_t result_len, + QuicString* result) const { + if (!handshake_confirmed()) { + QUIC_DLOG(ERROR) << "ExportKeyingMaterial was called before forward-secure" + << "encryption was established."; + return false; + } + return CryptoUtils::ExportKeyingMaterial( + crypto_negotiated_params().subkey_secret, label, context, result_len, + result); +} + +void QuicCryptoStream::WriteCryptoData(EncryptionLevel level, + QuicStringPiece data) { + if (session()->connection()->transport_version() < QUIC_VERSION_47) { + // The QUIC crypto handshake takes care of setting the appropriate + // encryption level before writing data. Since that is the only handshake + // supported in versions less than 47, |level| can be ignored here. + WriteOrBufferData(data, /* fin */ false, /* ack_listener */ nullptr); + return; + } + if (data.empty()) { + QUIC_BUG << "Empty crypto data being written"; + return; + } + // Append |data| to the send buffer for this encryption level. + struct iovec iov(QuicUtils::MakeIovec(data)); + QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer; + QuicStreamOffset offset = send_buffer->stream_offset(); + send_buffer->SaveStreamData(&iov, /*iov_count=*/1, /*iov_offset=*/0, + data.length()); + if (kMaxStreamLength - offset < data.length()) { + QUIC_BUG << "Writing too much crypto handshake data"; + // TODO(nharper): Switch this to an IETF QUIC error code, possibly + // INTERNAL_ERROR? + CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW, + "Writing too much crypto handshake data"); + } + + EncryptionLevel current_level = session()->connection()->encryption_level(); + session()->connection()->SetDefaultEncryptionLevel(level); + size_t bytes_consumed = + session()->connection()->SendCryptoData(level, data.length(), offset); + session()->connection()->SetDefaultEncryptionLevel(current_level); + + send_buffer->OnStreamDataConsumed(bytes_consumed); +} + +void QuicCryptoStream::OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) {} + +bool QuicCryptoStream::OnCryptoFrameAcked(const QuicCryptoFrame& frame, + QuicTime::Delta ack_delay_time) { + QuicByteCount newly_acked_length = 0; + if (!substreams_[frame.level].send_buffer.OnStreamDataAcked( + frame.offset, frame.data_length, &newly_acked_length)) { + CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "Trying to ack unsent crypto data."); + return false; + } + return newly_acked_length > 0; +} + +void QuicCryptoStream::NeuterUnencryptedStreamData() { + if (session()->connection()->transport_version() < QUIC_VERSION_47) { + for (const auto& interval : bytes_consumed_[ENCRYPTION_NONE]) { + QuicByteCount newly_acked_length = 0; + send_buffer().OnStreamDataAcked( + interval.min(), interval.max() - interval.min(), &newly_acked_length); + } + return; + } + QuicStreamSendBuffer* send_buffer = &substreams_[ENCRYPTION_NONE].send_buffer; + // TODO(nharper): Consider adding a Clear() method to QuicStreamSendBuffer to + // replace the following code. + QuicIntervalSet<QuicStreamOffset> to_ack = send_buffer->bytes_acked(); + to_ack.Complement(0, send_buffer->stream_offset()); + for (const auto& interval : to_ack) { + QuicByteCount newly_acked_length = 0; + send_buffer->OnStreamDataAcked( + interval.min(), interval.max() - interval.min(), &newly_acked_length); + } +} + +void QuicCryptoStream::OnStreamDataConsumed(size_t bytes_consumed) { + if (session()->connection()->transport_version() >= QUIC_VERSION_47) { + QUIC_BUG << "Stream data consumed when CRYPTO frames should be in use"; + } + if (bytes_consumed > 0) { + bytes_consumed_[session()->connection()->encryption_level()].Add( + stream_bytes_written(), stream_bytes_written() + bytes_consumed); + } + QuicStream::OnStreamDataConsumed(bytes_consumed); +} + +bool QuicCryptoStream::HasPendingCryptoRetransmission() { + if (session()->connection()->transport_version() < QUIC_VERSION_47) { + return false; + } + for (EncryptionLevel level : + {ENCRYPTION_NONE, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { + if (substreams_[level].send_buffer.HasPendingRetransmission()) { + return true; + } + } + return false; +} + +void QuicCryptoStream::WritePendingCryptoRetransmission() { + QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47) + << "Versions less than 47 don't write CRYPTO frames"; + EncryptionLevel current_encryption_level = + session()->connection()->encryption_level(); + for (EncryptionLevel level : + {ENCRYPTION_NONE, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { + QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer; + session()->connection()->SetDefaultEncryptionLevel(level); + while (send_buffer->HasPendingRetransmission()) { + auto pending = send_buffer->NextPendingRetransmission(); + size_t bytes_consumed = session()->connection()->SendCryptoData( + level, pending.length, pending.offset); + send_buffer->OnStreamDataRetransmitted(pending.offset, bytes_consumed); + } + } + session()->connection()->SetDefaultEncryptionLevel(current_encryption_level); +} + +void QuicCryptoStream::WritePendingRetransmission() { + while (HasPendingRetransmission()) { + StreamPendingRetransmission pending = + send_buffer().NextPendingRetransmission(); + QuicIntervalSet<QuicStreamOffset> retransmission( + pending.offset, pending.offset + pending.length); + EncryptionLevel retransmission_encryption_level = ENCRYPTION_NONE; + // Determine the encryption level to write the retransmission + // at. The retransmission should be written at the same encryption level + // as the original transmission. + for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) { + if (retransmission.Intersects(bytes_consumed_[i])) { + retransmission_encryption_level = static_cast<EncryptionLevel>(i); + retransmission.Intersection(bytes_consumed_[i]); + break; + } + } + pending.offset = retransmission.begin()->min(); + pending.length = + retransmission.begin()->max() - retransmission.begin()->min(); + EncryptionLevel current_encryption_level = + session()->connection()->encryption_level(); + // Set appropriate encryption level. + session()->connection()->SetDefaultEncryptionLevel( + retransmission_encryption_level); + QuicConsumedData consumed = session()->WritevData( + this, id(), pending.length, pending.offset, NO_FIN); + QUIC_DVLOG(1) << ENDPOINT << "stream " << id() + << " tries to retransmit stream data [" << pending.offset + << ", " << pending.offset + pending.length + << ") with encryption level: " + << retransmission_encryption_level + << ", consumed: " << consumed; + OnStreamFrameRetransmitted(pending.offset, consumed.bytes_consumed, + consumed.fin_consumed); + // Restore encryption level. + session()->connection()->SetDefaultEncryptionLevel( + current_encryption_level); + if (consumed.bytes_consumed < pending.length) { + // The connection is write blocked. + break; + } + } +} + +bool QuicCryptoStream::RetransmitStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + bool /*fin*/) { + QuicIntervalSet<QuicStreamOffset> retransmission(offset, + offset + data_length); + // Determine the encryption level to send data. This only needs to be once as + // [offset, offset + data_length) is guaranteed to be in the same packet. + EncryptionLevel send_encryption_level = ENCRYPTION_NONE; + for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) { + if (retransmission.Intersects(bytes_consumed_[i])) { + send_encryption_level = static_cast<EncryptionLevel>(i); + break; + } + } + retransmission.Difference(bytes_acked()); + EncryptionLevel current_encryption_level = + session()->connection()->encryption_level(); + for (const auto& interval : retransmission) { + QuicStreamOffset retransmission_offset = interval.min(); + QuicByteCount retransmission_length = interval.max() - interval.min(); + // Set appropriate encryption level. + session()->connection()->SetDefaultEncryptionLevel(send_encryption_level); + QuicConsumedData consumed = session()->WritevData( + this, id(), retransmission_length, retransmission_offset, NO_FIN); + QUIC_DVLOG(1) << ENDPOINT << "stream " << id() + << " is forced to retransmit stream data [" + << retransmission_offset << ", " + << retransmission_offset + retransmission_length + << "), with encryption level: " << send_encryption_level + << ", consumed: " << consumed; + OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed, + consumed.fin_consumed); + // Restore encryption level. + session()->connection()->SetDefaultEncryptionLevel( + current_encryption_level); + if (consumed.bytes_consumed < retransmission_length) { + // The connection is write blocked. + return false; + } + } + + return true; +} + +uint64_t QuicCryptoStream::crypto_bytes_read() const { + if (session()->connection()->transport_version() < QUIC_VERSION_47) { + return stream_bytes_read(); + } + return substreams_[ENCRYPTION_NONE].sequencer.NumBytesConsumed() + + substreams_[ENCRYPTION_ZERO_RTT].sequencer.NumBytesConsumed() + + substreams_[ENCRYPTION_FORWARD_SECURE].sequencer.NumBytesConsumed(); +} + +uint64_t QuicCryptoStream::BytesReadOnLevel(EncryptionLevel level) const { + return substreams_[level].sequencer.NumBytesConsumed(); +} + +bool QuicCryptoStream::WriteCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) { + QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47) + << "Versions less than 47 don't write CRYPTO frames (2)"; + return substreams_[level].send_buffer.WriteStreamData(offset, data_length, + writer); +} + +void QuicCryptoStream::OnCryptoFrameLost(QuicCryptoFrame* crypto_frame) { + QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47) + << "Versions less than 47 don't lose CRYPTO frames"; + substreams_[crypto_frame->level].send_buffer.OnStreamDataLost( + crypto_frame->offset, crypto_frame->data_length); +} + +void QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame) { + QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47) + << "Versions less than 47 don't retransmit CRYPTO frames"; + QuicIntervalSet<QuicStreamOffset> retransmission( + crypto_frame->offset, crypto_frame->offset + crypto_frame->data_length); + QuicStreamSendBuffer* send_buffer = + &substreams_[crypto_frame->level].send_buffer; + retransmission.Difference(send_buffer->bytes_acked()); + if (retransmission.Empty()) { + return; + } + EncryptionLevel current_encryption_level = + session()->connection()->encryption_level(); + for (const auto& interval : retransmission) { + size_t retransmission_offset = interval.min(); + size_t retransmission_length = interval.max() - interval.min(); + session()->connection()->SetDefaultEncryptionLevel(crypto_frame->level); + size_t bytes_consumed = session()->connection()->SendCryptoData( + crypto_frame->level, retransmission_length, retransmission_offset); + send_buffer->OnStreamDataRetransmitted(retransmission_offset, + bytes_consumed); + } + session()->connection()->SetDefaultEncryptionLevel(current_encryption_level); +} + +bool QuicCryptoStream::IsFrameOutstanding(EncryptionLevel level, + size_t offset, + size_t length) const { + if (session()->connection()->transport_version() < QUIC_VERSION_47) { + // This only happens if a client was originally configured for a version + // greater than 45, but received a version negotiation packet and is + // attempting to retransmit for a version less than 47. Outside of tests, + // this is a misconfiguration of the client, and this connection will be + // doomed. Return false here to avoid trying to retransmit CRYPTO frames on + // the wrong transport version. + return false; + } + return substreams_[level].send_buffer.IsStreamDataOutstanding(offset, length); +} + +bool QuicCryptoStream::IsWaitingForAcks() const { + if (session()->connection()->transport_version() < QUIC_VERSION_47) { + return QuicStream::IsWaitingForAcks(); + } + for (EncryptionLevel level : + {ENCRYPTION_NONE, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { + if (substreams_[level].send_buffer.stream_bytes_outstanding()) { + return true; + } + } + return false; +} + +QuicCryptoStream::CryptoSubstream::CryptoSubstream( + QuicCryptoStream* crypto_stream, + EncryptionLevel) + : sequencer(crypto_stream), + send_buffer(crypto_stream->session() + ->connection() + ->helper() + ->GetStreamSendBufferAllocator()) {} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_crypto_stream.h b/quic/core/quic_crypto_stream.h new file mode 100644 index 0000000..d2a729e --- /dev/null +++ b/quic/core/quic_crypto_stream.h
@@ -0,0 +1,174 @@ +// Copyright (c) 2012 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_QUIC_CRYPTO_STREAM_H_ +#define QUICHE_QUIC_CORE_QUIC_CRYPTO_STREAM_H_ + +#include <cstddef> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicSession; + +// Crypto handshake messages in QUIC take place over a reserved stream with the +// id 1. Each endpoint (client and server) will allocate an instance of a +// subclass of QuicCryptoStream to send and receive handshake messages. (In the +// normal 1-RTT handshake, the client will send a client hello, CHLO, message. +// The server will receive this message and respond with a server hello message, +// SHLO. At this point both sides will have established a crypto context they +// can use to send encrypted messages. +// +// For more details: +// https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g/edit?usp=sharing +class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream { + public: + explicit QuicCryptoStream(QuicSession* session); + QuicCryptoStream(const QuicCryptoStream&) = delete; + QuicCryptoStream& operator=(const QuicCryptoStream&) = delete; + + ~QuicCryptoStream() override; + + // Returns the per-packet framing overhead associated with sending a + // handshake message for |version|. + static QuicByteCount CryptoMessageFramingOverhead( + QuicTransportVersion version, + QuicConnectionId connection_id); + + // QuicStream implementation + void OnStreamFrame(const QuicStreamFrame& frame) override; + void OnDataAvailable() override; + + // Called when a CRYPTO frame is received. + void OnCryptoFrame(const QuicCryptoFrame& frame); + + // Called when a CRYPTO frame is ACKed. + bool OnCryptoFrameAcked(const QuicCryptoFrame& frame, + QuicTime::Delta ack_delay_time); + + // Performs key extraction to derive a new secret of |result_len| bytes + // dependent on |label|, |context|, and the stream's negotiated subkey secret. + // Returns false if the handshake has not been confirmed or the parameters are + // invalid (e.g. |label| contains null bytes); returns true on success. + bool ExportKeyingMaterial(QuicStringPiece label, + QuicStringPiece context, + size_t result_len, + QuicString* result) const; + + // Writes |data| to the QuicStream at level |level|. + virtual void WriteCryptoData(EncryptionLevel level, QuicStringPiece data); + + // Returns true once an encrypter has been set for the connection. + virtual bool encryption_established() const = 0; + + // Returns true once the crypto handshake has completed. + virtual bool handshake_confirmed() const = 0; + + // Returns the parameters negotiated in the crypto handshake. + virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const = 0; + + // Provides the message parser to use when data is received on this stream. + virtual CryptoMessageParser* crypto_message_parser() = 0; + + // Called when the underlying QuicConnection has agreed upon a QUIC version to + // use. + virtual void OnSuccessfulVersionNegotiation(const ParsedQuicVersion& version); + + // Called to cancel retransmission of unencrypted crypto stream data. + void NeuterUnencryptedStreamData(); + + // Override to record the encryption level of consumed data. + void OnStreamDataConsumed(size_t bytes_consumed) override; + + // Returns whether there are any bytes pending retransmission in CRYPTO + // frames. + virtual bool HasPendingCryptoRetransmission(); + + // Writes any pending CRYPTO frame retransmissions. + void WritePendingCryptoRetransmission(); + + // Override to retransmit lost crypto data with the appropriate encryption + // level. + void WritePendingRetransmission() override; + + // Override to send unacked crypto data with the appropriate encryption level. + bool RetransmitStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin) override; + + // Returns the number of bytes of handshake data that have been received from + // the peer in either CRYPTO or STREAM frames. + uint64_t crypto_bytes_read() const; + + // Returns the number of bytes of handshake data that have been received from + // the peer in CRYPTO frames at a particular encryption level. + QuicByteCount BytesReadOnLevel(EncryptionLevel level) const; + + // Writes |data_length| of data of a crypto frame to |writer|. The data + // written is from the send buffer for encryption level |level| and starts at + // |offset|. + bool WriteCryptoFrame(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer); + + // Called when data from a CRYPTO frame is considered lost. The lost data is + // identified by the encryption level, offset, and length in |crypto_frame|. + void OnCryptoFrameLost(QuicCryptoFrame* crypto_frame); + + // Called to retransmit any outstanding data in the range indicated by the + // encryption level, offset, and length in |crypto_frame|. + void RetransmitData(QuicCryptoFrame* crypto_frame); + + // Returns true if any portion of the data at encryption level |level| + // starting at |offset| for |length| bytes is outstanding. + bool IsFrameOutstanding(EncryptionLevel level, + size_t offset, + size_t length) const; + + // Returns true if the crypto handshake is still waiting for acks of sent + // data, and false if all data has been acked. + bool IsWaitingForAcks() const; + + private: + // Data sent and received in CRYPTO frames is sent at multiple encryption + // levels. Some of the state for the single logical crypto stream is split + // across encryption levels, and a CryptoSubstream is used to manage that + // state for a particular encryption level. + struct CryptoSubstream { + CryptoSubstream(QuicCryptoStream* crypto_stream, EncryptionLevel); + + QuicStreamSequencer sequencer; + QuicStreamSendBuffer send_buffer; + }; + + // Helper method for OnDataAvailable. Calls CryptoMessageParser::ProcessInput + // with the data available in |sequencer| and |level|, and marks the data + // passed to ProcessInput as consumed. + void OnDataAvailableInSequencer(QuicStreamSequencer* sequencer, + EncryptionLevel level); + + // Consumed data according to encryption levels. + // TODO(fayang): This is not needed once switching from QUIC crypto to + // TLS 1.3, which never encrypts crypto data. + QuicIntervalSet<QuicStreamOffset> bytes_consumed_[NUM_ENCRYPTION_LEVELS]; + + // Keeps state for data sent/received in CRYPTO frames at each encryption + // level. + CryptoSubstream substreams_[NUM_ENCRYPTION_LEVELS]; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_STREAM_H_
diff --git a/quic/core/quic_crypto_stream_test.cc b/quic/core/quic_crypto_stream_test.cc new file mode 100644 index 0000000..26ed335 --- /dev/null +++ b/quic/core/quic_crypto_stream_test.cc
@@ -0,0 +1,518 @@ +// Copyright (c) 2012 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/quic_crypto_stream.h" + +#include <cstdint> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::InSequence; +using testing::Invoke; +using testing::InvokeWithoutArgs; + +namespace quic { +namespace test { +namespace { + +class MockQuicCryptoStream : public QuicCryptoStream, + public QuicCryptoHandshaker { + public: + explicit MockQuicCryptoStream(QuicSession* session) + : QuicCryptoStream(session), + QuicCryptoHandshaker(this, session), + params_(new QuicCryptoNegotiatedParameters) {} + MockQuicCryptoStream(const MockQuicCryptoStream&) = delete; + MockQuicCryptoStream& operator=(const MockQuicCryptoStream&) = delete; + + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override { + messages_.push_back(message); + } + + std::vector<CryptoHandshakeMessage>* messages() { return &messages_; } + + bool encryption_established() const override { return false; } + bool handshake_confirmed() const override { return false; } + + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override { + return *params_; + } + CryptoMessageParser* crypto_message_parser() override { + return QuicCryptoHandshaker::crypto_message_parser(); + } + + private: + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; + std::vector<CryptoHandshakeMessage> messages_; +}; + +class QuicCryptoStreamTest : public QuicTest { + public: + QuicCryptoStreamTest() + : connection_(new MockQuicConnection(&helper_, + &alarm_factory_, + Perspective::IS_CLIENT)), + session_(connection_, /*create_mock_crypto_stream=*/false) { + stream_ = new MockQuicCryptoStream(&session_); + session_.SetCryptoStream(stream_); + session_.Initialize(); + message_.set_tag(kSHLO); + message_.SetStringPiece(1, "abc"); + message_.SetStringPiece(2, "def"); + ConstructHandshakeMessage(); + } + QuicCryptoStreamTest(const QuicCryptoStreamTest&) = delete; + QuicCryptoStreamTest& operator=(const QuicCryptoStreamTest&) = delete; + + void ConstructHandshakeMessage() { + CryptoFramer framer; + message_data_.reset(framer.ConstructHandshakeMessage(message_)); + } + + protected: + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* connection_; + MockQuicSpdySession session_; + MockQuicCryptoStream* stream_; + CryptoHandshakeMessage message_; + std::unique_ptr<QuicData> message_data_; +}; + +TEST_F(QuicCryptoStreamTest, NotInitiallyConected) { + EXPECT_FALSE(stream_->encryption_established()); + EXPECT_FALSE(stream_->handshake_confirmed()); +} + +TEST_F(QuicCryptoStreamTest, ProcessRawData) { + if (connection_->transport_version() < QUIC_VERSION_47) { + stream_->OnStreamFrame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + /*fin=*/false, + /*offset=*/0, message_data_->AsStringPiece())); + } else { + stream_->OnCryptoFrame(QuicCryptoFrame(ENCRYPTION_NONE, /*offset*/ 0, + message_data_->AsStringPiece())); + } + ASSERT_EQ(1u, stream_->messages()->size()); + const CryptoHandshakeMessage& message = (*stream_->messages())[0]; + EXPECT_EQ(kSHLO, message.tag()); + EXPECT_EQ(2u, message.tag_value_map().size()); + EXPECT_EQ("abc", crypto_test_utils::GetValueForTag(message, 1)); + EXPECT_EQ("def", crypto_test_utils::GetValueForTag(message, 2)); +} + +TEST_F(QuicCryptoStreamTest, ProcessBadData) { + QuicString bad(message_data_->data(), message_data_->length()); + const int kFirstTagIndex = sizeof(uint32_t) + // message tag + sizeof(uint16_t) + // number of tag-value pairs + sizeof(uint16_t); // padding + EXPECT_EQ(1, bad[kFirstTagIndex]); + bad[kFirstTagIndex] = 0x7F; // out of order tag + + EXPECT_CALL(*connection_, CloseConnection(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, + testing::_, testing::_)); + if (connection_->transport_version() < QUIC_VERSION_47) { + stream_->OnStreamFrame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + /*fin=*/false, /*offset=*/0, bad)); + } else { + stream_->OnCryptoFrame(QuicCryptoFrame(ENCRYPTION_NONE, /*offset*/ 0, bad)); + } +} + +TEST_F(QuicCryptoStreamTest, NoConnectionLevelFlowControl) { + EXPECT_FALSE( + QuicStreamPeer::StreamContributesToConnectionFlowControl(stream_)); +} + +TEST_F(QuicCryptoStreamTest, RetransmitCryptoData) { + if (connection_->transport_version() >= QUIC_VERSION_47) { + return; + } + InSequence s; + // Send [0, 1350) in ENCRYPTION_NONE. + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); + QuicString data(1350, 'a'); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 0, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(data, false, nullptr); + // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 1350, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(data, false, nullptr); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + // Lost [0, 1000). + stream_->OnStreamFrameLost(0, 1000, false); + EXPECT_TRUE(stream_->HasPendingRetransmission()); + // Lost [1200, 2000). + stream_->OnStreamFrameLost(1200, 800, false); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1000, 0, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + // Verify [1200, 2000) are sent in [1200, 1350) and [1350, 2000) because of + // they are in different encryption levels. + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 150, 1200, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 650, 1350, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->OnCanWrite(); + EXPECT_FALSE(stream_->HasPendingRetransmission()); + // Verify connection's encryption level has restored. + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); +} + +TEST_F(QuicCryptoStreamTest, RetransmitCryptoDataInCryptoFrames) { + if (connection_->transport_version() < QUIC_VERSION_47) { + return; + } + EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); + InSequence s; + // Send [0, 1350) in ENCRYPTION_NONE. + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); + QuicString data(1350, 'a'); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_NONE, data); + // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + std::unique_ptr<NullEncrypter> encrypter = + QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT); + connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter)); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + // Lost [0, 1000). + QuicCryptoFrame lost_frame(ENCRYPTION_NONE, 0, 1000); + stream_->OnCryptoFrameLost(&lost_frame); + EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); + // Lost [1200, 2000). + lost_frame = QuicCryptoFrame(ENCRYPTION_NONE, 1200, 150); + stream_->OnCryptoFrameLost(&lost_frame); + lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650); + stream_->OnCryptoFrameLost(&lost_frame); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1000, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + // Verify [1200, 2000) are sent in [1200, 1350) and [1350, 2000) because of + // they are in different encryption levels. + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 150, 1200)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WritePendingCryptoRetransmission(); + EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); + // Verify connection's encryption level has restored. + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); +} + +TEST_F(QuicCryptoStreamTest, NeuterUnencryptedStreamData) { + if (connection_->transport_version() >= QUIC_VERSION_47) { + return; + } + // Send [0, 1350) in ENCRYPTION_NONE. + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); + QuicString data(1350, 'a'); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 0, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(data, false, nullptr); + // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 1350, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(data, false, nullptr); + + // Lost [0, 1350). + stream_->OnStreamFrameLost(0, 1350, false); + EXPECT_TRUE(stream_->HasPendingRetransmission()); + // Neuters [0, 1350). + stream_->NeuterUnencryptedStreamData(); + EXPECT_FALSE(stream_->HasPendingRetransmission()); + // Lost [0, 1350) again. + stream_->OnStreamFrameLost(0, 1350, false); + EXPECT_FALSE(stream_->HasPendingRetransmission()); + + // Lost [1350, 2000). + stream_->OnStreamFrameLost(1350, 650, false); + EXPECT_TRUE(stream_->HasPendingRetransmission()); + stream_->NeuterUnencryptedStreamData(); + EXPECT_TRUE(stream_->HasPendingRetransmission()); +} + +TEST_F(QuicCryptoStreamTest, NeuterUnencryptedCryptoData) { + if (connection_->transport_version() < QUIC_VERSION_47) { + return; + } + // Send [0, 1350) in ENCRYPTION_NONE. + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); + QuicString data(1350, 'a'); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_NONE, data); + // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + std::unique_ptr<NullEncrypter> encrypter = + QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT); + connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter)); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); + + // Lost [0, 1350). + QuicCryptoFrame lost_frame(ENCRYPTION_NONE, 0, 1350); + stream_->OnCryptoFrameLost(&lost_frame); + EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); + // Neuters [0, 1350). + stream_->NeuterUnencryptedStreamData(); + EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); + // Lost [0, 1350) again. + stream_->OnCryptoFrameLost(&lost_frame); + EXPECT_FALSE(stream_->HasPendingCryptoRetransmission()); + + // Lost [1350, 2000), which starts at offset 0 at the ENCRYPTION_ZERO_RTT + // level. + lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650); + stream_->OnCryptoFrameLost(&lost_frame); + EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); + stream_->NeuterUnencryptedStreamData(); + EXPECT_TRUE(stream_->HasPendingCryptoRetransmission()); +} + +TEST_F(QuicCryptoStreamTest, RetransmitStreamData) { + if (connection_->transport_version() >= QUIC_VERSION_47) { + return; + } + InSequence s; + // Send [0, 1350) in ENCRYPTION_NONE. + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); + QuicString data(1350, 'a'); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 0, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(data, false, nullptr); + // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 1350, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(data, false, nullptr); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + // Ack [2000, 2500). + QuicByteCount newly_acked_length = 0; + stream_->OnStreamFrameAcked(2000, 500, false, QuicTime::Delta::Zero(), + &newly_acked_length); + EXPECT_EQ(500u, newly_acked_length); + + // Force crypto stream to send [1350, 2700) and only [1350, 1500) is consumed. + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 650, 1350, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData( + stream_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), 150, + 1350, NO_FIN); + })); + + EXPECT_FALSE(stream_->RetransmitStreamData(1350, 1350, false)); + // Verify connection's encryption level has restored. + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + // Force session to send [1350, 1500) again and all data is consumed. + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 650, 1350, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 200, 2500, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_TRUE(stream_->RetransmitStreamData(1350, 1350, false)); + // Verify connection's encryption level has restored. + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + EXPECT_CALL(session_, WritevData(_, _, _, _, _)).Times(0); + // Force to send an empty frame. + EXPECT_TRUE(stream_->RetransmitStreamData(0, 0, false)); +} + +TEST_F(QuicCryptoStreamTest, RetransmitStreamDataWithCryptoFrames) { + if (connection_->transport_version() < QUIC_VERSION_47) { + return; + } + InSequence s; + // Send [0, 1350) in ENCRYPTION_NONE. + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); + QuicString data(1350, 'a'); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_NONE, data); + // Send [1350, 2700) in ENCRYPTION_ZERO_RTT. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + std::unique_ptr<NullEncrypter> encrypter = + QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT); + connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter)); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level()); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + // Ack [2000, 2500). + QuicCryptoFrame acked_frame(ENCRYPTION_ZERO_RTT, 650, 500); + EXPECT_TRUE( + stream_->OnCryptoFrameAcked(acked_frame, QuicTime::Delta::Zero())); + + // Retransmit only [1350, 1500). + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 150, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + QuicCryptoFrame frame_to_retransmit(ENCRYPTION_ZERO_RTT, 0, 150); + stream_->RetransmitData(&frame_to_retransmit); + + // Verify connection's encryption level has restored. + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + // Retransmit [1350, 2700) again and all data is sent. + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 200, 1150)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + frame_to_retransmit = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 1350); + stream_->RetransmitData(&frame_to_retransmit); + // Verify connection's encryption level has restored. + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level()); + + EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0); + // Force to send an empty frame. + QuicCryptoFrame empty_frame(ENCRYPTION_FORWARD_SECURE, 0, 0); + stream_->RetransmitData(&empty_frame); +} + +// Regression test for b/115926584. +TEST_F(QuicCryptoStreamTest, HasUnackedCryptoData) { + if (connection_->transport_version() >= QUIC_VERSION_47) { + return; + } + QuicString data(1350, 'a'); + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 0, _)) + .WillOnce(testing::Return(QuicConsumedData(0, false))); + stream_->WriteOrBufferData(data, false, nullptr); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + // Although there is no outstanding data, verify session has pending crypto + // data. + EXPECT_EQ(GetQuicReloadableFlag(quic_fix_has_pending_crypto_data), + session_.HasUnackedCryptoData()); + + EXPECT_CALL( + session_, + WritevData(_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + 1350, 0, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->OnCanWrite(); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_TRUE(session_.HasUnackedCryptoData()); +} + +TEST_F(QuicCryptoStreamTest, HasUnackedCryptoDataWithCryptoFrames) { + if (connection_->transport_version() < QUIC_VERSION_47) { + return; + } + // Send [0, 1350) in ENCRYPTION_NONE. + EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level()); + QuicString data(1350, 'a'); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0)) + .WillOnce(Invoke(connection_, + &MockQuicConnection::QuicConnection_SendCryptoData)); + stream_->WriteCryptoData(ENCRYPTION_NONE, data); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_TRUE(session_.HasUnackedCryptoData()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_data_reader.cc b/quic/core/quic_data_reader.cc new file mode 100644 index 0000000..e1fbffe --- /dev/null +++ b/quic/core/quic_data_reader.cc
@@ -0,0 +1,313 @@ +// Copyright (c) 2012 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/quic_data_reader.h" + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +QuicDataReader::QuicDataReader(const char* data, const size_t len) + : QuicDataReader(data, len, NETWORK_BYTE_ORDER) {} + +QuicDataReader::QuicDataReader(const char* data, + const size_t len, + Endianness endianness) + : data_(data), len_(len), pos_(0), endianness_(endianness) {} + +bool QuicDataReader::ReadUInt8(uint8_t* result) { + return ReadBytes(result, sizeof(*result)); +} + +bool QuicDataReader::ReadUInt16(uint16_t* result) { + if (!ReadBytes(result, sizeof(*result))) { + return false; + } + if (endianness_ == NETWORK_BYTE_ORDER) { + *result = QuicEndian::NetToHost16(*result); + } + return true; +} + +bool QuicDataReader::ReadUInt32(uint32_t* result) { + if (!ReadBytes(result, sizeof(*result))) { + return false; + } + if (endianness_ == NETWORK_BYTE_ORDER) { + *result = QuicEndian::NetToHost32(*result); + } + return true; +} + +bool QuicDataReader::ReadUInt64(uint64_t* result) { + if (!ReadBytes(result, sizeof(*result))) { + return false; + } + if (endianness_ == NETWORK_BYTE_ORDER) { + *result = QuicEndian::NetToHost64(*result); + } + return true; +} + +bool QuicDataReader::ReadBytesToUInt64(size_t num_bytes, uint64_t* result) { + *result = 0u; + if (num_bytes > sizeof(*result)) { + return false; + } + if (endianness_ == HOST_BYTE_ORDER) { + return ReadBytes(result, num_bytes); + } + + if (!ReadBytes(reinterpret_cast<char*>(result) + sizeof(*result) - num_bytes, + num_bytes)) { + return false; + } + *result = QuicEndian::NetToHost64(*result); + return true; +} + +bool QuicDataReader::ReadUFloat16(uint64_t* result) { + uint16_t value; + if (!ReadUInt16(&value)) { + return false; + } + + *result = value; + if (*result < (1 << kUFloat16MantissaEffectiveBits)) { + // Fast path: either the value is denormalized (no hidden bit), or + // normalized (hidden bit set, exponent offset by one) with exponent zero. + // Zero exponent offset by one sets the bit exactly where the hidden bit is. + // So in both cases the value encodes itself. + return true; + } + + uint16_t exponent = + value >> kUFloat16MantissaBits; // No sign extend on uint! + // After the fast pass, the exponent is at least one (offset by one). + // Un-offset the exponent. + --exponent; + DCHECK_GE(exponent, 1); + DCHECK_LE(exponent, kUFloat16MaxExponent); + // Here we need to clear the exponent and set the hidden bit. We have already + // decremented the exponent, so when we subtract it, it leaves behind the + // hidden bit. + *result -= exponent << kUFloat16MantissaBits; + *result <<= exponent; + DCHECK_GE(*result, + static_cast<uint64_t>(1 << kUFloat16MantissaEffectiveBits)); + DCHECK_LE(*result, kUFloat16MaxValue); + return true; +} + +bool QuicDataReader::ReadStringPiece16(QuicStringPiece* result) { + // Read resultant length. + uint16_t result_len; + if (!ReadUInt16(&result_len)) { + // OnFailure() already called. + return false; + } + + return ReadStringPiece(result, result_len); +} + +bool QuicDataReader::ReadStringPiece(QuicStringPiece* result, size_t size) { + // Make sure that we have enough data to read. + if (!CanRead(size)) { + OnFailure(); + return false; + } + + // Set result. + *result = QuicStringPiece(data_ + pos_, size); + + // Iterate. + pos_ += size; + + return true; +} + +bool QuicDataReader::ReadConnectionId(QuicConnectionId* connection_id, + uint8_t length) { + DCHECK_LE(length, kQuicMaxConnectionIdLength); + if (length == 0) { + connection_id->set_length(0); + return true; + } + + const bool ok = ReadBytes(connection_id->mutable_data(), length); + if (ok) { + connection_id->set_length(length); + } + return ok; +} + +bool QuicDataReader::ReadTag(uint32_t* tag) { + return ReadBytes(tag, sizeof(*tag)); +} + +QuicStringPiece QuicDataReader::ReadRemainingPayload() { + QuicStringPiece payload = PeekRemainingPayload(); + pos_ = len_; + return payload; +} + +QuicStringPiece QuicDataReader::PeekRemainingPayload() const { + return QuicStringPiece(data_ + pos_, len_ - pos_); +} + +bool QuicDataReader::ReadBytes(void* result, size_t size) { + // Make sure that we have enough data to read. + if (!CanRead(size)) { + OnFailure(); + return false; + } + + // Read into result. + memcpy(result, data_ + pos_, size); + + // Iterate. + pos_ += size; + + return true; +} + +bool QuicDataReader::IsDoneReading() const { + return len_ == pos_; +} + +QuicVariableLengthIntegerLength QuicDataReader::PeekVarInt62Length() { + DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER); + const unsigned char* next = + reinterpret_cast<const unsigned char*>(data_ + pos_); + if (BytesRemaining() == 0) { + return VARIABLE_LENGTH_INTEGER_LENGTH_0; + } + return static_cast<QuicVariableLengthIntegerLength>( + 1 << ((*next & 0b11000000) >> 6)); +} + +size_t QuicDataReader::BytesRemaining() const { + return len_ - pos_; +} + +bool QuicDataReader::TruncateRemaining(size_t truncation_length) { + if (truncation_length > BytesRemaining()) { + return false; + } + len_ = pos_ + truncation_length; + return true; +} + +bool QuicDataReader::CanRead(size_t bytes) const { + return bytes <= (len_ - pos_); +} + +void QuicDataReader::OnFailure() { + // Set our iterator to the end of the buffer so that further reads fail + // immediately. + pos_ = len_; +} + +uint8_t QuicDataReader::PeekByte() const { + if (pos_ >= len_) { + QUIC_BUG << "Reading is done, cannot peek next byte. Tried to read pos = " + << pos_ << " buffer length = " << len_; + return 0; + } + return data_[pos_]; +} + +// Read an IETF/QUIC formatted 62-bit Variable Length Integer. +// +// Performance notes +// +// Measurements and experiments showed that unrolling the four cases +// like this and dereferencing next_ as we do (*(next_+n) --- and then +// doing a single pos_+=x at the end) gains about 10% over making a +// loop and dereferencing next_ such as *(next_++) +// +// Using a register for pos_ was not helpful. +// +// Branches are ordered to increase the likelihood of the first being +// taken. +// +// Low-level optimization is useful here because this function will be +// called frequently, leading to outsize benefits. +bool QuicDataReader::ReadVarInt62(uint64_t* result) { + DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER); + + size_t remaining = BytesRemaining(); + const unsigned char* next = + reinterpret_cast<const unsigned char*>(data_ + pos_); + if (remaining != 0) { + switch (*next & 0xc0) { + case 0xc0: + // Leading 0b11...... is 8 byte encoding + if (remaining >= 8) { + *result = (static_cast<uint64_t>((*(next)) & 0x3f) << 56) + + (static_cast<uint64_t>(*(next + 1)) << 48) + + (static_cast<uint64_t>(*(next + 2)) << 40) + + (static_cast<uint64_t>(*(next + 3)) << 32) + + (static_cast<uint64_t>(*(next + 4)) << 24) + + (static_cast<uint64_t>(*(next + 5)) << 16) + + (static_cast<uint64_t>(*(next + 6)) << 8) + + (static_cast<uint64_t>(*(next + 7)) << 0); + pos_ += 8; + return true; + } + return false; + + case 0x80: + // Leading 0b10...... is 4 byte encoding + if (remaining >= 4) { + *result = (((*(next)) & 0x3f) << 24) + (((*(next + 1)) << 16)) + + (((*(next + 2)) << 8)) + (((*(next + 3)) << 0)); + pos_ += 4; + return true; + } + return false; + + case 0x40: + // Leading 0b01...... is 2 byte encoding + if (remaining >= 2) { + *result = (((*(next)) & 0x3f) << 8) + (*(next + 1)); + pos_ += 2; + return true; + } + return false; + + case 0x00: + // Leading 0b00...... is 1 byte encoding + *result = (*next) & 0x3f; + pos_++; + return true; + } + } + return false; +} + +bool QuicDataReader::ReadVarIntStreamId(QuicStreamId* result) { + uint64_t temp_uint64; + // TODO(fkastenholz): We should disambiguate read-errors from + // value errors. + if (!this->ReadVarInt62(&temp_uint64)) { + return false; + } + if (temp_uint64 > kMaxQuicStreamId) { + return false; + } + *result = static_cast<QuicStreamId>(temp_uint64); + return true; +} + +QuicString QuicDataReader::DebugString() const { + return QuicStrCat(" { length: ", len_, ", position: ", pos_, " }"); +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_data_reader.h b/quic/core/quic_data_reader.h new file mode 100644 index 0000000..fff0ffd --- /dev/null +++ b/quic/core/quic_data_reader.h
@@ -0,0 +1,178 @@ +// Copyright (c) 2012 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_QUIC_DATA_READER_H_ +#define QUICHE_QUIC_CORE_QUIC_DATA_READER_H_ + +#include <cstddef> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +// Used for reading QUIC data. Though there isn't really anything terribly +// QUIC-specific here, it's a helper class that's useful when doing QUIC +// framing. +// +// To use, simply construct a QuicDataReader using the underlying buffer that +// you'd like to read fields from, then call one of the Read*() methods to +// actually do some reading. +// +// This class keeps an internal iterator to keep track of what's already been +// read and each successive Read*() call automatically increments said iterator +// on success. On failure, internal state of the QuicDataReader should not be +// trusted and it is up to the caller to throw away the failed instance and +// handle the error as appropriate. None of the Read*() methods should ever be +// called after failure, as they will also fail immediately. +class QUIC_EXPORT_PRIVATE QuicDataReader { + public: + // Constructs a reader using NETWORK_BYTE_ORDER endianness. + // Caller must provide an underlying buffer to work on. + QuicDataReader(const char* data, const size_t len); + // Constructs a reader using the specified endianness. + // Caller must provide an underlying buffer to work on. + QuicDataReader(const char* data, const size_t len, Endianness endianness); + QuicDataReader(const QuicDataReader&) = delete; + QuicDataReader& operator=(const QuicDataReader&) = delete; + + // Empty destructor. + ~QuicDataReader() {} + + // Reads an 8/16/32/64-bit unsigned integer into the given output + // parameter. Forwards the internal iterator on success. Returns true on + // success, false otherwise. + bool ReadUInt8(uint8_t* result); + bool ReadUInt16(uint16_t* result); + bool ReadUInt32(uint32_t* result); + bool ReadUInt64(uint64_t* result); + + // Set |result| to 0, then read |num_bytes| bytes in the correct byte order + // into least significant bytes of |result|. + bool ReadBytesToUInt64(size_t num_bytes, uint64_t* result); + + // Reads a 16-bit unsigned float into the given output parameter. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadUFloat16(uint64_t* result); + + // Reads a string prefixed with 16-bit length into the given output parameter. + // + // NOTE: Does not copy but rather references strings in the underlying buffer. + // This should be kept in mind when handling memory management! + // + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadStringPiece16(QuicStringPiece* result); + + // Reads a given number of bytes into the given buffer. The buffer + // must be of adequate size. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadStringPiece(QuicStringPiece* result, size_t len); + + // Reads connection ID into the given output parameter. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadConnectionId(QuicConnectionId* connection_id, uint8_t length); + + // Reads tag represented as 32-bit unsigned integer into given output + // parameter. Tags are in big endian on the wire (e.g., CHLO is + // 'C','H','L','O') and are read in byte order, so tags in memory are in big + // endian. + bool ReadTag(uint32_t* tag); + + // Returns the remaining payload as a QuicStringPiece. + // + // NOTE: Does not copy but rather references strings in the underlying buffer. + // This should be kept in mind when handling memory management! + // + // Forwards the internal iterator. + QuicStringPiece ReadRemainingPayload(); + + // Returns the remaining payload as a QuicStringPiece. + // + // NOTE: Does not copy but rather references strings in the underlying buffer. + // This should be kept in mind when handling memory management! + // + // DOES NOT forward the internal iterator. + QuicStringPiece PeekRemainingPayload() const; + + // Reads a given number of bytes into the given buffer. The buffer + // must be of adequate size. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadBytes(void* result, size_t size); + + // Returns true if the entirety of the underlying buffer has been read via + // Read*() calls. + bool IsDoneReading() const; + + // Returns the length in bytes of a variable length integer based on the next + // two bits available. Returns 1, 2, 4, or 8 on success, and 0 on failure. + QuicVariableLengthIntegerLength PeekVarInt62Length(); + + // Returns the number of bytes remaining to be read. + size_t BytesRemaining() const; + + // Truncates the reader down by reducing its internal length. + // If called immediately after calling this, BytesRemaining will + // return |truncation_length|. If truncation_length is less than the + // current value of BytesRemaining, this does nothing and returns false. + bool TruncateRemaining(size_t truncation_length); + + // Returns the next byte that to be read. Must not be called when there are no + // bytes to be read. + // + // DOES NOT forward the internal iterator. + uint8_t PeekByte() const; + + void set_endianness(Endianness endianness) { endianness_ = endianness; } + + // Read an IETF-encoded Variable Length Integer and place the result + // in |*result|. + // Returns true if it works, false if not. The only error is that + // there is not enough in the buffer to read the number. + // If there is an error, |*result| is not altered. + // Numbers are encoded per the rules in draft-ietf-quic-transport-10.txt + // and that the integers in the range 0 ... (2^62)-1. + bool ReadVarInt62(uint64_t* result); + + // Convenience method that reads a StreamId. + // Atempts to read a Stream ID into |result| using ReadVarInt62 and + // returns false if there is a read error or if the value is + // greater than (2^32)-1. + bool ReadVarIntStreamId(QuicStreamId* result); + + QuicString DebugString() const; + + private: + // Returns true if the underlying buffer has enough room to read the given + // amount of bytes. + bool CanRead(size_t bytes) const; + + // To be called when a read fails for any reason. + void OnFailure(); + + // TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not + // char. The data buffer that we're reading from. + const char* data_; + + // The length of the data buffer that we're reading from. + size_t len_; + + // The location of the next read from our data buffer. + size_t pos_; + + // The endianness to read integers and floating numbers. + Endianness endianness_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_DATA_READER_H_
diff --git a/quic/core/quic_data_writer.cc b/quic/core/quic_data_writer.cc new file mode 100644 index 0000000..c70bbdd --- /dev/null +++ b/quic/core/quic_data_writer.cc
@@ -0,0 +1,356 @@ +// Copyright (c) 2012 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/quic_data_writer.h" + +#include <algorithm> +#include <limits> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +QuicDataWriter::QuicDataWriter(size_t size, char* buffer) + : QuicDataWriter(size, buffer, NETWORK_BYTE_ORDER) {} + +QuicDataWriter::QuicDataWriter(size_t size, char* buffer, Endianness endianness) + : buffer_(buffer), capacity_(size), length_(0), endianness_(endianness) {} + +QuicDataWriter::~QuicDataWriter() {} + +char* QuicDataWriter::data() { + return buffer_; +} + +bool QuicDataWriter::WriteUInt8(uint8_t value) { + return WriteBytes(&value, sizeof(value)); +} + +bool QuicDataWriter::WriteUInt16(uint16_t value) { + if (endianness_ == NETWORK_BYTE_ORDER) { + value = QuicEndian::HostToNet16(value); + } + return WriteBytes(&value, sizeof(value)); +} + +bool QuicDataWriter::WriteUInt32(uint32_t value) { + if (endianness_ == NETWORK_BYTE_ORDER) { + value = QuicEndian::HostToNet32(value); + } + return WriteBytes(&value, sizeof(value)); +} + +bool QuicDataWriter::WriteUInt64(uint64_t value) { + if (endianness_ == NETWORK_BYTE_ORDER) { + value = QuicEndian::HostToNet64(value); + } + return WriteBytes(&value, sizeof(value)); +} + +bool QuicDataWriter::WriteBytesToUInt64(size_t num_bytes, uint64_t value) { + if (num_bytes > sizeof(value)) { + return false; + } + if (endianness_ == HOST_BYTE_ORDER) { + return WriteBytes(&value, num_bytes); + } + + value = QuicEndian::HostToNet64(value); + return WriteBytes(reinterpret_cast<char*>(&value) + sizeof(value) - num_bytes, + num_bytes); +} + +bool QuicDataWriter::WriteUFloat16(uint64_t value) { + uint16_t result; + if (value < (UINT64_C(1) << kUFloat16MantissaEffectiveBits)) { + // Fast path: either the value is denormalized, or has exponent zero. + // Both cases are represented by the value itself. + result = static_cast<uint16_t>(value); + } else if (value >= kUFloat16MaxValue) { + // Value is out of range; clamp it to the maximum representable. + result = std::numeric_limits<uint16_t>::max(); + } else { + // The highest bit is between position 13 and 42 (zero-based), which + // corresponds to exponent 1-30. In the output, mantissa is from 0 to 10, + // hidden bit is 11 and exponent is 11 to 15. Shift the highest bit to 11 + // and count the shifts. + uint16_t exponent = 0; + for (uint16_t offset = 16; offset > 0; offset /= 2) { + // Right-shift the value until the highest bit is in position 11. + // For offset of 16, 8, 4, 2 and 1 (binary search over 1-30), + // shift if the bit is at or above 11 + offset. + if (value >= (UINT64_C(1) << (kUFloat16MantissaBits + offset))) { + exponent += offset; + value >>= offset; + } + } + + DCHECK_GE(exponent, 1); + DCHECK_LE(exponent, kUFloat16MaxExponent); + DCHECK_GE(value, UINT64_C(1) << kUFloat16MantissaBits); + DCHECK_LT(value, UINT64_C(1) << kUFloat16MantissaEffectiveBits); + + // Hidden bit (position 11) is set. We should remove it and increment the + // exponent. Equivalently, we just add it to the exponent. + // This hides the bit. + result = static_cast<uint16_t>(value + (exponent << kUFloat16MantissaBits)); + } + + if (endianness_ == NETWORK_BYTE_ORDER) { + result = QuicEndian::HostToNet16(result); + } + return WriteBytes(&result, sizeof(result)); +} + +bool QuicDataWriter::WriteStringPiece16(QuicStringPiece val) { + if (val.size() > std::numeric_limits<uint16_t>::max()) { + return false; + } + if (!WriteUInt16(static_cast<uint16_t>(val.size()))) { + return false; + } + return WriteBytes(val.data(), val.size()); +} + +bool QuicDataWriter::WriteStringPiece(QuicStringPiece val) { + return WriteBytes(val.data(), val.size()); +} + +char* QuicDataWriter::BeginWrite(size_t length) { + if (length_ > capacity_) { + return nullptr; + } + + if (capacity_ - length_ < length) { + return nullptr; + } + +#ifdef ARCH_CPU_64_BITS + DCHECK_LE(length, std::numeric_limits<uint32_t>::max()); +#endif + + return buffer_ + length_; +} + +bool QuicDataWriter::WriteBytes(const void* data, size_t data_len) { + char* dest = BeginWrite(data_len); + if (!dest) { + return false; + } + + memcpy(dest, data, data_len); + + length_ += data_len; + return true; +} + +bool QuicDataWriter::WriteRepeatedByte(uint8_t byte, size_t count) { + char* dest = BeginWrite(count); + if (!dest) { + return false; + } + + memset(dest, byte, count); + + length_ += count; + return true; +} + +void QuicDataWriter::WritePadding() { + DCHECK_LE(length_, capacity_); + if (length_ > capacity_) { + return; + } + memset(buffer_ + length_, 0x00, capacity_ - length_); + length_ = capacity_; +} + +bool QuicDataWriter::WritePaddingBytes(size_t count) { + return WriteRepeatedByte(0x00, count); +} + +bool QuicDataWriter::WriteConnectionId(QuicConnectionId connection_id) { + if (connection_id.IsEmpty()) { + return true; + } + return WriteBytes(connection_id.data(), connection_id.length()); +} + +bool QuicDataWriter::WriteTag(uint32_t tag) { + return WriteBytes(&tag, sizeof(tag)); +} + +bool QuicDataWriter::WriteRandomBytes(QuicRandom* random, size_t length) { + char* dest = BeginWrite(length); + if (!dest) { + return false; + } + + random->RandBytes(dest, length); + length_ += length; + return true; +} + +// Converts a uint64_t into an IETF/Quic formatted Variable Length +// Integer. IETF Variable Length Integers have 62 significant bits, so +// the value to write must be in the range of 0..(2^62)-1. +// +// Performance notes +// +// Measurements and experiments showed that unrolling the four cases +// like this and dereferencing next_ as we do (*(next_+n)) gains about +// 10% over making a loop and dereferencing it as *(next_++) +// +// Using a register for next didn't help. +// +// Branches are ordered to increase the likelihood of the first being +// taken. +// +// Low-level optimization is useful here because this function will be +// called frequently, leading to outsize benefits. +bool QuicDataWriter::WriteVarInt62(uint64_t value) { + DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER); + + size_t remaining = capacity_ - length_; + char* next = buffer_ + length_; + + if ((value & kVarInt62ErrorMask) == 0) { + // We know the high 2 bits are 0 so |value| is legal. + // We can do the encoding. + if ((value & kVarInt62Mask8Bytes) != 0) { + // Someplace in the high-4 bytes is a 1-bit. Do an 8-byte + // encoding. + if (remaining >= 8) { + *(next + 0) = ((value >> 56) & 0x3f) + 0xc0; + *(next + 1) = (value >> 48) & 0xff; + *(next + 2) = (value >> 40) & 0xff; + *(next + 3) = (value >> 32) & 0xff; + *(next + 4) = (value >> 24) & 0xff; + *(next + 5) = (value >> 16) & 0xff; + *(next + 6) = (value >> 8) & 0xff; + *(next + 7) = value & 0xff; + length_ += 8; + return true; + } + return false; + } + // The high-order-4 bytes are all 0, check for a 1, 2, or 4-byte + // encoding + if ((value & kVarInt62Mask4Bytes) != 0) { + // The encoding will not fit into 2 bytes, Do a 4-byte + // encoding. + if (remaining >= 4) { + *(next + 0) = ((value >> 24) & 0x3f) + 0x80; + *(next + 1) = (value >> 16) & 0xff; + *(next + 2) = (value >> 8) & 0xff; + *(next + 3) = value & 0xff; + length_ += 4; + return true; + } + return false; + } + // The high-order bits are all 0. Check to see if the number + // can be encoded as one or two bytes. One byte encoding has + // only 6 significant bits (bits 0xffffffff ffffffc0 are all 0). + // Two byte encoding has more than 6, but 14 or less significant + // bits (bits 0xffffffff ffffc000 are 0 and 0x00000000 00003fc0 + // are not 0) + if ((value & kVarInt62Mask2Bytes) != 0) { + // Do 2-byte encoding + if (remaining >= 2) { + *(next + 0) = ((value >> 8) & 0x3f) + 0x40; + *(next + 1) = (value)&0xff; + length_ += 2; + return true; + } + return false; + } + if (remaining >= 1) { + // Do 1-byte encoding + *next = (value & 0x3f); + length_ += 1; + return true; + } + return false; + } + // Can not encode, high 2 bits not 0 + return false; +} + +bool QuicDataWriter::WriteVarInt62( + uint64_t value, + QuicVariableLengthIntegerLength write_length) { + DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER); + + size_t remaining = capacity_ - length_; + if (remaining < write_length) { + return false; + } + + const QuicVariableLengthIntegerLength min_length = GetVarInt62Len(value); + if (write_length < min_length) { + QUIC_BUG << "Cannot write value " << value << " with write_length " + << write_length; + return false; + } + if (write_length == min_length) { + return WriteVarInt62(value); + } + + if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_2) { + return WriteUInt8(0b01000000) && WriteUInt8(value); + } + if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_4) { + return WriteUInt8(0b10000000) && WriteUInt8(0) && WriteUInt16(value); + } + if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_8) { + return WriteUInt8(0b11000000) && WriteUInt8(0) && WriteUInt16(0) && + WriteUInt32(value); + } + + QUIC_BUG << "Invalid write_length " << static_cast<int>(write_length); + return false; +} + +// static +QuicVariableLengthIntegerLength QuicDataWriter::GetVarInt62Len(uint64_t value) { + if ((value & kVarInt62ErrorMask) != 0) { + QUIC_BUG << "Attempted to encode a value, " << value + << ", that is too big for VarInt62"; + return VARIABLE_LENGTH_INTEGER_LENGTH_0; + } + if ((value & kVarInt62Mask8Bytes) != 0) { + return VARIABLE_LENGTH_INTEGER_LENGTH_8; + } + if ((value & kVarInt62Mask4Bytes) != 0) { + return VARIABLE_LENGTH_INTEGER_LENGTH_4; + } + if ((value & kVarInt62Mask2Bytes) != 0) { + return VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + return VARIABLE_LENGTH_INTEGER_LENGTH_1; +} + +bool QuicDataWriter::WriteStringPieceVarInt62( + const QuicStringPiece& string_piece) { + if (!WriteVarInt62(string_piece.size())) { + return false; + } + if (!string_piece.empty()) { + if (!WriteBytes(string_piece.data(), string_piece.size())) { + return false; + } + } + return true; +} + +QuicString QuicDataWriter::DebugString() const { + return QuicStrCat(" { capacity: ", capacity_, ", length: ", length_, " }"); +} + +} // namespace quic
diff --git a/quic/core/quic_data_writer.h b/quic/core/quic_data_writer.h new file mode 100644 index 0000000..e107fcb --- /dev/null +++ b/quic/core/quic_data_writer.h
@@ -0,0 +1,145 @@ +// Copyright (c) 2012 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_QUIC_DATA_WRITER_H_ +#define QUICHE_QUIC_CORE_QUIC_DATA_WRITER_H_ + +#include <cstddef> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +class QuicRandom; + +// Maximum value that can be properly encoded using VarInt62 coding. +const uint64_t kVarInt62MaxValue = UINT64_C(0x3fffffffffffffff); + +// VarInt62 encoding masks +// If a uint64_t anded with a mask is not 0 then the value is encoded +// using that length (or is too big, in the case of kVarInt62ErrorMask). +// Values must be checked in order (error, 8-, 4-, and then 2- bytes) +// and if none are non-0, the value is encoded in 1 byte. +const uint64_t kVarInt62ErrorMask = UINT64_C(0xc000000000000000); +const uint64_t kVarInt62Mask8Bytes = UINT64_C(0x3fffffffc0000000); +const uint64_t kVarInt62Mask4Bytes = UINT64_C(0x000000003fffc000); +const uint64_t kVarInt62Mask2Bytes = UINT64_C(0x0000000000003fc0); + +// This class provides facilities for packing QUIC data. +// +// The QuicDataWriter supports appending primitive values (int, string, etc) +// to a frame instance. The internal memory buffer is exposed as the "data" +// of the QuicDataWriter. +class QUIC_EXPORT_PRIVATE QuicDataWriter { + public: + // Creates a QuicDataWriter where |buffer| is not owned + // using NETWORK_BYTE_ORDER endianness. + QuicDataWriter(size_t size, char* buffer); + // Creates a QuicDataWriter where |buffer| is not owned + // using the specified endianness. + QuicDataWriter(size_t size, char* buffer, Endianness endianness); + QuicDataWriter(const QuicDataWriter&) = delete; + QuicDataWriter& operator=(const QuicDataWriter&) = delete; + + ~QuicDataWriter(); + + // Returns the size of the QuicDataWriter's data. + size_t length() const { return length_; } + + // Retrieves the buffer from the QuicDataWriter without changing ownership. + char* data(); + + // Methods for adding to the payload. These values are appended to the end + // of the QuicDataWriter payload. + + // Writes 8/16/32/64-bit unsigned integers. + bool WriteUInt8(uint8_t value); + bool WriteUInt16(uint16_t value); + bool WriteUInt32(uint32_t value); + bool WriteUInt64(uint64_t value); + + // Write an unsigned-integer value per the IETF QUIC/Variable Length + // Integer encoding rules (see draft-ietf-quic-transport-08.txt). + // IETF Variable Length Integers have 62 significant bits, so the + // value to write must be in the range of 0...(2^62)-1. Returns + // false if the value is out of range or if there is no room in the + // buffer. + bool WriteVarInt62(uint64_t value); + + // Same as WriteVarInt62(uint64_t), but forces an encoding size to write to. + // This is not as optimized as WriteVarInt62(uint64_t). + // Returns false if the value does not fit in the specified write_length or if + // there is no room in the buffer. + bool WriteVarInt62(uint64_t value, + QuicVariableLengthIntegerLength write_length); + + // Writes a string piece as a consecutive length/content pair. The + // length is VarInt62 encoded. + bool WriteStringPieceVarInt62(const QuicStringPiece& string_piece); + + // Utility function to return the number of bytes needed to encode + // the given value using IETF VarInt62 encoding. Returns the number + // of bytes required to encode the given integer or 0 if the value + // is too large to encode. + static QuicVariableLengthIntegerLength GetVarInt62Len(uint64_t value); + + // Writes least significant |num_bytes| of a 64-bit unsigned integer in the + // correct byte order. + bool WriteBytesToUInt64(size_t num_bytes, uint64_t value); + + // Write unsigned floating point corresponding to the value. Large values are + // clamped to the maximum representable (kUFloat16MaxValue). Values that can + // not be represented directly are rounded down. + bool WriteUFloat16(uint64_t value); + bool WriteStringPiece(QuicStringPiece val); + bool WriteStringPiece16(QuicStringPiece val); + bool WriteBytes(const void* data, size_t data_len); + bool WriteRepeatedByte(uint8_t byte, size_t count); + // Fills the remaining buffer with null characters. + void WritePadding(); + // Write padding of |count| bytes. + bool WritePaddingBytes(size_t count); + + // Write connection ID to the payload. + bool WriteConnectionId(QuicConnectionId connection_id); + + // Write tag as a 32-bit unsigned integer to the payload. As tags are already + // converted to big endian (e.g., CHLO is 'C','H','L','O') in memory by TAG or + // MakeQuicTag and tags are written in byte order, so tags on the wire are + // in big endian. + bool WriteTag(uint32_t tag); + + // Write |length| random bytes generated by |random|. + bool WriteRandomBytes(QuicRandom* random, size_t length); + + size_t capacity() const { return capacity_; } + + size_t remaining() const { return capacity_ - length_; } + + QuicString DebugString() const; + + private: + // Returns the location that the data should be written at, or nullptr if + // there is not enough room. Call EndWrite with the returned offset and the + // given length to pad out for the next write. + char* BeginWrite(size_t length); + + // TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not + // char. + char* buffer_; + size_t capacity_; // Allocation size of payload (or -1 if buffer is const). + size_t length_; // Current length of the buffer. + + // The endianness to write integers and floating numbers. + Endianness endianness_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_DATA_WRITER_H_
diff --git a/quic/core/quic_data_writer_test.cc b/quic/core/quic_data_writer_test.cc new file mode 100644 index 0000000..aed14c1 --- /dev/null +++ b/quic/core/quic_data_writer_test.cc
@@ -0,0 +1,1102 @@ +// Copyright (c) 2012 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/quic_data_writer.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +char* AsChars(unsigned char* data) { + return reinterpret_cast<char*>(data); +} + +struct TestParams { + explicit TestParams(Endianness endianness) : endianness(endianness) {} + + Endianness endianness; +}; + +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + for (Endianness endianness : {NETWORK_BYTE_ORDER, HOST_BYTE_ORDER}) { + params.push_back(TestParams(endianness)); + } + return params; +} + +class QuicDataWriterTest : public QuicTestWithParam<TestParams> {}; + +INSTANTIATE_TEST_SUITE_P(QuicDataWriterTests, QuicDataWriterTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QuicDataWriterTest, SanityCheckUFloat16Consts) { + // Check the arithmetic on the constants - otherwise the values below make + // no sense. + EXPECT_EQ(30, kUFloat16MaxExponent); + EXPECT_EQ(11, kUFloat16MantissaBits); + EXPECT_EQ(12, kUFloat16MantissaEffectiveBits); + EXPECT_EQ(UINT64_C(0x3FFC0000000), kUFloat16MaxValue); +} + +TEST_P(QuicDataWriterTest, WriteUFloat16) { + struct TestCase { + uint64_t decoded; + uint16_t encoded; + }; + TestCase test_cases[] = { + // Small numbers represent themselves. + {0, 0}, + {1, 1}, + {2, 2}, + {3, 3}, + {4, 4}, + {5, 5}, + {6, 6}, + {7, 7}, + {15, 15}, + {31, 31}, + {42, 42}, + {123, 123}, + {1234, 1234}, + // Check transition through 2^11. + {2046, 2046}, + {2047, 2047}, + {2048, 2048}, + {2049, 2049}, + // Running out of mantissa at 2^12. + {4094, 4094}, + {4095, 4095}, + {4096, 4096}, + {4097, 4096}, + {4098, 4097}, + {4099, 4097}, + {4100, 4098}, + {4101, 4098}, + // Check transition through 2^13. + {8190, 6143}, + {8191, 6143}, + {8192, 6144}, + {8193, 6144}, + {8194, 6144}, + {8195, 6144}, + {8196, 6145}, + {8197, 6145}, + // Half-way through the exponents. + {0x7FF8000, 0x87FF}, + {0x7FFFFFF, 0x87FF}, + {0x8000000, 0x8800}, + {0xFFF0000, 0x8FFF}, + {0xFFFFFFF, 0x8FFF}, + {0x10000000, 0x9000}, + // Transition into the largest exponent. + {0x1FFFFFFFFFE, 0xF7FF}, + {0x1FFFFFFFFFF, 0xF7FF}, + {0x20000000000, 0xF800}, + {0x20000000001, 0xF800}, + {0x2003FFFFFFE, 0xF800}, + {0x2003FFFFFFF, 0xF800}, + {0x20040000000, 0xF801}, + {0x20040000001, 0xF801}, + // Transition into the max value and clamping. + {0x3FF80000000, 0xFFFE}, + {0x3FFBFFFFFFF, 0xFFFE}, + {0x3FFC0000000, 0xFFFF}, + {0x3FFC0000001, 0xFFFF}, + {0x3FFFFFFFFFF, 0xFFFF}, + {0x40000000000, 0xFFFF}, + {0xFFFFFFFFFFFFFFFF, 0xFFFF}, + }; + int num_test_cases = sizeof(test_cases) / sizeof(test_cases[0]); + + for (int i = 0; i < num_test_cases; ++i) { + char buffer[2]; + QuicDataWriter writer(2, buffer, GetParam().endianness); + EXPECT_TRUE(writer.WriteUFloat16(test_cases[i].decoded)); + uint16_t result = *reinterpret_cast<uint16_t*>(writer.data()); + if (GetParam().endianness == NETWORK_BYTE_ORDER) { + result = QuicEndian::HostToNet16(result); + } + EXPECT_EQ(test_cases[i].encoded, result); + } +} + +TEST_P(QuicDataWriterTest, ReadUFloat16) { + struct TestCase { + uint64_t decoded; + uint16_t encoded; + }; + TestCase test_cases[] = { + // There are fewer decoding test cases because encoding truncates, and + // decoding returns the smallest expansion. + // Small numbers represent themselves. + {0, 0}, + {1, 1}, + {2, 2}, + {3, 3}, + {4, 4}, + {5, 5}, + {6, 6}, + {7, 7}, + {15, 15}, + {31, 31}, + {42, 42}, + {123, 123}, + {1234, 1234}, + // Check transition through 2^11. + {2046, 2046}, + {2047, 2047}, + {2048, 2048}, + {2049, 2049}, + // Running out of mantissa at 2^12. + {4094, 4094}, + {4095, 4095}, + {4096, 4096}, + {4098, 4097}, + {4100, 4098}, + // Check transition through 2^13. + {8190, 6143}, + {8192, 6144}, + {8196, 6145}, + // Half-way through the exponents. + {0x7FF8000, 0x87FF}, + {0x8000000, 0x8800}, + {0xFFF0000, 0x8FFF}, + {0x10000000, 0x9000}, + // Transition into the largest exponent. + {0x1FFE0000000, 0xF7FF}, + {0x20000000000, 0xF800}, + {0x20040000000, 0xF801}, + // Transition into the max value. + {0x3FF80000000, 0xFFFE}, + {0x3FFC0000000, 0xFFFF}, + }; + int num_test_cases = sizeof(test_cases) / sizeof(test_cases[0]); + + for (int i = 0; i < num_test_cases; ++i) { + uint16_t encoded_ufloat = test_cases[i].encoded; + if (GetParam().endianness == NETWORK_BYTE_ORDER) { + encoded_ufloat = QuicEndian::HostToNet16(encoded_ufloat); + } + QuicDataReader reader(reinterpret_cast<char*>(&encoded_ufloat), 2, + GetParam().endianness); + uint64_t value; + EXPECT_TRUE(reader.ReadUFloat16(&value)); + EXPECT_EQ(test_cases[i].decoded, value); + } +} + +TEST_P(QuicDataWriterTest, RoundTripUFloat16) { + // Just test all 16-bit encoded values. 0 and max already tested above. + uint64_t previous_value = 0; + for (uint16_t i = 1; i < 0xFFFF; ++i) { + // Read the two bytes. + uint16_t read_number = i; + if (GetParam().endianness == NETWORK_BYTE_ORDER) { + read_number = QuicEndian::HostToNet16(read_number); + } + QuicDataReader reader(reinterpret_cast<char*>(&read_number), 2, + GetParam().endianness); + uint64_t value; + // All values must be decodable. + EXPECT_TRUE(reader.ReadUFloat16(&value)); + // Check that small numbers represent themselves + if (i < 4097) { + EXPECT_EQ(i, value); + } + // Check there's monotonic growth. + EXPECT_LT(previous_value, value); + // Check that precision is within 0.5% away from the denormals. + if (i > 2000) { + EXPECT_GT(previous_value * 1005, value * 1000); + } + // Check we're always within the promised range. + EXPECT_LT(value, UINT64_C(0x3FFC0000000)); + previous_value = value; + char buffer[6]; + QuicDataWriter writer(6, buffer, GetParam().endianness); + EXPECT_TRUE(writer.WriteUFloat16(value - 1)); + EXPECT_TRUE(writer.WriteUFloat16(value)); + EXPECT_TRUE(writer.WriteUFloat16(value + 1)); + // Check minimal decoding (previous decoding has previous encoding). + uint16_t encoded1 = *reinterpret_cast<uint16_t*>(writer.data()); + uint16_t encoded2 = *reinterpret_cast<uint16_t*>(writer.data() + 2); + uint16_t encoded3 = *reinterpret_cast<uint16_t*>(writer.data() + 4); + if (GetParam().endianness == NETWORK_BYTE_ORDER) { + encoded1 = QuicEndian::NetToHost16(encoded1); + encoded2 = QuicEndian::NetToHost16(encoded2); + encoded3 = QuicEndian::NetToHost16(encoded3); + } + EXPECT_EQ(i - 1, encoded1); + // Check roundtrip. + EXPECT_EQ(i, encoded2); + // Check next decoding. + EXPECT_EQ(i < 4096 ? i + 1 : i, encoded3); + } +} + +TEST_P(QuicDataWriterTest, WriteConnectionId) { + QuicConnectionId connection_id = + TestConnectionId(UINT64_C(0x0011223344556677)); + char big_endian[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + }; + EXPECT_EQ(connection_id.length(), QUIC_ARRAYSIZE(big_endian)); + ASSERT_LE(connection_id.length(), kQuicMaxConnectionIdLength); + char buffer[kQuicMaxConnectionIdLength]; + QuicDataWriter writer(connection_id.length(), buffer, GetParam().endianness); + EXPECT_TRUE(writer.WriteConnectionId(connection_id)); + test::CompareCharArraysWithHexError("connection_id", buffer, + connection_id.length(), big_endian, + connection_id.length()); + + QuicConnectionId read_connection_id; + QuicDataReader reader(buffer, connection_id.length(), GetParam().endianness); + EXPECT_TRUE( + reader.ReadConnectionId(&read_connection_id, QUIC_ARRAYSIZE(big_endian))); + EXPECT_EQ(connection_id, read_connection_id); +} + +TEST_P(QuicDataWriterTest, EmptyConnectionIds) { + QuicConnectionId empty_connection_id = EmptyQuicConnectionId(); + char buffer[2]; + QuicDataWriter writer(QUIC_ARRAYSIZE(buffer), buffer, GetParam().endianness); + EXPECT_TRUE(writer.WriteConnectionId(empty_connection_id)); + EXPECT_TRUE(writer.WriteUInt8(1)); + EXPECT_TRUE(writer.WriteConnectionId(empty_connection_id)); + EXPECT_TRUE(writer.WriteUInt8(2)); + EXPECT_TRUE(writer.WriteConnectionId(empty_connection_id)); + EXPECT_FALSE(writer.WriteUInt8(3)); + + EXPECT_EQ(buffer[0], 1); + EXPECT_EQ(buffer[1], 2); + + QuicConnectionId read_connection_id = TestConnectionId(); + uint8_t read_byte; + QuicDataReader reader(buffer, QUIC_ARRAYSIZE(buffer), GetParam().endianness); + EXPECT_TRUE(reader.ReadConnectionId(&read_connection_id, 0)); + EXPECT_EQ(read_connection_id, empty_connection_id); + EXPECT_TRUE(reader.ReadUInt8(&read_byte)); + EXPECT_EQ(read_byte, 1); + // Reset read_connection_id to something else to verify that + // ReadConnectionId properly sets it back to empty. + read_connection_id = TestConnectionId(); + EXPECT_TRUE(reader.ReadConnectionId(&read_connection_id, 0)); + EXPECT_EQ(read_connection_id, empty_connection_id); + EXPECT_TRUE(reader.ReadUInt8(&read_byte)); + EXPECT_EQ(read_byte, 2); + read_connection_id = TestConnectionId(); + EXPECT_TRUE(reader.ReadConnectionId(&read_connection_id, 0)); + EXPECT_EQ(read_connection_id, empty_connection_id); + EXPECT_FALSE(reader.ReadUInt8(&read_byte)); +} + +TEST_P(QuicDataWriterTest, WriteTag) { + char CHLO[] = { + 'C', + 'H', + 'L', + 'O', + }; + const int kBufferLength = sizeof(QuicTag); + char buffer[kBufferLength]; + QuicDataWriter writer(kBufferLength, buffer, GetParam().endianness); + writer.WriteTag(kCHLO); + test::CompareCharArraysWithHexError("CHLO", buffer, kBufferLength, CHLO, + kBufferLength); + + QuicTag read_chlo; + QuicDataReader reader(buffer, kBufferLength, GetParam().endianness); + reader.ReadTag(&read_chlo); + EXPECT_EQ(kCHLO, read_chlo); +} + +TEST_P(QuicDataWriterTest, Write16BitUnsignedIntegers) { + char little_endian16[] = {0x22, 0x11}; + char big_endian16[] = {0x11, 0x22}; + char buffer16[2]; + { + uint16_t in_memory16 = 0x1122; + QuicDataWriter writer(2, buffer16, GetParam().endianness); + writer.WriteUInt16(in_memory16); + test::CompareCharArraysWithHexError( + "uint16_t", buffer16, 2, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian16 + : little_endian16, + 2); + + uint16_t read_number16; + QuicDataReader reader(buffer16, 2, GetParam().endianness); + reader.ReadUInt16(&read_number16); + EXPECT_EQ(in_memory16, read_number16); + } + + { + uint64_t in_memory16 = 0x0000000000001122; + QuicDataWriter writer(2, buffer16, GetParam().endianness); + writer.WriteBytesToUInt64(2, in_memory16); + test::CompareCharArraysWithHexError( + "uint16_t", buffer16, 2, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian16 + : little_endian16, + 2); + + uint64_t read_number16; + QuicDataReader reader(buffer16, 2, GetParam().endianness); + reader.ReadBytesToUInt64(2, &read_number16); + EXPECT_EQ(in_memory16, read_number16); + } +} + +TEST_P(QuicDataWriterTest, Write24BitUnsignedIntegers) { + char little_endian24[] = {0x33, 0x22, 0x11}; + char big_endian24[] = {0x11, 0x22, 0x33}; + char buffer24[3]; + uint64_t in_memory24 = 0x0000000000112233; + QuicDataWriter writer(3, buffer24, GetParam().endianness); + writer.WriteBytesToUInt64(3, in_memory24); + test::CompareCharArraysWithHexError( + "uint24", buffer24, 3, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian24 + : little_endian24, + 3); + + uint64_t read_number24; + QuicDataReader reader(buffer24, 3, GetParam().endianness); + reader.ReadBytesToUInt64(3, &read_number24); + EXPECT_EQ(in_memory24, read_number24); +} + +TEST_P(QuicDataWriterTest, Write32BitUnsignedIntegers) { + char little_endian32[] = {0x44, 0x33, 0x22, 0x11}; + char big_endian32[] = {0x11, 0x22, 0x33, 0x44}; + char buffer32[4]; + { + uint32_t in_memory32 = 0x11223344; + QuicDataWriter writer(4, buffer32, GetParam().endianness); + writer.WriteUInt32(in_memory32); + test::CompareCharArraysWithHexError( + "uint32_t", buffer32, 4, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian32 + : little_endian32, + 4); + + uint32_t read_number32; + QuicDataReader reader(buffer32, 4, GetParam().endianness); + reader.ReadUInt32(&read_number32); + EXPECT_EQ(in_memory32, read_number32); + } + + { + uint64_t in_memory32 = 0x11223344; + QuicDataWriter writer(4, buffer32, GetParam().endianness); + writer.WriteBytesToUInt64(4, in_memory32); + test::CompareCharArraysWithHexError( + "uint32_t", buffer32, 4, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian32 + : little_endian32, + 4); + + uint64_t read_number32; + QuicDataReader reader(buffer32, 4, GetParam().endianness); + reader.ReadBytesToUInt64(4, &read_number32); + EXPECT_EQ(in_memory32, read_number32); + } +} + +TEST_P(QuicDataWriterTest, Write40BitUnsignedIntegers) { + uint64_t in_memory40 = 0x0000001122334455; + char little_endian40[] = {0x55, 0x44, 0x33, 0x22, 0x11}; + char big_endian40[] = {0x11, 0x22, 0x33, 0x44, 0x55}; + char buffer40[5]; + QuicDataWriter writer(5, buffer40, GetParam().endianness); + writer.WriteBytesToUInt64(5, in_memory40); + test::CompareCharArraysWithHexError( + "uint40", buffer40, 5, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian40 + : little_endian40, + 5); + + uint64_t read_number40; + QuicDataReader reader(buffer40, 5, GetParam().endianness); + reader.ReadBytesToUInt64(5, &read_number40); + EXPECT_EQ(in_memory40, read_number40); +} + +TEST_P(QuicDataWriterTest, Write48BitUnsignedIntegers) { + uint64_t in_memory48 = 0x0000112233445566; + char little_endian48[] = {0x66, 0x55, 0x44, 0x33, 0x22, 0x11}; + char big_endian48[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + char buffer48[6]; + QuicDataWriter writer(6, buffer48, GetParam().endianness); + writer.WriteBytesToUInt64(6, in_memory48); + test::CompareCharArraysWithHexError( + "uint48", buffer48, 6, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian48 + : little_endian48, + 6); + + uint64_t read_number48; + QuicDataReader reader(buffer48, 6, GetParam().endianness); + reader.ReadBytesToUInt64(6., &read_number48); + EXPECT_EQ(in_memory48, read_number48); +} + +TEST_P(QuicDataWriterTest, Write56BitUnsignedIntegers) { + uint64_t in_memory56 = 0x0011223344556677; + char little_endian56[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11}; + char big_endian56[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + char buffer56[7]; + QuicDataWriter writer(7, buffer56, GetParam().endianness); + writer.WriteBytesToUInt64(7, in_memory56); + test::CompareCharArraysWithHexError( + "uint56", buffer56, 7, + GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian56 + : little_endian56, + 7); + + uint64_t read_number56; + QuicDataReader reader(buffer56, 7, GetParam().endianness); + reader.ReadBytesToUInt64(7, &read_number56); + EXPECT_EQ(in_memory56, read_number56); +} + +TEST_P(QuicDataWriterTest, Write64BitUnsignedIntegers) { + uint64_t in_memory64 = 0x1122334455667788; + unsigned char little_endian64[] = {0x88, 0x77, 0x66, 0x55, + 0x44, 0x33, 0x22, 0x11}; + unsigned char big_endian64[] = {0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88}; + char buffer64[8]; + QuicDataWriter writer(8, buffer64, GetParam().endianness); + writer.WriteBytesToUInt64(8, in_memory64); + test::CompareCharArraysWithHexError( + "uint64_t", buffer64, 8, + GetParam().endianness == NETWORK_BYTE_ORDER ? AsChars(big_endian64) + : AsChars(little_endian64), + 8); + + uint64_t read_number64; + QuicDataReader reader(buffer64, 8, GetParam().endianness); + reader.ReadBytesToUInt64(8, &read_number64); + EXPECT_EQ(in_memory64, read_number64); + + QuicDataWriter writer2(8, buffer64, GetParam().endianness); + writer2.WriteUInt64(in_memory64); + test::CompareCharArraysWithHexError( + "uint64_t", buffer64, 8, + GetParam().endianness == NETWORK_BYTE_ORDER ? AsChars(big_endian64) + : AsChars(little_endian64), + 8); + read_number64 = 0u; + QuicDataReader reader2(buffer64, 8, GetParam().endianness); + reader2.ReadUInt64(&read_number64); + EXPECT_EQ(in_memory64, read_number64); +} + +TEST_P(QuicDataWriterTest, WriteIntegers) { + char buf[43]; + uint8_t i8 = 0x01; + uint16_t i16 = 0x0123; + uint32_t i32 = 0x01234567; + uint64_t i64 = 0x0123456789ABCDEF; + QuicDataWriter writer(46, buf, GetParam().endianness); + for (size_t i = 0; i < 10; ++i) { + switch (i) { + case 0u: + EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64)); + break; + case 1u: + EXPECT_TRUE(writer.WriteUInt8(i8)); + EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64)); + break; + case 2u: + EXPECT_TRUE(writer.WriteUInt16(i16)); + EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64)); + break; + case 3u: + EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64)); + break; + case 4u: + EXPECT_TRUE(writer.WriteUInt32(i32)); + EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64)); + break; + case 5u: + case 6u: + case 7u: + case 8u: + EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64)); + break; + default: + EXPECT_FALSE(writer.WriteBytesToUInt64(i, i64)); + } + } + + QuicDataReader reader(buf, 46, GetParam().endianness); + for (size_t i = 0; i < 10; ++i) { + uint8_t read8; + uint16_t read16; + uint32_t read32; + uint64_t read64; + switch (i) { + case 0u: + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(0u, read64); + break; + case 1u: + EXPECT_TRUE(reader.ReadUInt8(&read8)); + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(i8, read8); + EXPECT_EQ(0xEFu, read64); + break; + case 2u: + EXPECT_TRUE(reader.ReadUInt16(&read16)); + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(i16, read16); + EXPECT_EQ(0xCDEFu, read64); + break; + case 3u: + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(0xABCDEFu, read64); + break; + case 4u: + EXPECT_TRUE(reader.ReadUInt32(&read32)); + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(i32, read32); + EXPECT_EQ(0x89ABCDEFu, read64); + break; + case 5u: + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(0x6789ABCDEFu, read64); + break; + case 6u: + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(0x456789ABCDEFu, read64); + break; + case 7u: + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(0x23456789ABCDEFu, read64); + break; + case 8u: + EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64)); + EXPECT_EQ(0x0123456789ABCDEFu, read64); + break; + default: + EXPECT_FALSE(reader.ReadBytesToUInt64(i, &read64)); + } + } +} + +TEST_P(QuicDataWriterTest, WriteBytes) { + char bytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + char buf[QUIC_ARRAYSIZE(bytes)]; + QuicDataWriter writer(QUIC_ARRAYSIZE(buf), buf, GetParam().endianness); + EXPECT_TRUE(writer.WriteBytes(bytes, QUIC_ARRAYSIZE(bytes))); + for (unsigned int i = 0; i < QUIC_ARRAYSIZE(bytes); ++i) { + EXPECT_EQ(bytes[i], buf[i]); + } +} + +const int kVarIntBufferLength = 1024; + +// Encodes and then decodes a specified value, checks that the +// value that was encoded is the same as the decoded value, the length +// is correct, and that after decoding, all data in the buffer has +// been consumed.. +// Returns true if everything works, false if not. +bool EncodeDecodeValue(uint64_t value_in, char* buffer, size_t size_of_buffer) { + // Init the buffer to all 0, just for cleanliness. Makes for better + // output if, in debugging, we need to dump out the buffer. + memset(buffer, 0, size_of_buffer); + // make a writer. Note that for IETF encoding + // we do not care about endianness... It's always big-endian, + // but the c'tor expects to be told what endianness is in force... + QuicDataWriter writer(size_of_buffer, buffer, Endianness::NETWORK_BYTE_ORDER); + + // Try to write the value. + if (writer.WriteVarInt62(value_in) != true) { + return false; + } + // Look at the value we encoded. Determine how much should have been + // used based on the value, and then check the state of the writer + // to see that it matches. + size_t expected_length = 0; + if (value_in <= 0x3f) { + expected_length = 1; + } else if (value_in <= 0x3fff) { + expected_length = 2; + } else if (value_in <= 0x3fffffff) { + expected_length = 4; + } else { + expected_length = 8; + } + if (writer.length() != expected_length) { + return false; + } + + // set up a reader, just the length we've used, no more, no less. + QuicDataReader reader(buffer, expected_length, + Endianness::NETWORK_BYTE_ORDER); + uint64_t value_out; + + if (reader.ReadVarInt62(&value_out) == false) { + return false; + } + if (value_in != value_out) { + return false; + } + // We only write one value so there had better be nothing left to read + return reader.IsDoneReading(); +} + +// Test that 8-byte-encoded Variable Length Integers are properly laid +// out in the buffer. +TEST_P(QuicDataWriterTest, VarInt8Layout) { + char buffer[1024]; + + // Check that the layout of bytes in the buffer is correct. Bytes + // are always encoded big endian... + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142f3e4d5c6b7a8))); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)), + (0x31 + 0xc0)); // 0xc0 for encoding + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 1)), 0x42); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 2)), 0xf3); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 3)), 0xe4); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 4)), 0xd5); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 5)), 0xc6); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 6)), 0xb7); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 7)), 0xa8); +} + +// Test that 4-byte-encoded Variable Length Integers are properly laid +// out in the buffer. +TEST_P(QuicDataWriterTest, VarInt4Layout) { + char buffer[1024]; + + // Check that the layout of bytes in the buffer is correct. Bytes + // are always encoded big endian... + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer.WriteVarInt62(0x3243f4e5)); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)), + (0x32 + 0x80)); // 0x80 for encoding + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 1)), 0x43); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 2)), 0xf4); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 3)), 0xe5); +} + +// Test that 2-byte-encoded Variable Length Integers are properly laid +// out in the buffer. +TEST_P(QuicDataWriterTest, VarInt2Layout) { + char buffer[1024]; + + // Check that the layout of bytes in the buffer is correct. Bytes + // are always encoded big endian... + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer.WriteVarInt62(0x3647)); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)), + (0x36 + 0x40)); // 0x40 for encoding + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 1)), 0x47); +} + +// Test that 1-byte-encoded Variable Length Integers are properly laid +// out in the buffer. +TEST_P(QuicDataWriterTest, VarInt1Layout) { + char buffer[1024]; + + // Check that the layout of bytes in the buffer + // is correct. Bytes are always encoded big endian... + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer.WriteVarInt62(0x3f)); + EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)), 0x3f); +} + +// Test certain, targeted, values that are expected to succeed: +// 0, 1, +// 0x3e, 0x3f, 0x40, 0x41 (around the 1-2 byte transitions) +// 0x3ffe, 0x3fff, 0x4000, 0x4001 (the 2-4 byte transition) +// 0x3ffffffe, 0x3fffffff, 0x40000000, 0x40000001 (the 4-8 byte +// transition) +// 0x3ffffffffffffffe, 0x3fffffffffffffff, (the highest valid values) +// 0xfe, 0xff, 0x100, 0x101, +// 0xfffe, 0xffff, 0x10000, 0x10001, +// 0xfffffe, 0xffffff, 0x1000000, 0x1000001, +// 0xfffffffe, 0xffffffff, 0x100000000, 0x100000001, +// 0xfffffffffe, 0xffffffffff, 0x10000000000, 0x10000000001, +// 0xfffffffffffe, 0xffffffffffff, 0x1000000000000, 0x1000000000001, +// 0xfffffffffffffe, 0xffffffffffffff, 0x100000000000000, 0x100000000000001, +TEST_P(QuicDataWriterTest, VarIntGoodTargetedValues) { + char buffer[kVarIntBufferLength]; + uint64_t passing_values[] = { + 0, + 1, + 0x3e, + 0x3f, + 0x40, + 0x41, + 0x3ffe, + 0x3fff, + 0x4000, + 0x4001, + 0x3ffffffe, + 0x3fffffff, + 0x40000000, + 0x40000001, + 0x3ffffffffffffffe, + 0x3fffffffffffffff, + 0xfe, + 0xff, + 0x100, + 0x101, + 0xfffe, + 0xffff, + 0x10000, + 0x10001, + 0xfffffe, + 0xffffff, + 0x1000000, + 0x1000001, + 0xfffffffe, + 0xffffffff, + 0x100000000, + 0x100000001, + 0xfffffffffe, + 0xffffffffff, + 0x10000000000, + 0x10000000001, + 0xfffffffffffe, + 0xffffffffffff, + 0x1000000000000, + 0x1000000000001, + 0xfffffffffffffe, + 0xffffffffffffff, + 0x100000000000000, + 0x100000000000001, + }; + for (uint64_t test_val : passing_values) { + EXPECT_TRUE( + EncodeDecodeValue(test_val, static_cast<char*>(buffer), sizeof(buffer))) + << " encode/decode of " << test_val << " failed"; + } +} +// +// Test certain, targeted, values where failure is expected (the +// values are invalid w.r.t. IETF VarInt encoding): +// 0x4000000000000000, 0x4000000000000001, ( Just above max allowed value) +// 0xfffffffffffffffe, 0xffffffffffffffff, (should fail) +TEST_P(QuicDataWriterTest, VarIntBadTargetedValues) { + char buffer[kVarIntBufferLength]; + uint64_t failing_values[] = { + 0x4000000000000000, + 0x4000000000000001, + 0xfffffffffffffffe, + 0xffffffffffffffff, + }; + for (uint64_t test_val : failing_values) { + EXPECT_FALSE( + EncodeDecodeValue(test_val, static_cast<char*>(buffer), sizeof(buffer))) + << " encode/decode of " << test_val << " succeeded, but was an " + << "invalid value"; + } +} + +// Following tests all try to fill the buffer with multiple values, +// go one value more than the buffer can accommodate, then read +// the successfully encoded values, and try to read the unsuccessfully +// encoded value. The following is the number of values to encode. +const int kMultiVarCount = 1000; + +// Test writing & reading multiple 8-byte-encoded varints +TEST_P(QuicDataWriterTest, MultiVarInt8) { + uint64_t test_val; + char buffer[8 * kMultiVarCount]; + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + // Put N values into the buffer. Adding i to the value ensures that + // each value is different so we can detect if we overwrite values, + // or read the same value over and over. + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142f3e4d5c6b7a8) + i)); + } + EXPECT_EQ(writer.length(), 8u * kMultiVarCount); + + // N+1st should fail, the buffer is full. + EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x3142f3e4d5c6b7a8))); + + // Now we should be able to read out the N values that were + // successfully encoded. + QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER); + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, (UINT64_C(0x3142f3e4d5c6b7a8) + i)); + } + // And the N+1st should fail. + EXPECT_FALSE(reader.ReadVarInt62(&test_val)); +} + +// Test writing & reading multiple 4-byte-encoded varints +TEST_P(QuicDataWriterTest, MultiVarInt4) { + uint64_t test_val; + char buffer[4 * kMultiVarCount]; + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + // Put N values into the buffer. Adding i to the value ensures that + // each value is different so we can detect if we overwrite values, + // or read the same value over and over. + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142f3e4) + i)); + } + EXPECT_EQ(writer.length(), 4u * kMultiVarCount); + + // N+1st should fail, the buffer is full. + EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x3142f3e4))); + + // Now we should be able to read out the N values that were + // successfully encoded. + QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER); + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, (UINT64_C(0x3142f3e4) + i)); + } + // And the N+1st should fail. + EXPECT_FALSE(reader.ReadVarInt62(&test_val)); +} + +// Test writing & reading multiple 2-byte-encoded varints +TEST_P(QuicDataWriterTest, MultiVarInt2) { + uint64_t test_val; + char buffer[2 * kMultiVarCount]; + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + // Put N values into the buffer. Adding i to the value ensures that + // each value is different so we can detect if we overwrite values, + // or read the same value over and over. + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142) + i)); + } + EXPECT_EQ(writer.length(), 2u * kMultiVarCount); + + // N+1st should fail, the buffer is full. + EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x3142))); + + // Now we should be able to read out the N values that were + // successfully encoded. + QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER); + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, (UINT64_C(0x3142) + i)); + } + // And the N+1st should fail. + EXPECT_FALSE(reader.ReadVarInt62(&test_val)); +} + +// Test writing & reading multiple 1-byte-encoded varints +TEST_P(QuicDataWriterTest, MultiVarInt1) { + uint64_t test_val; + char buffer[1 * kMultiVarCount]; + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + // Put N values into the buffer. Adding i to the value ensures that + // each value is different so we can detect if we overwrite values, + // or read the same value over and over. &0xf ensures we do not + // overflow the max value for single-byte encoding. + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x30) + (i & 0xf))); + } + EXPECT_EQ(writer.length(), 1u * kMultiVarCount); + + // N+1st should fail, the buffer is full. + EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x31))); + + // Now we should be able to read out the N values that were + // successfully encoded. + QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER); + for (int i = 0; i < kMultiVarCount; i++) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, (UINT64_C(0x30) + (i & 0xf))); + } + // And the N+1st should fail. + EXPECT_FALSE(reader.ReadVarInt62(&test_val)); +} + +// Test writing varints with a forced length. +TEST_P(QuicDataWriterTest, VarIntFixedLength) { + char buffer[90]; + memset(buffer, 0, sizeof(buffer)); + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + + writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_1); + writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_2); + writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_4); + writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_8); + + writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_1); + writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_2); + writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_4); + writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_8); + + writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_2); + writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_4); + writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_8); + + writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_2); + writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_4); + writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_8); + + writer.WriteVarInt62(16384, VARIABLE_LENGTH_INTEGER_LENGTH_4); + writer.WriteVarInt62(16384, VARIABLE_LENGTH_INTEGER_LENGTH_8); + + writer.WriteVarInt62(1073741823, VARIABLE_LENGTH_INTEGER_LENGTH_4); + writer.WriteVarInt62(1073741823, VARIABLE_LENGTH_INTEGER_LENGTH_8); + + writer.WriteVarInt62(1073741824, VARIABLE_LENGTH_INTEGER_LENGTH_8); + + QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER); + + uint64_t test_val = 0; + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, 1u); + } + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, 63u); + } + + for (int i = 0; i < 3; ++i) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, 64u); + } + for (int i = 0; i < 3; ++i) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, 16383u); + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, 16384u); + } + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, 1073741823u); + } + + EXPECT_TRUE(reader.ReadVarInt62(&test_val)); + EXPECT_EQ(test_val, 1073741824u); + + // We are at the end of the buffer so this should fail. + EXPECT_FALSE(reader.ReadVarInt62(&test_val)); +} + +// Test encoding/decoding stream-id values. +void EncodeDecodeStreamId(uint64_t value_in, bool expected_decode_result) { + char buffer[1 * kMultiVarCount]; + memset(buffer, 0, sizeof(buffer)); + + // Encode the given Stream ID. + QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer), + Endianness::NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer.WriteVarInt62(value_in)); + + QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER); + QuicStreamId received_stream_id; + bool read_result = reader.ReadVarIntStreamId(&received_stream_id); + EXPECT_EQ(expected_decode_result, read_result); + if (read_result) { + EXPECT_EQ(value_in, received_stream_id); + } +} + +// Test writing & reading stream-ids of various value. +TEST_P(QuicDataWriterTest, StreamId1) { + // Check a 1-byte QuicStreamId, should work + EncodeDecodeStreamId(UINT64_C(0x15), true); + + // Check a 2-byte QuicStream ID. It should work. + EncodeDecodeStreamId(UINT64_C(0x1567), true); + + // Check a QuicStreamId that requires 4 bytes of encoding + // This should work. + EncodeDecodeStreamId(UINT64_C(0x34567890), true); + + // Check a QuicStreamId that requires 8 bytes of encoding + // but whose value is in the acceptable range. + // This should work. + EncodeDecodeStreamId(UINT64_C(0xf4567890), true); + + // Check QuicStreamIds that require 8 bytes of encoding + // and whose value is not acceptable. + // This should fail. + EncodeDecodeStreamId(UINT64_C(0x100000000), false); + EncodeDecodeStreamId(UINT64_C(0x3fffffffffffffff), false); +} + +TEST_P(QuicDataWriterTest, WriteRandomBytes) { + char buffer[20]; + char expected[20]; + for (size_t i = 0; i < 20; ++i) { + expected[i] = 'r'; + } + MockRandom random; + QuicDataWriter writer(20, buffer, GetParam().endianness); + EXPECT_FALSE(writer.WriteRandomBytes(&random, 30)); + + EXPECT_TRUE(writer.WriteRandomBytes(&random, 20)); + test::CompareCharArraysWithHexError("random", buffer, 20, expected, 20); +} + +TEST_P(QuicDataWriterTest, PeekVarInt62Length) { + // In range [0, 63], variable length should be 1 byte. + char buffer[20]; + QuicDataWriter writer(20, buffer, NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer.WriteVarInt62(50)); + QuicDataReader reader(buffer, 20, NETWORK_BYTE_ORDER); + EXPECT_EQ(1, reader.PeekVarInt62Length()); + // In range (63-16383], variable length should be 2 byte2. + char buffer2[20]; + QuicDataWriter writer2(20, buffer2, NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer2.WriteVarInt62(100)); + QuicDataReader reader2(buffer2, 20, NETWORK_BYTE_ORDER); + EXPECT_EQ(2, reader2.PeekVarInt62Length()); + // In range (16383, 1073741823], variable length should be 4 bytes. + char buffer3[20]; + QuicDataWriter writer3(20, buffer3, NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer3.WriteVarInt62(20000)); + QuicDataReader reader3(buffer3, 20, NETWORK_BYTE_ORDER); + EXPECT_EQ(4, reader3.PeekVarInt62Length()); + // In range (1073741823, 4611686018427387903], variable length should be 8 + // bytes. + char buffer4[20]; + QuicDataWriter writer4(20, buffer4, NETWORK_BYTE_ORDER); + EXPECT_TRUE(writer4.WriteVarInt62(2000000000)); + QuicDataReader reader4(buffer4, 20, NETWORK_BYTE_ORDER); + EXPECT_EQ(8, reader4.PeekVarInt62Length()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_default_packet_writer.cc b/quic/core/quic_default_packet_writer.cc new file mode 100644 index 0000000..3b16a74 --- /dev/null +++ b/quic/core/quic_default_packet_writer.cc
@@ -0,0 +1,68 @@ +// Copyright 2013 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/quic_default_packet_writer.h" + +#include "net/quic/platform/impl/quic_socket_utils.h" + +namespace quic { + +QuicDefaultPacketWriter::QuicDefaultPacketWriter(int fd) + : fd_(fd), write_blocked_(false) {} + +QuicDefaultPacketWriter::~QuicDefaultPacketWriter() = default; + +WriteResult QuicDefaultPacketWriter::WritePacket( + const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) { + DCHECK(!write_blocked_); + DCHECK(nullptr == options) + << "QuicDefaultPacketWriter does not accept any options."; + WriteResult result = QuicSocketUtils::WritePacket(fd_, buffer, buf_len, + self_address, peer_address); + if (IsWriteBlockedStatus(result.status)) { + write_blocked_ = true; + } + return result; +} + +bool QuicDefaultPacketWriter::IsWriteBlocked() const { + return write_blocked_; +} + +void QuicDefaultPacketWriter::SetWritable() { + write_blocked_ = false; +} + +QuicByteCount QuicDefaultPacketWriter::GetMaxPacketSize( + const QuicSocketAddress& peer_address) const { + return kMaxPacketSize; +} + +bool QuicDefaultPacketWriter::SupportsReleaseTime() const { + return false; +} + +bool QuicDefaultPacketWriter::IsBatchMode() const { + return false; +} + +char* QuicDefaultPacketWriter::GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) { + return nullptr; +} + +WriteResult QuicDefaultPacketWriter::Flush() { + return WriteResult(WRITE_STATUS_OK, 0); +} + +void QuicDefaultPacketWriter::set_write_blocked(bool is_blocked) { + write_blocked_ = is_blocked; +} + +} // namespace quic
diff --git a/quic/core/quic_default_packet_writer.h b/quic/core/quic_default_packet_writer.h new file mode 100644 index 0000000..b5473eb --- /dev/null +++ b/quic/core/quic_default_packet_writer.h
@@ -0,0 +1,56 @@ +// Copyright 2013 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_QUIC_DEFAULT_PACKET_WRITER_H_ +#define QUICHE_QUIC_CORE_QUIC_DEFAULT_PACKET_WRITER_H_ + +#include <cstddef> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +struct WriteResult; + +// Default packet writer which wraps QuicSocketUtils WritePacket. +class QUIC_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter { + public: + explicit QuicDefaultPacketWriter(int fd); + QuicDefaultPacketWriter(const QuicDefaultPacketWriter&) = delete; + QuicDefaultPacketWriter& operator=(const QuicDefaultPacketWriter&) = delete; + ~QuicDefaultPacketWriter() override; + + // QuicPacketWriter + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) override; + bool IsWriteBlocked() const override; + void SetWritable() override; + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& peer_address) const override; + bool SupportsReleaseTime() const override; + bool IsBatchMode() const override; + char* GetNextWriteLocation(const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override; + WriteResult Flush() override; + + void set_fd(int fd) { fd_ = fd; } + + protected: + void set_write_blocked(bool is_blocked); + int fd() { return fd_; } + + private: + int fd_; + bool write_blocked_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_DEFAULT_PACKET_WRITER_H_
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc new file mode 100644 index 0000000..14da558 --- /dev/null +++ b/quic/core/quic_dispatcher.cc
@@ -0,0 +1,1354 @@ +// Copyright (c) 2012 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/quic_dispatcher.h" + +#include <utility> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/chlo_extractor.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/stateless_rejector.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket; +typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList; +typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult; + +namespace { + +// An alarm that informs the QuicDispatcher to delete old sessions. +class DeleteSessionsAlarm : public QuicAlarm::Delegate { + public: + explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher) {} + DeleteSessionsAlarm(const DeleteSessionsAlarm&) = delete; + DeleteSessionsAlarm& operator=(const DeleteSessionsAlarm&) = delete; + + void OnAlarm() override { dispatcher_->DeleteSessions(); } + + private: + // Not owned. + QuicDispatcher* dispatcher_; +}; + +// Collects packets serialized by a QuicPacketCreator in order +// to be handed off to the time wait list manager. +class PacketCollector : public QuicPacketCreator::DelegateInterface, + public QuicStreamFrameDataProducer { + public: + explicit PacketCollector(QuicBufferAllocator* allocator) + : send_buffer_(allocator) {} + ~PacketCollector() override = default; + + // QuicPacketCreator::DelegateInterface methods: + void OnSerializedPacket(SerializedPacket* serialized_packet) override { + // Make a copy of the serialized packet to send later. + packets_.emplace_back( + new QuicEncryptedPacket(CopyBuffer(*serialized_packet), + serialized_packet->encrypted_length, true)); + serialized_packet->encrypted_buffer = nullptr; + DeleteFrames(&(serialized_packet->retransmittable_frames)); + serialized_packet->retransmittable_frames.clear(); + } + + char* GetPacketBuffer() override { + // Let QuicPacketCreator to serialize packets on stack buffer. + return nullptr; + } + + void OnUnrecoverableError(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) override {} + + void SaveStatelessRejectFrameData(QuicStringPiece reject) { + struct iovec iovec; + iovec.iov_base = const_cast<char*>(reject.data()); + iovec.iov_len = reject.length(); + send_buffer_.SaveStreamData(&iovec, 1, 0, iovec.iov_len); + } + + // QuicStreamFrameDataProducer + WriteStreamDataResult WriteStreamData(QuicStreamId id, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override { + if (send_buffer_.WriteStreamData(offset, data_length, writer)) { + return WRITE_SUCCESS; + } + return WRITE_FAILED; + } + bool WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override { + return send_buffer_.WriteStreamData(offset, data_length, writer); + } + + std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets() { + return &packets_; + } + + private: + std::vector<std::unique_ptr<QuicEncryptedPacket>> packets_; + // This is only needed until the packets are encrypted. Once packets are + // encrypted, the stream data is no longer required. + QuicStreamSendBuffer send_buffer_; +}; + +// Helper for statelessly closing connections by generating the +// correct termination packets and adding the connection to the time wait +// list manager. +class StatelessConnectionTerminator { + public: + StatelessConnectionTerminator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicConnectionHelperInterface* helper, + QuicTimeWaitListManager* time_wait_list_manager) + : connection_id_(connection_id), + framer_(framer), + collector_(helper->GetStreamSendBufferAllocator()), + creator_(connection_id, framer, &collector_), + time_wait_list_manager_(time_wait_list_manager) { + framer_->set_data_producer(&collector_); + } + + ~StatelessConnectionTerminator() { + // Clear framer's producer. + framer_->set_data_producer(nullptr); + } + + // Generates a packet containing a CONNECTION_CLOSE frame specifying + // |error_code| and |error_details| and add the connection to time wait. + void CloseConnection(QuicErrorCode error_code, + const QuicString& error_details, + bool ietf_quic) { + QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame; + frame->error_code = error_code; + frame->error_details = error_details; + if (!creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)) { + QUIC_BUG << "Unable to add frame to an empty packet"; + delete frame; + return; + } + creator_.Flush(); + DCHECK_EQ(1u, collector_.packets()->size()); + time_wait_list_manager_->AddConnectionIdToTimeWait( + connection_id_, ietf_quic, + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + quic::ENCRYPTION_NONE, collector_.packets()); + } + + // Generates a series of termination packets containing the crypto handshake + // message |reject|. Adds the connection to time wait list with the + // generated packets. + void RejectConnection(QuicStringPiece reject, bool ietf_quic) { + QuicStreamOffset offset = 0; + collector_.SaveStatelessRejectFrameData(reject); + while (offset < reject.length()) { + QuicFrame frame; + if (framer_->transport_version() < QUIC_VERSION_47) { + if (!creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(framer_->transport_version()), + reject.length(), offset, offset, + /*fin=*/false, + /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)) { + QUIC_BUG << "Unable to consume data into an empty packet."; + return; + } + offset += frame.stream_frame.data_length; + } else { + if (!creator_.ConsumeCryptoData(ENCRYPTION_NONE, + reject.length() - offset, offset, + NOT_RETRANSMISSION, &frame)) { + QUIC_BUG << "Unable to consume crypto data into an empty packet."; + return; + } + offset += frame.crypto_frame->data_length; + } + if (offset < reject.length()) { + DCHECK(!creator_.HasRoomForStreamFrame( + QuicUtils::GetCryptoStreamId(framer_->transport_version()), offset, + frame.stream_frame.data_length)); + } + creator_.Flush(); + } + time_wait_list_manager_->AddConnectionIdToTimeWait( + connection_id_, ietf_quic, + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_NONE, + collector_.packets()); + DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id_)); + } + + private: + QuicConnectionId connection_id_; + QuicFramer* framer_; // Unowned. + // Set as the visitor of |creator_| to collect any generated packets. + PacketCollector collector_; + QuicPacketCreator creator_; + QuicTimeWaitListManager* time_wait_list_manager_; +}; + +// Class which extracts the ALPN from a CHLO packet. +class ChloAlpnExtractor : public ChloExtractor::Delegate { + public: + void OnChlo(QuicTransportVersion version, + QuicConnectionId connection_id, + const CryptoHandshakeMessage& chlo) override { + QuicStringPiece alpn_value; + if (chlo.GetStringPiece(kALPN, &alpn_value)) { + alpn_ = QuicString(alpn_value); + } + } + + QuicString&& ConsumeAlpn() { return std::move(alpn_); } + + private: + QuicString alpn_; +}; + +// Class which sits between the ChloExtractor and the StatelessRejector +// to give the QuicDispatcher a chance to apply policy checks to the CHLO. +class ChloValidator : public ChloAlpnExtractor { + public: + ChloValidator(QuicCryptoServerStream::Helper* helper, + const QuicSocketAddress& client_address, + const QuicSocketAddress& peer_address, + const QuicSocketAddress& self_address, + StatelessRejector* rejector) + : helper_(helper), + client_address_(client_address), + peer_address_(peer_address), + self_address_(self_address), + rejector_(rejector), + can_accept_(false), + error_details_("CHLO not processed") {} + + // ChloExtractor::Delegate implementation. + void OnChlo(QuicTransportVersion version, + QuicConnectionId connection_id, + const CryptoHandshakeMessage& chlo) override { + // Extract the ALPN + ChloAlpnExtractor::OnChlo(version, connection_id, chlo); + if (helper_->CanAcceptClientHello(chlo, client_address_, peer_address_, + self_address_, &error_details_)) { + can_accept_ = true; + rejector_->OnChlo( + version, connection_id, + helper_->GenerateConnectionIdForReject(version, connection_id), chlo); + } + } + + bool can_accept() const { return can_accept_; } + + const QuicString& error_details() const { return error_details_; } + + private: + QuicCryptoServerStream::Helper* helper_; // Unowned. + // client_address_ and peer_address_ could be different values for proxy + // connections. + QuicSocketAddress client_address_; + QuicSocketAddress peer_address_; + QuicSocketAddress self_address_; + StatelessRejector* rejector_; // Unowned. + bool can_accept_; + QuicString error_details_; +}; + +} // namespace + +QuicDispatcher::QuicDispatcher( + const QuicConfig* config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory, + uint8_t expected_connection_id_length) + : config_(config), + crypto_config_(crypto_config), + compressed_certs_cache_( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), + helper_(std::move(helper)), + session_helper_(std::move(session_helper)), + alarm_factory_(std::move(alarm_factory)), + delete_sessions_alarm_( + alarm_factory_->CreateAlarm(new DeleteSessionsAlarm(this))), + buffered_packets_(this, helper_->GetClock(), alarm_factory_.get()), + current_packet_(nullptr), + version_manager_(version_manager), + framer_(GetSupportedVersions(), + /*unused*/ QuicTime::Zero(), + Perspective::IS_SERVER, + expected_connection_id_length), + last_error_(QUIC_NO_ERROR), + new_sessions_allowed_per_event_loop_(0u), + accept_new_connections_(true) { + framer_.set_visitor(this); +} + +QuicDispatcher::~QuicDispatcher() { + session_map_.clear(); + closed_session_list_.clear(); +} + +void QuicDispatcher::InitializeWithWriter(QuicPacketWriter* writer) { + DCHECK(writer_ == nullptr); + writer_.reset(writer); + time_wait_list_manager_.reset(CreateQuicTimeWaitListManager()); +} + +void QuicDispatcher::ProcessPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet) { + current_self_address_ = self_address; + current_peer_address_ = peer_address; + // GetClientAddress must be called after current_peer_address_ is set. + current_client_address_ = GetClientAddress(); + current_packet_ = &packet; + // ProcessPacket will cause the packet to be dispatched in + // OnUnauthenticatedPublicHeader, or sent to the time wait list manager + // in OnUnauthenticatedHeader. + framer_.ProcessPacket(packet); + // TODO(rjshade): Return a status describing if/why a packet was dropped, + // and log somehow. Maybe expose as a varz. + // TODO(wub): Consider invalidate the current_* variables so processing of the + // next packet does not use them incorrectly. +} + +bool QuicDispatcher::OnUnauthenticatedPublicHeader( + const QuicPacketHeader& header) { + current_connection_id_ = header.destination_connection_id; + + // Port zero is only allowed for unidirectional UDP, so is disallowed by QUIC. + // Given that we can't even send a reply rejecting the packet, just drop the + // packet. + if (current_peer_address_.port() == 0) { + return false; + } + + // The dispatcher requires the connection ID to be present in order to + // look up the matching QuicConnection, so we error out if it is absent. + if (header.destination_connection_id_included != CONNECTION_ID_PRESENT) { + return false; + } + + // Packets with connection IDs for active connections are processed + // immediately. + QuicConnectionId connection_id = header.destination_connection_id; + auto it = session_map_.find(connection_id); + if (it != session_map_.end()) { + DCHECK(!buffered_packets_.HasBufferedPackets(connection_id)); + it->second->ProcessUdpPacket(current_self_address_, current_peer_address_, + *current_packet_); + return false; + } + + if (buffered_packets_.HasChloForConnection(connection_id)) { + BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET, + header.version); + return false; + } + + // Check if we are buffering packets for this connection ID + if (temporarily_buffered_connections_.find(connection_id) != + temporarily_buffered_connections_.end()) { + // This packet was received while the a CHLO for the same connection ID was + // being processed. Buffer it. + BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET, + header.version); + return false; + } + + if (!OnUnauthenticatedUnknownPublicHeader(header)) { + return false; + } + + // If the packet is a public reset for a connection ID that is not active, + // there is nothing we must do or can do. + if (header.reset_flag) { + return false; + } + + if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) { + // This connection ID is already in time-wait state. + time_wait_list_manager_->ProcessPacket( + current_self_address_, current_peer_address_, + header.destination_connection_id, header.form, GetPerPacketContext()); + return false; + } + + // The packet has an unknown connection ID. + + // Unless the packet provides a version, assume that we can continue + // processing using our preferred version. + ParsedQuicVersion version = GetSupportedVersions().front(); + if (header.version_flag) { + ParsedQuicVersion packet_version = header.version; + if (framer_.supported_versions() != GetSupportedVersions()) { + // Reset framer's version if version flags change in flight. + framer_.SetSupportedVersions(GetSupportedVersions()); + } + if (!framer_.IsSupportedVersion(packet_version)) { + if (ShouldCreateSessionForUnknownVersion(framer_.last_version_label())) { + return true; + } + if (!crypto_config()->validate_chlo_size() || + current_packet_->length() >= kMinPacketSizeForVersionNegotiation) { + // Since the version is not supported, send a version negotiation + // packet and stop processing the current packet. + time_wait_list_manager()->SendVersionNegotiationPacket( + connection_id, header.form != GOOGLE_QUIC_PACKET, + GetSupportedVersions(), current_self_address_, + current_peer_address_, GetPerPacketContext()); + } + return false; + } + version = packet_version; + } + // Set the framer's version and continue processing. + framer_.set_version(version); + return true; +} + +bool QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) { + QuicConnectionId connection_id = header.destination_connection_id; + // Packet's connection ID is unknown. Apply the validity checks. + QuicPacketFate fate = ValidityChecks(header); + if (fate == kFateProcess) { + // Execute stateless rejection logic to determine the packet fate, then + // invoke ProcessUnauthenticatedHeaderFate. + MaybeRejectStatelessly(connection_id, header.form, header.version); + } else { + // If the fate is already known, process it without executing stateless + // rejection logic. + ProcessUnauthenticatedHeaderFate(fate, connection_id, header.form, + header.version); + } + + return false; +} + +void QuicDispatcher::ProcessUnauthenticatedHeaderFate( + QuicPacketFate fate, + QuicConnectionId connection_id, + PacketHeaderFormat form, + ParsedQuicVersion version) { + switch (fate) { + case kFateProcess: { + ProcessChlo(form, version); + break; + } + case kFateTimeWait: + // MaybeRejectStatelessly or OnExpiredPackets might have already added the + // connection to time wait, in which case it should not be added again. + if (!GetQuicReloadableFlag(quic_use_cheap_stateless_rejects) || + !time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) { + // Add this connection_id to the time-wait state, to safely reject + // future packets. + QUIC_DLOG(INFO) << "Adding connection ID " << connection_id + << "to time-wait list."; + QUIC_CODE_COUNT(quic_reject_fate_time_wait); + StatelesslyTerminateConnection( + connection_id, form, version, QUIC_HANDSHAKE_FAILED, + "Reject connection", + quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); + } + DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); + time_wait_list_manager_->ProcessPacket( + current_self_address_, current_peer_address_, connection_id, form, + GetPerPacketContext()); + + // Any packets which were buffered while the stateless rejector logic was + // running should be discarded. Do not inform the time wait list manager, + // which should already have a made a decision about sending a reject + // based on the CHLO alone. + buffered_packets_.DiscardPackets(connection_id); + break; + case kFateBuffer: + // This packet is a non-CHLO packet which has arrived before the + // corresponding CHLO, *or* this packet was received while the + // corresponding CHLO was being processed. Buffer it. + BufferEarlyPacket(connection_id, form != GOOGLE_QUIC_PACKET, version); + break; + case kFateDrop: + // Do nothing with the packet. + break; + } +} + +QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks( + const QuicPacketHeader& header) { + // To have all the checks work properly without tears, insert any new check + // into the framework of this method in the section for checks that return the + // check's fate value. The sections for checks must be ordered with the + // highest priority fate first. + + // Checks that return kFateDrop. + + // Checks that return kFateTimeWait. + + // All packets within a connection sent by a client before receiving a + // response from the server are required to have the version negotiation flag + // set. Since this may be a client continuing a connection we lost track of + // via server restart, send a rejection to fast-fail the connection. + if (!header.version_flag) { + QUIC_DLOG(INFO) + << "Packet without version arrived for unknown connection ID " + << header.destination_connection_id; + return kFateTimeWait; + } + + // initial packet number of 0 is always invalid. + if (!header.packet_number.IsInitialized()) { + return kFateTimeWait; + } + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + QUIC_RESTART_FLAG_COUNT_N(quic_enable_accept_random_ipn, 1, 2); + // Accepting Initial Packet Numbers in 1...((2^31)-1) range... check + // maximum accordingly. + if (header.packet_number > MaxRandomInitialPacketNumber()) { + return kFateTimeWait; + } + } else { + // Count those that would have been accepted if FLAGS..random_ipn + // were true -- to detect/diagnose potential issues prior to + // enabling the flag. + if ((header.packet_number > + QuicPacketNumber(kMaxReasonableInitialPacketNumber)) && + (header.packet_number <= MaxRandomInitialPacketNumber())) { + QUIC_CODE_COUNT_N(had_possibly_random_ipn, 1, 2); + } + // Check that the sequence number is within the range that the client is + // expected to send before receiving a response from the server. + if (header.packet_number > + QuicPacketNumber(kMaxReasonableInitialPacketNumber)) { + return kFateTimeWait; + } + } + return kFateProcess; +} + +void QuicDispatcher::CleanUpSession(SessionMap::iterator it, + QuicConnection* connection, + bool should_close_statelessly, + ConnectionCloseSource source) { + write_blocked_list_.erase(connection); + if (should_close_statelessly) { + DCHECK(connection->termination_packets() != nullptr && + !connection->termination_packets()->empty()); + } + QuicTimeWaitListManager::TimeWaitAction action = + QuicTimeWaitListManager::SEND_STATELESS_RESET; + if (connection->termination_packets() != nullptr && + !connection->termination_packets()->empty()) { + action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS; + } else if (connection->transport_version() > QUIC_VERSION_43) { + if (!connection->IsHandshakeConfirmed()) { + QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_handshake_failed); + action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS; + // This serializes a connection close termination packet with error code + // QUIC_HANDSHAKE_FAILED and adds the connection to the time wait list. + StatelesslyTerminateConnection( + connection->connection_id(), IETF_QUIC_LONG_HEADER_PACKET, + connection->version(), QUIC_HANDSHAKE_FAILED, + "Connection is closed by server before handshake confirmed", + // Although it is our intention to send termination packets, the + // |action| argument is not used by this call to + // StatelesslyTerminateConnection(). + action); + session_map_.erase(it); + return; + } + QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_stateless_reset); + } + time_wait_list_manager_->AddConnectionIdToTimeWait( + it->first, connection->transport_version() > QUIC_VERSION_43, action, + connection->encryption_level(), connection->termination_packets()); + session_map_.erase(it); +} + +void QuicDispatcher::StopAcceptingNewConnections() { + accept_new_connections_ = false; +} + +std::unique_ptr<QuicPerPacketContext> QuicDispatcher::GetPerPacketContext() + const { + return nullptr; +} + +void QuicDispatcher::DeleteSessions() { + if (GetQuicReloadableFlag( + quic_connection_do_not_add_to_write_blocked_list_if_disconnected) && + !write_blocked_list_.empty()) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_connection_do_not_add_to_write_blocked_list_if_disconnected, 2, 2); + for (const std::unique_ptr<QuicSession>& session : closed_session_list_) { + if (write_blocked_list_.erase(session->connection()) != 0) { + QUIC_BUG << "QuicConnection was in WriteBlockedList before destruction"; + } + } + } + closed_session_list_.clear(); +} + +void QuicDispatcher::OnCanWrite() { + // The socket is now writable. + writer_->SetWritable(); + + // Move every blocked writer in |write_blocked_list_| to a temporary list. + const size_t num_blocked_writers_before = write_blocked_list_.size(); + WriteBlockedList temp_list; + temp_list.swap(write_blocked_list_); + DCHECK(write_blocked_list_.empty()); + + // Give each blocked writer a chance to write what they indended to write. + // If they are blocked again, they will call |OnWriteBlocked| to add + // themselves back into |write_blocked_list_|. + while (!temp_list.empty()) { + QuicBlockedWriterInterface* blocked_writer = temp_list.begin()->first; + temp_list.erase(temp_list.begin()); + blocked_writer->OnBlockedWriterCanWrite(); + } + const size_t num_blocked_writers_after = write_blocked_list_.size(); + if (num_blocked_writers_after != 0) { + if (num_blocked_writers_before == num_blocked_writers_after) { + QUIC_CODE_COUNT(quic_zero_progress_on_can_write); + } else { + QUIC_CODE_COUNT(quic_blocked_again_on_can_write); + } + } +} + +bool QuicDispatcher::HasPendingWrites() const { + return !write_blocked_list_.empty(); +} + +void QuicDispatcher::Shutdown() { + while (!session_map_.empty()) { + QuicSession* session = session_map_.begin()->second.get(); + session->connection()->CloseConnection( + QUIC_PEER_GOING_AWAY, "Server shutdown imminent", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + // Validate that the session removes itself from the session map on close. + DCHECK(session_map_.empty() || + session_map_.begin()->second.get() != session); + } + DeleteSessions(); +} + +void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) { + auto it = session_map_.find(connection_id); + if (it == session_map_.end()) { + QUIC_BUG << "ConnectionId " << connection_id + << " does not exist in the session map. Error: " + << QuicErrorCodeToString(error); + QUIC_BUG << QuicStackTrace(); + return; + } + + QUIC_DLOG_IF(INFO, error != QUIC_NO_ERROR) + << "Closing connection (" << connection_id + << ") due to error: " << QuicErrorCodeToString(error) + << ", with details: " << error_details; + + QuicConnection* connection = it->second->connection(); + if (ShouldDestroySessionAsynchronously()) { + // Set up alarm to fire immediately to bring destruction of this session + // out of current call stack. + if (closed_session_list_.empty()) { + delete_sessions_alarm_->Update(helper()->GetClock()->ApproximateNow(), + QuicTime::Delta::Zero()); + } + closed_session_list_.push_back(std::move(it->second)); + } + const bool should_close_statelessly = + (error == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT); + CleanUpSession(it, connection, should_close_statelessly, source); +} + +void QuicDispatcher::OnWriteBlocked( + QuicBlockedWriterInterface* blocked_writer) { + if (!blocked_writer->IsWriterBlocked()) { + // It is a programming error if this ever happens. When we are sure it is + // not happening, replace it with a DCHECK. + QUIC_BUG + << "Tried to add writer into blocked list when it shouldn't be added"; + // Return without adding the connection to the blocked list, to avoid + // infinite loops in OnCanWrite. + return; + } + + write_blocked_list_.insert(std::make_pair(blocked_writer, true)); +} + +void QuicDispatcher::OnRstStreamReceived(const QuicRstStreamFrame& frame) {} + +void QuicDispatcher::OnStopSendingReceived(const QuicStopSendingFrame& frame) {} + +void QuicDispatcher::OnConnectionAddedToTimeWaitList( + QuicConnectionId connection_id) { + QUIC_DLOG(INFO) << "Connection " << connection_id + << " added to time wait list."; +} + +void QuicDispatcher::StatelesslyTerminateConnection( + QuicConnectionId connection_id, + PacketHeaderFormat format, + ParsedQuicVersion version, + QuicErrorCode error_code, + const QuicString& error_details, + QuicTimeWaitListManager::TimeWaitAction action) { + if (format != IETF_QUIC_LONG_HEADER_PACKET) { + QUIC_DVLOG(1) << "Statelessly terminating " << connection_id + << " based on a non-ietf-long packet, action:" << action + << ", error_code:" << error_code + << ", error_details:" << error_details; + time_wait_list_manager_->AddConnectionIdToTimeWait( + connection_id, format != GOOGLE_QUIC_PACKET, action, ENCRYPTION_NONE, + nullptr); + return; + } + + // If the version is known and supported by framer, send a connection close. + if (framer_.IsSupportedVersion(version)) { + QUIC_DVLOG(1) + << "Statelessly terminating " << connection_id + << " based on an ietf-long packet, which has a supported version:" + << version << ", error_code:" << error_code + << ", error_details:" << error_details; + // Set framer_ to the packet's version such that the connection close can be + // processed by the client. + ParsedQuicVersion original_version = framer_.version(); + framer_.set_version(version); + + StatelessConnectionTerminator terminator( + connection_id, &framer_, helper_.get(), time_wait_list_manager_.get()); + // This also adds the connection to time wait list. + terminator.CloseConnection(error_code, error_details, true); + + // Restore framer_ to the original version, as if nothing changed in it. + framer_.set_version(original_version); + return; + } + + QUIC_DVLOG(1) + << "Statelessly terminating " << connection_id + << " based on an ietf-long packet, which has an unsupported version:" + << version << ", error_code:" << error_code + << ", error_details:" << error_details; + // Version is unknown or unsupported by framer, send a version negotiation + // with an empty version list, which can be understood by the client. + std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; + termination_packets.push_back(QuicFramer::BuildVersionNegotiationPacket( + connection_id, /*ietf_quic=*/true, + ParsedQuicVersionVector{UnsupportedQuicVersion()})); + time_wait_list_manager()->AddConnectionIdToTimeWait( + connection_id, /*ietf_quic=*/true, + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_NONE, + &termination_packets); +} + +void QuicDispatcher::OnPacket() {} + +void QuicDispatcher::OnError(QuicFramer* framer) { + QuicErrorCode error = framer->error(); + SetLastError(error); + QUIC_DLOG(INFO) << QuicErrorCodeToString(error); +} + +bool QuicDispatcher::ShouldCreateSessionForUnknownVersion( + QuicVersionLabel /*version_label*/) { + return false; +} + +bool QuicDispatcher::OnProtocolVersionMismatch( + ParsedQuicVersion /*received_version*/, + PacketHeaderFormat /*form*/) { + QUIC_BUG_IF( + !time_wait_list_manager_->IsConnectionIdInTimeWait( + current_connection_id_) && + !ShouldCreateSessionForUnknownVersion(framer_.last_version_label())) + << "Unexpected version mismatch: " + << QuicVersionLabelToString(framer_.last_version_label()); + + // Keep processing after protocol mismatch - this will be dealt with by the + // time wait list or connection that we will create. + return true; +} + +void QuicDispatcher::OnPublicResetPacket( + const QuicPublicResetPacket& /*packet*/) { + DCHECK(false); +} + +void QuicDispatcher::OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& /*packet*/) { + DCHECK(false); +} + +void QuicDispatcher::OnDecryptedPacket(EncryptionLevel level) { + DCHECK(false); +} + +bool QuicDispatcher::OnPacketHeader(const QuicPacketHeader& /*header*/) { + DCHECK(false); + return false; +} + +void QuicDispatcher::OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) { + DCHECK(false); +} + +bool QuicDispatcher::OnStreamFrame(const QuicStreamFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnCryptoFrame(const QuicCryptoFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnAckFrameStart(QuicPacketNumber /*largest_acked*/, + QuicTime::Delta /*ack_delay_time*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnAckRange(QuicPacketNumber /*start*/, + QuicPacketNumber /*end*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnAckTimestamp(QuicPacketNumber /*packet_number*/, + QuicTime /*timestamp*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnAckFrameEnd(QuicPacketNumber /*start*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnStopWaitingFrame(const QuicStopWaitingFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnPaddingFrame(const QuicPaddingFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnPingFrame(const QuicPingFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnConnectionCloseFrame( + const QuicConnectionCloseFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnApplicationCloseFrame( + const QuicApplicationCloseFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) { + return true; +} + +bool QuicDispatcher::OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& frame) { + return true; +} + +bool QuicDispatcher::OnStopSendingFrame(const QuicStopSendingFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnPathChallengeFrame( + const QuicPathChallengeFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnPathResponseFrame( + const QuicPathResponseFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnWindowUpdateFrame( + const QuicWindowUpdateFrame& /*frame*/) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnBlockedFrame(const QuicBlockedFrame& frame) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnNewConnectionIdFrame( + const QuicNewConnectionIdFrame& frame) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnRetireConnectionIdFrame( + const QuicRetireConnectionIdFrame& frame) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnNewTokenFrame(const QuicNewTokenFrame& frame) { + DCHECK(false); + return false; +} + +bool QuicDispatcher::OnMessageFrame(const QuicMessageFrame& frame) { + DCHECK(false); + return false; +} + +void QuicDispatcher::OnPacketComplete() { + DCHECK(false); +} + +bool QuicDispatcher::IsValidStatelessResetToken(QuicUint128 token) const { + DCHECK(false); + return false; +} + +void QuicDispatcher::OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) { + DCHECK(false); +} + +void QuicDispatcher::OnExpiredPackets( + QuicConnectionId connection_id, + BufferedPacketList early_arrived_packets) { + QUIC_CODE_COUNT(quic_reject_buffered_packets_expired); + StatelesslyTerminateConnection( + connection_id, + early_arrived_packets.ietf_quic ? IETF_QUIC_LONG_HEADER_PACKET + : GOOGLE_QUIC_PACKET, + early_arrived_packets.version, QUIC_HANDSHAKE_FAILED, + "Packets buffered for too long", + quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); +} + +void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) { + // Reset the counter before starting creating connections. + new_sessions_allowed_per_event_loop_ = max_connections_to_create; + for (; new_sessions_allowed_per_event_loop_ > 0; + --new_sessions_allowed_per_event_loop_) { + QuicConnectionId connection_id; + BufferedPacketList packet_list = + buffered_packets_.DeliverPacketsForNextConnection(&connection_id); + const std::list<BufferedPacket>& packets = packet_list.buffered_packets; + if (packets.empty()) { + return; + } + QuicSession* session = + CreateQuicSession(connection_id, packets.front().peer_address, + packet_list.alpn, packet_list.version); + QUIC_DLOG(INFO) << "Created new session for " << connection_id; + session_map_.insert(std::make_pair(connection_id, QuicWrapUnique(session))); + DeliverPacketsToSession(packets, session); + } +} + +bool QuicDispatcher::HasChlosBuffered() const { + return buffered_packets_.HasChlosBuffered(); +} + +bool QuicDispatcher::ShouldCreateOrBufferPacketForConnection( + QuicConnectionId connection_id, + bool ietf_quic) { + VLOG(1) << "Received packet from new connection " << connection_id; + return true; +} + +// Return true if there is any packet buffered in the store. +bool QuicDispatcher::HasBufferedPackets(QuicConnectionId connection_id) { + return buffered_packets_.HasBufferedPackets(connection_id); +} + +void QuicDispatcher::OnBufferPacketFailure(EnqueuePacketResult result, + QuicConnectionId connection_id) { + QUIC_DLOG(INFO) << "Fail to buffer packet on connection " << connection_id + << " because of " << result; +} + +void QuicDispatcher::OnConnectionRejectedStatelessly() {} + +void QuicDispatcher::OnConnectionClosedStatelessly(QuicErrorCode error) {} + +bool QuicDispatcher::ShouldAttemptCheapStatelessRejection() { + return true; +} + +QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() { + return new QuicTimeWaitListManager(writer_.get(), this, helper_->GetClock(), + alarm_factory_.get()); +} + +void QuicDispatcher::BufferEarlyPacket(QuicConnectionId connection_id, + bool ietf_quic, + ParsedQuicVersion version) { + bool is_new_connection = !buffered_packets_.HasBufferedPackets(connection_id); + if (is_new_connection && + !ShouldCreateOrBufferPacketForConnection(connection_id, ietf_quic)) { + return; + } + + EnqueuePacketResult rs = buffered_packets_.EnqueuePacket( + connection_id, ietf_quic, *current_packet_, current_self_address_, + current_peer_address_, /*is_chlo=*/false, + /*alpn=*/"", version); + if (rs != EnqueuePacketResult::SUCCESS) { + OnBufferPacketFailure(rs, connection_id); + } +} + +void QuicDispatcher::ProcessChlo(PacketHeaderFormat form, + ParsedQuicVersion version) { + if (!accept_new_connections_) { + // Don't any create new connection. + QUIC_CODE_COUNT(quic_reject_stop_accepting_new_connections); + StatelesslyTerminateConnection( + current_connection_id(), form, version, QUIC_HANDSHAKE_FAILED, + "Stop accepting new connections", + quic::QuicTimeWaitListManager::SEND_STATELESS_RESET); + // Time wait list will reject the packet correspondingly. + time_wait_list_manager()->ProcessPacket( + current_self_address(), current_peer_address(), current_connection_id(), + form, GetPerPacketContext()); + return; + } + if (!buffered_packets_.HasBufferedPackets(current_connection_id_) && + !ShouldCreateOrBufferPacketForConnection(current_connection_id_, + form != GOOGLE_QUIC_PACKET)) { + return; + } + if (FLAGS_quic_allow_chlo_buffering && + new_sessions_allowed_per_event_loop_ <= 0) { + // Can't create new session any more. Wait till next event loop. + QUIC_BUG_IF(buffered_packets_.HasChloForConnection(current_connection_id_)); + EnqueuePacketResult rs = buffered_packets_.EnqueuePacket( + current_connection_id_, form != GOOGLE_QUIC_PACKET, *current_packet_, + current_self_address_, current_peer_address_, + /*is_chlo=*/true, current_alpn_, framer_.version()); + if (rs != EnqueuePacketResult::SUCCESS) { + OnBufferPacketFailure(rs, current_connection_id_); + } + return; + } + // Creates a new session and process all buffered packets for this connection. + QuicSession* session = + CreateQuicSession(current_connection_id_, current_peer_address_, + current_alpn_, framer_.version()); + QUIC_DLOG(INFO) << "Created new session for " << current_connection_id_; + session_map_.insert( + std::make_pair(current_connection_id_, QuicWrapUnique(session))); + std::list<BufferedPacket> packets = + buffered_packets_.DeliverPackets(current_connection_id_).buffered_packets; + // Process CHLO at first. + session->ProcessUdpPacket(current_self_address_, current_peer_address_, + *current_packet_); + // Deliver queued-up packets in the same order as they arrived. + // Do this even when flag is off because there might be still some packets + // buffered in the store before flag is turned off. + DeliverPacketsToSession(packets, session); + --new_sessions_allowed_per_event_loop_; +} + +const QuicSocketAddress QuicDispatcher::GetClientAddress() const { + return current_peer_address_; +} + +bool QuicDispatcher::ShouldDestroySessionAsynchronously() { + return true; +} + +void QuicDispatcher::SetLastError(QuicErrorCode error) { + last_error_ = error; +} + +bool QuicDispatcher::OnUnauthenticatedUnknownPublicHeader( + const QuicPacketHeader& header) { + return true; +} + +class StatelessRejectorProcessDoneCallback + : public StatelessRejector::ProcessDoneCallback { + public: + StatelessRejectorProcessDoneCallback(QuicDispatcher* dispatcher, + ParsedQuicVersion first_version, + PacketHeaderFormat form) + : dispatcher_(dispatcher), + current_client_address_(dispatcher->current_client_address_), + current_peer_address_(dispatcher->current_peer_address_), + current_self_address_(dispatcher->current_self_address_), + additional_context_(dispatcher->GetPerPacketContext()), + current_packet_( + dispatcher->current_packet_->Clone()), // Note: copies the packet + first_version_(first_version), + current_packet_format_(form) {} + + void Run(std::unique_ptr<StatelessRejector> rejector) override { + if (additional_context_ != nullptr) { + dispatcher_->RestorePerPacketContext(std::move(additional_context_)); + } + dispatcher_->OnStatelessRejectorProcessDone( + std::move(rejector), current_client_address_, current_peer_address_, + current_self_address_, std::move(current_packet_), first_version_, + current_packet_format_); + } + + private: + QuicDispatcher* dispatcher_; + QuicSocketAddress current_client_address_; + QuicSocketAddress current_peer_address_; + QuicSocketAddress current_self_address_; + // TODO(wub): Wrap all current_* variables into PerPacketContext. And rename + // |additional_context_| to |context_|. + std::unique_ptr<QuicPerPacketContext> additional_context_; + std::unique_ptr<QuicReceivedPacket> current_packet_; + ParsedQuicVersion first_version_; + const PacketHeaderFormat current_packet_format_; +}; + +void QuicDispatcher::MaybeRejectStatelessly(QuicConnectionId connection_id, + + PacketHeaderFormat form, + ParsedQuicVersion version) { + if (version.handshake_protocol == PROTOCOL_TLS1_3) { + ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form, + version); + return; + // TODO(nharper): Support buffering non-ClientHello packets when using TLS. + } + // TODO(rch): This logic should probably live completely inside the rejector. + if (!FLAGS_quic_allow_chlo_buffering || + !GetQuicReloadableFlag(quic_use_cheap_stateless_rejects) || + !GetQuicReloadableFlag(enable_quic_stateless_reject_support) || + !ShouldAttemptCheapStatelessRejection()) { + // Not use cheap stateless reject. + ChloAlpnExtractor alpn_extractor; + if (FLAGS_quic_allow_chlo_buffering && + !ChloExtractor::Extract(*current_packet_, GetSupportedVersions(), + config_->create_session_tag_indicators(), + &alpn_extractor, connection_id.length())) { + // Buffer non-CHLO packets. + ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form, + version); + return; + } + current_alpn_ = alpn_extractor.ConsumeAlpn(); + ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form, + version); + return; + } + + std::unique_ptr<StatelessRejector> rejector(new StatelessRejector( + version, GetSupportedVersions(), crypto_config_, &compressed_certs_cache_, + helper()->GetClock(), helper()->GetRandomGenerator(), + current_packet_->length(), current_client_address_, + current_self_address_)); + ChloValidator validator(session_helper_.get(), current_client_address_, + current_peer_address_, current_self_address_, + rejector.get()); + if (!ChloExtractor::Extract(*current_packet_, GetSupportedVersions(), + config_->create_session_tag_indicators(), + &validator, connection_id.length())) { + ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form, version); + return; + } + current_alpn_ = validator.ConsumeAlpn(); + + if (!validator.can_accept()) { + // This CHLO is prohibited by policy. + QUIC_CODE_COUNT(quic_reject_cant_accept_chlo); + StatelessConnectionTerminator terminator(connection_id, &framer_, helper(), + time_wait_list_manager_.get()); + terminator.CloseConnection(QUIC_HANDSHAKE_FAILED, validator.error_details(), + form != GOOGLE_QUIC_PACKET); + OnConnectionClosedStatelessly(QUIC_HANDSHAKE_FAILED); + ProcessUnauthenticatedHeaderFate(kFateTimeWait, connection_id, form, + version); + return; + } + + // If we were able to make a decision about this CHLO based purely on the + // information available in OnChlo, just invoke the done callback immediately. + if (rejector->state() != StatelessRejector::UNKNOWN) { + ProcessStatelessRejectorState(std::move(rejector), + version.transport_version, form); + return; + } + + // Insert into set of connection IDs to buffer + const bool ok = + temporarily_buffered_connections_.insert(connection_id).second; + QUIC_BUG_IF(!ok) + << "Processing multiple stateless rejections for connection ID " + << connection_id; + + // Continue stateless rejector processing + std::unique_ptr<StatelessRejectorProcessDoneCallback> cb( + new StatelessRejectorProcessDoneCallback(this, version, form)); + StatelessRejector::Process(std::move(rejector), std::move(cb)); +} + +void QuicDispatcher::OnStatelessRejectorProcessDone( + std::unique_ptr<StatelessRejector> rejector, + const QuicSocketAddress& current_client_address, + const QuicSocketAddress& current_peer_address, + const QuicSocketAddress& current_self_address, + std::unique_ptr<QuicReceivedPacket> current_packet, + ParsedQuicVersion first_version, + PacketHeaderFormat current_packet_format) { + // Reset current_* to correspond to the packet which initiated the stateless + // reject logic. + current_client_address_ = current_client_address; + current_peer_address_ = current_peer_address; + current_self_address_ = current_self_address; + current_packet_ = current_packet.get(); + current_connection_id_ = rejector->connection_id(); + framer_.set_version(first_version); + + // Stop buffering packets on this connection + const auto num_erased = + temporarily_buffered_connections_.erase(rejector->connection_id()); + QUIC_BUG_IF(num_erased != 1) << "Completing stateless rejection logic for " + "non-buffered connection ID " + << rejector->connection_id(); + + // If this connection has gone into time-wait during the async processing, + // don't proceed. + if (time_wait_list_manager_->IsConnectionIdInTimeWait( + rejector->connection_id())) { + time_wait_list_manager_->ProcessPacket( + current_self_address, current_peer_address, rejector->connection_id(), + current_packet_format, GetPerPacketContext()); + return; + } + + ProcessStatelessRejectorState(std::move(rejector), + first_version.transport_version, + current_packet_format); +} + +void QuicDispatcher::ProcessStatelessRejectorState( + std::unique_ptr<StatelessRejector> rejector, + QuicTransportVersion first_version, + PacketHeaderFormat form) { + QuicPacketFate fate; + switch (rejector->state()) { + case StatelessRejector::FAILED: { + // There was an error processing the client hello. + QUIC_CODE_COUNT(quic_reject_error_processing_chlo); + StatelessConnectionTerminator terminator(rejector->connection_id(), + &framer_, helper(), + time_wait_list_manager_.get()); + terminator.CloseConnection(rejector->error(), rejector->error_details(), + form != GOOGLE_QUIC_PACKET); + fate = kFateTimeWait; + break; + } + + case StatelessRejector::UNSUPPORTED: + // Cheap stateless rejects are not supported so process the packet. + fate = kFateProcess; + break; + + case StatelessRejector::ACCEPTED: + // Contains a valid CHLO, so process the packet and create a connection. + fate = kFateProcess; + break; + + case StatelessRejector::REJECTED: { + QUIC_BUG_IF(first_version != framer_.transport_version()) + << "SREJ: Client's version: " << QuicVersionToString(first_version) + << " is different from current dispatcher framer's version: " + << QuicVersionToString(framer_.transport_version()); + StatelessConnectionTerminator terminator(rejector->connection_id(), + &framer_, helper(), + time_wait_list_manager_.get()); + terminator.RejectConnection( + rejector->reply().GetSerialized().AsStringPiece(), + form != GOOGLE_QUIC_PACKET); + OnConnectionRejectedStatelessly(); + fate = kFateTimeWait; + break; + } + + default: + QUIC_BUG << "Rejector has invalid state " << rejector->state(); + fate = kFateDrop; + break; + } + ProcessUnauthenticatedHeaderFate(fate, rejector->connection_id(), form, + rejector->version()); +} + +const QuicTransportVersionVector& +QuicDispatcher::GetSupportedTransportVersions() { + return version_manager_->GetSupportedTransportVersions(); +} + +const ParsedQuicVersionVector& QuicDispatcher::GetSupportedVersions() { + return version_manager_->GetSupportedVersions(); +} + +void QuicDispatcher::DeliverPacketsToSession( + const std::list<BufferedPacket>& packets, + QuicSession* session) { + for (const BufferedPacket& packet : packets) { + session->ProcessUdpPacket(packet.self_address, packet.peer_address, + *(packet.packet)); + } +} + +void QuicDispatcher::DisableFlagValidation() { + framer_.set_validate_flags(false); +} + +} // namespace quic
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h new file mode 100644 index 0000000..85e6da6 --- /dev/null +++ b/quic/core/quic_dispatcher.h
@@ -0,0 +1,478 @@ +// Copyright (c) 2012 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. + +// A server side dispatcher which dispatches a given client's data to their +// stream. + +#ifndef QUICHE_QUIC_CORE_QUIC_DISPATCHER_H_ +#define QUICHE_QUIC_CORE_QUIC_DISPATCHER_H_ + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_version_manager.h" +#include "net/third_party/quiche/src/quic/core/stateless_rejector.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { +namespace test { +class QuicDispatcherPeer; +} // namespace test + +class QuicConfig; +class QuicCryptoServerConfig; + +class QuicDispatcher : public QuicTimeWaitListManager::Visitor, + public ProcessPacketInterface, + public QuicFramerVisitorInterface, + public QuicBufferedPacketStore::VisitorInterface { + public: + // Ideally we'd have a linked_hash_set: the boolean is unused. + typedef QuicLinkedHashMap<QuicBlockedWriterInterface*, bool> WriteBlockedList; + + QuicDispatcher(const QuicConfig* config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager, + std::unique_ptr<QuicConnectionHelperInterface> helper, + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, + std::unique_ptr<QuicAlarmFactory> alarm_factory, + uint8_t expected_connection_id_length); + QuicDispatcher(const QuicDispatcher&) = delete; + QuicDispatcher& operator=(const QuicDispatcher&) = delete; + + ~QuicDispatcher() override; + + // Takes ownership of |writer|. + void InitializeWithWriter(QuicPacketWriter* writer); + + // Process the incoming packet by creating a new session, passing it to + // an existing session, or passing it to the time wait list. + void ProcessPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet) override; + + // Called when the socket becomes writable to allow queued writes to happen. + virtual void OnCanWrite(); + + // Returns true if there's anything in the blocked writer list. + virtual bool HasPendingWrites() const; + + // Sends ConnectionClose frames to all connected clients. + void Shutdown(); + + // QuicSession::Visitor interface implementation (via inheritance of + // QuicTimeWaitListManager::Visitor): + // Ensure that the closed connection is cleaned up asynchronously. + void OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) override; + + // QuicSession::Visitor interface implementation (via inheritance of + // QuicTimeWaitListManager::Visitor): + // Queues the blocked writer for later resumption. + void OnWriteBlocked(QuicBlockedWriterInterface* blocked_writer) override; + + // QuicSession::Visitor interface implementation (via inheritance of + // QuicTimeWaitListManager::Visitor): + // Collects reset error code received on streams. + void OnRstStreamReceived(const QuicRstStreamFrame& frame) override; + + // QuicSession::Visitor interface implementation (via inheritance of + // QuicTimeWaitListManager::Visitor): + // Collects reset error code received on streams. + void OnStopSendingReceived(const QuicStopSendingFrame& frame) override; + + // QuicTimeWaitListManager::Visitor interface implementation + // Called whenever the time wait list manager adds a new connection to the + // time-wait list. + void OnConnectionAddedToTimeWaitList(QuicConnectionId connection_id) override; + + using SessionMap = QuicUnorderedMap<QuicConnectionId, + std::unique_ptr<QuicSession>, + QuicConnectionIdHash>; + + const SessionMap& session_map() const { return session_map_; } + + // Deletes all sessions on the closed session list and clears the list. + virtual void DeleteSessions(); + + // The largest packet number we expect to receive with a connection + // ID for a connection that is not established yet. The current design will + // send a handshake and then up to 50 or so data packets, and then it may + // resend the handshake packet up to 10 times. (Retransmitted packets are + // sent with unique packet numbers.) + static const uint64_t kMaxReasonableInitialPacketNumber = 100; + static_assert(kMaxReasonableInitialPacketNumber >= + kInitialCongestionWindow + 10, + "kMaxReasonableInitialPacketNumber is unreasonably small " + "relative to kInitialCongestionWindow."); + + // QuicFramerVisitorInterface implementation. Not expected to be called + // outside of this class. + void OnPacket() override; + // Called when the public header has been parsed. + bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override; + // Called when the private header has been parsed of a data packet that is + // destined for the time wait manager. + bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override; + void OnError(QuicFramer* framer) override; + bool OnProtocolVersionMismatch(ParsedQuicVersion received_version, + PacketHeaderFormat form) override; + + // The following methods should never get called because + // OnUnauthenticatedPublicHeader() or OnUnauthenticatedHeader() (whichever + // was called last), will return false and prevent a subsequent invocation + // of these methods. Thus, the payload of the packet is never processed in + // the dispatcher. + void OnPublicResetPacket(const QuicPublicResetPacket& packet) override; + void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) override; + void OnDecryptedPacket(EncryptionLevel level) override; + bool OnPacketHeader(const QuicPacketHeader& header) override; + void OnCoalescedPacket(const QuicEncryptedPacket& packet) 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 OnPaddingFrame(const QuicPaddingFrame& frame) override; + bool OnPingFrame(const QuicPingFrame& frame) override; + bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override; + bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override; + bool OnApplicationCloseFrame(const QuicApplicationCloseFrame& 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 OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override; + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override; + bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override; + bool OnBlockedFrame(const QuicBlockedFrame& frame) override; + bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override; + bool OnRetireConnectionIdFrame( + const QuicRetireConnectionIdFrame& frame) override; + bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override; + bool OnMessageFrame(const QuicMessageFrame& frame) override; + void OnPacketComplete() override; + bool IsValidStatelessResetToken(QuicUint128 token) const override; + void OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) override; + + // QuicBufferedPacketStore::VisitorInterface implementation. + void OnExpiredPackets(QuicConnectionId connection_id, + QuicBufferedPacketStore::BufferedPacketList + early_arrived_packets) override; + + // Create connections for previously buffered CHLOs as many as allowed. + virtual void ProcessBufferedChlos(size_t max_connections_to_create); + + // Return true if there is CHLO buffered. + virtual bool HasChlosBuffered() const; + + protected: + virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id, + const QuicSocketAddress& peer_address, + QuicStringPiece alpn, + const ParsedQuicVersion& version) = 0; + + // Called when a connection is rejected statelessly. + virtual void OnConnectionRejectedStatelessly(); + + // Called when a connection is closed statelessly. + virtual void OnConnectionClosedStatelessly(QuicErrorCode error); + + // Returns true if cheap stateless rejection should be attempted. + virtual bool ShouldAttemptCheapStatelessRejection(); + + // Values to be returned by ValidityChecks() to indicate what should be done + // with a packet. Fates with greater values are considered to be higher + // priority, in that if one validity check indicates a lower-valued fate and + // another validity check indicates a higher-valued fate, the higher-valued + // fate should be obeyed. + enum QuicPacketFate { + // Process the packet normally, which is usually to establish a connection. + kFateProcess, + // Put the connection ID into time-wait state and send a public reset. + kFateTimeWait, + // Buffer the packet. + kFateBuffer, + // Drop the packet (ignore and give no response). + kFateDrop, + }; + + // This method is called by OnUnauthenticatedHeader on packets not associated + // with a known connection ID. It applies validity checks and returns a + // QuicPacketFate to tell what should be done with the packet. + virtual QuicPacketFate ValidityChecks(const QuicPacketHeader& header); + + // Create and return the time wait list manager for this dispatcher, which + // will be owned by the dispatcher as time_wait_list_manager_ + virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager(); + + // Called when |connection_id| doesn't have an open connection yet, to buffer + // |current_packet_| until it can be delivered to the connection. + void BufferEarlyPacket(QuicConnectionId connection_id, + bool ietf_quic, + ParsedQuicVersion version); + + // Called when |current_packet_| is a CHLO packet. Creates a new connection + // and delivers any buffered packets for that connection id. + void ProcessChlo(PacketHeaderFormat form, ParsedQuicVersion version); + + // Returns the actual client address of the current packet. + // This function should only be called once per packet at the very beginning + // of ProcessPacket(), its result is saved to |current_client_address_|, which + // is guaranteed to be valid even in the stateless rejector's callback(i.e. + // OnStatelessRejectorProcessDone). + // By default, this function returns |current_peer_address_|, subclasses have + // the option to override this function to return a different address. + virtual const QuicSocketAddress GetClientAddress() const; + + // Return true if dispatcher wants to destroy session outside of + // OnConnectionClosed() call stack. + virtual bool ShouldDestroySessionAsynchronously(); + + QuicTimeWaitListManager* time_wait_list_manager() { + return time_wait_list_manager_.get(); + } + + const QuicTransportVersionVector& GetSupportedTransportVersions(); + + const ParsedQuicVersionVector& GetSupportedVersions(); + + QuicConnectionId current_connection_id() const { + return current_connection_id_; + } + const QuicSocketAddress& current_self_address() const { + return current_self_address_; + } + const QuicSocketAddress& current_peer_address() const { + return current_peer_address_; + } + const QuicSocketAddress& current_client_address() const { + return current_client_address_; + } + const QuicReceivedPacket& current_packet() const { return *current_packet_; } + + const QuicConfig& config() const { return *config_; } + + const QuicCryptoServerConfig* crypto_config() const { return crypto_config_; } + + QuicCompressedCertsCache* compressed_certs_cache() { + return &compressed_certs_cache_; + } + + QuicConnectionHelperInterface* helper() { return helper_.get(); } + + QuicCryptoServerStream::Helper* session_helper() { + return session_helper_.get(); + } + + QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); } + + QuicPacketWriter* writer() { return writer_.get(); } + + // Returns true if a session should be created for a connection with an + // unknown version identified by |version_label|. + virtual bool ShouldCreateSessionForUnknownVersion( + QuicVersionLabel version_label); + + void SetLastError(QuicErrorCode error); + + // Called when the public header has been parsed and the session has been + // looked up, and the session was not found in the active list of sessions. + // Returns false if processing should stop after this call. + virtual bool OnUnauthenticatedUnknownPublicHeader( + const QuicPacketHeader& header); + + // Called when a new connection starts to be handled by this dispatcher. + // Either this connection is created or its packets is buffered while waiting + // for CHLO. Returns true if a new connection should be created or its packets + // should be buffered, false otherwise. + virtual bool ShouldCreateOrBufferPacketForConnection( + QuicConnectionId connection_id, + bool ietf_quic); + + bool HasBufferedPackets(QuicConnectionId connection_id); + + // Called when BufferEarlyPacket() fail to buffer the packet. + virtual void OnBufferPacketFailure( + QuicBufferedPacketStore::EnqueuePacketResult result, + QuicConnectionId connection_id); + + // Removes the session from the session map and write blocked list, and adds + // the ConnectionId to the time-wait list. If |session_closed_statelessly| is + // true, any future packets for the ConnectionId will be black-holed. + virtual void CleanUpSession(SessionMap::iterator it, + QuicConnection* connection, + bool session_closed_statelessly, + ConnectionCloseSource source); + + void StopAcceptingNewConnections(); + + // Called to terminate a connection statelessly. Depending on |format|, either + // 1) send connection close with |error_code| and |error_details| and add + // connection to time wait list or 2) directly add connection to time wait + // list with |action|. + void StatelesslyTerminateConnection( + QuicConnectionId connection_id, + PacketHeaderFormat format, + ParsedQuicVersion version, + QuicErrorCode error_code, + const QuicString& error_details, + QuicTimeWaitListManager::TimeWaitAction action); + + // Save/Restore per packet context. Used by async stateless rejector. + virtual std::unique_ptr<QuicPerPacketContext> GetPerPacketContext() const; + virtual void RestorePerPacketContext( + std::unique_ptr<QuicPerPacketContext> /*context*/) {} + + // Skip validating that the public flags are set to legal values. + void DisableFlagValidation(); + + private: + friend class test::QuicDispatcherPeer; + friend class StatelessRejectorProcessDoneCallback; + + typedef QuicUnorderedSet<QuicConnectionId, QuicConnectionIdHash> + QuicConnectionIdSet; + + // Attempts to reject the connection statelessly, if stateless rejects are + // possible and if the current packet contains a CHLO message. Determines a + // fate which describes what subsequent processing should be performed on the + // packets, like ValidityChecks, and invokes ProcessUnauthenticatedHeaderFate. + void MaybeRejectStatelessly(QuicConnectionId connection_id, + PacketHeaderFormat form, + ParsedQuicVersion version); + + // Deliver |packets| to |session| for further processing. + void DeliverPacketsToSession( + const std::list<QuicBufferedPacketStore::BufferedPacket>& packets, + QuicSession* session); + + // Perform the appropriate actions on the current packet based on |fate| - + // either process, buffer, or drop it. + void ProcessUnauthenticatedHeaderFate(QuicPacketFate fate, + QuicConnectionId connection_id, + PacketHeaderFormat form, + ParsedQuicVersion version); + + // Invoked when StatelessRejector::Process completes. |first_version| is the + // version of the packet which initiated the stateless reject. + // WARNING: This function can be called when a async proof returns, i.e. not + // from a stack traceable to ProcessPacket(). + // TODO(fayang): maybe consider not using callback when there is no crypto + // involved. + void OnStatelessRejectorProcessDone( + std::unique_ptr<StatelessRejector> rejector, + const QuicSocketAddress& current_client_address, + const QuicSocketAddress& current_peer_address, + const QuicSocketAddress& current_self_address, + std::unique_ptr<QuicReceivedPacket> current_packet, + ParsedQuicVersion first_version, + PacketHeaderFormat current_packet_format); + + // Examine the state of the rejector and decide what to do with the current + // packet. + void ProcessStatelessRejectorState( + std::unique_ptr<StatelessRejector> rejector, + QuicTransportVersion first_version, + PacketHeaderFormat form); + + void set_new_sessions_allowed_per_event_loop( + int16_t new_sessions_allowed_per_event_loop) { + new_sessions_allowed_per_event_loop_ = new_sessions_allowed_per_event_loop; + } + + const QuicConfig* config_; + + const QuicCryptoServerConfig* crypto_config_; + + // The cache for most recently compressed certs. + QuicCompressedCertsCache compressed_certs_cache_; + + // The list of connections waiting to write. + WriteBlockedList write_blocked_list_; + + SessionMap session_map_; + + // Entity that manages connection_ids in time wait state. + std::unique_ptr<QuicTimeWaitListManager> time_wait_list_manager_; + + // The list of closed but not-yet-deleted sessions. + std::vector<std::unique_ptr<QuicSession>> closed_session_list_; + + // The helper used for all connections. + std::unique_ptr<QuicConnectionHelperInterface> helper_; + + // The helper used for all sessions. + std::unique_ptr<QuicCryptoServerStream::Helper> session_helper_; + + // Creates alarms. + std::unique_ptr<QuicAlarmFactory> alarm_factory_; + + // An alarm which deletes closed sessions. + std::unique_ptr<QuicAlarm> delete_sessions_alarm_; + + // The writer to write to the socket with. + std::unique_ptr<QuicPacketWriter> writer_; + + // Packets which are buffered until a connection can be created to handle + // them. + QuicBufferedPacketStore buffered_packets_; + + // Set of connection IDs for which asynchronous CHLO processing is in + // progress, making it necessary to buffer any other packets which arrive on + // that connection until CHLO processing is complete. + QuicConnectionIdSet temporarily_buffered_connections_; + + // Information about the packet currently being handled. + + // Used for stateless rejector to generate and validate source address token. + QuicSocketAddress current_client_address_; + QuicSocketAddress current_peer_address_; + QuicSocketAddress current_self_address_; + const QuicReceivedPacket* current_packet_; + // If |current_packet_| is a CHLO packet, the extracted alpn. + QuicString current_alpn_; + QuicConnectionId current_connection_id_; + + // Used to get the supported versions based on flag. Does not own. + QuicVersionManager* version_manager_; + + QuicFramer framer_; + + // The last error set by SetLastError(), which is called by + // framer_visitor_->OnError(). + QuicErrorCode last_error_; + + // A backward counter of how many new sessions can be create within current + // event loop. When reaches 0, it means can't create sessions for now. + int16_t new_sessions_allowed_per_event_loop_; + + // True if this dispatcher is not draining. + bool accept_new_connections_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_DISPATCHER_H_
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc new file mode 100644 index 0000000..e4a42a4 --- /dev/null +++ b/quic/core/quic_dispatcher_test.cc
@@ -0,0 +1,2753 @@ +// Copyright (c) 2012 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/quic_dispatcher.h" + +#include <memory> +#include <ostream> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/chlo_extractor.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h" +#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/stateless_rejector.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h" +#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h" + +using testing::_; +using testing::InSequence; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::WithArg; +using testing::WithoutArgs; + +static const size_t kDefaultMaxConnectionsInStore = 100; +static const size_t kMaxConnectionsWithoutCHLO = + kDefaultMaxConnectionsInStore / 2; +static const int16_t kMaxNumSessionsToCreate = 16; + +namespace quic { +namespace test { +namespace { + +class TestQuicSpdyServerSession : public QuicServerSessionBase { + public: + TestQuicSpdyServerSession(const QuicConfig& config, + QuicConnection* connection, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache) + : QuicServerSessionBase(config, + CurrentSupportedVersions(), + connection, + nullptr, + nullptr, + crypto_config, + compressed_certs_cache), + crypto_stream_(QuicServerSessionBase::GetMutableCryptoStream()) {} + TestQuicSpdyServerSession(const TestQuicSpdyServerSession&) = delete; + TestQuicSpdyServerSession& operator=(const TestQuicSpdyServerSession&) = + delete; + + ~TestQuicSpdyServerSession() override { delete connection(); } + + MOCK_METHOD3(OnConnectionClosed, + void(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source)); + MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id)); + MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream pending)); + MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*()); + MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*()); + + QuicCryptoServerStreamBase* CreateQuicCryptoServerStream( + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache) override { + return new QuicCryptoServerStream( + crypto_config, compressed_certs_cache, + GetQuicReloadableFlag(enable_quic_stateless_reject_support), this, + stream_helper()); + } + + void SetCryptoStream(QuicCryptoServerStream* crypto_stream) { + crypto_stream_ = crypto_stream; + } + + QuicCryptoServerStreamBase* GetMutableCryptoStream() override { + return crypto_stream_; + } + + const QuicCryptoServerStreamBase* GetCryptoStream() const override { + return crypto_stream_; + } + + QuicCryptoServerStream::Helper* stream_helper() { + return QuicServerSessionBase::stream_helper(); + } + + private: + QuicCryptoServerStreamBase* crypto_stream_; +}; + +class TestDispatcher : public QuicDispatcher { + public: + TestDispatcher(const QuicConfig* config, + const QuicCryptoServerConfig* crypto_config, + QuicVersionManager* version_manager) + : QuicDispatcher(config, + crypto_config, + version_manager, + QuicMakeUnique<MockQuicConnectionHelper>(), + std::unique_ptr<QuicCryptoServerStream::Helper>( + new QuicSimpleCryptoServerStreamHelper( + QuicRandom::GetInstance())), + QuicMakeUnique<MockAlarmFactory>(), + kQuicDefaultConnectionIdLength) {} + + MOCK_METHOD4(CreateQuicSession, + QuicServerSessionBase*(QuicConnectionId connection_id, + const QuicSocketAddress& peer_address, + QuicStringPiece alpn, + const quic::ParsedQuicVersion& version)); + + MOCK_METHOD2(ShouldCreateOrBufferPacketForConnection, + bool(QuicConnectionId connection_id, bool ietf_quic)); + + struct TestQuicPerPacketContext : public QuicPerPacketContext { + QuicString custom_packet_context; + }; + + std::unique_ptr<QuicPerPacketContext> GetPerPacketContext() const override { + auto test_context = QuicMakeUnique<TestQuicPerPacketContext>(); + test_context->custom_packet_context = custom_packet_context_; + return std::move(test_context); + } + + void RestorePerPacketContext( + std::unique_ptr<QuicPerPacketContext> context) override { + TestQuicPerPacketContext* test_context = + static_cast<TestQuicPerPacketContext*>(context.get()); + custom_packet_context_ = test_context->custom_packet_context; + } + + QuicString custom_packet_context_; + + using QuicDispatcher::current_client_address; + using QuicDispatcher::current_peer_address; + using QuicDispatcher::current_self_address; + using QuicDispatcher::writer; +}; + +// A Connection class which unregisters the session from the dispatcher when +// sending connection close. +// It'd be slightly more realistic to do this from the Session but it would +// involve a lot more mocking. +class MockServerConnection : public MockQuicConnection { + public: + MockServerConnection(QuicConnectionId connection_id, + MockQuicConnectionHelper* helper, + MockAlarmFactory* alarm_factory, + QuicDispatcher* dispatcher) + : MockQuicConnection(connection_id, + helper, + alarm_factory, + Perspective::IS_SERVER), + dispatcher_(dispatcher) {} + + void UnregisterOnConnectionClosed() { + QUIC_LOG(ERROR) << "Unregistering " << connection_id(); + dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR, + "Unregistering.", + ConnectionCloseSource::FROM_SELF); + } + + private: + QuicDispatcher* dispatcher_; +}; + +class QuicDispatcherTest : public QuicTest { + public: + QuicDispatcherTest() + : QuicDispatcherTest(crypto_test_utils::ProofSourceForTesting()) {} + + ParsedQuicVersionVector AllSupportedVersionsIncludingTls() { + SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true); + return AllSupportedVersions(); + } + + explicit QuicDispatcherTest(std::unique_ptr<ProofSource> proof_source) + : + + version_manager_(AllSupportedVersionsIncludingTls()), + crypto_config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance(), + std::move(proof_source), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + server_address_(QuicIpAddress::Any4(), 5), + dispatcher_(new NiceMock<TestDispatcher>(&config_, + &crypto_config_, + &version_manager_)), + time_wait_list_manager_(nullptr), + session1_(nullptr), + session2_(nullptr), + store_(nullptr) {} + + void SetUp() override { + dispatcher_->InitializeWithWriter(new MockPacketWriter()); + // Set the counter to some value to start with. + QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop( + dispatcher_.get(), kMaxNumSessionsToCreate); + ON_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(_, _)) + .WillByDefault(Return(true)); + } + + MockQuicConnection* connection1() { + if (session1_ == nullptr) { + return nullptr; + } + return reinterpret_cast<MockQuicConnection*>(session1_->connection()); + } + + MockQuicConnection* connection2() { + if (session2_ == nullptr) { + return nullptr; + } + return reinterpret_cast<MockQuicConnection*>(session2_->connection()); + } + + // Process a packet with an 8 byte connection id, + // 6 byte packet number, default path id, and packet number 1, + // using the first supported version. + void ProcessPacket(QuicSocketAddress peer_address, + QuicConnectionId connection_id, + bool has_version_flag, + const QuicString& data) { + ProcessPacket(peer_address, connection_id, has_version_flag, data, + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER); + } + + // Process a packet with a default path id, and packet number 1, + // using the first supported version. + void ProcessPacket(QuicSocketAddress peer_address, + QuicConnectionId connection_id, + bool has_version_flag, + const QuicString& data, + QuicConnectionIdIncluded connection_id_included, + QuicPacketNumberLength packet_number_length) { + ProcessPacket(peer_address, connection_id, has_version_flag, data, + connection_id_included, packet_number_length, 1); + } + + // Process a packet using the first supported version. + void ProcessPacket(QuicSocketAddress peer_address, + QuicConnectionId connection_id, + bool has_version_flag, + const QuicString& data, + QuicConnectionIdIncluded connection_id_included, + QuicPacketNumberLength packet_number_length, + uint64_t packet_number) { + ProcessPacket(peer_address, connection_id, has_version_flag, + CurrentSupportedVersions().front(), data, + connection_id_included, packet_number_length, packet_number); + } + + // Processes a packet. + void ProcessPacket(QuicSocketAddress peer_address, + QuicConnectionId connection_id, + bool has_version_flag, + ParsedQuicVersion version, + const QuicString& data, + QuicConnectionIdIncluded connection_id_included, + QuicPacketNumberLength packet_number_length, + uint64_t packet_number) { + ParsedQuicVersionVector versions(SupportedVersions(version)); + std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + connection_id, EmptyQuicConnectionId(), has_version_flag, false, + packet_number, data, connection_id_included, CONNECTION_ID_ABSENT, + packet_number_length, &versions)); + std::unique_ptr<QuicReceivedPacket> received_packet( + ConstructReceivedPacket(*packet, mock_helper_.GetClock()->Now())); + + if (ChloExtractor::Extract(*packet, versions, {}, nullptr, + connection_id.length())) { + // Add CHLO packet to the beginning to be verified first, because it is + // also processed first by new session. + data_connection_map_[connection_id].push_front( + QuicString(packet->data(), packet->length())); + } else { + // For non-CHLO, always append to last. + data_connection_map_[connection_id].push_back( + QuicString(packet->data(), packet->length())); + } + dispatcher_->ProcessPacket(server_address_, peer_address, *received_packet); + } + + void ValidatePacket(QuicConnectionId conn_id, + const QuicEncryptedPacket& packet) { + EXPECT_EQ(data_connection_map_[conn_id].front().length(), + packet.AsStringPiece().length()); + EXPECT_EQ(data_connection_map_[conn_id].front(), packet.AsStringPiece()); + data_connection_map_[conn_id].pop_front(); + } + + QuicServerSessionBase* CreateSession( + TestDispatcher* dispatcher, + const QuicConfig& config, + QuicConnectionId connection_id, + const QuicSocketAddress& peer_address, + MockQuicConnectionHelper* helper, + MockAlarmFactory* alarm_factory, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + TestQuicSpdyServerSession** session) { + MockServerConnection* connection = new MockServerConnection( + connection_id, helper, alarm_factory, dispatcher); + connection->SetQuicPacketWriter(dispatcher->writer(), + /*owns_writer=*/false); + *session = new TestQuicSpdyServerSession(config, connection, crypto_config, + compressed_certs_cache); + connection->set_visitor(*session); + ON_CALL(*connection, CloseConnection(_, _, _)) + .WillByDefault(WithoutArgs(Invoke( + connection, &MockServerConnection::UnregisterOnConnectionClosed))); + return *session; + } + + void CreateTimeWaitListManager() { + time_wait_list_manager_ = new MockTimeWaitListManager( + QuicDispatcherPeer::GetWriter(dispatcher_.get()), dispatcher_.get(), + mock_helper_.GetClock(), &mock_alarm_factory_); + // dispatcher_ takes the ownership of time_wait_list_manager_. + QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), + time_wait_list_manager_); + } + + QuicString SerializeCHLO() { + CryptoHandshakeMessage client_hello; + client_hello.set_tag(kCHLO); + client_hello.SetStringPiece(kALPN, "hq"); + return QuicString(client_hello.GetSerialized().AsStringPiece()); + } + + QuicString SerializeTlsClientHello() { return ""; } + + void MarkSession1Deleted() { session1_ = nullptr; } + + MockQuicConnectionHelper mock_helper_; + MockAlarmFactory mock_alarm_factory_; + QuicConfig config_; + QuicVersionManager version_manager_; + QuicCryptoServerConfig crypto_config_; + QuicSocketAddress server_address_; + std::unique_ptr<NiceMock<TestDispatcher>> dispatcher_; + MockTimeWaitListManager* time_wait_list_manager_; + TestQuicSpdyServerSession* session1_; + TestQuicSpdyServerSession* session2_; + std::map<QuicConnectionId, std::list<QuicString>> data_connection_map_; + QuicBufferedPacketStore* store_; +}; + +TEST_F(QuicDispatcherTest, TlsClientHelloCreatesSession) { + if (CurrentSupportedVersions().front().transport_version < QUIC_VERSION_47) { + // TLS is only supported in versions 47 and greater. + return; + } + FLAGS_quic_supports_tls_handshake = true; + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(1), client_address, + QuicStringPiece(""), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(1), client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _)); + ProcessPacket( + client_address, TestConnectionId(1), true, + ParsedQuicVersion(PROTOCOL_TLS1_3, + CurrentSupportedVersions().front().transport_version), + SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); + EXPECT_EQ(client_address, dispatcher_->current_peer_address()); + EXPECT_EQ(server_address_, dispatcher_->current_self_address()); +} + +TEST_F(QuicDispatcherTest, ProcessPackets) { + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(1), client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(1), client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _)); + ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); + EXPECT_EQ(client_address, dispatcher_->current_peer_address()); + EXPECT_EQ(server_address_, dispatcher_->current_self_address()); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(2), client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(2), client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(2), packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(TestConnectionId(2), _)); + ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO()); + + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(1) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + ProcessPacket(client_address, TestConnectionId(1), false, "data"); +} + +// Regression test of b/93325907. +TEST_F(QuicDispatcherTest, DispatcherDoesNotRejectPacketNumberZero) { + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(1), client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(1), client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + // Verify both packets 1 and 2 are processed by connection 1. + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(2) + .WillRepeatedly( + WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _)); + ProcessPacket( + client_address, TestConnectionId(1), true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, + CurrentSupportedVersions().front().transport_version), + SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); + // Packet number 256 with packet number length 1 would be considered as 0 in + // dispatcher. + ProcessPacket( + client_address, TestConnectionId(1), false, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, + CurrentSupportedVersions().front().transport_version), + "", CONNECTION_ID_PRESENT, PACKET_1BYTE_PACKET_NUMBER, 256); + EXPECT_EQ(client_address, dispatcher_->current_peer_address()); + EXPECT_EQ(server_address_, dispatcher_->current_self_address()); +} + +TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) { + CreateTimeWaitListManager(); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, + SendVersionNegotiationPacket(_, _, _, _, _, _)) + .Times(1); + QuicTransportVersion version = + static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1); + ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version); + // Pad the CHLO message with enough data to make the packet large enough + // to trigger version negotiation. + QuicString chlo = SerializeCHLO() + QuicString(1200, 'a'); + DCHECK_LE(1200u, chlo.length()); + ProcessPacket(client_address, TestConnectionId(1), true, parsed_version, chlo, + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); +} + +TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) { + CreateTimeWaitListManager(); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, + SendVersionNegotiationPacket(_, _, _, _, _, _)) + .Times(0); + QuicTransportVersion version = + static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1); + ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version); + QuicString chlo = SerializeCHLO() + QuicString(1200, 'a'); + // Truncate to 1100 bytes of payload which results in a packet just + // under 1200 bytes after framing, packet, and encryption overhead. + DCHECK_LE(1200u, chlo.length()); + QuicString truncated_chlo = chlo.substr(0, 1100); + DCHECK_EQ(1100u, truncated_chlo.length()); + ProcessPacket(client_address, TestConnectionId(1), true, parsed_version, + truncated_chlo, CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); +} + +// Disabling CHLO size validation allows the dispatcher to send version +// negotiation packets in response to a CHLO that is otherwise too small. +TEST_F(QuicDispatcherTest, VersionNegotiationWithoutChloSizeValidation) { + crypto_config_.set_validate_chlo_size(false); + + CreateTimeWaitListManager(); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, + SendVersionNegotiationPacket(_, _, _, _, _, _)) + .Times(1); + QuicTransportVersion version = + static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1); + ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version); + QuicString chlo = SerializeCHLO() + QuicString(1200, 'a'); + // Truncate to 1100 bytes of payload which results in a packet just + // under 1200 bytes after framing, packet, and encryption overhead. + DCHECK_LE(1200u, chlo.length()); + QuicString truncated_chlo = chlo.substr(0, 1100); + DCHECK_EQ(1100u, truncated_chlo.length()); + ProcessPacket(client_address, TestConnectionId(1), true, parsed_version, + truncated_chlo, CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); +} + +TEST_F(QuicDispatcherTest, Shutdown) { + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(_, client_address, QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(1), client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _)); + ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); + + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + CloseConnection(QUIC_PEER_GOING_AWAY, _, _)); + + dispatcher_->Shutdown(); +} + +TEST_F(QuicDispatcherTest, TimeWaitListManager) { + CreateTimeWaitListManager(); + + // Create a new session. + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _)); + ProcessPacket(client_address, connection_id, true, SerializeCHLO()); + + // Now close the connection, which should add it to the time wait list. + session1_->connection()->CloseConnection( + QUIC_INVALID_VERSION, + "Server: Packet 2 without version flag before version negotiated.", + ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); + + // Dispatcher forwards subsequent packets for this connection_id to the time + // wait list manager. + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, _, connection_id, _, _)) + .Times(1); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(_, _, _, _, _)) + .Times(0); + ProcessPacket(client_address, connection_id, true, "data"); +} + +TEST_F(QuicDispatcherTest, NoVersionPacketToTimeWaitListManager) { + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + // Dispatcher forwards all packets for this connection_id to the time wait + // list manager. + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, _, connection_id, _, _)) + .Times(1); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(_, _, _, _, _)) + .Times(1); + ProcessPacket(client_address, connection_id, false, SerializeCHLO()); +} + +TEST_F(QuicDispatcherTest, ProcessPacketWithZeroPort) { + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 0); + + // dispatcher_ should drop this packet. + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(1), client_address, + QuicStringPiece("hq"), _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(_, _, _, _, _)) + .Times(0); + ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); +} + +TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) { + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(1), client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(1), client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + + // A packet whose packet number is the largest that is allowed to start a + // connection. + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, SerializeCHLO(), + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, + QuicDispatcher::kMaxReasonableInitialPacketNumber); + EXPECT_EQ(client_address, dispatcher_->current_peer_address()); + EXPECT_EQ(server_address_, dispatcher_->current_self_address()); +} + +TEST_F(QuicDispatcherTest, TooBigSeqNoPacketToTimeWaitListManager) { + CreateTimeWaitListManager(); + SetQuicRestartFlag(quic_enable_accept_random_ipn, false); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + + // Dispatcher forwards this packet for this connection_id to the time wait + // list manager. + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, _, TestConnectionId(1), _, _)) + .Times(1); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, _, TestConnectionId(2), _, _)) + .Times(1); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(_, _, _, _, _)) + .Times(2); + // A packet whose packet number is one to large to be allowed to start a + // connection. + ProcessPacket(client_address, connection_id, true, SerializeCHLO(), + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, + QuicDispatcher::kMaxReasonableInitialPacketNumber + 1); + connection_id = TestConnectionId(2); + SetQuicRestartFlag(quic_enable_accept_random_ipn, true); + ProcessPacket(client_address, connection_id, true, SerializeCHLO(), + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, + MaxRandomInitialPacketNumber().ToUint64() + + QuicDispatcher::kMaxReasonableInitialPacketNumber + 1); +} + +TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) { + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, + "Supported versions out of sync"); + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, true); + SetQuicReloadableFlag(quic_enable_version_44, true); + SetQuicReloadableFlag(quic_enable_version_46, true); + SetQuicReloadableFlag(quic_enable_version_47, true); + SetQuicReloadableFlag(quic_enable_version_99, true); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + uint64_t conn_id = 1; + QuicConnectionId connection_id = TestConnectionId(conn_id); + + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .Times(0); + ParsedQuicVersion version( + PROTOCOL_QUIC_CRYPTO, + static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1)); + ProcessPacket(client_address, connection_id, true, version, SerializeCHLO(), + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, + QuicVersionMin().transport_version), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, QuicVersionMax(), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn off version 47. + SetQuicReloadableFlag(quic_enable_version_47, false); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .Times(0); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn on version 47. + SetQuicReloadableFlag(quic_enable_version_47, true); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn off version 46. + SetQuicReloadableFlag(quic_enable_version_46, false); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .Times(0); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn on version 46. + SetQuicReloadableFlag(quic_enable_version_46, true); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn off version 44. + SetQuicReloadableFlag(quic_enable_version_44, false); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .Times(0); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn on version 44. + SetQuicReloadableFlag(quic_enable_version_44, true); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn off version 43. + SetQuicReloadableFlag(quic_enable_version_43, false); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .Times(0); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn on version 43. + SetQuicReloadableFlag(quic_enable_version_43, true); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn off version 39. + SetQuicReloadableFlag(quic_disable_version_39, true); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .Times(0); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Turn on version 39. + SetQuicReloadableFlag(quic_disable_version_39, false); + connection_id = TestConnectionId(++conn_id); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)); + ProcessPacket(client_address, connection_id, true, + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39), + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); +} + +// Enables mocking of the handshake-confirmation for stateless rejects. +class MockQuicCryptoServerStream : public QuicCryptoServerStream { + public: + MockQuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + QuicServerSessionBase* session, + QuicCryptoServerStream::Helper* helper) + : QuicCryptoServerStream( + &crypto_config, + compressed_certs_cache, + GetQuicReloadableFlag(enable_quic_stateless_reject_support), + session, + helper), + handshake_confirmed_(false) {} + MockQuicCryptoServerStream(const MockQuicCryptoServerStream&) = delete; + MockQuicCryptoServerStream& operator=(const MockQuicCryptoServerStream&) = + delete; + + void set_handshake_confirmed_for_testing(bool handshake_confirmed) { + handshake_confirmed_ = handshake_confirmed; + } + + bool handshake_confirmed() const override { return handshake_confirmed_; } + + private: + bool handshake_confirmed_; +}; + +struct StatelessRejectTestParams { + StatelessRejectTestParams(bool enable_stateless_rejects_via_flag, + bool client_supports_statelesss_rejects, + bool crypto_handshake_successful) + : enable_stateless_rejects_via_flag(enable_stateless_rejects_via_flag), + client_supports_statelesss_rejects(client_supports_statelesss_rejects), + crypto_handshake_successful(crypto_handshake_successful) {} + + friend std::ostream& operator<<(std::ostream& os, + const StatelessRejectTestParams& p) { + os << "{ enable_stateless_rejects_via_flag: " + << p.enable_stateless_rejects_via_flag << std::endl; + os << " client_supports_statelesss_rejects: " + << p.client_supports_statelesss_rejects << std::endl; + os << " crypto_handshake_successful: " << p.crypto_handshake_successful + << " }"; + return os; + } + + // This only enables the stateless reject feature via the feature-flag. + // This should be a no-op if the peer does not support them. + bool enable_stateless_rejects_via_flag; + // Whether or not the client supports stateless rejects. + bool client_supports_statelesss_rejects; + // Should the initial crypto handshake succeed or not. + bool crypto_handshake_successful; +}; + +// Constructs various test permutations for stateless rejects. +std::vector<StatelessRejectTestParams> GetStatelessRejectTestParams() { + std::vector<StatelessRejectTestParams> params; + for (bool enable_stateless_rejects_via_flag : {true, false}) { + for (bool client_supports_statelesss_rejects : {true, false}) { + for (bool crypto_handshake_successful : {true, false}) { + params.push_back(StatelessRejectTestParams( + enable_stateless_rejects_via_flag, + client_supports_statelesss_rejects, crypto_handshake_successful)); + } + } + } + return params; +} + +class QuicDispatcherStatelessRejectTest + : public QuicDispatcherTest, + public testing::WithParamInterface<StatelessRejectTestParams> { + public: + QuicDispatcherStatelessRejectTest() + : QuicDispatcherTest(), crypto_stream1_(nullptr) {} + + ~QuicDispatcherStatelessRejectTest() override { + if (crypto_stream1_) { + delete crypto_stream1_; + } + } + + // This test setup assumes that all testing will be done using + // crypto_stream1_. + void SetUp() override { + QuicDispatcherTest::SetUp(); + SetQuicReloadableFlag(enable_quic_stateless_reject_support, + GetParam().enable_stateless_rejects_via_flag); + } + + // Returns true or false, depending on whether the server will emit + // a stateless reject, depending upon the parameters of the test. + bool ExpectStatelessReject() { + return GetParam().enable_stateless_rejects_via_flag && + !GetParam().crypto_handshake_successful && + GetParam().client_supports_statelesss_rejects; + } + + // Sets up dispatcher_, session1_, and crypto_stream1_ based on + // the test parameters. + QuicServerSessionBase* CreateSessionBasedOnTestParams( + QuicConnectionId connection_id, + const QuicSocketAddress& client_address) { + CreateSession(dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_); + + crypto_stream1_ = new MockQuicCryptoServerStream( + crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + session1_, session1_->stream_helper()); + session1_->SetCryptoStream(crypto_stream1_); + crypto_stream1_->set_handshake_confirmed_for_testing( + GetParam().crypto_handshake_successful); + crypto_stream1_->SetPeerSupportsStatelessRejects( + GetParam().client_supports_statelesss_rejects); + return session1_; + } + + MockQuicCryptoServerStream* crypto_stream1_; +}; + +// Parameterized test for stateless rejects. Should test all +// combinations of enabling/disabling, reject/no-reject for stateless +// rejects. +INSTANTIATE_TEST_SUITE_P(QuicDispatcherStatelessRejectTests, + QuicDispatcherStatelessRejectTest, + ::testing::ValuesIn(GetStatelessRejectTestParams())); + +TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) { + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("hq"), _)) + .WillOnce(testing::Return( + CreateSessionBasedOnTestParams(connection_id, client_address))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)) + .Times(1); + + // Process the first packet for the connection. + ProcessPacket(client_address, connection_id, true, SerializeCHLO()); + if (ExpectStatelessReject()) { + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _)); + // If this is a stateless reject, the crypto stream will close the + // connection. + session1_->connection()->CloseConnection( + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject", + ConnectionCloseBehavior::SILENT_CLOSE); + } + + // Send a second packet and check the results. If this is a stateless reject, + // the existing connection_id will go on the time-wait list. + EXPECT_EQ(ExpectStatelessReject(), + time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); + if (ExpectStatelessReject()) { + // The second packet will be processed on the time-wait list. + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, _, connection_id, _, _)) + .Times(1); + } else { + // The second packet will trigger a packet-validation + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(1) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + } + ProcessPacket(client_address, connection_id, true, "data"); +} + +TEST_P(QuicDispatcherStatelessRejectTest, CheapRejects) { + SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true); + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + if (GetParam().enable_stateless_rejects_via_flag) { + EXPECT_CALL(*dispatcher_, + CreateQuicSession(connection_id, client_address, _, _)) + .Times(0); + } else { + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("h2"), _)) + .WillOnce(testing::Return( + CreateSessionBasedOnTestParams(connection_id, client_address))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + } + + QUIC_LOG(INFO) << "ExpectStatelessReject: " << ExpectStatelessReject(); + QUIC_LOG(INFO) << "Params: " << GetParam(); + // Process the first packet for the connection. + CryptoHandshakeMessage client_hello = + crypto_test_utils::CreateCHLO({{"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"COPT", "SREJ"}, + {"NONC", "1234567890123456789012"}, + {"ALPN", "h2"}, + {"VER\0", "Q025"}}, + kClientHelloMinimumSize); + + if (GetParam().enable_stateless_rejects_via_flag) { + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, _, connection_id, _, _)) + .Times(1); + } else { + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)) + .Times(1); + } + ProcessPacket(client_address, connection_id, true, + QuicString(client_hello.GetSerialized().AsStringPiece())); + + if (GetParam().enable_stateless_rejects_via_flag) { + EXPECT_EQ(true, + time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); + } +} + +TEST_P(QuicDispatcherStatelessRejectTest, BufferNonChlo) { + SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true); + CreateTimeWaitListManager(); + + const QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + const QuicConnectionId connection_id = TestConnectionId(1); + + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(connection_id, _)) + .Times(1); + ProcessPacket(client_address, connection_id, true, "NOT DATA FOR A CHLO"); + + // Process the first packet for the connection. + CryptoHandshakeMessage client_hello = + crypto_test_utils::CreateCHLO({{"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"NONC", "1234567890123456789012"}, + {"ALPN", "h3"}, + {"VER\0", "Q025"}}, + kClientHelloMinimumSize); + + // If stateless rejects are enabled then a connection will be created now + // and the buffered packet will be processed + EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address, + QuicStringPiece("h3"), _)) + .WillOnce(testing::Return( + CreateSessionBasedOnTestParams(connection_id, client_address))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))); + // Expect both packets to be passed to ProcessUdpPacket(). And one of them + // is already expected in CreateSessionBasedOnTestParams(). + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)) + .WillOnce(WithArg<2>( + Invoke([this, connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(connection_id, packet); + }))) + .RetiresOnSaturation(); + ProcessPacket(client_address, connection_id, true, + QuicString(client_hello.GetSerialized().AsStringPiece())); + EXPECT_FALSE( + time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); +} + +// Verify the stopgap test: Packets with truncated connection IDs should be +// dropped. +class QuicDispatcherTestStrayPacketConnectionId : public QuicDispatcherTest {}; + +// Packets with truncated connection IDs should be dropped. +TEST_F(QuicDispatcherTestStrayPacketConnectionId, + StrayPacketTruncatedConnectionId) { + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _)) + .Times(0); + if (CurrentSupportedVersions()[0].transport_version > QUIC_VERSION_43) { + // This IETF packet has invalid connection ID length. + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(_, _, _, _, _)) + .Times(0); + } else { + // This GQUIC packet is considered as IETF QUIC packet with short header + // with unacceptable packet number. + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)) + .Times(1); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(_, _, _, _, _)) + .Times(1); + } + ProcessPacket(client_address, connection_id, true, "data", + CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER); +} + +class BlockingWriter : public QuicPacketWriterWrapper { + public: + BlockingWriter() : write_blocked_(false) {} + + bool IsWriteBlocked() const override { return write_blocked_; } + void SetWritable() override { write_blocked_ = false; } + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_client_address, + const QuicSocketAddress& peer_client_address, + PerPacketOptions* options) override { + // It would be quite possible to actually implement this method here with + // the fake blocked status, but it would be significantly more work in + // Chromium, and since it's not called anyway, don't bother. + QUIC_LOG(DFATAL) << "Not supported"; + return WriteResult(); + } + + bool write_blocked_; +}; + +class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest { + public: + void SetUp() override { + QuicDispatcherTest::SetUp(); + writer_ = new BlockingWriter; + QuicDispatcherPeer::UseWriter(dispatcher_.get(), writer_); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(_, client_address, QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(1), client_address, + &helper_, &alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(1), packet); + }))); + EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection( + TestConnectionId(1), _)); + ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); + + EXPECT_CALL(*dispatcher_, + CreateQuicSession(_, client_address, QuicStringPiece("hq"), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(2), client_address, + &helper_, &alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(2), packet); + }))); + EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection( + TestConnectionId(2), _)); + ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO()); + + blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(dispatcher_.get()); + } + + void TearDown() override { + if (connection1() != nullptr) { + EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _)); + } + + if (connection2() != nullptr) { + EXPECT_CALL(*connection2(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _)); + } + dispatcher_->Shutdown(); + } + + // Set the dispatcher's writer to be blocked. By default, all connections use + // the same writer as the dispatcher in this test. + void SetBlocked() { + QUIC_LOG(INFO) << "set writer " << writer_ << " to blocked"; + writer_->write_blocked_ = true; + } + + // Simulate what happens when connection1 gets blocked when writing. + void BlockConnection1() { + Connection1Writer()->write_blocked_ = true; + dispatcher_->OnWriteBlocked(connection1()); + } + + BlockingWriter* Connection1Writer() { + return static_cast<BlockingWriter*>(connection1()->writer()); + } + + // Simulate what happens when connection2 gets blocked when writing. + void BlockConnection2() { + Connection2Writer()->write_blocked_ = true; + dispatcher_->OnWriteBlocked(connection2()); + } + + BlockingWriter* Connection2Writer() { + return static_cast<BlockingWriter*>(connection2()->writer()); + } + + protected: + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + BlockingWriter* writer_; + QuicDispatcher::WriteBlockedList* blocked_list_; +}; + +TEST_F(QuicDispatcherWriteBlockedListTest, BasicOnCanWrite) { + // No OnCanWrite calls because no connections are blocked. + dispatcher_->OnCanWrite(); + + // Register connection 1 for events, and make sure it's notified. + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + EXPECT_CALL(*connection1(), OnCanWrite()); + dispatcher_->OnCanWrite(); + + // It should get only one notification. + EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteOrder) { + // Make sure we handle events in order. + InSequence s; + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); + EXPECT_CALL(*connection1(), OnCanWrite()); + EXPECT_CALL(*connection2(), OnCanWrite()); + dispatcher_->OnCanWrite(); + + // Check the other ordering. + SetBlocked(); + dispatcher_->OnWriteBlocked(connection2()); + dispatcher_->OnWriteBlocked(connection1()); + EXPECT_CALL(*connection2(), OnCanWrite()); + EXPECT_CALL(*connection1(), OnCanWrite()); + dispatcher_->OnCanWrite(); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteRemove) { + // Add and remove one connction. + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + blocked_list_->erase(connection1()); + EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); + dispatcher_->OnCanWrite(); + + // Add and remove one connction and make sure it doesn't affect others. + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); + blocked_list_->erase(connection1()); + EXPECT_CALL(*connection2(), OnCanWrite()); + dispatcher_->OnCanWrite(); + + // Add it, remove it, and add it back and make sure things are OK. + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + blocked_list_->erase(connection1()); + dispatcher_->OnWriteBlocked(connection1()); + EXPECT_CALL(*connection1(), OnCanWrite()).Times(1); + dispatcher_->OnCanWrite(); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, DoubleAdd) { + // Make sure a double add does not necessitate a double remove. + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); + blocked_list_->erase(connection1()); + EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); + dispatcher_->OnCanWrite(); + + // Make sure a double add does not result in two OnCanWrite calls. + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection1()); + EXPECT_CALL(*connection1(), OnCanWrite()).Times(1); + dispatcher_->OnCanWrite(); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlockConnection1) { + // If the 1st blocked writer gets blocked in OnCanWrite, it will be added back + // into the write blocked list. + InSequence s; + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); + EXPECT_CALL(*connection1(), OnCanWrite()) + .WillOnce( + Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection1)); + EXPECT_CALL(*connection2(), OnCanWrite()); + dispatcher_->OnCanWrite(); + + // connection1 should be still in the write blocked list. + EXPECT_TRUE(dispatcher_->HasPendingWrites()); + + // Now call OnCanWrite again, connection1 should get its second chance. + EXPECT_CALL(*connection1(), OnCanWrite()); + EXPECT_CALL(*connection2(), OnCanWrite()).Times(0); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlockConnection2) { + // If the 2nd blocked writer gets blocked in OnCanWrite, it will be added back + // into the write blocked list. + InSequence s; + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); + EXPECT_CALL(*connection1(), OnCanWrite()); + EXPECT_CALL(*connection2(), OnCanWrite()) + .WillOnce( + Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2)); + dispatcher_->OnCanWrite(); + + // connection2 should be still in the write blocked list. + EXPECT_TRUE(dispatcher_->HasPendingWrites()); + + // Now call OnCanWrite again, connection2 should get its second chance. + EXPECT_CALL(*connection1(), OnCanWrite()).Times(0); + EXPECT_CALL(*connection2(), OnCanWrite()); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, + OnCanWriteHandleBlockBothConnections) { + // Both connections get blocked in OnCanWrite, and added back into the write + // blocked list. + InSequence s; + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + dispatcher_->OnWriteBlocked(connection2()); + EXPECT_CALL(*connection1(), OnCanWrite()) + .WillOnce( + Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection1)); + EXPECT_CALL(*connection2(), OnCanWrite()) + .WillOnce( + Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2)); + dispatcher_->OnCanWrite(); + + // Both connections should be still in the write blocked list. + EXPECT_TRUE(dispatcher_->HasPendingWrites()); + + // Now call OnCanWrite again, both connections should get its second chance. + EXPECT_CALL(*connection1(), OnCanWrite()); + EXPECT_CALL(*connection2(), OnCanWrite()); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, PerConnectionWriterBlocked) { + // By default, all connections share the same packet writer with the + // dispatcher. + EXPECT_EQ(dispatcher_->writer(), connection1()->writer()); + EXPECT_EQ(dispatcher_->writer(), connection2()->writer()); + + // Test the case where connection1 shares the same packet writer as the + // dispatcher, whereas connection2 owns it's packet writer. + // Change connection2's writer. + connection2()->SetQuicPacketWriter(new BlockingWriter, /*owns_writer=*/true); + EXPECT_NE(dispatcher_->writer(), connection2()->writer()); + + BlockConnection2(); + EXPECT_TRUE(dispatcher_->HasPendingWrites()); + + EXPECT_CALL(*connection2(), OnCanWrite()); + dispatcher_->OnCanWrite(); + EXPECT_FALSE(dispatcher_->HasPendingWrites()); +} + +TEST_F(QuicDispatcherWriteBlockedListTest, + RemoveConnectionFromWriteBlockedListWhenDeletingSessions) { + if (!GetQuicReloadableFlag( + quic_connection_do_not_add_to_write_blocked_list_if_disconnected)) { + return; + } + + dispatcher_->OnConnectionClosed(connection1()->connection_id(), + QUIC_PACKET_WRITE_ERROR, "Closed by test.", + ConnectionCloseSource::FROM_SELF); + + SetBlocked(); + + ASSERT_FALSE(dispatcher_->HasPendingWrites()); + SetBlocked(); + dispatcher_->OnWriteBlocked(connection1()); + ASSERT_TRUE(dispatcher_->HasPendingWrites()); + + EXPECT_QUIC_BUG(dispatcher_->DeleteSessions(), + "QuicConnection was in WriteBlockedList before destruction"); + MarkSession1Deleted(); +} + +// Tests that bufferring packets works in stateful reject, expensive stateless +// reject and cheap stateless reject. +struct BufferedPacketStoreTestParams { + BufferedPacketStoreTestParams(bool enable_stateless_rejects_via_flag, + bool support_cheap_stateless_reject) + : enable_stateless_rejects_via_flag(enable_stateless_rejects_via_flag), + support_cheap_stateless_reject(support_cheap_stateless_reject) {} + + friend std::ostream& operator<<(std::ostream& os, + const BufferedPacketStoreTestParams& p) { + os << "{ enable_stateless_rejects_via_flag: " + << p.enable_stateless_rejects_via_flag << std::endl; + os << " support_cheap_stateless_reject: " + << p.support_cheap_stateless_reject << " }"; + return os; + } + + // This only enables the stateless reject feature via the feature-flag. + // This should be a no-op if the peer does not support them. + bool enable_stateless_rejects_via_flag; + // Whether to do cheap stateless or not. + bool support_cheap_stateless_reject; +}; + +std::vector<BufferedPacketStoreTestParams> GetBufferedPacketStoreTestParams() { + std::vector<BufferedPacketStoreTestParams> params; + for (bool enable_stateless_rejects_via_flag : {true, false}) { + for (bool support_cheap_stateless_reject : {true, false}) { + params.push_back(BufferedPacketStoreTestParams( + enable_stateless_rejects_via_flag, support_cheap_stateless_reject)); + } + } + return params; +} + +// A dispatcher whose stateless rejector will always ACCEPTs CHLO. +class BufferedPacketStoreTest + : public QuicDispatcherTest, + public testing::WithParamInterface<BufferedPacketStoreTestParams> { + public: + BufferedPacketStoreTest() + : QuicDispatcherTest(), + server_addr_(QuicSocketAddress(QuicIpAddress::Any4(), 5)), + client_addr_(QuicIpAddress::Loopback4(), 1234), + signed_config_(new QuicSignedServerConfig) { + SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, + GetParam().support_cheap_stateless_reject); + SetQuicReloadableFlag(enable_quic_stateless_reject_support, + GetParam().enable_stateless_rejects_via_flag); + } + + void SetUp() override { + QuicDispatcherTest::SetUp(); + clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock(); + + QuicTransportVersion version = AllSupportedTransportVersions().front(); + CryptoHandshakeMessage chlo = + crypto_test_utils::GenerateDefaultInchoateCHLO(clock_, version, + &crypto_config_); + chlo.SetVector(kCOPT, QuicTagVector{kSREJ}); + // Pass an inchoate CHLO. + crypto_test_utils::GenerateFullCHLO( + chlo, &crypto_config_, server_addr_, client_addr_, version, clock_, + signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &full_chlo_); + } + + QuicString SerializeFullCHLO() { + return QuicString(full_chlo_.GetSerialized().AsStringPiece()); + } + + protected: + QuicSocketAddress server_addr_; + QuicSocketAddress client_addr_; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + const QuicClock* clock_; + CryptoHandshakeMessage full_chlo_; +}; + +INSTANTIATE_TEST_SUITE_P( + BufferedPacketStoreTests, + BufferedPacketStoreTest, + ::testing::ValuesIn(GetBufferedPacketStoreTestParams())); + +TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) { + InSequence s; + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId conn_id = TestConnectionId(1); + // A bunch of non-CHLO should be buffered upon arrival, and the first one + // should trigger ShouldCreateOrBufferPacketForConnection(). + EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(conn_id, _)) + .Times(1); + for (size_t i = 1; i <= kDefaultMaxUndecryptablePackets + 1; ++i) { + ProcessPacket(client_address, conn_id, true, + QuicStrCat("data packet ", i + 1), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, /*packet_number=*/i + 1); + } + EXPECT_EQ(0u, dispatcher_->session_map().size()) + << "No session should be created before CHLO arrives."; + + // Pop out the last packet as it is also be dropped by the store. + data_connection_map_[conn_id].pop_back(); + // When CHLO arrives, a new session should be created, and all packets + // buffered should be delivered to the session. + EXPECT_CALL(*dispatcher_, + CreateQuicSession(conn_id, client_address, QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + + // Only |kDefaultMaxUndecryptablePackets| packets were buffered, and they + // should be delivered in arrival order. + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(kDefaultMaxUndecryptablePackets + 1) // + 1 for CHLO. + .WillRepeatedly( + WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id, packet); + }))); + ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); +} + +TEST_P(BufferedPacketStoreTest, + ProcessNonChloPacketsForDifferentConnectionsUptoLimit) { + InSequence s; + // A bunch of non-CHLO should be buffered upon arrival. + size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1; + for (size_t i = 1; i <= kNumConnections; ++i) { + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i); + QuicConnectionId conn_id = TestConnectionId(i); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id, _)); + ProcessPacket(client_address, conn_id, true, + QuicStrCat("data packet on connection ", i), + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, + /*packet_number=*/2); + } + + // Pop out the packet on last connection as it shouldn't be enqueued in store + // as well. + data_connection_map_[TestConnectionId(kNumConnections)].pop_front(); + + // Reset session creation counter to ensure processing CHLO can always + // create session. + QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(dispatcher_.get(), + kNumConnections); + // Process CHLOs to create session for these connections. + for (size_t i = 1; i <= kNumConnections; ++i) { + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i); + QuicConnectionId conn_id = TestConnectionId(i); + if (i == kNumConnections) { + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id, _)); + } + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + // First |kNumConnections| - 1 connections should have buffered + // a packet in store. The rest should have been dropped. + size_t num_packet_to_process = i <= kMaxConnectionsWithoutCHLO ? 2u : 1u; + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)) + .Times(num_packet_to_process) + .WillRepeatedly(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id, packet); + }))); + + ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); + } +} + +// Tests that store delivers empty packet list if CHLO arrives firstly. +TEST_P(BufferedPacketStoreTest, DeliverEmptyPackets) { + QuicConnectionId conn_id = TestConnectionId(1); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id, _)); + EXPECT_CALL(*dispatcher_, + CreateQuicSession(conn_id, client_address, QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, client_address, _)); + ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); +} + +// Tests that a retransmitted CHLO arrives after a connection for the +// CHLO has been created. +TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) { + InSequence s; + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId conn_id = TestConnectionId(1); + ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2), + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, + /*packet_number=*/2); + + // When CHLO arrives, a new session should be created, and all packets + // buffered should be delivered to the session. + EXPECT_CALL(*dispatcher_, + CreateQuicSession(conn_id, client_address, QuicStringPiece(), _)) + .Times(1) // Only triggered by 1st CHLO. + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, + &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(3) // Triggered by 1 data packet and 2 CHLOs. + .WillRepeatedly( + WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id, packet); + }))); + ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); + + ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); +} + +// Tests that expiration of a connection add connection id to time wait list. +TEST_P(BufferedPacketStoreTest, ReceiveCHLOAfterExpiration) { + InSequence s; + CreateTimeWaitListManager(); + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock()); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId conn_id = TestConnectionId(1); + ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2), + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, + /*packet_number=*/2); + + mock_helper_.AdvanceTime( + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)); + QuicAlarm* alarm = QuicBufferedPacketStorePeer::expiration_alarm(store); + // Cancel alarm as if it had been fired. + alarm->Cancel(); + store->OnExpirationTimeout(); + // New arrived CHLO will be dropped because this connection is in time wait + // list. + ASSERT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _)); + ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); +} + +TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) { + // Process more than (|kMaxNumSessionsToCreate| + + // |kDefaultMaxConnectionsInStore|) CHLOs, + // the first |kMaxNumSessionsToCreate| should create connections immediately, + // the next |kDefaultMaxConnectionsInStore| should be buffered, + // the rest should be dropped. + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + const size_t kNumCHLOs = + kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore + 1; + for (uint64_t conn_id = 1; conn_id <= kNumCHLOs; ++conn_id) { + EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection( + TestConnectionId(conn_id), _)); + if (conn_id <= kMaxNumSessionsToCreate) { + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), client_addr_, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(conn_id), + client_addr_, &mock_helper_, &mock_alarm_factory_, + &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(conn_id), packet); + }))); + } + ProcessPacket(client_addr_, TestConnectionId(conn_id), true, + SerializeFullCHLO()); + if (conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore && + conn_id > kMaxNumSessionsToCreate) { + EXPECT_TRUE(store->HasChloForConnection(TestConnectionId(conn_id))); + } else { + // First |kMaxNumSessionsToCreate| CHLOs should be passed to new + // connections immediately, and the last CHLO should be dropped as the + // store is full. + EXPECT_FALSE(store->HasChloForConnection(TestConnectionId(conn_id))); + } + } + + // Graduately consume buffered CHLOs. The buffered connections should be + // created but the dropped one shouldn't. + for (uint64_t conn_id = kMaxNumSessionsToCreate + 1; + conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore; + ++conn_id) { + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), client_addr_, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(conn_id), packet); + }))); + } + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(kNumCHLOs), client_addr_, + QuicStringPiece(), _)) + .Times(0); + + while (store->HasChlosBuffered()) { + dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); + } + + EXPECT_EQ(TestConnectionId(static_cast<size_t>(kMaxNumSessionsToCreate) + + kDefaultMaxConnectionsInStore), + session1_->connection_id()); +} + +// Duplicated CHLO shouldn't be buffered. +TEST_P(BufferedPacketStoreTest, BufferDuplicatedCHLO) { + for (uint64_t conn_id = 1; conn_id <= kMaxNumSessionsToCreate + 1; + ++conn_id) { + // Last CHLO will be buffered. Others will create connection right away. + if (conn_id <= kMaxNumSessionsToCreate) { + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), client_addr_, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(conn_id), + client_addr_, &mock_helper_, &mock_alarm_factory_, + &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(conn_id), packet); + }))); + } + ProcessPacket(client_addr_, TestConnectionId(conn_id), true, + SerializeFullCHLO()); + } + // Retransmit CHLO on last connection should be dropped. + QuicConnectionId last_connection = + TestConnectionId(kMaxNumSessionsToCreate + 1); + ProcessPacket(client_addr_, last_connection, true, SerializeFullCHLO()); + + size_t packets_buffered = 2; + + // Reset counter and process buffered CHLO. + EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, client_addr_, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, last_connection, client_addr_, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + // Only one packet(CHLO) should be process. + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(packets_buffered) + .WillRepeatedly(WithArg<2>( + Invoke([this, last_connection](const QuicEncryptedPacket& packet) { + ValidatePacket(last_connection, packet); + }))); + dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); +} + +TEST_P(BufferedPacketStoreTest, BufferNonChloPacketsUptoLimitWithChloBuffered) { + uint64_t last_conn_id = kMaxNumSessionsToCreate + 1; + QuicConnectionId last_connection_id = TestConnectionId(last_conn_id); + for (uint64_t conn_id = 1; conn_id <= last_conn_id; ++conn_id) { + // Last CHLO will be buffered. Others will create connection right away. + if (conn_id <= kMaxNumSessionsToCreate) { + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), client_addr_, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(conn_id), + client_addr_, &mock_helper_, &mock_alarm_factory_, + &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillRepeatedly(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(conn_id), packet); + }))); + } + ProcessPacket(client_addr_, TestConnectionId(conn_id), true, + SerializeFullCHLO()); + } + + // Process another |kDefaultMaxUndecryptablePackets| + 1 data packets. The + // last one should be dropped. + for (uint64_t packet_number = 2; + packet_number <= kDefaultMaxUndecryptablePackets + 2; ++packet_number) { + ProcessPacket(client_addr_, last_connection_id, true, "data packet"); + } + + // Reset counter and process buffered CHLO. + EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection_id, client_addr_, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, last_connection_id, client_addr_, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + // Only CHLO and following |kDefaultMaxUndecryptablePackets| data packets + // should be process. + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(kDefaultMaxUndecryptablePackets + 1) + .WillRepeatedly(WithArg<2>( + Invoke([this, last_connection_id](const QuicEncryptedPacket& packet) { + ValidatePacket(last_connection_id, packet); + }))); + dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); +} + +// Tests that when dispatcher's packet buffer is full, a CHLO on connection +// which doesn't have buffered CHLO should be buffered. +TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + + uint64_t conn_id = 1; + ProcessPacket(client_addr_, TestConnectionId(conn_id), true, "data packet", + CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, + /*packet_number=*/1); + // Fill packet buffer to full with CHLOs on other connections. Need to feed + // extra CHLOs because the first |kMaxNumSessionsToCreate| are going to create + // session directly. + for (conn_id = 2; + conn_id <= kDefaultMaxConnectionsInStore + kMaxNumSessionsToCreate; + ++conn_id) { + if (conn_id <= kMaxNumSessionsToCreate + 1) { + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), client_addr_, + QuicStringPiece(), _)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(conn_id), + client_addr_, &mock_helper_, &mock_alarm_factory_, + &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(conn_id), packet); + }))); + } + ProcessPacket(client_addr_, TestConnectionId(conn_id), true, + SerializeFullCHLO()); + } + EXPECT_FALSE(store->HasChloForConnection( + /*connection_id=*/TestConnectionId(1))); + + // CHLO on connection 1 should still be buffered. + ProcessPacket(client_addr_, /*connection_id=*/TestConnectionId(1), true, + SerializeFullCHLO()); + EXPECT_TRUE(store->HasChloForConnection( + /*connection_id=*/TestConnectionId(1))); +} + +// Regression test for b/117874922. +TEST_P(BufferedPacketStoreTest, ProcessBufferedChloWithDifferentVersion) { + // Turn off version 99, such that the preferred version is not supported by + // the server. + SetQuicReloadableFlag(quic_enable_version_99, false); + uint64_t last_connection_id = kMaxNumSessionsToCreate + 5; + ParsedQuicVersionVector supported_versions = CurrentSupportedVersions(); + for (uint64_t conn_id = 1; conn_id <= last_connection_id; ++conn_id) { + // Last 5 CHLOs will be buffered. Others will create connection right away. + ParsedQuicVersion version = + supported_versions[(conn_id - 1) % supported_versions.size()]; + if (conn_id <= kMaxNumSessionsToCreate) { + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), client_addr_, + QuicStringPiece(), version)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(conn_id), + client_addr_, &mock_helper_, &mock_alarm_factory_, + &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &session1_))); + EXPECT_CALL( + *reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillRepeatedly(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(conn_id), packet); + }))); + } + ProcessPacket(client_addr_, TestConnectionId(conn_id), true, version, + SerializeFullCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + } + + // Process buffered CHLOs. Verify the version is correct. + for (uint64_t conn_id = kMaxNumSessionsToCreate + 1; + conn_id <= last_connection_id; ++conn_id) { + ParsedQuicVersion version = + supported_versions[(conn_id - 1) % supported_versions.size()]; + EXPECT_CALL(*dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), client_addr_, + QuicStringPiece(), version)) + .WillOnce(testing::Return(CreateSession( + dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .WillRepeatedly(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(TestConnectionId(conn_id), packet); + }))); + } + dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); +} + +// Test which exercises the async GetProof codepaths, especially in the context +// of stateless rejection. +class AsyncGetProofTest : public QuicDispatcherTest { + public: + AsyncGetProofTest() + : QuicDispatcherTest( + std::unique_ptr<FakeProofSource>(new FakeProofSource())), + client_addr_(QuicIpAddress::Loopback4(), 1234), + client_addr_2_(QuicIpAddress::Loopback4(), 1357), + crypto_config_peer_(&crypto_config_), + server_addr_(QuicIpAddress::Any4(), 5), + signed_config_(new QuicSignedServerConfig) { + SetQuicReloadableFlag(enable_quic_stateless_reject_support, true); + SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true); + } + + void SetUp() override { + QuicDispatcherTest::SetUp(); + + clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock(); + QuicTransportVersion version = AllSupportedTransportVersions().front(); + chlo_ = crypto_test_utils::GenerateDefaultInchoateCHLO(clock_, version, + &crypto_config_); + chlo_.SetVector(kCOPT, QuicTagVector{kSREJ}); + chlo_.SetStringPiece(kALPN, "HTTP/1"); + // Pass an inchoate CHLO. + crypto_test_utils::GenerateFullCHLO( + chlo_, &crypto_config_, server_addr_, client_addr_, version, clock_, + signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &full_chlo_); + + crypto_test_utils::GenerateFullCHLO( + chlo_, &crypto_config_, server_addr_, client_addr_2_, version, clock_, + signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + &full_chlo_2_); + + GetFakeProofSource()->Activate(); + } + + FakeProofSource* GetFakeProofSource() const { + return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource()); + } + + QuicString SerializeFullCHLO() { + return QuicString(full_chlo_.GetSerialized().AsStringPiece()); + } + + QuicString SerializeFullCHLOForClient2() { + return QuicString(full_chlo_2_.GetSerialized().AsStringPiece()); + } + + QuicString SerializeCHLO() { + return QuicString(chlo_.GetSerialized().AsStringPiece()); + } + + // Sets up a session, and crypto stream based on the test parameters. + QuicServerSessionBase* GetSession(QuicConnectionId connection_id, + QuicSocketAddress client_address) { + auto it = sessions_.find(connection_id); + if (it != sessions_.end()) { + return it->second.session; + } + + TestQuicSpdyServerSession* session; + CreateSession(dispatcher_.get(), config_, connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session); + + std::unique_ptr<MockQuicCryptoServerStream> crypto_stream( + new MockQuicCryptoServerStream( + crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), + session, session->stream_helper())); + session->SetCryptoStream(crypto_stream.get()); + crypto_stream->SetPeerSupportsStatelessRejects(true); + const bool ok = + sessions_ + .insert(std::make_pair( + connection_id, SessionInfo{session, std::move(crypto_stream)})) + .second; + CHECK(ok); + return session; + } + + protected: + const QuicSocketAddress client_addr_; + const QuicSocketAddress client_addr_2_; + CryptoHandshakeMessage chlo_; + + private: + QuicCryptoServerConfigPeer crypto_config_peer_; + QuicSocketAddress server_addr_; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + const QuicClock* clock_; + CryptoHandshakeMessage full_chlo_; // CHLO for client_addr_ + CryptoHandshakeMessage full_chlo_2_; // CHLO for client_addr_2_ + + struct SessionInfo { + TestQuicSpdyServerSession* session; + std::unique_ptr<MockQuicCryptoServerStream> crypto_stream; + }; + std::map<QuicConnectionId, SessionInfo> sessions_; +}; + +// Test a simple situation of connections which the StatelessRejector will +// accept. +TEST_F(AsyncGetProofTest, BasicAccept) { + QuicConnectionId conn_id = TestConnectionId(1); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id, _)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_, + QuicStringPiece("HTTP/1"), _)) + .WillOnce(testing::Return(GetSession(conn_id, client_addr_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id, client_addr_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id, packet); + }))); + + EXPECT_CALL(check, Call(2)); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id, client_addr_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id, packet); + }))); + } + + // Send a CHLO that the StatelessRejector will accept. + ProcessPacket(client_addr_, conn_id, true, SerializeFullCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + check.Call(1); + // Complete the ProofSource::GetProof call and verify that a session is + // created. + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); + + check.Call(2); + // Verify that a data packet gets processed immediately. + ProcessPacket(client_addr_, conn_id, true, "My name is Data"); +} + +TEST_F(AsyncGetProofTest, RestorePacketContext) { + QuicConnectionId conn_id_1 = TestConnectionId(1); + QuicConnectionId conn_id_2 = TestConnectionId(2); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id_1, _)); + + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_, + QuicStringPiece("HTTP/1"), _)) + .WillOnce(testing::Return(GetSession(conn_id_1, client_addr_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id_1, client_addr_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillRepeatedly(WithArg<2>( + Invoke([this, conn_id_1](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id_1, packet); + }))); + + EXPECT_CALL(check, Call(2)); + + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id_2, _)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_2_, + QuicStringPiece("HTTP/1"), _)) + .WillOnce(testing::Return(GetSession(conn_id_2, client_addr_2_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id_2, client_addr_2_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id_2](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id_2, packet); + }))); + } + + // Send a CHLO that the StatelessRejector will accept. + dispatcher_->custom_packet_context_ = "connection 1"; + ProcessPacket(client_addr_, conn_id_1, true, SerializeFullCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Send another CHLO that the StatelessRejector will accept. + dispatcher_->custom_packet_context_ = "connection 2"; + ProcessPacket(client_addr_2_, conn_id_2, true, SerializeFullCHLOForClient2()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2); + + // Complete the first ProofSource::GetProof call and verify that a session is + // created. + check.Call(1); + + EXPECT_EQ(client_addr_2_, dispatcher_->current_client_address()); + EXPECT_EQ(client_addr_2_, dispatcher_->current_peer_address()); + EXPECT_EQ("connection 2", dispatcher_->custom_packet_context_); + + // Runs the async proof callback for conn_id_1 from client_addr_. + GetFakeProofSource()->InvokePendingCallback(0); + + EXPECT_EQ(client_addr_, dispatcher_->current_client_address()); + EXPECT_EQ(client_addr_, dispatcher_->current_peer_address()); + EXPECT_EQ("connection 1", dispatcher_->custom_packet_context_); + + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Complete the second ProofSource::GetProof call and verify that a session is + // created. + check.Call(2); + + EXPECT_EQ(client_addr_, dispatcher_->current_client_address()); + EXPECT_EQ(client_addr_, dispatcher_->current_peer_address()); + EXPECT_EQ("connection 1", dispatcher_->custom_packet_context_); + + // Runs the async proof callback for conn_id_2 from client_addr_2_. + GetFakeProofSource()->InvokePendingCallback(0); + + EXPECT_EQ(client_addr_2_, dispatcher_->current_client_address()); + EXPECT_EQ(client_addr_2_, dispatcher_->current_peer_address()); + EXPECT_EQ("connection 2", dispatcher_->custom_packet_context_); + + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); +} + +// Test a simple situation of connections which the StatelessRejector will +// reject. +TEST_F(AsyncGetProofTest, BasicReject) { + CreateTimeWaitListManager(); + + QuicConnectionId conn_id = TestConnectionId(1); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(conn_id, _, _, _, _)); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, client_addr_, conn_id, _, _)); + + EXPECT_CALL(check, Call(2)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_, + QuicStringPiece("hq"), _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, client_addr_, conn_id, _, _)); + } + + // Send a CHLO that the StatelessRejector will reject. + ProcessPacket(client_addr_, conn_id, true, SerializeCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Complete the ProofSource::GetProof call and verify that the connection and + // packet are processed by the time wait list manager. + check.Call(1); + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); + + // Verify that a data packet is passed to the time wait list manager. + check.Call(2); + ProcessPacket(client_addr_, conn_id, true, "My name is Data"); +} + +// Test a situation with multiple interleaved connections which the +// StatelessRejector will accept. +TEST_F(AsyncGetProofTest, MultipleAccept) { + QuicConnectionId conn_id_1 = TestConnectionId(1); + QuicConnectionId conn_id_2 = TestConnectionId(2); + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id_2, _)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_, + QuicStringPiece("HTTP/1"), _)) + .WillOnce(testing::Return(GetSession(conn_id_2, client_addr_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id_2, client_addr_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id_2](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id_2, packet); + }))); + + EXPECT_CALL(check, Call(2)); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id_2, client_addr_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id_2](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id_2, packet); + }))); + + EXPECT_CALL(check, Call(3)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id_1, _)); + + EXPECT_CALL(check, Call(4)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_, + QuicStringPiece("HTTP/1"), _)) + .WillOnce(testing::Return(GetSession(conn_id_1, client_addr_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id_1, client_addr_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillRepeatedly(WithArg<2>( + Invoke([this, conn_id_1](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id_1, packet); + }))); + } + + // Send a CHLO that the StatelessRejector will accept. + ProcessPacket(client_addr_, conn_id_1, true, SerializeFullCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Send another CHLO that the StatelessRejector will accept. + ProcessPacket(client_addr_, conn_id_2, true, SerializeFullCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2); + + // Complete the second ProofSource::GetProof call and verify that a session is + // created. + check.Call(1); + GetFakeProofSource()->InvokePendingCallback(1); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Verify that a data packet on that connection gets processed immediately. + check.Call(2); + ProcessPacket(client_addr_, conn_id_2, true, "My name is Data"); + + // Verify that a data packet on the other connection does not get processed + // yet. + check.Call(3); + ProcessPacket(client_addr_, conn_id_1, true, "My name is Data"); + EXPECT_TRUE(store->HasBufferedPackets(conn_id_1)); + EXPECT_FALSE(store->HasBufferedPackets(conn_id_2)); + + // Complete the first ProofSource::GetProof call and verify that a session is + // created and the buffered packet is processed. + check.Call(4); + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); +} + +// Test a situation with multiple interleaved connections which the +// StatelessRejector will reject. +TEST_F(AsyncGetProofTest, MultipleReject) { + CreateTimeWaitListManager(); + + QuicConnectionId conn_id_1 = TestConnectionId(1); + QuicConnectionId conn_id_2 = TestConnectionId(2); + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_, _, _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(conn_id_2, _, _, _, _)); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, client_addr_, conn_id_2, _, _)); + + EXPECT_CALL(check, Call(2)); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, client_addr_, conn_id_2, _, _)); + + EXPECT_CALL(check, Call(3)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id_1, _)); + + EXPECT_CALL(check, Call(4)); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(conn_id_1, _, _, _, _)); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, client_addr_, conn_id_1, _, _)); + } + + // Send a CHLO that the StatelessRejector will reject. + ProcessPacket(client_addr_, conn_id_1, true, SerializeCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Send another CHLO that the StatelessRejector will reject. + ProcessPacket(client_addr_, conn_id_2, true, SerializeCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2); + + // Complete the second ProofSource::GetProof call and verify that the + // connection and packet are processed by the time wait manager. + check.Call(1); + GetFakeProofSource()->InvokePendingCallback(1); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Verify that a data packet on that connection gets processed immediately by + // the time wait manager. + check.Call(2); + ProcessPacket(client_addr_, conn_id_2, true, "My name is Data"); + + // Verify that a data packet on the first connection gets buffered. + check.Call(3); + ProcessPacket(client_addr_, conn_id_1, true, "My name is Data"); + EXPECT_TRUE(store->HasBufferedPackets(conn_id_1)); + EXPECT_FALSE(store->HasBufferedPackets(conn_id_2)); + + // Complete the first ProofSource::GetProof call and verify that the CHLO is + // processed by the time wait manager and the remaining packets are discarded. + check.Call(4); + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); + EXPECT_FALSE(store->HasBufferedPackets(conn_id_1)); + EXPECT_FALSE(store->HasBufferedPackets(conn_id_2)); +} + +// Test a situation with multiple identical CHLOs which the StatelessRejector +// will reject. +TEST_F(AsyncGetProofTest, MultipleIdenticalReject) { + CreateTimeWaitListManager(); + + QuicConnectionId conn_id_1 = TestConnectionId(1); + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id_1, _)); + + EXPECT_CALL(check, Call(2)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_, + QuicStringPiece(), _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, + AddConnectionIdToTimeWait(conn_id_1, _, _, _, _)); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, client_addr_, conn_id_1, _, _)); + } + + // Send a CHLO that the StatelessRejector will reject. + ProcessPacket(client_addr_, conn_id_1, true, SerializeCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + EXPECT_FALSE(store->HasBufferedPackets(conn_id_1)); + + // Send an identical CHLO which should get buffered. + check.Call(1); + ProcessPacket(client_addr_, conn_id_1, true, SerializeCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + EXPECT_TRUE(store->HasBufferedPackets(conn_id_1)); + + // Complete the ProofSource::GetProof call and verify that the CHLO is + // rejected and the copy is discarded. + check.Call(2); + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); + EXPECT_FALSE(store->HasBufferedPackets(conn_id_1)); +} + +// Test dispatcher behavior when packets time out of the buffer while CHLO +// validation is still pending. +TEST_F(AsyncGetProofTest, BufferTimeout) { + CreateTimeWaitListManager(); + + QuicConnectionId conn_id = TestConnectionId(1); + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock()); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id, _)); + + EXPECT_CALL(check, Call(2)); + EXPECT_CALL(*time_wait_list_manager_, + ProcessPacket(_, client_addr_, conn_id, _, _)); + EXPECT_CALL(*dispatcher_, + CreateQuicSession(conn_id, client_addr_, QuicStringPiece(), _)) + .Times(0); + } + + // Send a CHLO that the StatelessRejector will accept. + ProcessPacket(client_addr_, conn_id, true, SerializeFullCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + EXPECT_FALSE(store->HasBufferedPackets(conn_id)); + + // Send a data packet that will get buffered + check.Call(1); + ProcessPacket(client_addr_, conn_id, true, "My name is Data"); + EXPECT_TRUE(store->HasBufferedPackets(conn_id)); + + // Pretend that enough time has gone by for the packets to get expired out of + // the buffer + mock_helper_.AdvanceTime( + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)); + QuicBufferedPacketStorePeer::expiration_alarm(store)->Cancel(); + store->OnExpirationTimeout(); + EXPECT_FALSE(store->HasBufferedPackets(conn_id)); + EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); + + // Now allow the CHLO validation to complete, and verify that no connection + // gets created. + check.Call(2); + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); + EXPECT_FALSE(store->HasBufferedPackets(conn_id)); + EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); +} + +// Test behavior when packets time out of the buffer *and* the connection times +// out of the time wait manager while CHLO validation is still pending. This +// *should* be impossible, but anything can happen with timing conditions. +TEST_F(AsyncGetProofTest, TimeWaitTimeout) { + QuicConnectionId conn_id = TestConnectionId(1); + QuicBufferedPacketStore* store = + QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); + QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock()); + CreateTimeWaitListManager(); + QuicTimeWaitListManagerPeer::set_clock(time_wait_list_manager_, + mock_helper_.GetClock()); + + testing::MockFunction<void(int check_point)> check; + { + InSequence s; + EXPECT_CALL(check, Call(1)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id, _)); + + EXPECT_CALL(check, Call(2)); + EXPECT_CALL(*dispatcher_, + ShouldCreateOrBufferPacketForConnection(conn_id, _)); + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_, + QuicStringPiece("HTTP/1"), _)) + .WillOnce(testing::Return(GetSession(conn_id, client_addr_))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( + GetSession(conn_id, client_addr_)->connection()), + ProcessUdpPacket(_, _, _)) + .WillOnce(WithArg<2>( + Invoke([this, conn_id](const QuicEncryptedPacket& packet) { + ValidatePacket(conn_id, packet); + }))); + } + + // Send a CHLO that the StatelessRejector will accept. + ProcessPacket(client_addr_, conn_id, true, SerializeFullCHLO()); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + EXPECT_FALSE(store->HasBufferedPackets(conn_id)); + + // Send a data packet that will get buffered + check.Call(1); + ProcessPacket(client_addr_, conn_id, true, "My name is Data"); + EXPECT_TRUE(store->HasBufferedPackets(conn_id)); + + // Pretend that enough time has gone by for the packets to get expired out of + // the buffer + mock_helper_.AdvanceTime( + QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)); + QuicBufferedPacketStorePeer::expiration_alarm(store)->Cancel(); + store->OnExpirationTimeout(); + EXPECT_FALSE(store->HasBufferedPackets(conn_id)); + EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); + + // Pretend that enough time has gone by for the connection ID to be removed + // from the time wait manager + mock_helper_.AdvanceTime( + QuicTimeWaitListManagerPeer::time_wait_period(time_wait_list_manager_)); + QuicTimeWaitListManagerPeer::expiration_alarm(time_wait_list_manager_) + ->Cancel(); + time_wait_list_manager_->CleanUpOldConnectionIds(); + EXPECT_FALSE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); + + // Now allow the CHLO validation to complete. Expect that a connection is + // indeed created, since QUIC has forgotten that this connection ever existed. + // This is a miniscule corner case which should never happen in the wild, so + // really we are just verifying that the dispatcher does not explode in this + // situation. + check.Call(2); + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); + EXPECT_FALSE(store->HasBufferedPackets(conn_id)); + EXPECT_FALSE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); +} + +// Regression test for +// https://bugs.chromium.org/p/chromium/issues/detail?id=748289 +TEST_F(AsyncGetProofTest, DispatcherFailedToPickUpVersionForAsyncProof) { + // This test mimics the scenario that dispatcher's framer can have different + // version when async proof returns. + // When dispatcher sends SREJ, the SREJ frame can be serialized in + // different endianness which causes the client to close the connection + // because of QUIC_INVALID_STREAM_DATA. + + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, true); + ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43); + chlo_.SetVersion(kVER, chlo_version); + // Send a CHLO with v43. Dispatcher framer's version is set to v43. + ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version, + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Send another CHLO with v39. Dispatcher framer's version is set to v39. + chlo_version.transport_version = QUIC_VERSION_39; + chlo_.SetVersion(kVER, chlo_version); + // Invalidate the cached serialized form. + chlo_.MarkDirty(); + ProcessPacket(client_addr_, TestConnectionId(2), true, chlo_version, + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2); + + // Complete the ProofSource::GetProof call for v43. This would cause the + // version mismatch between the CHLO packet and the dispatcher. + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); +} + +// Regression test for b/116200989. +TEST_F(AsyncGetProofTest, DispatcherHasWrongLastPacketIsIetfQuic) { + // Process a packet of v44. + ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44); + chlo_.SetVersion(kVER, chlo_version); + ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version, + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + + // Process another packet of v43. + chlo_version.transport_version = QUIC_VERSION_43; + chlo_.SetVersion(kVER, chlo_version); + // Invalidate the cached serialized form. + chlo_.MarkDirty(); + ProcessPacket(client_addr_, TestConnectionId(2), true, chlo_version, + SerializeCHLO(), CONNECTION_ID_PRESENT, + PACKET_4BYTE_PACKET_NUMBER, 1); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2); + + // Complete the ProofSource::GetProof call for v44. + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); + + // Complete the ProofSource::GetProof call for v43. + GetFakeProofSource()->InvokePendingCallback(0); + ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_epoll_alarm_factory.cc b/quic/core/quic_epoll_alarm_factory.cc new file mode 100644 index 0000000..75b375f --- /dev/null +++ b/quic/core/quic_epoll_alarm_factory.cc
@@ -0,0 +1,89 @@ +// Copyright (c) 2012 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/quic_epoll_alarm_factory.h" + +#include <type_traits> + +#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h" + +namespace quic { +namespace { + +class QuicEpollAlarm : public QuicAlarm { + public: + QuicEpollAlarm(QuicEpollServer* epoll_server, + QuicArenaScopedPtr<QuicAlarm::Delegate> delegate) + : QuicAlarm(std::move(delegate)), + epoll_server_(epoll_server), + epoll_alarm_impl_(this) {} + + protected: + void SetImpl() override { + DCHECK(deadline().IsInitialized()); + epoll_server_->RegisterAlarm( + (deadline() - QuicTime::Zero()).ToMicroseconds(), &epoll_alarm_impl_); + } + + void CancelImpl() override { + DCHECK(!deadline().IsInitialized()); + epoll_alarm_impl_.UnregisterIfRegistered(); + } + + void UpdateImpl() override { + DCHECK(deadline().IsInitialized()); + int64_t epoll_deadline = (deadline() - QuicTime::Zero()).ToMicroseconds(); + if (epoll_alarm_impl_.registered()) { + epoll_alarm_impl_.ReregisterAlarm(epoll_deadline); + } else { + epoll_server_->RegisterAlarm(epoll_deadline, &epoll_alarm_impl_); + } + } + + private: + class EpollAlarmImpl : public QuicEpollAlarmBase { + public: + using int64_epoll = decltype(QuicEpollAlarmBase().OnAlarm()); + + explicit EpollAlarmImpl(QuicEpollAlarm* alarm) : alarm_(alarm) {} + + // Use the same integer type as the base class. + int64_epoll OnAlarm() override { + QuicEpollAlarmBase::OnAlarm(); + alarm_->Fire(); + // Fire will take care of registering the alarm, if needed. + return 0; + } + + private: + QuicEpollAlarm* alarm_; + }; + + QuicEpollServer* epoll_server_; + EpollAlarmImpl epoll_alarm_impl_; +}; + +} // namespace + +QuicEpollAlarmFactory::QuicEpollAlarmFactory(QuicEpollServer* epoll_server) + : epoll_server_(epoll_server) {} + +QuicEpollAlarmFactory::~QuicEpollAlarmFactory() = default; + +QuicAlarm* QuicEpollAlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) { + return new QuicEpollAlarm(epoll_server_, + QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)); +} + +QuicArenaScopedPtr<QuicAlarm> QuicEpollAlarmFactory::CreateAlarm( + QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, + QuicConnectionArena* arena) { + if (arena != nullptr) { + return arena->New<QuicEpollAlarm>(epoll_server_, std::move(delegate)); + } + return QuicArenaScopedPtr<QuicAlarm>( + new QuicEpollAlarm(epoll_server_, std::move(delegate))); +} + +} // namespace quic
diff --git a/quic/core/quic_epoll_alarm_factory.h b/quic/core/quic_epoll_alarm_factory.h new file mode 100644 index 0000000..fc9b45c --- /dev/null +++ b/quic/core/quic_epoll_alarm_factory.h
@@ -0,0 +1,35 @@ +// Copyright (c) 2015 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_QUIC_EPOLL_ALARM_FACTORY_H_ +#define QUICHE_QUIC_CORE_QUIC_EPOLL_ALARM_FACTORY_H_ + +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" + +namespace quic { + +// Creates alarms that use the supplied EpollServer for timing and firing. +class QuicEpollAlarmFactory : public QuicAlarmFactory { + public: + explicit QuicEpollAlarmFactory(QuicEpollServer* eps); + QuicEpollAlarmFactory(const QuicEpollAlarmFactory&) = delete; + QuicEpollAlarmFactory& operator=(const QuicEpollAlarmFactory&) = delete; + ~QuicEpollAlarmFactory() override; + + // QuicAlarmFactory interface. + QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override; + QuicArenaScopedPtr<QuicAlarm> CreateAlarm( + QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, + QuicConnectionArena* arena) override; + + private: + QuicEpollServer* epoll_server_; // Not owned. +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_EPOLL_ALARM_FACTORY_H_
diff --git a/quic/core/quic_epoll_alarm_factory_test.cc b/quic/core/quic_epoll_alarm_factory_test.cc new file mode 100644 index 0000000..ac950c6 --- /dev/null +++ b/quic/core/quic_epoll_alarm_factory_test.cc
@@ -0,0 +1,140 @@ +// Copyright (c) 2015 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/quic_epoll_alarm_factory.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/quic/platform/impl/quic_epoll_clock.h" + +namespace quic { +namespace test { +namespace { + +class TestDelegate : public QuicAlarm::Delegate { + public: + TestDelegate() : fired_(false) {} + + void OnAlarm() override { fired_ = true; } + + bool fired() const { return fired_; } + + private: + bool fired_; +}; + +// The boolean parameter denotes whether or not to use an arena. +class QuicEpollAlarmFactoryTest : public QuicTestWithParam<bool> { + protected: + QuicEpollAlarmFactoryTest() + : clock_(&epoll_server_), alarm_factory_(&epoll_server_) {} + + QuicConnectionArena* GetArenaParam() { + return GetParam() ? &arena_ : nullptr; + } + + const QuicEpollClock clock_; + QuicEpollAlarmFactory alarm_factory_; + QuicFakeEpollServer epoll_server_; + QuicConnectionArena arena_; +}; + +INSTANTIATE_TEST_SUITE_P(UseArena, + QuicEpollAlarmFactoryTest, + ::testing::ValuesIn({true, false})); + +TEST_P(QuicEpollAlarmFactoryTest, CreateAlarm) { + QuicArenaScopedPtr<TestDelegate> delegate = + QuicArenaScopedPtr<TestDelegate>(new TestDelegate()); + QuicArenaScopedPtr<QuicAlarm> alarm( + alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam())); + + QuicTime start = clock_.Now(); + QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); + alarm->Set(start + delta); + + epoll_server_.AdvanceByAndWaitForEventsAndExecuteCallbacks( + delta.ToMicroseconds()); + EXPECT_EQ(start + delta, clock_.Now()); +} + +TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndCancel) { + QuicArenaScopedPtr<TestDelegate> delegate = + QuicArenaScopedPtr<TestDelegate>(new TestDelegate()); + TestDelegate* unowned_delegate = delegate.get(); + QuicArenaScopedPtr<QuicAlarm> alarm( + alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam())); + + QuicTime start = clock_.Now(); + QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); + alarm->Set(start + delta); + alarm->Cancel(); + + epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds()); + EXPECT_EQ(start + delta, clock_.Now()); + EXPECT_FALSE(unowned_delegate->fired()); +} + +TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndReset) { + QuicArenaScopedPtr<TestDelegate> delegate = + QuicArenaScopedPtr<TestDelegate>(new TestDelegate()); + TestDelegate* unowned_delegate = delegate.get(); + QuicArenaScopedPtr<QuicAlarm> alarm( + alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam())); + + QuicTime start = clock_.Now(); + QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); + alarm->Set(clock_.Now() + delta); + alarm->Cancel(); + QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3); + alarm->Set(clock_.Now() + new_delta); + + epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds()); + EXPECT_EQ(start + delta, clock_.Now()); + EXPECT_FALSE(unowned_delegate->fired()); + + epoll_server_.AdvanceByExactlyAndCallCallbacks( + (new_delta - delta).ToMicroseconds()); + EXPECT_EQ(start + new_delta, clock_.Now()); + EXPECT_TRUE(unowned_delegate->fired()); +} + +TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndUpdate) { + QuicArenaScopedPtr<TestDelegate> delegate = + QuicArenaScopedPtr<TestDelegate>(new TestDelegate()); + TestDelegate* unowned_delegate = delegate.get(); + QuicArenaScopedPtr<QuicAlarm> alarm( + alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam())); + + QuicTime start = clock_.Now(); + QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); + alarm->Set(clock_.Now() + delta); + QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3); + alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1)); + + epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds()); + EXPECT_EQ(start + delta, clock_.Now()); + EXPECT_FALSE(unowned_delegate->fired()); + + // Move the alarm forward 1us and ensure it doesn't move forward. + alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(2)); + + epoll_server_.AdvanceByExactlyAndCallCallbacks( + (new_delta - delta).ToMicroseconds()); + EXPECT_EQ(start + new_delta, clock_.Now()); + EXPECT_TRUE(unowned_delegate->fired()); + + // Set the alarm via an update call. + new_delta = QuicTime::Delta::FromMicroseconds(5); + alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1)); + EXPECT_TRUE(alarm->IsSet()); + + // Update it with an uninitialized time and ensure it's cancelled. + alarm->Update(QuicTime::Zero(), QuicTime::Delta::FromMicroseconds(1)); + EXPECT_FALSE(alarm->IsSet()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_epoll_connection_helper.cc b/quic/core/quic_epoll_connection_helper.cc new file mode 100644 index 0000000..1f5fb87 --- /dev/null +++ b/quic/core/quic_epoll_connection_helper.cc
@@ -0,0 +1,41 @@ +// Copyright (c) 2012 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/quic_epoll_connection_helper.h" + +#include <errno.h> +#include <sys/socket.h> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/quic/platform/impl/quic_socket_utils.h" + +namespace quic { + +QuicEpollConnectionHelper::QuicEpollConnectionHelper( + QuicEpollServer* epoll_server, + QuicAllocator type) + : clock_(epoll_server), + random_generator_(QuicRandom::GetInstance()), + allocator_type_(type) {} + +QuicEpollConnectionHelper::~QuicEpollConnectionHelper() = default; + +const QuicClock* QuicEpollConnectionHelper::GetClock() const { + return &clock_; +} + +QuicRandom* QuicEpollConnectionHelper::GetRandomGenerator() { + return random_generator_; +} + +QuicBufferAllocator* QuicEpollConnectionHelper::GetStreamSendBufferAllocator() { + if (allocator_type_ == QuicAllocator::BUFFER_POOL) { + return &stream_buffer_allocator_; + } else { + DCHECK(allocator_type_ == QuicAllocator::SIMPLE); + return &simple_buffer_allocator_; + } +} + +} // namespace quic
diff --git a/quic/core/quic_epoll_connection_helper.h b/quic/core/quic_epoll_connection_helper.h new file mode 100644 index 0000000..7041454 --- /dev/null +++ b/quic/core/quic_epoll_connection_helper.h
@@ -0,0 +1,55 @@ +// Copyright (c) 2012 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. + +// The epoll-specific helper for QuicConnection which uses +// EpollAlarm for alarms, and used an int fd_ for writing data. + +#ifndef QUICHE_QUIC_CORE_QUIC_EPOLL_CONNECTION_HELPER_H_ +#define QUICHE_QUIC_CORE_QUIC_EPOLL_CONNECTION_HELPER_H_ + +#include <sys/types.h> +#include <set> + +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_stream_buffer_allocator.h" +#include "net/quic/platform/impl/quic_epoll_clock.h" + +namespace quic { + +class QuicRandom; + +enum class QuicAllocator { SIMPLE, BUFFER_POOL }; + +class QuicEpollConnectionHelper : public QuicConnectionHelperInterface { + public: + QuicEpollConnectionHelper(QuicEpollServer* eps, QuicAllocator allocator); + QuicEpollConnectionHelper(const QuicEpollConnectionHelper&) = delete; + QuicEpollConnectionHelper& operator=(const QuicEpollConnectionHelper&) = + delete; + ~QuicEpollConnectionHelper() override; + + // QuicConnectionHelperInterface + const QuicClock* GetClock() const override; + QuicRandom* GetRandomGenerator() override; + QuicBufferAllocator* GetStreamSendBufferAllocator() override; + + private: + const QuicEpollClock clock_; + QuicRandom* random_generator_; + // Set up allocators. They take up minimal memory before use. + // Allocator for stream send buffers. + QuicStreamBufferAllocator stream_buffer_allocator_; + SimpleBufferAllocator simple_buffer_allocator_; + QuicAllocator allocator_type_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_EPOLL_CONNECTION_HELPER_H_
diff --git a/quic/core/quic_epoll_connection_helper_test.cc b/quic/core/quic_epoll_connection_helper_test.cc new file mode 100644 index 0000000..ea63948 --- /dev/null +++ b/quic/core/quic_epoll_connection_helper_test.cc
@@ -0,0 +1,41 @@ +// Copyright (c) 2012 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/quic_epoll_connection_helper.h" + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QuicEpollConnectionHelperTest : public QuicTest { + protected: + QuicEpollConnectionHelperTest() + : helper_(&epoll_server_, QuicAllocator::BUFFER_POOL) {} + + QuicFakeEpollServer epoll_server_; + QuicEpollConnectionHelper helper_; +}; + +TEST_F(QuicEpollConnectionHelperTest, GetClock) { + const QuicClock* clock = helper_.GetClock(); + QuicTime start = clock->Now(); + + QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(5); + epoll_server_.AdvanceBy(delta.ToMicroseconds()); + + EXPECT_EQ(start + delta, clock->Now()); +} + +TEST_F(QuicEpollConnectionHelperTest, GetRandomGenerator) { + QuicRandom* random = helper_.GetRandomGenerator(); + EXPECT_EQ(QuicRandom::GetInstance(), random); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc new file mode 100644 index 0000000..215f527 --- /dev/null +++ b/quic/core/quic_error_codes.cc
@@ -0,0 +1,171 @@ +// Copyright (c) 2012 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/quic_error_codes.h" + +namespace quic { + +#define RETURN_STRING_LITERAL(x) \ + case x: \ + return #x; + +const char* QuicRstStreamErrorCodeToString(QuicRstStreamErrorCode error) { + switch (error) { + RETURN_STRING_LITERAL(QUIC_STREAM_NO_ERROR); + RETURN_STRING_LITERAL(QUIC_STREAM_CONNECTION_ERROR); + RETURN_STRING_LITERAL(QUIC_ERROR_PROCESSING_STREAM); + RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS); + RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD); + RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY); + RETURN_STRING_LITERAL(QUIC_STREAM_CANCELLED); + RETURN_STRING_LITERAL(QUIC_RST_ACKNOWLEDGEMENT); + RETURN_STRING_LITERAL(QUIC_REFUSED_STREAM); + RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR); + RETURN_STRING_LITERAL(QUIC_INVALID_PROMISE_URL); + RETURN_STRING_LITERAL(QUIC_UNAUTHORIZED_PROMISE_URL); + RETURN_STRING_LITERAL(QUIC_DUPLICATE_PROMISE_URL); + RETURN_STRING_LITERAL(QUIC_PROMISE_VARY_MISMATCH); + RETURN_STRING_LITERAL(QUIC_INVALID_PROMISE_METHOD); + RETURN_STRING_LITERAL(QUIC_PUSH_STREAM_TIMED_OUT); + RETURN_STRING_LITERAL(QUIC_HEADERS_TOO_LARGE); + RETURN_STRING_LITERAL(QUIC_STREAM_TTL_EXPIRED); + } + // Return a default value so that we return this when |error| doesn't match + // any of the QuicRstStreamErrorCodes. This can happen when the RstStream + // frame sent by the peer (attacker) has invalid error code. + return "INVALID_RST_STREAM_ERROR_CODE"; +} + +const char* QuicErrorCodeToString(QuicErrorCode error) { + switch (error) { + RETURN_STRING_LITERAL(QUIC_NO_ERROR); + RETURN_STRING_LITERAL(QUIC_INTERNAL_ERROR); + RETURN_STRING_LITERAL(QUIC_STREAM_DATA_AFTER_TERMINATION); + RETURN_STRING_LITERAL(QUIC_INVALID_PACKET_HEADER); + RETURN_STRING_LITERAL(QUIC_INVALID_FRAME_DATA); + RETURN_STRING_LITERAL(QUIC_MISSING_PAYLOAD); + RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_OVERLAPPING_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_WINDOW_UPDATE_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_BLOCKED_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_STOP_WAITING_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_PATH_CLOSE_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET); + RETURN_STRING_LITERAL(QUIC_INVALID_PUBLIC_RST_PACKET); + RETURN_STRING_LITERAL(QUIC_DECRYPTION_FAILURE); + RETURN_STRING_LITERAL(QUIC_ENCRYPTION_FAILURE); + RETURN_STRING_LITERAL(QUIC_PACKET_TOO_LARGE); + RETURN_STRING_LITERAL(QUIC_PEER_GOING_AWAY); + RETURN_STRING_LITERAL(QUIC_HANDSHAKE_FAILED); + RETURN_STRING_LITERAL(QUIC_CRYPTO_TAGS_OUT_OF_ORDER); + RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_ENTRIES); + RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_REJECTS); + RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH) + RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); + RETURN_STRING_LITERAL(QUIC_CRYPTO_INTERNAL_ERROR); + RETURN_STRING_LITERAL(QUIC_CRYPTO_VERSION_NOT_SUPPORTED); + RETURN_STRING_LITERAL(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT); + RETURN_STRING_LITERAL(QUIC_CRYPTO_NO_SUPPORT); + RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); + RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER); + RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND); + RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP); + RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND); + RETURN_STRING_LITERAL(QUIC_UNSUPPORTED_PROOF_DEMAND); + RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID); + RETURN_STRING_LITERAL(QUIC_INVALID_PRIORITY); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS); + RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET); + RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); + RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID); + RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE); + RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE); + RETURN_STRING_LITERAL(QUIC_NETWORK_IDLE_TIMEOUT); + RETURN_STRING_LITERAL(QUIC_HANDSHAKE_TIMEOUT); + RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_ADDRESS); + RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_PORT); + RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR); + RETURN_STRING_LITERAL(QUIC_PACKET_READ_ERROR); + RETURN_STRING_LITERAL(QUIC_EMPTY_STREAM_FRAME_NO_FIN); + RETURN_STRING_LITERAL(QUIC_INVALID_HEADERS_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE); + RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA); + RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA); + RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_INVALID_WINDOW); + RETURN_STRING_LITERAL(QUIC_CONNECTION_IP_POOLED); + RETURN_STRING_LITERAL(QUIC_PROOF_INVALID); + RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG); + RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT); + RETURN_STRING_LITERAL(QUIC_CRYPTO_SERVER_CONFIG_EXPIRED); + RETURN_STRING_LITERAL(QUIC_INVALID_CHANNEL_ID_SIGNATURE); + RETURN_STRING_LITERAL(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED); + RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO); + RETURN_STRING_LITERAL(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE); + RETURN_STRING_LITERAL(QUIC_VERSION_NEGOTIATION_MISMATCH); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS); + RETURN_STRING_LITERAL(QUIC_CONNECTION_CANCELLED); + RETURN_STRING_LITERAL(QUIC_BAD_PACKET_LOSS_RATE); + RETURN_STRING_LITERAL(QUIC_PUBLIC_RESETS_POST_HANDSHAKE); + RETURN_STRING_LITERAL(QUIC_FAILED_TO_SERIALIZE_PACKET); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_AVAILABLE_STREAMS); + RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_FEC_DATA); + RETURN_STRING_LITERAL(QUIC_BAD_MULTIPATH_FLAG); + RETURN_STRING_LITERAL(QUIC_IP_ADDRESS_CHANGED); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_RTOS); + RETURN_STRING_LITERAL(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_MAYBE_CORRUPTED_MEMORY); + RETURN_STRING_LITERAL(QUIC_CRYPTO_CHLO_TOO_LARGE); + RETURN_STRING_LITERAL(QUIC_MULTIPATH_PATH_DOES_NOT_EXIST); + RETURN_STRING_LITERAL(QUIC_MULTIPATH_PATH_NOT_ACTIVE); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_STREAM_DATA_INTERVALS); + RETURN_STRING_LITERAL(QUIC_STREAM_SEQUENCER_INVALID_STATE); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_SESSIONS_ON_SERVER); + RETURN_STRING_LITERAL(QUIC_STREAM_LENGTH_OVERFLOW); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR); + RETURN_STRING_LITERAL(QUIC_INVALID_APPLICATION_CLOSE_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_MAX_DATA_FRAME_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_BLOCKED_DATA); + RETURN_STRING_LITERAL(QUIC_MAX_STREAM_ID_DATA); + RETURN_STRING_LITERAL(QUIC_STREAM_ID_BLOCKED_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_NEW_CONNECTION_ID_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_RETIRE_CONNECTION_ID_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_STOP_SENDING_FRAME_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_PATH_CHALLENGE_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_PATH_RESPONSE_DATA); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED); + RETURN_STRING_LITERAL(QUIC_INVALID_MESSAGE_DATA); + RETURN_STRING_LITERAL(IETF_QUIC_PROTOCOL_VIOLATION); + RETURN_STRING_LITERAL(QUIC_INVALID_NEW_TOKEN); + RETURN_STRING_LITERAL(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM); + RETURN_STRING_LITERAL(QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM); + RETURN_STRING_LITERAL(QUIC_STREAM_ID_BLOCKED_ERROR); + RETURN_STRING_LITERAL(QUIC_MAX_STREAM_ID_ERROR); + RETURN_STRING_LITERAL(QUIC_HTTP_DECODER_ERROR); + RETURN_STRING_LITERAL(QUIC_STALE_CONNECTION_CANCELLED); + + RETURN_STRING_LITERAL(QUIC_LAST_ERROR); + // Intentionally have no default case, so we'll break the build + // if we add errors and don't put them here. + } + // Return a default value so that we return this when |error| doesn't match + // any of the QuicErrorCodes. This can happen when the ConnectionClose + // frame sent by the peer (attacker) has invalid error code. + return "INVALID_ERROR_CODE"; +} + +#undef RETURN_STRING_LITERAL // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h new file mode 100644 index 0000000..d3446a8 --- /dev/null +++ b/quic/core/quic_error_codes.h
@@ -0,0 +1,348 @@ +// Copyright (c) 2012 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_QUIC_ERROR_CODES_H_ +#define QUICHE_QUIC_CORE_QUIC_ERROR_CODES_H_ + +#include <cstdint> +#include <limits> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +enum QuicRstStreamErrorCode { + // Complete response has been sent, sending a RST to ask the other endpoint + // to stop sending request data without discarding the response. + QUIC_STREAM_NO_ERROR = 0, + + // There was some error which halted stream processing. + QUIC_ERROR_PROCESSING_STREAM, + // We got two fin or reset offsets which did not match. + QUIC_MULTIPLE_TERMINATION_OFFSETS, + // We got bad payload and can not respond to it at the protocol level. + QUIC_BAD_APPLICATION_PAYLOAD, + // Stream closed due to connection error. No reset frame is sent when this + // happens. + QUIC_STREAM_CONNECTION_ERROR, + // GoAway frame sent. No more stream can be created. + QUIC_STREAM_PEER_GOING_AWAY, + // The stream has been cancelled. + QUIC_STREAM_CANCELLED, + // Closing stream locally, sending a RST to allow for proper flow control + // accounting. Sent in response to a RST from the peer. + QUIC_RST_ACKNOWLEDGEMENT, + // Receiver refused to create the stream (because its limit on open streams + // has been reached). The sender should retry the request later (using + // another stream). + QUIC_REFUSED_STREAM, + // Invalid URL in PUSH_PROMISE request header. + QUIC_INVALID_PROMISE_URL, + // Server is not authoritative for this URL. + QUIC_UNAUTHORIZED_PROMISE_URL, + // Can't have more than one active PUSH_PROMISE per URL. + QUIC_DUPLICATE_PROMISE_URL, + // Vary check failed. + QUIC_PROMISE_VARY_MISMATCH, + // Only GET and HEAD methods allowed. + QUIC_INVALID_PROMISE_METHOD, + // The push stream is unclaimed and timed out. + QUIC_PUSH_STREAM_TIMED_OUT, + // Received headers were too large. + QUIC_HEADERS_TOO_LARGE, + // The data is not likely arrive in time. + QUIC_STREAM_TTL_EXPIRED, + // No error. Used as bound while iterating. + QUIC_STREAM_LAST_ERROR, +}; +// QuicRstStreamErrorCode is encoded as a single octet on-the-wire. +static_assert(static_cast<int>(QUIC_STREAM_LAST_ERROR) <= + std::numeric_limits<uint8_t>::max(), + "QuicRstStreamErrorCode exceeds single octet"); + +// These values must remain stable as they are uploaded to UMA histograms. +// To add a new error code, use the current value of QUIC_LAST_ERROR and +// increment QUIC_LAST_ERROR. +enum QuicErrorCode { + QUIC_NO_ERROR = 0, + + // Connection has reached an invalid state. + QUIC_INTERNAL_ERROR = 1, + // There were data frames after the a fin or reset. + QUIC_STREAM_DATA_AFTER_TERMINATION = 2, + // Control frame is malformed. + QUIC_INVALID_PACKET_HEADER = 3, + // Frame data is malformed. + QUIC_INVALID_FRAME_DATA = 4, + // The packet contained no payload. + QUIC_MISSING_PAYLOAD = 48, + // FEC data is malformed. + QUIC_INVALID_FEC_DATA = 5, + // STREAM frame data is malformed. + QUIC_INVALID_STREAM_DATA = 46, + // STREAM frame data overlaps with buffered data. + QUIC_OVERLAPPING_STREAM_DATA = 87, + // Received STREAM frame data is not encrypted. + QUIC_UNENCRYPTED_STREAM_DATA = 61, + // Attempt to send unencrypted STREAM frame. + QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA = 88, + // Received a frame which is likely the result of memory corruption. + QUIC_MAYBE_CORRUPTED_MEMORY = 89, + // FEC frame data is not encrypted. + QUIC_UNENCRYPTED_FEC_DATA = 77, + // RST_STREAM frame data is malformed. + QUIC_INVALID_RST_STREAM_DATA = 6, + // CONNECTION_CLOSE frame data is malformed. + QUIC_INVALID_CONNECTION_CLOSE_DATA = 7, + // GOAWAY frame data is malformed. + QUIC_INVALID_GOAWAY_DATA = 8, + // WINDOW_UPDATE frame data is malformed. + QUIC_INVALID_WINDOW_UPDATE_DATA = 57, + // BLOCKED frame data is malformed. + QUIC_INVALID_BLOCKED_DATA = 58, + // STOP_WAITING frame data is malformed. + QUIC_INVALID_STOP_WAITING_DATA = 60, + // PATH_CLOSE frame data is malformed. + QUIC_INVALID_PATH_CLOSE_DATA = 78, + // ACK frame data is malformed. + QUIC_INVALID_ACK_DATA = 9, + // Message frame data is malformed. + QUIC_INVALID_MESSAGE_DATA = 112, + + // Version negotiation packet is malformed. + QUIC_INVALID_VERSION_NEGOTIATION_PACKET = 10, + // Public RST packet is malformed. + QUIC_INVALID_PUBLIC_RST_PACKET = 11, + // There was an error decrypting. + QUIC_DECRYPTION_FAILURE = 12, + // There was an error encrypting. + QUIC_ENCRYPTION_FAILURE = 13, + // The packet exceeded kMaxPacketSize. + QUIC_PACKET_TOO_LARGE = 14, + // The peer is going away. May be a client or server. + QUIC_PEER_GOING_AWAY = 16, + // A stream ID was invalid. + QUIC_INVALID_STREAM_ID = 17, + // A priority was invalid. + QUIC_INVALID_PRIORITY = 49, + // Too many streams already open. + QUIC_TOO_MANY_OPEN_STREAMS = 18, + // The peer created too many available streams. + QUIC_TOO_MANY_AVAILABLE_STREAMS = 76, + // Received public reset for this connection. + QUIC_PUBLIC_RESET = 19, + // Invalid protocol version. + QUIC_INVALID_VERSION = 20, + + // The Header ID for a stream was too far from the previous. + QUIC_INVALID_HEADER_ID = 22, + // Negotiable parameter received during handshake had invalid value. + QUIC_INVALID_NEGOTIATED_VALUE = 23, + // There was an error decompressing data. + QUIC_DECOMPRESSION_FAILURE = 24, + // The connection timed out due to no network activity. + QUIC_NETWORK_IDLE_TIMEOUT = 25, + // The connection timed out waiting for the handshake to complete. + QUIC_HANDSHAKE_TIMEOUT = 67, + // There was an error encountered migrating addresses. + QUIC_ERROR_MIGRATING_ADDRESS = 26, + // There was an error encountered migrating port only. + QUIC_ERROR_MIGRATING_PORT = 86, + // There was an error while writing to the socket. + QUIC_PACKET_WRITE_ERROR = 27, + // There was an error while reading from the socket. + QUIC_PACKET_READ_ERROR = 51, + // We received a STREAM_FRAME with no data and no fin flag set. + QUIC_EMPTY_STREAM_FRAME_NO_FIN = 50, + // We received invalid data on the headers stream. + QUIC_INVALID_HEADERS_STREAM_DATA = 56, + // Invalid data on the headers stream received because of decompression + // failure. + QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE = 97, + // The peer received too much data, violating flow control. + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA = 59, + // The peer sent too much data, violating flow control. + QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA = 63, + // The peer received an invalid flow control window. + QUIC_FLOW_CONTROL_INVALID_WINDOW = 64, + // The connection has been IP pooled into an existing connection. + QUIC_CONNECTION_IP_POOLED = 62, + // The connection has too many outstanding sent packets. + QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS = 68, + // The connection has too many outstanding received packets. + QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS = 69, + // The quic connection has been cancelled. + QUIC_CONNECTION_CANCELLED = 70, + // Disabled QUIC because of high packet loss rate. + QUIC_BAD_PACKET_LOSS_RATE = 71, + // Disabled QUIC because of too many PUBLIC_RESETs post handshake. + QUIC_PUBLIC_RESETS_POST_HANDSHAKE = 73, + // Closed because we failed to serialize a packet. + QUIC_FAILED_TO_SERIALIZE_PACKET = 75, + // QUIC timed out after too many RTOs. + QUIC_TOO_MANY_RTOS = 85, + + // Crypto errors. + + // Handshake failed. + QUIC_HANDSHAKE_FAILED = 28, + // Handshake message contained out of order tags. + QUIC_CRYPTO_TAGS_OUT_OF_ORDER = 29, + // Handshake message contained too many entries. + QUIC_CRYPTO_TOO_MANY_ENTRIES = 30, + // Handshake message contained an invalid value length. + QUIC_CRYPTO_INVALID_VALUE_LENGTH = 31, + // A crypto message was received after the handshake was complete. + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE = 32, + // A crypto message was received with an illegal message tag. + QUIC_INVALID_CRYPTO_MESSAGE_TYPE = 33, + // A crypto message was received with an illegal parameter. + QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER = 34, + // An invalid channel id signature was supplied. + QUIC_INVALID_CHANNEL_ID_SIGNATURE = 52, + // A crypto message was received with a mandatory parameter missing. + QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND = 35, + // A crypto message was received with a parameter that has no overlap + // with the local parameter. + QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP = 36, + // A crypto message was received that contained a parameter with too few + // values. + QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND = 37, + // A demand for an unsupport proof type was received. + QUIC_UNSUPPORTED_PROOF_DEMAND = 94, + // An internal error occurred in crypto processing. + QUIC_CRYPTO_INTERNAL_ERROR = 38, + // A crypto handshake message specified an unsupported version. + QUIC_CRYPTO_VERSION_NOT_SUPPORTED = 39, + // A crypto handshake message resulted in a stateless reject. + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT = 72, + // There was no intersection between the crypto primitives supported by the + // peer and ourselves. + QUIC_CRYPTO_NO_SUPPORT = 40, + // The server rejected our client hello messages too many times. + QUIC_CRYPTO_TOO_MANY_REJECTS = 41, + // The client rejected the server's certificate chain or signature. + QUIC_PROOF_INVALID = 42, + // A crypto message was received with a duplicate tag. + QUIC_CRYPTO_DUPLICATE_TAG = 43, + // A crypto message was received with the wrong encryption level (i.e. it + // should have been encrypted but was not.) + QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT = 44, + // The server config for a server has expired. + QUIC_CRYPTO_SERVER_CONFIG_EXPIRED = 45, + // We failed to setup the symmetric keys for a connection. + QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED = 53, + // A handshake message arrived, but we are still validating the + // previous handshake message. + QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO = 54, + // A server config update arrived before the handshake is complete. + QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE = 65, + // CHLO cannot fit in one packet. + QUIC_CRYPTO_CHLO_TOO_LARGE = 90, + // This connection involved a version negotiation which appears to have been + // tampered with. + QUIC_VERSION_NEGOTIATION_MISMATCH = 55, + + // Multipath errors. + // Multipath is not enabled, but a packet with multipath flag on is received. + QUIC_BAD_MULTIPATH_FLAG = 79, + // A path is supposed to exist but does not. + QUIC_MULTIPATH_PATH_DOES_NOT_EXIST = 91, + // A path is supposed to be active but is not. + QUIC_MULTIPATH_PATH_NOT_ACTIVE = 92, + + // IP address changed causing connection close. + QUIC_IP_ADDRESS_CHANGED = 80, + + // Connection migration errors. + // Network changed, but connection had no migratable streams. + QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS = 81, + // Connection changed networks too many times. + QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES = 82, + // Connection migration was attempted, but there was no new network to + // migrate to. + QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK = 83, + // Network changed, but connection had one or more non-migratable streams. + QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM = 84, + // Network changed, but connection migration was disabled by config. + QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG = 99, + // Network changed, but error was encountered on the alternative network. + QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR = 100, + // Network changed, but handshake is not confirmed yet. + QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED = 111, + + // Stream frames arrived too discontiguously so that stream sequencer buffer + // maintains too many intervals. + QUIC_TOO_MANY_STREAM_DATA_INTERVALS = 93, + + // Sequencer buffer get into weird state where continuing read/write will lead + // to crash. + QUIC_STREAM_SEQUENCER_INVALID_STATE = 95, + + // Connection closed because of server hits max number of sessions allowed. + QUIC_TOO_MANY_SESSIONS_ON_SERVER = 96, + + // Receive a RST_STREAM with offset larger than kMaxStreamLength. + QUIC_STREAM_LENGTH_OVERFLOW = 98, + // APPLICATION_CLOSE frame data is malformed. + QUIC_INVALID_APPLICATION_CLOSE_DATA = 101, + // Received a MAX DATA frame with errors. + QUIC_INVALID_MAX_DATA_FRAME_DATA = 102, + // Received a MAX STREAM DATA frame with errors. + QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA = 103, + // Received a MAX_STREAM_ID frame with bad data + QUIC_MAX_STREAM_ID_DATA = 104, + // Received a STREAM_ID_BLOCKED frame with bad data + QUIC_STREAM_ID_BLOCKED_DATA = 105, + // Error deframing a STREAM BLOCKED frame. + QUIC_INVALID_STREAM_BLOCKED_DATA = 106, + // NEW CONNECTION ID frame data is malformed. + QUIC_INVALID_NEW_CONNECTION_ID_DATA = 107, + // Received a MAX STREAM DATA frame with errors. + QUIC_INVALID_STOP_SENDING_FRAME_DATA = 108, + // Error deframing PATH CHALLENGE or PATH RESPONSE frames. + QUIC_INVALID_PATH_CHALLENGE_DATA = 109, + QUIC_INVALID_PATH_RESPONSE_DATA = 110, + // This is used to indicate an IETF QUIC PROTOCOL VIOLATION + // transport error within Google (pre-v99) QUIC. + IETF_QUIC_PROTOCOL_VIOLATION = 113, + QUIC_INVALID_NEW_TOKEN = 114, + + // Received stream data on a WRITE_UNIDIRECTIONAL stream. + QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM = 115, + // Try to send stream data on a READ_UNIDIRECTIONAL stream. + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM = 116, + + // RETIRE CONNECTION ID frame data is malformed. + QUIC_INVALID_RETIRE_CONNECTION_ID_DATA = 117, + // + // Error in a received STREAM ID BLOCKED frame. -- the stream ID is not + // consistent with the state of the endpoint. + QUIC_STREAM_ID_BLOCKED_ERROR = 118, + // Error in a received MAX STREAM ID frame -- the stream ID is not + // consistent with the state of the endpoint. + QUIC_MAX_STREAM_ID_ERROR = 119, + // Error in Http decoder + QUIC_HTTP_DECODER_ERROR = 120, + // Connection from stale host needs to be cancelled. + QUIC_STALE_CONNECTION_CANCELLED = 121, + + // No error. Used as bound while iterating. + QUIC_LAST_ERROR = 122, +}; +// QuicErrorCodes is encoded as a single octet on-the-wire. +static_assert(static_cast<int>(QUIC_LAST_ERROR) <= + std::numeric_limits<uint8_t>::max(), + "QuicErrorCode exceeds single octet"); + +// Returns the name of the QuicRstStreamErrorCode as a char* +QUIC_EXPORT_PRIVATE const char* QuicRstStreamErrorCodeToString( + QuicRstStreamErrorCode error); + +// Returns the name of the QuicErrorCode as a char* +QUIC_EXPORT const char* QuicErrorCodeToString(QuicErrorCode error); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_ERROR_CODES_H_
diff --git a/quic/core/quic_error_codes_test.cc b/quic/core/quic_error_codes_test.cc new file mode 100644 index 0000000..f9928bd --- /dev/null +++ b/quic/core/quic_error_codes_test.cc
@@ -0,0 +1,26 @@ +// Copyright (c) 2013 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/quic_error_codes.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QuicErrorCodesTest : public QuicTest {}; + +TEST_F(QuicErrorCodesTest, QuicRstStreamErrorCodeToString) { + EXPECT_STREQ("QUIC_BAD_APPLICATION_PAYLOAD", + QuicRstStreamErrorCodeToString(QUIC_BAD_APPLICATION_PAYLOAD)); +} + +TEST_F(QuicErrorCodesTest, QuicErrorCodeToString) { + EXPECT_STREQ("QUIC_NO_ERROR", QuicErrorCodeToString(QUIC_NO_ERROR)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_flow_controller.cc b/quic/core/quic_flow_controller.cc new file mode 100644 index 0000000..3fb5d75 --- /dev/null +++ b/quic/core/quic_flow_controller.cc
@@ -0,0 +1,308 @@ +// Copyright 2014 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/quic_flow_controller.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +#define ENDPOINT \ + (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") + +QuicFlowController::QuicFlowController( + QuicSession* session, + QuicStreamId id, + bool is_connection_flow_controller, + QuicStreamOffset send_window_offset, + QuicStreamOffset receive_window_offset, + QuicByteCount receive_window_size_limit, + bool should_auto_tune_receive_window, + QuicFlowControllerInterface* session_flow_controller) + : session_(session), + connection_(session->connection()), + id_(id), + is_connection_flow_controller_(is_connection_flow_controller), + perspective_(session->perspective()), + bytes_sent_(0), + send_window_offset_(send_window_offset), + bytes_consumed_(0), + highest_received_byte_offset_(0), + receive_window_offset_(receive_window_offset), + receive_window_size_(receive_window_offset), + receive_window_size_limit_(receive_window_size_limit), + auto_tune_receive_window_(should_auto_tune_receive_window), + session_flow_controller_(session_flow_controller), + last_blocked_send_window_offset_(0), + prev_window_update_time_(QuicTime::Zero()) { + DCHECK_LE(receive_window_size_, receive_window_size_limit_); + DCHECK_EQ(is_connection_flow_controller_, + QuicUtils::GetInvalidStreamId( + session_->connection()->transport_version()) == id_); + + QUIC_DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_ + << ", setting initial receive window offset to: " + << receive_window_offset_ + << ", max receive window to: " << receive_window_size_ + << ", max receive window limit to: " + << receive_window_size_limit_ + << ", setting send window offset to: " << send_window_offset_; +} + +void QuicFlowController::AddBytesConsumed(QuicByteCount bytes_consumed) { + bytes_consumed_ += bytes_consumed; + QUIC_DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed " + << bytes_consumed_ << " bytes."; + + MaybeSendWindowUpdate(); +} + +bool QuicFlowController::UpdateHighestReceivedOffset( + QuicStreamOffset new_offset) { + // Only update if offset has increased. + if (new_offset <= highest_received_byte_offset_) { + return false; + } + + QUIC_DVLOG(1) << ENDPOINT << "Stream " << id_ + << " highest byte offset increased from " + << highest_received_byte_offset_ << " to " << new_offset; + highest_received_byte_offset_ = new_offset; + return true; +} + +void QuicFlowController::AddBytesSent(QuicByteCount bytes_sent) { + if (bytes_sent_ + bytes_sent > send_window_offset_) { + QUIC_BUG << ENDPOINT << "Stream " << id_ << " Trying to send an extra " + << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_ + << ", and send_window_offset_ = " << send_window_offset_; + bytes_sent_ = send_window_offset_; + + // This is an error on our side, close the connection as soon as possible. + connection_->CloseConnection( + QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, + QuicStrCat(send_window_offset_ - (bytes_sent_ + bytes_sent), + "bytes over send window offset"), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + bytes_sent_ += bytes_sent; + QUIC_DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent " << bytes_sent_ + << " bytes."; +} + +bool QuicFlowController::FlowControlViolation() { + if (highest_received_byte_offset_ > receive_window_offset_) { + QUIC_DLOG(INFO) << ENDPOINT << "Flow control violation on stream " << id_ + << ", receive window offset: " << receive_window_offset_ + << ", highest received byte offset: " + << highest_received_byte_offset_; + return true; + } + return false; +} + +void QuicFlowController::MaybeIncreaseMaxWindowSize() { + // Core of receive window auto tuning. This method should be called before a + // WINDOW_UPDATE frame is sent. Ideally, window updates should occur close to + // once per RTT. If a window update happens much faster than RTT, it implies + // that the flow control window is imposing a bottleneck. To prevent this, + // this method will increase the receive window size (subject to a reasonable + // upper bound). For simplicity this algorithm is deliberately asymmetric, in + // that it may increase window size but never decreases. + + // Keep track of timing between successive window updates. + QuicTime now = connection_->clock()->ApproximateNow(); + QuicTime prev = prev_window_update_time_; + prev_window_update_time_ = now; + if (!prev.IsInitialized()) { + QUIC_DVLOG(1) << ENDPOINT << "first window update for stream " << id_; + return; + } + + if (!auto_tune_receive_window_) { + return; + } + + // Get outbound RTT. + QuicTime::Delta rtt = + connection_->sent_packet_manager().GetRttStats()->smoothed_rtt(); + if (rtt.IsZero()) { + QUIC_DVLOG(1) << ENDPOINT << "rtt zero for stream " << id_; + return; + } + + // Now we can compare timing of window updates with RTT. + QuicTime::Delta since_last = now - prev; + QuicTime::Delta two_rtt = 2 * rtt; + + if (since_last >= two_rtt) { + // If interval between window updates is sufficiently large, there + // is no need to increase receive_window_size_. + return; + } + QuicByteCount old_window = receive_window_size_; + IncreaseWindowSize(); + + if (receive_window_size_ > old_window) { + QUIC_DVLOG(1) << ENDPOINT << "New max window increase for stream " << id_ + << " after " << since_last.ToMicroseconds() + << " us, and RTT is " << rtt.ToMicroseconds() + << "us. max wndw: " << receive_window_size_; + if (session_flow_controller_ != nullptr) { + session_flow_controller_->EnsureWindowAtLeast( + kSessionFlowControlMultiplier * receive_window_size_); + } + } else { + // TODO(ckrasic) - add a varz to track this (?). + QUIC_LOG_FIRST_N(INFO, 1) + << ENDPOINT << "Max window at limit for stream " << id_ << " after " + << since_last.ToMicroseconds() << " us, and RTT is " + << rtt.ToMicroseconds() << "us. Limit size: " << receive_window_size_; + } +} + +void QuicFlowController::IncreaseWindowSize() { + receive_window_size_ *= 2; + receive_window_size_ = + std::min(receive_window_size_, receive_window_size_limit_); +} + +QuicByteCount QuicFlowController::WindowUpdateThreshold() { + return receive_window_size_ / 2; +} + +void QuicFlowController::MaybeSendWindowUpdate() { + // Send WindowUpdate to increase receive window if + // (receive window offset - consumed bytes) < (max window / 2). + // This is behaviour copied from SPDY. + DCHECK_LE(bytes_consumed_, receive_window_offset_); + QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_; + QuicByteCount threshold = WindowUpdateThreshold(); + + if (!prev_window_update_time_.IsInitialized()) { + // Treat the initial window as if it is a window update, so if 1/2 the + // window is used in less than 2 RTTs, the window is increased. + prev_window_update_time_ = connection_->clock()->ApproximateNow(); + } + + if (available_window >= threshold) { + QUIC_DVLOG(1) << ENDPOINT << "Not sending WindowUpdate for stream " << id_ + << ", available window: " << available_window + << " >= threshold: " << threshold; + return; + } + + MaybeIncreaseMaxWindowSize(); + UpdateReceiveWindowOffsetAndSendWindowUpdate(available_window); +} + +void QuicFlowController::UpdateReceiveWindowOffsetAndSendWindowUpdate( + QuicStreamOffset available_window) { + // Update our receive window. + receive_window_offset_ += (receive_window_size_ - available_window); + + QUIC_DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_ + << ", consumed bytes: " << bytes_consumed_ + << ", available window: " << available_window + << ", and threshold: " << WindowUpdateThreshold() + << ", and receive window size: " << receive_window_size_ + << ". New receive window offset is: " << receive_window_offset_; + + SendWindowUpdate(); +} + +bool QuicFlowController::ShouldSendBlocked() { + if (SendWindowSize() != 0 || + last_blocked_send_window_offset_ >= send_window_offset_) { + return false; + } + QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id_ + << " is flow control blocked. " + << "Send window: " << SendWindowSize() + << ", bytes sent: " << bytes_sent_ + << ", send limit: " << send_window_offset_; + // The entire send_window has been consumed, we are now flow control + // blocked. + + // Keep track of when we last sent a BLOCKED frame so that we only send one + // at a given send offset. + last_blocked_send_window_offset_ = send_window_offset_; + return true; +} + +bool QuicFlowController::UpdateSendWindowOffset( + QuicStreamOffset new_send_window_offset) { + // Only update if send window has increased. + if (new_send_window_offset <= send_window_offset_) { + return false; + } + + QUIC_DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_ + << " with new offset " << new_send_window_offset + << " current offset: " << send_window_offset_ + << " bytes_sent: " << bytes_sent_; + + // The flow is now unblocked but could have also been unblocked + // before. Return true iff this update caused a change from blocked + // to unblocked. + const bool was_previously_blocked = IsBlocked(); + send_window_offset_ = new_send_window_offset; + return was_previously_blocked; +} + +void QuicFlowController::EnsureWindowAtLeast(QuicByteCount window_size) { + if (receive_window_size_limit_ >= window_size) { + return; + } + + QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_; + IncreaseWindowSize(); + UpdateReceiveWindowOffsetAndSendWindowUpdate(available_window); +} + +bool QuicFlowController::IsBlocked() const { + return SendWindowSize() == 0; +} + +uint64_t QuicFlowController::SendWindowSize() const { + if (bytes_sent_ > send_window_offset_) { + return 0; + } + return send_window_offset_ - bytes_sent_; +} + +void QuicFlowController::UpdateReceiveWindowSize(QuicStreamOffset size) { + DCHECK_LE(size, receive_window_size_limit_); + QUIC_DVLOG(1) << ENDPOINT << "UpdateReceiveWindowSize for stream " << id_ + << ": " << size; + if (receive_window_size_ != receive_window_offset_) { + QUIC_BUG << "receive_window_size_:" << receive_window_size_ + << " != receive_window_offset:" << receive_window_offset_; + return; + } + receive_window_size_ = size; + receive_window_offset_ = size; +} + +void QuicFlowController::SendWindowUpdate() { + QuicStreamId id = id_; + if (is_connection_flow_controller_) { + id = QuicUtils::GetInvalidStreamId(connection_->transport_version()); + } + session_->SendWindowUpdate(id, receive_window_offset_); +} + +} // namespace quic
diff --git a/quic/core/quic_flow_controller.h b/quic/core/quic_flow_controller.h new file mode 100644 index 0000000..8643a7e --- /dev/null +++ b/quic/core/quic_flow_controller.h
@@ -0,0 +1,208 @@ +// Copyright 2014 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_QUIC_FLOW_CONTROLLER_H_ +#define QUICHE_QUIC_CORE_QUIC_FLOW_CONTROLLER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +namespace test { +class QuicFlowControllerPeer; +} // namespace test + +class QuicConnection; +class QuicSession; + +// How much larger the session flow control window needs to be relative to any +// stream's flow control window. +const float kSessionFlowControlMultiplier = 1.5; + +class QUIC_EXPORT_PRIVATE QuicFlowControllerInterface { + public: + virtual ~QuicFlowControllerInterface() {} + + // Ensures the flow control window is at least |window_size| and send out an + // update frame if it is increased. + virtual void EnsureWindowAtLeast(QuicByteCount window_size) = 0; +}; + +// QuicFlowController allows a QUIC stream or connection to perform flow +// control. The stream/connection owns a QuicFlowController which keeps track of +// bytes sent/received, can tell the owner if it is flow control blocked, and +// can send WINDOW_UPDATE or BLOCKED frames when needed. +class QUIC_EXPORT_PRIVATE QuicFlowController + : public QuicFlowControllerInterface { + public: + QuicFlowController(QuicSession* session, + QuicStreamId id, + bool is_connection_flow_controller, + QuicStreamOffset send_window_offset, + QuicStreamOffset receive_window_offset, + QuicByteCount receive_window_size_limit, + bool should_auto_tune_receive_window, + QuicFlowControllerInterface* session_flow_controller); + + QuicFlowController(const QuicFlowController&) = delete; + QuicFlowController(QuicFlowController&&) = default; + QuicFlowController& operator=(const QuicFlowController&) = delete; + + ~QuicFlowController() override {} + + // Called when we see a new highest received byte offset from the peer, either + // via a data frame or a RST. + // Returns true if this call changes highest_received_byte_offset_, and false + // in the case where |new_offset| is <= highest_received_byte_offset_. + bool UpdateHighestReceivedOffset(QuicStreamOffset new_offset); + + // Called when bytes received from the peer are consumed locally. This may + // trigger the sending of a WINDOW_UPDATE frame using |connection|. + void AddBytesConsumed(QuicByteCount bytes_consumed); + + // Called when bytes are sent to the peer. + void AddBytesSent(QuicByteCount bytes_sent); + + // Increases |send_window_offset_| if |new_send_window_offset| is + // greater than the current value. Returns true if this increase + // also causes us to change from a blocked state to unblocked. In + // all other cases, returns false. + bool UpdateSendWindowOffset(QuicStreamOffset new_send_window_offset); + + // QuicFlowControllerInterface. + void EnsureWindowAtLeast(QuicByteCount window_size) override; + + // Returns the current available send window. + QuicByteCount SendWindowSize() const; + + // Returns whether a BLOCKED frame should be sent. + bool ShouldSendBlocked(); + + // Returns true if flow control send limits have been reached. + bool IsBlocked() const; + + // Returns true if flow control receive limits have been violated by the peer. + bool FlowControlViolation(); + + // Inform the peer of new receive window. + void SendWindowUpdate(); + + QuicByteCount bytes_consumed() const { return bytes_consumed_; } + + QuicStreamOffset highest_received_byte_offset() const { + return highest_received_byte_offset_; + } + + void set_receive_window_size_limit(QuicByteCount receive_window_size_limit) { + DCHECK_GE(receive_window_size_limit, receive_window_size_limit_); + receive_window_size_limit_ = receive_window_size_limit; + } + + // Should only be called before any data is received. + void UpdateReceiveWindowSize(QuicStreamOffset size); + + bool auto_tune_receive_window() { return auto_tune_receive_window_; } + + private: + friend class test::QuicFlowControllerPeer; + + // Send a WINDOW_UPDATE frame if appropriate. + void MaybeSendWindowUpdate(); + + // Auto-tune the max receive window size. + void MaybeIncreaseMaxWindowSize(); + + // Updates the current offset and sends a window update frame. + void UpdateReceiveWindowOffsetAndSendWindowUpdate( + QuicStreamOffset available_window); + + // Double the window size as long as we haven't hit the max window size. + void IncreaseWindowSize(); + + // The parent session/connection, used to send connection close on flow + // control violation, and WINDOW_UPDATE and BLOCKED frames when appropriate. + // Not owned. + QuicSession* session_; + QuicConnection* connection_; + + // ID of stream this flow controller belongs to. If + // |is_connection_flow_controller_| is false, this must be a valid stream ID. + QuicStreamId id_; + + // Whether this flow controller is the connection level flow controller + // instead of the flow controller for a stream. If true, |id_| is ignored. + bool is_connection_flow_controller_; + + // Tracks if this is owned by a server or a client. + Perspective perspective_; + + // Tracks number of bytes sent to the peer. + QuicByteCount bytes_sent_; + + // The absolute offset in the outgoing byte stream. If this offset is reached + // then we become flow control blocked until we receive a WINDOW_UPDATE. + QuicStreamOffset send_window_offset_; + + // Overview of receive flow controller. + // + // 0=...===1=======2-------3 ...... FIN + // |<--- <= 4 --->| + // + + // 1) bytes_consumed_ - moves forward when data is read out of the + // stream. + // + // 2) highest_received_byte_offset_ - moves when data is received + // from the peer. + // + // 3) receive_window_offset_ - moves when WINDOW_UPDATE is sent. + // + // 4) receive_window_size_ - maximum allowed unread data (3 - 1). + // This value may be increased by auto-tuning. + // + // 5) receive_window_size_limit_ - limit on receive_window_size_; + // auto-tuning will not increase window size beyond this limit. + + // Track number of bytes received from the peer, which have been consumed + // locally. + QuicByteCount bytes_consumed_; + + // The highest byte offset we have seen from the peer. This could be the + // highest offset in a data frame, or a final value in a RST. + QuicStreamOffset highest_received_byte_offset_; + + // The absolute offset in the incoming byte stream. The peer should never send + // us bytes which are beyond this offset. + QuicStreamOffset receive_window_offset_; + + // Largest size the receive window can grow to. + QuicByteCount receive_window_size_; + + // Upper limit on receive_window_size_; + QuicByteCount receive_window_size_limit_; + + // Used to dynamically enable receive window auto-tuning. + bool auto_tune_receive_window_; + + // The session's flow controller. null if this is stream id 0. + // Not owned. + QuicFlowControllerInterface* session_flow_controller_; + + // Send window update when receive window size drops below this. + QuicByteCount WindowUpdateThreshold(); + + // Keep track of the last time we sent a BLOCKED frame. We should only send + // another when the number of bytes we have sent has changed. + QuicStreamOffset last_blocked_send_window_offset_; + + // Keep time of the last time a window update was sent. We use this + // as part of the receive window auto tuning. + QuicTime prev_window_update_time_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_FLOW_CONTROLLER_H_
diff --git a/quic/core/quic_flow_controller_test.cc b/quic/core/quic_flow_controller_test.cc new file mode 100644 index 0000000..771b2e2 --- /dev/null +++ b/quic/core/quic_flow_controller_test.cc
@@ -0,0 +1,408 @@ +// Copyright 2014 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/quic_flow_controller.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; + +namespace quic { +namespace test { + +// Receive window auto-tuning uses RTT in its logic. +const int64_t kRtt = 100; + +class MockFlowController : public QuicFlowControllerInterface { + public: + MockFlowController() {} + MockFlowController(const MockFlowController&) = delete; + MockFlowController& operator=(const MockFlowController&) = delete; + ~MockFlowController() override {} + + MOCK_METHOD1(EnsureWindowAtLeast, void(QuicByteCount)); +}; + +class QuicFlowControllerTest : public QuicTest { + public: + void Initialize() { + connection_ = new MockQuicConnection(&helper_, &alarm_factory_, + Perspective::IS_CLIENT); + session_ = QuicMakeUnique<MockQuicSession>(connection_); + flow_controller_ = QuicMakeUnique<QuicFlowController>( + session_.get(), stream_id_, /*is_connection_flow_controller*/ false, + send_window_, receive_window_, kStreamReceiveWindowLimit, + should_auto_tune_receive_window_, &session_flow_controller_); + } + + bool ClearControlFrame(const QuicFrame& frame) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + protected: + QuicStreamId stream_id_ = 1234; + QuicByteCount send_window_ = kInitialSessionFlowControlWindowForTest; + QuicByteCount receive_window_ = kInitialSessionFlowControlWindowForTest; + std::unique_ptr<QuicFlowController> flow_controller_; + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* connection_; + std::unique_ptr<MockQuicSession> session_; + MockFlowController session_flow_controller_; + bool should_auto_tune_receive_window_ = false; +}; + +TEST_F(QuicFlowControllerTest, SendingBytes) { + Initialize(); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Send some bytes, but not enough to block. + flow_controller_->AddBytesSent(send_window_ / 2); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize()); + + // Send enough bytes to block. + flow_controller_->AddBytesSent(send_window_ / 2); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); + + // BLOCKED frame should get sent. + EXPECT_TRUE(flow_controller_->ShouldSendBlocked()); + + // Update the send window, and verify this has unblocked. + EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_)); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Updating with a smaller offset doesn't change anything. + EXPECT_FALSE(flow_controller_->UpdateSendWindowOffset(send_window_ / 10)); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Try to send more bytes, violating flow control. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, _, _)); + EXPECT_QUIC_BUG( + flow_controller_->AddBytesSent(send_window_ * 10), + QuicStrCat("Trying to send an extra ", send_window_ * 10, " bytes")); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytes) { + Initialize(); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE( + flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ((receive_window_ / 2) - 1, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Consume enough bytes to send a WINDOW_UPDATE frame. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + + flow_controller_->AddBytesConsumed(1 + receive_window_ / 2); + + // Result is that once again we have a fully open receive window. + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); +} + +TEST_F(QuicFlowControllerTest, Move) { + Initialize(); + + flow_controller_->AddBytesSent(send_window_ / 2); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize()); + + EXPECT_TRUE( + flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ((receive_window_ / 2) - 1, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicFlowController flow_controller2(std::move(*flow_controller_)); + EXPECT_EQ(send_window_ / 2, flow_controller2.SendWindowSize()); + EXPECT_FALSE(flow_controller2.FlowControlViolation()); + EXPECT_EQ((receive_window_ / 2) - 1, + QuicFlowControllerPeer::ReceiveWindowSize(&flow_controller2)); +} + +TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) { + Initialize(); + + // Test that we don't send duplicate BLOCKED frames. We should only send one + // BLOCKED frame at a given send window offset. + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Send enough bytes to block. + flow_controller_->AddBytesSent(send_window_); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); + + // BLOCKED frame should get sent. + EXPECT_TRUE(flow_controller_->ShouldSendBlocked()); + + // BLOCKED frame should not get sent again until our send offset changes. + EXPECT_FALSE(flow_controller_->ShouldSendBlocked()); + EXPECT_FALSE(flow_controller_->ShouldSendBlocked()); + EXPECT_FALSE(flow_controller_->ShouldSendBlocked()); + EXPECT_FALSE(flow_controller_->ShouldSendBlocked()); + EXPECT_FALSE(flow_controller_->ShouldSendBlocked()); + + // Update the send window, then send enough bytes to block again. + EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_)); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + flow_controller_->AddBytesSent(send_window_); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); + + // BLOCKED frame should get sent as send offset has changed. + EXPECT_TRUE(flow_controller_->ShouldSendBlocked()); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytesFastIncreasesFlowWindow) { + should_auto_tune_receive_window_ = true; + Initialize(); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + EXPECT_TRUE(flow_controller_->auto_tune_receive_window()); + + // Make sure clock is inititialized. + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(connection_); + + RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + EXPECT_CALL( + session_flow_controller_, + EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5)); + + // Consume enough bytes to send a WINDOW_UPDATE frame. + flow_controller_->AddBytesConsumed(threshold + 1); + // Result is that once again we have a fully open receive window. + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(2 * kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt - 1)); + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + EXPECT_GT(new_threshold, threshold); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytesFastNoAutoTune) { + Initialize(); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(this, &QuicFlowControllerTest::ClearControlFrame)); + EXPECT_FALSE(flow_controller_->auto_tune_receive_window()); + + // Make sure clock is inititialized. + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(connection_); + + RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Consume enough bytes to send a WINDOW_UPDATE frame. + flow_controller_->AddBytesConsumed(threshold + 1); + // Result is that once again we have a fully open receive window. + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Move time forward, but by less than two RTTs. Then receive and consume + // some more, forcing a second WINDOW_UPDATE with an increased max window + // size. + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt - 1)); + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + EXPECT_EQ(new_threshold, threshold); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytesNormalStableFlowWindow) { + should_auto_tune_receive_window_ = true; + Initialize(); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + EXPECT_TRUE(flow_controller_->auto_tune_receive_window()); + + // Make sure clock is inititialized. + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(connection_); + RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + EXPECT_CALL( + session_flow_controller_, + EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5)); + flow_controller_->AddBytesConsumed(threshold + 1); + + // Result is that once again we have a fully open receive window. + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(2 * kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Move time forward, but by more than two RTTs. Then receive and consume + // some more, forcing a second WINDOW_UPDATE with unchanged max window size. + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt + 1)); + + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + EXPECT_EQ(new_threshold, 2 * threshold); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytesNormalNoAutoTune) { + Initialize(); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(this, &QuicFlowControllerTest::ClearControlFrame)); + EXPECT_FALSE(flow_controller_->auto_tune_receive_window()); + + // Make sure clock is inititialized. + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(connection_); + RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + flow_controller_->AddBytesConsumed(threshold + 1); + + // Result is that once again we have a fully open receive window. + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Move time forward, but by more than two RTTs. Then receive and consume + // some more, forcing a second WINDOW_UPDATE with unchanged max window size. + connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt + 1)); + + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + EXPECT_EQ(new_threshold, threshold); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc new file mode 100644 index 0000000..03cfcd6 --- /dev/null +++ b/quic/core/quic_framer.cc
@@ -0,0 +1,5694 @@ +// Copyright (c) 2012 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/quic_framer.h" + +#include <cstddef> +#include <cstdint> +#include <memory> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.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/null_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.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/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +namespace { + +#define ENDPOINT \ + (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") + +// How much to shift the timestamp in the IETF Ack frame. +// TODO(fkastenholz) when we get real IETF QUIC, need to get +// the currect shift from the transport parameters. +const int kIetfAckTimestampShift = 3; + +// Number of bits the packet number length bits are shifted from the right +// edge of the header. +const uint8_t kPublicHeaderSequenceNumberShift = 4; + +// There are two interpretations for the Frame Type byte in the QUIC protocol, +// resulting in two Frame Types: Special Frame Types and Regular Frame Types. +// +// Regular Frame Types use the Frame Type byte simply. Currently defined +// Regular Frame Types are: +// Padding : 0b 00000000 (0x00) +// ResetStream : 0b 00000001 (0x01) +// ConnectionClose : 0b 00000010 (0x02) +// GoAway : 0b 00000011 (0x03) +// WindowUpdate : 0b 00000100 (0x04) +// Blocked : 0b 00000101 (0x05) +// +// Special Frame Types encode both a Frame Type and corresponding flags +// all in the Frame Type byte. Currently defined Special Frame Types +// are: +// Stream : 0b 1xxxxxxx +// Ack : 0b 01xxxxxx +// +// Semantics of the flag bits above (the x bits) depends on the frame type. + +// Masks to determine if the frame type is a special use +// and for specific special frame types. +const uint8_t kQuicFrameTypeBrokenMask = 0xE0; // 0b 11100000 +const uint8_t kQuicFrameTypeSpecialMask = 0xC0; // 0b 11000000 +const uint8_t kQuicFrameTypeStreamMask = 0x80; +const uint8_t kQuicFrameTypeAckMask = 0x40; +static_assert(kQuicFrameTypeSpecialMask == + (kQuicFrameTypeStreamMask | kQuicFrameTypeAckMask), + "Invalid kQuicFrameTypeSpecialMask"); + +// The stream type format is 1FDOOOSS, where +// F is the fin bit. +// D is the data length bit (0 or 2 bytes). +// OO/OOO are the size of the offset. +// SS is the size of the stream ID. +// Note that the stream encoding can not be determined by inspection. It can +// be determined only by knowing the QUIC Version. +// Stream frame relative shifts and masks for interpreting the stream flags. +// StreamID may be 1, 2, 3, or 4 bytes. +const uint8_t kQuicStreamIdShift = 2; +const uint8_t kQuicStreamIDLengthMask = 0x03; + +// Offset may be 0, 2, 4, or 8 bytes. +const uint8_t kQuicStreamShift = 3; +const uint8_t kQuicStreamOffsetMask = 0x07; + +// Data length may be 0 or 2 bytes. +const uint8_t kQuicStreamDataLengthShift = 1; +const uint8_t kQuicStreamDataLengthMask = 0x01; + +// Fin bit may be set or not. +const uint8_t kQuicStreamFinShift = 1; +const uint8_t kQuicStreamFinMask = 0x01; + +// The format is 01M0LLOO, where +// M if set, there are multiple ack blocks in the frame. +// LL is the size of the largest ack field. +// OO is the size of the ack blocks offset field. +// packet number size shift used in AckFrames. +const uint8_t kQuicSequenceNumberLengthNumBits = 2; +const uint8_t kActBlockLengthOffset = 0; +const uint8_t kLargestAckedOffset = 2; + +// Acks may have only one ack block. +const uint8_t kQuicHasMultipleAckBlocksOffset = 5; + +// Timestamps are 4 bytes followed by 2 bytes. +const uint8_t kQuicNumTimestampsLength = 1; +const uint8_t kQuicFirstTimestampLength = 4; +const uint8_t kQuicTimestampLength = 2; +// Gaps between packet numbers are 1 byte. +const uint8_t kQuicTimestampPacketNumberGapLength = 1; + +// Maximum length of encoded error strings. +const int kMaxErrorStringLength = 256; + +const uint8_t kConnectionIdLengthAdjustment = 3; +const uint8_t kDestinationConnectionIdLengthMask = 0xF0; +const uint8_t kSourceConnectionIdLengthMask = 0x0F; + +// Returns the absolute value of the difference between |a| and |b|. +uint64_t Delta(uint64_t a, uint64_t b) { + // Since these are unsigned numbers, we can't just return abs(a - b) + if (a < b) { + return b - a; + } + return a - b; +} + +uint64_t ClosestTo(uint64_t target, uint64_t a, uint64_t b) { + return (Delta(target, a) < Delta(target, b)) ? a : b; +} + +uint64_t PacketNumberIntervalLength( + const QuicInterval<QuicPacketNumber>& interval) { + if (interval.Empty()) { + return 0u; + } + return interval.max() - interval.min(); +} + +QuicPacketNumberLength ReadSequenceNumberLength(uint8_t flags) { + switch (flags & PACKET_FLAGS_8BYTE_PACKET) { + case PACKET_FLAGS_8BYTE_PACKET: + return PACKET_6BYTE_PACKET_NUMBER; + case PACKET_FLAGS_4BYTE_PACKET: + return PACKET_4BYTE_PACKET_NUMBER; + case PACKET_FLAGS_2BYTE_PACKET: + return PACKET_2BYTE_PACKET_NUMBER; + case PACKET_FLAGS_1BYTE_PACKET: + return PACKET_1BYTE_PACKET_NUMBER; + default: + QUIC_BUG << "Unreachable case statement."; + return PACKET_6BYTE_PACKET_NUMBER; + } +} + +QuicPacketNumberLength ReadAckPacketNumberLength(QuicTransportVersion version, + uint8_t flags) { + switch (flags & PACKET_FLAGS_8BYTE_PACKET) { + case PACKET_FLAGS_8BYTE_PACKET: + return PACKET_6BYTE_PACKET_NUMBER; + case PACKET_FLAGS_4BYTE_PACKET: + return PACKET_4BYTE_PACKET_NUMBER; + case PACKET_FLAGS_2BYTE_PACKET: + return PACKET_2BYTE_PACKET_NUMBER; + case PACKET_FLAGS_1BYTE_PACKET: + return PACKET_1BYTE_PACKET_NUMBER; + default: + QUIC_BUG << "Unreachable case statement."; + return PACKET_6BYTE_PACKET_NUMBER; + } +} + +uint8_t PacketNumberLengthToOnWireValue( + QuicTransportVersion version, + QuicPacketNumberLength packet_number_length) { + if (version > QUIC_VERSION_44) { + return packet_number_length - 1; + } + switch (packet_number_length) { + case PACKET_1BYTE_PACKET_NUMBER: + return 0; + case PACKET_2BYTE_PACKET_NUMBER: + return 1; + case PACKET_4BYTE_PACKET_NUMBER: + return 2; + default: + QUIC_BUG << "Invalid packet number length."; + return 0; + } +} + +bool GetShortHeaderPacketNumberLength( + QuicTransportVersion version, + uint8_t type, + bool infer_packet_header_type_from_version, + QuicPacketNumberLength* packet_number_length) { + DCHECK(!(type & FLAGS_LONG_HEADER)); + const bool two_bits_packet_number_length = + infer_packet_header_type_from_version ? version > QUIC_VERSION_44 + : (type & FLAGS_FIXED_BIT); + if (two_bits_packet_number_length) { + *packet_number_length = + static_cast<QuicPacketNumberLength>((type & 0x03) + 1); + return true; + } + switch (type & 0x07) { + case 0: + *packet_number_length = PACKET_1BYTE_PACKET_NUMBER; + break; + case 1: + *packet_number_length = PACKET_2BYTE_PACKET_NUMBER; + break; + case 2: + *packet_number_length = PACKET_4BYTE_PACKET_NUMBER; + break; + default: + *packet_number_length = PACKET_6BYTE_PACKET_NUMBER; + return false; + } + return true; +} + +uint8_t LongHeaderTypeToOnWireValue(QuicTransportVersion version, + QuicLongHeaderType type) { + switch (type) { + case INITIAL: + return version > QUIC_VERSION_44 ? 0 : 0x7F; + case ZERO_RTT_PROTECTED: + return version > QUIC_VERSION_44 ? 1 << 4 : 0x7C; + case HANDSHAKE: + return version > QUIC_VERSION_44 ? 2 << 4 : 0x7D; + case RETRY: + return version > QUIC_VERSION_44 ? 3 << 4 : 0x7E; + case VERSION_NEGOTIATION: + return 0xF0; // Value does not matter + default: + QUIC_BUG << "Invalid long header type: " << type; + return 0xFF; + } +} + +bool GetLongHeaderType(QuicTransportVersion version, + uint8_t type, + QuicLongHeaderType* long_header_type) { + DCHECK((type & FLAGS_LONG_HEADER) && version != QUIC_VERSION_UNSUPPORTED); + if (version > QUIC_VERSION_44) { + switch ((type & 0x30) >> 4) { + case 0: + *long_header_type = INITIAL; + break; + case 1: + *long_header_type = ZERO_RTT_PROTECTED; + break; + case 2: + *long_header_type = HANDSHAKE; + break; + case 3: + *long_header_type = RETRY; + break; + default: + QUIC_BUG << "Unreachable statement"; + *long_header_type = VERSION_NEGOTIATION; + return false; + } + return true; + } + + switch (type & 0x7F) { + case 0x7F: + *long_header_type = INITIAL; + break; + case 0x7C: + *long_header_type = ZERO_RTT_PROTECTED; + break; + case 0x7D: + *long_header_type = HANDSHAKE; + break; + case 0x7E: + *long_header_type = RETRY; + break; + default: + // Invalid packet header type. Whether a packet is version negotiation is + // determined by the version field. + *long_header_type = INVALID_PACKET_TYPE; + return false; + } + return true; +} + +QuicPacketNumberLength GetLongHeaderPacketNumberLength( + QuicTransportVersion version, + uint8_t type) { + if (version > QUIC_VERSION_44) { + return static_cast<QuicPacketNumberLength>((type & 0x03) + 1); + } + return PACKET_4BYTE_PACKET_NUMBER; +} + +QuicStringPiece TruncateErrorString(QuicStringPiece error) { + if (error.length() <= kMaxErrorStringLength) { + return error; + } + return QuicStringPiece(error.data(), kMaxErrorStringLength); +} + +size_t TruncatedErrorStringSize(const QuicStringPiece& error) { + if (error.length() < kMaxErrorStringLength) { + return error.length(); + } + return kMaxErrorStringLength; +} + +uint8_t GetConnectionIdLengthValue(QuicConnectionIdLength length) { + if (length == 0) { + return 0; + } + return static_cast<uint8_t>(length - kConnectionIdLengthAdjustment); +} + +bool IsValidPacketNumberLength(QuicPacketNumberLength packet_number_length) { + size_t length = packet_number_length; + return length == 1 || length == 2 || length == 4 || length == 6 || + length == 8; +} + +bool IsValidFullPacketNumber(uint64_t full_packet_number, + QuicTransportVersion version) { + return full_packet_number > 0 || + (GetQuicRestartFlag(quic_uint64max_uninitialized_pn) && + version == QUIC_VERSION_99); +} + +// Convert a stream ID to a count of streams, for IETF QUIC/Version 99 only. +// There is no need to take into account whether the ID is for uni- or +// bi-directional streams, or whether it's server- or client- initiated. It +// always returns a valid count. +QuicStreamId StreamIdToCount(QuicTransportVersion version, + QuicStreamId stream_id) { + DCHECK_EQ(QUIC_VERSION_99, version); + if ((stream_id & 0x3) == 0) { + return (stream_id / QuicUtils::StreamIdDelta(version)); + } + return (stream_id / QuicUtils::StreamIdDelta(version)) + 1; +} + +// Returns the maximum value that a stream count may have, taking into account +// the fact that bidirectional, client initiated, streams have one fewer stream +// available than the others. This is because the old crypto streams, with ID == +// 0 are not included in the count. +// The version is not included in the call, nor does the method take the version +// into account, because this is called only from code used for IETF QUIC. +// TODO(fkastenholz): Remove this method and replace calls to it with direct +// references to kMaxQuicStreamIdCount when streamid 0 becomes a normal stream +// id. +QuicStreamId GetMaxStreamCount(bool unidirectional, Perspective perspective) { + if (!unidirectional && perspective == Perspective::IS_CLIENT) { + return kMaxQuicStreamId >> 2; + } + return (kMaxQuicStreamId >> 2) + 1; +} + +// Convert a stream count to the maximum stream ID for that count. +// Needs to know whether the resulting stream ID should be uni-directional, +// bi-directional, server-initiated, or client-initiated. +// Returns true if it works, false if not. The only error condition is that +// the stream_count is too big and it would generate a stream id that is larger +// than the implementation's maximum stream id value. +bool StreamCountToId(QuicStreamId stream_count, + bool unidirectional, + Perspective perspective, + QuicTransportVersion version, + QuicStreamId* generated_stream_id) { + DCHECK_EQ(QUIC_VERSION_99, version); + // TODO(fkastenholz): when the MAX_STREAMS and STREAMS_BLOCKED frames + // are connected all the way up to the stream_id_manager, handle count==0 + // properly (interpret it as "can open 0 streams") and the count being too + // large (close the connection). + if ((stream_count == 0) || + (stream_count > GetMaxStreamCount(unidirectional, perspective))) { + return false; + } + *generated_stream_id = + ((unidirectional) + ? QuicUtils::GetFirstUnidirectionalStreamId(version, perspective) + : QuicUtils::GetFirstBidirectionalStreamId(version, perspective)) + + ((stream_count - 1) * QuicUtils::StreamIdDelta(version)); + return true; +} + +bool AppendIetfConnectionIdsNew(bool version_flag, + QuicConnectionId destination_connection_id, + QuicConnectionId source_connection_id, + QuicDataWriter* writer) { + if (!version_flag) { + return writer->WriteConnectionId(destination_connection_id); + } + + // Compute connection ID length byte. + uint8_t dcil = GetConnectionIdLengthValue( + static_cast<QuicConnectionIdLength>(destination_connection_id.length())); + uint8_t scil = GetConnectionIdLengthValue( + static_cast<QuicConnectionIdLength>(source_connection_id.length())); + uint8_t connection_id_length = dcil << 4 | scil; + + return writer->WriteUInt8(connection_id_length) && + writer->WriteConnectionId(destination_connection_id) && + writer->WriteConnectionId(source_connection_id); +} + +enum class DroppedPacketReason { + // General errors + INVALID_PUBLIC_HEADER, + VERSION_MISMATCH, + // Version negotiation packet errors + INVALID_VERSION_NEGOTIATION_PACKET, + // Public reset packet errors, pre-v44 + INVALID_PUBLIC_RESET_PACKET, + // Data packet errors + INVALID_PACKET_NUMBER, + INVALID_DIVERSIFICATION_NONCE, + DECRYPTION_FAILURE, + NUM_REASONS, +}; + +void RecordDroppedPacketReason(DroppedPacketReason reason) { + QUIC_CLIENT_HISTOGRAM_ENUM("QuicDroppedPacketReason", reason, + DroppedPacketReason::NUM_REASONS, + "The reason a packet was not processed. Recorded " + "each time such a packet is dropped"); +} + +} // namespace + +QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, + QuicTime creation_time, + Perspective perspective, + uint8_t expected_connection_id_length) + : visitor_(nullptr), + error_(QUIC_NO_ERROR), + last_serialized_connection_id_(EmptyQuicConnectionId()), + last_version_label_(0), + version_(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED), + supported_versions_(supported_versions), + decrypter_level_(ENCRYPTION_NONE), + alternative_decrypter_level_(ENCRYPTION_NONE), + alternative_decrypter_latch_(false), + perspective_(perspective), + validate_flags_(true), + process_timestamps_(false), + creation_time_(creation_time), + last_timestamp_(QuicTime::Delta::Zero()), + first_sending_packet_number_(FirstSendingPacketNumber()), + data_producer_(nullptr), + infer_packet_header_type_from_version_(perspective == + Perspective::IS_CLIENT), + expected_connection_id_length_(expected_connection_id_length) { + DCHECK(!supported_versions.empty()); + version_ = supported_versions_[0]; + decrypter_ = QuicMakeUnique<NullDecrypter>(perspective); + encrypter_[ENCRYPTION_NONE] = QuicMakeUnique<NullEncrypter>(perspective); +} + +QuicFramer::~QuicFramer() {} + +// static +size_t QuicFramer::GetMinStreamFrameSize(QuicTransportVersion version, + QuicStreamId stream_id, + QuicStreamOffset offset, + bool last_frame_in_packet, + QuicPacketLength data_length) { + if (version == QUIC_VERSION_99) { + return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_id) + + (last_frame_in_packet + ? 0 + : QuicDataWriter::GetVarInt62Len(data_length)) + + (offset != 0 ? QuicDataWriter::GetVarInt62Len(offset) : 0); + } + return kQuicFrameTypeSize + GetStreamIdSize(stream_id) + + GetStreamOffsetSize(version, offset) + + (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize); +} + +// static +size_t QuicFramer::GetMinCryptoFrameSize(QuicStreamOffset offset, + QuicPacketLength data_length) { + return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(offset) + + QuicDataWriter::GetVarInt62Len(data_length); +} + +// static +size_t QuicFramer::GetMessageFrameSize(QuicTransportVersion version, + bool last_frame_in_packet, + QuicByteCount length) { + QUIC_BUG_IF(version <= QUIC_VERSION_44) + << "Try to serialize MESSAGE frame in " << version; + return kQuicFrameTypeSize + + (last_frame_in_packet ? 0 : QuicDataWriter::GetVarInt62Len(length)) + + length; +} + +// static +size_t QuicFramer::GetMinAckFrameSize( + QuicTransportVersion version, + QuicPacketNumberLength largest_observed_length) { + if (version == QUIC_VERSION_99) { + // The minimal ack frame consists of the following four fields: Largest + // Acknowledged, ACK Delay, ACK Block Count, and First ACK Block. Minimum + // size of each is 1 byte. + return kQuicFrameTypeSize + 4; + } + size_t min_size = kQuicFrameTypeSize + largest_observed_length + + kQuicDeltaTimeLargestObservedSize; + return min_size + kQuicNumTimestampsSize; +} + +// static +size_t QuicFramer::GetStopWaitingFrameSize( + QuicTransportVersion version, + QuicPacketNumberLength packet_number_length) { + size_t min_size = kQuicFrameTypeSize + packet_number_length; + return min_size; +} + +// static +size_t QuicFramer::GetRstStreamFrameSize(QuicTransportVersion version, + const QuicRstStreamFrame& frame) { + if (version == QUIC_VERSION_99) { + return QuicDataWriter::GetVarInt62Len(frame.stream_id) + + QuicDataWriter::GetVarInt62Len(frame.byte_offset) + + kQuicFrameTypeSize + kQuicIetfQuicErrorCodeSize; + } + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize + + kQuicErrorCodeSize; +} + +// static +size_t QuicFramer::GetMinConnectionCloseFrameSize( + QuicTransportVersion version, + const QuicConnectionCloseFrame& frame) { + if (version == QUIC_VERSION_99) { + return QuicDataWriter::GetVarInt62Len( + TruncatedErrorStringSize(frame.error_details)) + + QuicDataWriter::GetVarInt62Len(frame.frame_type) + + kQuicFrameTypeSize + kQuicIetfQuicErrorCodeSize; + } + return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize; +} + +// static +size_t QuicFramer::GetMinApplicationCloseFrameSize( + QuicTransportVersion version, + const QuicApplicationCloseFrame& frame) { + if (version != QUIC_VERSION_99) { + QUIC_BUG << "In version " << version + << " - not 99 - and tried to serialize ApplicationClose."; + } + return QuicDataWriter::GetVarInt62Len( + TruncatedErrorStringSize(frame.error_details)) + + kQuicFrameTypeSize + kQuicIetfQuicErrorCodeSize; +} + +// static +size_t QuicFramer::GetMinGoAwayFrameSize() { + return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize + + kQuicMaxStreamIdSize; +} + +// static +size_t QuicFramer::GetWindowUpdateFrameSize( + QuicTransportVersion version, + const QuicWindowUpdateFrame& frame) { + if (version != QUIC_VERSION_99) { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; + } + if (frame.stream_id == QuicUtils::GetInvalidStreamId(version)) { + // Frame would be a MAX DATA frame, which has only a Maximum Data field. + return kQuicFrameTypeSize + + QuicDataWriter::GetVarInt62Len(frame.byte_offset); + } + // Frame would be MAX STREAM DATA, has Maximum Stream Data and Stream ID + // fields. + return kQuicFrameTypeSize + + QuicDataWriter::GetVarInt62Len(frame.byte_offset) + + QuicDataWriter::GetVarInt62Len(frame.stream_id); +} + +// static +size_t QuicFramer::GetMaxStreamsFrameSize(QuicTransportVersion version, + const QuicMaxStreamIdFrame& frame) { + if (version != QUIC_VERSION_99) { + QUIC_BUG << "In version " << version + << " - not 99 - and tried to serialize MaxStreamId Frame."; + } + + // Convert from the stream id on which the connection is blocked to a count + QuicStreamId stream_count = StreamIdToCount(version, frame.max_stream_id); + + return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_count); +} + +// static +size_t QuicFramer::GetStreamsBlockedFrameSize( + QuicTransportVersion version, + const QuicStreamIdBlockedFrame& frame) { + if (version != QUIC_VERSION_99) { + QUIC_BUG << "In version " << version + << " - not 99 - and tried to serialize StreamIdBlocked Frame."; + } + + // Convert from the stream id on which the connection is blocked to a count + QuicStreamId stream_count = StreamIdToCount(version, frame.stream_id); + + return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_count); +} + +// static +size_t QuicFramer::GetBlockedFrameSize(QuicTransportVersion version, + const QuicBlockedFrame& frame) { + if (version != QUIC_VERSION_99) { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize; + } + if (frame.stream_id == QuicUtils::GetInvalidStreamId(version)) { + // return size of IETF QUIC Blocked frame + return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.offset); + } + // return size of IETF QUIC Stream Blocked frame. + return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.offset) + + QuicDataWriter::GetVarInt62Len(frame.stream_id); +} + +// static +size_t QuicFramer::GetStopSendingFrameSize(const QuicStopSendingFrame& frame) { + return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.stream_id) + + sizeof(QuicApplicationErrorCode); +} + +// static +size_t QuicFramer::GetPathChallengeFrameSize( + const QuicPathChallengeFrame& frame) { + return kQuicFrameTypeSize + sizeof(frame.data_buffer); +} + +// static +size_t QuicFramer::GetPathResponseFrameSize( + const QuicPathResponseFrame& frame) { + return kQuicFrameTypeSize + sizeof(frame.data_buffer); +} + +// static +size_t QuicFramer::GetRetransmittableControlFrameSize( + QuicTransportVersion version, + const QuicFrame& frame) { + switch (frame.type) { + case PING_FRAME: + // Ping has no payload. + return kQuicFrameTypeSize; + case RST_STREAM_FRAME: + return GetRstStreamFrameSize(version, *frame.rst_stream_frame); + case CONNECTION_CLOSE_FRAME: + return GetMinConnectionCloseFrameSize(version, + *frame.connection_close_frame) + + TruncatedErrorStringSize( + frame.connection_close_frame->error_details); + case GOAWAY_FRAME: + return GetMinGoAwayFrameSize() + + TruncatedErrorStringSize(frame.goaway_frame->reason_phrase); + case WINDOW_UPDATE_FRAME: + // For version 99, this could be either a MAX DATA or MAX STREAM DATA. + // GetWindowUpdateFrameSize figures this out and returns the correct + // length. + return GetWindowUpdateFrameSize(version, *frame.window_update_frame); + case BLOCKED_FRAME: + return GetBlockedFrameSize(version, *frame.blocked_frame); + case APPLICATION_CLOSE_FRAME: + return GetMinApplicationCloseFrameSize(version, + *frame.application_close_frame) + + TruncatedErrorStringSize( + frame.application_close_frame->error_details); + case NEW_CONNECTION_ID_FRAME: + return GetNewConnectionIdFrameSize(*frame.new_connection_id_frame); + case RETIRE_CONNECTION_ID_FRAME: + return GetRetireConnectionIdFrameSize(*frame.retire_connection_id_frame); + case NEW_TOKEN_FRAME: + return GetNewTokenFrameSize(*frame.new_token_frame); + case MAX_STREAM_ID_FRAME: + return GetMaxStreamsFrameSize(version, frame.max_stream_id_frame); + case STREAM_ID_BLOCKED_FRAME: + return GetStreamsBlockedFrameSize(version, frame.stream_id_blocked_frame); + case PATH_RESPONSE_FRAME: + return GetPathResponseFrameSize(*frame.path_response_frame); + case PATH_CHALLENGE_FRAME: + return GetPathChallengeFrameSize(*frame.path_challenge_frame); + case STOP_SENDING_FRAME: + return GetStopSendingFrameSize(*frame.stop_sending_frame); + + case STREAM_FRAME: + case ACK_FRAME: + case STOP_WAITING_FRAME: + case MTU_DISCOVERY_FRAME: + case PADDING_FRAME: + case MESSAGE_FRAME: + case CRYPTO_FRAME: + case NUM_FRAME_TYPES: + DCHECK(false); + return 0; + } + + // Not reachable, but some Chrome compilers can't figure that out. *sigh* + DCHECK(false); + return 0; +} + +// static +size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) { + // Sizes are 1 through 4 bytes. + for (int i = 1; i <= 4; ++i) { + stream_id >>= 8; + if (stream_id == 0) { + return i; + } + } + QUIC_BUG << "Failed to determine StreamIDSize."; + return 4; +} + +// static +size_t QuicFramer::GetStreamOffsetSize(QuicTransportVersion version, + QuicStreamOffset offset) { + // 0 is a special case. + if (offset == 0) { + return 0; + } + // 2 through 8 are the remaining sizes. + offset >>= 8; + for (int i = 2; i <= 8; ++i) { + offset >>= 8; + if (offset == 0) { + return i; + } + } + QUIC_BUG << "Failed to determine StreamOffsetSize."; + return 8; +} + +// static +size_t QuicFramer::GetNewConnectionIdFrameSize( + const QuicNewConnectionIdFrame& frame) { + return kQuicFrameTypeSize + + QuicDataWriter::GetVarInt62Len(frame.sequence_number) + + kConnectionIdLengthSize + frame.connection_id.length() + + sizeof(frame.stateless_reset_token); +} + +// static +size_t QuicFramer::GetRetireConnectionIdFrameSize( + const QuicRetireConnectionIdFrame& frame) { + return kQuicFrameTypeSize + + QuicDataWriter::GetVarInt62Len(frame.sequence_number); +} + +// static +size_t QuicFramer::GetNewTokenFrameSize(const QuicNewTokenFrame& frame) { + return kQuicFrameTypeSize + + QuicDataWriter::GetVarInt62Len(frame.token.length()) + + frame.token.length(); +} + +// TODO(nharper): Change this method to take a ParsedQuicVersion. +bool QuicFramer::IsSupportedTransportVersion( + const QuicTransportVersion version) const { + for (ParsedQuicVersion supported_version : supported_versions_) { + if (version == supported_version.transport_version) { + return true; + } + } + return false; +} + +bool QuicFramer::IsSupportedVersion(const ParsedQuicVersion version) const { + for (const ParsedQuicVersion& supported_version : supported_versions_) { + if (version == supported_version) { + return true; + } + } + return false; +} + +size_t QuicFramer::GetSerializedFrameLength( + const QuicFrame& frame, + size_t free_bytes, + bool first_frame, + bool last_frame, + QuicPacketNumberLength packet_number_length) { + // Prevent a rare crash reported in b/19458523. + if (frame.type == ACK_FRAME && frame.ack_frame == nullptr) { + QUIC_BUG << "Cannot compute the length of a null ack frame. free_bytes:" + << free_bytes << " first_frame:" << first_frame + << " last_frame:" << last_frame + << " seq num length:" << packet_number_length; + set_error(QUIC_INTERNAL_ERROR); + visitor_->OnError(this); + return 0; + } + if (frame.type == PADDING_FRAME) { + if (frame.padding_frame.num_padding_bytes == -1) { + // Full padding to the end of the packet. + return free_bytes; + } else { + // Lite padding. + return free_bytes < + static_cast<size_t>(frame.padding_frame.num_padding_bytes) + ? free_bytes + : frame.padding_frame.num_padding_bytes; + } + } + + size_t frame_len = + ComputeFrameLength(frame, last_frame, packet_number_length); + if (frame_len <= free_bytes) { + // Frame fits within packet. Note that acks may be truncated. + return frame_len; + } + // Only truncate the first frame in a packet, so if subsequent ones go + // over, stop including more frames. + if (!first_frame) { + return 0; + } + bool can_truncate = + frame.type == ACK_FRAME && + free_bytes >= GetMinAckFrameSize(version_.transport_version, + PACKET_6BYTE_PACKET_NUMBER); + if (can_truncate) { + // Truncate the frame so the packet will not exceed kMaxPacketSize. + // Note that we may not use every byte of the writer in this case. + QUIC_DLOG(INFO) << ENDPOINT + << "Truncating large frame, free bytes: " << free_bytes; + return free_bytes; + } + return 0; +} + +QuicFramer::AckFrameInfo::AckFrameInfo() + : max_block_length(0), first_block_length(0), num_ack_blocks(0) {} + +QuicFramer::AckFrameInfo::AckFrameInfo(const AckFrameInfo& other) = default; + +QuicFramer::AckFrameInfo::~AckFrameInfo() {} + +bool QuicFramer::WriteIetfLongHeaderLength(const QuicPacketHeader& header, + QuicDataWriter* writer, + size_t length_field_offset, + EncryptionLevel level) { + if (!QuicVersionHasLongHeaderLengths(transport_version()) || + !header.version_flag || length_field_offset == 0) { + return true; + } + if (writer->length() < length_field_offset || + writer->length() - length_field_offset < + kQuicDefaultLongHeaderLengthLength) { + set_detailed_error("Invalid length_field_offset."); + QUIC_BUG << "Invalid length_field_offset."; + return false; + } + size_t length_to_write = writer->length() - length_field_offset - + kQuicDefaultLongHeaderLengthLength; + // Add length of auth tag. + length_to_write = GetCiphertextSize(level, length_to_write); + + QuicDataWriter length_writer(writer->length() - length_field_offset, + writer->data() + length_field_offset); + if (!length_writer.WriteVarInt62(length_to_write, + kQuicDefaultLongHeaderLengthLength)) { + set_detailed_error("Failed to overwrite long header length."); + QUIC_BUG << "Failed to overwrite long header length."; + return false; + } + return true; +} + +size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames, + char* buffer, + size_t packet_length, + EncryptionLevel level) { + QuicDataWriter writer(packet_length, buffer); + size_t length_field_offset = 0; + if (!AppendPacketHeader(header, &writer, &length_field_offset)) { + QUIC_BUG << "AppendPacketHeader failed"; + return 0; + } + + if (transport_version() == QUIC_VERSION_99) { + if (AppendIetfFrames(frames, &writer) == 0) { + return 0; + } + if (!WriteIetfLongHeaderLength(header, &writer, length_field_offset, + level)) { + return 0; + } + return writer.length(); + } + // TODO(dschinazi) if we enable long header lengths before v99, we need to + // add support for fixing up lengths in QuicFramer::BuildDataPacket. + DCHECK(!QuicVersionHasLongHeaderLengths(transport_version())); + + size_t i = 0; + for (const QuicFrame& frame : frames) { + // Determine if we should write stream frame length in header. + const bool last_frame_in_packet = i == frames.size() - 1; + if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) { + QUIC_BUG << "AppendTypeByte failed"; + return 0; + } + + switch (frame.type) { + case PADDING_FRAME: + if (!AppendPaddingFrame(frame.padding_frame, &writer)) { + QUIC_BUG << "AppendPaddingFrame of " + << frame.padding_frame.num_padding_bytes << " failed"; + return 0; + } + break; + case STREAM_FRAME: + if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet, + &writer)) { + QUIC_BUG << "AppendStreamFrame failed"; + return 0; + } + break; + case ACK_FRAME: + if (!AppendAckFrameAndTypeByte(*frame.ack_frame, &writer)) { + QUIC_BUG << "AppendAckFrameAndTypeByte failed: " << detailed_error_; + return 0; + } + break; + case STOP_WAITING_FRAME: + if (!AppendStopWaitingFrame(header, frame.stop_waiting_frame, + &writer)) { + QUIC_BUG << "AppendStopWaitingFrame failed"; + return 0; + } + break; + case MTU_DISCOVERY_FRAME: + // MTU discovery frames are serialized as ping frames. + QUIC_FALLTHROUGH_INTENDED; + case PING_FRAME: + // Ping has no payload. + break; + case RST_STREAM_FRAME: + if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) { + QUIC_BUG << "AppendRstStreamFrame failed"; + return 0; + } + break; + case CONNECTION_CLOSE_FRAME: + if (!AppendConnectionCloseFrame(*frame.connection_close_frame, + &writer)) { + QUIC_BUG << "AppendConnectionCloseFrame failed"; + return 0; + } + break; + case GOAWAY_FRAME: + if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) { + QUIC_BUG << "AppendGoAwayFrame failed"; + return 0; + } + break; + case WINDOW_UPDATE_FRAME: + if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) { + QUIC_BUG << "AppendWindowUpdateFrame failed"; + return 0; + } + break; + case BLOCKED_FRAME: + if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) { + QUIC_BUG << "AppendBlockedFrame failed"; + return 0; + } + break; + case APPLICATION_CLOSE_FRAME: + set_detailed_error( + "Attempt to append APPLICATION_CLOSE frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case NEW_CONNECTION_ID_FRAME: + set_detailed_error( + "Attempt to append NEW_CONNECTION_ID frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case RETIRE_CONNECTION_ID_FRAME: + set_detailed_error( + "Attempt to append RETIRE_CONNECTION_ID frame and not in version " + "99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case NEW_TOKEN_FRAME: + set_detailed_error( + "Attempt to append NEW_TOKEN_ID frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case MAX_STREAM_ID_FRAME: + set_detailed_error( + "Attempt to append MAX_STREAM_ID frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case STREAM_ID_BLOCKED_FRAME: + set_detailed_error( + "Attempt to append STREAM_ID_BLOCKED frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case PATH_RESPONSE_FRAME: + set_detailed_error( + "Attempt to append PATH_RESPONSE frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case PATH_CHALLENGE_FRAME: + set_detailed_error( + "Attempt to append PATH_CHALLENGE frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case STOP_SENDING_FRAME: + set_detailed_error( + "Attempt to append STOP_SENDING frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case MESSAGE_FRAME: + if (!AppendMessageFrameAndTypeByte(*frame.message_frame, + last_frame_in_packet, &writer)) { + QUIC_BUG << "AppendMessageFrame failed"; + return 0; + } + break; + case CRYPTO_FRAME: + if (version_.transport_version < QUIC_VERSION_47) { + set_detailed_error( + "Attempt to append CRYPTO frame in version prior to 47."); + return RaiseError(QUIC_INTERNAL_ERROR); + } + if (!AppendCryptoFrame(*frame.crypto_frame, &writer)) { + QUIC_BUG << "AppendCryptoFrame failed"; + return 0; + } + break; + default: + RaiseError(QUIC_INVALID_FRAME_DATA); + QUIC_BUG << "QUIC_INVALID_FRAME_DATA"; + return 0; + } + ++i; + } + + return writer.length(); +} + +size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, + QuicDataWriter* writer) { + size_t i = 0; + for (const QuicFrame& frame : frames) { + // Determine if we should write stream frame length in header. + const bool last_frame_in_packet = i == frames.size() - 1; + if (!AppendIetfTypeByte(frame, last_frame_in_packet, writer)) { + QUIC_BUG << "AppendIetfTypeByte failed: " << detailed_error(); + return 0; + } + + switch (frame.type) { + case PADDING_FRAME: + if (!AppendPaddingFrame(frame.padding_frame, writer)) { + QUIC_BUG << "AppendPaddingFrame of " + << frame.padding_frame.num_padding_bytes + << " failed: " << detailed_error(); + return 0; + } + break; + case STREAM_FRAME: + if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet, + writer)) { + QUIC_BUG << "AppendStreamFrame failed: " << detailed_error(); + return 0; + } + break; + case ACK_FRAME: + if (!AppendIetfAckFrameAndTypeByte(*frame.ack_frame, writer)) { + QUIC_BUG << "AppendAckFrameAndTypeByte failed: " << detailed_error(); + return 0; + } + break; + case STOP_WAITING_FRAME: + set_detailed_error( + "Attempt to append STOP WAITING frame in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case MTU_DISCOVERY_FRAME: + // MTU discovery frames are serialized as ping frames. + QUIC_FALLTHROUGH_INTENDED; + case PING_FRAME: + // Ping has no payload. + break; + case RST_STREAM_FRAME: + if (!AppendRstStreamFrame(*frame.rst_stream_frame, writer)) { + QUIC_BUG << "AppendRstStreamFrame failed: " << detailed_error(); + return 0; + } + break; + case CONNECTION_CLOSE_FRAME: + if (!AppendConnectionCloseFrame(*frame.connection_close_frame, + writer)) { + QUIC_BUG << "AppendConnectionCloseFrame failed: " << detailed_error(); + return 0; + } + break; + case GOAWAY_FRAME: + set_detailed_error("Attempt to append GOAWAY frame in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case WINDOW_UPDATE_FRAME: + // Depending on whether there is a stream ID or not, will be either a + // MAX STREAM DATA frame or a MAX DATA frame. + if (frame.window_update_frame->stream_id == + QuicUtils::GetInvalidStreamId(transport_version())) { + if (!AppendMaxDataFrame(*frame.window_update_frame, writer)) { + QUIC_BUG << "AppendMaxDataFrame failed: " << detailed_error(); + return 0; + } + } else { + if (!AppendMaxStreamDataFrame(*frame.window_update_frame, writer)) { + QUIC_BUG << "AppendMaxStreamDataFrame failed: " << detailed_error(); + return 0; + } + } + break; + case BLOCKED_FRAME: + if (!AppendBlockedFrame(*frame.blocked_frame, writer)) { + QUIC_BUG << "AppendBlockedFrame failed: " << detailed_error(); + return 0; + } + break; + case APPLICATION_CLOSE_FRAME: + if (!AppendApplicationCloseFrame(*frame.application_close_frame, + writer)) { + QUIC_BUG << "AppendApplicationCloseFrame failed: " + << detailed_error(); + return 0; + } + break; + case MAX_STREAM_ID_FRAME: + if (!AppendMaxStreamsFrame(frame.max_stream_id_frame, writer)) { + QUIC_BUG << "AppendMaxStreamsFrame failed" << detailed_error(); + return 0; + } + break; + case STREAM_ID_BLOCKED_FRAME: + if (!AppendStreamsBlockedFrame(frame.stream_id_blocked_frame, writer)) { + QUIC_BUG << "AppendStreamsBlockedFrame failed" << detailed_error(); + return 0; + } + break; + case NEW_CONNECTION_ID_FRAME: + if (!AppendNewConnectionIdFrame(*frame.new_connection_id_frame, + writer)) { + QUIC_BUG << "AppendNewConnectionIdFrame failed: " << detailed_error(); + return 0; + } + break; + case RETIRE_CONNECTION_ID_FRAME: + if (!AppendRetireConnectionIdFrame(*frame.retire_connection_id_frame, + writer)) { + QUIC_BUG << "AppendRetireConnectionIdFrame failed: " + << detailed_error(); + return 0; + } + break; + case NEW_TOKEN_FRAME: + if (!AppendNewTokenFrame(*frame.new_token_frame, writer)) { + QUIC_BUG << "AppendNewTokenFrame failed: " << detailed_error(); + return 0; + } + break; + case STOP_SENDING_FRAME: + if (!AppendStopSendingFrame(*frame.stop_sending_frame, writer)) { + QUIC_BUG << "AppendStopSendingFrame failed: " << detailed_error(); + return 0; + } + break; + case PATH_CHALLENGE_FRAME: + if (!AppendPathChallengeFrame(*frame.path_challenge_frame, writer)) { + QUIC_BUG << "AppendPathChallengeFrame failed: " << detailed_error(); + return 0; + } + break; + case PATH_RESPONSE_FRAME: + if (!AppendPathResponseFrame(*frame.path_response_frame, writer)) { + QUIC_BUG << "AppendPathResponseFrame failed: " << detailed_error(); + return 0; + } + break; + case MESSAGE_FRAME: + if (!AppendMessageFrameAndTypeByte(*frame.message_frame, + last_frame_in_packet, writer)) { + QUIC_BUG << "AppendMessageFrame failed: " << detailed_error(); + return 0; + } + break; + case CRYPTO_FRAME: + if (!AppendCryptoFrame(*frame.crypto_frame, writer)) { + QUIC_BUG << "AppendCryptoFrame failed: " << detailed_error(); + return 0; + } + break; + default: + RaiseError(QUIC_INVALID_FRAME_DATA); + set_detailed_error("Tried to append unknown frame type."); + QUIC_BUG << "QUIC_INVALID_FRAME_DATA"; + return 0; + } + ++i; + } + + return writer->length(); +} + +size_t QuicFramer::BuildConnectivityProbingPacketNew( + const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + EncryptionLevel level) { + QuicFrames frames; + + // Write a PING frame, which has no data payload. + QuicPingFrame ping_frame; + frames.push_back(QuicFrame(ping_frame)); + + // Add padding to the rest of the packet. + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(padding_frame)); + + return BuildDataPacket(header, frames, buffer, packet_length, level); +} + +size_t QuicFramer::BuildConnectivityProbingPacket( + const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + EncryptionLevel level) { + if (transport_version() == QUIC_VERSION_99 || + QuicVersionHasLongHeaderLengths(transport_version()) || + GetQuicReloadableFlag(quic_simplify_build_connectivity_probing_packet)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_simplify_build_connectivity_probing_packet); + // TODO(rch): Remove this method when the flag is deprecated. + return BuildConnectivityProbingPacketNew(header, buffer, packet_length, + level); + } + + QuicDataWriter writer(packet_length, buffer); + + if (!AppendPacketHeader(header, &writer, nullptr)) { + QUIC_BUG << "AppendPacketHeader failed"; + return 0; + } + + // Write a PING frame, which has no data payload. + QuicPingFrame ping_frame; + if (!AppendTypeByte(QuicFrame(ping_frame), false, &writer)) { + QUIC_BUG << "AppendTypeByte failed for ping frame in probing packet"; + return 0; + } + // Add padding to the rest of the packet. + QuicPaddingFrame padding_frame; + if (!AppendTypeByte(QuicFrame(padding_frame), true, &writer)) { + QUIC_BUG << "AppendTypeByte failed for padding frame in probing packet"; + return 0; + } + if (!AppendPaddingFrame(padding_frame, &writer)) { + QUIC_BUG << "AppendPaddingFrame of " << padding_frame.num_padding_bytes + << " failed"; + return 0; + } + + return writer.length(); +} + +size_t QuicFramer::BuildPaddedPathChallengePacket( + const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + QuicPathFrameBuffer* payload, + QuicRandom* randomizer, + EncryptionLevel level) { + if (version_.transport_version != QUIC_VERSION_99) { + QUIC_BUG << "Attempt to build a PATH_CHALLENGE Connectivity Probing " + "packet and not doing IETF QUIC"; + return 0; + } + QuicFrames frames; + + // Write a PATH_CHALLENGE frame, which has a random 8-byte payload + randomizer->RandBytes(payload->data(), payload->size()); + + QuicPathChallengeFrame path_challenge_frame(0, *payload); + frames.push_back(QuicFrame(&path_challenge_frame)); + + // Add padding to the rest of the packet in order to assess Path MTU + // characteristics. + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(padding_frame)); + + return BuildDataPacket(header, frames, buffer, packet_length, level); +} + +size_t QuicFramer::BuildPathResponsePacket( + const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded, + EncryptionLevel level) { + if (payloads.empty()) { + QUIC_BUG + << "Attempt to generate connectivity response with no request payloads"; + return 0; + } + if (version_.transport_version != QUIC_VERSION_99) { + QUIC_BUG << "Attempt to build a PATH_RESPONSE Connectivity Probing " + "packet and not doing IETF QUIC"; + return 0; + } + + std::vector<std::unique_ptr<QuicPathResponseFrame>> path_response_frames; + for (const QuicPathFrameBuffer& payload : payloads) { + // Note that the control frame ID can be 0 since this is not retransmitted. + path_response_frames.push_back( + QuicMakeUnique<QuicPathResponseFrame>(0, payload)); + } + + QuicFrames frames; + for (const std::unique_ptr<QuicPathResponseFrame>& path_response_frame : + path_response_frames) { + frames.push_back(QuicFrame(path_response_frame.get())); + } + + if (is_padded) { + // Add padding to the rest of the packet in order to assess Path MTU + // characteristics. + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(padding_frame)); + } + + return BuildDataPacket(header, frames, buffer, packet_length, level); +} + +// static +std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildPublicResetPacket( + const QuicPublicResetPacket& packet) { + CryptoHandshakeMessage reset; + reset.set_tag(kPRST); + reset.SetValue(kRNON, packet.nonce_proof); + if (packet.client_address.host().address_family() != + IpAddressFamily::IP_UNSPEC) { + // packet.client_address is non-empty. + QuicSocketAddressCoder address_coder(packet.client_address); + QuicString serialized_address = address_coder.Encode(); + if (serialized_address.empty()) { + return nullptr; + } + reset.SetStringPiece(kCADR, serialized_address); + } + if (!packet.endpoint_id.empty()) { + reset.SetStringPiece(kEPID, packet.endpoint_id); + } + const QuicData& reset_serialized = reset.GetSerialized(); + + size_t len = kPublicFlagsSize + packet.connection_id.length() + + reset_serialized.length(); + std::unique_ptr<char[]> buffer(new char[len]); + // Endianness is not a concern here, as writer is not going to write integers + // or floating numbers. + QuicDataWriter writer(len, buffer.get()); + + uint8_t flags = static_cast<uint8_t>(PACKET_PUBLIC_FLAGS_RST | + PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID); + // This hack makes post-v33 public reset packet look like pre-v33 packets. + flags |= static_cast<uint8_t>(PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD); + if (!writer.WriteUInt8(flags)) { + return nullptr; + } + + if (!writer.WriteConnectionId(packet.connection_id)) { + return nullptr; + } + + if (!writer.WriteBytes(reset_serialized.data(), reset_serialized.length())) { + return nullptr; + } + + return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true); +} + +// static +std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( + QuicConnectionId connection_id, + QuicUint128 stateless_reset_token) { + QUIC_DVLOG(1) << "Building IETF stateless reset packet."; + size_t len = kPacketHeaderTypeSize + kMinRandomBytesLengthInStatelessReset + + sizeof(stateless_reset_token); + std::unique_ptr<char[]> buffer(new char[len]); + QuicDataWriter writer(len, buffer.get()); + + uint8_t type = 0; + type |= FLAGS_FIXED_BIT; + type |= FLAGS_SHORT_HEADER_RESERVED_1; + type |= FLAGS_SHORT_HEADER_RESERVED_2; + type |= PacketNumberLengthToOnWireValue(QUIC_VERSION_UNSUPPORTED, + PACKET_1BYTE_PACKET_NUMBER); + + // Append type byte. + if (!writer.WriteUInt8(type)) { + return nullptr; + } + // Append random bytes. + if (!writer.WriteRandomBytes(QuicRandom::GetInstance(), + kMinRandomBytesLengthInStatelessReset)) { + return nullptr; + } + + // Append stateless reset token. + if (!writer.WriteBytes(&stateless_reset_token, + sizeof(stateless_reset_token))) { + return nullptr; + } + return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true); +} + +// static +std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildVersionNegotiationPacket( + QuicConnectionId connection_id, + bool ietf_quic, + const ParsedQuicVersionVector& versions) { + if (ietf_quic) { + return BuildIetfVersionNegotiationPacket(connection_id, versions); + } + DCHECK(!versions.empty()); + size_t len = kPublicFlagsSize + connection_id.length() + + versions.size() * kQuicVersionSize; + std::unique_ptr<char[]> buffer(new char[len]); + // Endianness is not a concern here, version negotiation packet does not have + // integers or floating numbers. + QuicDataWriter writer(len, buffer.get()); + + uint8_t flags = static_cast<uint8_t>( + PACKET_PUBLIC_FLAGS_VERSION | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID | + // TODO(rch): Remove this QUIC_VERSION_32 is retired. + PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD); + if (!writer.WriteUInt8(flags)) { + return nullptr; + } + + if (!writer.WriteConnectionId(connection_id)) { + return nullptr; + } + + for (const ParsedQuicVersion& version : versions) { + // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed. + if (!writer.WriteTag( + QuicEndian::HostToNet32(CreateQuicVersionLabel(version)))) { + return nullptr; + } + } + + return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true); +} + +// static +std::unique_ptr<QuicEncryptedPacket> +QuicFramer::BuildIetfVersionNegotiationPacket( + QuicConnectionId connection_id, + const ParsedQuicVersionVector& versions) { + QUIC_DVLOG(1) << "Building IETF version negotiation packet."; + DCHECK(!versions.empty()); + size_t len = kPacketHeaderTypeSize + kConnectionIdLengthSize + + connection_id.length() + + (versions.size() + 1) * kQuicVersionSize; + std::unique_ptr<char[]> buffer(new char[len]); + QuicDataWriter writer(len, buffer.get()); + + // TODO(fayang): Randomly select a value for the type. + uint8_t type = static_cast<uint8_t>(FLAGS_LONG_HEADER | VERSION_NEGOTIATION); + if (!writer.WriteUInt8(type)) { + return nullptr; + } + + if (!writer.WriteUInt32(0)) { + return nullptr; + } + + if (!GetQuicReloadableFlag(quic_use_new_append_connection_id)) { + if (!AppendIetfConnectionId(true, EmptyQuicConnectionId(), + PACKET_0BYTE_CONNECTION_ID, connection_id, + PACKET_8BYTE_CONNECTION_ID, &writer)) { + return nullptr; + } + } else { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_new_append_connection_id, 1, 2); + if (!AppendIetfConnectionIdsNew(true, EmptyQuicConnectionId(), + connection_id, &writer)) { + return nullptr; + } + } + + for (const ParsedQuicVersion& version : versions) { + // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed. + if (!writer.WriteTag( + QuicEndian::HostToNet32(CreateQuicVersionLabel(version)))) { + return nullptr; + } + } + + return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true); +} + +bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { + QuicDataReader reader(packet.data(), packet.length()); + + bool packet_has_ietf_packet_header = false; + if (infer_packet_header_type_from_version_) { + packet_has_ietf_packet_header = + version_.transport_version > QUIC_VERSION_43; + } else if (!reader.IsDoneReading()) { + uint8_t type = reader.PeekByte(); + packet_has_ietf_packet_header = QuicUtils::IsIetfPacketHeader(type); + } + if (packet_has_ietf_packet_header) { + QUIC_DVLOG(1) << ENDPOINT << "Processing IETF QUIC packet."; + } + + visitor_->OnPacket(); + + QuicPacketHeader header; + if (!ProcessPublicHeader(&reader, packet_has_ietf_packet_header, &header)) { + DCHECK_NE("", detailed_error_); + QUIC_DVLOG(1) << ENDPOINT << "Unable to process public header. Error: " + << detailed_error_; + DCHECK_NE("", detailed_error_); + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_HEADER); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + + if (!visitor_->OnUnauthenticatedPublicHeader(header)) { + // The visitor suppresses further processing of the packet. + return true; + } + + if (perspective_ == Perspective::IS_SERVER && header.version_flag && + header.version != version_) { + if (!visitor_->OnProtocolVersionMismatch(header.version, header.form)) { + RecordDroppedPacketReason(DroppedPacketReason::VERSION_MISMATCH); + return true; + } + } + + bool rv; + if (IsVersionNegotiation(header, packet_has_ietf_packet_header)) { + QUIC_DVLOG(1) << ENDPOINT << "Received version negotiation packet"; + rv = ProcessVersionNegotiationPacket(&reader, header); + } else if (header.reset_flag) { + rv = ProcessPublicResetPacket(&reader, header); + } else if (packet.length() <= kMaxPacketSize) { + // The optimized decryption algorithm implementations run faster when + // operating on aligned memory. + QUIC_CACHELINE_ALIGNED char buffer[kMaxPacketSize]; + if (packet_has_ietf_packet_header) { + rv = ProcessIetfDataPacket(&reader, &header, packet, buffer, + kMaxPacketSize); + } else { + rv = ProcessDataPacket(&reader, &header, packet, buffer, kMaxPacketSize); + } + } else { + std::unique_ptr<char[]> large_buffer(new char[packet.length()]); + if (packet_has_ietf_packet_header) { + rv = ProcessIetfDataPacket(&reader, &header, packet, large_buffer.get(), + packet.length()); + } else { + rv = ProcessDataPacket(&reader, &header, packet, large_buffer.get(), + packet.length()); + } + QUIC_BUG_IF(rv) << "QUIC should never successfully process packets larger" + << "than kMaxPacketSize. packet size:" << packet.length(); + } + return rv; +} + +bool QuicFramer::ProcessVersionNegotiationPacket( + QuicDataReader* reader, + const QuicPacketHeader& header) { + DCHECK_EQ(Perspective::IS_CLIENT, perspective_); + + QuicVersionNegotiationPacket packet(header.destination_connection_id); + // Try reading at least once to raise error if the packet is invalid. + do { + QuicVersionLabel version_label; + if (!reader->ReadTag(&version_label)) { + set_detailed_error("Unable to read supported version in negotiation."); + RecordDroppedPacketReason( + DroppedPacketReason::INVALID_VERSION_NEGOTIATION_PACKET); + return RaiseError(QUIC_INVALID_VERSION_NEGOTIATION_PACKET); + } + // TODO(rch): Use ReadUInt32() once QUIC_VERSION_35 is removed. + version_label = QuicEndian::NetToHost32(version_label); + packet.versions.push_back(ParseQuicVersionLabel(version_label)); + } while (!reader->IsDoneReading()); + + visitor_->OnVersionNegotiationPacket(packet); + return true; +} + +bool QuicFramer::MaybeProcessIetfInitialRetryToken( + QuicDataReader* encrypted_reader, + QuicPacketHeader* header) { + if (!QuicVersionHasLongHeaderLengths(header->version.transport_version) || + header->form != IETF_QUIC_LONG_HEADER_PACKET || + header->long_packet_type != INITIAL) { + return true; + } + uint64_t retry_token_length = 0; + header->retry_token_length_length = encrypted_reader->PeekVarInt62Length(); + if (!encrypted_reader->ReadVarInt62(&retry_token_length)) { + set_detailed_error("Unable to read INITIAL retry token length."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + header->retry_token = encrypted_reader->PeekRemainingPayload(); + // Safety check to avoid spending ressources if malformed. + // At this point header->retry_token contains the rest of the packet + // so its length() is the amount of data remaining in the packet. + if (retry_token_length > header->retry_token.length()) { + set_detailed_error("INITIAL token length longer than packet."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + // Resize retry_token to make it only contain the retry token. + header->retry_token.remove_suffix(header->retry_token.length() - + retry_token_length); + // Advance encrypted_reader by retry_token_length. + uint8_t wasted_byte; + for (uint64_t i = 0; i < retry_token_length; ++i) { + if (!encrypted_reader->ReadUInt8(&wasted_byte)) { + set_detailed_error("Unable to read INITIAL retry token."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + } + return true; +} + +// Seeks the current packet to check for a coalesced packet at the end. +// If the IETF length field only spans part of the outer packet, +// then there is a coalesced packet after this one. +void QuicFramer::MaybeProcessCoalescedPacket( + const QuicDataReader& encrypted_reader, + uint64_t remaining_bytes_length, + const QuicPacketHeader& header) { + if (header.remaining_packet_length >= remaining_bytes_length) { + // There is no coalesced packet. + return; + } + + QuicStringPiece remaining_data = encrypted_reader.PeekRemainingPayload(); + DCHECK_EQ(remaining_data.length(), remaining_bytes_length); + + const char* coalesced_data = + remaining_data.data() + header.remaining_packet_length; + uint64_t coalesced_data_length = + remaining_bytes_length - header.remaining_packet_length; + QuicDataReader coalesced_reader(coalesced_data, coalesced_data_length); + + QuicPacketHeader coalesced_header; + if (!ProcessIetfPacketHeader(&coalesced_reader, &coalesced_header)) { + QUIC_PEER_BUG << ENDPOINT + << "Failed to parse received coalesced header of length " + << coalesced_data_length << ": " + << QuicTextUtils::HexEncode(coalesced_data, + coalesced_data_length) + << " previous header was " << header; + return; + } + + if (coalesced_header.destination_connection_id != + header.destination_connection_id || + (coalesced_header.form != IETF_QUIC_SHORT_HEADER_PACKET && + coalesced_header.version != header.version)) { + QUIC_PEER_BUG << ENDPOINT << "Received mismatched coalesced header " + << coalesced_header << " previous header was " << header; + return; + } + + QuicEncryptedPacket coalesced_packet(coalesced_data, coalesced_data_length, + /*owns_buffer=*/false); + visitor_->OnCoalescedPacket(coalesced_packet); +} + +bool QuicFramer::MaybeProcessIetfLength(QuicDataReader* encrypted_reader, + QuicPacketHeader* header) { + if (!QuicVersionHasLongHeaderLengths(header->version.transport_version) || + header->form != IETF_QUIC_LONG_HEADER_PACKET || + (header->long_packet_type != INITIAL && + header->long_packet_type != HANDSHAKE && + header->long_packet_type != ZERO_RTT_PROTECTED)) { + return true; + } + header->length_length = encrypted_reader->PeekVarInt62Length(); + if (!encrypted_reader->ReadVarInt62(&header->remaining_packet_length)) { + set_detailed_error("Unable to read long header payload length."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + uint64_t remaining_bytes_length = encrypted_reader->BytesRemaining(); + if (header->remaining_packet_length > remaining_bytes_length) { + set_detailed_error("Long header payload length longer than packet."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + + MaybeProcessCoalescedPacket(*encrypted_reader, remaining_bytes_length, + *header); + + if (!encrypted_reader->TruncateRemaining(header->remaining_packet_length)) { + set_detailed_error("Length TruncateRemaining failed."); + QUIC_BUG << "Length TruncateRemaining failed."; + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + return true; +} + +bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader, + QuicPacketHeader* header, + const QuicEncryptedPacket& packet, + char* decrypted_buffer, + size_t buffer_length) { + DCHECK_NE(GOOGLE_QUIC_PACKET, header->form); + DCHECK(!header->has_possible_stateless_reset_token); + header->retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; + header->retry_token = QuicStringPiece(); + header->length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0; + header->remaining_packet_length = 0; + if (header->form == IETF_QUIC_SHORT_HEADER_PACKET && + perspective_ == Perspective::IS_CLIENT) { + // Peek possible stateless reset token. Will only be used on decryption + // failure. + QuicStringPiece remaining = encrypted_reader->PeekRemainingPayload(); + if (remaining.length() >= sizeof(header->possible_stateless_reset_token)) { + header->has_possible_stateless_reset_token = true; + memcpy(&header->possible_stateless_reset_token, + &remaining.data()[remaining.length() - + sizeof(header->possible_stateless_reset_token)], + sizeof(header->possible_stateless_reset_token)); + } + } + + if (!MaybeProcessIetfInitialRetryToken(encrypted_reader, header)) { + return false; + } + + if (!MaybeProcessIetfLength(encrypted_reader, header)) { + return false; + } + + if (header->form == IETF_QUIC_SHORT_HEADER_PACKET || + header->long_packet_type != VERSION_NEGOTIATION) { + // Process packet number. + QuicPacketNumber base_packet_number = largest_packet_number_; + uint64_t full_packet_number; + if (!ProcessAndCalculatePacketNumber( + encrypted_reader, header->packet_number_length, base_packet_number, + &full_packet_number)) { + set_detailed_error("Unable to read packet number."); + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + + if (!IsValidFullPacketNumber(full_packet_number, transport_version())) { + if (IsIetfStatelessResetPacket(*header)) { + // This is a stateless reset packet. + QuicIetfStatelessResetPacket packet( + *header, header->possible_stateless_reset_token); + visitor_->OnAuthenticatedIetfStatelessResetPacket(packet); + return true; + } + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER); + set_detailed_error("packet numbers cannot be 0."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + header->packet_number = QuicPacketNumber(full_packet_number); + } + + // A nonce should only present in SHLO from the server to the client when + // using QUIC crypto. + if (header->form == IETF_QUIC_LONG_HEADER_PACKET && + header->long_packet_type == ZERO_RTT_PROTECTED && + perspective_ == Perspective::IS_CLIENT && + version_.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { + if (!encrypted_reader->ReadBytes( + reinterpret_cast<uint8_t*>(last_nonce_.data()), + last_nonce_.size())) { + set_detailed_error("Unable to read nonce."); + RecordDroppedPacketReason( + DroppedPacketReason::INVALID_DIVERSIFICATION_NONCE); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + + header->nonce = &last_nonce_; + } else { + header->nonce = nullptr; + } + + if (!visitor_->OnUnauthenticatedHeader(*header)) { + set_detailed_error( + "Visitor asked to stop processing of unauthenticated header."); + return false; + } + + QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload(); + QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket( + version_.transport_version, packet, + GetIncludedDestinationConnectionIdLength(*header), + GetIncludedSourceConnectionIdLength(*header), header->version_flag, + header->nonce != nullptr, header->packet_number_length, + header->retry_token_length_length, header->retry_token.length(), + header->length_length); + + size_t decrypted_length = 0; + if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer, + buffer_length, &decrypted_length)) { + if (IsIetfStatelessResetPacket(*header)) { + // This is a stateless reset packet. + QuicIetfStatelessResetPacket packet( + *header, header->possible_stateless_reset_token); + visitor_->OnAuthenticatedIetfStatelessResetPacket(packet); + return true; + } + set_detailed_error("Unable to decrypt payload."); + RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE); + return RaiseError(QUIC_DECRYPTION_FAILURE); + } + QuicDataReader reader(decrypted_buffer, decrypted_length); + + // Update the largest packet number after we have decrypted the packet + // so we are confident is not attacker controlled. + if (largest_packet_number_.IsInitialized()) { + largest_packet_number_ = + std::max(header->packet_number, largest_packet_number_); + } else { + largest_packet_number_ = header->packet_number; + } + + if (!visitor_->OnPacketHeader(*header)) { + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER); + // The visitor suppresses further processing of the packet. + return true; + } + + if (packet.length() > kMaxPacketSize) { + // If the packet has gotten this far, it should not be too large. + QUIC_BUG << "Packet too large:" << packet.length(); + return RaiseError(QUIC_PACKET_TOO_LARGE); + } + + // Handle the payload. + if (version_.transport_version == QUIC_VERSION_99) { + if (!ProcessIetfFrameData(&reader, *header)) { + DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessIetfFrameData sets the error. + DCHECK_NE("", detailed_error_); + QUIC_DLOG(WARNING) << ENDPOINT << "Unable to process frame data. Error: " + << detailed_error_; + return false; + } + } else { + if (!ProcessFrameData(&reader, *header)) { + DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. + DCHECK_NE("", detailed_error_); + QUIC_DLOG(WARNING) << ENDPOINT << "Unable to process frame data. Error: " + << detailed_error_; + return false; + } + } + + visitor_->OnPacketComplete(); + return true; +} + +bool QuicFramer::ProcessDataPacket(QuicDataReader* encrypted_reader, + QuicPacketHeader* header, + const QuicEncryptedPacket& packet, + char* decrypted_buffer, + size_t buffer_length) { + if (!ProcessUnauthenticatedHeader(encrypted_reader, header)) { + DCHECK_NE("", detailed_error_); + QUIC_DVLOG(1) + << ENDPOINT + << "Unable to process packet header. Stopping parsing. Error: " + << detailed_error_; + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER); + return false; + } + + QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload(); + QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket( + version_.transport_version, packet, + GetIncludedDestinationConnectionIdLength(*header), + GetIncludedSourceConnectionIdLength(*header), header->version_flag, + header->nonce != nullptr, header->packet_number_length, + header->retry_token_length_length, header->retry_token.length(), + header->length_length); + + size_t decrypted_length = 0; + if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer, + buffer_length, &decrypted_length)) { + RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE); + set_detailed_error("Unable to decrypt payload."); + return RaiseError(QUIC_DECRYPTION_FAILURE); + } + + QuicDataReader reader(decrypted_buffer, decrypted_length); + + // Update the largest packet number after we have decrypted the packet + // so we are confident is not attacker controlled. + if (largest_packet_number_.IsInitialized()) { + largest_packet_number_ = + std::max(header->packet_number, largest_packet_number_); + } else { + largest_packet_number_ = header->packet_number; + } + + if (!visitor_->OnPacketHeader(*header)) { + // The visitor suppresses further processing of the packet. + return true; + } + + if (packet.length() > kMaxPacketSize) { + // If the packet has gotten this far, it should not be too large. + QUIC_BUG << "Packet too large:" << packet.length(); + return RaiseError(QUIC_PACKET_TOO_LARGE); + } + + // Handle the payload. + if (!ProcessFrameData(&reader, *header)) { + DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. + DCHECK_NE("", detailed_error_); + QUIC_DLOG(WARNING) << ENDPOINT << "Unable to process frame data. Error: " + << detailed_error_; + return false; + } + + visitor_->OnPacketComplete(); + return true; +} + +bool QuicFramer::ProcessPublicResetPacket(QuicDataReader* reader, + const QuicPacketHeader& header) { + QuicPublicResetPacket packet(header.destination_connection_id); + + std::unique_ptr<CryptoHandshakeMessage> reset( + CryptoFramer::ParseMessage(reader->ReadRemainingPayload())); + if (!reset.get()) { + set_detailed_error("Unable to read reset message."); + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_RESET_PACKET); + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); + } + if (reset->tag() != kPRST) { + set_detailed_error("Incorrect message tag."); + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_RESET_PACKET); + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); + } + + if (reset->GetUint64(kRNON, &packet.nonce_proof) != QUIC_NO_ERROR) { + set_detailed_error("Unable to read nonce proof."); + RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_RESET_PACKET); + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); + } + // TODO(satyamshekhar): validate nonce to protect against DoS. + + QuicStringPiece address; + if (reset->GetStringPiece(kCADR, &address)) { + QuicSocketAddressCoder address_coder; + if (address_coder.Decode(address.data(), address.length())) { + packet.client_address = + QuicSocketAddress(address_coder.ip(), address_coder.port()); + } + } + + QuicStringPiece endpoint_id; + if (perspective_ == Perspective::IS_CLIENT && + reset->GetStringPiece(kEPID, &endpoint_id)) { + packet.endpoint_id = QuicString(endpoint_id); + packet.endpoint_id += '\0'; + } + + visitor_->OnPublicResetPacket(packet); + return true; +} + +bool QuicFramer::IsIetfStatelessResetPacket( + const QuicPacketHeader& header) const { + QUIC_BUG_IF(header.has_possible_stateless_reset_token && + perspective_ != Perspective::IS_CLIENT) + << "has_possible_stateless_reset_token can only be true at client side."; + return header.form == IETF_QUIC_SHORT_HEADER_PACKET && + header.has_possible_stateless_reset_token && + visitor_->IsValidStatelessResetToken( + header.possible_stateless_reset_token); +} + +bool QuicFramer::HasEncrypterOfEncryptionLevel(EncryptionLevel level) const { + return encrypter_[level] != nullptr; +} + +bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer, + size_t* length_field_offset) { + if (transport_version() > QUIC_VERSION_43) { + return AppendIetfPacketHeader(header, writer, length_field_offset); + } + QUIC_DVLOG(1) << ENDPOINT << "Appending header: " << header; + uint8_t public_flags = 0; + if (header.reset_flag) { + public_flags |= PACKET_PUBLIC_FLAGS_RST; + } + if (header.version_flag) { + public_flags |= PACKET_PUBLIC_FLAGS_VERSION; + } + + public_flags |= GetPacketNumberFlags(header.packet_number_length) + << kPublicHeaderSequenceNumberShift; + + if (header.nonce != nullptr) { + DCHECK_EQ(Perspective::IS_SERVER, perspective_); + public_flags |= PACKET_PUBLIC_FLAGS_NONCE; + } + DCHECK_EQ(CONNECTION_ID_ABSENT, header.source_connection_id_included); + switch (header.destination_connection_id_included) { + case CONNECTION_ID_ABSENT: + if (!writer->WriteUInt8(public_flags | + PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID)) { + return false; + } + break; + case CONNECTION_ID_PRESENT: + QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( + header.destination_connection_id, transport_version())) + << "AppendPacketHeader: attempted to use connection ID " + << header.destination_connection_id + << " which is invalid with version " + << QuicVersionToString(transport_version()); + + public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID; + if (perspective_ == Perspective::IS_CLIENT) { + public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD; + } + if (!writer->WriteUInt8(public_flags) || + !writer->WriteConnectionId(header.destination_connection_id)) { + return false; + } + break; + } + last_serialized_connection_id_ = header.destination_connection_id; + + if (header.version_flag) { + DCHECK_EQ(Perspective::IS_CLIENT, perspective_); + QuicVersionLabel version_label = CreateQuicVersionLabel(version_); + // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed. + if (!writer->WriteTag(QuicEndian::NetToHost32(version_label))) { + return false; + } + + QUIC_DVLOG(1) << ENDPOINT << "label = '" + << QuicVersionLabelToString(version_label) << "'"; + } + + if (header.nonce != nullptr && + !writer->WriteBytes(header.nonce, kDiversificationNonceSize)) { + return false; + } + + if (!AppendPacketNumber(header.packet_number_length, header.packet_number, + writer)) { + return false; + } + + return true; +} + +bool QuicFramer::AppendIetfHeaderTypeByte(const QuicPacketHeader& header, + QuicDataWriter* writer) { + uint8_t type = 0; + if (transport_version() > QUIC_VERSION_44) { + if (header.version_flag) { + type = static_cast<uint8_t>( + FLAGS_LONG_HEADER | FLAGS_FIXED_BIT | + LongHeaderTypeToOnWireValue(transport_version(), + header.long_packet_type) | + PacketNumberLengthToOnWireValue(transport_version(), + header.packet_number_length)); + } else { + type = static_cast<uint8_t>( + FLAGS_FIXED_BIT | + PacketNumberLengthToOnWireValue(transport_version(), + header.packet_number_length)); + } + return writer->WriteUInt8(type); + } + + if (header.version_flag) { + type = static_cast<uint8_t>( + FLAGS_LONG_HEADER | LongHeaderTypeToOnWireValue( + transport_version(), header.long_packet_type)); + DCHECK_EQ(PACKET_4BYTE_PACKET_NUMBER, header.packet_number_length); + } else { + type |= FLAGS_SHORT_HEADER_RESERVED_1; + type |= FLAGS_SHORT_HEADER_RESERVED_2; + DCHECK_GE(PACKET_4BYTE_PACKET_NUMBER, header.packet_number_length); + type |= PacketNumberLengthToOnWireValue(transport_version(), + header.packet_number_length); + } + return writer->WriteUInt8(type); +} + +bool QuicFramer::AppendIetfPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer, + size_t* length_field_offset) { + QUIC_DVLOG(1) << ENDPOINT << "Appending IETF header: " << header; + QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( + header.destination_connection_id, transport_version())) + << "AppendIetfPacketHeader: attempted to use connection ID " + << header.destination_connection_id << " which is invalid with version " + << QuicVersionToString(transport_version()); + if (!AppendIetfHeaderTypeByte(header, writer)) { + return false; + } + + if (header.version_flag) { + // Append version for long header. + QuicVersionLabel version_label = CreateQuicVersionLabel(version_); + // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed. + if (!writer->WriteTag(QuicEndian::NetToHost32(version_label))) { + return false; + } + } + + // Append connection ID. + if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion( + transport_version()) && + !GetQuicReloadableFlag(quic_use_new_append_connection_id)) { + if (!AppendIetfConnectionId( + header.version_flag, header.destination_connection_id, + GetIncludedDestinationConnectionIdLength(header), + header.source_connection_id, + GetIncludedSourceConnectionIdLength(header), writer)) { + return false; + } + } else { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_new_append_connection_id, 2, 2); + if (!AppendIetfConnectionIdsNew( + header.version_flag, + header.destination_connection_id_included != CONNECTION_ID_ABSENT + ? header.destination_connection_id + : EmptyQuicConnectionId(), + header.source_connection_id_included != CONNECTION_ID_ABSENT + ? header.source_connection_id + : EmptyQuicConnectionId(), + writer)) { + return false; + } + } + last_serialized_connection_id_ = header.destination_connection_id; + + if (QuicVersionHasLongHeaderLengths(transport_version()) && + header.version_flag) { + if (header.long_packet_type == INITIAL) { + // Write retry token length. + if (!writer->WriteVarInt62(header.retry_token.length(), + header.retry_token_length_length)) { + return false; + } + // Write retry token. + if (!header.retry_token.empty() && + !writer->WriteStringPiece(header.retry_token)) { + return false; + } + } + if (length_field_offset != nullptr) { + *length_field_offset = writer->length(); + } + // Add fake length to reserve two bytes to add length in later. + writer->WriteVarInt62(256); + } else if (length_field_offset != nullptr) { + *length_field_offset = 0; + } + + // Append packet number. + if (!AppendPacketNumber(header.packet_number_length, header.packet_number, + writer)) { + return false; + } + + if (!header.version_flag) { + return true; + } + + if (header.nonce != nullptr) { + DCHECK(header.version_flag); + DCHECK_EQ(ZERO_RTT_PROTECTED, header.long_packet_type); + DCHECK_EQ(Perspective::IS_SERVER, perspective_); + if (!writer->WriteBytes(header.nonce, kDiversificationNonceSize)) { + return false; + } + } + + return true; +} + +const QuicTime::Delta QuicFramer::CalculateTimestampFromWire( + uint32_t time_delta_us) { + // The new time_delta might have wrapped to the next epoch, or it + // might have reverse wrapped to the previous epoch, or it might + // remain in the same epoch. Select the time closest to the previous + // time. + // + // epoch_delta is the delta between epochs. A delta is 4 bytes of + // microseconds. + const uint64_t epoch_delta = UINT64_C(1) << 32; + uint64_t epoch = last_timestamp_.ToMicroseconds() & ~(epoch_delta - 1); + // Wrapping is safe here because a wrapped value will not be ClosestTo below. + uint64_t prev_epoch = epoch - epoch_delta; + uint64_t next_epoch = epoch + epoch_delta; + + uint64_t time = ClosestTo( + last_timestamp_.ToMicroseconds(), epoch + time_delta_us, + ClosestTo(last_timestamp_.ToMicroseconds(), prev_epoch + time_delta_us, + next_epoch + time_delta_us)); + + return QuicTime::Delta::FromMicroseconds(time); +} + +uint64_t QuicFramer::CalculatePacketNumberFromWire( + QuicPacketNumberLength packet_number_length, + QuicPacketNumber base_packet_number, + uint64_t packet_number) const { + // The new packet number might have wrapped to the next epoch, or + // it might have reverse wrapped to the previous epoch, or it might + // remain in the same epoch. Select the packet number closest to the + // next expected packet number, the previous packet number plus 1. + + // epoch_delta is the delta between epochs the packet number was serialized + // with, so the correct value is likely the same epoch as the last sequence + // number or an adjacent epoch. + if (!base_packet_number.IsInitialized()) { + return packet_number; + } + const uint64_t epoch_delta = UINT64_C(1) << (8 * packet_number_length); + uint64_t next_packet_number = base_packet_number.ToUint64() + 1; + uint64_t epoch = base_packet_number.ToUint64() & ~(epoch_delta - 1); + uint64_t prev_epoch = epoch - epoch_delta; + uint64_t next_epoch = epoch + epoch_delta; + + return ClosestTo(next_packet_number, epoch + packet_number, + ClosestTo(next_packet_number, prev_epoch + packet_number, + next_epoch + packet_number)); +} + +bool QuicFramer::ProcessPublicHeader(QuicDataReader* reader, + bool packet_has_ietf_packet_header, + QuicPacketHeader* header) { + if (packet_has_ietf_packet_header) { + return ProcessIetfPacketHeader(reader, header); + } + DCHECK(!QuicUtils::VariableLengthConnectionIdAllowedForVersion( + transport_version())); + uint8_t public_flags; + if (!reader->ReadBytes(&public_flags, 1)) { + set_detailed_error("Unable to read public flags."); + return false; + } + + header->reset_flag = (public_flags & PACKET_PUBLIC_FLAGS_RST) != 0; + header->version_flag = (public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0; + + if (validate_flags_ && !header->version_flag && + public_flags > PACKET_PUBLIC_FLAGS_MAX) { + set_detailed_error("Illegal public flags value."); + return false; + } + + if (header->reset_flag && header->version_flag) { + set_detailed_error("Got version flag in reset packet"); + return false; + } + + switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) { + case PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID: + if (!reader->ReadConnectionId(&header->destination_connection_id, + kQuicDefaultConnectionIdLength)) { + set_detailed_error("Unable to read ConnectionId."); + return false; + } + header->destination_connection_id_included = CONNECTION_ID_PRESENT; + break; + case PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID: + header->destination_connection_id_included = CONNECTION_ID_ABSENT; + header->destination_connection_id = last_serialized_connection_id_; + break; + } + + header->packet_number_length = ReadSequenceNumberLength( + public_flags >> kPublicHeaderSequenceNumberShift); + + // Read the version only if the packet is from the client. + // version flag from the server means version negotiation packet. + if (header->version_flag && perspective_ == Perspective::IS_SERVER) { + QuicVersionLabel version_label; + if (!reader->ReadTag(&version_label)) { + set_detailed_error("Unable to read protocol version."); + return false; + } + // TODO(rch): Use ReadUInt32() once QUIC_VERSION_35 is removed. + version_label = QuicEndian::NetToHost32(version_label); + + // If the version from the new packet is the same as the version of this + // framer, then the public flags should be set to something we understand. + // If not, this raises an error. + last_version_label_ = version_label; + ParsedQuicVersion version = ParseQuicVersionLabel(version_label); + if (version == version_ && public_flags > PACKET_PUBLIC_FLAGS_MAX) { + set_detailed_error("Illegal public flags value."); + return false; + } + header->version = version; + } + + // A nonce should only be present in packets from the server to the client, + // which are neither version negotiation nor public reset packets. + if (public_flags & PACKET_PUBLIC_FLAGS_NONCE && + !(public_flags & PACKET_PUBLIC_FLAGS_VERSION) && + !(public_flags & PACKET_PUBLIC_FLAGS_RST) && + // The nonce flag from a client is ignored and is assumed to be an older + // client indicating an eight-byte connection ID. + perspective_ == Perspective::IS_CLIENT) { + if (!reader->ReadBytes(reinterpret_cast<uint8_t*>(last_nonce_.data()), + last_nonce_.size())) { + set_detailed_error("Unable to read nonce."); + return false; + } + header->nonce = &last_nonce_; + } else { + header->nonce = nullptr; + } + + return true; +} + +// static +QuicPacketNumberLength QuicFramer::GetMinPacketNumberLength( + QuicTransportVersion version, + QuicPacketNumber packet_number) { + DCHECK(packet_number.IsInitialized()); + if (packet_number < QuicPacketNumber(1 << (PACKET_1BYTE_PACKET_NUMBER * 8))) { + return PACKET_1BYTE_PACKET_NUMBER; + } else if (packet_number < + QuicPacketNumber(1 << (PACKET_2BYTE_PACKET_NUMBER * 8))) { + return PACKET_2BYTE_PACKET_NUMBER; + } else if (packet_number < + QuicPacketNumber(UINT64_C(1) + << (PACKET_4BYTE_PACKET_NUMBER * 8))) { + return PACKET_4BYTE_PACKET_NUMBER; + } else { + return PACKET_6BYTE_PACKET_NUMBER; + } +} + +// static +uint8_t QuicFramer::GetPacketNumberFlags( + QuicPacketNumberLength packet_number_length) { + switch (packet_number_length) { + case PACKET_1BYTE_PACKET_NUMBER: + return PACKET_FLAGS_1BYTE_PACKET; + case PACKET_2BYTE_PACKET_NUMBER: + return PACKET_FLAGS_2BYTE_PACKET; + case PACKET_4BYTE_PACKET_NUMBER: + return PACKET_FLAGS_4BYTE_PACKET; + case PACKET_6BYTE_PACKET_NUMBER: + case PACKET_8BYTE_PACKET_NUMBER: + return PACKET_FLAGS_8BYTE_PACKET; + default: + QUIC_BUG << "Unreachable case statement."; + return PACKET_FLAGS_8BYTE_PACKET; + } +} + +// static +QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo( + const QuicAckFrame& frame) { + AckFrameInfo new_ack_info; + if (frame.packets.Empty()) { + return new_ack_info; + } + // The first block is the last interval. It isn't encoded with the gap-length + // encoding, so skip it. + new_ack_info.first_block_length = frame.packets.LastIntervalLength(); + auto itr = frame.packets.rbegin(); + QuicPacketNumber previous_start = itr->min(); + new_ack_info.max_block_length = PacketNumberIntervalLength(*itr); + ++itr; + + // Don't do any more work after getting information for 256 ACK blocks; any + // more can't be encoded anyway. + for (; itr != frame.packets.rend() && + new_ack_info.num_ack_blocks < std::numeric_limits<uint8_t>::max(); + previous_start = itr->min(), ++itr) { + const auto& interval = *itr; + const QuicPacketCount total_gap = previous_start - interval.max(); + new_ack_info.num_ack_blocks += + (total_gap + std::numeric_limits<uint8_t>::max() - 1) / + std::numeric_limits<uint8_t>::max(); + new_ack_info.max_block_length = std::max( + new_ack_info.max_block_length, PacketNumberIntervalLength(interval)); + } + return new_ack_info; +} + +bool QuicFramer::ProcessUnauthenticatedHeader(QuicDataReader* encrypted_reader, + QuicPacketHeader* header) { + QuicPacketNumber base_packet_number = largest_packet_number_; + uint64_t full_packet_number; + if (!ProcessAndCalculatePacketNumber( + encrypted_reader, header->packet_number_length, base_packet_number, + &full_packet_number)) { + set_detailed_error("Unable to read packet number."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + + if (!IsValidFullPacketNumber(full_packet_number, transport_version())) { + set_detailed_error("packet numbers cannot be 0."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + header->packet_number = QuicPacketNumber(full_packet_number); + + if (!visitor_->OnUnauthenticatedHeader(*header)) { + set_detailed_error( + "Visitor asked to stop processing of unauthenticated header."); + return false; + } + return true; +} + +bool QuicFramer::ProcessIetfHeaderTypeByte(QuicDataReader* reader, + QuicPacketHeader* header) { + uint8_t type; + if (!reader->ReadBytes(&type, 1)) { + set_detailed_error("Unable to read type."); + return false; + } + // Determine whether this is a long or short header. + header->form = type & FLAGS_LONG_HEADER ? IETF_QUIC_LONG_HEADER_PACKET + : IETF_QUIC_SHORT_HEADER_PACKET; + if (header->form == IETF_QUIC_LONG_HEADER_PACKET) { + // Version is always present in long headers. + header->version_flag = true; + // Long header packets received by client must include 8-byte source + // connection ID, and those received by server must include 8-byte + // destination connection ID. + header->destination_connection_id_included = + perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT + : CONNECTION_ID_PRESENT; + header->source_connection_id_included = + perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_PRESENT + : CONNECTION_ID_ABSENT; + // Read version tag. + QuicVersionLabel version_label; + if (!reader->ReadTag(&version_label)) { + set_detailed_error("Unable to read protocol version."); + return false; + } + // TODO(rch): Use ReadUInt32() once QUIC_VERSION_35 is removed. + version_label = QuicEndian::NetToHost32(version_label); + if (!version_label) { + // Version label is 0 indicating this is a version negotiation packet. + header->long_packet_type = VERSION_NEGOTIATION; + } else { + header->version = ParseQuicVersionLabel(version_label); + if (header->version.transport_version != QUIC_VERSION_UNSUPPORTED) { + if (header->version.transport_version > QUIC_VERSION_44 && + !(type & FLAGS_FIXED_BIT)) { + set_detailed_error("Fixed bit is 0 in long header."); + return false; + } + if (!GetLongHeaderType(header->version.transport_version, type, + &header->long_packet_type)) { + set_detailed_error("Illegal long header type value."); + return false; + } + header->packet_number_length = GetLongHeaderPacketNumberLength( + header->version.transport_version, type); + } + } + if (header->long_packet_type != VERSION_NEGOTIATION) { + // Do not save version of version negotiation packet. + last_version_label_ = version_label; + } + + QUIC_DVLOG(1) << ENDPOINT << "Received IETF long header: " + << QuicUtils::QuicLongHeaderTypetoString( + header->long_packet_type); + return true; + } + + QUIC_DVLOG(1) << ENDPOINT << "Received IETF short header"; + // Version is not present in short headers. + header->version_flag = false; + // Connection ID length depends on the perspective. Client does not expect + // destination connection ID, and server expects destination connection ID. + header->destination_connection_id_included = + perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT + : CONNECTION_ID_PRESENT; + header->source_connection_id_included = CONNECTION_ID_ABSENT; + if (infer_packet_header_type_from_version_ && + transport_version() > QUIC_VERSION_44 && !(type & FLAGS_FIXED_BIT)) { + set_detailed_error("Fixed bit is 0 in short header."); + return false; + } + if (!GetShortHeaderPacketNumberLength(transport_version(), type, + infer_packet_header_type_from_version_, + &header->packet_number_length)) { + set_detailed_error("Illegal short header type value."); + return false; + } + QUIC_DVLOG(1) << "packet_number_length = " << header->packet_number_length; + return true; +} + +bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader, + QuicPacketHeader* header) { + if (!ProcessIetfHeaderTypeByte(reader, header)) { + return false; + } + + uint8_t destination_connection_id_length = + header->destination_connection_id_included == CONNECTION_ID_PRESENT + ? expected_connection_id_length_ + : 0; + uint8_t source_connection_id_length = + header->source_connection_id_included == CONNECTION_ID_PRESENT + ? expected_connection_id_length_ + : 0; + if (header->form == IETF_QUIC_LONG_HEADER_PACKET) { + // Read and validate connection ID length. + uint8_t connection_id_lengths_byte; + if (!reader->ReadBytes(&connection_id_lengths_byte, 1)) { + set_detailed_error("Unable to read ConnectionId length."); + return false; + } + uint8_t dcil = + (connection_id_lengths_byte & kDestinationConnectionIdLengthMask) >> 4; + if (dcil != 0) { + dcil += kConnectionIdLengthAdjustment; + } + uint8_t scil = connection_id_lengths_byte & kSourceConnectionIdLengthMask; + if (scil != 0) { + scil += kConnectionIdLengthAdjustment; + } + if (dcil != destination_connection_id_length || + scil != source_connection_id_length) { + QUIC_DVLOG(1) << "dcil: " << static_cast<uint32_t>(dcil) + << ", scil: " << static_cast<uint32_t>(scil); + set_detailed_error("Invalid ConnectionId length."); + return false; + } + destination_connection_id_length = dcil; + source_connection_id_length = scil; + } + + // Read connection ID. + if (!reader->ReadConnectionId(&header->destination_connection_id, + destination_connection_id_length)) { + set_detailed_error("Unable to read Destination ConnectionId."); + return false; + } + + if (!reader->ReadConnectionId(&header->source_connection_id, + source_connection_id_length)) { + set_detailed_error("Unable to read Source ConnectionId."); + return false; + } + + if (header->source_connection_id_included == CONNECTION_ID_PRESENT) { + // Set destination connection ID to source connection ID. + DCHECK_EQ(EmptyQuicConnectionId(), header->destination_connection_id); + header->destination_connection_id = header->source_connection_id; + } else if (header->destination_connection_id_included == + CONNECTION_ID_ABSENT) { + header->destination_connection_id = last_serialized_connection_id_; + } + + return true; +} + +bool QuicFramer::ProcessAndCalculatePacketNumber( + QuicDataReader* reader, + QuicPacketNumberLength packet_number_length, + QuicPacketNumber base_packet_number, + uint64_t* packet_number) { + uint64_t wire_packet_number; + if (!reader->ReadBytesToUInt64(packet_number_length, &wire_packet_number)) { + return false; + } + + // TODO(ianswett): Explore the usefulness of trying multiple packet numbers + // in case the first guess is incorrect. + *packet_number = CalculatePacketNumberFromWire( + packet_number_length, base_packet_number, wire_packet_number); + return true; +} + +bool QuicFramer::ProcessFrameData(QuicDataReader* reader, + const QuicPacketHeader& header) { + DCHECK_NE(QUIC_VERSION_99, version_.transport_version) + << "Version 99 negotiated, but not processing frames as version 99."; + if (reader->IsDoneReading()) { + set_detailed_error("Packet has no frames."); + return RaiseError(QUIC_MISSING_PAYLOAD); + } + while (!reader->IsDoneReading()) { + uint8_t frame_type; + if (!reader->ReadBytes(&frame_type, 1)) { + set_detailed_error("Unable to read frame type."); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + const uint8_t special_mask = transport_version() <= QUIC_VERSION_44 + ? kQuicFrameTypeBrokenMask + : kQuicFrameTypeSpecialMask; + if (frame_type & special_mask) { + // Stream Frame + if (frame_type & kQuicFrameTypeStreamMask) { + QuicStreamFrame frame; + if (!ProcessStreamFrame(reader, frame_type, &frame)) { + return RaiseError(QUIC_INVALID_STREAM_DATA); + } + if (!visitor_->OnStreamFrame(frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + // Ack Frame + if (frame_type & kQuicFrameTypeAckMask) { + if (!ProcessAckFrame(reader, frame_type)) { + return RaiseError(QUIC_INVALID_ACK_DATA); + } + continue; + } + + // This was a special frame type that did not match any + // of the known ones. Error. + set_detailed_error("Illegal frame type."); + QUIC_DLOG(WARNING) << ENDPOINT << "Illegal frame type: " + << static_cast<int>(frame_type); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + + switch (frame_type) { + case PADDING_FRAME: { + QuicPaddingFrame frame; + ProcessPaddingFrame(reader, &frame); + if (!visitor_->OnPaddingFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case RST_STREAM_FRAME: { + QuicRstStreamFrame frame; + if (!ProcessRstStreamFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_RST_STREAM_DATA); + } + if (!visitor_->OnRstStreamFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case CONNECTION_CLOSE_FRAME: { + QuicConnectionCloseFrame frame; + if (!ProcessConnectionCloseFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA); + } + + if (!visitor_->OnConnectionCloseFrame(frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case GOAWAY_FRAME: { + QuicGoAwayFrame goaway_frame; + if (!ProcessGoAwayFrame(reader, &goaway_frame)) { + return RaiseError(QUIC_INVALID_GOAWAY_DATA); + } + if (!visitor_->OnGoAwayFrame(goaway_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case WINDOW_UPDATE_FRAME: { + QuicWindowUpdateFrame window_update_frame; + if (!ProcessWindowUpdateFrame(reader, &window_update_frame)) { + return RaiseError(QUIC_INVALID_WINDOW_UPDATE_DATA); + } + if (!visitor_->OnWindowUpdateFrame(window_update_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case BLOCKED_FRAME: { + QuicBlockedFrame blocked_frame; + if (!ProcessBlockedFrame(reader, &blocked_frame)) { + return RaiseError(QUIC_INVALID_BLOCKED_DATA); + } + if (!visitor_->OnBlockedFrame(blocked_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case STOP_WAITING_FRAME: { + QuicStopWaitingFrame stop_waiting_frame; + if (!ProcessStopWaitingFrame(reader, header, &stop_waiting_frame)) { + return RaiseError(QUIC_INVALID_STOP_WAITING_DATA); + } + if (!visitor_->OnStopWaitingFrame(stop_waiting_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + case PING_FRAME: { + // Ping has no payload. + QuicPingFrame ping_frame; + if (!visitor_->OnPingFrame(ping_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + case IETF_EXTENSION_MESSAGE_NO_LENGTH: + QUIC_FALLTHROUGH_INTENDED; + case IETF_EXTENSION_MESSAGE: { + QuicMessageFrame message_frame; + if (!ProcessMessageFrame(reader, + frame_type == IETF_EXTENSION_MESSAGE_NO_LENGTH, + &message_frame)) { + return RaiseError(QUIC_INVALID_MESSAGE_DATA); + } + if (!visitor_->OnMessageFrame(message_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case CRYPTO_FRAME: { + if (version_.transport_version < QUIC_VERSION_47) { + set_detailed_error("Illegal frame type."); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + QuicCryptoFrame frame; + if (!ProcessCryptoFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnCryptoFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + + default: + set_detailed_error("Illegal frame type."); + QUIC_DLOG(WARNING) << ENDPOINT << "Illegal frame type: " + << static_cast<int>(frame_type); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + } + + return true; +} + +bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, + const QuicPacketHeader& header) { + DCHECK_EQ(QUIC_VERSION_99, version_.transport_version) + << "Attempt to process frames as IETF frames but version is " + << version_.transport_version << ", not 99."; + if (reader->IsDoneReading()) { + set_detailed_error("Packet has no frames."); + return RaiseError(QUIC_MISSING_PAYLOAD); + } + while (!reader->IsDoneReading()) { + uint64_t frame_type; + // Will be the number of bytes into which frame_type was encoded. + size_t encoded_bytes = reader->BytesRemaining(); + if (!reader->ReadVarInt62(&frame_type)) { + set_detailed_error("Unable to read frame type."); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + + // Is now the number of bytes into which the frame type was encoded. + encoded_bytes -= reader->BytesRemaining(); + + // Check that the frame type is minimally encoded. + if (encoded_bytes != + static_cast<size_t>(QuicDataWriter::GetVarInt62Len(frame_type))) { + // The frame type was not minimally encoded. + set_detailed_error("Frame type not minimally encoded."); + return RaiseError(IETF_QUIC_PROTOCOL_VIOLATION); + } + + if (IS_IETF_STREAM_FRAME(frame_type)) { + QuicStreamFrame frame; + if (!ProcessIetfStreamFrame(reader, frame_type, &frame)) { + return RaiseError(QUIC_INVALID_STREAM_DATA); + } + if (!visitor_->OnStreamFrame(frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + } else { + switch (frame_type) { + case IETF_PADDING: { + QuicPaddingFrame frame; + ProcessPaddingFrame(reader, &frame); + if (!visitor_->OnPaddingFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_RST_STREAM: { + QuicRstStreamFrame frame; + if (!ProcessIetfResetStreamFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_RST_STREAM_DATA); + } + if (!visitor_->OnRstStreamFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_CONNECTION_CLOSE: { + QuicConnectionCloseFrame frame; + if (!ProcessIetfConnectionCloseFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA); + } + if (!visitor_->OnConnectionCloseFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_APPLICATION_CLOSE: { + QuicApplicationCloseFrame frame; + if (!ProcessApplicationCloseFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_APPLICATION_CLOSE_DATA); + } + if (!visitor_->OnApplicationCloseFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_MAX_DATA: { + QuicWindowUpdateFrame frame; + if (!ProcessMaxDataFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_MAX_DATA_FRAME_DATA); + } + // TODO(fkastenholz): Or should we create a new visitor function, + // OnMaxDataFrame()? + if (!visitor_->OnWindowUpdateFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_MAX_STREAM_DATA: { + QuicWindowUpdateFrame frame; + if (!ProcessMaxStreamDataFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA); + } + // TODO(fkastenholz): Or should we create a new visitor function, + // OnMaxStreamDataFrame()? + if (!visitor_->OnWindowUpdateFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_MAX_STREAMS_BIDIRECTIONAL: + case IETF_MAX_STREAMS_UNIDIRECTIONAL: { + QuicMaxStreamIdFrame frame; + if (!ProcessMaxStreamsFrame(reader, &frame, frame_type)) { + return RaiseError(QUIC_MAX_STREAM_ID_DATA); + } + QUIC_CODE_COUNT_N(max_stream_id_received, 1, 2); + if (!visitor_->OnMaxStreamIdFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_PING: { + // Ping has no payload. + QuicPingFrame ping_frame; + if (!visitor_->OnPingFrame(ping_frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_BLOCKED: { + QuicBlockedFrame frame; + if (!ProcessIetfBlockedFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_BLOCKED_DATA); + } + if (!visitor_->OnBlockedFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_STREAM_BLOCKED: { + QuicBlockedFrame frame; + if (!ProcessStreamBlockedFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_STREAM_BLOCKED_DATA); + } + if (!visitor_->OnBlockedFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_STREAMS_BLOCKED_UNIDIRECTIONAL: + case IETF_STREAMS_BLOCKED_BIDIRECTIONAL: { + QuicStreamIdBlockedFrame frame; + if (!ProcessStreamsBlockedFrame(reader, &frame, frame_type)) { + return RaiseError(QUIC_STREAM_ID_BLOCKED_DATA); + } + QUIC_CODE_COUNT_N(stream_id_blocked_received, 1, 2); + if (!visitor_->OnStreamIdBlockedFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_NEW_CONNECTION_ID: { + QuicNewConnectionIdFrame frame; + if (!ProcessNewConnectionIdFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_NEW_CONNECTION_ID_DATA); + } + if (!visitor_->OnNewConnectionIdFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_RETIRE_CONNECTION_ID: { + QuicRetireConnectionIdFrame frame; + if (!ProcessRetireConnectionIdFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_RETIRE_CONNECTION_ID_DATA); + } + if (!visitor_->OnRetireConnectionIdFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_NEW_TOKEN: { + QuicNewTokenFrame frame; + if (!ProcessNewTokenFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_NEW_TOKEN); + } + if (!visitor_->OnNewTokenFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_STOP_SENDING: { + QuicStopSendingFrame frame; + if (!ProcessStopSendingFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_STOP_SENDING_FRAME_DATA); + } + if (!visitor_->OnStopSendingFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_ACK_ECN: + case IETF_ACK: { + QuicAckFrame frame; + if (!ProcessIetfAckFrame(reader, frame_type, &frame)) { + return RaiseError(QUIC_INVALID_ACK_DATA); + } + break; + } + case IETF_PATH_CHALLENGE: { + QuicPathChallengeFrame frame; + if (!ProcessPathChallengeFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_PATH_CHALLENGE_DATA); + } + if (!visitor_->OnPathChallengeFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_PATH_RESPONSE: { + QuicPathResponseFrame frame; + if (!ProcessPathResponseFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_PATH_RESPONSE_DATA); + } + if (!visitor_->OnPathResponseFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_EXTENSION_MESSAGE_NO_LENGTH: + QUIC_FALLTHROUGH_INTENDED; + case IETF_EXTENSION_MESSAGE: { + QuicMessageFrame message_frame; + if (!ProcessMessageFrame( + reader, frame_type == IETF_EXTENSION_MESSAGE_NO_LENGTH, + &message_frame)) { + return RaiseError(QUIC_INVALID_MESSAGE_DATA); + } + if (!visitor_->OnMessageFrame(message_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + case IETF_CRYPTO: { + QuicCryptoFrame frame; + if (!ProcessCryptoFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnCryptoFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } + + default: + set_detailed_error("Illegal frame type."); + QUIC_DLOG(WARNING) + << ENDPOINT + << "Illegal frame type: " << static_cast<int>(frame_type); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + } + } + return true; +} + +namespace { +// Create a mask that sets the last |num_bits| to 1 and the rest to 0. +inline uint8_t GetMaskFromNumBits(uint8_t num_bits) { + return (1u << num_bits) - 1; +} + +// Extract |num_bits| from |flags| offset by |offset|. +uint8_t ExtractBits(uint8_t flags, uint8_t num_bits, uint8_t offset) { + return (flags >> offset) & GetMaskFromNumBits(num_bits); +} + +// Extract the bit at position |offset| from |flags| as a bool. +bool ExtractBit(uint8_t flags, uint8_t offset) { + return ((flags >> offset) & GetMaskFromNumBits(1)) != 0; +} + +// Set |num_bits|, offset by |offset| to |val| in |flags|. +void SetBits(uint8_t* flags, uint8_t val, uint8_t num_bits, uint8_t offset) { + DCHECK_LE(val, GetMaskFromNumBits(num_bits)); + *flags |= val << offset; +} + +// Set the bit at position |offset| to |val| in |flags|. +void SetBit(uint8_t* flags, bool val, uint8_t offset) { + SetBits(flags, val ? 1 : 0, 1, offset); +} +} // namespace + +bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader, + uint8_t frame_type, + QuicStreamFrame* frame) { + uint8_t stream_flags = frame_type; + + uint8_t stream_id_length = 0; + uint8_t offset_length = 4; + bool has_data_length = true; + stream_flags &= ~kQuicFrameTypeStreamMask; + + // Read from right to left: StreamID, Offset, Data Length, Fin. + stream_id_length = (stream_flags & kQuicStreamIDLengthMask) + 1; + stream_flags >>= kQuicStreamIdShift; + + offset_length = (stream_flags & kQuicStreamOffsetMask); + // There is no encoding for 1 byte, only 0 and 2 through 8. + if (offset_length > 0) { + offset_length += 1; + } + stream_flags >>= kQuicStreamShift; + + has_data_length = + (stream_flags & kQuicStreamDataLengthMask) == kQuicStreamDataLengthMask; + stream_flags >>= kQuicStreamDataLengthShift; + + frame->fin = (stream_flags & kQuicStreamFinMask) == kQuicStreamFinShift; + + uint64_t stream_id; + if (!reader->ReadBytesToUInt64(stream_id_length, &stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + frame->stream_id = static_cast<QuicStreamId>(stream_id); + + if (!reader->ReadBytesToUInt64(offset_length, &frame->offset)) { + set_detailed_error("Unable to read offset."); + return false; + } + + // TODO(ianswett): Don't use QuicStringPiece as an intermediary. + QuicStringPiece data; + if (has_data_length) { + if (!reader->ReadStringPiece16(&data)) { + set_detailed_error("Unable to read frame data."); + return false; + } + } else { + if (!reader->ReadStringPiece(&data, reader->BytesRemaining())) { + set_detailed_error("Unable to read frame data."); + return false; + } + } + frame->data_buffer = data.data(); + frame->data_length = static_cast<uint16_t>(data.length()); + + return true; +} + +bool QuicFramer::ProcessIetfStreamFrame(QuicDataReader* reader, + uint8_t frame_type, + QuicStreamFrame* frame) { + // Read stream id from the frame. It's always present. + if (!reader->ReadVarIntStreamId(&frame->stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + // If we have a data offset, read it. If not, set to 0. + if (frame_type & IETF_STREAM_FRAME_OFF_BIT) { + if (!reader->ReadVarInt62(&frame->offset)) { + set_detailed_error("Unable to read stream data offset."); + return false; + } + } else { + // no offset in the frame, ensure it's 0 in the Frame. + frame->offset = 0; + } + + // If we have a data length, read it. If not, set to 0. + if (frame_type & IETF_STREAM_FRAME_LEN_BIT) { + QuicIetfStreamDataLength length; + if (!reader->ReadVarInt62(&length)) { + set_detailed_error("Unable to read stream data length."); + return false; + } + if (length > 0xffff) { + set_detailed_error("Stream data length is too large."); + return false; + } + frame->data_length = length; + } else { + // no length in the frame, it is the number of bytes remaining in the + // packet. + frame->data_length = reader->BytesRemaining(); + } + + if (frame_type & IETF_STREAM_FRAME_FIN_BIT) { + frame->fin = true; + } else { + frame->fin = false; + } + + // TODO(ianswett): Don't use QuicStringPiece as an intermediary. + QuicStringPiece data; + if (!reader->ReadStringPiece(&data, frame->data_length)) { + set_detailed_error("Unable to read frame data."); + return false; + } + frame->data_buffer = data.data(); + frame->data_length = static_cast<QuicIetfStreamDataLength>(data.length()); + + return true; +} + +bool QuicFramer::ProcessCryptoFrame(QuicDataReader* reader, + QuicCryptoFrame* frame) { + if (!reader->ReadVarInt62(&frame->offset)) { + set_detailed_error("Unable to read crypto data offset."); + return false; + } + uint64_t len; + if (!reader->ReadVarInt62(&len) || + len > std::numeric_limits<QuicPacketLength>::max()) { + set_detailed_error("Invalid data length."); + return false; + } + frame->data_length = len; + + // TODO(ianswett): Don't use QuicStringPiece as an intermediary. + QuicStringPiece data; + if (!reader->ReadStringPiece(&data, frame->data_length)) { + set_detailed_error("Unable to read frame data."); + return false; + } + frame->data_buffer = data.data(); + return true; +} + +bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type) { + const bool has_ack_blocks = + ExtractBit(frame_type, kQuicHasMultipleAckBlocksOffset); + uint8_t num_ack_blocks = 0; + uint8_t num_received_packets = 0; + + // Determine the two lengths from the frame type: largest acked length, + // ack block length. + const QuicPacketNumberLength ack_block_length = ReadAckPacketNumberLength( + version_.transport_version, + ExtractBits(frame_type, kQuicSequenceNumberLengthNumBits, + kActBlockLengthOffset)); + const QuicPacketNumberLength largest_acked_length = ReadAckPacketNumberLength( + version_.transport_version, + ExtractBits(frame_type, kQuicSequenceNumberLengthNumBits, + kLargestAckedOffset)); + + uint64_t largest_acked; + if (!reader->ReadBytesToUInt64(largest_acked_length, &largest_acked)) { + set_detailed_error("Unable to read largest acked."); + return false; + } + + if (largest_acked < first_sending_packet_number_.ToUint64()) { + // Connection always sends packet starting from kFirstSendingPacketNumber > + // 0, peer has observed an unsent packet. + set_detailed_error("Largest acked is 0."); + return false; + } + + uint64_t ack_delay_time_us; + if (!reader->ReadUFloat16(&ack_delay_time_us)) { + set_detailed_error("Unable to read ack delay time."); + return false; + } + + if (!visitor_->OnAckFrameStart( + QuicPacketNumber(largest_acked), + ack_delay_time_us == kUFloat16MaxValue + ? QuicTime::Delta::Infinite() + : QuicTime::Delta::FromMicroseconds(ack_delay_time_us))) { + // The visitor suppresses further processing of the packet. Although this is + // not a parsing error, returns false as this is in middle of processing an + // ack frame, + set_detailed_error("Visitor suppresses further processing of ack frame."); + return false; + } + + if (has_ack_blocks && !reader->ReadUInt8(&num_ack_blocks)) { + set_detailed_error("Unable to read num of ack blocks."); + return false; + } + + uint64_t first_block_length; + if (!reader->ReadBytesToUInt64(ack_block_length, &first_block_length)) { + set_detailed_error("Unable to read first ack block length."); + return false; + } + + if (first_block_length == 0) { + set_detailed_error("First block length is zero."); + return false; + } + bool first_ack_block_underflow = first_block_length > largest_acked + 1; + if (first_block_length + first_sending_packet_number_.ToUint64() > + largest_acked + 1) { + first_ack_block_underflow = true; + } + if (first_ack_block_underflow) { + set_detailed_error(QuicStrCat("Underflow with first ack block length ", + first_block_length, " largest acked is ", + largest_acked, ".") + .c_str()); + return false; + } + + uint64_t first_received = largest_acked + 1 - first_block_length; + if (!visitor_->OnAckRange(QuicPacketNumber(first_received), + QuicPacketNumber(largest_acked + 1))) { + // The visitor suppresses further processing of the packet. Although + // this is not a parsing error, returns false as this is in middle + // of processing an ack frame, + set_detailed_error("Visitor suppresses further processing of ack frame."); + return false; + } + + if (num_ack_blocks > 0) { + for (size_t i = 0; i < num_ack_blocks; ++i) { + uint8_t gap = 0; + if (!reader->ReadUInt8(&gap)) { + set_detailed_error("Unable to read gap to next ack block."); + return false; + } + uint64_t current_block_length; + if (!reader->ReadBytesToUInt64(ack_block_length, ¤t_block_length)) { + set_detailed_error("Unable to ack block length."); + return false; + } + bool ack_block_underflow = first_received < gap + current_block_length; + if (first_received < gap + current_block_length + + first_sending_packet_number_.ToUint64()) { + ack_block_underflow = true; + } + if (ack_block_underflow) { + set_detailed_error( + QuicStrCat("Underflow with ack block length ", current_block_length, + ", end of block is ", first_received - gap, ".") + .c_str()); + return false; + } + + first_received -= (gap + current_block_length); + if (current_block_length > 0) { + if (!visitor_->OnAckRange( + QuicPacketNumber(first_received), + QuicPacketNumber(first_received) + current_block_length)) { + // The visitor suppresses further processing of the packet. Although + // this is not a parsing error, returns false as this is in middle + // of processing an ack frame, + set_detailed_error( + "Visitor suppresses further processing of ack frame."); + return false; + } + } + } + } + + if (!reader->ReadUInt8(&num_received_packets)) { + set_detailed_error("Unable to read num received packets."); + return false; + } + + if (!ProcessTimestampsInAckFrame(num_received_packets, + QuicPacketNumber(largest_acked), reader)) { + return false; + } + + // Done processing the ACK frame. + return visitor_->OnAckFrameEnd(QuicPacketNumber(first_received)); +} + +bool QuicFramer::ProcessTimestampsInAckFrame(uint8_t num_received_packets, + QuicPacketNumber largest_acked, + QuicDataReader* reader) { + if (num_received_packets == 0) { + return true; + } + uint8_t delta_from_largest_observed; + if (!reader->ReadUInt8(&delta_from_largest_observed)) { + set_detailed_error("Unable to read sequence delta in received packets."); + return false; + } + + if (largest_acked.ToUint64() <= delta_from_largest_observed) { + set_detailed_error(QuicStrCat("delta_from_largest_observed too high: ", + delta_from_largest_observed, + ", largest_acked: ", largest_acked.ToUint64()) + .c_str()); + return false; + } + + // Time delta from the framer creation. + uint32_t time_delta_us; + if (!reader->ReadUInt32(&time_delta_us)) { + set_detailed_error("Unable to read time delta in received packets."); + return false; + } + + QuicPacketNumber seq_num = largest_acked - delta_from_largest_observed; + if (process_timestamps_) { + last_timestamp_ = CalculateTimestampFromWire(time_delta_us); + + visitor_->OnAckTimestamp(seq_num, creation_time_ + last_timestamp_); + } + + for (uint8_t i = 1; i < num_received_packets; ++i) { + if (!reader->ReadUInt8(&delta_from_largest_observed)) { + set_detailed_error("Unable to read sequence delta in received packets."); + return false; + } + if (largest_acked.ToUint64() <= delta_from_largest_observed) { + set_detailed_error( + QuicStrCat("delta_from_largest_observed too high: ", + delta_from_largest_observed, + ", largest_acked: ", largest_acked.ToUint64()) + .c_str()); + return false; + } + seq_num = largest_acked - delta_from_largest_observed; + + // Time delta from the previous timestamp. + uint64_t incremental_time_delta_us; + if (!reader->ReadUFloat16(&incremental_time_delta_us)) { + set_detailed_error( + "Unable to read incremental time delta in received packets."); + return false; + } + + if (process_timestamps_) { + last_timestamp_ = last_timestamp_ + QuicTime::Delta::FromMicroseconds( + incremental_time_delta_us); + visitor_->OnAckTimestamp(seq_num, creation_time_ + last_timestamp_); + } + } + return true; +} + +bool QuicFramer::ProcessIetfAckFrame(QuicDataReader* reader, + uint64_t frame_type, + QuicAckFrame* ack_frame) { + uint64_t largest_acked; + if (!reader->ReadVarInt62(&largest_acked)) { + set_detailed_error("Unable to read largest acked."); + return false; + } + if (largest_acked < first_sending_packet_number_.ToUint64()) { + // Connection always sends packet starting from kFirstSendingPacketNumber > + // 0, peer has observed an unsent packet. + set_detailed_error("Largest acked is 0."); + return false; + } + ack_frame->largest_acked = static_cast<QuicPacketNumber>(largest_acked); + uint64_t ack_delay_time_in_us; + if (!reader->ReadVarInt62(&ack_delay_time_in_us)) { + set_detailed_error("Unable to read ack delay time."); + return false; + } + + // TODO(fkastenholz) when we get real IETF QUIC, need to get + // the currect shift from the transport parameters. + if (ack_delay_time_in_us == kVarInt62MaxValue) { + ack_frame->ack_delay_time = QuicTime::Delta::Infinite(); + } else { + ack_delay_time_in_us = (ack_delay_time_in_us << kIetfAckTimestampShift); + ack_frame->ack_delay_time = + QuicTime::Delta::FromMicroseconds(ack_delay_time_in_us); + } + if (frame_type == IETF_ACK_ECN) { + ack_frame->ecn_counters_populated = true; + if (!reader->ReadVarInt62(&ack_frame->ect_0_count)) { + set_detailed_error("Unable to read ack ect_0_count."); + return false; + } + if (!reader->ReadVarInt62(&ack_frame->ect_1_count)) { + set_detailed_error("Unable to read ack ect_1_count."); + return false; + } + if (!reader->ReadVarInt62(&ack_frame->ecn_ce_count)) { + set_detailed_error("Unable to read ack ecn_ce_count."); + return false; + } + } else { + ack_frame->ecn_counters_populated = false; + ack_frame->ect_0_count = 0; + ack_frame->ect_1_count = 0; + ack_frame->ecn_ce_count = 0; + } + if (!visitor_->OnAckFrameStart(QuicPacketNumber(largest_acked), + ack_frame->ack_delay_time)) { + // The visitor suppresses further processing of the packet. Although this is + // not a parsing error, returns false as this is in middle of processing an + // ACK frame. + set_detailed_error("Visitor suppresses further processing of ACK frame."); + return false; + } + + // Get number of ACK blocks from the packet. + uint64_t ack_block_count; + if (!reader->ReadVarInt62(&ack_block_count)) { + set_detailed_error("Unable to read ack block count."); + return false; + } + // There always is a first ACK block, which is the (number of packets being + // acked)-1, up to and including the packet at largest_acked. Therefore if the + // value is 0, then only largest is acked. If it is 1, then largest-1, + // largest] are acked, etc + uint64_t ack_block_value; + if (!reader->ReadVarInt62(&ack_block_value)) { + set_detailed_error("Unable to read first ack block length."); + return false; + } + // Calculate the packets being acked in the first block. + // +1 because AddRange implementation requires [low,high) + uint64_t block_high = largest_acked + 1; + uint64_t block_low = largest_acked - ack_block_value; + + // ack_block_value is the number of packets preceding the + // largest_acked packet which are in the block being acked. Thus, + // its maximum value is largest_acked-1. Test this, reporting an + // error if the value is wrong. + if (ack_block_value + first_sending_packet_number_.ToUint64() > + largest_acked) { + set_detailed_error(QuicStrCat("Underflow with first ack block length ", + ack_block_value + 1, " largest acked is ", + largest_acked, ".") + .c_str()); + return false; + } + + if (!visitor_->OnAckRange(QuicPacketNumber(block_low), + QuicPacketNumber(block_high))) { + // The visitor suppresses further processing of the packet. Although + // this is not a parsing error, returns false as this is in middle + // of processing an ACK frame. + set_detailed_error("Visitor suppresses further processing of ACK frame."); + return false; + } + + while (ack_block_count != 0) { + uint64_t gap_block_value; + // Get the sizes of the gap and ack blocks, + if (!reader->ReadVarInt62(&gap_block_value)) { + set_detailed_error("Unable to read gap block value."); + return false; + } + // It's an error if the gap is larger than the space from packet + // number 0 to the start of the block that's just been acked, PLUS + // there must be space for at least 1 packet to be acked. For + // example, if block_low is 10 and gap_block_value is 9, it means + // the gap block is 10 packets long, leaving no room for a packet + // to be acked. Thus, gap_block_value+2 can not be larger than + // block_low. + // The test is written this way to detect wrap-arounds. + if ((gap_block_value + 2) > block_low) { + set_detailed_error( + QuicStrCat("Underflow with gap block length ", gap_block_value + 1, + " previous ack block start is ", block_low, ".") + .c_str()); + return false; + } + + // Adjust block_high to be the top of the next ack block. + // There is a gap of |gap_block_value| packets between the bottom + // of ack block N and top of block N+1. Note that gap_block_value + // is he size of the gap minus 1 (per the QUIC protocol), and + // block_high is the packet number of the first packet of the gap + // (per the implementation of OnAckRange/AddAckRange, below). + block_high = block_low - 1 - gap_block_value; + + if (!reader->ReadVarInt62(&ack_block_value)) { + set_detailed_error("Unable to read ack block value."); + return false; + } + if (ack_block_value + first_sending_packet_number_.ToUint64() > + (block_high - 1)) { + set_detailed_error( + QuicStrCat("Underflow with ack block length ", ack_block_value + 1, + " latest ack block end is ", block_high - 1, ".") + .c_str()); + return false; + } + // Calculate the low end of the new nth ack block. The +1 is + // because the encoded value is the blocksize-1. + block_low = block_high - 1 - ack_block_value; + if (!visitor_->OnAckRange(QuicPacketNumber(block_low), + QuicPacketNumber(block_high))) { + // The visitor suppresses further processing of the packet. Although + // this is not a parsing error, returns false as this is in middle + // of processing an ACK frame. + set_detailed_error("Visitor suppresses further processing of ACK frame."); + return false; + } + + // Another one done. + ack_block_count--; + } + + return visitor_->OnAckFrameEnd(QuicPacketNumber(block_low)); +} + +bool QuicFramer::ProcessStopWaitingFrame(QuicDataReader* reader, + const QuicPacketHeader& header, + QuicStopWaitingFrame* stop_waiting) { + uint64_t least_unacked_delta; + if (!reader->ReadBytesToUInt64(header.packet_number_length, + &least_unacked_delta)) { + set_detailed_error("Unable to read least unacked delta."); + return false; + } + if (header.packet_number.ToUint64() <= least_unacked_delta) { + set_detailed_error("Invalid unacked delta."); + return false; + } + stop_waiting->least_unacked = header.packet_number - least_unacked_delta; + + return true; +} + +bool QuicFramer::ProcessRstStreamFrame(QuicDataReader* reader, + QuicRstStreamFrame* frame) { + if (!reader->ReadUInt32(&frame->stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + if (!reader->ReadUInt64(&frame->byte_offset)) { + set_detailed_error("Unable to read rst stream sent byte offset."); + return false; + } + + uint32_t error_code; + if (!reader->ReadUInt32(&error_code)) { + set_detailed_error("Unable to read rst stream error code."); + return false; + } + + if (error_code >= QUIC_STREAM_LAST_ERROR) { + // Ignore invalid stream error code if any. + error_code = QUIC_STREAM_LAST_ERROR; + } + + frame->error_code = static_cast<QuicRstStreamErrorCode>(error_code); + + return true; +} + +bool QuicFramer::ProcessConnectionCloseFrame(QuicDataReader* reader, + QuicConnectionCloseFrame* frame) { + uint32_t error_code; + if (!reader->ReadUInt32(&error_code)) { + set_detailed_error("Unable to read connection close error code."); + return false; + } + + if (error_code >= QUIC_LAST_ERROR) { + // Ignore invalid QUIC error code if any. + error_code = QUIC_LAST_ERROR; + } + + frame->error_code = static_cast<QuicErrorCode>(error_code); + + QuicStringPiece error_details; + if (!reader->ReadStringPiece16(&error_details)) { + set_detailed_error("Unable to read connection close error details."); + return false; + } + frame->error_details = QuicString(error_details); + + return true; +} + +bool QuicFramer::ProcessGoAwayFrame(QuicDataReader* reader, + QuicGoAwayFrame* frame) { + uint32_t error_code; + if (!reader->ReadUInt32(&error_code)) { + set_detailed_error("Unable to read go away error code."); + return false; + } + + if (error_code >= QUIC_LAST_ERROR) { + // Ignore invalid QUIC error code if any. + error_code = QUIC_LAST_ERROR; + } + frame->error_code = static_cast<QuicErrorCode>(error_code); + + uint32_t stream_id; + if (!reader->ReadUInt32(&stream_id)) { + set_detailed_error("Unable to read last good stream id."); + return false; + } + frame->last_good_stream_id = static_cast<QuicStreamId>(stream_id); + + QuicStringPiece reason_phrase; + if (!reader->ReadStringPiece16(&reason_phrase)) { + set_detailed_error("Unable to read goaway reason."); + return false; + } + frame->reason_phrase = QuicString(reason_phrase); + + return true; +} + +bool QuicFramer::ProcessWindowUpdateFrame(QuicDataReader* reader, + QuicWindowUpdateFrame* frame) { + if (!reader->ReadUInt32(&frame->stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + if (!reader->ReadUInt64(&frame->byte_offset)) { + set_detailed_error("Unable to read window byte_offset."); + return false; + } + + return true; +} + +bool QuicFramer::ProcessBlockedFrame(QuicDataReader* reader, + QuicBlockedFrame* frame) { + DCHECK_NE(QUIC_VERSION_99, version_.transport_version) + << "Attempt to process non-IETF frames but version is 99"; + + if (!reader->ReadUInt32(&frame->stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + return true; +} + +void QuicFramer::ProcessPaddingFrame(QuicDataReader* reader, + QuicPaddingFrame* frame) { + // Type byte has been read. + frame->num_padding_bytes = 1; + uint8_t next_byte; + while (!reader->IsDoneReading() && reader->PeekByte() == 0x00) { + reader->ReadBytes(&next_byte, 1); + DCHECK_EQ(0x00, next_byte); + ++frame->num_padding_bytes; + } +} + +bool QuicFramer::ProcessMessageFrame(QuicDataReader* reader, + bool no_message_length, + QuicMessageFrame* frame) { + if (no_message_length) { + QuicStringPiece remaining(reader->ReadRemainingPayload()); + frame->data = remaining.data(); + frame->message_length = remaining.length(); + return true; + } + + uint64_t message_length; + if (!reader->ReadVarInt62(&message_length)) { + set_detailed_error("Unable to read message length"); + return false; + } + + QuicStringPiece message_piece; + if (!reader->ReadStringPiece(&message_piece, message_length)) { + set_detailed_error("Unable to read message data"); + return false; + } + + frame->data = message_piece.data(); + frame->message_length = message_length; + + return true; +} + +// static +QuicStringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( + QuicTransportVersion version, + const QuicEncryptedPacket& encrypted, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool includes_version, + bool includes_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + uint64_t retry_token_length, + QuicVariableLengthIntegerLength length_length) { + // TODO(ianswett): This is identical to QuicData::AssociatedData. + return QuicStringPiece( + encrypted.data(), + GetStartOfEncryptedData(version, destination_connection_id_length, + source_connection_id_length, includes_version, + includes_diversification_nonce, + packet_number_length, retry_token_length_length, + retry_token_length, length_length)); +} + +void QuicFramer::SetDecrypter(EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter) { + DCHECK(alternative_decrypter_ == nullptr); + DCHECK_GE(level, decrypter_level_); + decrypter_ = std::move(decrypter); + decrypter_level_ = level; +} + +void QuicFramer::SetAlternativeDecrypter( + EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter, + bool latch_once_used) { + alternative_decrypter_ = std::move(decrypter); + alternative_decrypter_level_ = level; + alternative_decrypter_latch_ = latch_once_used; +} + +const QuicDecrypter* QuicFramer::decrypter() const { + return decrypter_.get(); +} + +const QuicDecrypter* QuicFramer::alternative_decrypter() const { + return alternative_decrypter_.get(); +} + +void QuicFramer::SetEncrypter(EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter) { + DCHECK_GE(level, 0); + DCHECK_LT(level, NUM_ENCRYPTION_LEVELS); + encrypter_[level] = std::move(encrypter); +} + +size_t QuicFramer::EncryptInPlace(EncryptionLevel level, + QuicPacketNumber packet_number, + size_t ad_len, + size_t total_len, + size_t buffer_len, + char* buffer) { + DCHECK(packet_number.IsInitialized()); + size_t output_length = 0; + if (!encrypter_[level]->EncryptPacket( + packet_number.ToUint64(), + QuicStringPiece(buffer, ad_len), // Associated data + QuicStringPiece(buffer + ad_len, total_len - ad_len), // Plaintext + buffer + ad_len, // Destination buffer + &output_length, buffer_len - ad_len)) { + RaiseError(QUIC_ENCRYPTION_FAILURE); + return 0; + } + + return ad_len + output_length; +} + +size_t QuicFramer::EncryptPayload(EncryptionLevel level, + QuicPacketNumber packet_number, + const QuicPacket& packet, + char* buffer, + size_t buffer_len) { + DCHECK(packet_number.IsInitialized()); + DCHECK(encrypter_[level] != nullptr); + + QuicStringPiece associated_data = + packet.AssociatedData(version_.transport_version); + // Copy in the header, because the encrypter only populates the encrypted + // plaintext content. + const size_t ad_len = associated_data.length(); + memmove(buffer, associated_data.data(), ad_len); + // Encrypt the plaintext into the buffer. + size_t output_length = 0; + if (!encrypter_[level]->EncryptPacket( + packet_number.ToUint64(), associated_data, + packet.Plaintext(version_.transport_version), buffer + ad_len, + &output_length, buffer_len - ad_len)) { + RaiseError(QUIC_ENCRYPTION_FAILURE); + return 0; + } + + return ad_len + output_length; +} + +size_t QuicFramer::GetCiphertextSize(EncryptionLevel level, + size_t plaintext_size) const { + return encrypter_[level]->GetCiphertextSize(plaintext_size); +} + +size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) { + // In order to keep the code simple, we don't have the current encryption + // level to hand. Both the NullEncrypter and AES-GCM have a tag length of 12. + size_t min_plaintext_size = ciphertext_size; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) { + if (encrypter_[i] != nullptr) { + size_t size = encrypter_[i]->GetMaxPlaintextSize(ciphertext_size); + if (size < min_plaintext_size) { + min_plaintext_size = size; + } + } + } + + return min_plaintext_size; +} + +bool QuicFramer::DecryptPayload(QuicStringPiece encrypted, + QuicStringPiece associated_data, + const QuicPacketHeader& header, + char* decrypted_buffer, + size_t buffer_length, + size_t* decrypted_length) { + DCHECK(decrypter_ != nullptr); + + bool success = decrypter_->DecryptPacket( + header.packet_number.ToUint64(), associated_data, encrypted, + decrypted_buffer, decrypted_length, buffer_length); + if (success) { + visitor_->OnDecryptedPacket(decrypter_level_); + } else if (alternative_decrypter_ != nullptr) { + if (header.nonce != nullptr) { + DCHECK_EQ(perspective_, Perspective::IS_CLIENT); + alternative_decrypter_->SetDiversificationNonce(*header.nonce); + } + bool try_alternative_decryption = true; + if (alternative_decrypter_level_ == ENCRYPTION_ZERO_RTT) { + if (perspective_ == Perspective::IS_CLIENT) { + if (header.nonce == nullptr) { + // Can not use INITIAL decryption without a diversification nonce. + try_alternative_decryption = false; + } + } else { + DCHECK(header.nonce == nullptr); + } + } + + if (try_alternative_decryption) { + success = alternative_decrypter_->DecryptPacket( + header.packet_number.ToUint64(), associated_data, encrypted, + decrypted_buffer, decrypted_length, buffer_length); + } + if (success) { + visitor_->OnDecryptedPacket(alternative_decrypter_level_); + if (alternative_decrypter_latch_) { + // Switch to the alternative decrypter and latch so that we cannot + // switch back. + decrypter_ = std::move(alternative_decrypter_); + decrypter_level_ = alternative_decrypter_level_; + alternative_decrypter_level_ = ENCRYPTION_NONE; + } else { + // Switch the alternative decrypter so that we use it first next time. + decrypter_.swap(alternative_decrypter_); + EncryptionLevel level = alternative_decrypter_level_; + alternative_decrypter_level_ = decrypter_level_; + decrypter_level_ = level; + } + } + } + + if (!success) { + QUIC_DVLOG(1) << ENDPOINT << "DecryptPacket failed for packet_number:" + << header.packet_number; + return false; + } + + return true; +} + +size_t QuicFramer::GetIetfAckFrameSize(const QuicAckFrame& frame) { + // Type byte, largest_acked, and delay_time are straight-forward. + size_t ack_frame_size = kQuicFrameTypeSize; + QuicPacketNumber largest_acked = LargestAcked(frame); + ack_frame_size += QuicDataWriter::GetVarInt62Len(largest_acked.ToUint64()); + uint64_t ack_delay_time_us; + ack_delay_time_us = frame.ack_delay_time.ToMicroseconds(); + ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift; + ack_frame_size += QuicDataWriter::GetVarInt62Len(ack_delay_time_us); + + // If |ecn_counters_populated| is true and any of the ecn counters is non-0 + // then the ecn counters are included... + if (frame.ecn_counters_populated && + (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { + ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_0_count); + ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_1_count); + ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ecn_ce_count); + } + + // The rest (ack_block_count, first_ack_block, and additional ack + // blocks, if any) depends: + uint64_t ack_block_count = frame.packets.NumIntervals(); + if (ack_block_count == 0) { + // If the QuicAckFrame has no Intervals, then it is interpreted + // as an ack of a single packet at QuicAckFrame.largest_acked. + // The resulting ack will consist of only the frame's + // largest_ack & first_ack_block fields. The first ack block will be 0 + // (indicating a single packet) and the ack block_count will be 0. + // Each 0 takes 1 byte when VarInt62 encoded. + ack_frame_size += 2; + return ack_frame_size; + } + + auto itr = frame.packets.rbegin(); + QuicPacketNumber ack_block_largest = largest_acked; + QuicPacketNumber ack_block_smallest; + if ((itr->max() - 1) == largest_acked) { + // If largest_acked + 1 is equal to the Max() of the first Interval + // in the QuicAckFrame then the first Interval is the first ack block of the + // frame; remaining Intervals are additional ack blocks. The QuicAckFrame's + // first Interval is encoded in the frame's largest_acked/first_ack_block, + // the remaining Intervals are encoded in additional ack blocks in the + // frame, and the packet's ack_block_count is the number of QuicAckFrame + // Intervals - 1. + ack_block_smallest = itr->min(); + itr++; + ack_block_count--; + } else { + // If QuicAckFrame.largest_acked is NOT equal to the Max() of + // the first Interval then it is interpreted as acking a single + // packet at QuicAckFrame.largest_acked, with additional + // Intervals indicating additional ack blocks. The encoding is + // a) The packet's largest_acked is the QuicAckFrame's largest + // acked, + // b) the first ack block size is 0, + // c) The packet's ack_block_count is the number of QuicAckFrame + // Intervals, and + // d) The QuicAckFrame Intervals are encoded in additional ack + // blocks in the packet. + ack_block_smallest = largest_acked; + } + size_t ack_block_count_size = QuicDataWriter::GetVarInt62Len(ack_block_count); + ack_frame_size += ack_block_count_size; + + uint64_t first_ack_block = ack_block_largest - ack_block_smallest; + size_t first_ack_block_size = QuicDataWriter::GetVarInt62Len(first_ack_block); + ack_frame_size += first_ack_block_size; + + // Account for the remaining Intervals, if any. + while (ack_block_count != 0) { + uint64_t gap_size = ack_block_smallest - itr->max(); + // Decrement per the protocol specification + size_t size_of_gap_size = QuicDataWriter::GetVarInt62Len(gap_size - 1); + ack_frame_size += size_of_gap_size; + + uint64_t block_size = itr->max() - itr->min(); + // Decrement per the protocol specification + size_t size_of_block_size = QuicDataWriter::GetVarInt62Len(block_size - 1); + ack_frame_size += size_of_block_size; + + ack_block_smallest = itr->min(); + itr++; + ack_block_count--; + } + + return ack_frame_size; +} + +size_t QuicFramer::GetAckFrameSize( + const QuicAckFrame& ack, + QuicPacketNumberLength packet_number_length) { + DCHECK(!ack.packets.Empty()); + size_t ack_size = 0; + + if (version_.transport_version == QUIC_VERSION_99) { + return GetIetfAckFrameSize(ack); + } + AckFrameInfo ack_info = GetAckFrameInfo(ack); + QuicPacketNumberLength largest_acked_length = + GetMinPacketNumberLength(version_.transport_version, LargestAcked(ack)); + QuicPacketNumberLength ack_block_length = GetMinPacketNumberLength( + version_.transport_version, QuicPacketNumber(ack_info.max_block_length)); + + ack_size = + GetMinAckFrameSize(version_.transport_version, largest_acked_length); + // First ack block length. + ack_size += ack_block_length; + if (ack_info.num_ack_blocks != 0) { + ack_size += kNumberOfAckBlocksSize; + ack_size += std::min(ack_info.num_ack_blocks, kMaxAckBlocks) * + (ack_block_length + PACKET_1BYTE_PACKET_NUMBER); + } + + // Include timestamps. + if (process_timestamps_) { + ack_size += GetAckFrameTimeStampSize(ack); + } + + return ack_size; +} + +size_t QuicFramer::GetAckFrameTimeStampSize(const QuicAckFrame& ack) { + if (ack.received_packet_times.empty()) { + return 0; + } + + return kQuicNumTimestampsLength + kQuicFirstTimestampLength + + (kQuicTimestampLength + kQuicTimestampPacketNumberGapLength) * + (ack.received_packet_times.size() - 1); +} + +size_t QuicFramer::ComputeFrameLength( + const QuicFrame& frame, + bool last_frame_in_packet, + QuicPacketNumberLength packet_number_length) { + switch (frame.type) { + case STREAM_FRAME: + return GetMinStreamFrameSize( + version_.transport_version, frame.stream_frame.stream_id, + frame.stream_frame.offset, last_frame_in_packet, + frame.stream_frame.data_length) + + frame.stream_frame.data_length; + case CRYPTO_FRAME: + return GetMinCryptoFrameSize(frame.crypto_frame->offset, + frame.crypto_frame->data_length) + + frame.crypto_frame->data_length; + case ACK_FRAME: { + return GetAckFrameSize(*frame.ack_frame, packet_number_length); + } + case STOP_WAITING_FRAME: + return GetStopWaitingFrameSize(version_.transport_version, + packet_number_length); + case MTU_DISCOVERY_FRAME: + // MTU discovery frames are serialized as ping frames. + return kQuicFrameTypeSize; + case MESSAGE_FRAME: + return GetMessageFrameSize(version_.transport_version, + last_frame_in_packet, + frame.message_frame->message_length); + case PADDING_FRAME: + DCHECK(false); + return 0; + default: + return GetRetransmittableControlFrameSize(version_.transport_version, + frame); + } +} + +bool QuicFramer::AppendTypeByte(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { + if (version_.transport_version == QUIC_VERSION_99) { + return AppendIetfTypeByte(frame, last_frame_in_packet, writer); + } + uint8_t type_byte = 0; + switch (frame.type) { + case STREAM_FRAME: + type_byte = + GetStreamFrameTypeByte(frame.stream_frame, last_frame_in_packet); + break; + case ACK_FRAME: + return true; + case MTU_DISCOVERY_FRAME: + type_byte = static_cast<uint8_t>(PING_FRAME); + break; + + case APPLICATION_CLOSE_FRAME: + set_detailed_error( + "Attempt to append APPLICATION_CLOSE frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case NEW_CONNECTION_ID_FRAME: + set_detailed_error( + "Attempt to append NEW_CONNECTION_ID frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case RETIRE_CONNECTION_ID_FRAME: + set_detailed_error( + "Attempt to append RETIRE_CONNECTION_ID frame and not in version " + "99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case NEW_TOKEN_FRAME: + set_detailed_error( + "Attempt to append NEW_TOKEN frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case MAX_STREAM_ID_FRAME: + set_detailed_error( + "Attempt to append MAX_STREAM_ID frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case STREAM_ID_BLOCKED_FRAME: + set_detailed_error( + "Attempt to append STREAM_ID_BLOCKED frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case PATH_RESPONSE_FRAME: + set_detailed_error( + "Attempt to append PATH_RESPONSE frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case PATH_CHALLENGE_FRAME: + set_detailed_error( + "Attempt to append PATH_CHALLENGE frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case STOP_SENDING_FRAME: + set_detailed_error( + "Attempt to append STOP_SENDING frame and not in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case MESSAGE_FRAME: + return true; + + default: + type_byte = static_cast<uint8_t>(frame.type); + break; + } + + return writer->WriteUInt8(type_byte); +} + +bool QuicFramer::AppendIetfTypeByte(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { + uint8_t type_byte = 0; + switch (frame.type) { + case PADDING_FRAME: + type_byte = IETF_PADDING; + break; + case RST_STREAM_FRAME: + type_byte = IETF_RST_STREAM; + break; + case CONNECTION_CLOSE_FRAME: + type_byte = IETF_CONNECTION_CLOSE; + break; + case GOAWAY_FRAME: + set_detailed_error( + "Attempt to create non-version-99 GOAWAY frame in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case WINDOW_UPDATE_FRAME: + // Depending on whether there is a stream ID or not, will be either a + // MAX_STREAM_DATA frame or a MAX_DATA frame. + if (frame.window_update_frame->stream_id == + QuicUtils::GetInvalidStreamId(transport_version())) { + type_byte = IETF_MAX_DATA; + } else { + type_byte = IETF_MAX_STREAM_DATA; + } + break; + case BLOCKED_FRAME: + if (frame.blocked_frame->stream_id == + QuicUtils::GetInvalidStreamId(transport_version())) { + type_byte = IETF_BLOCKED; + } else { + type_byte = IETF_STREAM_BLOCKED; + } + break; + case STOP_WAITING_FRAME: + set_detailed_error( + "Attempt to append type byte of STOP WAITING frame in version 99."); + return RaiseError(QUIC_INTERNAL_ERROR); + case PING_FRAME: + type_byte = IETF_PING; + break; + case STREAM_FRAME: + type_byte = + GetStreamFrameTypeByte(frame.stream_frame, last_frame_in_packet); + break; + case ACK_FRAME: + // Do nothing here, AppendIetfAckFrameAndTypeByte() will put the type byte + // in the buffer. + return true; + case MTU_DISCOVERY_FRAME: + // The path MTU discovery frame is encoded as a PING frame on the wire. + type_byte = IETF_PING; + break; + case APPLICATION_CLOSE_FRAME: + type_byte = IETF_APPLICATION_CLOSE; + break; + case NEW_CONNECTION_ID_FRAME: + type_byte = IETF_NEW_CONNECTION_ID; + break; + case RETIRE_CONNECTION_ID_FRAME: + type_byte = IETF_RETIRE_CONNECTION_ID; + break; + case NEW_TOKEN_FRAME: + type_byte = IETF_NEW_TOKEN; + break; + case MAX_STREAM_ID_FRAME: + if (QuicUtils::IsBidirectionalStreamId( + frame.max_stream_id_frame.max_stream_id)) { + type_byte = IETF_MAX_STREAMS_BIDIRECTIONAL; + } else { + type_byte = IETF_MAX_STREAMS_UNIDIRECTIONAL; + } + break; + case STREAM_ID_BLOCKED_FRAME: + if (QuicUtils::IsBidirectionalStreamId( + frame.max_stream_id_frame.max_stream_id)) { + type_byte = IETF_STREAMS_BLOCKED_BIDIRECTIONAL; + } else { + type_byte = IETF_STREAMS_BLOCKED_UNIDIRECTIONAL; + } + break; + case PATH_RESPONSE_FRAME: + type_byte = IETF_PATH_RESPONSE; + break; + case PATH_CHALLENGE_FRAME: + type_byte = IETF_PATH_CHALLENGE; + break; + case STOP_SENDING_FRAME: + type_byte = IETF_STOP_SENDING; + break; + case MESSAGE_FRAME: + return true; + case CRYPTO_FRAME: + type_byte = IETF_CRYPTO; + break; + default: + QUIC_BUG << "Attempt to generate a frame type for an unsupported value: " + << frame.type; + return false; + } + return writer->WriteUInt8(type_byte); +} + +// static +bool QuicFramer::AppendPacketNumber(QuicPacketNumberLength packet_number_length, + QuicPacketNumber packet_number, + QuicDataWriter* writer) { + DCHECK(packet_number.IsInitialized()); + if (!IsValidPacketNumberLength(packet_number_length)) { + QUIC_BUG << "Invalid packet_number_length: " << packet_number_length; + return false; + } + return writer->WriteBytesToUInt64(packet_number_length, + packet_number.ToUint64()); +} + +// static +bool QuicFramer::AppendStreamId(size_t stream_id_length, + QuicStreamId stream_id, + QuicDataWriter* writer) { + if (stream_id_length == 0 || stream_id_length > 4) { + QUIC_BUG << "Invalid stream_id_length: " << stream_id_length; + return false; + } + return writer->WriteBytesToUInt64(stream_id_length, stream_id); +} + +// static +bool QuicFramer::AppendStreamOffset(size_t offset_length, + QuicStreamOffset offset, + QuicDataWriter* writer) { + if (offset_length == 1 || offset_length > 8) { + QUIC_BUG << "Invalid stream_offset_length: " << offset_length; + return false; + } + + return writer->WriteBytesToUInt64(offset_length, offset); +} + +// static +bool QuicFramer::AppendAckBlock(uint8_t gap, + QuicPacketNumberLength length_length, + uint64_t length, + QuicDataWriter* writer) { + if (length == 0) { + if (!IsValidPacketNumberLength(length_length)) { + QUIC_BUG << "Invalid packet_number_length: " << length_length; + return false; + } + return writer->WriteUInt8(gap) && + writer->WriteBytesToUInt64(length_length, length); + } + return writer->WriteUInt8(gap) && + AppendPacketNumber(length_length, QuicPacketNumber(length), writer); +} + +bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame, + bool no_stream_frame_length, + QuicDataWriter* writer) { + if (version_.transport_version == QUIC_VERSION_99) { + return AppendIetfStreamFrame(frame, no_stream_frame_length, writer); + } + if (!AppendStreamId(GetStreamIdSize(frame.stream_id), frame.stream_id, + writer)) { + QUIC_BUG << "Writing stream id size failed."; + return false; + } + if (!AppendStreamOffset( + GetStreamOffsetSize(version_.transport_version, frame.offset), + frame.offset, writer)) { + QUIC_BUG << "Writing offset size failed."; + return false; + } + if (!no_stream_frame_length) { + if ((frame.data_length > std::numeric_limits<uint16_t>::max()) || + !writer->WriteUInt16(static_cast<uint16_t>(frame.data_length))) { + QUIC_BUG << "Writing stream frame length failed"; + return false; + } + } + + if (data_producer_ != nullptr) { + DCHECK_EQ(nullptr, frame.data_buffer); + if (frame.data_length == 0) { + return true; + } + if (data_producer_->WriteStreamData(frame.stream_id, frame.offset, + frame.data_length, + writer) != WRITE_SUCCESS) { + QUIC_BUG << "Writing frame data failed."; + return false; + } + return true; + } + + if (!writer->WriteBytes(frame.data_buffer, frame.data_length)) { + QUIC_BUG << "Writing frame data failed."; + return false; + } + return true; +} + +// static +bool QuicFramer::AppendIetfConnectionId( + bool version_flag, + QuicConnectionId destination_connection_id, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionId source_connection_id, + QuicConnectionIdLength source_connection_id_length, + QuicDataWriter* writer) { + if (version_flag) { + // Append connection ID length byte. + uint8_t dcil = GetConnectionIdLengthValue(destination_connection_id_length); + uint8_t scil = GetConnectionIdLengthValue(source_connection_id_length); + uint8_t connection_id_length = dcil << 4 | scil; + if (!writer->WriteBytes(&connection_id_length, 1)) { + return false; + } + } + if (destination_connection_id_length == PACKET_8BYTE_CONNECTION_ID && + !writer->WriteConnectionId(destination_connection_id)) { + return false; + } + if (source_connection_id_length == PACKET_8BYTE_CONNECTION_ID && + !writer->WriteConnectionId(source_connection_id)) { + return false; + } + return true; +} + +bool QuicFramer::AppendNewTokenFrame(const QuicNewTokenFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.token.length()))) { + set_detailed_error("Writing token length failed."); + return false; + } + if (!writer->WriteBytes(frame.token.data(), frame.token.length())) { + set_detailed_error("Writing token buffer failed."); + return false; + } + return true; +} + +bool QuicFramer::ProcessNewTokenFrame(QuicDataReader* reader, + QuicNewTokenFrame* frame) { + uint64_t length; + if (!reader->ReadVarInt62(&length)) { + set_detailed_error("Unable to read new token length."); + return false; + } + if (length > kMaxNewTokenTokenLength) { + set_detailed_error("Token length larger than maximum."); + return false; + } + + // TODO(ianswett): Don't use QuicStringPiece as an intermediary. + QuicStringPiece data; + if (!reader->ReadStringPiece(&data, length)) { + set_detailed_error("Unable to read new token data."); + return false; + } + frame->token = QuicString(data); + return true; +} + +// Add a new ietf-format stream frame. +// Bits controlling whether there is a frame-length and frame-offset +// are in the QuicStreamFrame. +bool QuicFramer::AppendIetfStreamFrame(const QuicStreamFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.stream_id))) { + set_detailed_error("Writing stream id failed."); + return false; + } + + if (frame.offset != 0) { + if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.offset))) { + set_detailed_error("Writing data offset failed."); + return false; + } + } + + if (!last_frame_in_packet) { + if (!writer->WriteVarInt62(frame.data_length)) { + set_detailed_error("Writing data length failed."); + return false; + } + } + + if (frame.data_length == 0) { + return true; + } + if (data_producer_ == nullptr) { + if (!writer->WriteBytes(frame.data_buffer, frame.data_length)) { + set_detailed_error("Writing frame data failed."); + return false; + } + } else { + DCHECK_EQ(nullptr, frame.data_buffer); + + if (data_producer_->WriteStreamData(frame.stream_id, frame.offset, + frame.data_length, + writer) != WRITE_SUCCESS) { + set_detailed_error("Writing frame data failed."); + return false; + } + } + return true; +} + +bool QuicFramer::AppendCryptoFrame(const QuicCryptoFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.offset))) { + set_detailed_error("Writing data offset failed."); + return false; + } + if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.data_length))) { + set_detailed_error("Writing data length failed."); + return false; + } + if (data_producer_ == nullptr) { + if (frame.data_buffer == nullptr || + !writer->WriteBytes(frame.data_buffer, frame.data_length)) { + set_detailed_error("Writing frame data failed."); + return false; + } + } else { + DCHECK_EQ(nullptr, frame.data_buffer); + if (!data_producer_->WriteCryptoData(frame.level, frame.offset, + frame.data_length, writer)) { + return false; + } + } + return true; +} + +void QuicFramer::set_version(const ParsedQuicVersion version) { + DCHECK(IsSupportedVersion(version)) << ParsedQuicVersionToString(version); + version_ = version; +} + +bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame, + QuicDataWriter* writer) { + if (transport_version() == QUIC_VERSION_99) { + return AppendIetfAckFrameAndTypeByte(frame, writer); + } + + const AckFrameInfo new_ack_info = GetAckFrameInfo(frame); + QuicPacketNumber largest_acked = LargestAcked(frame); + QuicPacketNumberLength largest_acked_length = + GetMinPacketNumberLength(version_.transport_version, largest_acked); + QuicPacketNumberLength ack_block_length = + GetMinPacketNumberLength(version_.transport_version, + QuicPacketNumber(new_ack_info.max_block_length)); + // Calculate available bytes for timestamps and ack blocks. + int32_t available_timestamp_and_ack_block_bytes = + writer->capacity() - writer->length() - ack_block_length - + GetMinAckFrameSize(version_.transport_version, largest_acked_length) - + (new_ack_info.num_ack_blocks != 0 ? kNumberOfAckBlocksSize : 0); + DCHECK_LE(0, available_timestamp_and_ack_block_bytes); + + // Write out the type byte by setting the low order bits and doing shifts + // to make room for the next bit flags to be set. + // Whether there are multiple ack blocks. + uint8_t type_byte = 0; + SetBit(&type_byte, new_ack_info.num_ack_blocks != 0, + kQuicHasMultipleAckBlocksOffset); + + SetBits(&type_byte, GetPacketNumberFlags(largest_acked_length), + kQuicSequenceNumberLengthNumBits, kLargestAckedOffset); + + SetBits(&type_byte, GetPacketNumberFlags(ack_block_length), + kQuicSequenceNumberLengthNumBits, kActBlockLengthOffset); + + type_byte |= kQuicFrameTypeAckMask; + + if (!writer->WriteUInt8(type_byte)) { + return false; + } + + size_t max_num_ack_blocks = available_timestamp_and_ack_block_bytes / + (ack_block_length + PACKET_1BYTE_PACKET_NUMBER); + + // Number of ack blocks. + size_t num_ack_blocks = + std::min(new_ack_info.num_ack_blocks, max_num_ack_blocks); + if (num_ack_blocks > std::numeric_limits<uint8_t>::max()) { + num_ack_blocks = std::numeric_limits<uint8_t>::max(); + } + + // Largest acked. + if (!AppendPacketNumber(largest_acked_length, largest_acked, writer)) { + return false; + } + + // Largest acked delta time. + uint64_t ack_delay_time_us = kUFloat16MaxValue; + if (!frame.ack_delay_time.IsInfinite()) { + DCHECK_LE(0u, frame.ack_delay_time.ToMicroseconds()); + ack_delay_time_us = frame.ack_delay_time.ToMicroseconds(); + } + if (!writer->WriteUFloat16(ack_delay_time_us)) { + return false; + } + + if (num_ack_blocks > 0) { + if (!writer->WriteBytes(&num_ack_blocks, 1)) { + return false; + } + } + + // First ack block length. + if (!AppendPacketNumber(ack_block_length, + QuicPacketNumber(new_ack_info.first_block_length), + writer)) { + return false; + } + + // Ack blocks. + if (num_ack_blocks > 0) { + size_t num_ack_blocks_written = 0; + // Append, in descending order from the largest ACKed packet, a series of + // ACK blocks that represents the successfully acknoweldged packets. Each + // appended gap/block length represents a descending delta from the previous + // block. i.e.: + // |--- length ---|--- gap ---|--- length ---|--- gap ---|--- largest ---| + // For gaps larger than can be represented by a single encoded gap, a 0 + // length gap of the maximum is used, i.e.: + // |--- length ---|--- gap ---|- 0 -|--- gap ---|--- largest ---| + auto itr = frame.packets.rbegin(); + QuicPacketNumber previous_start = itr->min(); + ++itr; + + for (; + itr != frame.packets.rend() && num_ack_blocks_written < num_ack_blocks; + previous_start = itr->min(), ++itr) { + const auto& interval = *itr; + const uint64_t total_gap = previous_start - interval.max(); + const size_t num_encoded_gaps = + (total_gap + std::numeric_limits<uint8_t>::max() - 1) / + std::numeric_limits<uint8_t>::max(); + DCHECK_LE(0u, num_encoded_gaps); + + // Append empty ACK blocks because the gap is longer than a single gap. + for (size_t i = 1; + i < num_encoded_gaps && num_ack_blocks_written < num_ack_blocks; + ++i) { + if (!AppendAckBlock(std::numeric_limits<uint8_t>::max(), + ack_block_length, 0, writer)) { + return false; + } + ++num_ack_blocks_written; + } + if (num_ack_blocks_written >= num_ack_blocks) { + if (QUIC_PREDICT_FALSE(num_ack_blocks_written != num_ack_blocks)) { + QUIC_BUG << "Wrote " << num_ack_blocks_written + << ", expected to write " << num_ack_blocks; + } + break; + } + + const uint8_t last_gap = + total_gap - + (num_encoded_gaps - 1) * std::numeric_limits<uint8_t>::max(); + // Append the final ACK block with a non-empty size. + if (!AppendAckBlock(last_gap, ack_block_length, + PacketNumberIntervalLength(interval), writer)) { + return false; + } + ++num_ack_blocks_written; + } + DCHECK_EQ(num_ack_blocks, num_ack_blocks_written); + } + // Timestamps. + // If we don't process timestamps or if we don't have enough available space + // to append all the timestamps, don't append any of them. + if (process_timestamps_ && writer->capacity() - writer->length() >= + GetAckFrameTimeStampSize(frame)) { + if (!AppendTimestampsToAckFrame(frame, writer)) { + return false; + } + } else { + uint8_t num_received_packets = 0; + if (!writer->WriteBytes(&num_received_packets, 1)) { + return false; + } + } + + return true; +} + +bool QuicFramer::AppendTimestampsToAckFrame(const QuicAckFrame& frame, + QuicDataWriter* writer) { + DCHECK_GE(std::numeric_limits<uint8_t>::max(), + frame.received_packet_times.size()); + // num_received_packets is only 1 byte. + if (frame.received_packet_times.size() > + std::numeric_limits<uint8_t>::max()) { + return false; + } + + uint8_t num_received_packets = frame.received_packet_times.size(); + if (!writer->WriteBytes(&num_received_packets, 1)) { + return false; + } + if (num_received_packets == 0) { + return true; + } + + auto it = frame.received_packet_times.begin(); + QuicPacketNumber packet_number = it->first; + uint64_t delta_from_largest_observed = LargestAcked(frame) - packet_number; + + DCHECK_GE(std::numeric_limits<uint8_t>::max(), delta_from_largest_observed); + if (delta_from_largest_observed > std::numeric_limits<uint8_t>::max()) { + return false; + } + + if (!writer->WriteUInt8(delta_from_largest_observed)) { + return false; + } + + // Use the lowest 4 bytes of the time delta from the creation_time_. + const uint64_t time_epoch_delta_us = UINT64_C(1) << 32; + uint32_t time_delta_us = + static_cast<uint32_t>((it->second - creation_time_).ToMicroseconds() & + (time_epoch_delta_us - 1)); + if (!writer->WriteUInt32(time_delta_us)) { + return false; + } + + QuicTime prev_time = it->second; + + for (++it; it != frame.received_packet_times.end(); ++it) { + packet_number = it->first; + delta_from_largest_observed = LargestAcked(frame) - packet_number; + + if (delta_from_largest_observed > std::numeric_limits<uint8_t>::max()) { + return false; + } + + if (!writer->WriteUInt8(delta_from_largest_observed)) { + return false; + } + + uint64_t frame_time_delta_us = (it->second - prev_time).ToMicroseconds(); + prev_time = it->second; + if (!writer->WriteUFloat16(frame_time_delta_us)) { + return false; + } + } + return true; +} + +bool QuicFramer::AppendStopWaitingFrame(const QuicPacketHeader& header, + const QuicStopWaitingFrame& frame, + QuicDataWriter* writer) { + DCHECK_GE(QUIC_VERSION_43, version_.transport_version); + DCHECK(frame.least_unacked.IsInitialized() && + header.packet_number >= frame.least_unacked); + const uint64_t least_unacked_delta = + header.packet_number - frame.least_unacked; + const uint64_t length_shift = header.packet_number_length * 8; + + if (least_unacked_delta >> length_shift > 0) { + QUIC_BUG << "packet_number_length " << header.packet_number_length + << " is too small for least_unacked_delta: " << least_unacked_delta + << " packet_number:" << header.packet_number + << " least_unacked:" << frame.least_unacked + << " version:" << version_.transport_version; + return false; + } + if (least_unacked_delta == 0) { + return writer->WriteBytesToUInt64(header.packet_number_length, + least_unacked_delta); + } + if (!AppendPacketNumber(header.packet_number_length, + QuicPacketNumber(least_unacked_delta), writer)) { + QUIC_BUG << " seq failed: " << header.packet_number_length; + return false; + } + + return true; +} + +int QuicFramer::CalculateIetfAckBlockCount(const QuicAckFrame& frame, + QuicDataWriter* writer, + size_t available_space) { + // Number of blocks requested in the frame + uint64_t ack_block_count = frame.packets.NumIntervals(); + + auto itr = frame.packets.rbegin(); + + int actual_block_count = 1; + uint64_t block_length = itr->max() - itr->min(); + size_t encoded_size = QuicDataWriter::GetVarInt62Len(block_length); + if (encoded_size > available_space) { + return 0; + } + available_space -= encoded_size; + QuicPacketNumber previous_ack_end = itr->min(); + ack_block_count--; + + while (ack_block_count) { + // Each block is a gap followed by another ACK. Calculate each value, + // determine the encoded lengths, and check against the available space. + itr++; + size_t gap = previous_ack_end - itr->max() - 1; + encoded_size = QuicDataWriter::GetVarInt62Len(gap); + + // Add the ACK block. + block_length = itr->max() - itr->min(); + encoded_size += QuicDataWriter::GetVarInt62Len(block_length); + + if (encoded_size > available_space) { + // No room for this block, so what we've + // done up to now is all that can be done. + return actual_block_count; + } + available_space -= encoded_size; + actual_block_count++; + previous_ack_end = itr->min(); + ack_block_count--; + } + // Ran through the whole thing! We can do all blocks. + return actual_block_count; +} + +bool QuicFramer::AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, + QuicDataWriter* writer) { + // Assume frame is an IETF_ACK frame. If |ecn_counters_populated| is true and + // any of the ECN counters is non-0 then turn it into an IETF_ACK+ECN frame. + uint8_t type = IETF_ACK; + if (frame.ecn_counters_populated && + (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { + type = IETF_ACK_ECN; + } + + if (!writer->WriteUInt8(type)) { + set_detailed_error("No room for frame-type"); + return false; + } + + QuicPacketNumber largest_acked = LargestAcked(frame); + if (!writer->WriteVarInt62(largest_acked.ToUint64())) { + set_detailed_error("No room for largest-acked in ack frame"); + return false; + } + + uint64_t ack_delay_time_us = kVarInt62MaxValue; + if (!frame.ack_delay_time.IsInfinite()) { + DCHECK_LE(0u, frame.ack_delay_time.ToMicroseconds()); + ack_delay_time_us = frame.ack_delay_time.ToMicroseconds(); + // TODO(fkastenholz): Use the shift from TLS transport parameters. + ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift; + } + + if (!writer->WriteVarInt62(ack_delay_time_us)) { + set_detailed_error("No room for ack-delay in ack frame"); + return false; + } + if (type == IETF_ACK_ECN) { + // Encode the ACK ECN fields + if (!writer->WriteVarInt62(frame.ect_0_count)) { + set_detailed_error("No room for ect_0_count in ack frame"); + return false; + } + if (!writer->WriteVarInt62(frame.ect_1_count)) { + set_detailed_error("No room for ect_1_count in ack frame"); + return false; + } + if (!writer->WriteVarInt62(frame.ecn_ce_count)) { + set_detailed_error("No room for ecn_ce_count in ack frame"); + return false; + } + } + + uint64_t ack_block_count = frame.packets.NumIntervals(); + if (ack_block_count == 0) { + // If the QuicAckFrame has no Intervals, then it is interpreted + // as an ack of a single packet at QuicAckFrame.largest_acked. + // The resulting ack will consist of only the frame's + // largest_ack & first_ack_block fields. The first ack block will be 0 + // (indicating a single packet) and the ack block_count will be 0. + if (!writer->WriteVarInt62(0)) { + set_detailed_error("No room for ack block count in ack frame"); + return false; + } + // size of the first block is 1 packet + if (!writer->WriteVarInt62(0)) { + set_detailed_error("No room for first ack block in ack frame"); + return false; + } + return true; + } + // Case 2 or 3 + auto itr = frame.packets.rbegin(); + + QuicPacketNumber ack_block_largest(largest_acked); + QuicPacketNumber ack_block_smallest; + if ((itr->max() - 1) == QuicPacketNumber(largest_acked)) { + // If largest_acked + 1 is equal to the Max() of the first Interval + // in the QuicAckFrame then the first Interval is the first ack block of the + // frame; remaining Intervals are additional ack blocks. The QuicAckFrame's + // first Interval is encoded in the frame's largest_acked/first_ack_block, + // the remaining Intervals are encoded in additional ack blocks in the + // frame, and the packet's ack_block_count is the number of QuicAckFrame + // Intervals - 1. + ack_block_smallest = itr->min(); + itr++; + ack_block_count--; + } else { + // If QuicAckFrame.largest_acked is NOT equal to the Max() of + // the first Interval then it is interpreted as acking a single + // packet at QuicAckFrame.largest_acked, with additional + // Intervals indicating additional ack blocks. The encoding is + // a) The packet's largest_acked is the QuicAckFrame's largest + // acked, + // b) the first ack block size is 0, + // c) The packet's ack_block_count is the number of QuicAckFrame + // Intervals, and + // d) The QuicAckFrame Intervals are encoded in additional ack + // blocks in the packet. + ack_block_smallest = largest_acked; + } + + if (!writer->WriteVarInt62(ack_block_count)) { + set_detailed_error("No room for ack block count in ack frame"); + return false; + } + + uint64_t first_ack_block = ack_block_largest - ack_block_smallest; + if (!writer->WriteVarInt62(first_ack_block)) { + set_detailed_error("No room for first ack block in ack frame"); + return false; + } + + // For the remaining QuicAckFrame Intervals, if any + while (ack_block_count != 0) { + uint64_t gap_size = ack_block_smallest - itr->max(); + if (!writer->WriteVarInt62(gap_size - 1)) { + set_detailed_error("No room for gap block in ack frame"); + return false; + } + + uint64_t block_size = itr->max() - itr->min(); + if (!writer->WriteVarInt62(block_size - 1)) { + set_detailed_error("No room for nth ack block in ack frame"); + return false; + } + + ack_block_smallest = itr->min(); + itr++; + ack_block_count--; + } + return true; +} + +bool QuicFramer::AppendRstStreamFrame(const QuicRstStreamFrame& frame, + QuicDataWriter* writer) { + if (version_.transport_version == QUIC_VERSION_99) { + return AppendIetfResetStreamFrame(frame, writer); + } + if (!writer->WriteUInt32(frame.stream_id)) { + return false; + } + + if (!writer->WriteUInt64(frame.byte_offset)) { + return false; + } + + uint32_t error_code = static_cast<uint32_t>(frame.error_code); + if (!writer->WriteUInt32(error_code)) { + return false; + } + + return true; +} + +bool QuicFramer::AppendConnectionCloseFrame( + const QuicConnectionCloseFrame& frame, + QuicDataWriter* writer) { + if (version_.transport_version == QUIC_VERSION_99) { + return AppendIetfConnectionCloseFrame(frame, writer); + } + uint32_t error_code = static_cast<uint32_t>(frame.error_code); + if (!writer->WriteUInt32(error_code)) { + return false; + } + if (!writer->WriteStringPiece16(TruncateErrorString(frame.error_details))) { + return false; + } + return true; +} + +bool QuicFramer::AppendGoAwayFrame(const QuicGoAwayFrame& frame, + QuicDataWriter* writer) { + uint32_t error_code = static_cast<uint32_t>(frame.error_code); + if (!writer->WriteUInt32(error_code)) { + return false; + } + uint32_t stream_id = static_cast<uint32_t>(frame.last_good_stream_id); + if (!writer->WriteUInt32(stream_id)) { + return false; + } + if (!writer->WriteStringPiece16(TruncateErrorString(frame.reason_phrase))) { + return false; + } + return true; +} + +bool QuicFramer::AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer) { + uint32_t stream_id = static_cast<uint32_t>(frame.stream_id); + if (!writer->WriteUInt32(stream_id)) { + return false; + } + if (!writer->WriteUInt64(frame.byte_offset)) { + return false; + } + return true; +} + +bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer) { + if (version_.transport_version == QUIC_VERSION_99) { + if (frame.stream_id == QuicUtils::GetInvalidStreamId(transport_version())) { + return AppendIetfBlockedFrame(frame, writer); + } + return AppendStreamBlockedFrame(frame, writer); + } + uint32_t stream_id = static_cast<uint32_t>(frame.stream_id); + if (!writer->WriteUInt32(stream_id)) { + return false; + } + return true; +} + +bool QuicFramer::AppendPaddingFrame(const QuicPaddingFrame& frame, + QuicDataWriter* writer) { + if (frame.num_padding_bytes == 0) { + return false; + } + if (frame.num_padding_bytes < 0) { + QUIC_BUG_IF(frame.num_padding_bytes != -1); + writer->WritePadding(); + return true; + } + // Please note, num_padding_bytes includes type byte which has been written. + return writer->WritePaddingBytes(frame.num_padding_bytes - 1); +} + +bool QuicFramer::AppendMessageFrameAndTypeByte(const QuicMessageFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { + uint8_t type_byte = last_frame_in_packet ? IETF_EXTENSION_MESSAGE_NO_LENGTH + : IETF_EXTENSION_MESSAGE; + if (!writer->WriteUInt8(type_byte)) { + return false; + } + if (!last_frame_in_packet && !writer->WriteVarInt62(frame.message_length)) { + return false; + } + for (const auto& slice : frame.message_data) { + if (!writer->WriteBytes(slice.data(), slice.length())) { + return false; + } + } + return true; +} + +bool QuicFramer::RaiseError(QuicErrorCode error) { + QUIC_DLOG(INFO) << ENDPOINT << "Error: " << QuicErrorCodeToString(error) + << " detail: " << detailed_error_; + set_error(error); + visitor_->OnError(this); + return false; +} + +bool QuicFramer::IsVersionNegotiation( + const QuicPacketHeader& header, + bool packet_has_ietf_packet_header) const { + if (perspective_ == Perspective::IS_SERVER) { + return false; + } + if (!packet_has_ietf_packet_header) { + return header.version_flag; + } + if (header.form == IETF_QUIC_SHORT_HEADER_PACKET) { + return false; + } + return header.long_packet_type == VERSION_NEGOTIATION; +} + +bool QuicFramer::StartsWithChlo(QuicStreamId id, + QuicStreamOffset offset) const { + if (data_producer_ == nullptr) { + QUIC_BUG << "Does not have data producer."; + return false; + } + char buf[sizeof(kCHLO)]; + QuicDataWriter writer(sizeof(kCHLO), buf); + if (data_producer_->WriteStreamData(id, offset, sizeof(kCHLO), &writer) != + WRITE_SUCCESS) { + QUIC_BUG << "Failed to write data for stream " << id << " with offset " + << offset << " data_length = " << sizeof(kCHLO); + return false; + } + + return strncmp(buf, reinterpret_cast<const char*>(&kCHLO), sizeof(kCHLO)) == + 0; +} + +bool QuicFramer::AppendIetfConnectionCloseFrame( + const QuicConnectionCloseFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteUInt16(static_cast<const uint16_t>(frame.error_code))) { + set_detailed_error("Can not write connection close frame error code"); + return false; + } + if (!writer->WriteVarInt62(frame.frame_type)) { + set_detailed_error("Writing frame type failed."); + return false; + } + + if (!writer->WriteStringPieceVarInt62( + TruncateErrorString(frame.error_details))) { + set_detailed_error("Can not write connection close phrase"); + return false; + } + return true; +} + +bool QuicFramer::AppendApplicationCloseFrame( + const QuicApplicationCloseFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteUInt16(static_cast<const uint16_t>(frame.error_code))) { + set_detailed_error("Can not write application close frame error code"); + return false; + } + + if (!writer->WriteStringPieceVarInt62( + TruncateErrorString(frame.error_details))) { + set_detailed_error("Can not write application close phrase"); + return false; + } + return true; +} + +bool QuicFramer::ProcessIetfConnectionCloseFrame( + QuicDataReader* reader, + QuicConnectionCloseFrame* frame) { + uint16_t code; + if (!reader->ReadUInt16(&code)) { + set_detailed_error("Unable to read connection close error code."); + return false; + } + frame->ietf_error_code = static_cast<QuicIetfTransportErrorCodes>(code); + + if (!reader->ReadVarInt62(&frame->frame_type)) { + set_detailed_error("Unable to read connection close frame type."); + return false; + } + + uint64_t phrase_length; + if (!reader->ReadVarInt62(&phrase_length)) { + set_detailed_error("Unable to read connection close error details."); + return false; + } + QuicStringPiece phrase; + if (!reader->ReadStringPiece(&phrase, static_cast<size_t>(phrase_length))) { + set_detailed_error("Unable to read connection close error details."); + return false; + } + frame->error_details = QuicString(phrase); + + return true; +} + +bool QuicFramer::ProcessApplicationCloseFrame( + QuicDataReader* reader, + QuicApplicationCloseFrame* frame) { + uint16_t code; + if (!reader->ReadUInt16(&code)) { + set_detailed_error("Unable to read application close error code."); + return false; + } + frame->error_code = static_cast<QuicErrorCode>(code); + + uint64_t phrase_length; + if (!reader->ReadVarInt62(&phrase_length)) { + set_detailed_error("Unable to read application close error details."); + return false; + } + QuicStringPiece phrase; + if (!reader->ReadStringPiece(&phrase, static_cast<size_t>(phrase_length))) { + set_detailed_error("Unable to read application close error details."); + return false; + } + frame->error_details = QuicString(phrase); + + return true; +} + +// IETF Quic Path Challenge/Response frames. +bool QuicFramer::ProcessPathChallengeFrame(QuicDataReader* reader, + QuicPathChallengeFrame* frame) { + if (!reader->ReadBytes(frame->data_buffer.data(), + frame->data_buffer.size())) { + set_detailed_error("Can not read path challenge data."); + return false; + } + return true; +} + +bool QuicFramer::ProcessPathResponseFrame(QuicDataReader* reader, + QuicPathResponseFrame* frame) { + if (!reader->ReadBytes(frame->data_buffer.data(), + frame->data_buffer.size())) { + set_detailed_error("Can not read path response data."); + return false; + } + return true; +} + +bool QuicFramer::AppendPathChallengeFrame(const QuicPathChallengeFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteBytes(frame.data_buffer.data(), frame.data_buffer.size())) { + set_detailed_error("Writing Path Challenge data failed."); + return false; + } + return true; +} + +bool QuicFramer::AppendPathResponseFrame(const QuicPathResponseFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteBytes(frame.data_buffer.data(), frame.data_buffer.size())) { + set_detailed_error("Writing Path Response data failed."); + return false; + } + return true; +} + +// Add a new ietf-format stream reset frame. +// General format is +// stream id +// application error code +// final offset +bool QuicFramer::AppendIetfResetStreamFrame(const QuicRstStreamFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.stream_id))) { + set_detailed_error("Writing reset-stream stream id failed."); + return false; + } + if (!writer->WriteUInt16(frame.ietf_error_code)) { + set_detailed_error("Writing reset-stream error code failed."); + return false; + } + if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.byte_offset))) { + set_detailed_error("Writing reset-stream final-offset failed."); + return false; + } + return true; +} + +bool QuicFramer::ProcessIetfResetStreamFrame(QuicDataReader* reader, + QuicRstStreamFrame* frame) { + // Get Stream ID from frame. ReadVarIntStreamID returns false + // if either A) there is a read error or B) the resulting value of + // the Stream ID is larger than the maximum allowed value. + if (!reader->ReadVarIntStreamId(&frame->stream_id)) { + set_detailed_error("Unable to read rst stream stream id."); + return false; + } + + if (!reader->ReadUInt16(&frame->ietf_error_code)) { + set_detailed_error("Unable to read rst stream error code."); + return false; + } + + if (!reader->ReadVarInt62(&frame->byte_offset)) { + set_detailed_error("Unable to read rst stream sent byte offset."); + return false; + } + return true; +} + +bool QuicFramer::ProcessStopSendingFrame( + QuicDataReader* reader, + QuicStopSendingFrame* stop_sending_frame) { + if (!reader->ReadVarIntStreamId(&stop_sending_frame->stream_id)) { + set_detailed_error("Unable to read stop sending stream id."); + return false; + } + + if (!reader->ReadUInt16(&stop_sending_frame->application_error_code)) { + set_detailed_error("Unable to read stop sending application error code."); + return false; + } + return true; +} + +bool QuicFramer::AppendStopSendingFrame( + const QuicStopSendingFrame& stop_sending_frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(stop_sending_frame.stream_id)) { + set_detailed_error("Can not write stop sending stream id"); + return false; + } + if (!writer->WriteUInt16(stop_sending_frame.application_error_code)) { + set_detailed_error("Can not write application error code"); + return false; + } + return true; +} + +// Append/process IETF-Format MAX_DATA Frame +bool QuicFramer::AppendMaxDataFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.byte_offset)) { + set_detailed_error("Can not write MAX_DATA byte-offset"); + return false; + } + return true; +} + +bool QuicFramer::ProcessMaxDataFrame(QuicDataReader* reader, + QuicWindowUpdateFrame* frame) { + frame->stream_id = QuicUtils::GetInvalidStreamId(transport_version()); + if (!reader->ReadVarInt62(&frame->byte_offset)) { + set_detailed_error("Can not read MAX_DATA byte-offset"); + return false; + } + return true; +} + +// Append/process IETF-Format MAX_STREAM_DATA Frame +bool QuicFramer::AppendMaxStreamDataFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.stream_id)) { + set_detailed_error("Can not write MAX_STREAM_DATA stream id"); + return false; + } + if (!writer->WriteVarInt62(frame.byte_offset)) { + set_detailed_error("Can not write MAX_STREAM_DATA byte-offset"); + return false; + } + return true; +} + +bool QuicFramer::ProcessMaxStreamDataFrame(QuicDataReader* reader, + QuicWindowUpdateFrame* frame) { + if (!reader->ReadVarIntStreamId(&frame->stream_id)) { + set_detailed_error("Can not read MAX_STREAM_DATA stream id"); + return false; + } + if (!reader->ReadVarInt62(&frame->byte_offset)) { + set_detailed_error("Can not read MAX_STREAM_DATA byte-count"); + return false; + } + return true; +} + +bool QuicFramer::AppendMaxStreamsFrame(const QuicMaxStreamIdFrame& frame, + QuicDataWriter* writer) { + // Convert from the stream id on which the connection is blocked to a count + QuicStreamId stream_count = + StreamIdToCount(version_.transport_version, frame.max_stream_id); + + if (!writer->WriteVarInt62(stream_count)) { + set_detailed_error("Can not write MAX_STREAMS stream count"); + return false; + } + return true; +} + +bool QuicFramer::ProcessMaxStreamsFrame(QuicDataReader* reader, + QuicMaxStreamIdFrame* frame, + uint64_t frame_type) { + QuicStreamId received_stream_count; + if (!reader->ReadVarIntStreamId(&received_stream_count)) { + set_detailed_error("Can not read MAX_STREAMS stream count."); + return false; + } + // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED + // frame is implemented and passed up to the stream ID manager. + if (received_stream_count == 0) { + set_detailed_error("MAX_STREAMS stream count of 0 not supported."); + return false; + } + // Note that this code assumes that the only possible error that + // StreamCountToId can detect is that the stream count is too big or is 0. + // Too big is prevented by passing in the minimum of the received count + // and the maximum supported count, ensuring that the stream ID is + // pegged at the maximum allowed ID. + // count==0 is handled above, so that detailed_error_ may be set + // properly. + return StreamCountToId( + std::min( + received_stream_count, + GetMaxStreamCount((frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL), + perspective_)), + /*unidirectional=*/(frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL), + perspective_, version_.transport_version, &frame->max_stream_id); +} + +bool QuicFramer::AppendIetfBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.offset)) { + set_detailed_error("Can not write blocked offset."); + return false; + } + return true; +} + +bool QuicFramer::ProcessIetfBlockedFrame(QuicDataReader* reader, + QuicBlockedFrame* frame) { + // Indicates that it is a BLOCKED frame (as opposed to STREAM_BLOCKED). + frame->stream_id = QuicUtils::GetInvalidStreamId(transport_version()); + if (!reader->ReadVarInt62(&frame->offset)) { + set_detailed_error("Can not read blocked offset."); + return false; + } + return true; +} + +bool QuicFramer::AppendStreamBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.stream_id)) { + set_detailed_error("Can not write stream blocked stream id."); + return false; + } + if (!writer->WriteVarInt62(frame.offset)) { + set_detailed_error("Can not write stream blocked offset."); + return false; + } + return true; +} + +bool QuicFramer::ProcessStreamBlockedFrame(QuicDataReader* reader, + QuicBlockedFrame* frame) { + if (!reader->ReadVarIntStreamId(&frame->stream_id)) { + set_detailed_error("Can not read stream blocked stream id."); + return false; + } + if (!reader->ReadVarInt62(&frame->offset)) { + set_detailed_error("Can not read stream blocked offset."); + return false; + } + return true; +} + +bool QuicFramer::AppendStreamsBlockedFrame( + const QuicStreamIdBlockedFrame& frame, + QuicDataWriter* writer) { + // Convert from the stream id on which the connection is blocked to a count + QuicStreamId stream_count = + StreamIdToCount(version_.transport_version, frame.stream_id); + + if (!writer->WriteVarInt62(stream_count)) { + set_detailed_error("Can not write STREAMS_BLOCKED stream count"); + return false; + } + return true; +} + +bool QuicFramer::ProcessStreamsBlockedFrame(QuicDataReader* reader, + QuicStreamIdBlockedFrame* frame, + uint64_t frame_type) { + QuicStreamId received_stream_count; + if (!reader->ReadVarIntStreamId(&received_stream_count)) { + set_detailed_error("Can not read STREAMS_BLOCKED stream id."); + return false; + } + // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED + // frame is implemented and passed up to the stream ID manager. + if (received_stream_count == 0) { + set_detailed_error("STREAMS_BLOCKED stream count 0 not supported."); + return false; + } + // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED + // frame is implemented and passed up to the stream ID manager. + if (received_stream_count > + GetMaxStreamCount((frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL), + ((perspective_ == Perspective::IS_CLIENT) + ? Perspective::IS_SERVER + : Perspective::IS_CLIENT))) { + // If stream count is such that the resulting stream ID would exceed our + // implementation limit, generate an error. + set_detailed_error( + "STREAMS_BLOCKED stream count exceeds implementation limit."); + return false; + } + // Convert the stream count to an ID that can be used. + // The STREAMS_BLOCKED frame is a request for more streams + // that the peer will initiate. If this node is a client, it + // means that the peer is a server, and wants server-initiated + // stream IDs. + return StreamCountToId( + received_stream_count, + /*unidirectional=*/(frame_type == IETF_STREAMS_BLOCKED_UNIDIRECTIONAL), + (perspective_ == Perspective::IS_CLIENT) ? Perspective::IS_SERVER + : Perspective::IS_CLIENT, + version_.transport_version, &frame->stream_id); +} + +bool QuicFramer::AppendNewConnectionIdFrame( + const QuicNewConnectionIdFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.sequence_number)) { + set_detailed_error("Can not write New Connection ID sequence number"); + return false; + } + if (!writer->WriteUInt8(frame.connection_id.length())) { + set_detailed_error( + "Can not write New Connection ID frame connection ID Length"); + return false; + } + if (!writer->WriteConnectionId(frame.connection_id)) { + set_detailed_error("Can not write New Connection ID frame connection ID"); + return false; + } + + if (!writer->WriteBytes( + static_cast<const void*>(&frame.stateless_reset_token), + sizeof(frame.stateless_reset_token))) { + set_detailed_error("Can not write New Connection ID Reset Token"); + return false; + } + return true; +} + +bool QuicFramer::ProcessNewConnectionIdFrame(QuicDataReader* reader, + QuicNewConnectionIdFrame* frame) { + if (!reader->ReadVarInt62(&frame->sequence_number)) { + set_detailed_error( + "Unable to read new connection ID frame sequence number."); + return false; + } + + uint8_t connection_id_length; + if (!reader->ReadUInt8(&connection_id_length)) { + set_detailed_error( + "Unable to read new connection ID frame connection id length."); + return false; + } + + if (connection_id_length != kQuicDefaultConnectionIdLength) { + set_detailed_error("Invalid new connection ID length."); + return false; + } + + if (!reader->ReadConnectionId(&frame->connection_id, connection_id_length)) { + set_detailed_error("Unable to read new connection ID frame connection id."); + return false; + } + + if (!reader->ReadBytes(&frame->stateless_reset_token, + sizeof(frame->stateless_reset_token))) { + set_detailed_error("Can not read new connection ID frame reset token."); + return false; + } + return true; +} + +bool QuicFramer::AppendRetireConnectionIdFrame( + const QuicRetireConnectionIdFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.sequence_number)) { + set_detailed_error("Can not write Retire Connection ID sequence number"); + return false; + } + return true; +} + +bool QuicFramer::ProcessRetireConnectionIdFrame( + QuicDataReader* reader, + QuicRetireConnectionIdFrame* frame) { + if (!reader->ReadVarInt62(&frame->sequence_number)) { + set_detailed_error( + "Unable to read retire connection ID frame sequence number."); + return false; + } + return true; +} + +uint8_t QuicFramer::GetStreamFrameTypeByte(const QuicStreamFrame& frame, + bool last_frame_in_packet) const { + if (version_.transport_version == QUIC_VERSION_99) { + return GetIetfStreamFrameTypeByte(frame, last_frame_in_packet); + } + uint8_t type_byte = 0; + // Fin bit. + type_byte |= frame.fin ? kQuicStreamFinMask : 0; + + // Data Length bit. + type_byte <<= kQuicStreamDataLengthShift; + type_byte |= last_frame_in_packet ? 0 : kQuicStreamDataLengthMask; + + // Offset 3 bits. + type_byte <<= kQuicStreamShift; + const size_t offset_len = + GetStreamOffsetSize(version_.transport_version, frame.offset); + if (offset_len > 0) { + type_byte |= offset_len - 1; + } + + // stream id 2 bits. + type_byte <<= kQuicStreamIdShift; + type_byte |= GetStreamIdSize(frame.stream_id) - 1; + type_byte |= kQuicFrameTypeStreamMask; // Set Stream Frame Type to 1. + + return type_byte; +} + +uint8_t QuicFramer::GetIetfStreamFrameTypeByte( + const QuicStreamFrame& frame, + bool last_frame_in_packet) const { + DCHECK_EQ(QUIC_VERSION_99, version_.transport_version); + uint8_t type_byte = IETF_STREAM; + if (!last_frame_in_packet) { + type_byte |= IETF_STREAM_FRAME_LEN_BIT; + } + if (frame.offset != 0) { + type_byte |= IETF_STREAM_FRAME_OFF_BIT; + } + if (frame.fin) { + type_byte |= IETF_STREAM_FRAME_FIN_BIT; + } + return type_byte; +} + +void QuicFramer::InferPacketHeaderTypeFromVersion() { + // This function should only be called when server connection negotiates the + // version. + DCHECK(perspective_ == Perspective::IS_SERVER && + !infer_packet_header_type_from_version_); + infer_packet_header_type_from_version_ = true; +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h new file mode 100644 index 0000000..168fef5 --- /dev/null +++ b/quic/core/quic_framer.h
@@ -0,0 +1,929 @@ +// Copyright (c) 2012 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_QUIC_FRAMER_H_ +#define QUICHE_QUIC_CORE_QUIC_FRAMER_H_ + +#include <cstddef> +#include <cstdint> +#include <memory> + +#include "base/macros.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/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +namespace test { +class QuicFramerPeer; +} // namespace test + +class QuicDataReader; +class QuicDataWriter; +class QuicFramer; +class QuicStreamFrameDataProducer; + +// Number of bytes reserved for the frame type preceding each frame. +const size_t kQuicFrameTypeSize = 1; +// Number of bytes reserved for error code. +const size_t kQuicErrorCodeSize = 4; +// Number of bytes reserved to denote the length of error details field. +const size_t kQuicErrorDetailsLengthSize = 2; + +// Maximum number of bytes reserved for stream id. +const size_t kQuicMaxStreamIdSize = 4; +// Maximum number of bytes reserved for byte offset in stream frame. +const size_t kQuicMaxStreamOffsetSize = 8; +// Number of bytes reserved to store payload length in stream frame. +const size_t kQuicStreamPayloadLengthSize = 2; +// Number of bytes to reserve for IQ Error codes (for the Connection Close, +// Application Close, and Reset Stream frames). +const size_t kQuicIetfQuicErrorCodeSize = 2; +// Minimum size of the IETF QUIC Error Phrase's length field +const size_t kIetfQuicMinErrorPhraseLengthSize = 1; + +// Size in bytes reserved for the delta time of the largest observed +// packet number in ack frames. +const size_t kQuicDeltaTimeLargestObservedSize = 2; +// Size in bytes reserved for the number of received packets with timestamps. +const size_t kQuicNumTimestampsSize = 1; +// Size in bytes reserved for the number of missing packets in ack frames. +const size_t kNumberOfNackRangesSize = 1; +// Size in bytes reserved for the number of ack blocks in ack frames. +const size_t kNumberOfAckBlocksSize = 1; +// Maximum number of missing packet ranges that can fit within an ack frame. +const size_t kMaxNackRanges = (1 << (kNumberOfNackRangesSize * 8)) - 1; +// Maximum number of ack blocks that can fit within an ack frame. +const size_t kMaxAckBlocks = (1 << (kNumberOfAckBlocksSize * 8)) - 1; + +// This class receives callbacks from the framer when packets +// are processed. +class QUIC_EXPORT_PRIVATE QuicFramerVisitorInterface { + public: + virtual ~QuicFramerVisitorInterface() {} + + // Called if an error is detected in the QUIC protocol. + virtual void OnError(QuicFramer* framer) = 0; + + // Called only when |perspective_| is IS_SERVER and the framer gets a + // packet with version flag true and the version on the packet doesn't match + // |quic_version_|. The visitor should return true after it updates the + // version of the |framer_| to |received_version| or false to stop processing + // this packet. + virtual bool OnProtocolVersionMismatch(ParsedQuicVersion received_version, + PacketHeaderFormat form) = 0; + + // Called when a new packet has been received, before it + // has been validated or processed. + virtual void OnPacket() = 0; + + // Called when a public reset packet has been parsed but has not yet + // been validated. + virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) = 0; + + // Called only when |perspective_| is IS_CLIENT and a version negotiation + // packet has been parsed. + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) = 0; + + // Called when all fields except packet number has been parsed, but has not + // been authenticated. If it returns false, framing for this packet will + // cease. + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketHeader& header) = 0; + + // Called when the unauthenticated portion of the header has been parsed. + // If OnUnauthenticatedHeader returns false, framing for this packet will + // cease. + virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) = 0; + + // Called when a packet has been decrypted. |level| is the encryption level + // of the packet. + virtual void OnDecryptedPacket(EncryptionLevel level) = 0; + + // Called when the complete header of a packet had been parsed. + // If OnPacketHeader returns false, framing for this packet will cease. + virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0; + + // Called when the packet being processed contains multiple IETF QUIC packets, + // which is due to there being more data after what is covered by the length + // field. |packet| contains the remaining data which can be processed. + // Note that this is called when the framer parses the length field, before + // it attempts to decrypt the first payload. It is the visitor's + // responsibility to buffer the packet and call ProcessPacket on it + // after the framer is done parsing the current payload. |packet| does not + // own its internal buffer, the visitor should make a copy of it. + virtual void OnCoalescedPacket(const QuicEncryptedPacket& packet) = 0; + + // Called when a StreamFrame has been parsed. + virtual bool OnStreamFrame(const QuicStreamFrame& frame) = 0; + + // Called when a CRYPTO frame has been parsed. + virtual bool OnCryptoFrame(const QuicCryptoFrame& frame) = 0; + + // Called when largest acked of an AckFrame has been parsed. + virtual bool OnAckFrameStart(QuicPacketNumber largest_acked, + QuicTime::Delta ack_delay_time) = 0; + + // Called when ack range [start, end) of an AckFrame has been parsed. + virtual bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) = 0; + + // Called when a timestamp in the AckFrame has been parsed. + virtual bool OnAckTimestamp(QuicPacketNumber packet_number, + QuicTime timestamp) = 0; + + // Called after the last ack range in an AckFrame has been parsed. + // |start| is the starting value of the last ack range. + virtual bool OnAckFrameEnd(QuicPacketNumber start) = 0; + + // Called when a StopWaitingFrame has been parsed. + virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) = 0; + + // Called when a QuicPaddingFrame has been parsed. + virtual bool OnPaddingFrame(const QuicPaddingFrame& frame) = 0; + + // Called when a PingFrame has been parsed. + virtual bool OnPingFrame(const QuicPingFrame& frame) = 0; + + // Called when a RstStreamFrame has been parsed. + virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0; + + // Called when a ConnectionCloseFrame has been parsed. + virtual bool OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) = 0; + + // Called when an IETF ApplicationCloseFrame has been parsed. + virtual bool OnApplicationCloseFrame( + const QuicApplicationCloseFrame& frame) = 0; + + // Called when a StopSendingFrame has been parsed. + virtual bool OnStopSendingFrame(const QuicStopSendingFrame& frame) = 0; + + // Called when a PathChallengeFrame has been parsed. + virtual bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) = 0; + + // Called when a PathResponseFrame has been parsed. + virtual bool OnPathResponseFrame(const QuicPathResponseFrame& frame) = 0; + + // Called when a GoAwayFrame has been parsed. + virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) = 0; + + // Called when a WindowUpdateFrame has been parsed. + virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0; + + // Called when a BlockedFrame has been parsed. + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) = 0; + + // Called when a NewConnectionIdFrame has been parsed. + virtual bool OnNewConnectionIdFrame( + const QuicNewConnectionIdFrame& frame) = 0; + + // Called when a RetireConnectionIdFrame has been parsed. + virtual bool OnRetireConnectionIdFrame( + const QuicRetireConnectionIdFrame& frame) = 0; + + // Called when a NewTokenFrame has been parsed. + virtual bool OnNewTokenFrame(const QuicNewTokenFrame& frame) = 0; + + // Called when a message frame has been parsed. + virtual bool OnMessageFrame(const QuicMessageFrame& frame) = 0; + + // Called when a packet has been completely processed. + virtual void OnPacketComplete() = 0; + + // Called to check whether |token| is a valid stateless reset token. + virtual bool IsValidStatelessResetToken(QuicUint128 token) const = 0; + + // Called when an IETF stateless reset packet has been parsed and validated + // with the stateless reset token. + virtual void OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) = 0; + + // Called when an IETF MaxStreamId frame has been parsed. + virtual bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) = 0; + + // Called when an IETF StreamIdBlocked frame has been parsed. + virtual bool OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& frame) = 0; +}; + +// Class for parsing and constructing QUIC packets. It has a +// QuicFramerVisitorInterface that is called when packets are parsed. +class QUIC_EXPORT_PRIVATE QuicFramer { + public: + // Constructs a new framer that installs a kNULL QuicEncrypter and + // QuicDecrypter for level ENCRYPTION_NONE. |supported_versions| specifies the + // list of supported QUIC versions. |quic_version_| is set to the maximum + // version in |supported_versions|. + QuicFramer(const ParsedQuicVersionVector& supported_versions, + QuicTime creation_time, + Perspective perspective, + uint8_t expected_connection_id_length); + QuicFramer(const QuicFramer&) = delete; + QuicFramer& operator=(const QuicFramer&) = delete; + + virtual ~QuicFramer(); + + // Returns true if |version| is a supported transport version. + bool IsSupportedTransportVersion(const QuicTransportVersion version) const; + + // Returns true if |version| is a supported protocol version. + bool IsSupportedVersion(const ParsedQuicVersion version) const; + + // Set callbacks to be called from the framer. A visitor must be set, or + // else the framer will likely crash. It is acceptable for the visitor + // to do nothing. If this is called multiple times, only the last visitor + // will be used. + void set_visitor(QuicFramerVisitorInterface* visitor) { visitor_ = visitor; } + + const ParsedQuicVersionVector& supported_versions() const { + return supported_versions_; + } + + QuicTransportVersion transport_version() const { + return version_.transport_version; + } + + ParsedQuicVersion version() const { return version_; } + + void set_version(const ParsedQuicVersion version); + + // Does not DCHECK for supported version. Used by tests to set unsupported + // version to trigger version negotiation. + void set_version_for_tests(const ParsedQuicVersion version) { + version_ = version; + } + + QuicErrorCode error() const { return error_; } + + // Allows enabling or disabling of timestamp processing and serialization. + void set_process_timestamps(bool process_timestamps) { + process_timestamps_ = process_timestamps; + } + + // Pass a UDP packet into the framer for parsing. + // Return true if the packet was processed succesfully. |packet| must be a + // single, complete UDP packet (not a frame of a packet). This packet + // might be null padded past the end of the payload, which will be correctly + // ignored. + bool ProcessPacket(const QuicEncryptedPacket& packet); + + // Largest size in bytes of all stream frame fields without the payload. + static size_t GetMinStreamFrameSize(QuicTransportVersion version, + QuicStreamId stream_id, + QuicStreamOffset offset, + bool last_frame_in_packet, + QuicPacketLength data_length); + // Returns the overhead of framing a CRYPTO frame with the specific offset and + // data length provided, but not counting the size of the data payload. + static size_t GetMinCryptoFrameSize(QuicStreamOffset offset, + QuicPacketLength data_length); + static size_t GetMessageFrameSize(QuicTransportVersion version, + bool last_frame_in_packet, + QuicByteCount length); + // Size in bytes of all ack frame fields without the missing packets or ack + // blocks. + static size_t GetMinAckFrameSize( + QuicTransportVersion version, + QuicPacketNumberLength largest_observed_length); + // Size in bytes of a stop waiting frame. + static size_t GetStopWaitingFrameSize( + QuicTransportVersion version, + QuicPacketNumberLength packet_number_length); + // Size in bytes of all reset stream frame fields. + static size_t GetRstStreamFrameSize(QuicTransportVersion version, + const QuicRstStreamFrame& frame); + // Size in bytes of all connection close frame fields without the error + // details and the missing packets from the enclosed ack frame. + static size_t GetMinConnectionCloseFrameSize( + QuicTransportVersion version, + const QuicConnectionCloseFrame& frame); + static size_t GetMinApplicationCloseFrameSize( + QuicTransportVersion version, + const QuicApplicationCloseFrame& frame); + // Size in bytes of all GoAway frame fields without the reason phrase. + static size_t GetMinGoAwayFrameSize(); + // Size in bytes of all WindowUpdate frame fields. + // For version 99, determines whether a MAX DATA or MAX STREAM DATA frame will + // be generated and calculates the appropriate size. + static size_t GetWindowUpdateFrameSize(QuicTransportVersion version, + const QuicWindowUpdateFrame& frame); + // Size in bytes of all MaxStreams frame fields. + static size_t GetMaxStreamsFrameSize(QuicTransportVersion version, + const QuicMaxStreamIdFrame& frame); + // Size in bytes of all StreamsBlocked frame fields. + static size_t GetStreamsBlockedFrameSize( + QuicTransportVersion version, + const QuicStreamIdBlockedFrame& frame); + // Size in bytes of all Blocked frame fields. + static size_t GetBlockedFrameSize(QuicTransportVersion version, + const QuicBlockedFrame& frame); + // Size in bytes of PathChallenge frame. + static size_t GetPathChallengeFrameSize(const QuicPathChallengeFrame& frame); + // Size in bytes of PathResponse frame. + static size_t GetPathResponseFrameSize(const QuicPathResponseFrame& frame); + // Size in bytes required to serialize the stream id. + static size_t GetStreamIdSize(QuicStreamId stream_id); + // Size in bytes required to serialize the stream offset. + static size_t GetStreamOffsetSize(QuicTransportVersion version, + QuicStreamOffset offset); + // Size in bytes for a serialized new connection id frame + static size_t GetNewConnectionIdFrameSize( + const QuicNewConnectionIdFrame& frame); + + // Size in bytes for a serialized retire connection id frame + static size_t GetRetireConnectionIdFrameSize( + const QuicRetireConnectionIdFrame& frame); + + // Size in bytes for a serialized new token frame + static size_t GetNewTokenFrameSize(const QuicNewTokenFrame& frame); + + // Size in bytes required for a serialized stop sending frame. + static size_t GetStopSendingFrameSize(const QuicStopSendingFrame& frame); + + // Size in bytes required for a serialized retransmittable control |frame|. + static size_t GetRetransmittableControlFrameSize(QuicTransportVersion version, + const QuicFrame& frame); + + // Returns the number of bytes added to the packet for the specified frame, + // and 0 if the frame doesn't fit. Includes the header size for the first + // frame. + size_t GetSerializedFrameLength(const QuicFrame& frame, + size_t free_bytes, + bool first_frame_in_packet, + bool last_frame_in_packet, + QuicPacketNumberLength packet_number_length); + + // Returns the associated data from the encrypted packet |encrypted| as a + // stringpiece. + static QuicStringPiece GetAssociatedDataFromEncryptedPacket( + QuicTransportVersion version, + const QuicEncryptedPacket& encrypted, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool includes_version, + bool includes_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + uint64_t retry_token_length, + QuicVariableLengthIntegerLength length_length); + + // Serializes a packet containing |frames| into |buffer|. + // Returns the length of the packet, which must not be longer than + // |packet_length|. Returns 0 if it fails to serialize. + size_t BuildDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames, + char* buffer, + size_t packet_length, + EncryptionLevel level); + + // Serializes a probing packet, which is a padded PING packet. Returns the + // length of the packet. Returns 0 if it fails to serialize. + size_t BuildConnectivityProbingPacket(const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + EncryptionLevel level); + + // Serializes a probing packet, which is a padded PING packet. Returns the + // length of the packet. Returns 0 if it fails to serialize. + size_t BuildConnectivityProbingPacketNew(const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + EncryptionLevel level); + + // Serialize a probing packet that uses IETF QUIC's PATH CHALLENGE frame. Also + // fills the packet with padding. + size_t BuildPaddedPathChallengePacket(const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + QuicPathFrameBuffer* payload, + QuicRandom* randomizer, + EncryptionLevel level); + + // Serialize a probing response packet that uses IETF QUIC's PATH RESPONSE + // frame. Also fills the packet with padding if |is_padded| is + // true. |payloads| is always emptied, even if the packet can not be + // successfully built. + size_t BuildPathResponsePacket(const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded, + EncryptionLevel level); + + // Returns a new public reset packet. + static std::unique_ptr<QuicEncryptedPacket> BuildPublicResetPacket( + const QuicPublicResetPacket& packet); + + // Returns a new IETF stateless reset packet. + static std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket( + QuicConnectionId connection_id, + QuicUint128 stateless_reset_token); + + // Returns a new version negotiation packet. + static std::unique_ptr<QuicEncryptedPacket> BuildVersionNegotiationPacket( + QuicConnectionId connection_id, + bool ietf_quic, + const ParsedQuicVersionVector& versions); + + // Returns a new IETF version negotiation packet. + static std::unique_ptr<QuicEncryptedPacket> BuildIetfVersionNegotiationPacket( + QuicConnectionId connection_id, + const ParsedQuicVersionVector& versions); + + // If header.version_flag is set, the version in the + // packet will be set -- but it will be set from version_ not + // header.versions. + bool AppendPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer, + size_t* length_field_offset); + bool AppendIetfHeaderTypeByte(const QuicPacketHeader& header, + QuicDataWriter* writer); + bool AppendIetfPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer, + size_t* length_field_offset); + bool WriteIetfLongHeaderLength(const QuicPacketHeader& header, + QuicDataWriter* writer, + size_t length_field_offset, + EncryptionLevel level); + bool AppendTypeByte(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); + bool AppendIetfTypeByte(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); + size_t AppendIetfFrames(const QuicFrames& frames, QuicDataWriter* writer); + bool AppendStreamFrame(const QuicStreamFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); + bool AppendCryptoFrame(const QuicCryptoFrame& frame, QuicDataWriter* writer); + + // SetDecrypter sets the primary decrypter, replacing any that already exists. + // If an alternative decrypter is in place then the function DCHECKs. This is + // intended for cases where one knows that future packets will be using the + // new decrypter and the previous decrypter is now obsolete. |level| indicates + // the encryption level of the new decrypter. + void SetDecrypter(EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter); + + // SetAlternativeDecrypter sets a decrypter that may be used to decrypt + // future packets. |level| indicates the encryption level of the decrypter. If + // |latch_once_used| is true, then the first time that the decrypter is + // successful it will replace the primary decrypter. Otherwise both + // decrypters will remain active and the primary decrypter will be the one + // last used. + void SetAlternativeDecrypter(EncryptionLevel level, + std::unique_ptr<QuicDecrypter> decrypter, + bool latch_once_used); + + const QuicDecrypter* decrypter() const; + const QuicDecrypter* alternative_decrypter() const; + + // Changes the encrypter used for level |level| to |encrypter|. + void SetEncrypter(EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter); + + // Encrypts a payload in |buffer|. |ad_len| is the length of the associated + // data. |total_len| is the length of the associated data plus plaintext. + // |buffer_len| is the full length of the allocated buffer. + size_t EncryptInPlace(EncryptionLevel level, + QuicPacketNumber packet_number, + size_t ad_len, + size_t total_len, + size_t buffer_len, + char* buffer); + + // Returns the length of the data encrypted into |buffer| if |buffer_len| is + // long enough, and otherwise 0. + size_t EncryptPayload(EncryptionLevel level, + QuicPacketNumber packet_number, + const QuicPacket& packet, + char* buffer, + size_t buffer_len); + + // Returns the length of the ciphertext that would be generated by encrypting + // to plaintext of size |plaintext_size| at the given level. + size_t GetCiphertextSize(EncryptionLevel level, size_t plaintext_size) const; + + // Returns the maximum length of plaintext that can be encrypted + // to ciphertext no larger than |ciphertext_size|. + size_t GetMaxPlaintextSize(size_t ciphertext_size); + + const QuicString& detailed_error() { return detailed_error_; } + + // The minimum packet number length required to represent |packet_number|. + static QuicPacketNumberLength GetMinPacketNumberLength( + QuicTransportVersion version, + QuicPacketNumber packet_number); + + void SetSupportedVersions(const ParsedQuicVersionVector& versions) { + supported_versions_ = versions; + version_ = versions[0]; + } + + // Tell framer to infer packet header type from version_. + void InferPacketHeaderTypeFromVersion(); + + // Returns true if data with |offset| of stream |id| starts with 'CHLO'. + bool StartsWithChlo(QuicStreamId id, QuicStreamOffset offset) const; + + // Returns true if |header| is considered as an stateless reset packet. + bool IsIetfStatelessResetPacket(const QuicPacketHeader& header) const; + + // Returns true if encrypter of |level| is available. + bool HasEncrypterOfEncryptionLevel(EncryptionLevel level) const; + + void set_validate_flags(bool value) { validate_flags_ = value; } + + Perspective perspective() const { return perspective_; } + + QuicVersionLabel last_version_label() const { return last_version_label_; } + + void set_data_producer(QuicStreamFrameDataProducer* data_producer) { + data_producer_ = data_producer; + } + + // Returns true if we are doing IETF-formatted packets. + // In the future this could encompass a wide variety of + // versions. Doing the test by name ("ietf format") rather + // than version number localizes the version/ietf-ness binding + // to this method. + bool is_ietf_format() { + return version_.transport_version == QUIC_VERSION_99; + } + + QuicTime creation_time() const { return creation_time_; } + + QuicPacketNumber first_sending_packet_number() const { + return first_sending_packet_number_; + } + + private: + friend class test::QuicFramerPeer; + + typedef std::map<QuicPacketNumber, uint8_t> NackRangeMap; + + struct AckFrameInfo { + AckFrameInfo(); + AckFrameInfo(const AckFrameInfo& other); + ~AckFrameInfo(); + + // The maximum ack block length. + QuicPacketCount max_block_length; + // Length of first ack block. + QuicPacketCount first_block_length; + // Number of ACK blocks needed for the ACK frame. + size_t num_ack_blocks; + }; + + bool ProcessDataPacket(QuicDataReader* reader, + QuicPacketHeader* header, + const QuicEncryptedPacket& packet, + char* decrypted_buffer, + size_t buffer_length); + + bool ProcessIetfDataPacket(QuicDataReader* encrypted_reader, + QuicPacketHeader* header, + const QuicEncryptedPacket& packet, + char* decrypted_buffer, + size_t buffer_length); + + bool ProcessPublicResetPacket(QuicDataReader* reader, + const QuicPacketHeader& header); + + bool ProcessVersionNegotiationPacket(QuicDataReader* reader, + const QuicPacketHeader& header); + + bool MaybeProcessIetfInitialRetryToken(QuicDataReader* encrypted_reader, + QuicPacketHeader* header); + + void MaybeProcessCoalescedPacket(const QuicDataReader& encrypted_reader, + uint64_t remaining_bytes_length, + const QuicPacketHeader& header); + + bool MaybeProcessIetfLength(QuicDataReader* encrypted_reader, + QuicPacketHeader* header); + + bool ProcessPublicHeader(QuicDataReader* reader, + bool packet_has_ietf_packet_header, + QuicPacketHeader* header); + + // Processes the unauthenticated portion of the header into |header| from + // the current QuicDataReader. Returns true on success, false on failure. + bool ProcessUnauthenticatedHeader(QuicDataReader* encrypted_reader, + QuicPacketHeader* header); + + bool ProcessIetfHeaderTypeByte(QuicDataReader* reader, + QuicPacketHeader* header); + bool ProcessIetfPacketHeader(QuicDataReader* reader, + QuicPacketHeader* header); + + // First processes possibly truncated packet number. Calculates the full + // packet number from the truncated one and the last seen packet number, and + // stores it to |packet_number|. + bool ProcessAndCalculatePacketNumber( + QuicDataReader* reader, + QuicPacketNumberLength packet_number_length, + QuicPacketNumber base_packet_number, + uint64_t* packet_number); + bool ProcessFrameData(QuicDataReader* reader, const QuicPacketHeader& header); + bool ProcessIetfFrameData(QuicDataReader* reader, + const QuicPacketHeader& header); + bool ProcessStreamFrame(QuicDataReader* reader, + uint8_t frame_type, + QuicStreamFrame* frame); + bool ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type); + bool ProcessTimestampsInAckFrame(uint8_t num_received_packets, + QuicPacketNumber largest_acked, + QuicDataReader* reader); + bool ProcessIetfAckFrame(QuicDataReader* reader, + uint64_t frame_type, + QuicAckFrame* ack_frame); + bool ProcessStopWaitingFrame(QuicDataReader* reader, + const QuicPacketHeader& header, + QuicStopWaitingFrame* stop_waiting); + bool ProcessRstStreamFrame(QuicDataReader* reader, QuicRstStreamFrame* frame); + bool ProcessConnectionCloseFrame(QuicDataReader* reader, + QuicConnectionCloseFrame* frame); + bool ProcessGoAwayFrame(QuicDataReader* reader, QuicGoAwayFrame* frame); + bool ProcessWindowUpdateFrame(QuicDataReader* reader, + QuicWindowUpdateFrame* frame); + bool ProcessBlockedFrame(QuicDataReader* reader, QuicBlockedFrame* frame); + void ProcessPaddingFrame(QuicDataReader* reader, QuicPaddingFrame* frame); + bool ProcessMessageFrame(QuicDataReader* reader, + bool no_message_length, + QuicMessageFrame* frame); + + bool DecryptPayload(QuicStringPiece encrypted, + QuicStringPiece associated_data, + const QuicPacketHeader& header, + char* decrypted_buffer, + size_t buffer_length, + size_t* decrypted_length); + + // Returns the full packet number from the truncated + // wire format version and the last seen packet number. + uint64_t CalculatePacketNumberFromWire( + QuicPacketNumberLength packet_number_length, + QuicPacketNumber base_packet_number, + uint64_t packet_number) const; + + // Returns the QuicTime::Delta corresponding to the time from when the framer + // was created. + const QuicTime::Delta CalculateTimestampFromWire(uint32_t time_delta_us); + + // Computes the wire size in bytes of time stamps in |ack|. + size_t GetAckFrameTimeStampSize(const QuicAckFrame& ack); + + // Computes the wire size in bytes of the |ack| frame. + size_t GetAckFrameSize(const QuicAckFrame& ack, + QuicPacketNumberLength packet_number_length); + // Computes the wire-size, in bytes, of the |frame| ack frame, for IETF Quic. + size_t GetIetfAckFrameSize(const QuicAckFrame& frame); + + // Computes the wire size in bytes of the |ack| frame. + size_t GetAckFrameSize(const QuicAckFrame& ack); + + // Computes the wire size in bytes of the payload of |frame|. + size_t ComputeFrameLength(const QuicFrame& frame, + bool last_frame_in_packet, + QuicPacketNumberLength packet_number_length); + + static bool AppendPacketNumber(QuicPacketNumberLength packet_number_length, + QuicPacketNumber packet_number, + QuicDataWriter* writer); + static bool AppendStreamId(size_t stream_id_length, + QuicStreamId stream_id, + QuicDataWriter* writer); + static bool AppendStreamOffset(size_t offset_length, + QuicStreamOffset offset, + QuicDataWriter* writer); + + // Appends a single ACK block to |writer| and returns true if the block was + // successfully appended. + static bool AppendAckBlock(uint8_t gap, + QuicPacketNumberLength length_length, + uint64_t length, + QuicDataWriter* writer); + + static uint8_t GetPacketNumberFlags( + QuicPacketNumberLength packet_number_length); + + static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame); + + static bool AppendIetfConnectionId( + bool version_flag, + QuicConnectionId destination_connection_id, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionId source_connection_id, + QuicConnectionIdLength source_connection_id_length, + QuicDataWriter* writer); + + // The Append* methods attempt to write the provided header or frame using the + // |writer|, and return true if successful. + + bool AppendAckFrameAndTypeByte(const QuicAckFrame& frame, + QuicDataWriter* builder); + bool AppendTimestampsToAckFrame(const QuicAckFrame& frame, + QuicDataWriter* writer); + + // Append IETF format ACK frame. + // + // AppendIetfAckFrameAndTypeByte adds the IETF type byte and the body + // of the frame. + bool AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, + QuicDataWriter* writer); + + // Used by AppendIetfAckFrameAndTypeByte to figure out how many ack + // blocks can be included. + int CalculateIetfAckBlockCount(const QuicAckFrame& frame, + QuicDataWriter* writer, + size_t available_space); + bool AppendStopWaitingFrame(const QuicPacketHeader& header, + const QuicStopWaitingFrame& frame, + QuicDataWriter* builder); + bool AppendRstStreamFrame(const QuicRstStreamFrame& frame, + QuicDataWriter* builder); + bool AppendConnectionCloseFrame(const QuicConnectionCloseFrame& frame, + QuicDataWriter* builder); + bool AppendGoAwayFrame(const QuicGoAwayFrame& frame, QuicDataWriter* writer); + bool AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer); + bool AppendBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer); + bool AppendPaddingFrame(const QuicPaddingFrame& frame, + QuicDataWriter* writer); + bool AppendMessageFrameAndTypeByte(const QuicMessageFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); + + // IETF frame processing methods. + bool ProcessIetfStreamFrame(QuicDataReader* reader, + uint8_t frame_type, + QuicStreamFrame* frame); + bool ProcessIetfConnectionCloseFrame(QuicDataReader* reader, + QuicConnectionCloseFrame* frame); + bool ProcessApplicationCloseFrame(QuicDataReader* reader, + QuicApplicationCloseFrame* frame); + bool ProcessPathChallengeFrame(QuicDataReader* reader, + QuicPathChallengeFrame* frame); + bool ProcessPathResponseFrame(QuicDataReader* reader, + QuicPathResponseFrame* frame); + bool ProcessIetfResetStreamFrame(QuicDataReader* reader, + QuicRstStreamFrame* frame); + bool ProcessStopSendingFrame(QuicDataReader* reader, + QuicStopSendingFrame* stop_sending_frame); + bool ProcessCryptoFrame(QuicDataReader* reader, QuicCryptoFrame* frame); + + // IETF frame appending methods. All methods append the type byte as well. + bool AppendIetfStreamFrame(const QuicStreamFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); + bool AppendIetfConnectionCloseFrame(const QuicConnectionCloseFrame& frame, + QuicDataWriter* writer); + bool AppendApplicationCloseFrame(const QuicApplicationCloseFrame& frame, + QuicDataWriter* writer); + bool AppendPathChallengeFrame(const QuicPathChallengeFrame& frame, + QuicDataWriter* writer); + bool AppendPathResponseFrame(const QuicPathResponseFrame& frame, + QuicDataWriter* writer); + bool AppendIetfResetStreamFrame(const QuicRstStreamFrame& frame, + QuicDataWriter* writer); + bool AppendStopSendingFrame(const QuicStopSendingFrame& stop_sending_frame, + QuicDataWriter* writer); + + // Append/consume IETF-Format MAX_DATA and MAX_STREAM_DATA frames + bool AppendMaxDataFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer); + bool AppendMaxStreamDataFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer); + bool ProcessMaxDataFrame(QuicDataReader* reader, + QuicWindowUpdateFrame* frame); + bool ProcessMaxStreamDataFrame(QuicDataReader* reader, + QuicWindowUpdateFrame* frame); + + bool AppendMaxStreamsFrame(const QuicMaxStreamIdFrame& frame, + QuicDataWriter* writer); + bool ProcessMaxStreamsFrame(QuicDataReader* reader, + QuicMaxStreamIdFrame* frame, + uint64_t frame_type); + + bool AppendIetfBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer); + bool ProcessIetfBlockedFrame(QuicDataReader* reader, QuicBlockedFrame* frame); + + bool AppendStreamBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer); + bool ProcessStreamBlockedFrame(QuicDataReader* reader, + QuicBlockedFrame* frame); + + bool AppendStreamsBlockedFrame(const QuicStreamIdBlockedFrame& frame, + QuicDataWriter* writer); + bool ProcessStreamsBlockedFrame(QuicDataReader* reader, + QuicStreamIdBlockedFrame* frame, + uint64_t frame_type); + + bool AppendNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame, + QuicDataWriter* writer); + bool ProcessNewConnectionIdFrame(QuicDataReader* reader, + QuicNewConnectionIdFrame* frame); + bool AppendRetireConnectionIdFrame(const QuicRetireConnectionIdFrame& frame, + QuicDataWriter* writer); + bool ProcessRetireConnectionIdFrame(QuicDataReader* reader, + QuicRetireConnectionIdFrame* frame); + + bool AppendNewTokenFrame(const QuicNewTokenFrame& frame, + QuicDataWriter* writer); + bool ProcessNewTokenFrame(QuicDataReader* reader, QuicNewTokenFrame* frame); + + bool RaiseError(QuicErrorCode error); + + // Returns true if |header| indicates a version negotiation packet. + bool IsVersionNegotiation(const QuicPacketHeader& header, + bool packet_has_ietf_packet_header) const; + + // Calculates and returns type byte of stream frame. + uint8_t GetStreamFrameTypeByte(const QuicStreamFrame& frame, + bool last_frame_in_packet) const; + uint8_t GetIetfStreamFrameTypeByte(const QuicStreamFrame& frame, + bool last_frame_in_packet) const; + + void set_error(QuicErrorCode error) { error_ = error; } + + void set_detailed_error(const char* error) { detailed_error_ = error; } + + QuicString detailed_error_; + QuicFramerVisitorInterface* visitor_; + QuicErrorCode error_; + // Updated by ProcessPacketHeader when it succeeds decrypting a larger packet. + QuicPacketNumber largest_packet_number_; + // Updated by WritePacketHeader. + QuicConnectionId last_serialized_connection_id_; + // The last QUIC version label received. + QuicVersionLabel last_version_label_; + // Version of the protocol being used. + ParsedQuicVersion version_; + // This vector contains QUIC versions which we currently support. + // This should be ordered such that the highest supported version is the first + // element, with subsequent elements in descending order (versions can be + // skipped as necessary). + ParsedQuicVersionVector supported_versions_; + // Primary decrypter used to decrypt packets during parsing. + std::unique_ptr<QuicDecrypter> decrypter_; + // Alternative decrypter that can also be used to decrypt packets. + std::unique_ptr<QuicDecrypter> alternative_decrypter_; + // The encryption level of |decrypter_|. + EncryptionLevel decrypter_level_; + // The encryption level of |alternative_decrypter_|. + EncryptionLevel alternative_decrypter_level_; + // |alternative_decrypter_latch_| is true if, when |alternative_decrypter_| + // successfully decrypts a packet, we should install it as the only + // decrypter. + bool alternative_decrypter_latch_; + // Encrypters used to encrypt packets via EncryptPayload(). + std::unique_ptr<QuicEncrypter> encrypter_[NUM_ENCRYPTION_LEVELS]; + // Tracks if the framer is being used by the entity that received the + // connection or the entity that initiated it. + Perspective perspective_; + // If false, skip validation that the public flags are set to legal values. + bool validate_flags_; + // The diversification nonce from the last received packet. + DiversificationNonce last_nonce_; + // If true, send and process timestamps in the ACK frame. + bool process_timestamps_; + // The creation time of the connection, used to calculate timestamps. + QuicTime creation_time_; + // The last timestamp received if process_timestamps_ is true. + QuicTime::Delta last_timestamp_; + + // If this is a framer of a connection, this is the packet number of first + // sending packet. If this is a framer of a framer of dispatcher, this is the + // packet number of sent packets (for those which have packet number). + const QuicPacketNumber first_sending_packet_number_; + + // If not null, framer asks data_producer_ to write stream frame data. Not + // owned. TODO(fayang): Consider add data producer to framer's constructor. + QuicStreamFrameDataProducer* data_producer_; + + // If true, framer infers packet header type (IETF/GQUIC) from version_. + // Otherwise, framer infers packet header type from first byte of a received + // packet. + bool infer_packet_header_type_from_version_; + + // IETF short headers contain a destination connection ID but do not + // encode its length. This variable contains the length we expect to read. + // This is also used to validate the long header connection ID lengths in + // older versions of QUIC. + const uint8_t expected_connection_id_length_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_FRAMER_H_
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc new file mode 100644 index 0000000..bcd9db6 --- /dev/null +++ b/quic/core/quic_framer_test.cc
@@ -0,0 +1,12929 @@ +// Copyright (c) 2012 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/quic_framer.h" + +#include <algorithm> +#include <cstdint> +#include <map> +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.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_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h" + +using testing::_; +using testing::Return; +using testing::Truly; + +namespace quic { +namespace test { +namespace { + +const uint64_t kEpoch = UINT64_C(1) << 32; +const uint64_t kMask = kEpoch - 1; + +const QuicUint128 kTestStatelessResetToken = 1010101; // 0x0F69B5 + +// Use fields in which each byte is distinct to ensure that every byte is +// framed correctly. The values are otherwise arbitrary. +QuicConnectionId FramerTestConnectionId() { + return TestConnectionId(UINT64_C(0xFEDCBA9876543210)); +} + +QuicConnectionId FramerTestConnectionIdPlusOne() { + return TestConnectionId(UINT64_C(0xFEDCBA9876543211)); +} + +const QuicPacketNumber kPacketNumber = QuicPacketNumber(UINT64_C(0x12345678)); +const QuicPacketNumber kSmallLargestObserved = + QuicPacketNumber(UINT16_C(0x1234)); +const QuicPacketNumber kSmallMissingPacket = QuicPacketNumber(UINT16_C(0x1233)); +const QuicPacketNumber kLeastUnacked = QuicPacketNumber(UINT64_C(0x012345670)); +const QuicStreamId kStreamId = UINT64_C(0x01020304); +// Note that the high 4 bits of the stream offset must be less than 0x40 +// in order to ensure that the value can be encoded using VarInt62 encoding. +const QuicStreamOffset kStreamOffset = UINT64_C(0x3A98FEDC32107654); +const QuicPublicResetNonceProof kNonceProof = UINT64_C(0xABCDEF0123456789); + +// In testing that we can ack the full range of packets... +// This is the largest packet number that can be represented in IETF QUIC +// varint62 format. +const QuicPacketNumber kLargestIetfLargestObserved = + QuicPacketNumber(UINT64_C(0x3fffffffffffffff)); +// Encodings for the two bits in a VarInt62 that +// describe the length of the VarInt61. For binary packet +// formats in this file, the convention is to code the +// first byte as +// kVarInt62FourBytes + 0x<value_in_that_byte> +const uint8_t kVarInt62OneByte = 0x00; +const uint8_t kVarInt62TwoBytes = 0x40; +const uint8_t kVarInt62FourBytes = 0x80; +const uint8_t kVarInt62EightBytes = 0xc0; + +class TestEncrypter : public QuicEncrypter { + public: + ~TestEncrypter() override {} + bool SetKey(QuicStringPiece key) override { return true; } + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; } + bool SetIV(QuicStringPiece iv) override { return true; } + bool EncryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece plaintext, + char* output, + size_t* output_length, + size_t max_output_length) override { + packet_number_ = QuicPacketNumber(packet_number); + associated_data_ = QuicString(associated_data); + plaintext_ = QuicString(plaintext); + memcpy(output, plaintext.data(), plaintext.length()); + *output_length = plaintext.length(); + return true; + } + size_t GetKeySize() const override { return 0; } + size_t GetNoncePrefixSize() const override { return 0; } + size_t GetIVSize() const override { return 0; } + size_t GetMaxPlaintextSize(size_t ciphertext_size) const override { + return ciphertext_size; + } + size_t GetCiphertextSize(size_t plaintext_size) const override { + return plaintext_size; + } + QuicStringPiece GetKey() const override { return QuicStringPiece(); } + QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); } + + QuicPacketNumber packet_number_; + QuicString associated_data_; + QuicString plaintext_; +}; + +class TestDecrypter : public QuicDecrypter { + public: + ~TestDecrypter() override {} + bool SetKey(QuicStringPiece key) override { return true; } + bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; } + bool SetIV(QuicStringPiece iv) override { return true; } + bool SetPreliminaryKey(QuicStringPiece key) override { + QUIC_BUG << "should not be called"; + return false; + } + bool SetDiversificationNonce(const DiversificationNonce& key) override { + return true; + } + bool DecryptPacket(uint64_t packet_number, + QuicStringPiece associated_data, + QuicStringPiece ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) override { + packet_number_ = QuicPacketNumber(packet_number); + associated_data_ = QuicString(associated_data); + ciphertext_ = QuicString(ciphertext); + memcpy(output, ciphertext.data(), ciphertext.length()); + *output_length = ciphertext.length(); + return true; + } + size_t GetKeySize() const override { return 0; } + size_t GetIVSize() const override { return 0; } + QuicStringPiece GetKey() const override { return QuicStringPiece(); } + QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); } + // Use a distinct value starting with 0xFFFFFF, which is never used by TLS. + uint32_t cipher_id() const override { return 0xFFFFFFF2; } + QuicPacketNumber packet_number_; + QuicString associated_data_; + QuicString ciphertext_; +}; + +class TestQuicVisitor : public QuicFramerVisitorInterface { + public: + TestQuicVisitor() + : error_count_(0), + version_mismatch_(0), + packet_count_(0), + frame_count_(0), + complete_packets_(0), + accept_packet_(true), + accept_public_header_(true) {} + + ~TestQuicVisitor() override {} + + void OnError(QuicFramer* f) override { + QUIC_DLOG(INFO) << "QuicFramer Error: " << QuicErrorCodeToString(f->error()) + << " (" << f->error() << ")"; + ++error_count_; + } + + void OnPacket() override {} + + void OnPublicResetPacket(const QuicPublicResetPacket& packet) override { + public_reset_packet_ = QuicMakeUnique<QuicPublicResetPacket>((packet)); + } + + void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) override { + version_negotiation_packet_ = + QuicMakeUnique<QuicVersionNegotiationPacket>((packet)); + } + + bool OnProtocolVersionMismatch(ParsedQuicVersion received_version, + PacketHeaderFormat /*form*/) override { + QUIC_DLOG(INFO) << "QuicFramer Version Mismatch, version: " + << received_version; + ++version_mismatch_; + return true; + } + + bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override { + header_ = QuicMakeUnique<QuicPacketHeader>((header)); + return accept_public_header_; + } + + bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override { + return true; + } + + void OnDecryptedPacket(EncryptionLevel level) override {} + + bool OnPacketHeader(const QuicPacketHeader& header) override { + ++packet_count_; + header_ = QuicMakeUnique<QuicPacketHeader>((header)); + return accept_packet_; + } + + void OnCoalescedPacket(const QuicEncryptedPacket& packet) override { + size_t coalesced_data_length = packet.length(); + char* coalesced_data = new char[coalesced_data_length]; + memcpy(coalesced_data, packet.data(), coalesced_data_length); + coalesced_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>( + coalesced_data, coalesced_data_length, + /*owns_buffer=*/true)); + } + + bool OnStreamFrame(const QuicStreamFrame& frame) override { + ++frame_count_; + // Save a copy of the data so it is valid after the packet is processed. + QuicString* string_data = + new QuicString(frame.data_buffer, frame.data_length); + stream_data_.push_back(QuicWrapUnique(string_data)); + stream_frames_.push_back(QuicMakeUnique<QuicStreamFrame>( + frame.stream_id, frame.fin, frame.offset, *string_data)); + return true; + } + + bool OnCryptoFrame(const QuicCryptoFrame& frame) override { + ++frame_count_; + // Save a copy of the data so it is valid after the packet is processed. + QuicString* string_data = + new QuicString(frame.data_buffer, frame.data_length); + crypto_data_.push_back(QuicWrapUnique(string_data)); + crypto_frames_.push_back(QuicMakeUnique<QuicCryptoFrame>( + ENCRYPTION_NONE, frame.offset, *string_data)); + return true; + } + + bool OnAckFrameStart(QuicPacketNumber largest_acked, + QuicTime::Delta ack_delay_time) override { + ++frame_count_; + QuicAckFrame ack_frame; + ack_frame.largest_acked = largest_acked; + ack_frame.ack_delay_time = ack_delay_time; + ack_frames_.push_back(QuicMakeUnique<QuicAckFrame>(ack_frame)); + return true; + } + + bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override { + DCHECK(!ack_frames_.empty()); + ack_frames_[ack_frames_.size() - 1]->packets.AddRange(start, end); + return true; + } + + bool OnAckTimestamp(QuicPacketNumber packet_number, + QuicTime timestamp) override { + ack_frames_[ack_frames_.size() - 1]->received_packet_times.push_back( + std::make_pair(packet_number, timestamp)); + return true; + } + + bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; } + + bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override { + ++frame_count_; + stop_waiting_frames_.push_back(QuicMakeUnique<QuicStopWaitingFrame>(frame)); + return true; + } + + bool OnPaddingFrame(const QuicPaddingFrame& frame) override { + padding_frames_.push_back(QuicMakeUnique<QuicPaddingFrame>(frame)); + return true; + } + + bool OnPingFrame(const QuicPingFrame& frame) override { + ++frame_count_; + ping_frames_.push_back(QuicMakeUnique<QuicPingFrame>(frame)); + return true; + } + + bool OnMessageFrame(const QuicMessageFrame& frame) override { + ++frame_count_; + message_frames_.push_back( + QuicMakeUnique<QuicMessageFrame>(frame.data, frame.message_length)); + return true; + } + + void OnPacketComplete() override { ++complete_packets_; } + + bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override { + rst_stream_frame_ = frame; + return true; + } + + bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override { + connection_close_frame_ = frame; + return true; + } + + bool OnApplicationCloseFrame( + const QuicApplicationCloseFrame& frame) override { + application_close_frame_ = frame; + return true; + } + + bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override { + stop_sending_frame_ = frame; + return true; + } + + bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override { + path_challenge_frame_ = frame; + return true; + } + + bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override { + path_response_frame_ = frame; + return true; + } + + bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override { + goaway_frame_ = frame; + return true; + } + + bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override { + max_stream_id_frame_ = frame; + return true; + } + + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override { + stream_id_blocked_frame_ = frame; + return true; + } + + bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override { + window_update_frame_ = frame; + return true; + } + + bool OnBlockedFrame(const QuicBlockedFrame& frame) override { + blocked_frame_ = frame; + return true; + } + + bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override { + new_connection_id_ = frame; + return true; + } + + bool OnRetireConnectionIdFrame( + const QuicRetireConnectionIdFrame& frame) override { + retire_connection_id_ = frame; + return true; + } + + bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override { + new_token_ = frame; + return true; + } + + bool IsValidStatelessResetToken(QuicUint128 token) const override { + return token == kTestStatelessResetToken; + } + + void OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) override { + stateless_reset_packet_ = + QuicMakeUnique<QuicIetfStatelessResetPacket>(packet); + } + + // Counters from the visitor_ callbacks. + int error_count_; + int version_mismatch_; + int packet_count_; + int frame_count_; + int complete_packets_; + bool accept_packet_; + bool accept_public_header_; + + std::unique_ptr<QuicPacketHeader> header_; + std::unique_ptr<QuicPublicResetPacket> public_reset_packet_; + std::unique_ptr<QuicIetfStatelessResetPacket> stateless_reset_packet_; + std::unique_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; + std::vector<std::unique_ptr<QuicStreamFrame>> stream_frames_; + std::vector<std::unique_ptr<QuicCryptoFrame>> crypto_frames_; + std::vector<std::unique_ptr<QuicAckFrame>> ack_frames_; + std::vector<std::unique_ptr<QuicStopWaitingFrame>> stop_waiting_frames_; + std::vector<std::unique_ptr<QuicPaddingFrame>> padding_frames_; + std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_; + std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_; + std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_; + QuicRstStreamFrame rst_stream_frame_; + QuicConnectionCloseFrame connection_close_frame_; + QuicApplicationCloseFrame application_close_frame_; + QuicStopSendingFrame stop_sending_frame_; + QuicGoAwayFrame goaway_frame_; + QuicPathChallengeFrame path_challenge_frame_; + QuicPathResponseFrame path_response_frame_; + QuicWindowUpdateFrame window_update_frame_; + QuicBlockedFrame blocked_frame_; + QuicStreamIdBlockedFrame stream_id_blocked_frame_; + QuicMaxStreamIdFrame max_stream_id_frame_; + QuicNewConnectionIdFrame new_connection_id_; + QuicRetireConnectionIdFrame retire_connection_id_; + QuicNewTokenFrame new_token_; + std::vector<std::unique_ptr<QuicString>> stream_data_; + std::vector<std::unique_ptr<QuicString>> crypto_data_; +}; + +// Simple struct for defining a packet's content, and associated +// parse error. +struct PacketFragment { + QuicString error_if_missing; + std::vector<unsigned char> fragment; +}; + +using PacketFragments = std::vector<struct PacketFragment>; + +ParsedQuicVersionVector AllSupportedVersionsIncludingTls() { + QuicFlagSaver flags; + SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true); + return AllSupportedVersions(); +} + +class QuicFramerTest : public QuicTestWithParam<ParsedQuicVersion> { + public: + QuicFramerTest() + : encrypter_(new test::TestEncrypter()), + decrypter_(new test::TestDecrypter()), + version_(GetParam()), + start_(QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(0x10)), + framer_(AllSupportedVersionsIncludingTls(), + start_, + Perspective::IS_SERVER, + kQuicDefaultConnectionIdLength) { + SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true); + framer_.set_version(version_); + framer_.SetDecrypter(ENCRYPTION_NONE, + std::unique_ptr<QuicDecrypter>(decrypter_)); + framer_.SetEncrypter(ENCRYPTION_NONE, + std::unique_ptr<QuicEncrypter>(encrypter_)); + + framer_.set_visitor(&visitor_); + framer_.InferPacketHeaderTypeFromVersion(); + } + + // Helper function to get unsigned char representation of the handshake + // protocol byte of the current QUIC version number. + unsigned char GetQuicVersionProtocolByte() { + return (CreateQuicVersionLabel(version_) >> 24) & 0xff; + } + + // Helper function to get unsigned char representation of digit in the + // units place of the current QUIC version number. + unsigned char GetQuicVersionDigitOnes() { + return CreateQuicVersionLabel(version_) & 0xff; + } + + // Helper function to get unsigned char representation of digit in the + // tens place of the current QUIC version number. + unsigned char GetQuicVersionDigitTens() { + return (CreateQuicVersionLabel(version_) >> 8) & 0xff; + } + + bool CheckEncryption(QuicPacketNumber packet_number, QuicPacket* packet) { + if (packet_number != encrypter_->packet_number_) { + QUIC_LOG(ERROR) << "Encrypted incorrect packet number. expected " + << packet_number + << " actual: " << encrypter_->packet_number_; + return false; + } + if (packet->AssociatedData(framer_.transport_version()) != + encrypter_->associated_data_) { + QUIC_LOG(ERROR) << "Encrypted incorrect associated data. expected " + << packet->AssociatedData(framer_.transport_version()) + << " actual: " << encrypter_->associated_data_; + return false; + } + if (packet->Plaintext(framer_.transport_version()) != + encrypter_->plaintext_) { + QUIC_LOG(ERROR) << "Encrypted incorrect plaintext data. expected " + << packet->Plaintext(framer_.transport_version()) + << " actual: " << encrypter_->plaintext_; + return false; + } + return true; + } + + bool CheckDecryption(const QuicEncryptedPacket& encrypted, + bool includes_version, + bool includes_diversification_nonce, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length) { + return CheckDecryption( + encrypted, includes_version, includes_diversification_nonce, + destination_connection_id_length, source_connection_id_length, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0); + } + + bool CheckDecryption( + const QuicEncryptedPacket& encrypted, + bool includes_version, + bool includes_diversification_nonce, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + QuicVariableLengthIntegerLength retry_token_length_length, + size_t retry_token_length, + QuicVariableLengthIntegerLength length_length) { + if (visitor_.header_->packet_number != decrypter_->packet_number_) { + QUIC_LOG(ERROR) << "Decrypted incorrect packet number. expected " + << visitor_.header_->packet_number + << " actual: " << decrypter_->packet_number_; + return false; + } + QuicStringPiece associated_data = + QuicFramer::GetAssociatedDataFromEncryptedPacket( + framer_.transport_version(), encrypted, + destination_connection_id_length, source_connection_id_length, + includes_version, includes_diversification_nonce, + PACKET_4BYTE_PACKET_NUMBER, retry_token_length_length, + retry_token_length, length_length); + if (associated_data != decrypter_->associated_data_) { + QUIC_LOG(ERROR) << "Decrypted incorrect associated data. expected " + << QuicTextUtils::HexEncode(associated_data) + << " actual: " + << QuicTextUtils::HexEncode(decrypter_->associated_data_); + return false; + } + QuicStringPiece ciphertext( + encrypted.AsStringPiece().substr(GetStartOfEncryptedData( + framer_.transport_version(), destination_connection_id_length, + source_connection_id_length, includes_version, + includes_diversification_nonce, PACKET_4BYTE_PACKET_NUMBER, + retry_token_length_length, retry_token_length, length_length))); + if (ciphertext != decrypter_->ciphertext_) { + QUIC_LOG(ERROR) << "Decrypted incorrect ciphertext data. expected " + << QuicTextUtils::HexEncode(ciphertext) << " actual: " + << QuicTextUtils::HexEncode(decrypter_->ciphertext_) + << " associated data: " + << QuicTextUtils::HexEncode(associated_data); + return false; + } + return true; + } + + char* AsChars(unsigned char* data) { return reinterpret_cast<char*>(data); } + + // Creates a new QuicEncryptedPacket by concatenating the various + // packet fragments in |fragments|. + std::unique_ptr<QuicEncryptedPacket> AssemblePacketFromFragments( + const PacketFragments& fragments) { + char* buffer = new char[kMaxPacketSize + 1]; + size_t len = 0; + for (const auto& fragment : fragments) { + memcpy(buffer + len, fragment.fragment.data(), fragment.fragment.size()); + len += fragment.fragment.size(); + } + return QuicMakeUnique<QuicEncryptedPacket>(buffer, len, true); + } + + void CheckFramingBoundaries(const PacketFragments& fragments, + QuicErrorCode error_code) { + std::unique_ptr<QuicEncryptedPacket> packet( + AssemblePacketFromFragments(fragments)); + // Check all the various prefixes of |packet| for the expected + // parse error and error code. + for (size_t i = 0; i < packet->length(); ++i) { + QuicString expected_error; + size_t len = 0; + for (const auto& fragment : fragments) { + len += fragment.fragment.size(); + if (i < len) { + expected_error = fragment.error_if_missing; + break; + } + } + + if (expected_error.empty()) + continue; + + CheckProcessingFails(*packet, i, expected_error, error_code); + } + } + + void CheckProcessingFails(const QuicEncryptedPacket& packet, + size_t len, + QuicString expected_error, + QuicErrorCode error_code) { + QuicEncryptedPacket encrypted(packet.data(), len, false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)) << "len: " << len; + EXPECT_EQ(expected_error, framer_.detailed_error()) << "len: " << len; + EXPECT_EQ(error_code, framer_.error()) << "len: " << len; + } + + void CheckProcessingFails(unsigned char* packet, + size_t len, + QuicString expected_error, + QuicErrorCode error_code) { + QuicEncryptedPacket encrypted(AsChars(packet), len, false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)) << "len: " << len; + EXPECT_EQ(expected_error, framer_.detailed_error()) << "len: " << len; + EXPECT_EQ(error_code, framer_.error()) << "len: " << len; + } + + // Checks if the supplied string matches data in the supplied StreamFrame. + void CheckStreamFrameData(QuicString str, QuicStreamFrame* frame) { + EXPECT_EQ(str, QuicString(frame->data_buffer, frame->data_length)); + } + + void CheckCalculatePacketNumber(uint64_t expected_packet_number, + QuicPacketNumber last_packet_number) { + uint64_t wire_packet_number = expected_packet_number & kMask; + EXPECT_EQ(expected_packet_number, + QuicFramerPeer::CalculatePacketNumberFromWire( + &framer_, PACKET_4BYTE_PACKET_NUMBER, last_packet_number, + wire_packet_number)) + << "last_packet_number: " << last_packet_number + << " wire_packet_number: " << wire_packet_number; + } + + std::unique_ptr<QuicPacket> BuildDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames) { + return BuildUnsizedDataPacket(&framer_, header, frames); + } + + std::unique_ptr<QuicPacket> BuildDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames, + size_t packet_size) { + return BuildUnsizedDataPacket(&framer_, header, frames, packet_size); + } + + // N starts at 1. + QuicStreamId GetNthStreamid(QuicTransportVersion transport_version, + Perspective perspective, + bool bidirectional, + int n) { + if (bidirectional) { + return QuicUtils::GetFirstBidirectionalStreamId(transport_version, + perspective) + + ((n - 1) * QuicUtils::StreamIdDelta(transport_version)); + } + // Unidirectional + return QuicUtils::GetFirstUnidirectionalStreamId(transport_version, + perspective) + + ((n - 1) * QuicUtils::StreamIdDelta(transport_version)); + } + + test::TestEncrypter* encrypter_; + test::TestDecrypter* decrypter_; + ParsedQuicVersion version_; + QuicTime start_; + QuicFramer framer_; + test::TestQuicVisitor visitor_; + SimpleBufferAllocator allocator_; +}; + +// Multiple test cases of QuicFramerTest use byte arrays to define packets for +// testing, and these byte arrays contain the QUIC version. This macro explodes +// the 32-bit version into four bytes in network order. Since it uses methods of +// QuicFramerTest, it is only valid to use this in a QuicFramerTest. +#define QUIC_VERSION_BYTES \ + GetQuicVersionProtocolByte(), '0', GetQuicVersionDigitTens(), \ + GetQuicVersionDigitOnes() + +// Run all framer tests with all supported versions of QUIC. +INSTANTIATE_TEST_SUITE_P( + QuicFramerTests, + QuicFramerTest, + ::testing::ValuesIn(AllSupportedVersionsIncludingTls())); + +TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearEpochStart) { + // A few quick manual sanity checks. + CheckCalculatePacketNumber(UINT64_C(1), QuicPacketNumber()); + CheckCalculatePacketNumber(kEpoch + 1, QuicPacketNumber(kMask)); + CheckCalculatePacketNumber(kEpoch, QuicPacketNumber(kMask)); + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(j, QuicPacketNumber()); + CheckCalculatePacketNumber(kEpoch - 1 - j, QuicPacketNumber()); + } + + // Cases where the last number was close to the start of the range. + for (QuicPacketNumber last = QuicPacketNumber(1); last < QuicPacketNumber(10); + last++) { + // Small numbers should not wrap (even if they're out of order). + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(j, last); + } + + // Large numbers should not wrap either (because we're near 0 already). + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(kEpoch - 1 - j, last); + } + } +} + +TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearEpochEnd) { + // Cases where the last number was close to the end of the range + for (uint64_t i = 0; i < 10; i++) { + QuicPacketNumber last = QuicPacketNumber(kEpoch - i); + + // Small numbers should wrap. + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(kEpoch + j, last); + } + + // Large numbers should not (even if they're out of order). + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(kEpoch - 1 - j, last); + } + } +} + +// Next check where we're in a non-zero epoch to verify we handle +// reverse wrapping, too. +TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearPrevEpoch) { + const uint64_t prev_epoch = 1 * kEpoch; + const uint64_t cur_epoch = 2 * kEpoch; + // Cases where the last number was close to the start of the range + for (uint64_t i = 0; i < 10; i++) { + QuicPacketNumber last = QuicPacketNumber(cur_epoch + i); + // Small number should not wrap (even if they're out of order). + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(cur_epoch + j, last); + } + + // But large numbers should reverse wrap. + for (uint64_t j = 0; j < 10; j++) { + uint64_t num = kEpoch - 1 - j; + CheckCalculatePacketNumber(prev_epoch + num, last); + } + } +} + +TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearNextEpoch) { + const uint64_t cur_epoch = 2 * kEpoch; + const uint64_t next_epoch = 3 * kEpoch; + // Cases where the last number was close to the end of the range + for (uint64_t i = 0; i < 10; i++) { + QuicPacketNumber last = QuicPacketNumber(next_epoch - 1 - i); + + // Small numbers should wrap. + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(next_epoch + j, last); + } + + // but large numbers should not (even if they're out of order). + for (uint64_t j = 0; j < 10; j++) { + uint64_t num = kEpoch - 1 - j; + CheckCalculatePacketNumber(cur_epoch + num, last); + } + } +} + +TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearNextMax) { + const uint64_t max_number = std::numeric_limits<uint64_t>::max(); + const uint64_t max_epoch = max_number & ~kMask; + + // Cases where the last number was close to the end of the range + for (uint64_t i = 0; i < 10; i++) { + // Subtract 1, because the expected next packet number is 1 more than the + // last packet number. + QuicPacketNumber last = QuicPacketNumber(max_number - i - 1); + + // Small numbers should not wrap, because they have nowhere to go. + for (uint64_t j = 0; j < 10; j++) { + CheckCalculatePacketNumber(max_epoch + j, last); + } + + // Large numbers should not wrap either. + for (uint64_t j = 0; j < 10; j++) { + uint64_t num = kEpoch - 1 - j; + CheckCalculatePacketNumber(max_epoch + num, last); + } + } +} + +TEST_P(QuicFramerTest, EmptyPacket) { + char packet[] = {0x00}; + QuicEncryptedPacket encrypted(packet, 0, false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); +} + +TEST_P(QuicFramerTest, LargePacket) { + // clang-format off + unsigned char packet[kMaxPacketSize + 1] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, 0x56, 0x34, 0x12, + // private flags + 0x00, + }; + unsigned char packet44[kMaxPacketSize + 1] = { + // type (short header 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, 0x56, 0x34, 0x12, + }; + unsigned char packet46[kMaxPacketSize + 1] = { + // type (short header 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, 0x56, 0x34, 0x12, + }; + // clang-format on + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + const size_t header_size = GetPacketHeaderSize( + framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0); + + memset(p + header_size, 0, kMaxPacketSize - header_size); + + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + EXPECT_QUIC_BUG(framer_.ProcessPacket(encrypted), "Packet too large:1"); + + ASSERT_TRUE(visitor_.header_.get()); + // Make sure we've parsed the packet header, so we can send an error. + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + // Make sure the correct error is propagated. + EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error()); +} + +TEST_P(QuicFramerTest, PacketHeader) { + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"Unable to read public flags.", + {0x28}}, + // connection_id + {"Unable to read ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + // clang-format on + + PacketFragments& fragments = packet; + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_FALSE(visitor_.header_->version_flag); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); +} + +TEST_P(QuicFramerTest, LongPacketHeader) { + // clang-format off + PacketFragments packet44 = { + // type (long header with packet type INITIAL) + {"Unable to read type.", + {0xFF}}, + // version tag + {"Unable to read protocol version.", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"Unable to read ConnectionId length.", + {0x50}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + PacketFragments packet46 = { + // type (long header with packet type INITIAL) + {"Unable to read type.", + {0xC3}}, + // version tag + {"Unable to read protocol version.", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"Unable to read ConnectionId length.", + {0x50}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + // clang-format on + + if (framer_.transport_version() <= QUIC_VERSION_43 || + QuicVersionHasLongHeaderLengths(framer_.transport_version())) { + return; + } + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44; + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_TRUE(visitor_.header_->version_flag); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries( + framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44, + QUIC_INVALID_PACKET_HEADER); +} + +TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { + QuicFramerPeer::SetLastSerializedConnectionId(&framer_, + FramerTestConnectionId()); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + // clang-format off + PacketFragments packet = { + // public flags (0 byte connection_id) + {"Unable to read public flags.", + {0x20}}, + // connection_id + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"Unable to read type.", + {0x32}}, + // connection_id + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"Unable to read type.", + {0x43}}, + // connection_id + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_FALSE(visitor_.header_->version_flag); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); +} + +TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { + // clang-format off + PacketFragments packet = { + // public flags (0 byte connection_id) + {"Unable to read public flags.", + {0x29}}, + // connection_id + {"Unable to read ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // version tag + {"Unable to read protocol version.", + {QUIC_VERSION_BYTES}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + + PacketFragments packet44 = { + // type (long header with packet type ZERO_RTT_PROTECTED) + {"Unable to read type.", + {0xFC}}, + // version tag + {"Unable to read protocol version.", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"Unable to read ConnectionId length.", + {0x50}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + + PacketFragments packet46 = { + // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes + // packet number) + {"Unable to read type.", + {0xD3}}, + // version tag + {"Unable to read protocol version.", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"Unable to read ConnectionId length.", + {0x50}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + + PacketFragments packet99 = { + // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes + // packet number) + {"Unable to read type.", + {0xD3}}, + // version tag + {"Unable to read protocol version.", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"Unable to read ConnectionId length.", + {0x50}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // long header packet length + {"Unable to read long header payload length.", + {0x04}}, + // packet number + {"Long header payload length longer than packet.", + {0x12, 0x34, 0x56, 0x78}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_TRUE(visitor_.header_->version_flag); + EXPECT_EQ(GetParam(), visitor_.header_->version); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); +} + +TEST_P(QuicFramerTest, PacketHeaderWith4BytePacketNumber) { + QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2); + + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id and 4 byte packet number) + {"Unable to read public flags.", + {0x28}}, + // connection_id + {"Unable to read ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"Unable to read type.", + {0x32}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"Unable to read type.", + {0x43}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x12, 0x34, 0x56, 0x78}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_FALSE(visitor_.header_->version_flag); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); +} + +TEST_P(QuicFramerTest, PacketHeaderWith2BytePacketNumber) { + QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2); + + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id and 2 byte packet number) + {"Unable to read public flags.", + {0x18}}, + // connection_id + {"Unable to read ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x56, 0x78}}, + }; + + PacketFragments packet44 = { + // type (short header, 2 byte packet number) + {"Unable to read type.", + {0x31}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x56, 0x78}}, + }; + + PacketFragments packet46 = { + // type (short header, 2 byte packet number) + {"Unable to read type.", + {0x41}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x56, 0x78}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_FALSE(visitor_.header_->version_flag); + EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); +} + +TEST_P(QuicFramerTest, PacketHeaderWith1BytePacketNumber) { + QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2); + + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id and 1 byte packet number) + {"Unable to read public flags.", + {0x08}}, + // connection_id + {"Unable to read ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x78}}, + }; + + PacketFragments packet44 = { + // type (8 byte connection_id and 1 byte packet number) + {"Unable to read type.", + {0x30}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x78}}, + }; + + PacketFragments packet46 = { + // type (8 byte connection_id and 1 byte packet number) + {"Unable to read type.", + {0x40}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"Unable to read packet number.", + {0x78}}, + }; + + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_FALSE(visitor_.header_->version_flag); + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER); +} + +TEST_P(QuicFramerTest, PacketNumberDecreasesThenIncreases) { + // Test the case when a packet is received from the past and future packet + // numbers are still calculated relative to the largest received packet. + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber - 2; + + QuicFrames frames = {QuicFrame(QuicPaddingFrame())}; + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + QuicEncryptedPacket encrypted(data->data(), data->length(), false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length); + EXPECT_EQ(kPacketNumber - 2, visitor_.header_->packet_number); + + // Receive a 1 byte packet number. + header.packet_number = kPacketNumber; + header.packet_number_length = PACKET_1BYTE_PACKET_NUMBER; + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + data = BuildDataPacket(header, frames); + QuicEncryptedPacket encrypted1(data->data(), data->length(), false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(encrypted1)); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + // Process a 2 byte packet number 256 packets ago. + header.packet_number = kPacketNumber - 256; + header.packet_number_length = PACKET_2BYTE_PACKET_NUMBER; + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + data = BuildDataPacket(header, frames); + QuicEncryptedPacket encrypted2(data->data(), data->length(), false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(encrypted2)); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length); + EXPECT_EQ(kPacketNumber - 256, visitor_.header_->packet_number); + + // Process another 1 byte packet number and ensure it works. + header.packet_number = kPacketNumber - 1; + header.packet_number_length = PACKET_1BYTE_PACKET_NUMBER; + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + data = BuildDataPacket(header, frames); + QuicEncryptedPacket encrypted3(data->data(), data->length(), false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(encrypted3)); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.header_->destination_connection_id); + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length); + EXPECT_EQ(kPacketNumber - 1, visitor_.header_->packet_number); +} + +TEST_P(QuicFramerTest, PacketWithDiversificationNonce) { + // clang-format off + unsigned char packet[] = { + // public flags: includes nonce flag + 0x2C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // nonce + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet44[] = { + // type: Long header with packet type ZERO_RTT_PROTECTED + 0xFC, + // version tag + QUIC_VERSION_BYTES, + // connection_id length + 0x05, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // nonce + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + + // frame type (padding) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet46[] = { + // type: Long header with packet type ZERO_RTT_PROTECTED and 1 byte packet + // number. + 0xD0, + // version tag + QUIC_VERSION_BYTES, + // connection_id length + 0x05, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, + // nonce + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + + // frame type (padding) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet99[] = { + // type: Long header with packet type ZERO_RTT_PROTECTED and 1 byte packet + // number. + 0xD0, + // version tag + QUIC_VERSION_BYTES, + // connection_id length + 0x05, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x26, + // packet number + 0x78, + // nonce + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + + // frame type (padding) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + if (framer_.version().handshake_protocol != PROTOCOL_QUIC_CRYPTO) { + return; + } + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + ASSERT_TRUE(visitor_.header_->nonce != nullptr); + for (char i = 0; i < 32; ++i) { + EXPECT_EQ(i, (*visitor_.header_->nonce)[static_cast<size_t>(i)]); + } + EXPECT_EQ(1u, visitor_.padding_frames_.size()); + EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes); +} + +TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id, version flag and an unknown flag) + 0x29, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + 'Q', '0', '0', '0', + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet44[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xFC, + // version tag + 'Q', '0', '0', '0', + // connection_id length + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + QuicEncryptedPacket encrypted( + AsChars(framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(0, visitor_.frame_count_); + EXPECT_EQ(1, visitor_.version_mismatch_); + EXPECT_EQ(1u, visitor_.padding_frames_.size()); + EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes); +} + +TEST_P(QuicFramerTest, PaddingFrame) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type - IETF_STREAM with FIN, LEN, and OFFSET bits set. + 0x08 | 0x01 | 0x02 | 0x04, + + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(2u, visitor_.padding_frames_.size()); + EXPECT_EQ(2, visitor_.padding_frames_[0]->num_padding_bytes); + EXPECT_EQ(2, visitor_.padding_frames_[1]->num_padding_bytes); + EXPECT_EQ(kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); +} + +TEST_P(QuicFramerTest, StreamFrame) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFF}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFF}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFF}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type - IETF_STREAM with FIN, LEN, and OFFSET bits set. + {"", + { 0x08 | 0x01 | 0x02 | 0x04 }}, + // stream id + {"Unable to read stream_id.", + {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}}, + // offset + {"Unable to read stream data offset.", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // data length + {"Unable to read stream data length.", + {kVarInt62OneByte + 0x0c}}, + // data + {"Unable to read frame data.", + { 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA); +} + +// Test an empty (no data) stream frame. +TEST_P(QuicFramerTest, EmptyStreamFrame) { + // Only the IETF QUIC spec explicitly says that empty + // stream frames are supported. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type - IETF_STREAM with FIN, LEN, and OFFSET bits set. + {"", + { 0x08 | 0x01 | 0x02 | 0x04 }}, + // stream id + {"Unable to read stream_id.", + {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}}, + // offset + {"Unable to read stream data offset.", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // data length + {"Unable to read stream data length.", + {kVarInt62OneByte + 0x00}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + EXPECT_EQ(visitor_.stream_frames_[0].get()->data_length, 0u); + + CheckFramingBoundaries(packet, QUIC_INVALID_STREAM_DATA); +} + +TEST_P(QuicFramerTest, MissingDiversificationNonce) { + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + framer_.SetDecrypter(ENCRYPTION_NONE, + QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT)); + decrypter_ = new test::TestDecrypter(); + framer_.SetAlternativeDecrypter( + ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false); + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet44[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xFC, + // version tag + QUIC_VERSION_BYTES, + // connection_id length + 0x50, + // connection_id + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type - STREAM with LEN, FIN, and OFFSET bits set. + 0x10 | 0x01 | 0x02 | 0x04, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + QuicEncryptedPacket encrypted(AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 + ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet), + false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + if (framer_.transport_version() > QUIC_VERSION_43) { + // Cannot read diversification nonce. + EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); + } else { + EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error()); + } +} + +TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { + if (framer_.transport_version() > QUIC_VERSION_43) { + // This test is nonsensical for IETF Quic. + return; + } + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFE}}, + // stream id + {"Unable to read stream_id.", + {0x02, 0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + // clang-format on + + PacketFragments& fragments = packet; + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + // Stream ID should be the last 3 bytes of kStreamId. + EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA); +} + +TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFD}}, + // stream id + {"Unable to read stream_id.", + {0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFD}}, + // stream id + {"Unable to read stream_id.", + {0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFD}}, + // stream id + {"Unable to read stream_id.", + {0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_STREAM frame with LEN, FIN, and OFFSET bits set) + {"", + {0x08 | 0x01 | 0x02 | 0x04}}, + // stream id + {"Unable to read stream_id.", + {kVarInt62TwoBytes + 0x03, 0x04}}, + // offset + {"Unable to read stream data offset.", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // data length + {"Unable to read stream data length.", + {kVarInt62OneByte + 0x0c}}, + // data + {"Unable to read frame data.", + { 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + // Stream ID should be the last 2 bytes of kStreamId. + EXPECT_EQ(0x0000FFFF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA); +} + +TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFC}}, + // stream id + {"Unable to read stream_id.", + {0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFC}}, + // stream id + {"Unable to read stream_id.", + {0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFC}}, + // stream id + {"Unable to read stream_id.", + {0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_STREAM frame with LEN, FIN, and OFFSET bits set) + {"", + {0x08 | 0x01 | 0x02 | 0x04}}, + // stream id + {"Unable to read stream_id.", + {kVarInt62OneByte + 0x04}}, + // offset + {"Unable to read stream data offset.", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // data length + {"Unable to read stream data length.", + {kVarInt62OneByte + 0x0c}}, + // data + {"Unable to read frame data.", + { 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + // Stream ID should be the last 1 byte of kStreamId. + EXPECT_EQ(0x000000FF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA); +} + +TEST_P(QuicFramerTest, StreamFrameWithVersion) { + // clang-format off + PacketFragments packet = { + // public flags (version, 8 byte connection_id) + {"", + {0x29}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // version tag + {"", + {QUIC_VERSION_BYTES}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFE}}, + // stream id + {"Unable to read stream_id.", + {0x02, 0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet44 = { + // public flags (long header with packet type ZERO_RTT_PROTECTED) + {"", + {0xFC}}, + // version tag + {"", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"", + {0x50}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFE}}, + // stream id + {"Unable to read stream_id.", + {0x02, 0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet46 = { + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + {"", + {0xD3}}, + // version tag + {"", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"", + {0x50}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stream frame with fin) + {"", + {0xFE}}, + // stream id + {"Unable to read stream_id.", + {0x02, 0x03, 0x04}}, + // offset + {"Unable to read offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + {"Unable to read frame data.", + { + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + + PacketFragments packet99 = { + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + {"", + {0xD3}}, + // version tag + {"", + {QUIC_VERSION_BYTES}}, + // connection_id length + {"", + {0x50}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // long header packet length + {"", + {0x1E}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + {"", + {0x08 | 0x01 | 0x02 | 0x04}}, + // stream id + {"Long header payload length longer than packet.", + {kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04}}, + // offset + {"Long header payload length longer than packet.", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // data length + {"Long header payload length longer than packet.", + {kVarInt62OneByte + 0x0c}}, + // data + {"Long header payload length longer than packet.", + { 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + // clang-format on + + QuicVariableLengthIntegerLength retry_token_length_length = + VARIABLE_LENGTH_INTEGER_LENGTH_0; + size_t retry_token_length = 0; + QuicVariableLengthIntegerLength length_length = + QuicVersionHasLongHeaderLengths(framer_.transport_version()) + ? VARIABLE_LENGTH_INTEGER_LENGTH_1 + : VARIABLE_LENGTH_INTEGER_LENGTH_0; + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID, + retry_token_length_length, retry_token_length, length_length)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + // Stream ID should be the last 3 bytes of kStreamId. + EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + CheckFramingBoundaries(fragments, + framer_.transport_version() == QUIC_VERSION_99 + ? QUIC_INVALID_PACKET_HEADER + : QUIC_INVALID_STREAM_DATA); +} + +TEST_P(QuicFramerTest, RejectPacket) { + visitor_.accept_packet_ = false; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (STREAM Frame with FIN, LEN, and OFFSET bits set) + 0x10 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (STREAM Frame with FIN, LEN, and OFFSET bits set) + 0x10 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + QuicEncryptedPacket encrypted(AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 + ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(0u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); +} + +TEST_P(QuicFramerTest, RejectPublicHeader) { + visitor_.accept_public_header_ = false; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + }; + + unsigned char packet44[] = { + // type (short header, 1 byte packet number) + 0x30, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x01, + }; + + unsigned char packet46[] = { + // type (short header, 1 byte packet number) + 0x40, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x01, + }; + // clang-format on + + QuicEncryptedPacket encrypted( + framer_.transport_version() > QUIC_VERSION_44 + ? AsChars(packet46) + : (framer_.transport_version() > QUIC_VERSION_43 ? AsChars(packet44) + : AsChars(packet)), + framer_.transport_version() > QUIC_VERSION_44 + ? QUIC_ARRAYSIZE(packet46) + : (framer_.transport_version() > QUIC_VERSION_43 + ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_FALSE(visitor_.header_->packet_number.IsInitialized()); +} + +TEST_P(QuicFramerTest, AckFrameOneAckBlock) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x2C}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + {"", + {0x45}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x12, 0x34}}, + // num timestamps. + {"Unable to read num received packets.", + {0x00}} + }; + + PacketFragments packet44 = { + // type (short packet, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + {"", + {0x45}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x12, 0x34}}, + // num timestamps. + {"Unable to read num received packets.", + {0x00}} + }; + + PacketFragments packet46 = { + // type (short packet, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + {"", + {0x45}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x12, 0x34}}, + // num timestamps. + {"Unable to read num received packets.", + {0x00}} + }; + + PacketFragments packet99 = { + // type (short packet, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_ACK) + // (one ack block, 2 byte largest observed, 2 byte block length) + // IETF-Quic ignores the bit-fields in the ack type, all of + // that information is encoded elsewhere in the frame. + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62TwoBytes + 0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Ack block count (0 -- no blocks after the first) + {"Unable to read ack block count.", + {kVarInt62OneByte + 0x00}}, + // first ack block length - 1. + // IETF Quic defines the ack block's value as the "number of + // packets that preceed the largest packet number in the block" + // which for the 1st ack block is the largest acked field, + // above. This means that if we are acking just packet 0x1234 + // then the 1st ack block will be 0. + {"Unable to read first ack block length.", + {kVarInt62TwoBytes + 0x12, 0x33}} + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(kSmallLargestObserved, LargestAcked(frame)); + ASSERT_EQ(4660u, frame.packets.NumPacketsSlow()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA); +} + +// This test checks that the ack frame processor correctly identifies +// and handles the case where the first ack block is larger than the +// largest_acked packet. +TEST_P(QuicFramerTest, FirstAckFrameUnderflow) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x2C}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + {"", + {0x45}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x88, 0x88}}, + // num timestamps. + {"Underflow with first ack block length 34952 largest acked is 4660.", + {0x00}} + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + {"", + {0x45}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x88, 0x88}}, + // num timestamps. + {"Underflow with first ack block length 34952 largest acked is 4660.", + {0x00}} + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + {"", + {0x45}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x88, 0x88}}, + // num timestamps. + {"Underflow with first ack block length 34952 largest acked is 4660.", + {0x00}} + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_ACK) + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62TwoBytes + 0x12, 0x34}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Ack block count (0 -- no blocks after the first) + {"Unable to read ack block count.", + {kVarInt62OneByte + 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {kVarInt62TwoBytes + 0x28, 0x88}} + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA); +} + +// This test checks that the ack frame processor correctly identifies +// and handles the case where the third ack block's gap is larger than the +// available space in the ack range. +TEST_P(QuicFramerTest, ThirdAckBlockUnderflowGap) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // for now, only v99 + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_ACK frame) + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62OneByte + 63}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Ack block count (2 -- 2 blocks after the first) + {"Unable to read ack block count.", + {kVarInt62OneByte + 0x02}}, + // first ack block length. + {"Unable to read first ack block length.", + {kVarInt62OneByte + 13}}, // Ack 14 packets, range 50..63 (inclusive) + + {"Unable to read gap block value.", + {kVarInt62OneByte + 9}}, // Gap 10 packets, 40..49 (inclusive) + {"Unable to read ack block value.", + {kVarInt62OneByte + 9}}, // Ack 10 packets, 30..39 (inclusive) + {"Unable to read gap block value.", + {kVarInt62OneByte + 29}}, // A gap of 30 packets (0..29 inclusive) + // should be too big, leaving no room + // for the ack. + {"Underflow with gap block length 30 previous ack block start is 30.", + {kVarInt62OneByte + 10}}, // Don't care + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ( + framer_.detailed_error(), + "Underflow with gap block length 30 previous ack block start is 30."); + CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); +} + +// This test checks that the ack frame processor correctly identifies +// and handles the case where the third ack block's length is larger than the +// available space in the ack range. +TEST_P(QuicFramerTest, ThirdAckBlockUnderflowAck) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // for now, only v99 + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_ACK frame) + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62OneByte + 63}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Ack block count (2 -- 2 blocks after the first) + {"Unable to read ack block count.", + {kVarInt62OneByte + 0x02}}, + // first ack block length. + {"Unable to read first ack block length.", + {kVarInt62OneByte + 13}}, // only 50 packet numbers "left" + + {"Unable to read gap block value.", + {kVarInt62OneByte + 10}}, // Only 40 packet numbers left + {"Unable to read ack block value.", + {kVarInt62OneByte + 10}}, // only 30 packet numbers left. + {"Unable to read gap block value.", + {kVarInt62OneByte + 1}}, // Gap is OK, 29 packet numbers left + {"Unable to read ack block value.", + {kVarInt62OneByte + 30}}, // Use up all 30, should be an error + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(framer_.detailed_error(), + "Underflow with ack block length 31 latest ack block end is 25."); + CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); +} + +// Tests a variety of ack block wrap scenarios. For example, if the +// N-1th block causes packet 0 to be acked, then a gap would wrap +// around to 0x3fffffff ffffffff... Make sure we detect this +// condition. +TEST_P(QuicFramerTest, AckBlockUnderflowGapWrap) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // for now, only v99 + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_ACK frame) + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62OneByte + 10}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Ack block count (1 -- 1 blocks after the first) + {"Unable to read ack block count.", + {kVarInt62OneByte + 1}}, + // first ack block length. + {"Unable to read first ack block length.", + {kVarInt62OneByte + 9}}, // Ack packets 1..10 (inclusive) + + {"Unable to read gap block value.", + {kVarInt62OneByte + 1}}, // Gap of 2 packets (-1...0), should wrap + {"Underflow with gap block length 2 previous ack block start is 1.", + {kVarInt62OneByte + 9}}, // irrelevant + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(framer_.detailed_error(), + "Underflow with gap block length 2 previous ack block start is 1."); + CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); +} + +// As AckBlockUnderflowGapWrap, but in this test, it's the ack +// component of the ack-block that causes the wrap, not the gap. +TEST_P(QuicFramerTest, AckBlockUnderflowAckWrap) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // for now, only v99 + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_ACK frame) + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62OneByte + 10}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Ack block count (1 -- 1 blocks after the first) + {"Unable to read ack block count.", + {kVarInt62OneByte + 1}}, + // first ack block length. + {"Unable to read first ack block length.", + {kVarInt62OneByte + 6}}, // Ack packets 4..10 (inclusive) + + {"Unable to read gap block value.", + {kVarInt62OneByte + 1}}, // Gap of 2 packets (2..3) + {"Unable to read ack block value.", + {kVarInt62OneByte + 9}}, // Should wrap. + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(framer_.detailed_error(), + "Underflow with ack block length 10 latest ack block end is 1."); + CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); +} + +// An ack block that acks the entire range, 1...0x3fffffffffffffff +TEST_P(QuicFramerTest, AckBlockAcksEverything) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // for now, only v99 + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_ACK frame) + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62EightBytes + 0x3f, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Ack block count No additional blocks + {"Unable to read ack block count.", + {kVarInt62OneByte + 0}}, + // first ack block length. + {"Unable to read first ack block length.", + {kVarInt62EightBytes + 0x3f, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(1u, frame.packets.NumIntervals()); + EXPECT_EQ(kLargestIetfLargestObserved, LargestAcked(frame)); + EXPECT_EQ(kLargestIetfLargestObserved.ToUint64(), + frame.packets.NumPacketsSlow()); +} + +// This test looks for a malformed ack where +// - There is a largest-acked value (that is, the frame is acking +// something, +// - But the length of the first ack block is 0 saying that no frames +// are being acked with the largest-acked value or there are no +// additional ack blocks. +// +TEST_P(QuicFramerTest, AckFrameFirstAckBlockLengthZero) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // Not applicable to version 99 -- first ack block contains the + // number of packets that preceed the largest_acked packet. + // A value of 0 means no packets preceed --- that the block's + // length is 1. Therefore the condition that this test checks can + // not arise. + return; + } + + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + { 0x2C }}, + // connection_id + {"", + { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, + // packet number + {"", + { 0x12, 0x34, 0x56, 0x78 }}, + + // frame type (ack frame) + // (more than one ack block, 2 byte largest observed, 2 byte block length) + {"", + { 0x65 }}, + // largest acked + {"Unable to read largest acked.", + { 0x12, 0x34 }}, + // Zero delta time. + {"Unable to read ack delay time.", + { 0x00, 0x00 }}, + // num ack blocks ranges. + {"Unable to read num of ack blocks.", + { 0x01 }}, + // first ack block length. + {"Unable to read first ack block length.", + { 0x00, 0x00 }}, + // gap to next block. + { "First block length is zero.", + { 0x01 }}, + // ack block length. + { "First block length is zero.", + { 0x0e, 0xaf }}, + // Number of timestamps. + { "First block length is zero.", + { 0x00 }}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + { 0x32 }}, + // connection_id + {"", + { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, + // packet number + {"", + { 0x12, 0x34, 0x56, 0x78 }}, + + // frame type (ack frame) + // (more than one ack block, 2 byte largest observed, 2 byte block length) + {"", + { 0x65 }}, + // largest acked + {"Unable to read largest acked.", + { 0x12, 0x34 }}, + // Zero delta time. + {"Unable to read ack delay time.", + { 0x00, 0x00 }}, + // num ack blocks ranges. + {"Unable to read num of ack blocks.", + { 0x01 }}, + // first ack block length. + {"Unable to read first ack block length.", + { 0x00, 0x00 }}, + // gap to next block. + { "First block length is zero.", + { 0x01 }}, + // ack block length. + { "First block length is zero.", + { 0x0e, 0xaf }}, + // Number of timestamps. + { "First block length is zero.", + { 0x00 }}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + { 0x43 }}, + // connection_id + {"", + { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, + // packet number + {"", + { 0x12, 0x34, 0x56, 0x78 }}, + + // frame type (ack frame) + // (more than one ack block, 2 byte largest observed, 2 byte block length) + {"", + { 0x65 }}, + // largest acked + {"Unable to read largest acked.", + { 0x12, 0x34 }}, + // Zero delta time. + {"Unable to read ack delay time.", + { 0x00, 0x00 }}, + // num ack blocks ranges. + {"Unable to read num of ack blocks.", + { 0x01 }}, + // first ack block length. + {"Unable to read first ack block length.", + { 0x00, 0x00 }}, + // gap to next block. + { "First block length is zero.", + { 0x01 }}, + // ack block length. + { "First block length is zero.", + { 0x0e, 0xaf }}, + // Number of timestamps. + { "First block length is zero.", + { 0x00 }}, + }; + + // clang-format on + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_INVALID_ACK_DATA, framer_.error()); + + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA); +} + +TEST_P(QuicFramerTest, AckFrameOneAckBlockMaxLength) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x2C}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (ack frame) + // (one ack block, 4 byte largest observed, 2 byte block length) + {"", + {0x49}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34, 0x56, 0x78}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x12, 0x34}}, + // num timestamps. + {"Unable to read num received packets.", + {0x00}} + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x56, 0x78, 0x9A, 0xBC}}, + // frame type (ack frame) + // (one ack block, 4 byte largest observed, 2 byte block length) + {"", + {0x49}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34, 0x56, 0x78}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x12, 0x34}}, + // num timestamps. + {"Unable to read num received packets.", + {0x00}} + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x56, 0x78, 0x9A, 0xBC}}, + // frame type (ack frame) + // (one ack block, 4 byte largest observed, 2 byte block length) + {"", + {0x49}}, + // largest acked + {"Unable to read largest acked.", + {0x12, 0x34, 0x56, 0x78}}, + // Zero delta time. + {"Unable to read ack delay time.", + {0x00, 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {0x12, 0x34}}, + // num timestamps. + {"Unable to read num received packets.", + {0x00}} + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x56, 0x78, 0x9A, 0xBC}}, + // frame type (IETF_ACK frame) + {"", + {0x02}}, + // largest acked + {"Unable to read largest acked.", + {kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x78}}, + // Zero delta time. + {"Unable to read ack delay time.", + {kVarInt62OneByte + 0x00}}, + // Number of ack blocks after first + {"Unable to read ack block count.", + {kVarInt62OneByte + 0x00}}, + // first ack block length. + {"Unable to read first ack block length.", + {kVarInt62TwoBytes + 0x12, 0x33}} + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(kPacketNumber, LargestAcked(frame)); + ASSERT_EQ(4660u, frame.packets.NumPacketsSlow()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA); +} + +// Tests ability to handle multiple ackblocks after the first ack +// block. Non-version-99 tests include multiple timestamps as well. +TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + { 0x2C }}, + // connection_id + {"", + { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, + // packet number + {"", + { 0x12, 0x34, 0x56, 0x78 }}, + + // frame type (ack frame) + // (more than one ack block, 2 byte largest observed, 2 byte block length) + {"", + { 0x65 }}, + // largest acked + {"Unable to read largest acked.", + { 0x12, 0x34 }}, + // Zero delta time. + {"Unable to read ack delay time.", + { 0x00, 0x00 }}, + // num ack blocks ranges. + {"Unable to read num of ack blocks.", + { 0x04 }}, + // first ack block length. + {"Unable to read first ack block length.", + { 0x00, 0x01 }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x01 }}, + // ack block length. + { "Unable to ack block length.", + { 0x0e, 0xaf }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0xff }}, + // ack block length. + { "Unable to ack block length.", + { 0x00, 0x00 }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x91 }}, + // ack block length. + { "Unable to ack block length.", + { 0x01, 0xea }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x05 }}, + // ack block length. + { "Unable to ack block length.", + { 0x00, 0x04 }}, + // Number of timestamps. + { "Unable to read num received packets.", + { 0x02 }}, + // Delta from largest observed. + { "Unable to read sequence delta in received packets.", + { 0x01 }}, + // Delta time. + { "Unable to read time delta in received packets.", + { 0x76, 0x54, 0x32, 0x10 }}, + // Delta from largest observed. + { "Unable to read sequence delta in received packets.", + { 0x02 }}, + // Delta time. + { "Unable to read incremental time delta in received packets.", + { 0x32, 0x10 }}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + { 0x32 }}, + // connection_id + {"", + { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, + // packet number + {"", + { 0x12, 0x34, 0x56, 0x78 }}, + + // frame type (ack frame) + // (more than one ack block, 2 byte largest observed, 2 byte block length) + {"", + { 0x65 }}, + // largest acked + {"Unable to read largest acked.", + { 0x12, 0x34 }}, + // Zero delta time. + {"Unable to read ack delay time.", + { 0x00, 0x00 }}, + // num ack blocks ranges. + {"Unable to read num of ack blocks.", + { 0x04 }}, + // first ack block length. + {"Unable to read first ack block length.", + { 0x00, 0x01 }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x01 }}, + // ack block length. + { "Unable to ack block length.", + { 0x0e, 0xaf }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0xff }}, + // ack block length. + { "Unable to ack block length.", + { 0x00, 0x00 }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x91 }}, + // ack block length. + { "Unable to ack block length.", + { 0x01, 0xea }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x05 }}, + // ack block length. + { "Unable to ack block length.", + { 0x00, 0x04 }}, + // Number of timestamps. + { "Unable to read num received packets.", + { 0x02 }}, + // Delta from largest observed. + { "Unable to read sequence delta in received packets.", + { 0x01 }}, + // Delta time. + { "Unable to read time delta in received packets.", + { 0x76, 0x54, 0x32, 0x10 }}, + // Delta from largest observed. + { "Unable to read sequence delta in received packets.", + { 0x02 }}, + // Delta time. + { "Unable to read incremental time delta in received packets.", + { 0x32, 0x10 }}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + { 0x43 }}, + // connection_id + {"", + { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, + // packet number + {"", + { 0x12, 0x34, 0x56, 0x78 }}, + + // frame type (ack frame) + // (more than one ack block, 2 byte largest observed, 2 byte block length) + {"", + { 0x65 }}, + // largest acked + {"Unable to read largest acked.", + { 0x12, 0x34 }}, + // Zero delta time. + {"Unable to read ack delay time.", + { 0x00, 0x00 }}, + // num ack blocks ranges. + {"Unable to read num of ack blocks.", + { 0x04 }}, + // first ack block length. + {"Unable to read first ack block length.", + { 0x00, 0x01 }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x01 }}, + // ack block length. + { "Unable to ack block length.", + { 0x0e, 0xaf }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0xff }}, + // ack block length. + { "Unable to ack block length.", + { 0x00, 0x00 }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x91 }}, + // ack block length. + { "Unable to ack block length.", + { 0x01, 0xea }}, + // gap to next block. + { "Unable to read gap to next ack block.", + { 0x05 }}, + // ack block length. + { "Unable to ack block length.", + { 0x00, 0x04 }}, + // Number of timestamps. + { "Unable to read num received packets.", + { 0x02 }}, + // Delta from largest observed. + { "Unable to read sequence delta in received packets.", + { 0x01 }}, + // Delta time. + { "Unable to read time delta in received packets.", + { 0x76, 0x54, 0x32, 0x10 }}, + // Delta from largest observed. + { "Unable to read sequence delta in received packets.", + { 0x02 }}, + // Delta time. + { "Unable to read incremental time delta in received packets.", + { 0x32, 0x10 }}, + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + { 0x43 }}, + // connection_id + {"", + { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }}, + // packet number + {"", + { 0x12, 0x34, 0x56, 0x78 }}, + + // frame type (IETF_ACK frame) + {"", + { 0x02 }}, + // largest acked + {"Unable to read largest acked.", + { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 + // Zero delta time. + {"Unable to read ack delay time.", + { kVarInt62OneByte + 0x00 }}, + // number of additional ack blocks + {"Unable to read ack block count.", + { kVarInt62OneByte + 0x03 }}, + // first ack block length. + {"Unable to read first ack block length.", + { kVarInt62OneByte + 0x00 }}, // 1st block length = 1 + + // Additional ACK Block #1 + // gap to next block. + { "Unable to read gap block value.", + { kVarInt62OneByte + 0x00 }}, // gap of 1 packet + // ack block length. + { "Unable to read ack block value.", + { kVarInt62TwoBytes + 0x0e, 0xae }}, // 3759 + + // pre-version-99 test includes an ack block of 0 length. this + // can not happen in version 99. ergo the second block is not + // present in the v99 test and the gap length of the next block + // is the sum of the two gaps in the pre-version-99 tests. + // Additional ACK Block #2 + // gap to next block. + { "Unable to read gap block value.", + { kVarInt62TwoBytes + 0x01, 0x8f }}, // Gap is 400 (0x190) pkts + // ack block length. + { "Unable to read ack block value.", + { kVarInt62TwoBytes + 0x01, 0xe9 }}, // block is 389 (x1ea) pkts + + // Additional ACK Block #3 + // gap to next block. + { "Unable to read gap block value.", + { kVarInt62OneByte + 0x04 }}, // Gap is 5 packets. + // ack block length. + { "Unable to read ack block value.", + { kVarInt62OneByte + 0x03 }}, // block is 3 packets. + }; + + // clang-format on + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + + framer_.set_process_timestamps(true); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(kSmallLargestObserved, LargestAcked(frame)); + ASSERT_EQ(4254u, frame.packets.NumPacketsSlow()); + EXPECT_EQ(4u, frame.packets.NumIntervals()); + if (framer_.transport_version() == QUIC_VERSION_99) { + EXPECT_EQ(0u, frame.received_packet_times.size()); + } else { + EXPECT_EQ(2u, frame.received_packet_times.size()); + } + CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA); +} + +TEST_P(QuicFramerTest, AckFrameTimeStampDeltaTooHigh) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 1 byte largest observed, 1 byte block length) + 0x40, + // largest acked + 0x01, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x01, + // num timestamps. + 0x01, + // Delta from largest observed. + 0x01, + // Delta time. + 0x10, 0x32, 0x54, 0x76, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 1 byte largest observed, 1 byte block length) + 0x40, + // largest acked + 0x01, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x01, + // num timestamps. + 0x01, + // Delta from largest observed. + 0x01, + // Delta time. + 0x10, 0x32, 0x54, 0x76, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 1 byte largest observed, 1 byte block length) + 0x40, + // largest acked + 0x01, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x01, + // num timestamps. + 0x01, + // Delta from largest observed. + 0x01, + // Delta time. + 0x10, 0x32, 0x54, 0x76, + }; + // clang-format on + if (framer_.transport_version() == QUIC_VERSION_99) { + return; + } + QuicEncryptedPacket encrypted( + AsChars(framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)), + QUIC_ARRAYSIZE(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_TRUE(QuicTextUtils::StartsWith( + framer_.detailed_error(), "delta_from_largest_observed too high")); +} + +TEST_P(QuicFramerTest, AckFrameTimeStampSecondDeltaTooHigh) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 1 byte largest observed, 1 byte block length) + 0x40, + // largest acked + 0x03, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x03, + // num timestamps. + 0x02, + // Delta from largest observed. + 0x01, + // Delta time. + 0x10, 0x32, 0x54, 0x76, + // Delta from largest observed. + 0x03, + // Delta time. + 0x10, 0x32, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 1 byte largest observed, 1 byte block length) + 0x40, + // largest acked + 0x03, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x03, + // num timestamps. + 0x02, + // Delta from largest observed. + 0x01, + // Delta time. + 0x10, 0x32, 0x54, 0x76, + // Delta from largest observed. + 0x03, + // Delta time. + 0x10, 0x32, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 1 byte largest observed, 1 byte block length) + 0x40, + // largest acked + 0x03, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x03, + // num timestamps. + 0x02, + // Delta from largest observed. + 0x01, + // Delta time. + 0x10, 0x32, 0x54, 0x76, + // Delta from largest observed. + 0x03, + // Delta time. + 0x10, 0x32, + }; + // clang-format on + if (framer_.transport_version() == QUIC_VERSION_99) { + return; + } + QuicEncryptedPacket encrypted( + AsChars(framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)), + QUIC_ARRAYSIZE(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_TRUE(QuicTextUtils::StartsWith( + framer_.detailed_error(), "delta_from_largest_observed too high")); +} + +TEST_P(QuicFramerTest, NewStopWaitingFrame) { + if (version_.transport_version == QUIC_VERSION_99) { + return; + } + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x2C}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stop waiting frame) + {"", + {0x06}}, + // least packet number awaiting an ack, delta from packet number. + {"Unable to read least unacked delta.", + {0x00, 0x00, 0x00, 0x08}} + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stop waiting frame) + {"", + {0x06}}, + // least packet number awaiting an ack, delta from packet number. + {"Unable to read least unacked delta.", + {0x00, 0x00, 0x00, 0x08}} + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (stop waiting frame) + {"", + {0x06}}, + // least packet number awaiting an ack, delta from packet number. + {"Unable to read least unacked delta.", + {0x00, 0x00, 0x00, 0x08}} + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.stop_waiting_frames_.size()); + const QuicStopWaitingFrame& frame = *visitor_.stop_waiting_frames_[0]; + EXPECT_EQ(kLeastUnacked, frame.least_unacked); + + CheckFramingBoundaries(fragments, QUIC_INVALID_STOP_WAITING_DATA); +} + +TEST_P(QuicFramerTest, InvalidNewStopWaitingFrame) { + if (version_.transport_version == QUIC_VERSION_99) { + return; + } + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x2C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (stop waiting frame) + 0x06, + // least packet number awaiting an ack, delta from packet number. + 0x13, 0x34, 0x56, 0x78, + 0x9A, 0xA8, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (stop waiting frame) + 0x06, + // least packet number awaiting an ack, delta from packet number. + 0x57, 0x78, 0x9A, 0xA8, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (stop waiting frame) + 0x06, + // least packet number awaiting an ack, delta from packet number. + 0x57, 0x78, 0x9A, 0xA8, + }; + // clang-format on + + QuicEncryptedPacket encrypted( + AsChars(framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet), + false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_INVALID_STOP_WAITING_DATA, framer_.error()); + EXPECT_EQ("Invalid unacked delta.", framer_.detailed_error()); +} + +TEST_P(QuicFramerTest, RstStreamFrame) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (rst stream frame) + {"", + {0x01}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // sent byte offset + {"Unable to read rst stream sent byte offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // error code + {"Unable to read rst stream error code.", + {0x00, 0x00, 0x00, 0x01}} + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (rst stream frame) + {"", + {0x01}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // sent byte offset + {"Unable to read rst stream sent byte offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // error code + {"Unable to read rst stream error code.", + {0x00, 0x00, 0x00, 0x01}} + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (rst stream frame) + {"", + {0x01}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // sent byte offset + {"Unable to read rst stream sent byte offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // error code + {"Unable to read rst stream error code.", + {0x00, 0x00, 0x00, 0x01}} + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_RST_STREAM frame) + {"", + {0x04}}, + // stream id + {"Unable to read rst stream stream id.", + {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}}, + // application error code + {"Unable to read rst stream error code.", + {0x00, 0x01}}, // Not varint62 encoded + // Final Offset + {"Unable to read rst stream sent byte offset.", + {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}} + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(kStreamId, visitor_.rst_stream_frame_.stream_id); + EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code); + EXPECT_EQ(kStreamOffset, visitor_.rst_stream_frame_.byte_offset); + CheckFramingBoundaries(fragments, QUIC_INVALID_RST_STREAM_DATA); +} + +TEST_P(QuicFramerTest, ConnectionCloseFrame) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (connection close frame) + {"", + {0x02}}, + // error code + {"Unable to read connection close error code.", + {0x00, 0x00, 0x00, 0x11}}, + {"Unable to read connection close error details.", + { + // error details length + 0x0, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (connection close frame) + {"", + {0x02}}, + // error code + {"Unable to read connection close error code.", + {0x00, 0x00, 0x00, 0x11}}, + {"Unable to read connection close error details.", + { + // error details length + 0x0, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (connection close frame) + {"", + {0x02}}, + // error code + {"Unable to read connection close error code.", + {0x00, 0x00, 0x00, 0x11}}, + {"Unable to read connection close error details.", + { + // error details length + 0x0, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_CONNECTION_CLOSE frame) + {"", + {0x1c}}, + // error code + {"Unable to read connection close error code.", + {0x00, 0x11}}, + {"Unable to read connection close frame type.", + {kVarInt62TwoBytes + 0x12, 0x34 }}, + {"Unable to read connection close error details.", + { + // error details length + kVarInt62OneByte + 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(0x11, visitor_.connection_close_frame_.error_code); + EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details); + if (framer_.transport_version() == QUIC_VERSION_99) { + EXPECT_EQ(0x1234u, visitor_.connection_close_frame_.frame_type); + } + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + CheckFramingBoundaries(fragments, QUIC_INVALID_CONNECTION_CLOSE_DATA); +} + +TEST_P(QuicFramerTest, ApplicationCloseFrame) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame does not exist in versions other than 99. + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_APPLICATION_CLOSE frame) + {"", + {0x1d}}, + // error code + {"Unable to read application close error code.", + {0x00, 0x11}}, + {"Unable to read application close error details.", + { + // error details length + kVarInt62OneByte + 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(0x11, visitor_.application_close_frame_.error_code); + EXPECT_EQ("because I can", visitor_.application_close_frame_.error_details); + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + CheckFramingBoundaries(packet99, QUIC_INVALID_APPLICATION_CLOSE_DATA); +} + +TEST_P(QuicFramerTest, GoAwayFrame) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // This frame is not supported in version 99. + return; + } + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (go away frame) + {"", + {0x03}}, + // error code + {"Unable to read go away error code.", + {0x00, 0x00, 0x00, 0x09}}, + // stream id + {"Unable to read last good stream id.", + {0x01, 0x02, 0x03, 0x04}}, + // stream id + {"Unable to read goaway reason.", + { + // error details length + 0x0, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (go away frame) + {"", + {0x03}}, + // error code + {"Unable to read go away error code.", + {0x00, 0x00, 0x00, 0x09}}, + // stream id + {"Unable to read last good stream id.", + {0x01, 0x02, 0x03, 0x04}}, + // stream id + {"Unable to read goaway reason.", + { + // error details length + 0x0, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (go away frame) + {"", + {0x03}}, + // error code + {"Unable to read go away error code.", + {0x00, 0x00, 0x00, 0x09}}, + // stream id + {"Unable to read last good stream id.", + {0x01, 0x02, 0x03, 0x04}}, + // stream id + {"Unable to read goaway reason.", + { + // error details length + 0x0, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n'} + } + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(kStreamId, visitor_.goaway_frame_.last_good_stream_id); + EXPECT_EQ(0x9, visitor_.goaway_frame_.error_code); + EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase); + + CheckFramingBoundaries(fragments, QUIC_INVALID_GOAWAY_DATA); +} + +TEST_P(QuicFramerTest, WindowUpdateFrame) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // This frame is not in version 99, see MaxDataFrame and MaxStreamDataFrame + // for Version 99 equivalents. + return; + } + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (window update frame) + {"", + {0x04}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // byte offset + {"Unable to read window byte_offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (window update frame) + {"", + {0x04}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // byte offset + {"Unable to read window byte_offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (window update frame) + {"", + {0x04}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + // byte offset + {"Unable to read window byte_offset.", + {0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + }; + + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(kStreamId, visitor_.window_update_frame_.stream_id); + EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.byte_offset); + + CheckFramingBoundaries(fragments, QUIC_INVALID_WINDOW_UPDATE_DATA); +} + +TEST_P(QuicFramerTest, MaxDataFrame) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is available only in version 99. + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_MAX_DATA frame) + {"", + {0x10}}, + // byte offset + {"Can not read MAX_DATA byte-offset", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()), + visitor_.window_update_frame_.stream_id); + EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.byte_offset); + + CheckFramingBoundaries(packet99, QUIC_INVALID_MAX_DATA_FRAME_DATA); +} + +TEST_P(QuicFramerTest, MaxStreamDataFrame) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame available only in version 99. + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_MAX_STREAM_DATA frame) + {"", + {0x11}}, + // stream id + {"Can not read MAX_STREAM_DATA stream id", + {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}}, + // byte offset + {"Can not read MAX_STREAM_DATA byte-count", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(kStreamId, visitor_.window_update_frame_.stream_id); + EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.byte_offset); + + CheckFramingBoundaries(packet99, QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA); +} + +TEST_P(QuicFramerTest, BlockedFrame) { + // clang-format off + PacketFragments packet = { + // public flags (8 byte connection_id) + {"", + {0x28}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (blocked frame) + {"", + {0x05}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + }; + + PacketFragments packet44 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (blocked frame) + {"", + {0x05}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (blocked frame) + {"", + {0x05}}, + // stream id + {"Unable to read stream_id.", + {0x01, 0x02, 0x03, 0x04}}, + }; + + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_STREAM_BLOCKED frame) + {"", + {0x15}}, + // stream id + {"Can not read stream blocked stream id.", + {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}}, + // Offset + {"Can not read stream blocked offset.", + {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}}, + }; + // clang-format on + + PacketFragments& fragments = + framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 + : packet)); + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + if (framer_.transport_version() == QUIC_VERSION_99) { + EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset); + } else { + EXPECT_EQ(0u, visitor_.blocked_frame_.offset); + } + EXPECT_EQ(kStreamId, visitor_.blocked_frame_.stream_id); + + if (framer_.transport_version() == QUIC_VERSION_99) { + CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_BLOCKED_DATA); + } else { + CheckFramingBoundaries(fragments, QUIC_INVALID_BLOCKED_DATA); + } +} + +TEST_P(QuicFramerTest, PingFrame) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ping frame) + 0x07, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_PING frame) + 0x01, + }; + // clang-format on + + QuicEncryptedPacket encrypted( + AsChars(framer_.transport_version() == QUIC_VERSION_99 + ? packet99 + : (framer_.transport_version() > QUIC_VERSION_44 + ? packet46 + : framer_.transport_version() > QUIC_VERSION_43 + ? packet44 + : packet)), + framer_.transport_version() == QUIC_VERSION_99 + ? QUIC_ARRAYSIZE(packet99) + : (framer_.transport_version() > QUIC_VERSION_44 + ? QUIC_ARRAYSIZE(packet46) + : framer_.transport_version() > QUIC_VERSION_43 + ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(1u, visitor_.ping_frames_.size()); + + // No need to check the PING frame boundaries because it has no payload. +} + +TEST_P(QuicFramerTest, MessageFrame) { + if (framer_.transport_version() <= QUIC_VERSION_44) { + return; + } + // clang-format off + PacketFragments packet45 = { + // type (short header, 4 byte packet number) + {"", + {0x32}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // message frame type. + {"", + { 0x21 }}, + // message length + {"Unable to read message length", + {0x07}}, + // message data + {"Unable to read message data", + {'m', 'e', 's', 's', 'a', 'g', 'e'}}, + // message frame no length. + {"", + { 0x20 }}, + // message data + {{}, + {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}}, + }; + + PacketFragments packet46 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // message frame type. + {"", + { 0x21 }}, + // message length + {"Unable to read message length", + {0x07}}, + // message data + {"Unable to read message data", + {'m', 'e', 's', 's', 'a', 'g', 'e'}}, + // message frame no length. + {"", + { 0x20 }}, + // message data + {{}, + {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted(AssemblePacketFromFragments( + framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(2u, visitor_.message_frames_.size()); + EXPECT_EQ(7u, visitor_.message_frames_[0]->message_length); + EXPECT_EQ(8u, visitor_.message_frames_[1]->message_length); + + CheckFramingBoundaries( + framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45, + QUIC_INVALID_MESSAGE_DATA); +} + +TEST_P(QuicFramerTest, PublicResetPacketV33) { + // clang-format off + PacketFragments packet = { + // public flags (public reset, 8 byte connection_id) + {"", + {0x0A}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + {"Unable to read reset message.", + { + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected packet number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + } + } + }; + // clang-format on + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.public_reset_packet_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.public_reset_packet_->connection_id); + EXPECT_EQ(kNonceProof, visitor_.public_reset_packet_->nonce_proof); + EXPECT_EQ( + IpAddressFamily::IP_UNSPEC, + visitor_.public_reset_packet_->client_address.host().address_family()); + + CheckFramingBoundaries(packet, QUIC_INVALID_PUBLIC_RST_PACKET); +} + +TEST_P(QuicFramerTest, PublicResetPacket) { + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + // clang-format off + PacketFragments packet = { + // public flags (public reset, 8 byte connection_id) + {"", + {0x0E}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + {"Unable to read reset message.", + { + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected packet number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + } + } + }; + // clang-format on + + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.public_reset_packet_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.public_reset_packet_->connection_id); + EXPECT_EQ(kNonceProof, visitor_.public_reset_packet_->nonce_proof); + EXPECT_EQ( + IpAddressFamily::IP_UNSPEC, + visitor_.public_reset_packet_->client_address.host().address_family()); + + CheckFramingBoundaries(packet, QUIC_INVALID_PUBLIC_RST_PACKET); +} + +TEST_P(QuicFramerTest, PublicResetPacketWithTrailingJunk) { + // clang-format off + unsigned char packet[] = { + // public flags (public reset, 8 byte connection_id) + 0x0A, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected packet number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + // trailing junk + 'j', 'u', 'n', 'k', + }; + // clang-format on + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + ASSERT_EQ(QUIC_INVALID_PUBLIC_RST_PACKET, framer_.error()); + EXPECT_EQ("Unable to read reset message.", framer_.detailed_error()); +} + +TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) { + // clang-format off + PacketFragments packet = { + // public flags (public reset, 8 byte connection_id) + {"", + {0x0A}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + {"Unable to read reset message.", + { + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x03, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // tag kCADR + 'C', 'A', 'D', 'R', + // end offset 24 + 0x18, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected packet number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + // client address: 4.31.198.44:443 + 0x02, 0x00, + 0x04, 0x1F, 0xC6, 0x2C, + 0xBB, 0x01, + } + } + }; + // clang-format on + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.public_reset_packet_.get()); + EXPECT_EQ(FramerTestConnectionId(), + visitor_.public_reset_packet_->connection_id); + EXPECT_EQ(kNonceProof, visitor_.public_reset_packet_->nonce_proof); + EXPECT_EQ("4.31.198.44", + visitor_.public_reset_packet_->client_address.host().ToString()); + EXPECT_EQ(443, visitor_.public_reset_packet_->client_address.port()); + + CheckFramingBoundaries(packet, QUIC_INVALID_PUBLIC_RST_PACKET); +} + +TEST_P(QuicFramerTest, IetfStatelessResetPacket) { + // clang-format off + unsigned char packet[] = { + // type (short packet, 1 byte packet number) + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // Random bytes + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, + 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, + // stateless reset token + 0xB5, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + if (framer_.transport_version() <= QUIC_VERSION_43) { + return; + } + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + framer_.SetDecrypter(ENCRYPTION_NONE, + QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT)); + decrypter_ = new test::TestDecrypter(); + framer_.SetAlternativeDecrypter( + ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false); + // This packet cannot be decrypted because diversification nonce is missing. + QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.stateless_reset_packet_.get()); + EXPECT_EQ(kTestStatelessResetToken, + visitor_.stateless_reset_packet_->stateless_reset_token); +} + +TEST_P(QuicFramerTest, IetfStatelessResetPacketInvalidStatelessResetToken) { + // clang-format off + unsigned char packet[] = { + // type (short packet, 1 byte packet number) + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // stateless reset token + 0xB6, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + if (framer_.transport_version() <= QUIC_VERSION_43) { + return; + } + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + framer_.SetDecrypter(ENCRYPTION_NONE, + QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT)); + decrypter_ = new test::TestDecrypter(); + framer_.SetAlternativeDecrypter( + ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false); + // This packet cannot be decrypted because diversification nonce is missing. + QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error()); + ASSERT_FALSE(visitor_.stateless_reset_packet_); +} + +TEST_P(QuicFramerTest, VersionNegotiationPacket) { + // clang-format off + PacketFragments packet = { + // public flags (version, 8 byte connection_id) + {"", + {0x29}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // version tag + {"Unable to read supported version in negotiation.", + {QUIC_VERSION_BYTES, + 'Q', '2', '.', '0'}}, + }; + + PacketFragments packet44 = { + // type (long header) + {"", + {0x8F}}, + // version tag + {"", + {0x00, 0x00, 0x00, 0x00}}, + {"", + {0x05}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // Supported versions + {"Unable to read supported version in negotiation.", + {QUIC_VERSION_BYTES, + 'Q', '2', '.', '0'}}, + }; + // clang-format on + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + PacketFragments& fragments = + framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet; + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(fragments)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.version_negotiation_packet_.get()); + EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size()); + EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]); + + // Remove the last version from the packet so that every truncated + // version of the packet is invalid, otherwise checking boundaries + // is annoyingly complicated. + for (size_t i = 0; i < 4; ++i) { + fragments.back().fragment.pop_back(); + } + CheckFramingBoundaries(fragments, QUIC_INVALID_VERSION_NEGOTIATION_PACKET); +} + +TEST_P(QuicFramerTest, OldVersionNegotiationPacket) { + // clang-format off + PacketFragments packet = { + // public flags (version, 8 byte connection_id) + {"", + {0x2D}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // version tag + {"Unable to read supported version in negotiation.", + {QUIC_VERSION_BYTES, + 'Q', '2', '.', '0'}}, + }; + // clang-format on + + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.version_negotiation_packet_.get()); + EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size()); + EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]); + + // Remove the last version from the packet so that every truncated + // version of the packet is invalid, otherwise checking boundaries + // is annoyingly complicated. + for (size_t i = 0; i < 4; ++i) { + packet.back().fragment.pop_back(); + } + CheckFramingBoundaries(packet, QUIC_INVALID_VERSION_NEGOTIATION_PACKET); +} + +TEST_P(QuicFramerTest, BuildPaddingFramePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicFrames frames = {QuicFrame(QuicPaddingFrame())}; + + // clang-format off + unsigned char packet[kMaxPacketSize] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet44[kMaxPacketSize] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet46[kMaxPacketSize] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet99[kMaxPacketSize] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + uint64_t header_size = GetPacketHeaderSize( + framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0); + memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset, + QuicStringPiece("hello world!")); + QuicPaddingFrame padding_frame(2); + QuicFrames frames = {QuicFrame(padding_frame), QuicFrame(stream_frame), + QuicFrame(padding_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // paddings + 0x00, 0x00, + // frame type (IETF_STREAM with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number_length = PACKET_4BYTE_PACKET_NUMBER; + header.packet_number = kPacketNumber; + + QuicFrames frames = {QuicFrame(QuicPaddingFrame())}; + + // clang-format off + unsigned char packet[kMaxPacketSize] = { + // public flags (8 byte connection_id and 4 byte packet number) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet44[kMaxPacketSize] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet46[kMaxPacketSize] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet99[kMaxPacketSize] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + uint64_t header_size = GetPacketHeaderSize( + framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0); + memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number_length = PACKET_2BYTE_PACKET_NUMBER; + header.packet_number = kPacketNumber; + + QuicFrames frames = {QuicFrame(QuicPaddingFrame())}; + + // clang-format off + unsigned char packet[kMaxPacketSize] = { + // public flags (8 byte connection_id and 2 byte packet number) + 0x18, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet44[kMaxPacketSize] = { + // type (short header, 2 byte packet number) + 0x31, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet46[kMaxPacketSize] = { + // type (short header, 2 byte packet number) + 0x41, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet99[kMaxPacketSize] = { + // type (short header, 2 byte packet number) + 0x41, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x56, 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + uint64_t header_size = GetPacketHeaderSize( + framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_2BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0); + memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number_length = PACKET_1BYTE_PACKET_NUMBER; + header.packet_number = kPacketNumber; + + QuicFrames frames = {QuicFrame(QuicPaddingFrame())}; + + // clang-format off + unsigned char packet[kMaxPacketSize] = { + // public flags (8 byte connection_id and 1 byte packet number) + 0x08, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet44[kMaxPacketSize] = { + // type (short header, 1 byte packet number) + 0x30, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet46[kMaxPacketSize] = { + // type (short header, 1 byte packet number) + 0x40, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet99[kMaxPacketSize] = { + // type (short header, 1 byte packet number) + 0x40, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x78, + + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + uint64_t header_size = GetPacketHeaderSize( + framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_1BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0); + memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildStreamFramePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + if (QuicVersionHasLongHeaderLengths(framer_.transport_version())) { + header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + + QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset, + QuicStringPiece("hello world!")); + + QuicFrames frames = {QuicFrame(stream_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin and no length) + 0xDF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin and no length) + 0xDF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin and no length) + 0xDF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAM frame with FIN and OFFSET, no length) + 0x08 | 0x01 | 0x04, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = true; + if (framer_.transport_version() > QUIC_VERSION_43) { + header.long_packet_type = ZERO_RTT_PROTECTED; + } + header.packet_number = kPacketNumber; + if (QuicVersionHasLongHeaderLengths(framer_.transport_version())) { + header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + + QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset, + QuicStringPiece("hello world!")); + QuicFrames frames = {QuicFrame(stream_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (version, 8 byte connection_id) + 0x2D, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + QUIC_VERSION_BYTES, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin and no length) + 0xDF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', + }; + + unsigned char packet44[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xFC, + // version tag + QUIC_VERSION_BYTES, + // connection_id length + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin and no length) + 0xDF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', + }; + + unsigned char packet46[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xD3, + // version tag + QUIC_VERSION_BYTES, + // connection_id length + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin and no length) + 0xDF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', + }; + + unsigned char packet99[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xD3, + // version tag + QUIC_VERSION_BYTES, + // connection_id length + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // length + 0x40, 0x1D, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAM frame with fin and offset, no length) + 0x08 | 0x01 | 0x04, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', + }; + // clang-format on + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildCryptoFramePacket) { + if (framer_.transport_version() < QUIC_VERSION_99) { + // CRYPTO frames aren't supported prior to v46. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + SimpleDataProducer data_producer; + framer_.set_data_producer(&data_producer); + + QuicStringPiece crypto_frame_contents("hello world!"); + QuicCryptoFrame crypto_frame(ENCRYPTION_NONE, kStreamOffset, + crypto_frame_contents.length()); + data_producer.SaveCryptoData(ENCRYPTION_NONE, kStreamOffset, + crypto_frame_contents); + + QuicFrames frames = {QuicFrame(&crypto_frame)}; + + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_CRYPTO frame) + 0x06, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // length + kVarInt62OneByte + 12, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + // clang-format on + + size_t packet_size = QUIC_ARRAYSIZE(packet); + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + packet_size); +} + +TEST_P(QuicFramerTest, CryptoFrame) { + if (framer_.transport_version() < QUIC_VERSION_99) { + // CRYPTO frames aren't supported prior to v46. + return; + } + + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_CRYPTO frame) + {"", + {0x06}}, + // offset + {"", + {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54}}, + // data length + {"Invalid data length.", + {kVarInt62OneByte + 12}}, + // data + {"Unable to read frame data.", + {'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!'}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + ASSERT_EQ(1u, visitor_.crypto_frames_.size()); + QuicCryptoFrame* frame = visitor_.crypto_frames_[0].get(); + EXPECT_EQ(kStreamOffset, frame->offset); + EXPECT_EQ("hello world!", QuicString(frame->data_buffer, frame->data_length)); + + CheckFramingBoundaries(packet, QUIC_INVALID_FRAME_DATA); +} + +TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { + // clang-format off + unsigned char packet[] = { + // public flags (version, 8 byte connection_id) + 0x0D, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + QUIC_VERSION_BYTES, + }; + unsigned char packet44[] = { + // type (long header) + 0x80, + // version tag + 0x00, 0x00, 0x00, 0x00, + // connection_id length + 0x05, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + QUIC_VERSION_BYTES, + }; + // clang-format on + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + QuicConnectionId connection_id = FramerTestConnectionId(); + std::unique_ptr<QuicEncryptedPacket> data( + framer_.BuildVersionNegotiationPacket( + connection_id, framer_.transport_version() > QUIC_VERSION_43, + SupportedVersions(GetParam()))); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // Use kSmallLargestObserved to make this test finished in a short time. + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + + QuicFrames frames = {QuicFrame(&ack_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 2 byte largest observed, 2 byte block length) + 0x45, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, + // num timestamps. + 0x00, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 2 byte largest observed, 2 byte block length) + 0x45, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, + // num timestamps. + 0x00, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 2 byte largest observed, 2 byte block length) + 0x45, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, + // num timestamps. + 0x00, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_ACK frame) + 0x02, + // largest acked + kVarInt62TwoBytes + 0x12, 0x34, + // Zero delta time. + kVarInt62OneByte + 0x00, + // Number of additional ack blocks. + kVarInt62OneByte + 0x00, + // first ack block length. + kVarInt62TwoBytes + 0x12, 0x33, + }; + // clang-format on + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kPacketNumber); + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + + QuicFrames frames = {QuicFrame(&ack_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 4 byte largest observed, 4 byte block length) + 0x4A, + // largest acked + 0x12, 0x34, 0x56, 0x78, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, 0x56, 0x78, + // num timestamps. + 0x00, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 4 byte largest observed, 4 byte block length) + 0x4A, + // largest acked + 0x12, 0x34, 0x56, 0x78, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, 0x56, 0x78, + // num timestamps. + 0x00, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (no ack blocks, 4 byte largest observed, 4 byte block length) + 0x4A, + // largest acked + 0x12, 0x34, 0x56, 0x78, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, 0x56, 0x78, + // num timestamps. + 0x00, + }; + + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_ACK frame) + 0x02, + // largest acked + kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x78, + // Zero delta time. + kVarInt62OneByte + 0x00, + // Nr. of additional ack blocks + kVarInt62OneByte + 0x00, + // first ack block length. + kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x77, + }; + // clang-format on + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // Use kSmallLargestObserved to make this test finished in a short time. + QuicAckFrame ack_frame = + InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(5)}, + {QuicPacketNumber(10), QuicPacketNumber(500)}, + {QuicPacketNumber(900), kSmallMissingPacket}, + {kSmallMissingPacket + 1, kSmallLargestObserved + 1}}); + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + + QuicFrames frames = {QuicFrame(&ack_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0x65, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // num ack blocks ranges. + 0x04, + // first ack block length. + 0x00, 0x01, + // gap to next block. + 0x01, + // ack block length. + 0x0e, 0xaf, + // gap to next block. + 0xff, + // ack block length. + 0x00, 0x00, + // gap to next block. + 0x91, + // ack block length. + 0x01, 0xea, + // gap to next block. + 0x05, + // ack block length. + 0x00, 0x04, + // num timestamps. + 0x00, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0x65, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // num ack blocks ranges. + 0x04, + // first ack block length. + 0x00, 0x01, + // gap to next block. + 0x01, + // ack block length. + 0x0e, 0xaf, + // gap to next block. + 0xff, + // ack block length. + 0x00, 0x00, + // gap to next block. + 0x91, + // ack block length. + 0x01, 0xea, + // gap to next block. + 0x05, + // ack block length. + 0x00, 0x04, + // num timestamps. + 0x00, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0x65, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // num ack blocks ranges. + 0x04, + // first ack block length. + 0x00, 0x01, + // gap to next block. + 0x01, + // ack block length. + 0x0e, 0xaf, + // gap to next block. + 0xff, + // ack block length. + 0x00, 0x00, + // gap to next block. + 0x91, + // ack block length. + 0x01, 0xea, + // gap to next block. + 0x05, + // ack block length. + 0x00, 0x04, + // num timestamps. + 0x00, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_ACK frame) + 0x02, + // largest acked + kVarInt62TwoBytes + 0x12, 0x34, + // Zero delta time. + kVarInt62OneByte + 0x00, + // num additional ack blocks. + kVarInt62OneByte + 0x03, + // first ack block length. + kVarInt62OneByte + 0x00, + + // gap to next block. + kVarInt62OneByte + 0x00, + // ack block length. + kVarInt62TwoBytes + 0x0e, 0xae, + + // gap to next block. + kVarInt62TwoBytes + 0x01, 0x8f, + // ack block length. + kVarInt62TwoBytes + 0x01, 0xe9, + + // gap to next block. + kVarInt62OneByte + 0x04, + // ack block length. + kVarInt62OneByte + 0x03, + }; + // clang-format on + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // Use kSmallLargestObservedto make this test finished in a short time. + QuicAckFrame ack_frame; + ack_frame.largest_acked = kSmallLargestObserved; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + // 300 ack blocks. + for (size_t i = 2; i < 2 * 300; i += 2) { + ack_frame.packets.Add(QuicPacketNumber(i)); + } + ack_frame.packets.AddRange(QuicPacketNumber(600), kSmallLargestObserved + 1); + + QuicFrames frames = {QuicFrame(&ack_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0x65, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // num ack blocks ranges. + 0xff, + // first ack block length. + 0x0f, 0xdd, + // 255 = 4 * 63 + 3 + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + // num timestamps. + 0x00, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0x65, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // num ack blocks ranges. + 0xff, + // first ack block length. + 0x0f, 0xdd, + // 255 = 4 * 63 + 3 + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + // num timestamps. + 0x00, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0x65, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // num ack blocks ranges. + 0xff, + // first ack block length. + 0x0f, 0xdd, + // 255 = 4 * 63 + 3 + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + // num timestamps. + 0x00, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (IETF_ACK frame) + 0x02, + // largest acked + kVarInt62TwoBytes + 0x12, 0x34, + // Zero delta time. + kVarInt62OneByte + 0x00, + // num ack blocks ranges. + kVarInt62TwoBytes + 0x01, 0x2b, + // first ack block length. + kVarInt62TwoBytes + 0x0f, 0xdc, + // 255 added blocks of gap_size == 1, ack_size == 1 +#define V99AddedBLOCK kVarInt62OneByte + 0x00, kVarInt62OneByte + 0x00 + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, + +#undef V99AddedBLOCK + }; + // clang-format on + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildNewStopWaitingPacket) { + if (version_.transport_version > QUIC_VERSION_43) { + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicStopWaitingFrame stop_waiting_frame; + stop_waiting_frame.least_unacked = kLeastUnacked; + + QuicFrames frames = {QuicFrame(stop_waiting_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stop waiting frame) + 0x06, + // least packet number awaiting an ack, delta from packet number. + 0x00, 0x00, 0x00, 0x08, + }; + + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicRstStreamFrame rst_frame; + rst_frame.stream_id = kStreamId; + if (framer_.transport_version() == QUIC_VERSION_99) { + rst_frame.ietf_error_code = 0x01; + } else { + rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708); + } + rst_frame.byte_offset = 0x0807060504030201; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (rst stream frame) + 0x01, + // stream id + 0x01, 0x02, 0x03, 0x04, + // sent byte offset + 0x08, 0x07, 0x06, 0x05, + 0x04, 0x03, 0x02, 0x01, + // error code + 0x05, 0x06, 0x07, 0x08, + }; + + unsigned char packet44[] = { + // type (short packet, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (rst stream frame) + 0x01, + // stream id + 0x01, 0x02, 0x03, 0x04, + // sent byte offset + 0x08, 0x07, 0x06, 0x05, + 0x04, 0x03, 0x02, 0x01, + // error code + 0x05, 0x06, 0x07, 0x08, + }; + + unsigned char packet46[] = { + // type (short packet, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (rst stream frame) + 0x01, + // stream id + 0x01, 0x02, 0x03, 0x04, + // sent byte offset + 0x08, 0x07, 0x06, 0x05, + 0x04, 0x03, 0x02, 0x01, + // error code + 0x05, 0x06, 0x07, 0x08, + }; + + unsigned char packet99[] = { + // type (short packet, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_RST_STREAM frame) + 0x04, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // error code (not VarInt32 encoded) + 0x00, 0x01, + // sent byte offset + kVarInt62EightBytes + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 + }; + // clang-format on + + QuicFrames frames = {QuicFrame(&rst_frame)}; + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildCloseFramePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicConnectionCloseFrame close_frame; + if (framer_.transport_version() == QUIC_VERSION_99) { + close_frame.ietf_error_code = + static_cast<QuicIetfTransportErrorCodes>(0x11); + close_frame.frame_type = 0x05; + } else { + close_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + } + close_frame.error_details = "because I can"; + + QuicFrames frames = {QuicFrame(&close_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (connection close frame) + 0x02, + // error code + 0x05, 0x06, 0x07, 0x08, + // error details length + 0x00, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (connection close frame) + 0x02, + // error code + 0x05, 0x06, 0x07, 0x08, + // error details length + 0x00, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (connection close frame) + 0x02, + // error code + 0x05, 0x06, 0x07, 0x08, + // error details length + 0x00, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_CONNECTION_CLOSE frame) + 0x1c, + // error code + 0x00, 0x11, + // Frame type within the CONNECTION_CLOSE frame + kVarInt62OneByte + 0x05, + // error details length + kVarInt62OneByte + 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildTruncatedCloseFramePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicConnectionCloseFrame close_frame; + if (framer_.transport_version() == QUIC_VERSION_99) { + close_frame.ietf_error_code = PROTOCOL_VIOLATION; // value is 0x0a + EXPECT_EQ(0u, close_frame.frame_type); + } else { + close_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + } + close_frame.error_details = QuicString(2048, 'A'); + QuicFrames frames = {QuicFrame(&close_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (connection close frame) + 0x02, + // error code + 0x05, 0x06, 0x07, 0x08, + // error details length + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (connection close frame) + 0x02, + // error code + 0x05, 0x06, 0x07, 0x08, + // error details length + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (connection close frame) + 0x02, + // error code + 0x05, 0x06, 0x07, 0x08, + // error details length + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_CONNECTION_CLOSE frame) + 0x1c, + // error code + 0x00, 0x0a, + // Frame type within the CONNECTION_CLOSE frame + kVarInt62OneByte + 0x00, + // error details length + kVarInt62TwoBytes + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildApplicationCloseFramePacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // Versions other than 99 do not have ApplicationClose + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicApplicationCloseFrame app_close_frame; + app_close_frame.error_code = static_cast<QuicErrorCode>(0x11); + app_close_frame.error_details = "because I can"; + + QuicFrames frames = {QuicFrame(&app_close_frame)}; + + // clang-format off + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_APPLICATION_CLOSE frame) + 0x1d, + // error code + 0x00, 0x11, + // error details length + kVarInt62OneByte + 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildTruncatedApplicationCloseFramePacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // Versions other than 99 do not have this frame. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicApplicationCloseFrame app_close_frame; + app_close_frame.error_code = static_cast<QuicErrorCode>(0x11); + app_close_frame.error_details = QuicString(2048, 'A'); + + QuicFrames frames = {QuicFrame(&app_close_frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_APPLICATION_CLOSE frame) + 0x1d, + // error code + 0x00, 0x11, + // error details length + kVarInt62TwoBytes + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildGoAwayPacket) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // This frame type is not supported in version 99. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicGoAwayFrame goaway_frame; + goaway_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + goaway_frame.last_good_stream_id = kStreamId; + goaway_frame.reason_phrase = "because I can"; + + QuicFrames frames = {QuicFrame(&goaway_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (go away frame) + 0x03, + // error code + 0x05, 0x06, 0x07, 0x08, + // stream id + 0x01, 0x02, 0x03, 0x04, + // error details length + 0x00, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (go away frame) + 0x03, + // error code + 0x05, 0x06, 0x07, 0x08, + // stream id + 0x01, 0x02, 0x03, 0x04, + // error details length + 0x00, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (go away frame) + 0x03, + // error code + 0x05, 0x06, 0x07, 0x08, + // stream id + 0x01, 0x02, 0x03, 0x04, + // error details length + 0x00, 0x0d, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildTruncatedGoAwayPacket) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // This frame type is not supported in version 99. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicGoAwayFrame goaway_frame; + goaway_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + goaway_frame.last_good_stream_id = kStreamId; + goaway_frame.reason_phrase = QuicString(2048, 'A'); + + QuicFrames frames = {QuicFrame(&goaway_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (go away frame) + 0x03, + // error code + 0x05, 0x06, 0x07, 0x08, + // stream id + 0x01, 0x02, 0x03, 0x04, + // error details length + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (go away frame) + 0x03, + // error code + 0x05, 0x06, 0x07, 0x08, + // stream id + 0x01, 0x02, 0x03, 0x04, + // error details length + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (go away frame) + 0x03, + // error code + 0x05, 0x06, 0x07, 0x08, + // stream id + 0x01, 0x02, 0x03, 0x04, + // error details length + 0x01, 0x00, + // error details (truncated to 256 bytes) + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicWindowUpdateFrame window_update_frame; + window_update_frame.stream_id = kStreamId; + window_update_frame.byte_offset = 0x1122334455667788; + + QuicFrames frames = {QuicFrame(&window_update_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (window update frame) + 0x04, + // stream id + 0x01, 0x02, 0x03, 0x04, + // byte offset + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (window update frame) + 0x04, + // stream id + 0x01, 0x02, 0x03, 0x04, + // byte offset + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (window update frame) + 0x04, + // stream id + 0x01, 0x02, 0x03, 0x04, + // byte offset + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MAX_STREAM_DATA frame) + 0x11, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // byte offset + kVarInt62EightBytes + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildMaxStreamDataPacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is available only in this version. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicWindowUpdateFrame window_update_frame; + window_update_frame.stream_id = kStreamId; + window_update_frame.byte_offset = 0x1122334455667788; + + QuicFrames frames = {QuicFrame(&window_update_frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MAX_STREAM_DATA frame) + 0x11, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // byte offset + kVarInt62EightBytes + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildMaxDataPacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is available only in this version. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicWindowUpdateFrame window_update_frame; + window_update_frame.stream_id = + QuicUtils::GetInvalidStreamId(framer_.transport_version()); + window_update_frame.byte_offset = 0x1122334455667788; + + QuicFrames frames = {QuicFrame(&window_update_frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MAX_DATA frame) + 0x10, + // byte offset + kVarInt62EightBytes + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildBlockedPacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicBlockedFrame blocked_frame; + if (framer_.transport_version() == QUIC_VERSION_99) { + // For V99, the stream ID must be <invalid> for the frame + // to be a BLOCKED frame. if it's valid, it will be a + // STREAM_BLOCKED frame. + blocked_frame.stream_id = + QuicUtils::GetInvalidStreamId(framer_.transport_version()); + } else { + blocked_frame.stream_id = kStreamId; + } + blocked_frame.offset = kStreamOffset; + + QuicFrames frames = {QuicFrame(&blocked_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (blocked frame) + 0x05, + // stream id + 0x01, 0x02, 0x03, 0x04, + }; + + unsigned char packet44[] = { + // type (short packet, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (blocked frame) + 0x05, + // stream id + 0x01, 0x02, 0x03, 0x04, + }; + + unsigned char packet46[] = { + // type (short packet, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (blocked frame) + 0x05, + // stream id + 0x01, 0x02, 0x03, 0x04, + }; + + unsigned char packet99[] = { + // type (short packet, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_BLOCKED frame) + 0x14, + // Offset + kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), p_size); +} + +TEST_P(QuicFramerTest, BuildPingPacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicFrames frames = {QuicFrame(QuicPingFrame())}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ping frame) + 0x07, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_PING frame) + 0x01, + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildMessagePacket) { + if (framer_.transport_version() <= QUIC_VERSION_44) { + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); + + QuicMessageFrame frame(1); + MakeSpan(&allocator_, "message", &storage).SaveMemSlicesAsMessageData(&frame); + QuicMessageFrame frame2(2); + MakeSpan(&allocator_, "message2", &storage) + .SaveMemSlicesAsMessageData(&frame2); + QuicFrames frames = {QuicFrame(&frame), QuicFrame(&frame2)}; + + // clang-format off + unsigned char packet45[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (message frame) + 0x21, + // Length + 0x07, + // Message Data + 'm', 'e', 's', 's', 'a', 'g', 'e', + // frame type (message frame no length) + 0x20, + // Message Data + 'm', 'e', 's', 's', 'a', 'g', 'e', '2' + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (message frame) + 0x21, + // Length + 0x07, + // Message Data + 'm', 'e', 's', 's', 'a', 'g', 'e', + // frame type (message frame no length) + 0x20, + // Message Data + 'm', 'e', 's', 's', 'a', 'g', 'e', '2' + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MESSAGE frame) + 0x21, + // Length + 0x07, + // Message Data + 'm', 'e', 's', 's', 'a', 'g', 'e', + // frame type (message frame no length) + 0x20, + // Message Data + 'm', 'e', 's', 's', 'a', 'g', 'e', '2' + }; + // clang-format on + + unsigned char* p = packet45; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + QUIC_ARRAYSIZE(packet45)); +} + +// Test that the connectivity probing packet is serialized correctly as a +// padded PING packet. +TEST_P(QuicFramerTest, BuildConnectivityProbingPacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ping frame) + 0x07, + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_PING frame) + 0x01, + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + unsigned char* p = packet; + size_t packet_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + packet_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + packet_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + packet_size = QUIC_ARRAYSIZE(packet44); + } + + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + + size_t length = framer_.BuildConnectivityProbingPacket( + header, buffer.get(), packet_size, ENCRYPTION_NONE); + + EXPECT_NE(0u, length); + QuicPacket data(framer_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError("constructed packet", data.data(), + data.length(), AsChars(p), packet_size); +} + +// Test that the path challenge connectivity probing packet is serialized +// correctly as a padded PATH CHALLENGE packet. +TEST_P(QuicFramerTest, BuildPaddedPathChallengePacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload; + + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // Path Challenge Frame type (IETF_PATH_CHALLENGE) + 0x1a, + // 8 "random" bytes, MockRandom makes lots of r's + 'r', 'r', 'r', 'r', 'r', 'r', 'r', 'r', + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + MockRandom randomizer; + + size_t length = framer_.BuildPaddedPathChallengePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), &payload, &randomizer, + ENCRYPTION_NONE); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + + // Payload has the random bytes that were generated. Copy them into packet, + // above, before checking that the generated packet is correct. + EXPECT_EQ(kQuicPathFrameBufferSize, payload.size()); + + QuicPacket data(framer_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError("constructed packet", data.data(), + data.length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +// Several tests that the path response connectivity probing packet is +// serialized correctly as either a padded and unpadded PATH RESPONSE +// packet. Also generates packets with 1 and 3 PATH_RESPONSES in them to +// exercised the single- and multiple- payload cases. +TEST_P(QuicFramerTest, BuildPathResponsePacket1ResponseUnpadded) { + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + + // Build 1 PATH RESPONSE, not padded + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // Path Response Frame type (IETF_PATH_RESPONSE) + 0x1b, + // 8 "random" bytes + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }; + // clang-format on + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + size_t length = framer_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/false, ENCRYPTION_NONE); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(framer_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError("constructed packet", data.data(), + data.length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildPathResponsePacket1ResponsePadded) { + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + + // Build 1 PATH RESPONSE, padded + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // Path Response Frame type (IETF_PATH_RESPONSE) + 0x1b, + // 8 "random" bytes + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + // Padding type and pad + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + size_t length = framer_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/true, ENCRYPTION_NONE); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(framer_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError("constructed packet", data.data(), + data.length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildPathResponsePacket3ResponsesUnpadded) { + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + QuicPathFrameBuffer payload1 = { + {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; + QuicPathFrameBuffer payload2 = { + {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; + + // Build one packet with 3 PATH RESPONSES, no padding + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // 3 path response frames (IETF_PATH_RESPONSE type byte and payload) + 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + }; + // clang-format on + + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); + size_t length = framer_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/false, ENCRYPTION_NONE); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(framer_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError("constructed packet", data.data(), + data.length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildPathResponsePacket3ResponsesPadded) { + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + QuicPathFrameBuffer payload1 = { + {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; + QuicPathFrameBuffer payload2 = { + {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; + + // Build one packet with 3 PATH RESPONSES, with padding + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // 3 path response frames (IETF_PATH_RESPONSE byte and payload) + 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + // Padding + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); + size_t length = framer_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/true, ENCRYPTION_NONE); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(framer_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError("constructed packet", data.data(), + data.length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +// Test that the MTU discovery packet is serialized correctly as a PING packet. +TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicFrames frames = {QuicFrame(QuicMtuDiscoveryFrame())}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ping frame) + 0x07, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_PING frame) + 0x01, + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildPublicResetPacket) { + QuicPublicResetPacket reset_packet; + reset_packet.connection_id = FramerTestConnectionId(); + reset_packet.nonce_proof = kNonceProof; + + // clang-format off + unsigned char packet[] = { + // public flags (public reset, 8 byte ConnectionId) + 0x0E, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (1) + padding + 0x01, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + }; + // clang-format on + + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + std::unique_ptr<QuicEncryptedPacket> data( + framer_.BuildPublicResetPacket(reset_packet)); + ASSERT_TRUE(data != nullptr); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) { + QuicPublicResetPacket reset_packet; + reset_packet.connection_id = FramerTestConnectionId(); + reset_packet.nonce_proof = kNonceProof; + reset_packet.client_address = + QuicSocketAddress(QuicIpAddress::Loopback4(), 0x1234); + + // clang-format off + unsigned char packet[] = { + // public flags (public reset, 8 byte ConnectionId) + 0x0E, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, + 0x76, 0x54, 0x32, 0x10, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kCADR + 'C', 'A', 'D', 'R', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // client address + 0x02, 0x00, + 0x7F, 0x00, 0x00, 0x01, + 0x34, 0x12, + }; + // clang-format on + + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + std::unique_ptr<QuicEncryptedPacket> data( + framer_.BuildPublicResetPacket(reset_packet)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, BuildPublicResetPacketWithEndpointId) { + QuicPublicResetPacket reset_packet; + reset_packet.connection_id = FramerTestConnectionId(); + reset_packet.nonce_proof = kNonceProof; + reset_packet.endpoint_id = "FakeServerId"; + + // The tag value map in CryptoHandshakeMessage is a std::map, so the two tags + // in the packet, kRNON and kEPID, have unspecified ordering w.r.t each other. + // clang-format off + unsigned char packet_variant1[] = { + // public flags (public reset, 8 byte ConnectionId) + 0x0E, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, + 0x76, 0x54, 0x32, 0x10, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kEPID + 'E', 'P', 'I', 'D', + // end offset 20 + 0x14, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // Endpoint ID + 'F', 'a', 'k', 'e', 'S', 'e', 'r', 'v', 'e', 'r', 'I', 'd', + }; + unsigned char packet_variant2[] = { + // public flags (public reset, 8 byte ConnectionId) + 0x0E, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, + 0x76, 0x54, 0x32, 0x10, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kEPID + 'E', 'P', 'I', 'D', + // end offset 12 + 0x0C, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 20 + 0x14, 0x00, 0x00, 0x00, + // Endpoint ID + 'F', 'a', 'k', 'e', 'S', 'e', 'r', 'v', 'e', 'r', 'I', 'd', + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + }; + // clang-format on + + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + + std::unique_ptr<QuicEncryptedPacket> data( + framer_.BuildPublicResetPacket(reset_packet)); + ASSERT_TRUE(data != nullptr); + + // Variant 1 ends with char 'd'. Variant 1 ends with char 0xAB. + if ('d' == data->data()[data->length() - 1]) { + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), + AsChars(packet_variant1), QUIC_ARRAYSIZE(packet_variant1)); + } else { + test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), + AsChars(packet_variant2), QUIC_ARRAYSIZE(packet_variant2)); + } +} + +TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { + // clang-format off + unsigned char packet44[] = { + // type (short header, 1 byte packet number) + 0x70, + // random packet number + 0xFE, + // stateless reset token + 0xB5, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> data( + framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(), + kTestStatelessResetToken)); + ASSERT_TRUE(data != nullptr); + // Skip packet number byte which is random in stateless reset packet. + test::CompareCharArraysWithHexError("constructed packet", data->data(), 1, + AsChars(packet44), 1); + const size_t random_bytes_length = + data->length() - kPacketHeaderTypeSize - sizeof(kTestStatelessResetToken); + EXPECT_EQ(kMinRandomBytesLengthInStatelessReset, random_bytes_length); + // Verify stateless reset token is correct. + test::CompareCharArraysWithHexError( + "constructed packet", + data->data() + data->length() - sizeof(kTestStatelessResetToken), + sizeof(kTestStatelessResetToken), + AsChars(packet44) + QUIC_ARRAYSIZE(packet44) - + sizeof(kTestStatelessResetToken), + sizeof(kTestStatelessResetToken)); +} + +TEST_P(QuicFramerTest, EncryptPacket) { + QuicPacketNumber packet_number = kPacketNumber; + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + std::unique_ptr<QuicPacket> raw(new QuicPacket( + AsChars(p), QUIC_ARRAYSIZE(packet), false, PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = framer_.EncryptPayload( + ENCRYPTION_NONE, packet_number, *raw, buffer, kMaxPacketSize); + + ASSERT_NE(0u, encrypted_length); + EXPECT_TRUE(CheckEncryption(packet_number, raw.get())); +} + +TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { + QuicPacketNumber packet_number = kPacketNumber; + // clang-format off + unsigned char packet[] = { + // public flags (version, 8 byte connection_id) + 0x29, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + 'Q', '.', '1', '0', + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + unsigned char packet44[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xFC, + // version tag + 'Q', '.', '1', '0', + // connection_id length + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + unsigned char packet46[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xD3, + // version tag + 'Q', '.', '1', '0', + // connection_id length + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + unsigned char packet99[] = { + // type (long header with packet type ZERO_RTT_PROTECTED) + 0xD3, + // version tag + 'Q', '.', '1', '0', + // connection_id length + 0x50, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + + std::unique_ptr<QuicPacket> raw(new QuicPacket( + AsChars(p), + framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet), + false, PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID, + kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_4BYTE_PACKET_NUMBER, VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, + VARIABLE_LENGTH_INTEGER_LENGTH_0)); + char buffer[kMaxPacketSize]; + size_t encrypted_length = framer_.EncryptPayload( + ENCRYPTION_NONE, packet_number, *raw, buffer, kMaxPacketSize); + + ASSERT_NE(0u, encrypted_length); + EXPECT_TRUE(CheckEncryption(packet_number, raw.get())); +} + +TEST_P(QuicFramerTest, AckTruncationLargePacket) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // This test is not applicable to this version; the range count is + // effectively unlimited + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame; + // Create a packet with just the ack. + ack_frame = MakeAckFrameWithAckBlocks(300, 0u); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + // Build an ack packet with truncation due to limit in number of nack ranges. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames)); + ASSERT_TRUE(raw_ack_packet != nullptr); + char buffer[kMaxPacketSize]; + size_t encrypted_length = + framer_.EncryptPayload(ENCRYPTION_NONE, header.packet_number, + *raw_ack_packet, buffer, kMaxPacketSize); + ASSERT_NE(0u, encrypted_length); + // Now make sure we can turn our ack packet back into an ack frame. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + ASSERT_TRUE(framer_.ProcessPacket( + QuicEncryptedPacket(buffer, encrypted_length, false))); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(QuicPacketNumber(600u), LargestAcked(processed_ack_frame)); + ASSERT_EQ(256u, processed_ack_frame.packets.NumPacketsSlow()); + EXPECT_EQ(QuicPacketNumber(90u), processed_ack_frame.packets.Min()); + EXPECT_EQ(QuicPacketNumber(600u), processed_ack_frame.packets.Max()); +} + +TEST_P(QuicFramerTest, AckTruncationSmallPacket) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // This test is not applicable to this version; the range count is + // effectively unlimited + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // Create a packet with just the ack. + QuicAckFrame ack_frame; + ack_frame = MakeAckFrameWithAckBlocks(300, 0u); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + // Build an ack packet with truncation due to limit in number of nack ranges. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> raw_ack_packet( + BuildDataPacket(header, frames, 500)); + ASSERT_TRUE(raw_ack_packet != nullptr); + char buffer[kMaxPacketSize]; + size_t encrypted_length = + framer_.EncryptPayload(ENCRYPTION_NONE, header.packet_number, + *raw_ack_packet, buffer, kMaxPacketSize); + ASSERT_NE(0u, encrypted_length); + // Now make sure we can turn our ack packet back into an ack frame. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + ASSERT_TRUE(framer_.ProcessPacket( + QuicEncryptedPacket(buffer, encrypted_length, false))); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(QuicPacketNumber(600u), LargestAcked(processed_ack_frame)); + ASSERT_EQ(240u, processed_ack_frame.packets.NumPacketsSlow()); + EXPECT_EQ(QuicPacketNumber(122u), processed_ack_frame.packets.Min()); + EXPECT_EQ(QuicPacketNumber(600u), processed_ack_frame.packets.Max()); +} + +TEST_P(QuicFramerTest, CleanTruncation) { + if (framer_.transport_version() == QUIC_VERSION_99) { + // This test is not applicable to this version; the range count is + // effectively unlimited + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(201); + + // Create a packet with just the ack. + QuicFrames frames = {QuicFrame(&ack_frame)}; + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames)); + ASSERT_TRUE(raw_ack_packet != nullptr); + + char buffer[kMaxPacketSize]; + size_t encrypted_length = + framer_.EncryptPayload(ENCRYPTION_NONE, header.packet_number, + *raw_ack_packet, buffer, kMaxPacketSize); + ASSERT_NE(0u, encrypted_length); + + // Now make sure we can turn our ack packet back into an ack frame. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + ASSERT_TRUE(framer_.ProcessPacket( + QuicEncryptedPacket(buffer, encrypted_length, false))); + + // Test for clean truncation of the ack by comparing the length of the + // original packets to the re-serialized packets. + frames.clear(); + frames.push_back(QuicFrame(visitor_.ack_frames_[0].get())); + + size_t original_raw_length = raw_ack_packet->length(); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + raw_ack_packet = BuildDataPacket(header, frames); + ASSERT_TRUE(raw_ack_packet != nullptr); + EXPECT_EQ(original_raw_length, raw_ack_packet->length()); + ASSERT_TRUE(raw_ack_packet != nullptr); +} + +TEST_P(QuicFramerTest, StopPacketProcessing) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x28, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + + // frame type (ack frame) + 0x40, + // least packet number awaiting an ack + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xA0, + // largest observed packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBF, + // num missing packets + 0x01, + // missing packet + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBE, + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + + // frame type (ack frame) + 0x40, + // least packet number awaiting an ack + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xA0, + // largest observed packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBF, + // num missing packets + 0x01, + // missing packet + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBE, + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + + // frame type (ack frame) + 0x40, + // least packet number awaiting an ack + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xA0, + // largest observed packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBF, + // num missing packets + 0x01, + // missing packet + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBE, + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAM frame with fin, length, and offset bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62TwoBytes + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + + // frame type (ack frame) + 0x0d, + // largest observed packet number + kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x78, + // Delta time + kVarInt62OneByte + 0x00, + // Ack Block count + kVarInt62OneByte + 0x01, + // First block size (one packet) + kVarInt62OneByte + 0x00, + + // Next gap size & ack. Missing all preceding packets + kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x77, + kVarInt62OneByte + 0x00, + }; + // clang-format on + + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()); + EXPECT_CALL(visitor, OnPacketHeader(_)); + EXPECT_CALL(visitor, OnStreamFrame(_)).WillOnce(Return(false)); + EXPECT_CALL(visitor, OnPacketComplete()); + EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)).WillOnce(Return(true)); + EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)).WillOnce(Return(true)); + EXPECT_CALL(visitor, OnDecryptedPacket(_)); + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); +} + +static char kTestString[] = "At least 20 characters."; +static QuicStreamId kTestQuicStreamId = 1; +static bool ExpectedStreamFrame(const QuicStreamFrame& frame) { + return (frame.stream_id == kTestQuicStreamId || + frame.stream_id == QuicUtils::GetCryptoStreamId(QUIC_VERSION_99)) && + !frame.fin && frame.offset == 0 && + QuicString(frame.data_buffer, frame.data_length) == kTestString; + // FIN is hard-coded false in ConstructEncryptedPacket. + // Offset 0 is hard-coded in ConstructEncryptedPacket. +} + +// Verify that the packet returned by ConstructEncryptedPacket() can be properly +// parsed by the framer. +TEST_P(QuicFramerTest, ConstructEncryptedPacket) { + // Since we are using ConstructEncryptedPacket, we have to set the framer's + // crypto to be Null. + framer_.SetDecrypter(ENCRYPTION_NONE, + QuicMakeUnique<NullDecrypter>(framer_.perspective())); + framer_.SetEncrypter(ENCRYPTION_NONE, + QuicMakeUnique<NullEncrypter>(framer_.perspective())); + ParsedQuicVersionVector versions; + versions.push_back(framer_.version()); + std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + TestConnectionId(), EmptyQuicConnectionId(), false, false, + kTestQuicStreamId, kTestString, CONNECTION_ID_PRESENT, + CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &versions)); + + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()).Times(1); + EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnPacketHeader(_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(visitor, OnDecryptedPacket(_)).Times(1); + EXPECT_CALL(visitor, OnError(_)).Times(0); + EXPECT_CALL(visitor, OnStreamFrame(_)).Times(0); + if (framer_.version().transport_version < QUIC_VERSION_47) { + EXPECT_CALL(visitor, OnStreamFrame(Truly(ExpectedStreamFrame))).Times(1); + } else { + EXPECT_CALL(visitor, OnCryptoFrame(_)).Times(1); + } + EXPECT_CALL(visitor, OnPacketComplete()).Times(1); + + EXPECT_TRUE(framer_.ProcessPacket(*packet)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); +} + +// Verify that the packet returned by ConstructMisFramedEncryptedPacket() +// does cause the framer to return an error. +TEST_P(QuicFramerTest, ConstructMisFramedEncryptedPacket) { + // Since we are using ConstructEncryptedPacket, we have to set the framer's + // crypto to be Null. + framer_.SetDecrypter(ENCRYPTION_NONE, + QuicMakeUnique<NullDecrypter>(framer_.perspective())); + framer_.SetEncrypter(ENCRYPTION_NONE, + QuicMakeUnique<NullEncrypter>(framer_.perspective())); + ParsedQuicVersionVector versions; + versions.push_back(framer_.version()); + std::unique_ptr<QuicEncryptedPacket> packet(ConstructMisFramedEncryptedPacket( + TestConnectionId(), EmptyQuicConnectionId(), false, false, + kTestQuicStreamId, kTestString, CONNECTION_ID_PRESENT, + CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &versions, + Perspective::IS_CLIENT)); + + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()).Times(1); + EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnPacketHeader(_)).Times(1); + EXPECT_CALL(visitor, OnDecryptedPacket(_)).Times(1); + EXPECT_CALL(visitor, OnError(_)).Times(1); + EXPECT_CALL(visitor, OnStreamFrame(_)).Times(0); + EXPECT_CALL(visitor, OnPacketComplete()).Times(0); + + EXPECT_FALSE(framer_.ProcessPacket(*packet)); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); +} + +// Tests for fuzzing with Dr. Fuzz +// Xref http://www.chromium.org/developers/testing/dr-fuzz for more details. +#ifdef __cplusplus +extern "C" { +#endif + +// target function to be fuzzed by Dr. Fuzz +void QuicFramerFuzzFunc(unsigned char* data, + size_t size, + const ParsedQuicVersion& version) { + QuicFramer framer(AllSupportedVersions(), QuicTime::Zero(), + Perspective::IS_SERVER, kQuicDefaultConnectionIdLength); + ASSERT_EQ(GetQuicFlag(FLAGS_quic_supports_tls_handshake), true); + const char* const packet_bytes = reinterpret_cast<const char*>(data); + + // Test the CryptoFramer. + QuicStringPiece crypto_input(packet_bytes, size); + std::unique_ptr<CryptoHandshakeMessage> handshake_message( + CryptoFramer::ParseMessage(crypto_input)); + + // Test the regular QuicFramer with the same input. + NoOpFramerVisitor visitor; + framer.set_visitor(&visitor); + framer.set_version(version); + QuicEncryptedPacket packet(packet_bytes, size); + framer.ProcessPacket(packet); +} + +#ifdef __cplusplus +} +#endif + +TEST_P(QuicFramerTest, FramerFuzzTest) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x2C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin, length, and offset bits set) + 0x10 | 0x01 | 0x02 | 0x04, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (stream frame with fin, length, and offset bits set) + 0x10 | 0x01 | 0x02 | 0x04, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAM frame with fin, length, and offset bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + } + QuicFramerFuzzFunc(p, + framer_.transport_version() > QUIC_VERSION_43 + ? QUIC_ARRAYSIZE(packet44) + : QUIC_ARRAYSIZE(packet), + framer_.version()); +} + +TEST_P(QuicFramerTest, StartsWithChlo) { + SimpleDataProducer producer; + framer_.set_data_producer(&producer); + QuicStringPiece data("CHLOCHLO"); + struct iovec iovec; + iovec.iov_base = const_cast<char*>(data.data()); + iovec.iov_len = data.length(); + producer.SaveStreamData( + QuicUtils::GetCryptoStreamId(framer_.transport_version()), &iovec, 1, 0, + data.length()); + for (size_t offset = 0; offset < 5; ++offset) { + if (offset == 0 || offset == 4) { + EXPECT_TRUE(framer_.StartsWithChlo( + QuicUtils::GetCryptoStreamId(framer_.transport_version()), offset)); + } else { + EXPECT_FALSE(framer_.StartsWithChlo( + QuicUtils::GetCryptoStreamId(framer_.transport_version()), offset)); + } + } +} + +TEST_P(QuicFramerTest, IetfBlockedFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_BLOCKED) + {"", + {0x14}}, + // blocked offset + {"Can not read blocked offset.", + {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset); + + CheckFramingBoundaries(packet99, QUIC_INVALID_BLOCKED_DATA); +} + +TEST_P(QuicFramerTest, BuildIetfBlockedPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicBlockedFrame frame; + frame.stream_id = QuicUtils::GetInvalidStreamId(framer_.transport_version()); + frame.offset = kStreamOffset; + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_BLOCKED) + 0x14, + // Offset + kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, IetfStreamBlockedFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_STREAM_BLOCKED) + {"", + {0x15}}, + // blocked offset + {"Can not read stream blocked stream id.", + {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}}, + {"Can not read stream blocked offset.", + {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(kStreamId, visitor_.blocked_frame_.stream_id); + EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset); + + CheckFramingBoundaries(packet99, QUIC_INVALID_STREAM_BLOCKED_DATA); +} + +TEST_P(QuicFramerTest, BuildIetfStreamBlockedPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicBlockedFrame frame; + frame.stream_id = kStreamId; + frame.offset = kStreamOffset; + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAM_BLOCKED) + 0x15, + // Stream ID + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // Offset + kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, ServerBiDiMaxStreamsFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL) + {"", + {0x12}}, + // max. streams + {"Can not read MAX_STREAMS stream count.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a server receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a server-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) | 0x1 = 0x9 + // count-to-id server inited, bidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/true, 3), + visitor_.max_stream_id_frame_.max_stream_id); + CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA); +} + +TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // Test runs in client mode, no connection id + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL) + {"", + {0x12}}, + // max. streams + {"Can not read MAX_STREAMS stream count.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a client receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a client-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) = 0xc + // It is not 8 because a client-initiated, bidi stream ID's + // low bits are 00 - which means that the old crypto stream + // falls into this category, and the first stream is streamid=4, + // not streamid=0. + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/true, 3), + visitor_.max_stream_id_frame_.max_stream_id); + + CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA); +} + +TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL) + {"", + {0x13}}, + // max. streams + {"Can not read MAX_STREAMS stream count.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a server receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a server-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) | 0x1 | 0x2 = 0xb + // count-to-id server inited, unidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/false, 3), + visitor_.max_stream_id_frame_.max_stream_id); + + CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA); +} + +TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // Test runs in client mode, no connection id + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL) + {"", + {0x13}}, + // max. streams + {"Can not read MAX_STREAMS stream count.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a client receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a client-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) | 0x02= 0xa + // count-to-id client/unidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/false, 3), + visitor_.max_stream_id_frame_.max_stream_id); + + CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA); +} + +// The following four tests ensure that the framer can deserialize a stream +// count that is large enough to cause the resulting stream ID to exceed the +// current implementation limit(32 bits). The intent is that when this happens, +// the stream limit is pegged to the maximum supported value. There are four +// tests, for the four combinations of uni- and bi-directional, server- and +// client- initiated. +TEST_P(QuicFramerTest, ServerBiDiMaxStreamsFrameTooBig) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x9A, 0xBC, + // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL) + 0x12, + + // max. streams. Max stream ID allowed is 0xffffffff + // This encodes a count of 0x40000000, leading to stream + // IDs in the range 0x1 00000000 to 0x1 00000003. + kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00 + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a server receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a server-initiated + // stream ID. The expected Stream ID is + // 0xfffffffc | 0x01 --> 0xfffffffd + // maxid server inited, bidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/true, 0x40000000), + visitor_.max_stream_id_frame_.max_stream_id); +} + +TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrameTooBig) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // Test runs in client mode, no connection id + // packet number + 0x12, 0x34, 0x9A, 0xBC, + // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL) + 0x12, + + // max. streams. Max stream ID allowed is 0xffffffff + // This encodes a count of 0x40000000, leading to stream + // IDs in the range 0x1 00000000 to 0x1 00000003. + kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00 + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99), + false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a client receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a client-initiated + // stream ID. The expected Stream ID is + // 0xfffffffc --> 0xfffffffc + // max id bidi/client-inited + // TODO(fkastenholz): Change -2 to -1 when stream id 0 is no longer + // special. + // Subtract 1 because client/bidi stream ids start counting at + // 4, not 0. If we didn;t subtract 1, the resulting math would wrap to stream + // id 0, not 0xfffffffc. + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/true, (0x40000000 - 1)), + visitor_.max_stream_id_frame_.max_stream_id); +} + +TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrameTooBig) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x9A, 0xBC, + // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL) + 0x13, + + // max. streams. Max stream ID allowed is 0xffffffff + // This encodes a count of 0x40000000, leading to stream + // IDs in the range 0x1 00000000 to 0x1 00000003. + kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00 + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a server receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a server-initiated + // stream ID. The expected Stream ID is + // 0xfffffffc | 0x1 | 0x2 = 0xffffffff + // maxid server inited, unidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/false, 0x40000000), + visitor_.max_stream_id_frame_.max_stream_id); +} + +TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrameTooBig) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // Test runs in client mode, no connection id + // packet number + 0x12, 0x34, 0x9A, 0xBC, + // frame type (IETF_MAX_STREAMS_UNDIRECTIONAL) + 0x13, + + // max. streams. Max stream ID allowed is 0xffffffff + // This encodes a count of 0x40000000, leading to stream + // IDs in the range 0x1 00000000 to 0x1 00000003. + kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00 + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99), + false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a client receiving a MAX_STREAMS frame. The + // stream ID that it generates should be a client-initiated + // stream ID. The expected Stream ID is + // 0xfffffffc | 0x02= 0xfffffffe + // maxid client/unidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/false, 0x40000000), + visitor_.max_stream_id_frame_.max_stream_id); +} + +// Check that a stream count of 0 is rejected. +// Directionality and intiation are not important for +// this test. +TEST_P(QuicFramerTest, MaxStreamsFrameZeroCount) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x9A, 0xBC, + // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL) + 0x12, + // max. streams == 0. + kVarInt62OneByte + 0x00 + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99), + false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_MAX_STREAM_ID_DATA, framer_.error()); + EXPECT_EQ(framer_.detailed_error(), + "MAX_STREAMS stream count of 0 not supported."); +} + +TEST_P(QuicFramerTest, ServerBiDiStreamsBlockedFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame) + {"", + {0x16}}, + // stream id + {"Can not read STREAMS_BLOCKED stream id.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a server receiving a STREAMS_BLOCKED frame. The + // stream ID that it generates should be a client-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) = 0xc + // count-to-id client inited, bidi + // It is not 8 because a client-initiated, bidi stream ID's + // low bits are 00 - which means that the old crypto stream + // falls into this category, and the first stream is streamid=4, + // not streamid=0. + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/true, 3), + visitor_.stream_id_blocked_frame_.stream_id); + + CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA); +} + +TEST_P(QuicFramerTest, ClientBiDiStreamsBlockedFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // Test runs in client mode, no connection id + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame) + {"", + {0x16}}, + // stream id + {"Can not read STREAMS_BLOCKED stream id.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a client receiving a STREAMS_BLOCKED frame. The + // stream ID that it generates should be a server-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) | 0x01 = 0x9 + // count-to-id server inited, bidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/true, 3), + visitor_.stream_id_blocked_frame_.stream_id); + + CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA); +} + +TEST_P(QuicFramerTest, ServerUniDiStreamsBlockedFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame) + {"", + {0x17}}, + // stream id + {"Can not read STREAMS_BLOCKED stream id.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a server receiving a STREAMS_BLOCKED frame. The + // stream ID that it generates should be a client-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) | 0x2 = 0xa + // count-to-id client inited, unidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/false, 3), + visitor_.stream_id_blocked_frame_.stream_id); + + CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA); +} + +TEST_P(QuicFramerTest, ClientUniDiStreamsBlockedFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // Test runs in client mode, no connection id + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame) + {"", + {0x17}}, + // stream id + {"Can not read STREAMS_BLOCKED stream id.", + {kVarInt62OneByte + 0x03}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + // This test is a client receiving a STREAMS_BLOCKED frame. The + // stream ID that it generates should be a server-initiated + // stream ID. The expected Stream ID is + // ((0x3-1) * 4) | 0x01 | 0x2 = 0xb + // count-to-id server inited, bidi + EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/false, 3), + visitor_.stream_id_blocked_frame_.stream_id); + + CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA); +} + +// Check that when we get a STREAMS_BLOCKED frame that specifies too large +// a stream count, we reject with an appropriate error. There is no need to +// check for different combinations of Uni/Bi directional and client/server +// initiated; the logic does not take these into account. +TEST_P(QuicFramerTest, StreamsBlockedFrameTooBig) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // Test runs in client mode, no connection id + // packet number + 0x12, 0x34, 0x9A, 0xBC, + // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL) + 0x17, + + // max. streams. Max stream ID allowed is 0xffffffff + // This encodes a count of 0x40000000, leading to stream + // IDs in the range 0x1 00000000 to 0x1 00000003. + kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01 + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99), + false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_STREAM_ID_BLOCKED_DATA, framer_.error()); + EXPECT_EQ(framer_.detailed_error(), + "STREAMS_BLOCKED stream count exceeds implementation limit."); +} + +// Test that count==0 is rejected. +TEST_P(QuicFramerTest, StreamsBlockedFrameZeroCount) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // Test runs in client mode, no connection id + // packet number + 0x12, 0x34, 0x9A, 0xBC, + // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL) + 0x17, + + // max. streams = 0 + kVarInt62OneByte + 0x00 + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99), + false); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_STREAM_ID_BLOCKED_DATA, framer_.error()); + EXPECT_EQ(framer_.detailed_error(), + "STREAMS_BLOCKED stream count 0 not supported."); +} + +TEST_P(QuicFramerTest, BuildServerBiDiStreamsBlockedPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicStreamIdBlockedFrame frame; + // A server building a STREAMS_BLOCKED frame generates + // a server-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 01 + // Expected value is 0x8u | 0x1u; + frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/true, 3); + + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame) + 0x16, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildClientBiDiStreamsBlockedPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // This test runs in client mode. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicStreamIdBlockedFrame frame; + // A client building a STREAMS_BLOCKED frame generates + // a client-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 00. Expected value is 0x8 + frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/true, 3); + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame) + 0x16, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildServerUniStreamsBlockedPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicStreamIdBlockedFrame frame; + // A server building a STREAMS_BLOCKED frame generates + // a server-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 11. Expected value is 0xb + frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/false, 3); + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame) + 0x17, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildClientUniDiStreamsBlockedPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // This test runs in client mode. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicStreamIdBlockedFrame frame; + // A client building a STREAMS_BLOCKED frame generates + // a client-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 10. Expected value is 0xa + frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/false, 3); + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame) + 0x17, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildServerBiDiMaxStreamsPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicMaxStreamIdFrame frame; + // A server building a MAX_STREAMS frame generates + // a client-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 00. Expected value is 0xc + // because streamid==0 is special and the first client/bidi + // stream is 4, not 0. + frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/true, 3); + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL frame) + 0x12, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildClientBiDiMaxStreamsPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // This test runs in client mode. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicMaxStreamIdFrame frame; + // A client building a MAX_STREAMS frame generates + // a server-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 01. Expected value is 0x9 + frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/true, 3); + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL frame) + 0x12, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildServerUniMaxStreamsPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicMaxStreamIdFrame frame; + // A server building a MAX_STREAMS frame generates + // a client-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 10. Expected value is 0xa + frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT, + /*bidirectional=*/false, 3); + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL frame) + 0x13, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, BuildClientUniDiMaxStreamsPacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // This test runs in client mode. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicMaxStreamIdFrame frame; + // A client building a MAX_STREAMS frame generates + // a server-initiated stream ID. This test is bidirectional. + // The low two bits of the stream ID are 11. Expected value is 0xb + frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER, + /*bidirectional=*/false, 3); + QuicFrames frames = {QuicFrame(frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL frame) + 0x13, + // Stream count + kVarInt62OneByte + 0x03 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, NewConnectionIdFrame) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is only for version 99. + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_NEW_CONNECTION_ID frame) + {"", + {0x18}}, + // error code + {"Unable to read new connection ID frame sequence number.", + {kVarInt62OneByte + 0x11}}, + {"Unable to read new connection ID frame connection id length.", + {0x08}}, // connection ID length + {"Unable to read new connection ID frame connection id.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11}}, + {"Can not read new connection ID frame reset token.", + {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(FramerTestConnectionIdPlusOne(), + visitor_.new_connection_id_.connection_id); + EXPECT_EQ(0x11u, visitor_.new_connection_id_.sequence_number); + EXPECT_EQ(kTestStatelessResetToken, + visitor_.new_connection_id_.stateless_reset_token); + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA); +} + +TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is only for version 99. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicNewConnectionIdFrame frame; + frame.sequence_number = 0x11; + // Use this value to force a 4-byte encoded variable length connection ID + // in the frame. + frame.connection_id = FramerTestConnectionIdPlusOne(); + frame.stateless_reset_token = kTestStatelessResetToken; + + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_NEW_CONNECTION_ID frame) + 0x18, + // sequence number + kVarInt62OneByte + 0x11, + // new connection id length + 0x08, + // new connection id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, + // stateless reset token + 0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, NewTokenFrame) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is only for version 99. + return; + } + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_NEW_TOKEN frame) + {"", + {0x07}}, + // Length + {"Unable to read new token length.", + {kVarInt62OneByte + 0x08}}, + {"Unable to read new token data.", + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}} + }; + // clang-format on + uint8_t expected_token_value[] = {0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07}; + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(sizeof(expected_token_value), visitor_.new_token_.token.length()); + EXPECT_EQ(0, memcmp(expected_token_value, visitor_.new_token_.token.data(), + sizeof(expected_token_value))); + + CheckFramingBoundaries(packet, QUIC_INVALID_NEW_TOKEN); +} + +TEST_P(QuicFramerTest, BuildNewTokenFramePacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is only for version 99. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + uint8_t expected_token_value[] = {0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07}; + + QuicNewTokenFrame frame(0, QuicString((const char*)(expected_token_value), + sizeof(expected_token_value))); + + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_NEW_TOKEN frame) + 0x07, + // Length and token + kVarInt62OneByte + 0x08, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicFramerTest, IetfStopSendingFrame) { + // This test is only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_STOP_SENDING frame) + {"", + {0x05}}, + // stream id + {"Unable to read stop sending stream id.", + {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}}, + {"Unable to read stop sending application error code.", + {0x76, 0x54}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(kStreamId, visitor_.stop_sending_frame_.stream_id); + EXPECT_EQ(0x7654, visitor_.stop_sending_frame_.application_error_code); + + CheckFramingBoundaries(packet99, QUIC_INVALID_STOP_SENDING_FRAME_DATA); +} + +TEST_P(QuicFramerTest, BuildIetfStopSendingPacket) { + // This test is only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicStopSendingFrame frame; + frame.stream_id = kStreamId; + frame.application_error_code = 0xffff; + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_STOP_SENDING frame) + 0x05, + // Stream ID + kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04, + // Application error code + 0xff, 0xff + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, IetfPathChallengeFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_PATH_CHALLENGE) + {"", + {0x1a}}, + // data + {"Can not read path challenge data.", + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}), + visitor_.path_challenge_frame_.data_buffer); + + CheckFramingBoundaries(packet99, QUIC_INVALID_PATH_CHALLENGE_DATA); +} + +TEST_P(QuicFramerTest, BuildIetfPathChallengePacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicPathChallengeFrame frame; + frame.data_buffer = QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}); + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_PATH_CHALLENGE) + 0x1a, + // Data + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, IetfPathResponseFrame) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (IETF_PATH_RESPONSE) + {"", + {0x1b}}, + // data + {"Can not read path response data.", + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}), + visitor_.path_response_frame_.data_buffer); + + CheckFramingBoundaries(packet99, QUIC_INVALID_PATH_RESPONSE_DATA); +} + +TEST_P(QuicFramerTest, BuildIetfPathResponsePacket) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicPathResponseFrame frame; + frame.data_buffer = QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}); + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_PATH_RESPONSE) + 0x1b, + // Data + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, GetRetransmittableControlFrameSize) { + QuicRstStreamFrame rst_stream(1, 3, QUIC_STREAM_CANCELLED, 1024); + EXPECT_EQ(QuicFramer::GetRstStreamFrameSize(framer_.transport_version(), + rst_stream), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&rst_stream))); + + QuicString error_detail(2048, 'e'); + QuicConnectionCloseFrame connection_close(QUIC_NETWORK_IDLE_TIMEOUT, + error_detail); + EXPECT_EQ(QuicFramer::GetMinConnectionCloseFrameSize( + framer_.transport_version(), connection_close) + + 256, + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&connection_close))); + + QuicGoAwayFrame goaway(2, QUIC_PEER_GOING_AWAY, 3, error_detail); + EXPECT_EQ(QuicFramer::GetMinGoAwayFrameSize() + 256, + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&goaway))); + + QuicWindowUpdateFrame window_update(3, 3, 1024); + EXPECT_EQ(QuicFramer::GetWindowUpdateFrameSize(framer_.transport_version(), + window_update), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&window_update))); + + QuicBlockedFrame blocked(4, 3, 1024); + EXPECT_EQ( + QuicFramer::GetBlockedFrameSize(framer_.transport_version(), blocked), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&blocked))); + + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + QuicApplicationCloseFrame application_close; + EXPECT_EQ(QuicFramer::GetMinApplicationCloseFrameSize( + framer_.transport_version(), application_close), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&application_close))); + + QuicNewConnectionIdFrame new_connection_id(5, TestConnectionId(), 1, 101111); + EXPECT_EQ(QuicFramer::GetNewConnectionIdFrameSize(new_connection_id), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&new_connection_id))); + + QuicMaxStreamIdFrame max_stream_id(6, 3); + EXPECT_EQ(QuicFramer::GetMaxStreamsFrameSize(framer_.transport_version(), + max_stream_id), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(max_stream_id))); + + QuicStreamIdBlockedFrame stream_id_blocked(7, 3); + EXPECT_EQ(QuicFramer::GetStreamsBlockedFrameSize(framer_.transport_version(), + stream_id_blocked), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(stream_id_blocked))); + + QuicPathFrameBuffer buffer = { + {0x80, 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe5, 0xf7}}; + QuicPathResponseFrame path_response_frame(8, buffer); + EXPECT_EQ(QuicFramer::GetPathResponseFrameSize(path_response_frame), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&path_response_frame))); + + QuicPathChallengeFrame path_challenge_frame(9, buffer); + EXPECT_EQ(QuicFramer::GetPathChallengeFrameSize(path_challenge_frame), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&path_challenge_frame))); + + QuicStopSendingFrame stop_sending_frame(10, 3, 20); + EXPECT_EQ(QuicFramer::GetStopSendingFrameSize(stop_sending_frame), + QuicFramer::GetRetransmittableControlFrameSize( + framer_.transport_version(), QuicFrame(&stop_sending_frame))); +} + +// A set of tests to ensure that bad frame-type encodings +// are properly detected and handled. +// First, four tests to see that unknown frame types generate +// a QUIC_INVALID_FRAME_DATA error with detailed information +// "Illegal frame type." This regardless of the encoding of the type +// (1/2/4/8 bytes). +// This only for version 99. +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown1Byte) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (unknown value, single-byte encoding) + {"", + {0x38}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); + EXPECT_EQ("Illegal frame type.", framer_.detailed_error()); +} + +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown2Bytes) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (unknown value, two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x01, 0x38}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); + EXPECT_EQ("Illegal frame type.", framer_.detailed_error()); +} + +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown4Bytes) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (unknown value, four-byte encoding) + {"", + {kVarInt62FourBytes + 0x01, 0x00, 0x00, 0x38}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); + EXPECT_EQ("Illegal frame type.", framer_.detailed_error()); +} + +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown8Bytes) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (unknown value, eight-byte encoding) + {"", + {kVarInt62EightBytes + 0x01, 0x00, 0x00, 0x01, 0x02, 0x34, 0x56, 0x38}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); + EXPECT_EQ("Illegal frame type.", framer_.detailed_error()); +} + +// Three tests to check that known frame types that are not minimally +// encoded generate IETF_QUIC_PROTOCOL_VIOLATION errors with detailed +// information "Frame type not minimally encoded." +// Look at the frame-type encoded in 2, 4, and 8 bytes. +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown2Bytes) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (Blocked, two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x08}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error()); + EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error()); +} + +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown4Bytes) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (Blocked, four-byte encoding) + {"", + {kVarInt62FourBytes + 0x00, 0x00, 0x00, 0x08}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error()); + EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error()); +} + +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown8Bytes) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + // clang-format off + PacketFragments packet = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (Blocked, eight-byte encoding) + {"", + {kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error()); + EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error()); +} + +// Tests to check that all known OETF frame types that are not minimally +// encoded generate IETF_QUIC_PROTOCOL_VIOLATION errors with detailed +// information "Frame type not minimally encoded." +// Just look at 2-byte encoding. +TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown2BytesAllTypes) { + // This test only for version 99. + if (framer_.transport_version() != QUIC_VERSION_99) { + return; + } + + // clang-format off + PacketFragments packets[] = { + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x00}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x01}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x02}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x03}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x04}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x05}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x06}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x07}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x08}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x09}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x0a}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x0b}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x0c}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x0d}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x0e}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x0f}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x10}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x11}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x12}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x13}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x14}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x15}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x16}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x17}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x18}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x20}} + }, + { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x9A, 0xBC}}, + // frame type (two-byte encoding) + {"", + {kVarInt62TwoBytes + 0x00, 0x21}} + }, + }; + // clang-format on + + for (PacketFragments& packet : packets) { + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error()); + EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error()); + } +} + +TEST_P(QuicFramerTest, RetireConnectionIdFrame) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is only for version 99. + return; + } + // clang-format off + PacketFragments packet99 = { + // type (short header, 4 byte packet number) + {"", + {0x43}}, + // connection_id + {"", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}}, + // packet number + {"", + {0x12, 0x34, 0x56, 0x78}}, + // frame type (IETF_RETIRE_CONNECTION_ID frame) + {"", + {0x19}}, + // Sequence number + {"Unable to read retire connection ID frame sequence number.", + {kVarInt62TwoBytes + 0x11, 0x22}} + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet99)); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(0x1122u, visitor_.retire_connection_id_.sequence_number); + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + CheckFramingBoundaries(packet99, QUIC_INVALID_RETIRE_CONNECTION_ID_DATA); +} + +TEST_P(QuicFramerTest, BuildRetireConnectionIdFramePacket) { + if (framer_.transport_version() != QUIC_VERSION_99) { + // This frame is only for version 99. + return; + } + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicRetireConnectionIdFrame frame; + frame.sequence_number = 0x1122; + + QuicFrames frames = {QuicFrame(&frame)}; + + // clang-format off + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_RETIRE_CONNECTION_ID frame) + 0x19, + // sequence number + kVarInt62TwoBytes + 0x11, 0x22 + }; + // clang-format on + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet99), + QUIC_ARRAYSIZE(packet99)); +} + +TEST_P(QuicFramerTest, AckFrameWithInvalidLargestObserved) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x2C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x45, + // largest observed + 0x00, 0x00, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x00, + // num timestamps. + 0x00 + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x45, + // largest observed + 0x00, 0x00, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x00, + // num timestamps. + 0x00 + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x45, + // largest observed + 0x00, 0x00, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x00, + // num timestamps. + 0x00 + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_ACK frame) + 0x02, + // Largest acked + kVarInt62OneByte + 0x00, + // Zero delta time. + kVarInt62OneByte + 0x00, + // Ack block count 0 + kVarInt62OneByte + 0x00, + // First ack block length + kVarInt62OneByte + 0x00, + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(framer_.detailed_error(), "Largest acked is 0."); +} + +TEST_P(QuicFramerTest, FirstAckBlockJustUnderFlow) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x2C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x45, + // largest observed + 0x00, 0x02, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x03, + // num timestamps. + 0x00 + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x45, + // largest observed + 0x00, 0x02, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x03, + // num timestamps. + 0x00 + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x45, + // largest observed + 0x00, 0x02, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x03, + // num timestamps. + 0x00 + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_ACK frame) + 0x02, + // Largest acked + kVarInt62OneByte + 0x02, + // Zero delta time. + kVarInt62OneByte + 0x00, + // Ack block count 0 + kVarInt62OneByte + 0x00, + // First ack block length + kVarInt62OneByte + 0x02, + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(framer_.detailed_error(), + "Underflow with first ack block length 3 largest acked is 2."); +} + +TEST_P(QuicFramerTest, ThirdAckBlockJustUnderflow) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x2C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x60, + // largest observed + 0x0A, + // Zero delta time. + 0x00, 0x00, + // Num of ack blocks + 0x02, + // first ack block length. + 0x02, + // gap to next block + 0x01, + // ack block length + 0x01, + // gap to next block + 0x01, + // ack block length + 0x06, + // num timestamps. + 0x00 + }; + + unsigned char packet44[] = { + // type (short header, 4 byte packet number) + 0x32, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x60, + // largest observed + 0x0A, + // Zero delta time. + 0x00, 0x00, + // Num of ack blocks + 0x02, + // first ack block length. + 0x02, + // gap to next block + 0x01, + // ack block length + 0x01, + // gap to next block + 0x01, + // ack block length + 0x06, + // num timestamps. + 0x00 + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ack frame) + 0x60, + // largest observed + 0x0A, + // Zero delta time. + 0x00, 0x00, + // Num of ack blocks + 0x02, + // first ack block length. + 0x02, + // gap to next block + 0x01, + // ack block length + 0x01, + // gap to next block + 0x01, + // ack block length + 0x06, + // num timestamps. + 0x00 + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_ACK frame) + 0x02, + // Largest acked + kVarInt62OneByte + 0x0A, + // Zero delta time. + kVarInt62OneByte + 0x00, + // Ack block count 2 + kVarInt62OneByte + 0x02, + // First ack block length + kVarInt62OneByte + 0x01, + // gap to next block length + kVarInt62OneByte + 0x00, + // ack block length + kVarInt62OneByte + 0x00, + // gap to next block length + kVarInt62OneByte + 0x00, + // ack block length + kVarInt62OneByte + 0x05, + }; + // clang-format on + + unsigned char* p = packet; + size_t p_size = QUIC_ARRAYSIZE(packet); + if (framer_.transport_version() == QUIC_VERSION_99) { + p = packet99; + p_size = QUIC_ARRAYSIZE(packet99); + } else if (framer_.transport_version() > QUIC_VERSION_44) { + p = packet46; + p_size = QUIC_ARRAYSIZE(packet46); + } else if (framer_.transport_version() > QUIC_VERSION_43) { + p = packet44; + p_size = QUIC_ARRAYSIZE(packet44); + } + + QuicEncryptedPacket encrypted(AsChars(p), p_size, false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + if (framer_.transport_version() == QUIC_VERSION_99) { + EXPECT_EQ(framer_.detailed_error(), + "Underflow with ack block length 6 latest ack block end is 5."); + } else { + EXPECT_EQ(framer_.detailed_error(), + "Underflow with ack block length 6, end of block is 6."); + } +} + +TEST_P(QuicFramerTest, CoalescedPacket) { + if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) { + return; + } + // clang-format off + unsigned char packet[] = { + // first coalesced packet + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x50, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x1E, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // second coalesced packet + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x50, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x1E, + // packet number + 0x12, 0x34, 0x56, 0x79, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'H', 'E', 'L', 'L', + 'O', '_', 'W', 'O', + 'R', 'L', 'D', '?', + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + + // Stream ID should be the last 3 bytes of kStreamId. + EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + ASSERT_EQ(visitor_.coalesced_packets_.size(), 1u); + EXPECT_TRUE(framer_.ProcessPacket(*visitor_.coalesced_packets_[0].get())); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + ASSERT_EQ(2u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + + // Stream ID should be the last 3 bytes of kStreamId. + EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[1]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[1]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[1]->offset); + CheckStreamFrameData("HELLO_WORLD?", visitor_.stream_frames_[1].get()); +} + +TEST_P(QuicFramerTest, MismatchedCoalescedPacket) { + if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) { + return; + } + // clang-format off + unsigned char packet[] = { + // first coalesced packet + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x50, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x1E, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // second coalesced packet + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x50, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, + // long header packet length + 0x1E, + // packet number + 0x12, 0x34, 0x56, 0x79, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'H', 'E', 'L', 'L', + 'O', '_', 'W', 'O', + 'R', 'L', 'D', '?', + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)), + "Server: Received mismatched coalesced header.*"); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + + // Stream ID should be the last 3 bytes of kStreamId. + EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + ASSERT_EQ(visitor_.coalesced_packets_.size(), 0u); +} + +TEST_P(QuicFramerTest, InvalidCoalescedPacket) { + if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) { + return; + } + // clang-format off + unsigned char packet[] = { + // first coalesced packet + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x50, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // long header packet length + 0x1E, + // packet number + 0x12, 0x34, 0x56, 0x78, + // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set) + 0x08 | 0x01 | 0x02 | 0x04, + // stream id + kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04, + // offset + kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + kVarInt62OneByte + 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // second coalesced packet + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version would be here but we cut off the invalid coalesced header. + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false); + EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)), + "Server: Failed to parse received coalesced header.*"); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + + // Stream ID should be the last 3 bytes of kStreamId. + EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset); + CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get()); + + ASSERT_EQ(visitor_.coalesced_packets_.size(), 0u); +} + +TEST_P(QuicFramerTest, PacketHeaderWithVariableLengthConnectionId) { + if (framer_.transport_version() < QUIC_VERSION_46) { + return; + } + char connection_id_bytes[9] = {0xFE, 0xDC, 0xBA, 0x98, 0x76, + 0x54, 0x32, 0x10, 0x42}; + QuicConnectionId connection_id(connection_id_bytes, + sizeof(connection_id_bytes)); + QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2); + QuicFramerPeer::SetExpectedConnectionIDLength(&framer_, + connection_id.length()); + + // clang-format off + PacketFragments packet = { + // type (8 byte connection_id and 1 byte packet number) + {"Unable to read type.", + {0x40}}, + // connection_id + {"Unable to read Destination ConnectionId.", + {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}}, + // packet number + {"Unable to read packet number.", + {0x78}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet)); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(connection_id, visitor_.header_->destination_connection_id); + EXPECT_FALSE(visitor_.header_->reset_flag); + EXPECT_FALSE(visitor_.header_->version_flag); + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length); + EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); + + CheckFramingBoundaries(packet, QUIC_INVALID_PACKET_HEADER); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_ietf_framer_test.cc b/quic/core/quic_ietf_framer_test.cc new file mode 100644 index 0000000..a959791 --- /dev/null +++ b/quic/core/quic_ietf_framer_test.cc
@@ -0,0 +1,1481 @@ +// Copyright (c) 2018 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. + +// gunit tests for the IETF-format framer --- generally does a simple test +// for each framer; we generate the template object (eg +// QuicIetfStreamFrame) with the correct stuff in it, ask that a frame +// be serialized (call AppendIetf<mumble>) then deserialized (call +// ProcessIetf<mumble>) and then check that the gazintas and gazoutas +// are the same. +// +// We do minimal checking of the serialized frame +// +// We do look at various different values (resulting in different +// length varints, etc) + +#include "net/third_party/quiche/src/quic/core/quic_framer.h" + +#include <algorithm> +#include <cstdint> +#include <map> +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.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_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h" + +namespace quic { +namespace test { +namespace { + +const size_t kNormalPacketBufferSize = 1400; +// Several different stream ids, should be encoded +// in 8, 4, 2, and 1 byte, respectively. Last one +// checks that value==0 works. +// All stream IDs end in 0x0, so the client/server- initiated +// and Uni/Bi-directional bits are available to alter, as any +// given test may wish. +const QuicIetfStreamId kStreamId8 = UINT64_C(0x3EDCBA9876543210); +const QuicIetfStreamId kStreamId4 = UINT64_C(0x36543210); +const QuicIetfStreamId kStreamId2 = UINT64_C(0x3210); +const QuicIetfStreamId kStreamId1 = UINT64_C(0x10); +const QuicIetfStreamId kStreamId0 = UINT64_C(0x00); + +// Ditto for the offsets. +const QuicIetfStreamOffset kOffset8 = UINT64_C(0x3210BA9876543210); +const QuicIetfStreamOffset kOffset4 = UINT64_C(0x32109876); +const QuicIetfStreamOffset kOffset2 = UINT64_C(0x3456); +const QuicIetfStreamOffset kOffset1 = UINT64_C(0x3f); +const QuicIetfStreamOffset kOffset0 = UINT64_C(0x00); + +// Structures used to create various ack frames. + +// Defines an ack frame to feed through the framer/deframer. +struct ack_frame { + uint64_t delay_time; + bool is_ack_ecn; + QuicPacketCount ect_0_count; + QuicPacketCount ect_1_count; + QuicPacketCount ecn_ce_count; + const std::vector<QuicAckBlock>& ranges; + uint64_t expected_frame_type; +}; + +class TestQuicVisitor : public QuicFramerVisitorInterface { + public: + TestQuicVisitor() {} + + ~TestQuicVisitor() override {} + + void OnError(QuicFramer* f) override { + QUIC_DLOG(INFO) << "QuicIetfFramer Error: " + << QuicErrorCodeToString(f->error()) << " (" << f->error() + << ")"; + } + + void OnPacket() override {} + + void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {} + + void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) override {} + + bool OnProtocolVersionMismatch(ParsedQuicVersion received_version, + PacketHeaderFormat form) override { + return true; + } + + bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override { + return true; + } + + 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 {} + + bool OnStreamFrame(const QuicStreamFrame& frame) override { return true; } + + bool OnCryptoFrame(const QuicCryptoFrame& frame) override { return true; } + + 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 OnPaddingFrame(const QuicPaddingFrame& frame) override { return true; } + + bool OnPingFrame(const QuicPingFrame& frame) override { return true; } + + bool OnMessageFrame(const QuicMessageFrame& frame) override { return true; } + + void OnPacketComplete() override {} + + bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override { + return true; + } + + bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override { + return true; + } + + bool OnApplicationCloseFrame( + const QuicApplicationCloseFrame& 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 OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override { + return true; + } + + bool OnBlockedFrame(const QuicBlockedFrame& 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 IsValidStatelessResetToken(QuicUint128 token) const override { + return true; + } + + void OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) override {} + + bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override { + return true; + } + + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override { + return true; + } +}; + +class QuicIetfFramerTest : public QuicTestWithParam<ParsedQuicVersion> { + public: + QuicIetfFramerTest() + : start_(QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(0x10)), + framer_(AllSupportedVersions(), + start_, + Perspective::IS_SERVER, + kQuicDefaultConnectionIdLength) { + framer_.set_visitor(&visitor_); + } + + // Utility functions to do actual framing/deframing. + void TryStreamFrame(char* packet_buffer, + size_t packet_buffer_size, + const char* xmit_packet_data, + size_t xmit_packet_data_size, + QuicIetfStreamId stream_id, + QuicIetfStreamOffset offset, + bool fin_bit, + bool last_frame_bit, + QuicIetfFrameType frame_type) { + // initialize a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(packet_buffer_size, packet_buffer, + NETWORK_BYTE_ORDER); // do not really care + // about endianness. + // set up to define the source frame we wish to send. + QuicStreamFrame source_stream_frame( + stream_id, fin_bit, offset, xmit_packet_data, xmit_packet_data_size); + + // Write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendIetfStreamFrame( + &framer_, source_stream_frame, last_frame_bit, &writer)); + // Better have something in the packet buffer. + EXPECT_NE(0u, writer.length()); + // Now set up a reader to read in the frame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + // A StreamFrame to hold the results... we know the frame type, + // put it into the QuicIetfStreamFrame + QuicStreamFrame sink_stream_frame; + if (xmit_packet_data_size) { + EXPECT_EQ(sink_stream_frame.data_buffer, nullptr); + EXPECT_EQ(sink_stream_frame.data_length, 0u); + } + + EXPECT_TRUE(QuicFramerPeer::ProcessIetfStreamFrame( + &framer_, &reader, frame_type, &sink_stream_frame)); + + // Now check that the streamid, fin-bit, offset, and + // data len all match the input. + EXPECT_EQ(sink_stream_frame.stream_id, source_stream_frame.stream_id); + EXPECT_EQ(sink_stream_frame.fin, source_stream_frame.fin); + EXPECT_EQ(sink_stream_frame.data_length, source_stream_frame.data_length); + if (frame_type & IETF_STREAM_FRAME_OFF_BIT) { + // There was an offset in the frame, see if xmit and rcv vales equal. + EXPECT_EQ(sink_stream_frame.offset, source_stream_frame.offset); + } else { + // Offset not in frame, so it better come out 0. + EXPECT_EQ(sink_stream_frame.offset, 0u); + } + if (xmit_packet_data_size) { + ASSERT_NE(sink_stream_frame.data_buffer, nullptr); + ASSERT_NE(source_stream_frame.data_buffer, nullptr); + EXPECT_EQ(strcmp(sink_stream_frame.data_buffer, + source_stream_frame.data_buffer), + 0); + } else { + // No data in the frame. + EXPECT_EQ(source_stream_frame.data_length, 0u); + EXPECT_EQ(sink_stream_frame.data_length, 0u); + } + } + + // Overall ack frame encode/decode/compare function + // Encodes an ack frame as specified at |*frame| + // Then decodes the frame, + // Then compares the two + // Does some basic checking: + // - did the writer write something? + // - did the reader read the entire packet? + // - did the things the reader read match what the writer wrote? + // Returns true if it all worked false if not. + bool TryAckFrame(char* packet_buffer, + size_t packet_buffer_size, + struct ack_frame* frame) { + QuicAckFrame transmit_frame = InitAckFrame(frame->ranges); + if (frame->is_ack_ecn) { + transmit_frame.ecn_counters_populated = true; + transmit_frame.ect_0_count = frame->ect_0_count; + transmit_frame.ect_1_count = frame->ect_1_count; + transmit_frame.ecn_ce_count = frame->ecn_ce_count; + } + transmit_frame.ack_delay_time = + QuicTime::Delta::FromMicroseconds(frame->delay_time); + size_t expected_size = + QuicFramerPeer::GetIetfAckFrameSize(&framer_, transmit_frame); + + // Make a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(expected_size, packet_buffer, NETWORK_BYTE_ORDER); + + // Write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendIetfAckFrameAndTypeByte( + &framer_, transmit_frame, &writer)); + + size_t expected_frame_length = QuicFramerPeer::ComputeFrameLength( + &framer_, QuicFrame(&transmit_frame), false, + static_cast<QuicPacketNumberLength>(123456u)); + + // Encoded length should match what ComputeFrameLength returns + EXPECT_EQ(expected_frame_length, writer.length()); + // and what is in the buffer should be the expected size. + EXPECT_EQ(expected_size, writer.length()) << "Frame is " << transmit_frame; + // Now set up a reader to read in the frame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + // read in the frame type + uint8_t received_frame_type; + EXPECT_TRUE(reader.ReadUInt8(&received_frame_type)); + EXPECT_EQ(frame->expected_frame_type, received_frame_type); + + // an AckFrame to hold the results + QuicAckFrame receive_frame; + + EXPECT_TRUE(QuicFramerPeer::ProcessIetfAckFrame( + &framer_, &reader, received_frame_type, &receive_frame)); + + if (frame->is_ack_ecn && + (frame->ect_0_count || frame->ect_1_count || frame->ecn_ce_count)) { + EXPECT_TRUE(receive_frame.ecn_counters_populated); + EXPECT_EQ(receive_frame.ect_0_count, frame->ect_0_count); + EXPECT_EQ(receive_frame.ect_1_count, frame->ect_1_count); + EXPECT_EQ(receive_frame.ecn_ce_count, frame->ecn_ce_count); + } else { + EXPECT_FALSE(receive_frame.ecn_counters_populated); + EXPECT_EQ(receive_frame.ect_0_count, 0u); + EXPECT_EQ(receive_frame.ect_1_count, 0u); + EXPECT_EQ(receive_frame.ecn_ce_count, 0u); + } + + // Now check that the received frame matches the sent frame. + EXPECT_EQ(transmit_frame.largest_acked, receive_frame.largest_acked); + // The ~0x7 needs some explaining. The ack frame format down shifts the + // delay time by 3 (divide by 8) to allow for greater ranges in delay time. + // Therefore, if we give the framer a delay time that is not an + // even multiple of 8, the value that the deframer produces will + // not be the same as what the framer got. The downshift on + // framing and upshift on deframing results in clearing the 3 + // low-order bits ... The masking basically does the same thing, + // so the compare works properly. + return true; + } + + // encode, decode, and check a Path Challenge frame. + bool TryPathChallengeFrame(char* packet_buffer, + size_t packet_buffer_size, + const QuicPathFrameBuffer& data) { + // Make a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(packet_buffer_size, packet_buffer, + NETWORK_BYTE_ORDER); + + QuicPathChallengeFrame transmit_frame(0, data); + + // write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendPathChallengeFrame( + &framer_, transmit_frame, &writer)); + + // Check for correct length in the packet buffer. + EXPECT_EQ(kQuicPathChallengeFrameSize, writer.length()); + + // now set up a reader to read in the frame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + QuicPathChallengeFrame receive_frame; + + EXPECT_TRUE(QuicFramerPeer::ProcessPathChallengeFrame(&framer_, &reader, + &receive_frame)); + + // Now check that the received frame matches the sent frame. + EXPECT_EQ( + 0, memcmp(transmit_frame.data_buffer.data(), + receive_frame.data_buffer.data(), kQuicPathFrameBufferSize)); + return true; + } + + // encode, decode, and check a Path Response frame. + bool TryPathResponseFrame(char* packet_buffer, + size_t packet_buffer_size, + const QuicPathFrameBuffer& data) { + // Make a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(packet_buffer_size, packet_buffer, + NETWORK_BYTE_ORDER); + + QuicPathResponseFrame transmit_frame(0, data); + + // Write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendPathResponseFrame( + &framer_, transmit_frame, &writer)); + + // Check for correct length in the packet buffer. + EXPECT_EQ(kQuicPathResponseFrameSize, writer.length()); + + // Set up a reader to read in the frame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + QuicPathResponseFrame receive_frame; + + EXPECT_TRUE(QuicFramerPeer::ProcessPathResponseFrame(&framer_, &reader, + &receive_frame)); + + // Now check that the received frame matches the sent frame. + EXPECT_EQ( + 0, memcmp(transmit_frame.data_buffer.data(), + receive_frame.data_buffer.data(), kQuicPathFrameBufferSize)); + return true; + } + + // Test the Serialization/deserialization of a Reset Stream Frame. + void TryResetFrame(char* packet_buffer, + size_t packet_buffer_size, + QuicStreamId stream_id, + uint16_t error_code, + QuicStreamOffset final_offset) { + // Initialize a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(packet_buffer_size, packet_buffer, + NETWORK_BYTE_ORDER); + + QuicRstStreamFrame transmit_frame(static_cast<QuicControlFrameId>(1), + stream_id, error_code, final_offset); + + // Write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendIetfResetStreamFrame( + &framer_, transmit_frame, &writer)); + // Check that the size of the serialzed frame is in the allowed range. + EXPECT_LT(3u, writer.length()); + EXPECT_GT(19u, writer.length()); + // Now set up a reader to read in the thing in. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + // A QuicRstStreamFrame to hold the results + QuicRstStreamFrame receive_frame; + EXPECT_TRUE(QuicFramerPeer::ProcessIetfResetStreamFrame(&framer_, &reader, + &receive_frame)); + + // Now check that the received values match the input. + EXPECT_EQ(receive_frame.stream_id, transmit_frame.stream_id); + EXPECT_EQ(receive_frame.ietf_error_code, transmit_frame.ietf_error_code); + EXPECT_EQ(receive_frame.byte_offset, transmit_frame.byte_offset); + } + + void TryMaxStreamsFrame(QuicStreamId stream_id, + bool unidirectional, + bool stream_id_server_initiated) { + if (!unidirectional && !stream_id_server_initiated && stream_id == 0) { + // For bidirectional, client initiated, streams, 0 is not allowed, + // it's used for the crypto stream and is not included in the counting. + return; + } + + char packet_buffer[kNormalPacketBufferSize]; + memset(packet_buffer, 0, sizeof(packet_buffer)); + + Perspective old_perspective = framer_.perspective(); + // Set up the writer and transmit QuicMaxStreamIdFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + if (stream_id_server_initiated) { + stream_id |= 0x01; + } + if (unidirectional) { + stream_id |= 0x02; + } + + // Set the perspective of the sender. If the stream id is supposed to + // be server-initiated, then the sender of the MAX_STREAMS should be + // a client, and vice versa. Do this prior to constructing the frame or + // generating the packet, so that any internal dependencies are satisfied. + QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated) + ? Perspective::IS_CLIENT + : Perspective::IS_SERVER); + QuicMaxStreamIdFrame transmit_frame(0, stream_id); + + // Add the frame. + EXPECT_TRUE(QuicFramerPeer::AppendMaxStreamsFrame(&framer_, transmit_frame, + &writer)); + + // Check that buffer length is in the expected range + EXPECT_LE(1u, writer.length()); + EXPECT_GE(8u, writer.length()); + + // Set the perspective for the receiver. + QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated) + ? Perspective::IS_SERVER + : Perspective::IS_CLIENT); + + // Set up reader and empty receive QuicPaddingFrame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicMaxStreamIdFrame receive_frame; + + // Deframe it + EXPECT_TRUE(QuicFramerPeer::ProcessMaxStreamsFrame( + &framer_, &reader, &receive_frame, + (unidirectional) ? IETF_MAX_STREAMS_UNIDIRECTIONAL + : IETF_MAX_STREAMS_BIDIRECTIONAL)) + << " Error: " << framer_.detailed_error(); + + // Now check that received and sent data are equivalent + EXPECT_EQ(stream_id, receive_frame.max_stream_id); + EXPECT_EQ(transmit_frame.max_stream_id, receive_frame.max_stream_id); + QuicFramerPeer::SetPerspective(&framer_, old_perspective); + } + + void TryStreamsBlockedFrame(QuicStreamId stream_id, + bool unidirectional, + bool stream_id_server_initiated) { + if (!unidirectional && !stream_id_server_initiated && stream_id == 0) { + // For bidirectional, client initiated, streams, 0 is not allowed, + // it's used for the crypto stream and is not included in the counting. + return; + } + + char packet_buffer[kNormalPacketBufferSize]; + memset(packet_buffer, 0, sizeof(packet_buffer)); + + Perspective old_perspective = framer_.perspective(); + // Set up the writer and transmit QuicMaxStreamIdFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + if (stream_id_server_initiated) { + stream_id |= 0x01; + } + if (unidirectional) { + stream_id |= 0x02; + } + + // Set the perspective of the sender. If the stream id is supposed to + // be server-initiated, then the sender of the MAX_STREAMS should be + // a client, and vice versa. Do this prior to constructing the frame or + // generating the packet, so that any internal dependencies are satisfied. + QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated) + ? Perspective::IS_SERVER + : Perspective::IS_CLIENT); + QuicStreamIdBlockedFrame transmit_frame(0, stream_id); + + // Add the frame. + EXPECT_TRUE(QuicFramerPeer::AppendStreamsBlockedFrame( + &framer_, transmit_frame, &writer)); + + // Check that buffer length is in the expected range + EXPECT_LE(1u, writer.length()); + EXPECT_GE(8u, writer.length()); + + // Set the perspective for the receiver. + QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated) + ? Perspective::IS_CLIENT + : Perspective::IS_SERVER); + + // Set up reader and empty receive QuicPaddingFrame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicStreamIdBlockedFrame receive_frame; + + // Deframe it + EXPECT_TRUE(QuicFramerPeer::ProcessStreamsBlockedFrame( + &framer_, &reader, &receive_frame, + (unidirectional) ? IETF_STREAMS_BLOCKED_UNIDIRECTIONAL + : IETF_STREAMS_BLOCKED_BIDIRECTIONAL)); + + // Now check that received and sent data are equivalent + EXPECT_EQ(stream_id, receive_frame.stream_id); + EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id); + QuicFramerPeer::SetPerspective(&framer_, old_perspective); + } + + QuicTime start_; + QuicFramer framer_; + test::TestQuicVisitor visitor_; +}; + +struct stream_frame_variant { + QuicIetfStreamId stream_id; + QuicIetfStreamOffset offset; + bool fin_bit; + bool last_frame_bit; + uint8_t frame_type; +} stream_frame_to_test[] = { +#define IETF_STREAM0 (((uint8_t)IETF_STREAM)) + +#define IETF_STREAM1 (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_FIN_BIT) + +#define IETF_STREAM2 (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_LEN_BIT) + +#define IETF_STREAM3 \ + (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_LEN_BIT | \ + IETF_STREAM_FRAME_FIN_BIT) + +#define IETF_STREAM4 (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT) + +#define IETF_STREAM5 \ + (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT | \ + IETF_STREAM_FRAME_FIN_BIT) + +#define IETF_STREAM6 \ + (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT | \ + IETF_STREAM_FRAME_LEN_BIT) + +#define IETF_STREAM7 \ + (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT | \ + IETF_STREAM_FRAME_LEN_BIT | IETF_STREAM_FRAME_FIN_BIT) + + {kStreamId8, kOffset8, true, false, IETF_STREAM7}, + {kStreamId8, kOffset8, false, false, IETF_STREAM6}, + {kStreamId8, kOffset4, true, false, IETF_STREAM7}, + {kStreamId8, kOffset4, false, false, IETF_STREAM6}, + {kStreamId8, kOffset2, true, false, IETF_STREAM7}, + {kStreamId8, kOffset2, false, false, IETF_STREAM6}, + {kStreamId8, kOffset1, true, false, IETF_STREAM7}, + {kStreamId8, kOffset1, false, false, IETF_STREAM6}, + {kStreamId8, kOffset0, true, false, IETF_STREAM3}, + {kStreamId8, kOffset0, false, false, IETF_STREAM2}, + {kStreamId4, kOffset8, true, false, IETF_STREAM7}, + {kStreamId4, kOffset8, false, false, IETF_STREAM6}, + {kStreamId4, kOffset4, true, false, IETF_STREAM7}, + {kStreamId4, kOffset4, false, false, IETF_STREAM6}, + {kStreamId4, kOffset2, true, false, IETF_STREAM7}, + {kStreamId4, kOffset2, false, false, IETF_STREAM6}, + {kStreamId4, kOffset1, true, false, IETF_STREAM7}, + {kStreamId4, kOffset1, false, false, IETF_STREAM6}, + {kStreamId4, kOffset0, true, false, IETF_STREAM3}, + {kStreamId4, kOffset0, false, false, IETF_STREAM2}, + {kStreamId2, kOffset8, true, false, IETF_STREAM7}, + {kStreamId2, kOffset8, false, false, IETF_STREAM6}, + {kStreamId2, kOffset4, true, false, IETF_STREAM7}, + {kStreamId2, kOffset4, false, false, IETF_STREAM6}, + {kStreamId2, kOffset2, true, false, IETF_STREAM7}, + {kStreamId2, kOffset2, false, false, IETF_STREAM6}, + {kStreamId2, kOffset1, true, false, IETF_STREAM7}, + {kStreamId2, kOffset1, false, false, IETF_STREAM6}, + {kStreamId2, kOffset0, true, false, IETF_STREAM3}, + {kStreamId2, kOffset0, false, false, IETF_STREAM2}, + {kStreamId1, kOffset8, true, false, IETF_STREAM7}, + {kStreamId1, kOffset8, false, false, IETF_STREAM6}, + {kStreamId1, kOffset4, true, false, IETF_STREAM7}, + {kStreamId1, kOffset4, false, false, IETF_STREAM6}, + {kStreamId1, kOffset2, true, false, IETF_STREAM7}, + {kStreamId1, kOffset2, false, false, IETF_STREAM6}, + {kStreamId1, kOffset1, true, false, IETF_STREAM7}, + {kStreamId1, kOffset1, false, false, IETF_STREAM6}, + {kStreamId1, kOffset0, true, false, IETF_STREAM3}, + {kStreamId1, kOffset0, false, false, IETF_STREAM2}, + {kStreamId0, kOffset8, true, false, IETF_STREAM7}, + {kStreamId0, kOffset8, false, false, IETF_STREAM6}, + {kStreamId0, kOffset4, true, false, IETF_STREAM7}, + {kStreamId0, kOffset4, false, false, IETF_STREAM6}, + {kStreamId0, kOffset2, true, false, IETF_STREAM7}, + {kStreamId0, kOffset2, false, false, IETF_STREAM6}, + {kStreamId0, kOffset1, true, false, IETF_STREAM7}, + {kStreamId0, kOffset1, false, false, IETF_STREAM6}, + {kStreamId0, kOffset0, true, false, IETF_STREAM3}, + {kStreamId0, kOffset0, false, false, IETF_STREAM2}, + + {kStreamId8, kOffset8, true, true, IETF_STREAM5}, + {kStreamId8, kOffset8, false, true, IETF_STREAM4}, + {kStreamId8, kOffset4, true, true, IETF_STREAM5}, + {kStreamId8, kOffset4, false, true, IETF_STREAM4}, + {kStreamId8, kOffset2, true, true, IETF_STREAM5}, + {kStreamId8, kOffset2, false, true, IETF_STREAM4}, + {kStreamId8, kOffset1, true, true, IETF_STREAM5}, + {kStreamId8, kOffset1, false, true, IETF_STREAM4}, + {kStreamId8, kOffset0, true, true, IETF_STREAM1}, + {kStreamId8, kOffset0, false, true, IETF_STREAM0}, + {kStreamId4, kOffset8, true, true, IETF_STREAM5}, + {kStreamId4, kOffset8, false, true, IETF_STREAM4}, + {kStreamId4, kOffset4, true, true, IETF_STREAM5}, + {kStreamId4, kOffset4, false, true, IETF_STREAM4}, + {kStreamId4, kOffset2, true, true, IETF_STREAM5}, + {kStreamId4, kOffset2, false, true, IETF_STREAM4}, + {kStreamId4, kOffset1, true, true, IETF_STREAM5}, + {kStreamId4, kOffset1, false, true, IETF_STREAM4}, + {kStreamId4, kOffset0, true, true, IETF_STREAM1}, + {kStreamId4, kOffset0, false, true, IETF_STREAM0}, + {kStreamId2, kOffset8, true, true, IETF_STREAM5}, + {kStreamId2, kOffset8, false, true, IETF_STREAM4}, + {kStreamId2, kOffset4, true, true, IETF_STREAM5}, + {kStreamId2, kOffset4, false, true, IETF_STREAM4}, + {kStreamId2, kOffset2, true, true, IETF_STREAM5}, + {kStreamId2, kOffset2, false, true, IETF_STREAM4}, + {kStreamId2, kOffset1, true, true, IETF_STREAM5}, + {kStreamId2, kOffset1, false, true, IETF_STREAM4}, + {kStreamId2, kOffset0, true, true, IETF_STREAM1}, + {kStreamId2, kOffset0, false, true, IETF_STREAM0}, + {kStreamId1, kOffset8, true, true, IETF_STREAM5}, + {kStreamId1, kOffset8, false, true, IETF_STREAM4}, + {kStreamId1, kOffset4, true, true, IETF_STREAM5}, + {kStreamId1, kOffset4, false, true, IETF_STREAM4}, + {kStreamId1, kOffset2, true, true, IETF_STREAM5}, + {kStreamId1, kOffset2, false, true, IETF_STREAM4}, + {kStreamId1, kOffset1, true, true, IETF_STREAM5}, + {kStreamId1, kOffset1, false, true, IETF_STREAM4}, + {kStreamId1, kOffset0, true, true, IETF_STREAM1}, + {kStreamId1, kOffset0, false, true, IETF_STREAM0}, + {kStreamId0, kOffset8, true, true, IETF_STREAM5}, + {kStreamId0, kOffset8, false, true, IETF_STREAM4}, + {kStreamId0, kOffset4, true, true, IETF_STREAM5}, + {kStreamId0, kOffset4, false, true, IETF_STREAM4}, + {kStreamId0, kOffset2, true, true, IETF_STREAM5}, + {kStreamId0, kOffset2, false, true, IETF_STREAM4}, + {kStreamId0, kOffset1, true, true, IETF_STREAM5}, + {kStreamId0, kOffset1, false, true, IETF_STREAM4}, + {kStreamId0, kOffset0, true, true, IETF_STREAM1}, + {kStreamId0, kOffset0, false, true, IETF_STREAM0}, +}; + +TEST_F(QuicIetfFramerTest, StreamFrame) { + char packet_buffer[kNormalPacketBufferSize]; + const char* transmit_packet_data = + "this is a test of some packet data, " + "can do a simple strcmp to see if the " + "input and output are the same!"; + + size_t transmit_packet_data_len = strlen(transmit_packet_data) + 1; + for (size_t i = 0; i < QUIC_ARRAYSIZE(stream_frame_to_test); ++i) { + SCOPED_TRACE(i); + struct stream_frame_variant* variant = &stream_frame_to_test[i]; + TryStreamFrame(packet_buffer, sizeof(packet_buffer), transmit_packet_data, + transmit_packet_data_len, variant->stream_id, + variant->offset, variant->fin_bit, variant->last_frame_bit, + static_cast<QuicIetfFrameType>(variant->frame_type)); + } +} +// As the previous test, but with no data. +TEST_F(QuicIetfFramerTest, ZeroLengthStreamFrame) { + char packet_buffer[kNormalPacketBufferSize]; + + for (size_t i = 0; i < QUIC_ARRAYSIZE(stream_frame_to_test); ++i) { + SCOPED_TRACE(i); + struct stream_frame_variant* variant = &stream_frame_to_test[i]; + TryStreamFrame(packet_buffer, sizeof(packet_buffer), + /* xmit_packet_data = */ NULL, + /* xmit_packet_data_size = */ 0, variant->stream_id, + variant->offset, variant->fin_bit, variant->last_frame_bit, + static_cast<QuicIetfFrameType>(variant->frame_type)); + } +} + +TEST_F(QuicIetfFramerTest, CryptoFrame) { + SimpleDataProducer data_producer; + framer_.set_data_producer(&data_producer); + char packet_buffer[kNormalPacketBufferSize]; + + QuicStringPiece frame_data("This is a CRYPTO frame."); + + QuicStreamOffset offsets[] = {kOffset8, kOffset4, kOffset2, kOffset1, + kOffset0}; + for (QuicStreamOffset offset : offsets) { + QuicCryptoFrame frame(ENCRYPTION_NONE, offset, frame_data.length()); + data_producer.SaveCryptoData(ENCRYPTION_NONE, offset, frame_data); + + QuicDataWriter writer(QUIC_ARRAYSIZE(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + + // Write the frame. + EXPECT_TRUE(QuicFramerPeer::AppendCryptoFrame(&framer_, frame, &writer)); + EXPECT_NE(0u, writer.length()); + // Read it back. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicCryptoFrame read_frame; + EXPECT_TRUE( + QuicFramerPeer::ProcessCryptoFrame(&framer_, &reader, &read_frame)); + + // Check that the frames match: + QuicStringPiece read_data(read_frame.data_buffer, read_frame.data_length); + EXPECT_EQ(read_frame.data_length, frame.data_length); + EXPECT_EQ(read_frame.offset, frame.offset); + EXPECT_EQ(read_data, frame_data); + } +} + +TEST_F(QuicIetfFramerTest, ConnectionCloseEmptyString) { + char packet_buffer[kNormalPacketBufferSize]; + + // initialize a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + + // empty string, + QuicString test_string = "Ich Bin Ein Jelly Donut?"; + QuicConnectionCloseFrame sent_frame; + sent_frame.error_code = static_cast<QuicErrorCode>(0); + sent_frame.error_details = test_string; + sent_frame.frame_type = 123; + // write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendIetfConnectionCloseFrame( + &framer_, sent_frame, &writer)); + + // better have something in the packet buffer. + EXPECT_NE(0u, writer.length()); + + // now set up a reader to read in the frame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + // a QuicConnectionCloseFrame to hold the results. + QuicConnectionCloseFrame sink_frame; + + EXPECT_TRUE(QuicFramerPeer::ProcessIetfConnectionCloseFrame(&framer_, &reader, + &sink_frame)); + + // Now check that received == sent + EXPECT_EQ(sink_frame.error_code, static_cast<QuicErrorCode>(0)); + EXPECT_EQ(sink_frame.error_details, test_string); +} + +TEST_F(QuicIetfFramerTest, ApplicationCloseEmptyString) { + char packet_buffer[kNormalPacketBufferSize]; + + // initialize a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + + // empty string, + QuicString test_string = "Ich Bin Ein Jelly Donut?"; + QuicApplicationCloseFrame sent_frame; + sent_frame.error_code = static_cast<QuicErrorCode>(0); + sent_frame.error_details = test_string; + // write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendApplicationCloseFrame(&framer_, sent_frame, + &writer)); + + // better have something in the packet buffer. + EXPECT_NE(0u, writer.length()); + + // now set up a reader to read in the frame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + // a QuicConnectionCloseFrame to hold the results. + QuicApplicationCloseFrame sink_frame; + + EXPECT_TRUE(QuicFramerPeer::ProcessApplicationCloseFrame(&framer_, &reader, + &sink_frame)); + + // Now check that received == sent + EXPECT_EQ(sink_frame.error_code, static_cast<QuicErrorCode>(0)); + EXPECT_EQ(sink_frame.error_details, test_string); +} + +// Testing for the IETF ACK framer. +// clang-format off +struct ack_frame ack_frame_variants[] = { + {90000, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1000), QuicPacketNumber(2001)}}, + IETF_ACK}, + {0, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1000), QuicPacketNumber(2001)}}, + IETF_ACK}, + {1, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(2)}, + {QuicPacketNumber(5), QuicPacketNumber(6)}}, + IETF_ACK}, + {63, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(2)}, + {QuicPacketNumber(5), QuicPacketNumber(6)}}, + IETF_ACK}, + {64, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(2)}, + {QuicPacketNumber(3), QuicPacketNumber(4)}, + {QuicPacketNumber(5), QuicPacketNumber(6)}, + {QuicPacketNumber(7), QuicPacketNumber(8)}, + {QuicPacketNumber(9), QuicPacketNumber(10)}, + {QuicPacketNumber(11), QuicPacketNumber(12)}}, + IETF_ACK}, + {10000, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(2)}, + {QuicPacketNumber(3), QuicPacketNumber(4)}, + {QuicPacketNumber(5), QuicPacketNumber(6)}, + {QuicPacketNumber(7), QuicPacketNumber(8)}, + {QuicPacketNumber(9), QuicPacketNumber(10)}, + {QuicPacketNumber(11), QuicPacketNumber(12)}}, + IETF_ACK}, + {100000000, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(2)}, + {QuicPacketNumber(3), QuicPacketNumber(4)}, + {QuicPacketNumber(5), QuicPacketNumber(6)}, + {QuicPacketNumber(7), QuicPacketNumber(8)}, + {QuicPacketNumber(9), QuicPacketNumber(10)}, + {QuicPacketNumber(11), QuicPacketNumber(12)}}, + IETF_ACK}, + {0, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}}, + IETF_ACK}, + {9223372036854775807, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(11)}, + {QuicPacketNumber(74), QuicPacketNumber(138)}}, + IETF_ACK}, + // This ack is for packets 60 & 125. There are 64 packets in the gap. + // The encoded value is gap_size - 1, or 63. Crosses a VarInt62 encoding + // boundary... + {1, + false, + 0, + 0, + 0, + {{QuicPacketNumber(60), QuicPacketNumber(61)}, + {QuicPacketNumber(125), QuicPacketNumber(126)}}, + IETF_ACK}, + {2, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(130)}}, + IETF_ACK}, + {3, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(195)}}, + IETF_ACK}, + {4, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(194)}}, + IETF_ACK}, + {5, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(193)}}, + IETF_ACK}, + {6, + false, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(192)}}, + IETF_ACK}, + // declare some ack_ecn frames to try. + {6, + false, + 100, + 200, + 300, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(192)}}, + IETF_ACK}, + {6, + true, + 100, + 200, + 300, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(192)}}, + IETF_ACK_ECN}, + {6, + true, + 100, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(192)}}, + IETF_ACK_ECN}, + {6, + true, + 0, + 200, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(192)}}, + IETF_ACK_ECN}, + {6, + true, + 0, + 0, + 300, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(192)}}, + IETF_ACK_ECN}, + {6, + true, + 0, + 0, + 0, + {{QuicPacketNumber(1), QuicPacketNumber(65)}, + {QuicPacketNumber(129), QuicPacketNumber(192)}}, + IETF_ACK}, +}; +// clang-format on + +TEST_F(QuicIetfFramerTest, AckFrame) { + char packet_buffer[kNormalPacketBufferSize]; + for (auto ack_frame_variant : ack_frame_variants) { + EXPECT_TRUE( + TryAckFrame(packet_buffer, sizeof(packet_buffer), &ack_frame_variant)); + } +} + +// Test the case of having a QuicAckFrame with no ranges in it. By +// examination of the Google Quic Ack code and tests, this case should +// be handled as an ack with no "ranges after the first"; the +// AckBlockCount should be 0 and the FirstAckBlock should be +// |LargestAcked| - 1 (number of packets preceding the LargestAcked. +TEST_F(QuicIetfFramerTest, AckFrameNoRanges) { + char packet_buffer[kNormalPacketBufferSize]; + + // Make a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + + QuicAckFrame transmit_frame; + transmit_frame.largest_acked = QuicPacketNumber(1); + transmit_frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(0); + + size_t expected_size = + QuicFramerPeer::GetIetfAckFrameSize(&framer_, transmit_frame); + // Write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendIetfAckFrameAndTypeByte( + &framer_, transmit_frame, &writer)); + + uint8_t packet[] = { + 0x02, // type, IETF_ACK + 0x01, // largest_acked, + 0x00, // delay + 0x00, // count of additional ack blocks + 0x00, // size of first ack block (packets preceding largest_acked) + }; + EXPECT_EQ(expected_size, sizeof(packet)); + EXPECT_EQ(sizeof(packet), writer.length()); + EXPECT_EQ(0, memcmp(packet, packet_buffer, writer.length())); + + // Now set up a reader to read in the frame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + // an AckFrame to hold the results + QuicAckFrame receive_frame; + + // read in the frame type + uint8_t received_frame_type; + EXPECT_TRUE(reader.ReadUInt8(&received_frame_type)); + EXPECT_EQ(received_frame_type, IETF_ACK); + + EXPECT_TRUE(QuicFramerPeer::ProcessIetfAckFrame(&framer_, &reader, IETF_ACK, + &receive_frame)); + + // Now check that the received frame matches the sent frame. + EXPECT_EQ(transmit_frame.largest_acked, receive_frame.largest_acked); +} + +TEST_F(QuicIetfFramerTest, PathChallengeFrame) { + // Double-braces needed on some platforms due to + // https://bugs.llvm.org/show_bug.cgi?id=21629 + QuicPathFrameBuffer buffer0 = {{0, 0, 0, 0, 0, 0, 0, 0}}; + QuicPathFrameBuffer buffer1 = { + {0x80, 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe5, 0xf7}}; + char packet_buffer[kNormalPacketBufferSize]; + EXPECT_TRUE( + TryPathChallengeFrame(packet_buffer, sizeof(packet_buffer), buffer0)); + EXPECT_TRUE( + TryPathChallengeFrame(packet_buffer, sizeof(packet_buffer), buffer1)); +} + +TEST_F(QuicIetfFramerTest, PathResponseFrame) { + // Double-braces needed on some platforms due to + // https://bugs.llvm.org/show_bug.cgi?id=21629 + QuicPathFrameBuffer buffer0 = {{0, 0, 0, 0, 0, 0, 0, 0}}; + QuicPathFrameBuffer buffer1 = { + {0x80, 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe5, 0xf7}}; + char packet_buffer[kNormalPacketBufferSize]; + EXPECT_TRUE( + TryPathResponseFrame(packet_buffer, sizeof(packet_buffer), buffer0)); + EXPECT_TRUE( + TryPathResponseFrame(packet_buffer, sizeof(packet_buffer), buffer1)); +} + +TEST_F(QuicIetfFramerTest, ResetStreamFrame) { + char packet_buffer[kNormalPacketBufferSize]; + struct resets { + QuicStreamId stream_id; + uint16_t error_code; + QuicStreamOffset final_offset; + } reset_frames[] = { + {0, 55, 0}, + {0x10, 73, 0x300}, + }; + for (auto reset : reset_frames) { + TryResetFrame(packet_buffer, sizeof(packet_buffer), reset.stream_id, + reset.error_code, reset.final_offset); + } +} + +TEST_F(QuicIetfFramerTest, StopSendingFrame) { + char packet_buffer[kNormalPacketBufferSize]; + + // Make a writer so that the serialized packet is placed in + // packet_buffer. + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + + QuicStopSendingFrame transmit_frame; + transmit_frame.stream_id = 12345; + transmit_frame.application_error_code = 543; + + // Write the frame to the packet buffer. + EXPECT_TRUE(QuicFramerPeer::AppendStopSendingFrame(&framer_, transmit_frame, + &writer)); + // Check that the number of bytes in the buffer is in the + // allowed range. + EXPECT_LE(3u, writer.length()); + EXPECT_GE(10u, writer.length()); + + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + + // A frame to hold the results + QuicStopSendingFrame receive_frame; + + EXPECT_TRUE(QuicFramerPeer::ProcessStopSendingFrame(&framer_, &reader, + &receive_frame)); + + // Verify that the transmitted and received values are the same. + EXPECT_EQ(receive_frame.stream_id, 12345u); + EXPECT_EQ(receive_frame.application_error_code, 543u); + EXPECT_EQ(receive_frame.stream_id, transmit_frame.stream_id); + EXPECT_EQ(receive_frame.application_error_code, + transmit_frame.application_error_code); +} + +TEST_F(QuicIetfFramerTest, MaxDataFrame) { + char packet_buffer[kNormalPacketBufferSize]; + QuicStreamOffset window_sizes[] = {0, 1, 2, 5, 10, + 20, 50, 100, 200, 500, + 1000000, kOffset8, kOffset4, kOffset2}; + for (QuicStreamOffset window_size : window_sizes) { + memset(packet_buffer, 0, sizeof(packet_buffer)); + + // Set up the writer and transmit QuicWindowUpdateFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + QuicWindowUpdateFrame transmit_frame(0, 99, window_size); + + // Add the frame. + EXPECT_TRUE( + QuicFramerPeer::AppendMaxDataFrame(&framer_, transmit_frame, &writer)); + + // Check that the number of bytes in the buffer is in the expected range. + EXPECT_LE(1u, writer.length()); + EXPECT_GE(8u, writer.length()); + + // Set up reader and an empty QuicWindowUpdateFrame + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicWindowUpdateFrame receive_frame; + + // Deframe it + EXPECT_TRUE( + QuicFramerPeer::ProcessMaxDataFrame(&framer_, &reader, &receive_frame)); + + // Now check that the received data equals the sent data. + EXPECT_EQ(transmit_frame.byte_offset, window_size); + EXPECT_EQ(transmit_frame.byte_offset, receive_frame.byte_offset); + EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()), + receive_frame.stream_id); + } +} + +TEST_F(QuicIetfFramerTest, MaxStreamDataFrame) { + char packet_buffer[kNormalPacketBufferSize]; + QuicStreamOffset window_sizes[] = {0, 1, 2, 5, 10, + 20, 50, 100, 200, 500, + 1000000, kOffset8, kOffset4, kOffset2}; + QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1, + kStreamId0}; + + for (QuicIetfStreamId stream_id : stream_ids) { + for (QuicStreamOffset window_size : window_sizes) { + memset(packet_buffer, 0, sizeof(packet_buffer)); + + // Set up the writer and transmit QuicWindowUpdateFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + QuicWindowUpdateFrame transmit_frame(0, stream_id, window_size); + + // Add the frame. + EXPECT_TRUE(QuicFramerPeer::AppendMaxStreamDataFrame( + &framer_, transmit_frame, &writer)); + + // Check that number of bytes in the buffer is in the expected range. + EXPECT_LE(2u, writer.length()); + EXPECT_GE(16u, writer.length()); + + // Set up reader and empty receive QuicPaddingFrame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicWindowUpdateFrame receive_frame; + + // Deframe it + EXPECT_TRUE(QuicFramerPeer::ProcessMaxStreamDataFrame(&framer_, &reader, + &receive_frame)); + + // Now check that received data and sent data are equal. + EXPECT_EQ(transmit_frame.byte_offset, window_size); + EXPECT_EQ(transmit_frame.byte_offset, receive_frame.byte_offset); + EXPECT_EQ(stream_id, receive_frame.stream_id); + EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id); + } + } +} + +TEST_F(QuicIetfFramerTest, MaxStreamsFrame) { + QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1, + kStreamId0}; + + for (QuicIetfStreamId stream_id : stream_ids) { + // Cover all four combinations of uni/bi-directional and + // server-/client- initiation. + TryMaxStreamsFrame(stream_id, /*unidirectional=*/true, + /*stream_id_server_initiated=*/true); + TryMaxStreamsFrame(stream_id, /*unidirectional=*/true, + /*stream_id_server_initiated=*/false); + TryMaxStreamsFrame(stream_id, /*unidirectional=*/false, + /*stream_id_server_initiated=*/true); + TryMaxStreamsFrame(stream_id, /*unidirectional=*/false, + /*stream_id_server_initiated=*/false); + } +} + +TEST_F(QuicIetfFramerTest, BlockedFrame) { + char packet_buffer[kNormalPacketBufferSize]; + QuicStreamOffset offsets[] = {kOffset8, kOffset4, kOffset2, kOffset1, + kOffset0}; + + for (QuicStreamOffset offset : offsets) { + memset(packet_buffer, 0, sizeof(packet_buffer)); + + // Set up the writer and transmit QuicBlockedFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + QuicBlockedFrame transmit_frame( + 0, QuicUtils::GetInvalidStreamId(framer_.transport_version()), offset); + + // Add the frame. + EXPECT_TRUE(QuicFramerPeer::AppendIetfBlockedFrame(&framer_, transmit_frame, + &writer)); + + // Check that buffer length is in the expected range + EXPECT_LE(1u, writer.length()); + EXPECT_GE(8u, writer.length()); + + // Set up reader and empty receive QuicFrame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicBlockedFrame receive_frame; + + // Deframe it + EXPECT_TRUE(QuicFramerPeer::ProcessIetfBlockedFrame(&framer_, &reader, + &receive_frame)); + + // Check that received and sent data are equivalent + EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()), + receive_frame.stream_id); + EXPECT_EQ(offset, receive_frame.offset); + EXPECT_EQ(transmit_frame.offset, receive_frame.offset); + } +} + +TEST_F(QuicIetfFramerTest, StreamBlockedFrame) { + char packet_buffer[kNormalPacketBufferSize]; + QuicStreamOffset offsets[] = {0, 1, 2, 5, 10, + 20, 50, 100, 200, 500, + 1000000, kOffset8, kOffset4, kOffset2}; + QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1, + kStreamId0}; + + for (QuicIetfStreamId stream_id : stream_ids) { + for (QuicStreamOffset offset : offsets) { + memset(packet_buffer, 0, sizeof(packet_buffer)); + + // Set up the writer and transmit QuicWindowUpdateFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + QuicBlockedFrame transmit_frame(0, stream_id, offset); + + // Add the frame. + EXPECT_TRUE(QuicFramerPeer::AppendStreamBlockedFrame( + &framer_, transmit_frame, &writer)); + + // Check that number of bytes in the buffer is in the expected range. + EXPECT_LE(2u, writer.length()); + EXPECT_GE(16u, writer.length()); + + // Set up reader and empty receive QuicPaddingFrame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicBlockedFrame receive_frame; + + // Deframe it + EXPECT_TRUE(QuicFramerPeer::ProcessStreamBlockedFrame(&framer_, &reader, + &receive_frame)); + + // Now check that received == sent + EXPECT_EQ(transmit_frame.offset, offset); + EXPECT_EQ(transmit_frame.offset, receive_frame.offset); + EXPECT_EQ(stream_id, receive_frame.stream_id); + EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id); + } + } +} + +TEST_F(QuicIetfFramerTest, StreamsBlockedFrame) { + QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1, + kStreamId0}; + + for (QuicIetfStreamId stream_id : stream_ids) { + TryStreamsBlockedFrame(stream_id, + /*unidirectional=*/false, + /*stream_id_server_initiated=*/false); + TryStreamsBlockedFrame(stream_id, + /*unidirectional=*/false, + /*stream_id_server_initiated=*/true); + TryStreamsBlockedFrame(stream_id, + /*unidirectional=*/true, + /*stream_id_server_initiated=*/false); + TryStreamsBlockedFrame(stream_id, + /*unidirectional=*/true, + /*stream_id_server_initiated=*/true); + } +} + +TEST_F(QuicIetfFramerTest, NewConnectionIdFrame) { + char packet_buffer[kNormalPacketBufferSize]; + + QuicNewConnectionIdFrame transmit_frame; + transmit_frame.connection_id = TestConnectionId(UINT64_C(0x0edcba9876543201)); + transmit_frame.sequence_number = 0x01020304; + // The token is defined as a uint128 -- a 16-byte integer. + // The value is set in this manner because we want each + // byte to have a specific value so that the binary + // packet check (below) is good. If we used integer + // operations (eg. "token = 0x12345...") then the bytes + // would be set in host order. + unsigned char token_bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f}; + memcpy(&transmit_frame.stateless_reset_token, token_bytes, + sizeof(transmit_frame.stateless_reset_token)); + + memset(packet_buffer, 0, sizeof(packet_buffer)); + + // Set up the writer and transmit QuicStreamIdBlockedFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + + // Add the frame. + EXPECT_TRUE(QuicFramerPeer::AppendNewConnectionIdFrame( + &framer_, transmit_frame, &writer)); + // Check that buffer length is correct + EXPECT_EQ(29u, writer.length()); + // clang-format off + uint8_t packet[] = { + // sequence number, 0x80 for varint62 encoding + 0x80 + 0x01, 0x02, 0x03, 0x04, + // new connection id length, is not varint62 encoded. + 0x08, + // new connection id, is not varint62 encoded. + 0x0E, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x01, + // the reset token: + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + + // clang-format on + EXPECT_EQ(0, memcmp(packet_buffer, packet, sizeof(packet))); + + // Set up reader and empty receive QuicPaddingFrame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicNewConnectionIdFrame receive_frame; + + // Deframe it + EXPECT_TRUE(QuicFramerPeer::ProcessNewConnectionIdFrame(&framer_, &reader, + &receive_frame)); + + // Now check that received == sent + EXPECT_EQ(transmit_frame.connection_id, receive_frame.connection_id); + EXPECT_EQ(transmit_frame.sequence_number, receive_frame.sequence_number); + EXPECT_EQ(transmit_frame.stateless_reset_token, + receive_frame.stateless_reset_token); +} + +TEST_F(QuicIetfFramerTest, RetireConnectionIdFrame) { + char packet_buffer[kNormalPacketBufferSize]; + + QuicRetireConnectionIdFrame transmit_frame; + transmit_frame.sequence_number = 0x01020304; + + memset(packet_buffer, 0, sizeof(packet_buffer)); + + // Set up the writer and transmit QuicStreamIdBlockedFrame + QuicDataWriter writer(sizeof(packet_buffer), packet_buffer, + NETWORK_BYTE_ORDER); + + // Add the frame. + EXPECT_TRUE(QuicFramerPeer::AppendRetireConnectionIdFrame( + &framer_, transmit_frame, &writer)); + // Check that buffer length is correct + EXPECT_EQ(4u, writer.length()); + // clang-format off + uint8_t packet[] = { + // sequence number, 0x80 for varint62 encoding + 0x80 + 0x01, 0x02, 0x03, 0x04, + }; + + // clang-format on + EXPECT_EQ(0, memcmp(packet_buffer, packet, sizeof(packet))); + + // Set up reader and empty receive QuicPaddingFrame. + QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER); + QuicRetireConnectionIdFrame receive_frame; + + // Deframe it + EXPECT_TRUE(QuicFramerPeer::ProcessRetireConnectionIdFrame(&framer_, &reader, + &receive_frame)); + + // Now check that received == sent + EXPECT_EQ(transmit_frame.sequence_number, receive_frame.sequence_number); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_interval.h b/quic/core/quic_interval.h new file mode 100644 index 0000000..c860e88 --- /dev/null +++ b/quic/core/quic_interval.h
@@ -0,0 +1,378 @@ +// Copyright (c) 2019 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_QUIC_INTERVAL_H_ +#define QUICHE_QUIC_CORE_QUIC_INTERVAL_H_ + +// An QuicInterval<T> is a data structure used to represent a contiguous, +// mutable range over an ordered type T. Supported operations include testing a +// value to see whether it is included in the QuicInterval, comparing two +// QuicIntervals, and performing their union, intersection, and difference. For +// the purposes of this library, an "ordered type" is any type that induces a +// total order on its values via its less-than operator (operator<()). Examples +// of such types are basic arithmetic types like int and double as well as class +// types like string. +// +// An QuicInterval<T> is represented using the usual C++ STL convention, namely +// as the half-open QuicInterval [min, max). A point p is considered to be +// contained in the QuicInterval iff p >= min && p < max. One consequence of +// this definition is that for any non-empty QuicInterval, min is contained in +// the QuicInterval but max is not. There is no canonical representation for the +// empty QuicInterval; rather, any QuicInterval where max <= min is regarded as +// empty. As a consequence, two empty QuicIntervals will still compare as equal +// despite possibly having different underlying min() or max() values. Also +// beware of the terminology used here: the library uses the terms "min" and +// "max" rather than "begin" and "end" as is conventional for the STL. +// +// T is required to be default- and copy-constructable, to have an assignment +// operator, and the full complement of comparison operators (<, <=, ==, !=, >=, +// >). A difference operator (operator-()) is required if +// QuicInterval<T>::Length is used. +// +// QuicInterval supports operator==. Two QuicIntervals are considered equal if +// either they are both empty or if their corresponding min and max fields +// compare equal. QuicInterval also provides an operator<. Unfortunately, +// operator< is currently buggy because its behavior is inconsistent with +// operator==: two empty ranges with different representations may be regarded +// as equal by operator== but regarded as different by operator<. Bug 9240050 +// has been created to address this. +// +// +// Examples: +// QuicInterval<int> r1(0, 100); // The QuicInterval [0, 100). +// EXPECT_TRUE(r1.Contains(0)); +// EXPECT_TRUE(r1.Contains(50)); +// EXPECT_FALSE(r1.Contains(100)); // 100 is just outside the QuicInterval. +// +// QuicInterval<int> r2(50, 150); // The QuicInterval [50, 150). +// EXPECT_TRUE(r1.Intersects(r2)); +// EXPECT_FALSE(r1.Contains(r2)); +// EXPECT_TRUE(r1.IntersectWith(r2)); // Mutates r1. +// EXPECT_EQ(QuicInterval<int>(50, 100), r1); // r1 is now [50, 100). +// +// QuicInterval<int> r3(1000, 2000); // The QuicInterval [1000, 2000). +// EXPECT_TRUE(r1.IntersectWith(r3)); // Mutates r1. +// EXPECT_TRUE(r1.Empty()); // Now r1 is empty. +// EXPECT_FALSE(r1.Contains(r1.min())); // e.g. doesn't contain its own min. + +#include <stddef.h> +#include <algorithm> +#include <ostream> +#include <type_traits> +#include <utility> +#include <vector> + +namespace quic { + +template <typename T> +class QuicInterval { + private: + // Type trait for deriving the return type for QuicInterval::Length. If + // operator-() is not defined for T, then the return type is void. This makes + // the signature for Length compile so that the class can be used for such T, + // but code that calls Length would still generate a compilation error. + template <typename U> + class DiffTypeOrVoid { + private: + template <typename V> + static auto f(const V* v) -> decltype(*v - *v); + template <typename V> + static void f(...); + + public: + using type = typename std::decay<decltype(f<U>(nullptr))>::type; + }; + + public: + // Construct an QuicInterval representing an empty QuicInterval. + QuicInterval() : min_(), max_() {} + + // Construct an QuicInterval representing the QuicInterval [min, max). If min + // < max, the constructed object will represent the non-empty QuicInterval + // containing all values from min up to (but not including) max. On the other + // hand, if min >= max, the constructed object will represent the empty + // QuicInterval. + QuicInterval(const T& min, const T& max) : min_(min), max_(max) {} + + template <typename U1, + typename U2, + typename = typename std::enable_if< + std::is_convertible<U1, T>::value && + std::is_convertible<U2, T>::value>::type> + QuicInterval(U1&& min, U2&& max) + : min_(std::forward<U1>(min)), max_(std::forward<U2>(max)) {} + + const T& min() const { return min_; } + const T& max() const { return max_; } + void SetMin(const T& t) { min_ = t; } + void SetMax(const T& t) { max_ = t; } + + void Set(const T& min, const T& max) { + SetMin(min); + SetMax(max); + } + + void Clear() { *this = {}; } + + bool Empty() const { return min() >= max(); } + + // Returns the length of this QuicInterval. The value returned is zero if + // Empty() is true; otherwise the value returned is max() - min(). + typename DiffTypeOrVoid<T>::type Length() const { + return (Empty() ? min() : max()) - min(); + } + + // Returns true iff t >= min() && t < max(). + bool Contains(const T& t) const { return min() <= t && max() > t; } + + // Returns true iff *this and i are non-empty, and *this includes i. "*this + // includes i" means that for all t, if i.Contains(t) then this->Contains(t). + // Note the unintuitive consequence of this definition: this method always + // returns false when i is the empty QuicInterval. + bool Contains(const QuicInterval& i) const { + return !Empty() && !i.Empty() && min() <= i.min() && max() >= i.max(); + } + + // Returns true iff there exists some point t for which this->Contains(t) && + // i.Contains(t) evaluates to true, i.e. if the intersection is non-empty. + bool Intersects(const QuicInterval& i) const { + return !Empty() && !i.Empty() && min() < i.max() && max() > i.min(); + } + + // Returns true iff there exists some point t for which this->Contains(t) && + // i.Contains(t) evaluates to true, i.e. if the intersection is non-empty. + // Furthermore, if the intersection is non-empty and the out pointer is not + // null, this method stores the calculated intersection in *out. + bool Intersects(const QuicInterval& i, QuicInterval* out) const; + + // Sets *this to be the intersection of itself with i. Returns true iff + // *this was modified. + bool IntersectWith(const QuicInterval& i); + + // Calculates the smallest QuicInterval containing both *this i, and updates + // *this to represent that QuicInterval, and returns true iff *this was + // modified. + bool SpanningUnion(const QuicInterval& i); + + // Determines the difference between two QuicIntervals by finding all points + // that are contained in *this but not in i, coalesces those points into the + // largest possible contiguous QuicIntervals, and appends those QuicIntervals + // to the *difference vector. Intuitively this can be thought of as "erasing" + // i from *this. This will either completely erase *this (leaving nothing + // behind), partially erase some of *this from the left or right side (leaving + // some residual behind), or erase a hole in the middle of *this (leaving + // behind an QuicInterval on either side). Therefore, 0, 1, or 2 QuicIntervals + // will be appended to *difference. The method returns true iff the + // intersection of *this and i is non-empty. The caller owns the vector and + // the QuicInterval* pointers inside it. The difference vector is required to + // be non-null. + bool Difference(const QuicInterval& i, + std::vector<QuicInterval*>* difference) const; + + // Determines the difference between two QuicIntervals as in + // Difference(QuicInterval&, vector*), but stores the results directly in out + // parameters rather than dynamically allocating an QuicInterval* and + // appending it to a vector. If two results are generated, the one with the + // smaller value of min() will be stored in *lo and the other in *hi. + // Otherwise (if fewer than two results are generated), unused arguments will + // be set to the empty QuicInterval (it is possible that *lo will be empty and + // *hi non-empty). The method returns true iff the intersection of *this and i + // is non-empty. + bool Difference(const QuicInterval& i, + QuicInterval* lo, + QuicInterval* hi) const; + + friend bool operator==(const QuicInterval& a, const QuicInterval& b) { + bool ae = a.Empty(); + bool be = b.Empty(); + if (ae && be) + return true; // All empties are equal. + if (ae != be) + return false; // Empty cannot equal nonempty. + return a.min() == b.min() && a.max() == b.max(); + } + + friend bool operator!=(const QuicInterval& a, const QuicInterval& b) { + return !(a == b); + } + + // Defines a comparator which can be used to induce an order on QuicIntervals, + // so that, for example, they can be stored in an ordered container such as + // std::set. The ordering is arbitrary, but does provide the guarantee that, + // for non-empty QuicIntervals X and Y, if X contains Y, then X <= Y. + // TODO(kosak): The current implementation of this comparator has a problem + // because the ordering it induces is inconsistent with that of Equals(). In + // particular, this comparator does not properly consider all empty + // QuicIntervals equivalent. Bug 9240050 has been created to track this. + friend bool operator<(const QuicInterval& a, const QuicInterval& b) { + return a.min() < b.min() || (!(b.min() < a.min()) && b.max() < a.max()); + } + + private: + T min_; // Inclusive lower bound. + T max_; // Exclusive upper bound. +}; + +// Constructs an QuicInterval by deducing the types from the function arguments. +template <typename T> +QuicInterval<T> MakeQuicInterval(T&& lhs, T&& rhs) { + return QuicInterval<T>(std::forward<T>(lhs), std::forward<T>(rhs)); +} + +// Note: ideally we'd use +// decltype(out << "[" << i.min() << ", " << i.max() << ")") +// as return type of the function, but as of July 2017 this triggers g++ +// "sorry, unimplemented: string literal in function template signature" error. +template <typename T> +auto operator<<(std::ostream& out, const QuicInterval<T>& i) + -> decltype(out << i.min()) { + return out << "[" << i.min() << ", " << i.max() << ")"; +} + +//============================================================================== +// Implementation details: Clients can stop reading here. + +template <typename T> +bool QuicInterval<T>::Intersects(const QuicInterval& i, + QuicInterval* out) const { + if (!Intersects(i)) + return false; + if (out != nullptr) { + *out = QuicInterval(std::max(min(), i.min()), std::min(max(), i.max())); + } + return true; +} + +template <typename T> +bool QuicInterval<T>::IntersectWith(const QuicInterval& i) { + if (Empty()) + return false; + bool modified = false; + if (i.min() > min()) { + SetMin(i.min()); + modified = true; + } + if (i.max() < max()) { + SetMax(i.max()); + modified = true; + } + return modified; +} + +template <typename T> +bool QuicInterval<T>::SpanningUnion(const QuicInterval& i) { + if (i.Empty()) + return false; + if (Empty()) { + *this = i; + return true; + } + bool modified = false; + if (i.min() < min()) { + SetMin(i.min()); + modified = true; + } + if (i.max() > max()) { + SetMax(i.max()); + modified = true; + } + return modified; +} + +template <typename T> +bool QuicInterval<T>::Difference(const QuicInterval& i, + std::vector<QuicInterval*>* difference) const { + if (Empty()) { + // <empty> - <i> = <empty> + return false; + } + if (i.Empty()) { + // <this> - <empty> = <this> + difference->push_back(new QuicInterval(*this)); + return false; + } + if (min() < i.max() && min() >= i.min() && max() > i.max()) { + // [------ this ------) + // [------ i ------) + // [-- result ---) + difference->push_back(new QuicInterval(i.max(), max())); + return true; + } + if (max() > i.min() && max() <= i.max() && min() < i.min()) { + // [------ this ------) + // [------ i ------) + // [- result -) + difference->push_back(new QuicInterval(min(), i.min())); + return true; + } + if (min() < i.min() && max() > i.max()) { + // [------- this --------) + // [---- i ----) + // [ R1 ) [ R2 ) + // There are two results: R1 and R2. + difference->push_back(new QuicInterval(min(), i.min())); + difference->push_back(new QuicInterval(i.max(), max())); + return true; + } + if (min() >= i.min() && max() <= i.max()) { + // [--- this ---) + // [------ i --------) + // Intersection is <this>, so difference yields the empty QuicInterval. + // Nothing is appended to *difference. + return true; + } + // No intersection. Append <this>. + difference->push_back(new QuicInterval(*this)); + return false; +} + +template <typename T> +bool QuicInterval<T>::Difference(const QuicInterval& i, + QuicInterval* lo, + QuicInterval* hi) const { + // Initialize *lo and *hi to empty + *lo = {}; + *hi = {}; + if (Empty()) + return false; + if (i.Empty()) { + *lo = *this; + return false; + } + if (min() < i.max() && min() >= i.min() && max() > i.max()) { + // [------ this ------) + // [------ i ------) + // [-- result ---) + *hi = QuicInterval(i.max(), max()); + return true; + } + if (max() > i.min() && max() <= i.max() && min() < i.min()) { + // [------ this ------) + // [------ i ------) + // [- result -) + *lo = QuicInterval(min(), i.min()); + return true; + } + if (min() < i.min() && max() > i.max()) { + // [------- this --------) + // [---- i ----) + // [ R1 ) [ R2 ) + // There are two results: R1 and R2. + *lo = QuicInterval(min(), i.min()); + *hi = QuicInterval(i.max(), max()); + return true; + } + if (min() >= i.min() && max() <= i.max()) { + // [--- this ---) + // [------ i --------) + // Intersection is <this>, so difference yields the empty QuicInterval. + return true; + } + *lo = *this; // No intersection. + return false; +} + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_INTERVAL_H_
diff --git a/quic/core/quic_interval_set.h b/quic/core/quic_interval_set.h new file mode 100644 index 0000000..e2ca7a5 --- /dev/null +++ b/quic/core/quic_interval_set.h
@@ -0,0 +1,913 @@ +// Copyright (c) 2019 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_QUIC_INTERVAL_SET_H_ +#define QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_ + +// QuicIntervalSet<T> is a data structure used to represent a sorted set of +// non-empty, non-adjacent, and mutually disjoint intervals. Mutations to an +// interval set preserve these properties, altering the set as needed. For +// example, adding [2, 3) to a set containing only [1, 2) would result in the +// set containing the single interval [1, 3). +// +// Supported operations include testing whether an Interval is contained in the +// QuicIntervalSet, comparing two QuicIntervalSets, and performing +// QuicIntervalSet union, intersection, and difference. +// +// QuicIntervalSet maintains the minimum number of entries needed to represent +// the set of underlying intervals. When the QuicIntervalSet is modified (e.g. +// due to an Add operation), other interval entries may be coalesced, removed, +// or otherwise modified in order to maintain this invariant. The intervals are +// maintained in sorted order, by ascending min() value. +// +// The reader is cautioned to beware of the terminology used here: this library +// uses the terms "min" and "max" rather than "begin" and "end" as is +// conventional for the STL. The terminology [min, max) refers to the half-open +// interval which (if the interval is not empty) contains min but does not +// contain max. An interval is considered empty if min >= max. +// +// T is required to be default- and copy-constructible, to have an assignment +// operator, a difference operator (operator-()), and the full complement of +// comparison operators (<, <=, ==, !=, >=, >). These requirements are inherited +// from value_type. +// +// QuicIntervalSet has constant-time move operations. +// +// +// Examples: +// QuicIntervalSet<int> intervals; +// intervals.Add(Interval<int>(10, 20)); +// intervals.Add(Interval<int>(30, 40)); +// // intervals contains [10,20) and [30,40). +// intervals.Add(Interval<int>(15, 35)); +// // intervals has been coalesced. It now contains the single range [10,40). +// EXPECT_EQ(1, intervals.Size()); +// EXPECT_TRUE(intervals.Contains(Interval<int>(10, 40))); +// +// intervals.Difference(Interval<int>(10, 20)); +// // intervals should now contain the single range [20, 40). +// EXPECT_EQ(1, intervals.Size()); +// EXPECT_TRUE(intervals.Contains(Interval<int>(20, 40))); + +#include <stddef.h> +#include <algorithm> +#include <initializer_list> +#include <set> +#include <utility> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +template <typename T> +class QuicIntervalSet { + public: + typedef QuicInterval<T> value_type; + + private: + struct IntervalLess { + bool operator()(const value_type& a, const value_type& b) const; + }; + typedef std::set<value_type, IntervalLess> Set; + + public: + typedef typename Set::const_iterator const_iterator; + typedef typename Set::const_reverse_iterator const_reverse_iterator; + + // Instantiates an empty QuicIntervalSet. + QuicIntervalSet() {} + + // Instantiates an QuicIntervalSet containing exactly one initial half-open + // interval [min, max), unless the given interval is empty, in which case the + // QuicIntervalSet will be empty. + explicit QuicIntervalSet(const value_type& interval) { Add(interval); } + + // Instantiates an QuicIntervalSet containing the half-open interval [min, + // max). + QuicIntervalSet(const T& min, const T& max) { Add(min, max); } + + QuicIntervalSet(std::initializer_list<value_type> il) { assign(il); } + + // Clears this QuicIntervalSet. + void Clear() { intervals_.clear(); } + + // Returns the number of disjoint intervals contained in this QuicIntervalSet. + size_t Size() const { return intervals_.size(); } + + // Returns the smallest interval that contains all intervals in this + // QuicIntervalSet, or the empty interval if the set is empty. + value_type SpanningInterval() const; + + // Adds "interval" to this QuicIntervalSet. Adding the empty interval has no + // effect. + void Add(const value_type& interval); + + // Adds the interval [min, max) to this QuicIntervalSet. Adding the empty + // interval has no effect. + void Add(const T& min, const T& max) { Add(value_type(min, max)); } + + // Same semantics as Add(const value_type&), but optimized for the case where + // rbegin()->min() <= |interval|.min() <= rbegin()->max(). + void AddOptimizedForAppend(const value_type& interval) { + if (Empty()) { + Add(interval); + return; + } + + const_reverse_iterator last_interval = intervals_.rbegin(); + + // If interval.min() is outside of [last_interval->min, last_interval->max], + // we can not simply extend last_interval->max. + if (interval.min() < last_interval->min() || + interval.min() > last_interval->max()) { + Add(interval); + return; + } + + if (interval.max() <= last_interval->max()) { + // interval is fully contained by last_interval. + return; + } + + // Extend last_interval.max to interval.max, in place. + // + // Set does not allow in-place updates due to the potential of violating its + // ordering requirements. But we know setting the max of the last interval + // is safe w.r.t set ordering and other invariants of QuicIntervalSet, so we + // force an in-place update for performance. + const_cast<value_type*>(&(*last_interval))->SetMax(interval.max()); + } + + // Same semantics as Add(const T&, const T&), but optimized for the case where + // rbegin()->max() == |min|. + void AddOptimizedForAppend(const T& min, const T& max) { + AddOptimizedForAppend(value_type(min, max)); + } + + // TODO(wub): Similar to AddOptimizedForAppend, we can also have a + // AddOptimizedForPrepend if there is a use case. + + // Returns true if this QuicIntervalSet is empty. + bool Empty() const { return intervals_.empty(); } + + // Returns true if any interval in this QuicIntervalSet contains the indicated + // value. + bool Contains(const T& value) const; + + // Returns true if there is some interval in this QuicIntervalSet that wholly + // contains the given interval. An interval O "wholly contains" a non-empty + // interval I if O.Contains(p) is true for every p in I. This is the same + // definition used by value_type::Contains(). This method returns false on + // the empty interval, due to a (perhaps unintuitive) convention inherited + // from value_type. + // Example: + // Assume an QuicIntervalSet containing the entries { [10,20), [30,40) }. + // Contains(Interval(15, 16)) returns true, because [10,20) contains + // [15,16). However, Contains(Interval(15, 35)) returns false. + bool Contains(const value_type& interval) const; + + // Returns true if for each interval in "other", there is some (possibly + // different) interval in this QuicIntervalSet which wholly contains it. See + // Contains(const value_type& interval) for the meaning of "wholly contains". + // Perhaps unintuitively, this method returns false if "other" is the empty + // set. The algorithmic complexity of this method is O(other.Size() * + // log(this->Size())). The method could be rewritten to run in O(other.Size() + // + this->Size()), and this alternative could be implemented as a free + // function using the public API. + bool Contains(const QuicIntervalSet<T>& other) const; + + // Returns true if there is some interval in this QuicIntervalSet that wholly + // contains the interval [min, max). See Contains(const value_type&). + bool Contains(const T& min, const T& max) const { + return Contains(value_type(min, max)); + } + + // Returns true if for some interval in "other", there is some interval in + // this QuicIntervalSet that intersects with it. See value_type::Intersects() + // for the definition of interval intersection. + bool Intersects(const QuicIntervalSet& other) const; + + // Returns an iterator to the value_type in the QuicIntervalSet that contains + // the given value. In other words, returns an iterator to the unique interval + // [min, max) in the QuicIntervalSet that has the property min <= value < max. + // If there is no such interval, this method returns end(). + const_iterator Find(const T& value) const; + + // Returns an iterator to the value_type in the QuicIntervalSet that wholly + // contains the given interval. In other words, returns an iterator to the + // unique interval outer in the QuicIntervalSet that has the property that + // outer.Contains(interval). If there is no such interval, or if interval is + // empty, returns end(). + const_iterator Find(const value_type& interval) const; + + // Returns an iterator to the value_type in the QuicIntervalSet that wholly + // contains [min, max). In other words, returns an iterator to the unique + // interval outer in the QuicIntervalSet that has the property that + // outer.Contains(Interval<T>(min, max)). If there is no such interval, or if + // interval is empty, returns end(). + const_iterator Find(const T& min, const T& max) const { + return Find(value_type(min, max)); + } + + // Returns an iterator pointing to the first value_type which contains or + // goes after the given value. + // + // Example: + // [10, 20) [30, 40) + // ^ LowerBound(10) + // ^ LowerBound(15) + // ^ LowerBound(20) + // ^ LowerBound(25) + const_iterator LowerBound(const T& value) const; + + // Returns an iterator pointing to the first value_type which goes after + // the given value. + // + // Example: + // [10, 20) [30, 40) + // ^ UpperBound(10) + // ^ UpperBound(15) + // ^ UpperBound(20) + // ^ UpperBound(25) + const_iterator UpperBound(const T& value) const; + + // Returns true if every value within the passed interval is not Contained + // within the QuicIntervalSet. + // Note that empty intervals are always considered disjoint from the + // QuicIntervalSet (even though the QuicIntervalSet doesn't `Contain` them). + bool IsDisjoint(const value_type& interval) const; + + // Merges all the values contained in "other" into this QuicIntervalSet. + void Union(const QuicIntervalSet& other); + + // Modifies this QuicIntervalSet so that it contains only those values that + // are currently present both in *this and in the QuicIntervalSet "other". + void Intersection(const QuicIntervalSet& other); + + // Mutates this QuicIntervalSet so that it contains only those values that are + // currently in *this but not in "interval". + void Difference(const value_type& interval); + + // Mutates this QuicIntervalSet so that it contains only those values that are + // currently in *this but not in the interval [min, max). + void Difference(const T& min, const T& max); + + // Mutates this QuicIntervalSet so that it contains only those values that are + // currently in *this but not in the QuicIntervalSet "other". + void Difference(const QuicIntervalSet& other); + + // Mutates this QuicIntervalSet so that it contains only those values that are + // in [min, max) but not currently in *this. + void Complement(const T& min, const T& max); + + // QuicIntervalSet's begin() iterator. The invariants of QuicIntervalSet + // guarantee that for each entry e in the set, e.min() < e.max() (because the + // entries are non-empty) and for each entry f that appears later in the set, + // e.max() < f.min() (because the entries are ordered, pairwise-disjoint, and + // non-adjacent). Modifications to this QuicIntervalSet invalidate these + // iterators. + const_iterator begin() const { return intervals_.begin(); } + + // QuicIntervalSet's end() iterator. + const_iterator end() const { return intervals_.end(); } + + // QuicIntervalSet's rbegin() and rend() iterators. Iterator invalidation + // semantics are the same as those for begin() / end(). + const_reverse_iterator rbegin() const { return intervals_.rbegin(); } + + const_reverse_iterator rend() const { return intervals_.rend(); } + + template <typename Iter> + void assign(Iter first, Iter last) { + Clear(); + for (; first != last; ++first) + Add(*first); + } + + void assign(std::initializer_list<value_type> il) { + assign(il.begin(), il.end()); + } + + // Returns a human-readable representation of this set. This will typically be + // (though is not guaranteed to be) of the form + // "[a1, b1) [a2, b2) ... [an, bn)" + // where the intervals are in the same order as given by traversal from + // begin() to end(). This representation is intended for human consumption; + // computer programs should not rely on the output being in exactly this form. + QuicString ToString() const; + + QuicIntervalSet& operator=(std::initializer_list<value_type> il) { + assign(il.begin(), il.end()); + return *this; + } + + // Swap this QuicIntervalSet with *other. This is a constant-time operation. + void Swap(QuicIntervalSet<T>* other) { intervals_.swap(other->intervals_); } + + friend bool operator==(const QuicIntervalSet& a, const QuicIntervalSet& b) { + return a.Size() == b.Size() && + std::equal(a.begin(), a.end(), b.begin(), NonemptyIntervalEq()); + } + + friend bool operator!=(const QuicIntervalSet& a, const QuicIntervalSet& b) { + return !(a == b); + } + + private: + // Simple member-wise equality, since all intervals are non-empty. + struct NonemptyIntervalEq { + bool operator()(const value_type& a, const value_type& b) const { + return a.min() == b.min() && a.max() == b.max(); + } + }; + + // Removes overlapping ranges and coalesces adjacent intervals as needed. + void Compact(const typename Set::iterator& begin, + const typename Set::iterator& end); + + // Returns true if this set is valid (i.e. all intervals in it are non-empty, + // non-adjacent, and mutually disjoint). Currently this is used as an + // integrity check by the Intersection() and Difference() methods, but is only + // invoked for debug builds (via DCHECK). + bool Valid() const; + + // Finds the first interval that potentially intersects 'other'. + const_iterator FindIntersectionCandidate(const QuicIntervalSet& other) const; + + // Finds the first interval that potentially intersects 'interval'. + const_iterator FindIntersectionCandidate(const value_type& interval) const; + + // Helper for Intersection() and Difference(): Finds the next pair of + // intervals from 'x' and 'y' that intersect. 'mine' is an iterator + // over x->intervals_. 'theirs' is an iterator over y.intervals_. 'mine' + // and 'theirs' are advanced until an intersecting pair is found. + // Non-intersecting intervals (aka "holes") from x->intervals_ can be + // optionally erased by "on_hole". + template <typename X, typename Func> + static bool FindNextIntersectingPairImpl(X* x, + const QuicIntervalSet& y, + const_iterator* mine, + const_iterator* theirs, + Func on_hole); + + // The variant of the above method that doesn't mutate this QuicIntervalSet. + bool FindNextIntersectingPair(const QuicIntervalSet& other, + const_iterator* mine, + const_iterator* theirs) const { + return FindNextIntersectingPairImpl( + this, other, mine, theirs, + [](const QuicIntervalSet*, const_iterator, const_iterator) {}); + } + + // The variant of the above method that mutates this QuicIntervalSet by + // erasing holes. + bool FindNextIntersectingPairAndEraseHoles(const QuicIntervalSet& other, + const_iterator* mine, + const_iterator* theirs) { + return FindNextIntersectingPairImpl( + this, other, mine, theirs, + [](QuicIntervalSet* x, const_iterator from, const_iterator to) { + x->intervals_.erase(from, to); + }); + } + + // The representation for the intervals. The intervals in this set are + // non-empty, pairwise-disjoint, non-adjacent and ordered in ascending order + // by min(). + Set intervals_; +}; + +template <typename T> +auto operator<<(std::ostream& out, const QuicIntervalSet<T>& seq) + -> decltype(out << *seq.begin()) { + out << "{"; + for (const auto& interval : seq) { + out << " " << interval; + } + out << " }"; + + return out; +} + +template <typename T> +void swap(QuicIntervalSet<T>& x, QuicIntervalSet<T>& y); + +//============================================================================== +// Implementation details: Clients can stop reading here. + +template <typename T> +typename QuicIntervalSet<T>::value_type QuicIntervalSet<T>::SpanningInterval() + const { + value_type result; + if (!intervals_.empty()) { + result.SetMin(intervals_.begin()->min()); + result.SetMax(intervals_.rbegin()->max()); + } + return result; +} + +template <typename T> +void QuicIntervalSet<T>::Add(const value_type& interval) { + if (interval.Empty()) + return; + std::pair<typename Set::iterator, bool> ins = intervals_.insert(interval); + if (!ins.second) { + // This interval already exists. + return; + } + // Determine the minimal range that will have to be compacted. We know that + // the QuicIntervalSet was valid before the addition of the interval, so only + // need to start with the interval itself (although Compact takes an open + // range so begin needs to be the interval to the left). We don't know how + // many ranges this interval may cover, so we need to find the appropriate + // interval to end with on the right. + typename Set::iterator begin = ins.first; + if (begin != intervals_.begin()) + --begin; + const value_type target_end(interval.max(), interval.max()); + const typename Set::iterator end = intervals_.upper_bound(target_end); + Compact(begin, end); +} + +template <typename T> +bool QuicIntervalSet<T>::Contains(const T& value) const { + value_type tmp(value, value); + // Find the first interval with min() > value, then move back one step + const_iterator it = intervals_.upper_bound(tmp); + if (it == intervals_.begin()) + return false; + --it; + return it->Contains(value); +} + +template <typename T> +bool QuicIntervalSet<T>::Contains(const value_type& interval) const { + // Find the first interval with min() > value, then move back one step. + const_iterator it = intervals_.upper_bound(interval); + if (it == intervals_.begin()) + return false; + --it; + return it->Contains(interval); +} + +template <typename T> +bool QuicIntervalSet<T>::Contains(const QuicIntervalSet<T>& other) const { + if (!SpanningInterval().Contains(other.SpanningInterval())) { + return false; + } + + for (const_iterator i = other.begin(); i != other.end(); ++i) { + // If we don't contain the interval, can return false now. + if (!Contains(*i)) { + return false; + } + } + return true; +} + +// This method finds the interval that Contains() "value", if such an interval +// exists in the QuicIntervalSet. The way this is done is to locate the +// "candidate interval", the only interval that could *possibly* contain value, +// and test it using Contains(). The candidate interval is the interval with the +// largest min() having min() <= value. +// +// Determining the candidate interval takes a couple of steps. First, since the +// underlying std::set stores intervals, not values, we need to create a "probe +// interval" suitable for use as a search key. The probe interval used is +// [value, value). Now we can restate the problem as finding the largest +// interval in the QuicIntervalSet that is <= the probe interval. +// +// This restatement only works if the set's comparator behaves in a certain way. +// In particular it needs to order first by ascending min(), and then by +// descending max(). The comparator used by this library is defined in exactly +// this way. To see why descending max() is required, consider the following +// example. Assume an QuicIntervalSet containing these intervals: +// +// [0, 5) [10, 20) [50, 60) +// +// Consider searching for the value 15. The probe interval [15, 15) is created, +// and [10, 20) is identified as the largest interval in the set <= the probe +// interval. This is the correct interval needed for the Contains() test, which +// will then return true. +// +// Now consider searching for the value 30. The probe interval [30, 30) is +// created, and again [10, 20] is identified as the largest interval <= the +// probe interval. This is again the correct interval needed for the Contains() +// test, which in this case returns false. +// +// Finally, consider searching for the value 10. The probe interval [10, 10) is +// created. Here the ordering relationship between [10, 10) and [10, 20) becomes +// vitally important. If [10, 10) were to come before [10, 20), then [0, 5) +// would be the largest interval <= the probe, leading to the wrong choice of +// interval for the Contains() test. Therefore [10, 10) needs to come after +// [10, 20). The simplest way to make this work in the general case is to order +// by ascending min() but descending max(). In this ordering, the empty interval +// is larger than any non-empty interval with the same min(). The comparator +// used by this library is careful to induce this ordering. +// +// Another detail involves the choice of which std::set method to use to try to +// find the candidate interval. The most appropriate entry point is +// set::upper_bound(), which finds the smallest interval which is > the probe +// interval. The semantics of upper_bound() are slightly different from what we +// want (namely, to find the largest interval which is <= the probe interval) +// but they are close enough; the interval found by upper_bound() will always be +// one step past the interval we are looking for (if it exists) or at begin() +// (if it does not). Getting to the proper interval is a simple matter of +// decrementing the iterator. +template <typename T> +typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::Find( + const T& value) const { + value_type tmp(value, value); + const_iterator it = intervals_.upper_bound(tmp); + if (it == intervals_.begin()) + return intervals_.end(); + --it; + if (it->Contains(value)) + return it; + else + return intervals_.end(); +} + +// This method finds the interval that Contains() the interval "probe", if such +// an interval exists in the QuicIntervalSet. The way this is done is to locate +// the "candidate interval", the only interval that could *possibly* contain +// "probe", and test it using Contains(). The candidate interval is the largest +// interval that is <= the probe interval. +// +// The search for the candidate interval only works if the comparator used +// behaves in a certain way. In particular it needs to order first by ascending +// min(), and then by descending max(). The comparator used by this library is +// defined in exactly this way. To see why descending max() is required, +// consider the following example. Assume an QuicIntervalSet containing these +// intervals: +// +// [0, 5) [10, 20) [50, 60) +// +// Consider searching for the probe [15, 17). [10, 20) is the largest interval +// in the set which is <= the probe interval. This is the correct interval +// needed for the Contains() test, which will then return true, because [10, 20) +// contains [15, 17). +// +// Now consider searching for the probe [30, 32). Again [10, 20] is the largest +// interval <= the probe interval. This is again the correct interval needed for +// the Contains() test, which in this case returns false, because [10, 20) does +// not contain [30, 32). +// +// Finally, consider searching for the probe [10, 12). Here the ordering +// relationship between [10, 12) and [10, 20) becomes vitally important. If +// [10, 12) were to come before [10, 20), then [0, 5) would be the largest +// interval <= the probe, leading to the wrong choice of interval for the +// Contains() test. Therefore [10, 12) needs to come after [10, 20). The +// simplest way to make this work in the general case is to order by ascending +// min() but descending max(). In this ordering, given two intervals with the +// same min(), the wider one goes before the narrower one. The comparator used +// by this library is careful to induce this ordering. +// +// Another detail involves the choice of which std::set method to use to try to +// find the candidate interval. The most appropriate entry point is +// set::upper_bound(), which finds the smallest interval which is > the probe +// interval. The semantics of upper_bound() are slightly different from what we +// want (namely, to find the largest interval which is <= the probe interval) +// but they are close enough; the interval found by upper_bound() will always be +// one step past the interval we are looking for (if it exists) or at begin() +// (if it does not). Getting to the proper interval is a simple matter of +// decrementing the iterator. +template <typename T> +typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::Find( + const value_type& probe) const { + const_iterator it = intervals_.upper_bound(probe); + if (it == intervals_.begin()) + return intervals_.end(); + --it; + if (it->Contains(probe)) + return it; + else + return intervals_.end(); +} + +template <typename T> +typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::LowerBound( + const T& value) const { + const_iterator it = intervals_.lower_bound(value_type(value, value)); + if (it == intervals_.begin()) { + return it; + } + + // The previous intervals_.lower_bound() checking is essentially based on + // interval.min(), so we need to check whether the `value` is contained in + // the previous interval. + --it; + if (it->Contains(value)) { + return it; + } else { + return ++it; + } +} + +template <typename T> +typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::UpperBound( + const T& value) const { + return intervals_.upper_bound(value_type(value, value)); +} + +template <typename T> +bool QuicIntervalSet<T>::IsDisjoint(const value_type& interval) const { + if (interval.Empty()) + return true; + value_type tmp(interval.min(), interval.min()); + // Find the first interval with min() > interval.min() + const_iterator it = intervals_.upper_bound(tmp); + if (it != intervals_.end() && interval.max() > it->min()) + return false; + if (it == intervals_.begin()) + return true; + --it; + return it->max() <= interval.min(); +} + +template <typename T> +void QuicIntervalSet<T>::Union(const QuicIntervalSet& other) { + intervals_.insert(other.begin(), other.end()); + Compact(intervals_.begin(), intervals_.end()); +} + +template <typename T> +typename QuicIntervalSet<T>::const_iterator +QuicIntervalSet<T>::FindIntersectionCandidate( + const QuicIntervalSet& other) const { + return FindIntersectionCandidate(*other.intervals_.begin()); +} + +template <typename T> +typename QuicIntervalSet<T>::const_iterator +QuicIntervalSet<T>::FindIntersectionCandidate( + const value_type& interval) const { + // Use upper_bound to efficiently find the first interval in intervals_ + // where min() is greater than interval.min(). If the result + // isn't the beginning of intervals_ then move backwards one interval since + // the interval before it is the first candidate where max() may be + // greater than interval.min(). + // In other words, no interval before that can possibly intersect with any + // of other.intervals_. + const_iterator mine = intervals_.upper_bound(interval); + if (mine != intervals_.begin()) { + --mine; + } + return mine; +} + +template <typename T> +template <typename X, typename Func> +bool QuicIntervalSet<T>::FindNextIntersectingPairImpl(X* x, + const QuicIntervalSet& y, + const_iterator* mine, + const_iterator* theirs, + Func on_hole) { + CHECK(x != nullptr); + if ((*mine == x->intervals_.end()) || (*theirs == y.intervals_.end())) { + return false; + } + while (!(**mine).Intersects(**theirs)) { + const_iterator erase_first = *mine; + // Skip over intervals in 'mine' that don't reach 'theirs'. + while (*mine != x->intervals_.end() && (**mine).max() <= (**theirs).min()) { + ++(*mine); + } + on_hole(x, erase_first, *mine); + // We're done if the end of intervals_ is reached. + if (*mine == x->intervals_.end()) { + return false; + } + // Skip over intervals 'theirs' that don't reach 'mine'. + while (*theirs != y.intervals_.end() && + (**theirs).max() <= (**mine).min()) { + ++(*theirs); + } + // If the end of other.intervals_ is reached, we're done. + if (*theirs == y.intervals_.end()) { + on_hole(x, *mine, x->intervals_.end()); + return false; + } + } + return true; +} + +template <typename T> +void QuicIntervalSet<T>::Intersection(const QuicIntervalSet& other) { + if (!SpanningInterval().Intersects(other.SpanningInterval())) { + intervals_.clear(); + return; + } + + const_iterator mine = FindIntersectionCandidate(other); + // Remove any intervals that cannot possibly intersect with other.intervals_. + intervals_.erase(intervals_.begin(), mine); + const_iterator theirs = other.FindIntersectionCandidate(*this); + + while (FindNextIntersectingPairAndEraseHoles(other, &mine, &theirs)) { + // OK, *mine and *theirs intersect. Now, we find the largest + // span of intervals in other (starting at theirs) - say [a..b] + // - that intersect *mine, and we replace *mine with (*mine + // intersect x) for all x in [a..b] Note that subsequent + // intervals in this can't intersect any intervals in [a..b) -- + // they may only intersect b or subsequent intervals in other. + value_type i(*mine); + intervals_.erase(mine); + mine = intervals_.end(); + value_type intersection; + while (theirs != other.intervals_.end() && + i.Intersects(*theirs, &intersection)) { + std::pair<typename Set::iterator, bool> ins = + intervals_.insert(intersection); + DCHECK(ins.second); + mine = ins.first; + ++theirs; + } + DCHECK(mine != intervals_.end()); + --theirs; + ++mine; + } + DCHECK(Valid()); +} + +template <typename T> +bool QuicIntervalSet<T>::Intersects(const QuicIntervalSet& other) const { + if (!SpanningInterval().Intersects(other.SpanningInterval())) { + return false; + } + + const_iterator mine = FindIntersectionCandidate(other); + if (mine == intervals_.end()) { + return false; + } + const_iterator theirs = other.FindIntersectionCandidate(*mine); + + return FindNextIntersectingPair(other, &mine, &theirs); +} + +template <typename T> +void QuicIntervalSet<T>::Difference(const value_type& interval) { + if (!SpanningInterval().Intersects(interval)) { + return; + } + Difference(QuicIntervalSet<T>(interval)); +} + +template <typename T> +void QuicIntervalSet<T>::Difference(const T& min, const T& max) { + Difference(value_type(min, max)); +} + +template <typename T> +void QuicIntervalSet<T>::Difference(const QuicIntervalSet& other) { + if (!SpanningInterval().Intersects(other.SpanningInterval())) { + return; + } + + const_iterator mine = FindIntersectionCandidate(other); + // If no interval in mine reaches the first interval of theirs then we're + // done. + if (mine == intervals_.end()) { + return; + } + const_iterator theirs = other.FindIntersectionCandidate(*this); + + while (FindNextIntersectingPair(other, &mine, &theirs)) { + // At this point *mine and *theirs overlap. Remove mine from + // intervals_ and replace it with the possibly two intervals that are + // the difference between mine and theirs. + value_type i(*mine); + intervals_.erase(mine++); + value_type lo; + value_type hi; + i.Difference(*theirs, &lo, &hi); + + if (!lo.Empty()) { + // We have a low end. This can't intersect anything else. + std::pair<typename Set::iterator, bool> ins = intervals_.insert(lo); + DCHECK(ins.second); + } + + if (!hi.Empty()) { + std::pair<typename Set::iterator, bool> ins = intervals_.insert(hi); + DCHECK(ins.second); + mine = ins.first; + } + } + DCHECK(Valid()); +} + +template <typename T> +void QuicIntervalSet<T>::Complement(const T& min, const T& max) { + QuicIntervalSet<T> span(min, max); + span.Difference(*this); + intervals_.swap(span.intervals_); +} + +template <typename T> +QuicString QuicIntervalSet<T>::ToString() const { + std::ostringstream os; + os << *this; + return os.str(); +} + +// This method compacts the QuicIntervalSet, merging pairs of overlapping +// intervals into a single interval. In the steady state, the QuicIntervalSet +// does not contain any such pairs. However, the way the Union() and Add() +// methods work is to temporarily put the QuicIntervalSet into such a state and +// then to call Compact() to "fix it up" so that it is no longer in that state. +// +// Compact() needs the interval set to allow two intervals [a,b) and [a,c) +// (having the same min() but different max()) to briefly coexist in the set at +// the same time, and be adjacent to each other, so that they can be efficiently +// located and merged into a single interval. This state would be impossible +// with a comparator which only looked at min(), as such a comparator would +// consider such pairs equal. Fortunately, the comparator used by +// QuicIntervalSet does exactly what is needed, ordering first by ascending +// min(), then by descending max(). +template <typename T> +void QuicIntervalSet<T>::Compact(const typename Set::iterator& begin, + const typename Set::iterator& end) { + if (begin == end) + return; + typename Set::iterator next = begin; + typename Set::iterator prev = begin; + typename Set::iterator it = begin; + ++it; + ++next; + while (it != end) { + ++next; + if (prev->max() >= it->min()) { + // Overlapping / coalesced range; merge the two intervals. + T min = prev->min(); + T max = std::max(prev->max(), it->max()); + value_type i(min, max); + intervals_.erase(prev); + intervals_.erase(it); + std::pair<typename Set::iterator, bool> ins = intervals_.insert(i); + DCHECK(ins.second); + prev = ins.first; + } else { + prev = it; + } + it = next; + } +} + +template <typename T> +bool QuicIntervalSet<T>::Valid() const { + const_iterator prev = end(); + for (const_iterator it = begin(); it != end(); ++it) { + // invalid or empty interval. + if (it->min() >= it->max()) + return false; + // Not sorted, not disjoint, or adjacent. + if (prev != end() && prev->max() >= it->min()) + return false; + prev = it; + } + return true; +} + +template <typename T> +void swap(QuicIntervalSet<T>& x, QuicIntervalSet<T>& y) { + x.Swap(&y); +} + +// This comparator orders intervals first by ascending min() and then by +// descending max(). Readers who are satisified with that explanation can stop +// reading here. The remainder of this comment is for the benefit of future +// maintainers of this library. +// +// The reason for this ordering is that this comparator has to serve two +// masters. First, it has to maintain the intervals in its internal set in the +// order that clients expect to see them. Clients see these intervals via the +// iterators provided by begin()/end() or as a result of invoking Get(). For +// this reason, the comparator orders intervals by ascending min(). +// +// If client iteration were the only consideration, then ordering by ascending +// min() would be good enough. This is because the intervals in the +// QuicIntervalSet are non-empty, non-adjacent, and mutually disjoint; such +// intervals happen to always have disjoint min() values, so such a comparator +// would never even have to look at max() in order to work correctly for this +// class. +// +// However, in addition to ordering by ascending min(), this comparator also has +// a second responsibility: satisfying the special needs of this library's +// peculiar internal implementation. These needs require the comparator to order +// first by ascending min() and then by descending max(). The best way to +// understand why this is so is to check out the comments associated with the +// Find() and Compact() methods. +template <typename T> +bool QuicIntervalSet<T>::IntervalLess::operator()(const value_type& a, + const value_type& b) const { + return a.min() < b.min() || (a.min() == b.min() && a.max() > b.max()); +} + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_
diff --git a/quic/core/quic_interval_set_test.cc b/quic/core/quic_interval_set_test.cc new file mode 100644 index 0000000..e823e4f --- /dev/null +++ b/quic/core/quic_interval_set_test.cc
@@ -0,0 +1,1009 @@ +// Copyright (c) 2019 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/quic_interval_set.h" + +#include <stdarg.h> +#include <algorithm> +#include <iostream> +#include <iterator> +#include <limits> +#include <vector> + +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +using ::testing::ElementsAreArray; + +class QuicIntervalSetTest : public QuicTest { + protected: + virtual void SetUp() { + // Initialize two QuicIntervalSets for union, intersection, and difference + // tests + is.Add(100, 200); + is.Add(300, 400); + is.Add(500, 600); + is.Add(700, 800); + is.Add(900, 1000); + is.Add(1100, 1200); + is.Add(1300, 1400); + is.Add(1500, 1600); + is.Add(1700, 1800); + is.Add(1900, 2000); + is.Add(2100, 2200); + + // Lots of different cases: + other.Add(50, 70); // disjoint, at the beginning + other.Add(2250, 2270); // disjoint, at the end + other.Add(650, 670); // disjoint, in the middle + other.Add(350, 360); // included + other.Add(370, 380); // also included (two at once) + other.Add(470, 530); // overlaps low end + other.Add(770, 830); // overlaps high end + other.Add(870, 900); // meets at low end + other.Add(1200, 1230); // meets at high end + other.Add(1270, 1830); // overlaps multiple ranges + } + + virtual void TearDown() { + is.Clear(); + EXPECT_TRUE(is.Empty()); + other.Clear(); + EXPECT_TRUE(other.Empty()); + } + QuicIntervalSet<int> is; + QuicIntervalSet<int> other; +}; + +TEST_F(QuicIntervalSetTest, IsDisjoint) { + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(0, 99))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(0, 100))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 200))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 299))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(400, 407))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(405, 499))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(2300, 2300))); + EXPECT_TRUE( + is.IsDisjoint(QuicInterval<int>(2300, std::numeric_limits<int>::max()))); + EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(100, 105))); + EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(199, 300))); + EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(250, 450))); + EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(299, 400))); + EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(250, 2000))); + EXPECT_FALSE( + is.IsDisjoint(QuicInterval<int>(2199, std::numeric_limits<int>::max()))); + // Empty intervals. + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(90, 90))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(100, 100))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(100, 90))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(150, 150))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 200))); + EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(400, 300))); +} + +// Base helper method for verifying the contents of an interval set. +// Returns true iff <is> contains <count> intervals whose successive +// endpoints match the sequence of args in <ap>: +static bool VA_Check(const QuicIntervalSet<int>& is, int count, va_list ap) { + std::vector<QuicInterval<int>> intervals(is.begin(), is.end()); + if (count != static_cast<int>(intervals.size())) { + LOG(ERROR) << "Expected " << count << " intervals, got " << intervals.size() + << ": " << is; + return false; + } + if (count != static_cast<int>(is.Size())) { + LOG(ERROR) << "Expected " << count << " intervals, got Size " << is.Size() + << ": " << is; + return false; + } + bool result = true; + for (int i = 0; i < count; i++) { + int min = va_arg(ap, int); + int max = va_arg(ap, int); + if (min != intervals[i].min() || max != intervals[i].max()) { + LOG(ERROR) << "Expected: [" << min << ", " << max << ") got " + << intervals[i] << " in " << is; + result = false; + } + } + return result; +} + +static bool Check(const QuicIntervalSet<int>& is, int count, ...) { + va_list ap; + va_start(ap, count); + const bool result = VA_Check(is, count, ap); + va_end(ap); + return result; +} + +// Some helper functions for testing Contains and Find, which are logically the +// same. +static void TestContainsAndFind(const QuicIntervalSet<int>& is, int value) { + EXPECT_TRUE(is.Contains(value)) << "Set does not contain " << value; + auto it = is.Find(value); + EXPECT_NE(it, is.end()) << "No iterator to interval containing " << value; + EXPECT_TRUE(it->Contains(value)) << "Iterator does not contain " << value; +} + +static void TestContainsAndFind(const QuicIntervalSet<int>& is, + int min, + int max) { + EXPECT_TRUE(is.Contains(min, max)) + << "Set does not contain interval with min " << min << "and max " << max; + auto it = is.Find(min, max); + EXPECT_NE(it, is.end()) << "No iterator to interval with min " << min + << "and max " << max; + EXPECT_TRUE(it->Contains(QuicInterval<int>(min, max))) + << "Iterator does not contain interval with min " << min << "and max " + << max; +} + +static void TestNotContainsAndFind(const QuicIntervalSet<int>& is, int value) { + EXPECT_FALSE(is.Contains(value)) << "Set contains " << value; + auto it = is.Find(value); + EXPECT_EQ(it, is.end()) << "There is iterator to interval containing " + << value; +} + +static void TestNotContainsAndFind(const QuicIntervalSet<int>& is, + int min, + int max) { + EXPECT_FALSE(is.Contains(min, max)) + << "Set contains interval with min " << min << "and max " << max; + auto it = is.Find(min, max); + EXPECT_EQ(it, is.end()) << "There is iterator to interval with min " << min + << "and max " << max; +} + +TEST_F(QuicIntervalSetTest, AddOptimizedForAppend) { + QuicIntervalSet<int> empty_one, empty_two; + empty_one.AddOptimizedForAppend(QuicInterval<int>(0, 99)); + EXPECT_TRUE(Check(empty_one, 1, 0, 99)); + + empty_two.AddOptimizedForAppend(1, 50); + EXPECT_TRUE(Check(empty_two, 1, 1, 50)); + + QuicIntervalSet<int> iset; + iset.AddOptimizedForAppend(100, 150); + iset.AddOptimizedForAppend(200, 250); + EXPECT_TRUE(Check(iset, 2, 100, 150, 200, 250)); + + iset.AddOptimizedForAppend(199, 200); + EXPECT_TRUE(Check(iset, 2, 100, 150, 199, 250)); + + iset.AddOptimizedForAppend(251, 260); + EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 260)); + + iset.AddOptimizedForAppend(252, 260); + EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 260)); + + iset.AddOptimizedForAppend(252, 300); + EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 300)); + + iset.AddOptimizedForAppend(300, 350); + EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 350)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetBasic) { + // Test Add, Get, Contains and Find + QuicIntervalSet<int> iset; + EXPECT_TRUE(iset.Empty()); + EXPECT_EQ(0u, iset.Size()); + iset.Add(100, 200); + EXPECT_FALSE(iset.Empty()); + EXPECT_EQ(1u, iset.Size()); + iset.Add(100, 150); + iset.Add(150, 200); + iset.Add(130, 170); + iset.Add(90, 150); + iset.Add(170, 220); + iset.Add(300, 400); + iset.Add(250, 450); + EXPECT_FALSE(iset.Empty()); + EXPECT_EQ(2u, iset.Size()); + EXPECT_TRUE(Check(iset, 2, 90, 220, 250, 450)); + + // Test two intervals with a.max == b.min, that will just join up. + iset.Clear(); + iset.Add(100, 200); + iset.Add(200, 300); + EXPECT_FALSE(iset.Empty()); + EXPECT_EQ(1u, iset.Size()); + EXPECT_TRUE(Check(iset, 1, 100, 300)); + + // Test adding two sets together. + iset.Clear(); + QuicIntervalSet<int> iset_add; + iset.Add(100, 200); + iset.Add(100, 150); + iset.Add(150, 200); + iset.Add(130, 170); + iset_add.Add(90, 150); + iset_add.Add(170, 220); + iset_add.Add(300, 400); + iset_add.Add(250, 450); + + iset.Union(iset_add); + EXPECT_FALSE(iset.Empty()); + EXPECT_EQ(2u, iset.Size()); + EXPECT_TRUE(Check(iset, 2, 90, 220, 250, 450)); + + // Test begin()/end(), and rbegin()/rend() + // to iterate over intervals. + { + std::vector<QuicInterval<int>> expected(iset.begin(), iset.end()); + + std::vector<QuicInterval<int>> actual1; + std::copy(iset.begin(), iset.end(), back_inserter(actual1)); + ASSERT_EQ(expected.size(), actual1.size()); + + std::vector<QuicInterval<int>> actual2; + std::copy(iset.begin(), iset.end(), back_inserter(actual2)); + ASSERT_EQ(expected.size(), actual2.size()); + + for (size_t i = 0; i < expected.size(); i++) { + EXPECT_EQ(expected[i].min(), actual1[i].min()); + EXPECT_EQ(expected[i].max(), actual1[i].max()); + + EXPECT_EQ(expected[i].min(), actual2[i].min()); + EXPECT_EQ(expected[i].max(), actual2[i].max()); + } + + // Ensure that the rbegin()/rend() iterators correctly yield the intervals + // in reverse order. + EXPECT_THAT(std::vector<QuicInterval<int>>(iset.rbegin(), iset.rend()), + ElementsAreArray(expected.rbegin(), expected.rend())); + } + + TestNotContainsAndFind(iset, 89); + TestContainsAndFind(iset, 90); + TestContainsAndFind(iset, 120); + TestContainsAndFind(iset, 219); + TestNotContainsAndFind(iset, 220); + TestNotContainsAndFind(iset, 235); + TestNotContainsAndFind(iset, 249); + TestContainsAndFind(iset, 250); + TestContainsAndFind(iset, 300); + TestContainsAndFind(iset, 449); + TestNotContainsAndFind(iset, 450); + TestNotContainsAndFind(iset, 451); + + TestNotContainsAndFind(iset, 50, 60); + TestNotContainsAndFind(iset, 50, 90); + TestNotContainsAndFind(iset, 50, 200); + TestNotContainsAndFind(iset, 90, 90); + TestContainsAndFind(iset, 90, 200); + TestContainsAndFind(iset, 100, 200); + TestContainsAndFind(iset, 100, 220); + TestNotContainsAndFind(iset, 100, 221); + TestNotContainsAndFind(iset, 220, 220); + TestNotContainsAndFind(iset, 240, 300); + TestContainsAndFind(iset, 250, 300); + TestContainsAndFind(iset, 260, 300); + TestContainsAndFind(iset, 300, 450); + TestNotContainsAndFind(iset, 300, 451); + + QuicIntervalSet<int> iset_contains; + iset_contains.Add(50, 90); + EXPECT_FALSE(iset.Contains(iset_contains)); + iset_contains.Clear(); + + iset_contains.Add(90, 200); + EXPECT_TRUE(iset.Contains(iset_contains)); + iset_contains.Add(100, 200); + EXPECT_TRUE(iset.Contains(iset_contains)); + iset_contains.Add(100, 220); + EXPECT_TRUE(iset.Contains(iset_contains)); + iset_contains.Add(250, 300); + EXPECT_TRUE(iset.Contains(iset_contains)); + iset_contains.Add(300, 450); + EXPECT_TRUE(iset.Contains(iset_contains)); + iset_contains.Add(300, 451); + EXPECT_FALSE(iset.Contains(iset_contains)); + EXPECT_FALSE(iset.Contains(QuicInterval<int>())); + EXPECT_FALSE(iset.Contains(QuicIntervalSet<int>())); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetContainsEmpty) { + const QuicIntervalSet<int> empty; + const QuicIntervalSet<int> other_empty; + const QuicIntervalSet<int> non_empty({{10, 20}, {40, 50}}); + EXPECT_FALSE(empty.Contains(empty)); + EXPECT_FALSE(empty.Contains(other_empty)); + EXPECT_FALSE(empty.Contains(non_empty)); + EXPECT_FALSE(non_empty.Contains(empty)); +} + +TEST_F(QuicIntervalSetTest, Equality) { + QuicIntervalSet<int> is_copy = is; + EXPECT_EQ(is, is); + EXPECT_EQ(is, is_copy); + EXPECT_NE(is, other); + EXPECT_NE(is, QuicIntervalSet<int>()); + EXPECT_EQ(QuicIntervalSet<int>(), QuicIntervalSet<int>()); +} + +TEST_F(QuicIntervalSetTest, LowerAndUpperBound) { + QuicIntervalSet<int> intervals; + intervals.Add(10, 20); + intervals.Add(30, 40); + + // [10, 20) [30, 40) end + // ^ LowerBound(5) + // ^ LowerBound(10) + // ^ LowerBound(15) + // ^ LowerBound(20) + // ^ LowerBound(25) + // ^ LowerBound(30) + // ^ LowerBound(35) + // ^ LowerBound(40) + // ^ LowerBound(50) + EXPECT_EQ(intervals.LowerBound(5)->min(), 10); + EXPECT_EQ(intervals.LowerBound(10)->min(), 10); + EXPECT_EQ(intervals.LowerBound(15)->min(), 10); + EXPECT_EQ(intervals.LowerBound(20)->min(), 30); + EXPECT_EQ(intervals.LowerBound(25)->min(), 30); + EXPECT_EQ(intervals.LowerBound(30)->min(), 30); + EXPECT_EQ(intervals.LowerBound(35)->min(), 30); + EXPECT_EQ(intervals.LowerBound(40), intervals.end()); + EXPECT_EQ(intervals.LowerBound(50), intervals.end()); + + // [10, 20) [30, 40) end + // ^ UpperBound(5) + // ^ UpperBound(10) + // ^ UpperBound(15) + // ^ UpperBound(20) + // ^ UpperBound(25) + // ^ UpperBound(30) + // ^ UpperBound(35) + // ^ UpperBound(40) + // ^ UpperBound(50) + EXPECT_EQ(intervals.UpperBound(5)->min(), 10); + EXPECT_EQ(intervals.UpperBound(10)->min(), 30); + EXPECT_EQ(intervals.UpperBound(15)->min(), 30); + EXPECT_EQ(intervals.UpperBound(20)->min(), 30); + EXPECT_EQ(intervals.UpperBound(25)->min(), 30); + EXPECT_EQ(intervals.UpperBound(30), intervals.end()); + EXPECT_EQ(intervals.UpperBound(35), intervals.end()); + EXPECT_EQ(intervals.UpperBound(40), intervals.end()); + EXPECT_EQ(intervals.UpperBound(50), intervals.end()); +} + +TEST_F(QuicIntervalSetTest, SpanningInterval) { + // Spanning interval of an empty set is empty: + { + QuicIntervalSet<int> iset; + const QuicInterval<int>& ival = iset.SpanningInterval(); + EXPECT_TRUE(ival.Empty()); + } + + // Spanning interval of a set with one interval is that interval: + { + QuicIntervalSet<int> iset; + iset.Add(100, 200); + const QuicInterval<int>& ival = iset.SpanningInterval(); + EXPECT_EQ(100, ival.min()); + EXPECT_EQ(200, ival.max()); + } + + // Spanning interval of a set with multiple elements is determined + // by the endpoints of the first and last element: + { + const QuicInterval<int>& ival = is.SpanningInterval(); + EXPECT_EQ(100, ival.min()); + EXPECT_EQ(2200, ival.max()); + } + { + const QuicInterval<int>& ival = other.SpanningInterval(); + EXPECT_EQ(50, ival.min()); + EXPECT_EQ(2270, ival.max()); + } +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetUnion) { + is.Union(other); + EXPECT_TRUE(Check(is, 12, 50, 70, 100, 200, 300, 400, 470, 600, 650, 670, 700, + 830, 870, 1000, 1100, 1230, 1270, 1830, 1900, 2000, 2100, + 2200, 2250, 2270)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersection) { + EXPECT_TRUE(is.Intersects(other)); + EXPECT_TRUE(other.Intersects(is)); + is.Intersection(other); + EXPECT_TRUE(Check(is, 7, 350, 360, 370, 380, 500, 530, 770, 800, 1300, 1400, + 1500, 1600, 1700, 1800)); + EXPECT_TRUE(is.Intersects(other)); + EXPECT_TRUE(other.Intersects(is)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionBothEmpty) { + QuicIntervalSet<QuicString> mine, theirs; + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionEmptyMine) { + QuicIntervalSet<QuicString> mine; + QuicIntervalSet<QuicString> theirs("a", "b"); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionEmptyTheirs) { + QuicIntervalSet<QuicString> mine("a", "b"); + QuicIntervalSet<QuicString> theirs; + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionTheirsBeforeMine) { + QuicIntervalSet<QuicString> mine("y", "z"); + QuicIntervalSet<QuicString> theirs; + theirs.Add("a", "b"); + theirs.Add("c", "d"); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionMineBeforeTheirs) { + QuicIntervalSet<QuicString> mine; + mine.Add("a", "b"); + mine.Add("c", "d"); + QuicIntervalSet<QuicString> theirs("y", "z"); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, + QuicIntervalSetIntersectionTheirsBeforeMineInt64Singletons) { + QuicIntervalSet<int64_t> mine({{10, 15}}); + QuicIntervalSet<int64_t> theirs({{-20, -5}}); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, + QuicIntervalSetIntersectionMineBeforeTheirsIntSingletons) { + QuicIntervalSet<int> mine({{10, 15}}); + QuicIntervalSet<int> theirs({{90, 95}}); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionTheirsBetweenMine) { + QuicIntervalSet<int64_t> mine({{0, 5}, {40, 50}}); + QuicIntervalSet<int64_t> theirs({{10, 15}}); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionMineBetweenTheirs) { + QuicIntervalSet<int> mine({{20, 25}}); + QuicIntervalSet<int> theirs({{10, 15}, {30, 32}}); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionAlternatingIntervals) { + QuicIntervalSet<int> mine, theirs; + mine.Add(10, 20); + mine.Add(40, 50); + mine.Add(60, 70); + theirs.Add(25, 39); + theirs.Add(55, 59); + theirs.Add(75, 79); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(mine.Empty()); + EXPECT_FALSE(mine.Intersects(theirs)); + EXPECT_FALSE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, + QuicIntervalSetIntersectionAdjacentAlternatingNonIntersectingIntervals) { + // Make sure that intersection with adjacent interval set is empty. + const QuicIntervalSet<int> x1({{0, 10}}); + const QuicIntervalSet<int> y1({{-50, 0}, {10, 95}}); + + QuicIntervalSet<int> result1 = x1; + result1.Intersection(y1); + EXPECT_TRUE(result1.Empty()) << result1; + + const QuicIntervalSet<int16_t> x2({{0, 10}, {20, 30}, {40, 90}}); + const QuicIntervalSet<int16_t> y2( + {{-50, -40}, {-2, 0}, {10, 20}, {32, 40}, {90, 95}}); + + QuicIntervalSet<int16_t> result2 = x2; + result2.Intersection(y2); + EXPECT_TRUE(result2.Empty()) << result2; + + const QuicIntervalSet<int64_t> x3({{-1, 5}, {5, 10}}); + const QuicIntervalSet<int64_t> y3({{-10, -1}, {10, 95}}); + + QuicIntervalSet<int64_t> result3 = x3; + result3.Intersection(y3); + EXPECT_TRUE(result3.Empty()) << result3; +} + +TEST_F(QuicIntervalSetTest, + QuicIntervalSetIntersectionAlternatingIntersectingIntervals) { + const QuicIntervalSet<int> x1({{0, 10}}); + const QuicIntervalSet<int> y1({{-50, 1}, {9, 95}}); + const QuicIntervalSet<int> expected_result1({{0, 1}, {9, 10}}); + + QuicIntervalSet<int> result1 = x1; + result1.Intersection(y1); + EXPECT_EQ(result1, expected_result1); + + const QuicIntervalSet<int16_t> x2({{0, 10}, {20, 30}, {40, 90}}); + const QuicIntervalSet<int16_t> y2( + {{-50, -40}, {-2, 2}, {9, 21}, {32, 41}, {85, 95}}); + const QuicIntervalSet<int16_t> expected_result2( + {{0, 2}, {9, 10}, {20, 21}, {40, 41}, {85, 90}}); + + QuicIntervalSet<int16_t> result2 = x2; + result2.Intersection(y2); + EXPECT_EQ(result2, expected_result2); + + const QuicIntervalSet<int64_t> x3({{-1, 5}, {5, 10}}); + const QuicIntervalSet<int64_t> y3({{-10, 3}, {4, 95}}); + const QuicIntervalSet<int64_t> expected_result3({{-1, 3}, {4, 10}}); + + QuicIntervalSet<int64_t> result3 = x3; + result3.Intersection(y3); + EXPECT_EQ(result3, expected_result3); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionIdentical) { + QuicIntervalSet<int> copy(is); + EXPECT_TRUE(copy.Intersects(is)); + EXPECT_TRUE(is.Intersects(copy)); + is.Intersection(copy); + EXPECT_EQ(copy, is); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionSuperset) { + QuicIntervalSet<int> mine(-1, 10000); + EXPECT_TRUE(mine.Intersects(is)); + EXPECT_TRUE(is.Intersects(mine)); + mine.Intersection(is); + EXPECT_EQ(is, mine); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionSubset) { + QuicIntervalSet<int> copy(is); + QuicIntervalSet<int> theirs(-1, 10000); + EXPECT_TRUE(copy.Intersects(theirs)); + EXPECT_TRUE(theirs.Intersects(copy)); + is.Intersection(theirs); + EXPECT_EQ(copy, is); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionLargeSet) { + QuicIntervalSet<int> mine, theirs; + // mine: [0, 9), [10, 19), ..., [990, 999) + for (int i = 0; i < 1000; i += 10) { + mine.Add(i, i + 9); + } + + theirs.Add(500, 520); + theirs.Add(535, 545); + theirs.Add(801, 809); + EXPECT_TRUE(mine.Intersects(theirs)); + EXPECT_TRUE(theirs.Intersects(mine)); + mine.Intersection(theirs); + EXPECT_TRUE(Check(mine, 5, 500, 509, 510, 519, 535, 539, 540, 545, 801, 809)); + EXPECT_TRUE(mine.Intersects(theirs)); + EXPECT_TRUE(theirs.Intersects(mine)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifference) { + is.Difference(other); + EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600, + 700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200)); + QuicIntervalSet<int> copy = is; + is.Difference(copy); + EXPECT_TRUE(is.Empty()); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceSingleBounds) { + std::vector<QuicInterval<int>> ivals(other.begin(), other.end()); + for (const QuicInterval<int>& ival : ivals) { + is.Difference(ival.min(), ival.max()); + } + EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600, + 700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceSingleInterval) { + std::vector<QuicInterval<int>> ivals(other.begin(), other.end()); + for (const QuicInterval<int>& ival : ivals) { + is.Difference(ival); + } + EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600, + 700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceAlternatingIntervals) { + QuicIntervalSet<int> mine, theirs; + mine.Add(10, 20); + mine.Add(40, 50); + mine.Add(60, 70); + theirs.Add(25, 39); + theirs.Add(55, 59); + theirs.Add(75, 79); + + mine.Difference(theirs); + EXPECT_TRUE(Check(mine, 3, 10, 20, 40, 50, 60, 70)); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceEmptyMine) { + QuicIntervalSet<QuicString> mine, theirs; + theirs.Add("a", "b"); + + mine.Difference(theirs); + EXPECT_TRUE(mine.Empty()); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceEmptyTheirs) { + QuicIntervalSet<QuicString> mine, theirs; + mine.Add("a", "b"); + + mine.Difference(theirs); + EXPECT_EQ(1u, mine.Size()); + EXPECT_EQ("a", mine.begin()->min()); + EXPECT_EQ("b", mine.begin()->max()); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceTheirsBeforeMine) { + QuicIntervalSet<QuicString> mine, theirs; + mine.Add("y", "z"); + theirs.Add("a", "b"); + + mine.Difference(theirs); + EXPECT_EQ(1u, mine.Size()); + EXPECT_EQ("y", mine.begin()->min()); + EXPECT_EQ("z", mine.begin()->max()); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceMineBeforeTheirs) { + QuicIntervalSet<QuicString> mine, theirs; + mine.Add("a", "b"); + theirs.Add("y", "z"); + + mine.Difference(theirs); + EXPECT_EQ(1u, mine.Size()); + EXPECT_EQ("a", mine.begin()->min()); + EXPECT_EQ("b", mine.begin()->max()); +} + +TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceIdentical) { + QuicIntervalSet<QuicString> mine; + mine.Add("a", "b"); + mine.Add("c", "d"); + QuicIntervalSet<QuicString> theirs(mine); + + mine.Difference(theirs); + EXPECT_TRUE(mine.Empty()); +} + +TEST_F(QuicIntervalSetTest, EmptyComplement) { + // The complement of an empty set is the input interval: + QuicIntervalSet<int> iset; + iset.Complement(100, 200); + EXPECT_TRUE(Check(iset, 1, 100, 200)); +} + +TEST(QuicIntervalSetMultipleCompactionTest, OuterCovering) { + QuicIntervalSet<int> iset; + // First add a bunch of disjoint ranges + iset.Add(100, 150); + iset.Add(200, 250); + iset.Add(300, 350); + iset.Add(400, 450); + EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450)); + // Now add a big range that covers all of these ranges + iset.Add(0, 500); + EXPECT_TRUE(Check(iset, 1, 0, 500)); +} + +TEST(QuicIntervalSetMultipleCompactionTest, InnerCovering) { + QuicIntervalSet<int> iset; + // First add a bunch of disjoint ranges + iset.Add(100, 150); + iset.Add(200, 250); + iset.Add(300, 350); + iset.Add(400, 450); + EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450)); + // Now add a big range that partially covers the left and right most ranges. + iset.Add(125, 425); + EXPECT_TRUE(Check(iset, 1, 100, 450)); +} + +TEST(QuicIntervalSetMultipleCompactionTest, LeftCovering) { + QuicIntervalSet<int> iset; + // First add a bunch of disjoint ranges + iset.Add(100, 150); + iset.Add(200, 250); + iset.Add(300, 350); + iset.Add(400, 450); + EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450)); + // Now add a big range that partially covers the left most range. + iset.Add(125, 500); + EXPECT_TRUE(Check(iset, 1, 100, 500)); +} + +TEST(QuicIntervalSetMultipleCompactionTest, RightCovering) { + QuicIntervalSet<int> iset; + // First add a bunch of disjoint ranges + iset.Add(100, 150); + iset.Add(200, 250); + iset.Add(300, 350); + iset.Add(400, 450); + EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450)); + // Now add a big range that partially covers the right most range. + iset.Add(0, 425); + EXPECT_TRUE(Check(iset, 1, 0, 450)); +} + +// Helper method for testing and verifying the results of a one-interval +// completement case. +static bool CheckOneComplement(int add_min, + int add_max, + int comp_min, + int comp_max, + int count, + ...) { + QuicIntervalSet<int> iset; + iset.Add(add_min, add_max); + iset.Complement(comp_min, comp_max); + bool result = true; + va_list ap; + va_start(ap, count); + if (!VA_Check(iset, count, ap)) { + result = false; + } + va_end(ap); + return result; +} + +TEST_F(QuicIntervalSetTest, SingleIntervalComplement) { + // Verify the complement of a set with one interval (i): + // |----- i -----| + // |----- args -----| + EXPECT_TRUE(CheckOneComplement(0, 10, 50, 150, 1, 50, 150)); + + // |----- i -----| + // |----- args -----| + EXPECT_TRUE(CheckOneComplement(50, 150, 0, 100, 1, 0, 50)); + + // |----- i -----| + // |----- args -----| + EXPECT_TRUE(CheckOneComplement(50, 150, 50, 150, 0)); + + // |---------- i ----------| + // |----- args -----| + EXPECT_TRUE(CheckOneComplement(50, 500, 100, 300, 0)); + + // |----- i -----| + // |---------- args ----------| + EXPECT_TRUE(CheckOneComplement(50, 500, 0, 800, 2, 0, 50, 500, 800)); + + // |----- i -----| + // |----- args -----| + EXPECT_TRUE(CheckOneComplement(50, 150, 100, 300, 1, 150, 300)); + + // |----- i -----| + // |----- args -----| + EXPECT_TRUE(CheckOneComplement(50, 150, 200, 300, 1, 200, 300)); +} + +// Helper method that copies <iset> and takes its complement, +// returning false if Check succeeds. +static bool CheckComplement(const QuicIntervalSet<int>& iset, + int comp_min, + int comp_max, + int count, + ...) { + QuicIntervalSet<int> iset_copy = iset; + iset_copy.Complement(comp_min, comp_max); + bool result = true; + va_list ap; + va_start(ap, count); + if (!VA_Check(iset_copy, count, ap)) { + result = false; + } + va_end(ap); + return result; +} + +TEST_F(QuicIntervalSetTest, MultiIntervalComplement) { + // Initialize a small test set: + QuicIntervalSet<int> iset; + iset.Add(100, 200); + iset.Add(300, 400); + iset.Add(500, 600); + + // |----- i -----| + // |----- comp -----| + EXPECT_TRUE(CheckComplement(iset, 0, 50, 1, 0, 50)); + + // |----- i -----| + // |----- comp -----| + EXPECT_TRUE(CheckComplement(iset, 0, 200, 1, 0, 100)); + EXPECT_TRUE(CheckComplement(iset, 0, 220, 2, 0, 100, 200, 220)); + + // |----- i -----| + // |----- comp -----| + EXPECT_TRUE(CheckComplement(iset, 100, 600, 2, 200, 300, 400, 500)); + + // |---------- i ----------| + // |----- comp -----| + EXPECT_TRUE(CheckComplement(iset, 300, 400, 0)); + EXPECT_TRUE(CheckComplement(iset, 250, 400, 1, 250, 300)); + EXPECT_TRUE(CheckComplement(iset, 300, 450, 1, 400, 450)); + EXPECT_TRUE(CheckComplement(iset, 250, 450, 2, 250, 300, 400, 450)); + + // |----- i -----| + // |---------- comp ----------| + EXPECT_TRUE( + CheckComplement(iset, 0, 700, 4, 0, 100, 200, 300, 400, 500, 600, 700)); + + // |----- i -----| + // |----- comp -----| + EXPECT_TRUE(CheckComplement(iset, 400, 700, 2, 400, 500, 600, 700)); + EXPECT_TRUE(CheckComplement(iset, 350, 700, 2, 400, 500, 600, 700)); + + // |----- i -----| + // |----- comp -----| + EXPECT_TRUE(CheckComplement(iset, 700, 800, 1, 700, 800)); +} + +// Verifies ToString, operator<< don't assert. +TEST_F(QuicIntervalSetTest, ToString) { + QuicIntervalSet<int> iset; + iset.Add(300, 400); + iset.Add(100, 200); + iset.Add(500, 600); + EXPECT_TRUE(!iset.ToString().empty()); + QUIC_VLOG(2) << iset; + // Order and format of ToString() output is guaranteed. + EXPECT_EQ("{ [100, 200) [300, 400) [500, 600) }", iset.ToString()); + EXPECT_EQ("{ [1, 2) }", QuicIntervalSet<int>(1, 2).ToString()); + EXPECT_EQ("{ }", QuicIntervalSet<int>().ToString()); +} + +TEST_F(QuicIntervalSetTest, ConstructionDiscardsEmptyInterval) { + EXPECT_TRUE(QuicIntervalSet<int>(QuicInterval<int>(2, 2)).Empty()); + EXPECT_TRUE(QuicIntervalSet<int>(2, 2).Empty()); + EXPECT_FALSE(QuicIntervalSet<int>(QuicInterval<int>(2, 3)).Empty()); + EXPECT_FALSE(QuicIntervalSet<int>(2, 3).Empty()); +} + +TEST_F(QuicIntervalSetTest, Swap) { + QuicIntervalSet<int> a, b; + a.Add(300, 400); + b.Add(100, 200); + b.Add(500, 600); + a.Swap(&b); + EXPECT_TRUE(Check(a, 2, 100, 200, 500, 600)); + EXPECT_TRUE(Check(b, 1, 300, 400)); + swap(a, b); + EXPECT_TRUE(Check(a, 1, 300, 400)); + EXPECT_TRUE(Check(b, 2, 100, 200, 500, 600)); +} + +TEST_F(QuicIntervalSetTest, OutputReturnsOstreamRef) { + std::stringstream ss; + const QuicIntervalSet<int> v(QuicInterval<int>(1, 2)); + auto return_type_is_a_ref = [](std::ostream&) {}; + return_type_is_a_ref(ss << v); +} + +struct NotOstreamable { + bool operator<(const NotOstreamable& other) const { return false; } + bool operator>(const NotOstreamable& other) const { return false; } + bool operator!=(const NotOstreamable& other) const { return false; } + bool operator>=(const NotOstreamable& other) const { return true; } + bool operator<=(const NotOstreamable& other) const { return true; } + bool operator==(const NotOstreamable& other) const { return true; } +}; + +TEST_F(QuicIntervalSetTest, IntervalOfTypeWithNoOstreamSupport) { + const NotOstreamable v; + const QuicIntervalSet<NotOstreamable> d(QuicInterval<NotOstreamable>(v, v)); + // EXPECT_EQ builds a string representation of d. If d::operator<<() + // would be defined then this test would not compile because NotOstreamable + // objects lack the operator<<() support. + EXPECT_EQ(d, d); +} + +class QuicIntervalSetInitTest : public QuicTest { + protected: + const std::vector<QuicInterval<int>> intervals_{{0, 1}, {2, 4}}; +}; + +TEST_F(QuicIntervalSetInitTest, DirectInit) { + std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}}; + QuicIntervalSet<int> s(il); + EXPECT_THAT(s, ElementsAreArray(intervals_)); +} + +TEST_F(QuicIntervalSetInitTest, CopyInit) { + std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}}; + QuicIntervalSet<int> s = il; + EXPECT_THAT(s, ElementsAreArray(intervals_)); +} + +TEST_F(QuicIntervalSetInitTest, AssignIterPair) { + QuicIntervalSet<int> s(0, 1000); // Make sure assign clears. + s.assign(intervals_.begin(), intervals_.end()); + EXPECT_THAT(s, ElementsAreArray(intervals_)); +} + +TEST_F(QuicIntervalSetInitTest, AssignInitList) { + QuicIntervalSet<int> s(0, 1000); // Make sure assign clears. + s.assign({{0, 1}, {2, 3}, {3, 4}}); + EXPECT_THAT(s, ElementsAreArray(intervals_)); +} + +TEST_F(QuicIntervalSetInitTest, AssignmentInitList) { + std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}}; + QuicIntervalSet<int> s; + s = il; + EXPECT_THAT(s, ElementsAreArray(intervals_)); +} + +TEST_F(QuicIntervalSetInitTest, BracedInitThenBracedAssign) { + QuicIntervalSet<int> s{{0, 1}, {2, 3}, {3, 4}}; + s = {{0, 1}, {2, 4}}; + EXPECT_THAT(s, ElementsAreArray(intervals_)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_interval_test.cc b/quic/core/quic_interval_test.cc new file mode 100644 index 0000000..143d417 --- /dev/null +++ b/quic/core/quic_interval_test.cc
@@ -0,0 +1,457 @@ +// Copyright (c) 2019 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/quic_interval.h" + +#include <sstream> +#include <type_traits> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +template <typename ForwardIterator> +void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) { + while (begin != end) { + auto temp = begin; + ++begin; + delete *temp; + } +} + +template <typename T> +void STLDeleteElements(T* container) { + if (!container) + return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +class ConstructorListener { + public: + ConstructorListener(int* copy_construct_counter, int* move_construct_counter) + : copy_construct_counter_(copy_construct_counter), + move_construct_counter_(move_construct_counter) { + *copy_construct_counter_ = 0; + *move_construct_counter_ = 0; + } + ConstructorListener(const ConstructorListener& other) { + copy_construct_counter_ = other.copy_construct_counter_; + move_construct_counter_ = other.move_construct_counter_; + ++*copy_construct_counter_; + } + ConstructorListener(ConstructorListener&& other) { + copy_construct_counter_ = other.copy_construct_counter_; + move_construct_counter_ = other.move_construct_counter_; + ++*move_construct_counter_; + } + bool operator<(const ConstructorListener&) { return false; } + bool operator>(const ConstructorListener&) { return false; } + bool operator<=(const ConstructorListener&) { return true; } + bool operator>=(const ConstructorListener&) { return true; } + bool operator==(const ConstructorListener&) { return true; } + + private: + int* copy_construct_counter_; + int* move_construct_counter_; +}; + +TEST(QuicIntervalConstructorTest, Move) { + int object1_copy_count, object1_move_count; + ConstructorListener object1(&object1_copy_count, &object1_move_count); + int object2_copy_count, object2_move_count; + ConstructorListener object2(&object2_copy_count, &object2_move_count); + + QuicInterval<ConstructorListener> interval(object1, std::move(object2)); + EXPECT_EQ(1, object1_copy_count); + EXPECT_EQ(0, object1_move_count); + EXPECT_EQ(0, object2_copy_count); + EXPECT_EQ(1, object2_move_count); +} + +TEST(QuicIntervalConstructorTest, ImplicitConversion) { + struct WrappedInt { + WrappedInt(int value) : value(value) {} + bool operator<(const WrappedInt& other) { return value < other.value; } + bool operator>(const WrappedInt& other) { return value > other.value; } + bool operator<=(const WrappedInt& other) { return value <= other.value; } + bool operator>=(const WrappedInt& other) { return value >= other.value; } + bool operator==(const WrappedInt& other) { return value == other.value; } + int value; + }; + + static_assert(std::is_convertible<int, WrappedInt>::value, ""); + static_assert( + std::is_constructible<QuicInterval<WrappedInt>, int, int>::value, ""); + + QuicInterval<WrappedInt> i(10, 20); + EXPECT_EQ(10, i.min().value); + EXPECT_EQ(20, i.max().value); +} + +class QuicIntervalTest : public QuicTest { + protected: + // Test intersection between the two intervals i1 and i2. Tries + // i1.IntersectWith(i2) and vice versa. The intersection should change i1 iff + // changes_i1 is true, and the same for changes_i2. The resulting + // intersection should be result. + void TestIntersect(const QuicInterval<int64_t>& i1, + const QuicInterval<int64_t>& i2, + bool changes_i1, + bool changes_i2, + const QuicInterval<int64_t>& result) { + QuicInterval<int64_t> i; + i = i1; + EXPECT_TRUE(i.IntersectWith(i2) == changes_i1 && i == result); + i = i2; + EXPECT_TRUE(i.IntersectWith(i1) == changes_i2 && i == result); + } +}; + +TEST_F(QuicIntervalTest, ConstructorsCopyAndClear) { + QuicInterval<int32_t> empty; + EXPECT_TRUE(empty.Empty()); + + QuicInterval<int32_t> d2(0, 100); + EXPECT_EQ(0, d2.min()); + EXPECT_EQ(100, d2.max()); + EXPECT_EQ(QuicInterval<int32_t>(0, 100), d2); + EXPECT_NE(QuicInterval<int32_t>(0, 99), d2); + + empty = d2; + EXPECT_EQ(0, d2.min()); + EXPECT_EQ(100, d2.max()); + EXPECT_TRUE(empty == d2); + EXPECT_EQ(empty, d2); + EXPECT_TRUE(d2 == empty); + EXPECT_EQ(d2, empty); + + QuicInterval<int32_t> max_less_than_min(40, 20); + EXPECT_TRUE(max_less_than_min.Empty()); + EXPECT_EQ(40, max_less_than_min.min()); + EXPECT_EQ(20, max_less_than_min.max()); + + QuicInterval<int> d3(10, 20); + d3.Clear(); + EXPECT_TRUE(d3.Empty()); +} + +TEST_F(QuicIntervalTest, MakeQuicInterval) { + static_assert( + std::is_same<QuicInterval<int>, decltype(MakeQuicInterval(0, 3))>::value, + "Type is deduced incorrectly."); + static_assert(std::is_same<QuicInterval<double>, + decltype(MakeQuicInterval(0., 3.))>::value, + "Type is deduced incorrectly."); + + EXPECT_EQ(MakeQuicInterval(0., 3.), QuicInterval<double>(0, 3)); +} + +TEST_F(QuicIntervalTest, GettersSetters) { + QuicInterval<int32_t> d1(100, 200); + + // SetMin: + d1.SetMin(30); + EXPECT_EQ(30, d1.min()); + EXPECT_EQ(200, d1.max()); + + // SetMax: + d1.SetMax(220); + EXPECT_EQ(30, d1.min()); + EXPECT_EQ(220, d1.max()); + + // Set: + d1.Clear(); + d1.Set(30, 220); + EXPECT_EQ(30, d1.min()); + EXPECT_EQ(220, d1.max()); + + // SpanningUnion: + QuicInterval<int32_t> d2; + EXPECT_TRUE(!d1.SpanningUnion(d2)); + EXPECT_EQ(30, d1.min()); + EXPECT_EQ(220, d1.max()); + + EXPECT_TRUE(d2.SpanningUnion(d1)); + EXPECT_EQ(30, d2.min()); + EXPECT_EQ(220, d2.max()); + + d2.SetMin(40); + d2.SetMax(100); + EXPECT_TRUE(!d1.SpanningUnion(d2)); + EXPECT_EQ(30, d1.min()); + EXPECT_EQ(220, d1.max()); + + d2.SetMin(20); + d2.SetMax(100); + EXPECT_TRUE(d1.SpanningUnion(d2)); + EXPECT_EQ(20, d1.min()); + EXPECT_EQ(220, d1.max()); + + d2.SetMin(50); + d2.SetMax(300); + EXPECT_TRUE(d1.SpanningUnion(d2)); + EXPECT_EQ(20, d1.min()); + EXPECT_EQ(300, d1.max()); + + d2.SetMin(0); + d2.SetMax(500); + EXPECT_TRUE(d1.SpanningUnion(d2)); + EXPECT_EQ(0, d1.min()); + EXPECT_EQ(500, d1.max()); + + d2.SetMin(100); + d2.SetMax(0); + EXPECT_TRUE(!d1.SpanningUnion(d2)); + EXPECT_EQ(0, d1.min()); + EXPECT_EQ(500, d1.max()); + EXPECT_TRUE(d2.SpanningUnion(d1)); + EXPECT_EQ(0, d2.min()); + EXPECT_EQ(500, d2.max()); +} + +TEST_F(QuicIntervalTest, CoveringOps) { + const QuicInterval<int64_t> empty; + const QuicInterval<int64_t> d(100, 200); + const QuicInterval<int64_t> d1(0, 50); + const QuicInterval<int64_t> d2(50, 110); + const QuicInterval<int64_t> d3(110, 180); + const QuicInterval<int64_t> d4(180, 220); + const QuicInterval<int64_t> d5(220, 300); + const QuicInterval<int64_t> d6(100, 150); + const QuicInterval<int64_t> d7(150, 200); + const QuicInterval<int64_t> d8(0, 300); + + // Intersection: + EXPECT_TRUE(d.Intersects(d)); + EXPECT_TRUE(!empty.Intersects(d) && !d.Intersects(empty)); + EXPECT_TRUE(!d.Intersects(d1) && !d1.Intersects(d)); + EXPECT_TRUE(d.Intersects(d2) && d2.Intersects(d)); + EXPECT_TRUE(d.Intersects(d3) && d3.Intersects(d)); + EXPECT_TRUE(d.Intersects(d4) && d4.Intersects(d)); + EXPECT_TRUE(!d.Intersects(d5) && !d5.Intersects(d)); + EXPECT_TRUE(d.Intersects(d6) && d6.Intersects(d)); + EXPECT_TRUE(d.Intersects(d7) && d7.Intersects(d)); + EXPECT_TRUE(d.Intersects(d8) && d8.Intersects(d)); + + QuicInterval<int64_t> i; + EXPECT_TRUE(d.Intersects(d, &i) && d == i); + EXPECT_TRUE(!empty.Intersects(d, nullptr) && !d.Intersects(empty, nullptr)); + EXPECT_TRUE(!d.Intersects(d1, nullptr) && !d1.Intersects(d, nullptr)); + EXPECT_TRUE(d.Intersects(d2, &i) && i == QuicInterval<int64_t>(100, 110)); + EXPECT_TRUE(d2.Intersects(d, &i) && i == QuicInterval<int64_t>(100, 110)); + EXPECT_TRUE(d.Intersects(d3, &i) && i == d3); + EXPECT_TRUE(d3.Intersects(d, &i) && i == d3); + EXPECT_TRUE(d.Intersects(d4, &i) && i == QuicInterval<int64_t>(180, 200)); + EXPECT_TRUE(d4.Intersects(d, &i) && i == QuicInterval<int64_t>(180, 200)); + EXPECT_TRUE(!d.Intersects(d5, nullptr) && !d5.Intersects(d, nullptr)); + EXPECT_TRUE(d.Intersects(d6, &i) && i == d6); + EXPECT_TRUE(d6.Intersects(d, &i) && i == d6); + EXPECT_TRUE(d.Intersects(d7, &i) && i == d7); + EXPECT_TRUE(d7.Intersects(d, &i) && i == d7); + EXPECT_TRUE(d.Intersects(d8, &i) && i == d); + EXPECT_TRUE(d8.Intersects(d, &i) && i == d); + + // Test IntersectsWith(). + // Arguments are TestIntersect(i1, i2, changes_i1, changes_i2, result). + TestIntersect(empty, d, false, true, empty); + TestIntersect(d, d1, true, true, empty); + TestIntersect(d1, d2, true, true, empty); + TestIntersect(d, d2, true, true, QuicInterval<int64_t>(100, 110)); + TestIntersect(d8, d, true, false, d); + TestIntersect(d8, d1, true, false, d1); + TestIntersect(d8, d5, true, false, d5); + + // Contains: + EXPECT_TRUE(!empty.Contains(d) && !d.Contains(empty)); + EXPECT_TRUE(d.Contains(d)); + EXPECT_TRUE(!d.Contains(d1) && !d1.Contains(d)); + EXPECT_TRUE(!d.Contains(d2) && !d2.Contains(d)); + EXPECT_TRUE(d.Contains(d3) && !d3.Contains(d)); + EXPECT_TRUE(!d.Contains(d4) && !d4.Contains(d)); + EXPECT_TRUE(!d.Contains(d5) && !d5.Contains(d)); + EXPECT_TRUE(d.Contains(d6) && !d6.Contains(d)); + EXPECT_TRUE(d.Contains(d7) && !d7.Contains(d)); + EXPECT_TRUE(!d.Contains(d8) && d8.Contains(d)); + + EXPECT_TRUE(d.Contains(100)); + EXPECT_TRUE(!d.Contains(200)); + EXPECT_TRUE(d.Contains(150)); + EXPECT_TRUE(!d.Contains(99)); + EXPECT_TRUE(!d.Contains(201)); + + // Difference: + std::vector<QuicInterval<int64_t>*> diff; + + EXPECT_TRUE(!d.Difference(empty, &diff)); + EXPECT_EQ(1u, diff.size()); + EXPECT_EQ(100, diff[0]->min()); + EXPECT_EQ(200, diff[0]->max()); + STLDeleteElements(&diff); + EXPECT_TRUE(!empty.Difference(d, &diff) && diff.empty()); + + EXPECT_TRUE(d.Difference(d, &diff) && diff.empty()); + EXPECT_TRUE(!d.Difference(d1, &diff)); + EXPECT_EQ(1u, diff.size()); + EXPECT_EQ(100, diff[0]->min()); + EXPECT_EQ(200, diff[0]->max()); + STLDeleteElements(&diff); + + QuicInterval<int64_t> lo; + QuicInterval<int64_t> hi; + + EXPECT_TRUE(d.Difference(d2, &lo, &hi)); + EXPECT_TRUE(lo.Empty()); + EXPECT_EQ(110, hi.min()); + EXPECT_EQ(200, hi.max()); + EXPECT_TRUE(d.Difference(d2, &diff)); + EXPECT_EQ(1u, diff.size()); + EXPECT_EQ(110, diff[0]->min()); + EXPECT_EQ(200, diff[0]->max()); + STLDeleteElements(&diff); + + EXPECT_TRUE(d.Difference(d3, &lo, &hi)); + EXPECT_EQ(100, lo.min()); + EXPECT_EQ(110, lo.max()); + EXPECT_EQ(180, hi.min()); + EXPECT_EQ(200, hi.max()); + EXPECT_TRUE(d.Difference(d3, &diff)); + EXPECT_EQ(2u, diff.size()); + EXPECT_EQ(100, diff[0]->min()); + EXPECT_EQ(110, diff[0]->max()); + EXPECT_EQ(180, diff[1]->min()); + EXPECT_EQ(200, diff[1]->max()); + STLDeleteElements(&diff); + + EXPECT_TRUE(d.Difference(d4, &lo, &hi)); + EXPECT_EQ(100, lo.min()); + EXPECT_EQ(180, lo.max()); + EXPECT_TRUE(hi.Empty()); + EXPECT_TRUE(d.Difference(d4, &diff)); + EXPECT_EQ(1u, diff.size()); + EXPECT_EQ(100, diff[0]->min()); + EXPECT_EQ(180, diff[0]->max()); + STLDeleteElements(&diff); + + EXPECT_FALSE(d.Difference(d5, &lo, &hi)); + EXPECT_EQ(100, lo.min()); + EXPECT_EQ(200, lo.max()); + EXPECT_TRUE(hi.Empty()); + EXPECT_FALSE(d.Difference(d5, &diff)); + EXPECT_EQ(1u, diff.size()); + EXPECT_EQ(100, diff[0]->min()); + EXPECT_EQ(200, diff[0]->max()); + STLDeleteElements(&diff); + + EXPECT_TRUE(d.Difference(d6, &lo, &hi)); + EXPECT_TRUE(lo.Empty()); + EXPECT_EQ(150, hi.min()); + EXPECT_EQ(200, hi.max()); + EXPECT_TRUE(d.Difference(d6, &diff)); + EXPECT_EQ(1u, diff.size()); + EXPECT_EQ(150, diff[0]->min()); + EXPECT_EQ(200, diff[0]->max()); + STLDeleteElements(&diff); + + EXPECT_TRUE(d.Difference(d7, &lo, &hi)); + EXPECT_EQ(100, lo.min()); + EXPECT_EQ(150, lo.max()); + EXPECT_TRUE(hi.Empty()); + EXPECT_TRUE(d.Difference(d7, &diff)); + EXPECT_EQ(1u, diff.size()); + EXPECT_EQ(100, diff[0]->min()); + EXPECT_EQ(150, diff[0]->max()); + STLDeleteElements(&diff); + + EXPECT_TRUE(d.Difference(d8, &lo, &hi)); + EXPECT_TRUE(lo.Empty()); + EXPECT_TRUE(hi.Empty()); + EXPECT_TRUE(d.Difference(d8, &diff) && diff.empty()); +} + +TEST_F(QuicIntervalTest, Length) { + const QuicInterval<int> empty1; + const QuicInterval<int> empty2(1, 1); + const QuicInterval<int> empty3(1, 0); + const QuicInterval<QuicTime> empty4( + QuicTime::Zero() + QuicTime::Delta::FromSeconds(1), QuicTime::Zero()); + const QuicInterval<int> d1(1, 2); + const QuicInterval<int> d2(0, 50); + const QuicInterval<QuicTime> d3( + QuicTime::Zero(), QuicTime::Zero() + QuicTime::Delta::FromSeconds(1)); + const QuicInterval<QuicTime> d4( + QuicTime::Zero() + QuicTime::Delta::FromSeconds(3600), + QuicTime::Zero() + QuicTime::Delta::FromSeconds(5400)); + + EXPECT_EQ(0, empty1.Length()); + EXPECT_EQ(0, empty2.Length()); + EXPECT_EQ(0, empty3.Length()); + EXPECT_EQ(QuicTime::Delta::Zero(), empty4.Length()); + EXPECT_EQ(1, d1.Length()); + EXPECT_EQ(50, d2.Length()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(1), d3.Length()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(1800), d4.Length()); +} + +TEST_F(QuicIntervalTest, IntervalOfTypeWithNoOperatorMinus) { + // QuicInterval<T> should work even if T does not support operator-(). We + // just can't call QuicInterval<T>::Length() for such types. + const QuicInterval<QuicString> d1("a", "b"); + const QuicInterval<std::pair<int, int>> d2({1, 2}, {4, 3}); + EXPECT_EQ("a", d1.min()); + EXPECT_EQ("b", d1.max()); + EXPECT_EQ(std::make_pair(1, 2), d2.min()); + EXPECT_EQ(std::make_pair(4, 3), d2.max()); +} + +struct NoEquals { + NoEquals(int v) : value(v) {} // NOLINT + int value; + bool operator<(const NoEquals& other) const { return value < other.value; } +}; + +TEST_F(QuicIntervalTest, OrderedComparisonForTypeWithoutEquals) { + const QuicInterval<NoEquals> d1(0, 4); + const QuicInterval<NoEquals> d2(0, 3); + const QuicInterval<NoEquals> d3(1, 4); + const QuicInterval<NoEquals> d4(1, 5); + const QuicInterval<NoEquals> d6(0, 4); + EXPECT_TRUE(d1 < d2); + EXPECT_TRUE(d1 < d3); + EXPECT_TRUE(d1 < d4); + EXPECT_FALSE(d1 < d6); +} + +TEST_F(QuicIntervalTest, OutputReturnsOstreamRef) { + std::stringstream ss; + const QuicInterval<int> v(1, 2); + // If (ss << v) were to return a value, it wouldn't match the signature of + // return_type_is_a_ref() function. + auto return_type_is_a_ref = [](std::ostream&) {}; + return_type_is_a_ref(ss << v); +} + +struct NotOstreamable { + bool operator<(const NotOstreamable& other) const { return false; } + bool operator>=(const NotOstreamable& other) const { return true; } + bool operator==(const NotOstreamable& other) const { return true; } +}; + +TEST_F(QuicIntervalTest, IntervalOfTypeWithNoOstreamSupport) { + const NotOstreamable v; + const QuicInterval<NotOstreamable> d(v, v); + // EXPECT_EQ builds a string representation of d. If d::operator<<() would be + // defined then this test would not compile because NotOstreamable objects + // lack the operator<<() support. + EXPECT_EQ(d, d); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_lru_cache.h b/quic/core/quic_lru_cache.h new file mode 100644 index 0000000..b8c78c6 --- /dev/null +++ b/quic/core/quic_lru_cache.h
@@ -0,0 +1,74 @@ +// Copyright (c) 2018 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_QUIC_LRU_CACHE_H_ +#define QUICHE_QUIC_CORE_QUIC_LRU_CACHE_H_ + +#include <memory> + +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +// A LRU cache that maps from type Key to Value* in QUIC. +// This cache CANNOT be shared by multiple threads (even with locks) because +// Value* returned by Lookup() can be invalid if the entry is evicted by other +// threads. +template <class K, class V> +class QuicLRUCache { + public: + explicit QuicLRUCache(size_t capacity) : capacity_(capacity) {} + QuicLRUCache(const QuicLRUCache&) = delete; + QuicLRUCache& operator=(const QuicLRUCache&) = delete; + + // Inserts one unit of |key|, |value| pair to the cache. Cache takes ownership + // of inserted |value|. + void Insert(const K& key, std::unique_ptr<V> value) { + auto it = cache_.find(key); + if (it != cache_.end()) { + cache_.erase(it); + } + cache_.emplace(key, std::move(value)); + + if (cache_.size() > capacity_) { + cache_.pop_front(); + } + DCHECK_LE(cache_.size(), capacity_); + } + + // If cache contains an entry for |key|, return a pointer to it. This returned + // value is guaranteed to be valid until Insert or Clear. + // Else return nullptr. + V* Lookup(const K& key) { + auto it = cache_.find(key); + if (it == cache_.end()) { + return nullptr; + } + + std::unique_ptr<V> value = std::move(it->second); + cache_.erase(it); + auto result = cache_.emplace(key, std::move(value)); + DCHECK(result.second); + return result.first->second.get(); + } + + // Removes all entries from the cache. + void Clear() { cache_.clear(); } + + // Returns maximum size of the cache. + size_t MaxSize() const { return capacity_; } + + // Returns current size of the cache. + size_t Size() const { return cache_.size(); } + + private: + QuicLinkedHashMap<K, std::unique_ptr<V>> cache_; + const size_t capacity_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_LRU_CACHE_H_
diff --git a/quic/core/quic_lru_cache_test.cc b/quic/core/quic_lru_cache_test.cc new file mode 100644 index 0000000..92f788b --- /dev/null +++ b/quic/core/quic_lru_cache_test.cc
@@ -0,0 +1,76 @@ +// 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/quic_lru_cache.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +struct CachedItem { + explicit CachedItem(uint32_t new_value) : value(new_value) {} + + uint32_t value; +}; + +TEST(QuicLRUCacheTest, InsertAndLookup) { + QuicLRUCache<int, CachedItem> cache(5); + EXPECT_EQ(nullptr, cache.Lookup(1)); + EXPECT_EQ(0u, cache.Size()); + EXPECT_EQ(5u, cache.MaxSize()); + + // Check that item 1 was properly inserted. + std::unique_ptr<CachedItem> item1(new CachedItem(11)); + cache.Insert(1, std::move(item1)); + EXPECT_EQ(1u, cache.Size()); + EXPECT_EQ(11u, cache.Lookup(1)->value); + + // Check that item 2 overrides item 1. + std::unique_ptr<CachedItem> item2(new CachedItem(12)); + cache.Insert(1, std::move(item2)); + EXPECT_EQ(1u, cache.Size()); + EXPECT_EQ(12u, cache.Lookup(1)->value); + + std::unique_ptr<CachedItem> item3(new CachedItem(13)); + cache.Insert(3, std::move(item3)); + EXPECT_EQ(2u, cache.Size()); + EXPECT_EQ(13u, cache.Lookup(3)->value); + + // No memory leakage. + cache.Clear(); + EXPECT_EQ(0u, cache.Size()); +} + +TEST(QuicLRUCacheTest, Eviction) { + QuicLRUCache<int, CachedItem> cache(3); + + for (size_t i = 1; i <= 4; ++i) { + std::unique_ptr<CachedItem> item(new CachedItem(10 + i)); + cache.Insert(i, std::move(item)); + } + + EXPECT_EQ(3u, cache.Size()); + EXPECT_EQ(3u, cache.MaxSize()); + + // Make sure item 1 is evicted. + EXPECT_EQ(nullptr, cache.Lookup(1)); + EXPECT_EQ(14u, cache.Lookup(4)->value); + + EXPECT_EQ(12u, cache.Lookup(2)->value); + std::unique_ptr<CachedItem> item5(new CachedItem(15)); + cache.Insert(5, std::move(item5)); + // Make sure item 3 is evicted. + EXPECT_EQ(nullptr, cache.Lookup(3)); + EXPECT_EQ(15u, cache.Lookup(5)->value); + + // No memory leakage. + cache.Clear(); + EXPECT_EQ(0u, cache.Size()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_one_block_arena.h b/quic/core/quic_one_block_arena.h new file mode 100644 index 0000000..65b52af --- /dev/null +++ b/quic/core/quic_one_block_arena.h
@@ -0,0 +1,83 @@ +// 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. + +// An arena that consists of a single inlined block of |ArenaSize|. Useful to +// avoid repeated calls to malloc/new and to improve memory locality. DCHECK's +// if an allocation out of the arena ever fails in debug builds; falls back to +// heap allocation in release builds. + +#ifndef QUICHE_QUIC_CORE_QUIC_ONE_BLOCK_ARENA_H_ +#define QUICHE_QUIC_CORE_QUIC_ONE_BLOCK_ARENA_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +template <uint32_t ArenaSize> +class QuicOneBlockArena { + static const uint32_t kMaxAlign = 8; + + public: + QuicOneBlockArena(); + QuicOneBlockArena(const QuicOneBlockArena&) = delete; + QuicOneBlockArena& operator=(const QuicOneBlockArena&) = delete; + + // Instantiates an object of type |T| with |args|. |args| are perfectly + // forwarded to |T|'s constructor. The returned pointer's lifetime is + // controlled by QuicArenaScopedPtr. + template <typename T, typename... Args> + QuicArenaScopedPtr<T> New(Args&&... args); + + private: + // Returns the size of |T| aligned up to |kMaxAlign|. + template <typename T> + static inline uint32_t AlignedSize() { + return ((sizeof(T) + (kMaxAlign - 1)) / kMaxAlign) * kMaxAlign; + } + + // Actual storage. + // Subtle/annoying: the value '8' must be coded explicitly into the alignment + // declaration for MSVC. + QUIC_ALIGNED(8) char storage_[ArenaSize]; + // Current offset into the storage. + uint32_t offset_; +}; + +template <uint32_t ArenaSize> +QuicOneBlockArena<ArenaSize>::QuicOneBlockArena() : offset_(0) {} + +template <uint32_t ArenaSize> +template <typename T, typename... Args> +QuicArenaScopedPtr<T> QuicOneBlockArena<ArenaSize>::New(Args&&... args) { + DCHECK_LT(AlignedSize<T>(), ArenaSize) + << "Object is too large for the arena."; + static_assert(QUIC_ALIGN_OF(T) > 1, + "Objects added to the arena must be at least 2B aligned."); + if (QUIC_PREDICT_FALSE(offset_ > ArenaSize - AlignedSize<T>())) { + QUIC_BUG << "Ran out of space in QuicOneBlockArena at " << this + << ", max size was " << ArenaSize << ", failing request was " + << AlignedSize<T>() << ", end of arena was " << offset_; + return QuicArenaScopedPtr<T>(new T(std::forward<Args>(args)...)); + } + + void* buf = &storage_[offset_]; + new (buf) T(std::forward<Args>(args)...); + offset_ += AlignedSize<T>(); + return QuicArenaScopedPtr<T>(buf, + QuicArenaScopedPtr<T>::ConstructFrom::kArena); +} + +// QuicConnections currently use around 1KB of polymorphic types which would +// ordinarily be on the heap. Instead, store them inline in an arena. +using QuicConnectionArena = QuicOneBlockArena<1024>; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_ONE_BLOCK_ARENA_H_
diff --git a/quic/core/quic_one_block_arena_test.cc b/quic/core/quic_one_block_arena_test.cc new file mode 100644 index 0000000..3175ac5 --- /dev/null +++ b/quic/core/quic_one_block_arena_test.cc
@@ -0,0 +1,60 @@ +// 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/quic_one_block_arena.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace { + +static const uint32_t kMaxAlign = 8; + +struct TestObject { + uint32_t value; +}; + +class QuicOneBlockArenaTest : public QuicTest {}; + +TEST_F(QuicOneBlockArenaTest, AllocateSuccess) { + QuicOneBlockArena<1024> arena; + QuicArenaScopedPtr<TestObject> ptr = arena.New<TestObject>(); + EXPECT_TRUE(ptr.is_from_arena()); +} + +TEST_F(QuicOneBlockArenaTest, Exhaust) { + QuicOneBlockArena<1024> arena; + for (size_t i = 0; i < 1024 / kMaxAlign; ++i) { + QuicArenaScopedPtr<TestObject> ptr = arena.New<TestObject>(); + EXPECT_TRUE(ptr.is_from_arena()); + } + QuicArenaScopedPtr<TestObject> ptr; + EXPECT_QUIC_BUG(ptr = arena.New<TestObject>(), + "Ran out of space in QuicOneBlockArena"); + EXPECT_FALSE(ptr.is_from_arena()); +} + +TEST_F(QuicOneBlockArenaTest, NoOverlaps) { + QuicOneBlockArena<1024> arena; + std::vector<QuicArenaScopedPtr<TestObject>> objects; + QuicIntervalSet<uintptr_t> used; + for (size_t i = 0; i < 1024 / kMaxAlign; ++i) { + QuicArenaScopedPtr<TestObject> ptr = arena.New<TestObject>(); + EXPECT_TRUE(ptr.is_from_arena()); + + uintptr_t begin = reinterpret_cast<uintptr_t>(ptr.get()); + uintptr_t end = begin + sizeof(TestObject); + EXPECT_FALSE(used.Contains(begin)); + EXPECT_FALSE(used.Contains(end - 1)); + used.Add(begin, end); + } +} + +} // namespace +} // namespace quic
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc new file mode 100644 index 0000000..9d823bc --- /dev/null +++ b/quic/core/quic_packet_creator.cc
@@ -0,0 +1,1007 @@ +// Copyright (c) 2012 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/quic_packet_creator.h" + +#include <algorithm> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.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 { + +QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) { + switch (level) { + case ENCRYPTION_NONE: + return INITIAL; + case ENCRYPTION_ZERO_RTT: + return ZERO_RTT_PROTECTED; + case ENCRYPTION_FORWARD_SECURE: + QUIC_BUG + << "Try to derive long header type for packet with encryption level: " + << QuicUtils::EncryptionLevelToString(level); + return INVALID_PACKET_TYPE; + default: + QUIC_BUG << QuicUtils::EncryptionLevelToString(level); + return INVALID_PACKET_TYPE; + } +} + +} // namespace + +#define ENDPOINT \ + (framer_->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") + +QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id, + QuicFramer* framer, + DelegateInterface* delegate) + : QuicPacketCreator(connection_id, + framer, + QuicRandom::GetInstance(), + delegate) {} + +QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random, + DelegateInterface* delegate) + : delegate_(delegate), + debug_delegate_(nullptr), + framer_(framer), + random_(random), + send_version_in_packet_(framer->perspective() == Perspective::IS_CLIENT), + have_diversification_nonce_(false), + max_packet_length_(0), + connection_id_included_(CONNECTION_ID_PRESENT), + packet_size_(0), + connection_id_(connection_id), + packet_(QuicPacketNumber(), + PACKET_1BYTE_PACKET_NUMBER, + nullptr, + 0, + false, + false), + pending_padding_bytes_(0), + needs_full_padding_(false), + can_set_transmission_type_(false), + set_transmission_type_for_next_frame_( + GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) { + SetMaxPacketLength(kDefaultMaxPacketSize); +} + +QuicPacketCreator::~QuicPacketCreator() { + DeleteFrames(&packet_.retransmittable_frames); +} + +void QuicPacketCreator::SetEncrypter(EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter) { + framer_->SetEncrypter(level, std::move(encrypter)); + max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_); +} + +bool QuicPacketCreator::CanSetMaxPacketLength() const { + // |max_packet_length_| should not be changed mid-packet. + return queued_frames_.empty(); +} + +void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) { + DCHECK(CanSetMaxPacketLength()); + + // Avoid recomputing |max_plaintext_size_| if the length does not actually + // change. + if (length == max_packet_length_) { + return; + } + + max_packet_length_ = length; + max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_); +} + +// Stops serializing version of the protocol in packets sent after this call. +// A packet that is already open might send kQuicVersionSize bytes less than the +// maximum packet size if we stop sending version before it is serialized. +void QuicPacketCreator::StopSendingVersion() { + DCHECK(send_version_in_packet_); + DCHECK_LE(framer_->transport_version(), QUIC_VERSION_43); + send_version_in_packet_ = false; + if (packet_size_ > 0) { + DCHECK_LT(kQuicVersionSize, packet_size_); + packet_size_ -= kQuicVersionSize; + } +} + +void QuicPacketCreator::SetDiversificationNonce( + const DiversificationNonce& nonce) { + DCHECK(!have_diversification_nonce_); + have_diversification_nonce_ = true; + diversification_nonce_ = nonce; +} + +void QuicPacketCreator::UpdatePacketNumberLength( + QuicPacketNumber least_packet_awaited_by_peer, + QuicPacketCount max_packets_in_flight) { + if (!queued_frames_.empty()) { + // Don't change creator state if there are frames queued. + QUIC_BUG << "Called UpdatePacketNumberLength with " << queued_frames_.size() + << " queued_frames. First frame type:" + << queued_frames_.front().type + << " last frame type:" << queued_frames_.back().type; + return; + } + + DCHECK_LE(least_packet_awaited_by_peer, packet_.packet_number + 1); + const uint64_t current_delta = + packet_.packet_number + 1 - least_packet_awaited_by_peer; + const uint64_t delta = std::max(current_delta, max_packets_in_flight); + packet_.packet_number_length = QuicFramer::GetMinPacketNumberLength( + framer_->transport_version(), QuicPacketNumber(delta * 4)); +} + +bool QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset, + TransmissionType transmission_type, + QuicFrame* frame) { + if (!CreateCryptoFrame(level, write_length, offset, frame)) { + return false; + } + // When crypto data was sent in stream frames, ConsumeData is called with + // |needs_full_padding = true|. Keep the same behavior here when sending + // crypto frames. + // + // TODO(nharper): Check what the IETF drafts say about padding out initial + // messages and change this as appropriate. + needs_full_padding_ = true; + return AddFrame(*frame, /*save_retransmittable_frames*/ true, + transmission_type); +} + +bool QuicPacketCreator::ConsumeData(QuicStreamId id, + size_t write_length, + size_t iov_offset, + QuicStreamOffset offset, + bool fin, + bool needs_full_padding, + TransmissionType transmission_type, + QuicFrame* frame) { + if (!HasRoomForStreamFrame(id, offset, write_length - iov_offset)) { + return false; + } + CreateStreamFrame(id, write_length, iov_offset, offset, fin, frame); + // Explicitly disallow multi-packet CHLOs. + if (FLAGS_quic_enforce_single_packet_chlo && + StreamFrameStartsWithChlo(frame->stream_frame) && + frame->stream_frame.data_length < write_length) { + const QuicString error_details = + "Client hello won't fit in a single packet."; + QUIC_BUG << error_details << " Constructed stream frame length: " + << frame->stream_frame.data_length + << " CHLO length: " << write_length; + delegate_->OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, error_details, + ConnectionCloseSource::FROM_SELF); + return false; + } + if (!AddFrame(*frame, /*save_retransmittable_frames=*/true, + transmission_type)) { + // Fails if we try to write unencrypted stream data. + return false; + } + if (needs_full_padding) { + needs_full_padding_ = true; + } + + return true; +} + +bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, + QuicStreamOffset offset, + size_t data_size) { + return BytesFree() > + QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id, + offset, true, data_size); +} + +bool QuicPacketCreator::HasRoomForMessageFrame(QuicByteCount length) { + return BytesFree() >= QuicFramer::GetMessageFrameSize( + framer_->transport_version(), true, length); +} + +// TODO(fkastenholz): this method should not use constant values for +// the last-frame-in-packet and data-length parameters to +// GetMinStreamFrameSize. Proper values should be plumbed in from +// higher up. This was left this way for now for a few reasons. First, +// higher up calls to StreamFramePacketOverhead() do not always know +// this information, leading to a cascade of changes and B) the +// higher-up software does not always loop, calling +// StreamFramePacketOverhead() once for every packet -- eg there is +// a test in quic_connection_test that calls it once and assumes that +// the value is the same for all packets. + +// static +size_t QuicPacketCreator::StreamFramePacketOverhead( + QuicTransportVersion version, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool include_version, + bool include_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicVariableLengthIntegerLength length_length, + QuicStreamOffset offset) { + return GetPacketHeaderSize(version, destination_connection_id_length, + source_connection_id_length, include_version, + include_diversification_nonce, + packet_number_length, retry_token_length_length, 0, + length_length) + + + // Assumes this is a packet with a single stream frame in it. Since + // last_frame_in_packet is set true, the size of the length field is + // not included in the calculation. This is OK because in other places + // in the code, the logic adds back 2 (the size of the Google QUIC + // length) when a frame is not the last frame of the packet. This is + // also acceptable for IETF Quic; even though the length field could be + // 8 bytes long, in practice it will not be longer than 2 bytes (enough + // to encode 16K). A length that would be encoded in 2 bytes (0xfff) + // is passed just for cleanliness. + // + // TODO(fkastenholz): This is very hacky and feels brittle. Ideally we + // would calculate the correct lengths at the correct time, based on + // the state at that time/place. + QuicFramer::GetMinStreamFrameSize(version, 1u, offset, true, + kMaxPacketSize); +} + +void QuicPacketCreator::CreateStreamFrame(QuicStreamId id, + size_t write_length, + size_t iov_offset, + QuicStreamOffset offset, + bool fin, + QuicFrame* frame) { + const size_t data_size = write_length - iov_offset; + DCHECK_GT( + max_packet_length_, + StreamFramePacketOverhead( + framer_->transport_version(), GetDestinationConnectionIdLength(), + GetSourceConnectionIdLength(), kIncludeVersion, + IncludeNonceInPublicHeader(), PACKET_6BYTE_PACKET_NUMBER, + GetRetryTokenLengthLength(), GetLengthLength(), offset)); + + QUIC_BUG_IF(!HasRoomForStreamFrame(id, offset, data_size)) + << "No room for Stream frame, BytesFree: " << BytesFree() + << " MinStreamFrameSize: " + << QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id, + offset, true, data_size); + + if (iov_offset == write_length) { + QUIC_BUG_IF(!fin) << "Creating a stream frame with no data or fin."; + // Create a new packet for the fin, if necessary. + *frame = QuicFrame(QuicStreamFrame(id, true, offset, QuicStringPiece())); + return; + } + + size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( + framer_->transport_version(), id, offset, + /* last_frame_in_packet= */ true, data_size); + size_t bytes_consumed = + std::min<size_t>(BytesFree() - min_frame_size, data_size); + + bool set_fin = fin && bytes_consumed == data_size; // Last frame. + *frame = QuicFrame(QuicStreamFrame(id, set_fin, offset, bytes_consumed)); +} + +bool QuicPacketCreator::CreateCryptoFrame(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset, + QuicFrame* frame) { + size_t min_frame_size = + QuicFramer::GetMinCryptoFrameSize(write_length, offset); + if (BytesFree() <= min_frame_size) { + return false; + } + size_t max_write_length = BytesFree() - min_frame_size; + size_t bytes_consumed = std::min<size_t>(max_write_length, write_length); + *frame = QuicFrame(new QuicCryptoFrame(level, offset, bytes_consumed)); + return true; +} + +void QuicPacketCreator::ReserializeAllFrames( + const QuicPendingRetransmission& retransmission, + char* buffer, + size_t buffer_len) { + DCHECK(queued_frames_.empty()); + DCHECK_EQ(0, packet_.num_padding_bytes); + QUIC_BUG_IF(retransmission.retransmittable_frames.empty()) + << "Attempt to serialize empty packet"; + const EncryptionLevel default_encryption_level = packet_.encryption_level; + + // Temporarily set the packet number length and change the encryption level. + packet_.packet_number_length = retransmission.packet_number_length; + if (retransmission.num_padding_bytes == -1) { + // Only retransmit padding when original packet needs full padding. Padding + // from pending_padding_bytes_ are not retransmitted. + needs_full_padding_ = true; + } + // Only preserve the original encryption level if it's a handshake packet or + // if we haven't gone forward secure. + if (retransmission.has_crypto_handshake || + packet_.encryption_level != ENCRYPTION_FORWARD_SECURE) { + packet_.encryption_level = retransmission.encryption_level; + } + + // Serialize the packet and restore packet number length state. + for (const QuicFrame& frame : retransmission.retransmittable_frames) { + bool success = AddFrame(frame, false, retransmission.transmission_type); + QUIC_BUG_IF(!success) << " Failed to add frame of type:" << frame.type + << " num_frames:" + << retransmission.retransmittable_frames.size() + << " retransmission.packet_number_length:" + << retransmission.packet_number_length + << " packet_.packet_number_length:" + << packet_.packet_number_length; + } + packet_.transmission_type = retransmission.transmission_type; + SerializePacket(buffer, buffer_len); + packet_.original_packet_number = retransmission.packet_number; + OnSerializedPacket(); + // Restore old values. + packet_.encryption_level = default_encryption_level; +} + +void QuicPacketCreator::Flush() { + if (!HasPendingFrames() && pending_padding_bytes_ == 0) { + return; + } + + QUIC_CACHELINE_ALIGNED char stack_buffer[kMaxPacketSize]; + char* serialized_packet_buffer = delegate_->GetPacketBuffer(); + if (serialized_packet_buffer == nullptr) { + serialized_packet_buffer = stack_buffer; + } + + SerializePacket(serialized_packet_buffer, kMaxPacketSize); + OnSerializedPacket(); +} + +void QuicPacketCreator::OnSerializedPacket() { + if (packet_.encrypted_buffer == nullptr) { + const QuicString error_details = "Failed to SerializePacket."; + QUIC_BUG << error_details; + delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, + error_details, + ConnectionCloseSource::FROM_SELF); + return; + } + + SerializedPacket packet(std::move(packet_)); + ClearPacket(); + delegate_->OnSerializedPacket(&packet); +} + +void QuicPacketCreator::ClearPacket() { + packet_.has_ack = false; + packet_.has_stop_waiting = false; + packet_.has_crypto_handshake = NOT_HANDSHAKE; + packet_.num_padding_bytes = 0; + packet_.original_packet_number.Clear(); + if (!can_set_transmission_type_ || ShouldSetTransmissionTypeForNextFrame()) { + packet_.transmission_type = NOT_RETRANSMISSION; + } + packet_.encrypted_buffer = nullptr; + packet_.encrypted_length = 0; + DCHECK(packet_.retransmittable_frames.empty()); + packet_.largest_acked.Clear(); + needs_full_padding_ = false; +} + +void QuicPacketCreator::CreateAndSerializeStreamFrame( + QuicStreamId id, + size_t write_length, + QuicStreamOffset iov_offset, + QuicStreamOffset stream_offset, + bool fin, + TransmissionType transmission_type, + size_t* num_bytes_consumed) { + DCHECK(queued_frames_.empty()); + // Write out the packet header + QuicPacketHeader header; + FillPacketHeader(&header); + + QUIC_CACHELINE_ALIGNED char stack_buffer[kMaxPacketSize]; + char* encrypted_buffer = delegate_->GetPacketBuffer(); + if (encrypted_buffer == nullptr) { + encrypted_buffer = stack_buffer; + } + + QuicDataWriter writer(kMaxPacketSize, encrypted_buffer); + size_t length_field_offset = 0; + if (!framer_->AppendPacketHeader(header, &writer, &length_field_offset)) { + QUIC_BUG << "AppendPacketHeader failed"; + return; + } + + // Create a Stream frame with the remaining space. + QUIC_BUG_IF(iov_offset == write_length && !fin) + << "Creating a stream frame with no data or fin."; + const size_t remaining_data_size = write_length - iov_offset; + const size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( + framer_->transport_version(), id, stream_offset, + /* last_frame_in_packet= */ true, remaining_data_size); + const size_t available_size = + max_plaintext_size_ - writer.length() - min_frame_size; + const size_t bytes_consumed = + std::min<size_t>(available_size, remaining_data_size); + + const bool set_fin = fin && (bytes_consumed == remaining_data_size); + QuicStreamFrame frame(id, set_fin, stream_offset, bytes_consumed); + QUIC_DVLOG(1) << ENDPOINT << "Adding frame: " << frame; + + // TODO(ianswett): AppendTypeByte and AppendStreamFrame could be optimized + // into one method that takes a QuicStreamFrame, if warranted. + if (!framer_->AppendTypeByte(QuicFrame(frame), + /* no stream frame length */ true, &writer)) { + QUIC_BUG << "AppendTypeByte failed"; + return; + } + if (!framer_->AppendStreamFrame(frame, /* no stream frame length */ true, + &writer)) { + QUIC_BUG << "AppendStreamFrame failed"; + return; + } + + if (!framer_->WriteIetfLongHeaderLength(header, &writer, length_field_offset, + packet_.encryption_level)) { + return; + } + + if (ShouldSetTransmissionTypeForNextFrame()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_set_transmission_type_for_next_frame, 1, + 2); + packet_.transmission_type = transmission_type; + } + + size_t encrypted_length = framer_->EncryptInPlace( + packet_.encryption_level, packet_.packet_number, + GetStartOfEncryptedData(framer_->transport_version(), header), + writer.length(), kMaxPacketSize, encrypted_buffer); + if (encrypted_length == 0) { + QUIC_BUG << "Failed to encrypt packet number " << header.packet_number; + return; + } + // TODO(ianswett): Optimize the storage so RetransmitableFrames can be + // unioned with a QuicStreamFrame and a UniqueStreamBuffer. + *num_bytes_consumed = bytes_consumed; + packet_size_ = 0; + packet_.encrypted_buffer = encrypted_buffer; + packet_.encrypted_length = encrypted_length; + packet_.retransmittable_frames.push_back(QuicFrame(frame)); + OnSerializedPacket(); +} + +bool QuicPacketCreator::HasPendingFrames() const { + return !queued_frames_.empty(); +} + +bool QuicPacketCreator::HasPendingRetransmittableFrames() const { + return !packet_.retransmittable_frames.empty(); +} + +bool QuicPacketCreator::HasPendingStreamFramesOfStream(QuicStreamId id) const { + for (const auto& frame : packet_.retransmittable_frames) { + if (frame.type == STREAM_FRAME && frame.stream_frame.stream_id == id) { + return true; + } + } + return false; +} + +size_t QuicPacketCreator::ExpansionOnNewFrame() const { + // If the last frame in the packet is a message frame, then it will expand to + // include the varint message length when a new frame is added. + const bool has_trailing_message_frame = + !queued_frames_.empty() && queued_frames_.back().type == MESSAGE_FRAME; + if (has_trailing_message_frame) { + return QuicDataWriter::GetVarInt62Len( + queued_frames_.back().message_frame->message_length); + } + // If the last frame in the packet is a stream frame, then it will expand to + // include the stream_length field when a new frame is added. + const bool has_trailing_stream_frame = + !queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME; + if (!has_trailing_stream_frame) { + return 0; + } + if (framer_->transport_version() == QUIC_VERSION_99) { + return QuicDataWriter::GetVarInt62Len( + queued_frames_.back().stream_frame.data_length); + } + return kQuicStreamPayloadLengthSize; +} + +size_t QuicPacketCreator::BytesFree() { + DCHECK_GE(max_plaintext_size_, PacketSize()); + return max_plaintext_size_ - + std::min(max_plaintext_size_, PacketSize() + ExpansionOnNewFrame()); +} + +size_t QuicPacketCreator::PacketSize() { + if (!queued_frames_.empty()) { + return packet_size_; + } + packet_size_ = GetPacketHeaderSize( + framer_->transport_version(), GetDestinationConnectionIdLength(), + GetSourceConnectionIdLength(), IncludeVersionInHeader(), + IncludeNonceInPublicHeader(), GetPacketNumberLength(), + GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength()); + return packet_size_; +} + +bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame, + TransmissionType transmission_type) { + return AddFrame(frame, /*save_retransmittable_frames=*/true, + transmission_type); +} + +bool QuicPacketCreator::AddPaddedSavedFrame( + const QuicFrame& frame, + TransmissionType transmission_type) { + if (AddFrame(frame, /*save_retransmittable_frames=*/true, + transmission_type)) { + needs_full_padding_ = true; + return true; + } + return false; +} + +void QuicPacketCreator::SerializePacket(char* encrypted_buffer, + size_t encrypted_buffer_len) { + DCHECK_LT(0u, encrypted_buffer_len); + QUIC_BUG_IF(queued_frames_.empty() && pending_padding_bytes_ == 0) + << "Attempt to serialize empty packet"; + QuicPacketHeader header; + // FillPacketHeader increments packet_number_. + FillPacketHeader(&header); + + MaybeAddPadding(); + + DCHECK_GE(max_plaintext_size_, packet_size_); + // Use the packet_size_ instead of the buffer size to ensure smaller + // packet sizes are properly used. + size_t length = + framer_->BuildDataPacket(header, queued_frames_, encrypted_buffer, + packet_size_, packet_.encryption_level); + if (length == 0) { + QUIC_BUG << "Failed to serialize " << queued_frames_.size() << " frames."; + return; + } + + // ACK Frames will be truncated due to length only if they're the only frame + // in the packet, and if packet_size_ was set to max_plaintext_size_. If + // truncation due to length occurred, then GetSerializedFrameLength will have + // returned all bytes free. + bool possibly_truncated_by_length = packet_size_ == max_plaintext_size_ && + queued_frames_.size() == 1 && + queued_frames_.back().type == ACK_FRAME; + // Because of possible truncation, we can't be confident that our + // packet size calculation worked correctly. + if (!possibly_truncated_by_length) { + DCHECK_EQ(packet_size_, length); + } + const size_t encrypted_length = framer_->EncryptInPlace( + packet_.encryption_level, packet_.packet_number, + GetStartOfEncryptedData(framer_->transport_version(), header), length, + encrypted_buffer_len, encrypted_buffer); + if (encrypted_length == 0) { + QUIC_BUG << "Failed to encrypt packet number " << packet_.packet_number; + return; + } + + packet_size_ = 0; + queued_frames_.clear(); + packet_.encrypted_buffer = encrypted_buffer; + packet_.encrypted_length = encrypted_length; +} + +std::unique_ptr<QuicEncryptedPacket> +QuicPacketCreator::SerializeVersionNegotiationPacket( + bool ietf_quic, + const ParsedQuicVersionVector& supported_versions) { + DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective()); + std::unique_ptr<QuicEncryptedPacket> encrypted = + QuicFramer::BuildVersionNegotiationPacket(connection_id_, ietf_quic, + supported_versions); + DCHECK(encrypted); + DCHECK_GE(max_packet_length_, encrypted->length()); + return encrypted; +} + +OwningSerializedPacketPointer +QuicPacketCreator::SerializeConnectivityProbingPacket() { + QUIC_BUG_IF(framer_->transport_version() == QUIC_VERSION_99) + << "Must not be version 99 to serialize padded ping connectivity probe"; + QuicPacketHeader header; + // FillPacketHeader increments packet_number_. + FillPacketHeader(&header); + + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + size_t length = framer_->BuildConnectivityProbingPacket( + header, buffer.get(), max_plaintext_size_, packet_.encryption_level); + DCHECK(length); + + const size_t encrypted_length = framer_->EncryptInPlace( + packet_.encryption_level, packet_.packet_number, + GetStartOfEncryptedData(framer_->transport_version(), header), length, + kMaxPacketSize, buffer.get()); + DCHECK(encrypted_length); + + OwningSerializedPacketPointer serialize_packet(new SerializedPacket( + header.packet_number, header.packet_number_length, buffer.release(), + encrypted_length, /*has_ack=*/false, /*has_stop_waiting=*/false)); + + serialize_packet->encryption_level = packet_.encryption_level; + serialize_packet->transmission_type = NOT_RETRANSMISSION; + + return serialize_packet; +} + +OwningSerializedPacketPointer +QuicPacketCreator::SerializePathChallengeConnectivityProbingPacket( + QuicPathFrameBuffer* payload) { + QUIC_BUG_IF(framer_->transport_version() != QUIC_VERSION_99) + << "Must be version 99 to serialize path challenge connectivity probe, " + "is version " + << framer_->transport_version(); + QuicPacketHeader header; + // FillPacketHeader increments packet_number_. + FillPacketHeader(&header); + + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + size_t length = framer_->BuildPaddedPathChallengePacket( + header, buffer.get(), max_plaintext_size_, payload, random_, + packet_.encryption_level); + DCHECK(length); + + const size_t encrypted_length = framer_->EncryptInPlace( + packet_.encryption_level, packet_.packet_number, + GetStartOfEncryptedData(framer_->transport_version(), header), length, + kMaxPacketSize, buffer.get()); + DCHECK(encrypted_length); + + OwningSerializedPacketPointer serialize_packet(new SerializedPacket( + header.packet_number, header.packet_number_length, buffer.release(), + encrypted_length, /*has_ack=*/false, /*has_stop_waiting=*/false)); + + serialize_packet->encryption_level = packet_.encryption_level; + serialize_packet->transmission_type = NOT_RETRANSMISSION; + + return serialize_packet; +} + +OwningSerializedPacketPointer +QuicPacketCreator::SerializePathResponseConnectivityProbingPacket( + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded) { + QUIC_BUG_IF(framer_->transport_version() != QUIC_VERSION_99) + << "Must be version 99 to serialize path response connectivity probe, is " + "version " + << framer_->transport_version(); + QuicPacketHeader header; + // FillPacketHeader increments packet_number_. + FillPacketHeader(&header); + + std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]); + size_t length = framer_->BuildPathResponsePacket( + header, buffer.get(), max_plaintext_size_, payloads, is_padded, + packet_.encryption_level); + DCHECK(length); + + const size_t encrypted_length = framer_->EncryptInPlace( + packet_.encryption_level, packet_.packet_number, + GetStartOfEncryptedData(framer_->transport_version(), header), length, + kMaxPacketSize, buffer.get()); + DCHECK(encrypted_length); + + OwningSerializedPacketPointer serialize_packet(new SerializedPacket( + header.packet_number, header.packet_number_length, buffer.release(), + encrypted_length, /*has_ack=*/false, /*has_stop_waiting=*/false)); + + serialize_packet->encryption_level = packet_.encryption_level; + serialize_packet->transmission_type = NOT_RETRANSMISSION; + + return serialize_packet; +} + +// TODO(b/74062209): Make this a public method of framer? +SerializedPacket QuicPacketCreator::NoPacket() { + return SerializedPacket(QuicPacketNumber(), PACKET_1BYTE_PACKET_NUMBER, + nullptr, 0, false, false); +} + +QuicConnectionIdIncluded QuicPacketCreator::GetDestinationConnectionIdIncluded() + const { + if (framer_->transport_version() > QUIC_VERSION_43) { + // Packets sent by client always include destination connection ID, and + // those sent by the server do not include destination connection ID. + return framer_->perspective() == Perspective::IS_CLIENT + ? CONNECTION_ID_PRESENT + : CONNECTION_ID_ABSENT; + } + return connection_id_included_; +} + +QuicConnectionIdIncluded QuicPacketCreator::GetSourceConnectionIdIncluded() + const { + // Long header packets sent by server include source connection ID. + if (HasIetfLongHeader() && framer_->perspective() == Perspective::IS_SERVER) { + return CONNECTION_ID_PRESENT; + } + return CONNECTION_ID_ABSENT; +} + +QuicConnectionIdLength QuicPacketCreator::GetDestinationConnectionIdLength() + const { + DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id_, + transport_version())); + return GetDestinationConnectionIdIncluded() == CONNECTION_ID_PRESENT + ? static_cast<QuicConnectionIdLength>(connection_id_.length()) + : PACKET_0BYTE_CONNECTION_ID; +} + +QuicConnectionIdLength QuicPacketCreator::GetSourceConnectionIdLength() const { + DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id_, + transport_version())); + return GetSourceConnectionIdIncluded() == CONNECTION_ID_PRESENT + ? static_cast<QuicConnectionIdLength>(connection_id_.length()) + : PACKET_0BYTE_CONNECTION_ID; +} + +QuicPacketNumberLength QuicPacketCreator::GetPacketNumberLength() const { + if (HasIetfLongHeader() && framer_->transport_version() != QUIC_VERSION_99) { + return PACKET_4BYTE_PACKET_NUMBER; + } + return packet_.packet_number_length; +} + +QuicVariableLengthIntegerLength QuicPacketCreator::GetRetryTokenLengthLength() + const { + if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) && + HasIetfLongHeader() && + EncryptionlevelToLongHeaderType(packet_.encryption_level) == INITIAL) { + return QuicDataWriter::GetVarInt62Len(GetRetryToken().length()); + } + return VARIABLE_LENGTH_INTEGER_LENGTH_0; +} + +QuicStringPiece QuicPacketCreator::GetRetryToken() const { + return retry_token_; +} + +void QuicPacketCreator::SetRetryToken(QuicStringPiece retry_token) { + retry_token_ = QuicString(retry_token); +} + +QuicVariableLengthIntegerLength QuicPacketCreator::GetLengthLength() const { + if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) && + HasIetfLongHeader()) { + QuicLongHeaderType long_header_type = + EncryptionlevelToLongHeaderType(packet_.encryption_level); + if (long_header_type == INITIAL || long_header_type == ZERO_RTT_PROTECTED || + long_header_type == HANDSHAKE) { + return VARIABLE_LENGTH_INTEGER_LENGTH_2; + } + } + return VARIABLE_LENGTH_INTEGER_LENGTH_0; +} + +void QuicPacketCreator::FillPacketHeader(QuicPacketHeader* header) { + header->destination_connection_id = connection_id_; + header->destination_connection_id_included = + GetDestinationConnectionIdIncluded(); + header->source_connection_id = connection_id_; + header->source_connection_id_included = GetSourceConnectionIdIncluded(); + header->reset_flag = false; + header->version_flag = IncludeVersionInHeader(); + if (IncludeNonceInPublicHeader()) { + DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective()); + header->nonce = &diversification_nonce_; + } else { + header->nonce = nullptr; + } + if (!packet_.packet_number.IsInitialized()) { + packet_.packet_number = framer_->first_sending_packet_number(); + } else { + ++packet_.packet_number; + } + header->packet_number = packet_.packet_number; + header->packet_number_length = GetPacketNumberLength(); + header->retry_token_length_length = GetRetryTokenLengthLength(); + header->retry_token = GetRetryToken(); + header->length_length = GetLengthLength(); + header->remaining_packet_length = 0; + if (!HasIetfLongHeader()) { + return; + } + header->long_packet_type = + EncryptionlevelToLongHeaderType(packet_.encryption_level); +} + +bool QuicPacketCreator::AddFrame(const QuicFrame& frame, + bool save_retransmittable_frames, + TransmissionType transmission_type) { + QUIC_DVLOG(1) << ENDPOINT << "Adding frame with transmission type " + << transmission_type << ": " << frame; + if (frame.type == STREAM_FRAME && + frame.stream_frame.stream_id != + QuicUtils::GetCryptoStreamId(framer_->transport_version()) && + packet_.encryption_level == ENCRYPTION_NONE) { + const QuicString error_details = + "Cannot send stream data without encryption."; + QUIC_BUG << error_details; + delegate_->OnUnrecoverableError( + QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA, error_details, + ConnectionCloseSource::FROM_SELF); + return false; + } + size_t frame_len = framer_->GetSerializedFrameLength( + frame, BytesFree(), queued_frames_.empty(), + /* last_frame_in_packet= */ true, GetPacketNumberLength()); + if (frame_len == 0) { + // Current open packet is full. + Flush(); + return false; + } + DCHECK_LT(0u, packet_size_); + + packet_size_ += ExpansionOnNewFrame() + frame_len; + + if (save_retransmittable_frames && + QuicUtils::IsRetransmittableFrame(frame.type)) { + packet_.retransmittable_frames.push_back(frame); + queued_frames_.push_back(frame); + if (QuicUtils::IsHandshakeFrame(frame, framer_->transport_version())) { + packet_.has_crypto_handshake = IS_HANDSHAKE; + } + } else { + queued_frames_.push_back(frame); + } + + if (frame.type == ACK_FRAME) { + packet_.has_ack = true; + packet_.largest_acked = LargestAcked(*frame.ack_frame); + } + if (frame.type == STOP_WAITING_FRAME) { + packet_.has_stop_waiting = true; + } + if (debug_delegate_ != nullptr) { + debug_delegate_->OnFrameAddedToPacket(frame); + } + + // Packet transmission type is determined by the last added retransmittable + // frame. + if (ShouldSetTransmissionTypeForNextFrame() && + QuicUtils::IsRetransmittableFrame(frame.type)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_set_transmission_type_for_next_frame, 2, + 2); + packet_.transmission_type = transmission_type; + } + return true; +} + +void QuicPacketCreator::MaybeAddPadding() { + // The current packet should have no padding bytes because padding is only + // added when this method is called just before the packet is serialized. + DCHECK_EQ(0, packet_.num_padding_bytes); + if (BytesFree() == 0) { + // Don't pad full packets. + return; + } + + if (packet_.transmission_type == PROBING_RETRANSMISSION) { + needs_full_padding_ = true; + } + + if (!needs_full_padding_ && pending_padding_bytes_ == 0) { + // Do not need padding. + return; + } + + if (needs_full_padding_) { + // Full padding does not consume pending padding bytes. + packet_.num_padding_bytes = -1; + } else { + packet_.num_padding_bytes = + std::min<int16_t>(pending_padding_bytes_, BytesFree()); + pending_padding_bytes_ -= packet_.num_padding_bytes; + } + + bool success = + AddFrame(QuicFrame(QuicPaddingFrame(packet_.num_padding_bytes)), false, + packet_.transmission_type); + DCHECK(success); +} + +bool QuicPacketCreator::IncludeNonceInPublicHeader() const { + return have_diversification_nonce_ && + packet_.encryption_level == ENCRYPTION_ZERO_RTT; +} + +bool QuicPacketCreator::IncludeVersionInHeader() const { + if (framer_->transport_version() > QUIC_VERSION_43) { + return packet_.encryption_level < ENCRYPTION_FORWARD_SECURE; + } + return send_version_in_packet_; +} + +void QuicPacketCreator::AddPendingPadding(QuicByteCount size) { + pending_padding_bytes_ += size; +} + +bool QuicPacketCreator::StreamFrameStartsWithChlo( + const QuicStreamFrame& frame) const { + if (framer_->perspective() == Perspective::IS_SERVER || + frame.stream_id != + QuicUtils::GetCryptoStreamId(framer_->transport_version()) || + frame.data_length < sizeof(kCHLO)) { + return false; + } + return framer_->StartsWithChlo(frame.stream_id, frame.offset); +} + +void QuicPacketCreator::SetConnectionIdIncluded( + QuicConnectionIdIncluded connection_id_included) { + DCHECK(connection_id_included == CONNECTION_ID_PRESENT || + connection_id_included == CONNECTION_ID_ABSENT); + DCHECK(framer_->perspective() == Perspective::IS_SERVER || + connection_id_included != CONNECTION_ID_ABSENT); + connection_id_included_ = connection_id_included; +} + +void QuicPacketCreator::SetTransmissionType(TransmissionType type) { + DCHECK(can_set_transmission_type_); + + if (!ShouldSetTransmissionTypeForNextFrame()) { + QUIC_DVLOG_IF(1, type != packet_.transmission_type) + << ENDPOINT << "Setting Transmission type to " + << QuicUtils::TransmissionTypeToString(type); + + packet_.transmission_type = type; + } +} + +QuicPacketLength QuicPacketCreator::GetLargestMessagePayload() const { + if (framer_->transport_version() <= QUIC_VERSION_44) { + return 0; + } + const size_t packet_header_size = GetPacketHeaderSize( + framer_->transport_version(), GetDestinationConnectionIdLength(), + GetSourceConnectionIdLength(), IncludeVersionInHeader(), + IncludeNonceInPublicHeader(), GetPacketNumberLength(), + GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength()); + // This is the largest possible message payload when the length field is + // omitted. + return max_plaintext_size_ - + std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize); +} + +bool QuicPacketCreator::HasIetfLongHeader() const { + return framer_->transport_version() > QUIC_VERSION_43 && + packet_.encryption_level < ENCRYPTION_FORWARD_SECURE; +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h new file mode 100644 index 0000000..f4512c3 --- /dev/null +++ b/quic/core/quic_packet_creator.h
@@ -0,0 +1,416 @@ +// Copyright (c) 2012 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. + +// Accumulates frames for the next packet until more frames no longer fit or +// it's time to create a packet from them. + +#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_CREATOR_H_ +#define QUICHE_QUIC_CORE_QUIC_PACKET_CREATOR_H_ + +#include <cstddef> +#include <memory> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.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_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { +namespace test { +class QuicPacketCreatorPeer; +} + +class QUIC_EXPORT_PRIVATE QuicPacketCreator { + public: + // A delegate interface for further processing serialized packet. + class QUIC_EXPORT_PRIVATE DelegateInterface + : public QuicConnectionCloseDelegateInterface { + public: + ~DelegateInterface() override {} + // Get a buffer of kMaxPacketSize bytes to serialize the next packet. + // If return nullptr, QuicPacketCreator will serialize on a stack buffer. + virtual char* GetPacketBuffer() = 0; + // Called when a packet is serialized. Delegate does not take the ownership + // of |serialized_packet|, but takes ownership of any frames it removes + // from |packet.retransmittable_frames|. + virtual void OnSerializedPacket(SerializedPacket* serialized_packet) = 0; + }; + + // Interface which gets callbacks from the QuicPacketCreator at interesting + // points. Implementations must not mutate the state of the creator + // as a result of these callbacks. + class QUIC_EXPORT_PRIVATE DebugDelegate { + public: + virtual ~DebugDelegate() {} + + // Called when a frame has been added to the current packet. + virtual void OnFrameAddedToPacket(const QuicFrame& frame) {} + }; + + QuicPacketCreator(QuicConnectionId connection_id, + QuicFramer* framer, + DelegateInterface* delegate); + QuicPacketCreator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random, + DelegateInterface* delegate); + QuicPacketCreator(const QuicPacketCreator&) = delete; + QuicPacketCreator& operator=(const QuicPacketCreator&) = delete; + + ~QuicPacketCreator(); + + // Makes the framer not serialize the protocol version in sent packets. + void StopSendingVersion(); + + // SetDiversificationNonce sets the nonce that will be sent in each public + // header of packets encrypted at the initial encryption level. Should only + // be called by servers. + void SetDiversificationNonce(const DiversificationNonce& nonce); + + // Update the packet number length to use in future packets as soon as it + // can be safely changed. + // TODO(fayang): Directly set packet number length instead of compute it in + // creator. + void UpdatePacketNumberLength(QuicPacketNumber least_packet_awaited_by_peer, + QuicPacketCount max_packets_in_flight); + + // The overhead the framing will add for a packet with one frame. + static size_t StreamFramePacketOverhead( + QuicTransportVersion version, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool include_version, + bool include_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicVariableLengthIntegerLength length_length, + QuicStreamOffset offset); + + // Returns false and flushes all pending frames if current open packet is + // full. + // If current packet is not full, creates a stream frame that fits into the + // open packet and adds it to the packet. + bool ConsumeData(QuicStreamId id, + size_t write_length, + size_t iov_offset, + QuicStreamOffset offset, + bool fin, + bool needs_full_padding, + TransmissionType transmission_type, + QuicFrame* frame); + + // Creates a CRYPTO frame that fits into the current packet (which must be + // empty) and adds it to the packet. + bool ConsumeCryptoData(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset, + TransmissionType transmission_type, + QuicFrame* frame); + + // Returns true if current open packet can accommodate more stream frames of + // stream |id| at |offset| and data length |data_size|, false otherwise. + bool HasRoomForStreamFrame(QuicStreamId id, + QuicStreamOffset offset, + size_t data_size); + + // Returns true if current open packet can accommodate a message frame of + // |length|. + bool HasRoomForMessageFrame(QuicByteCount length); + + // Re-serializes frames with the original packet's packet number length. + // Used for retransmitting packets to ensure they aren't too long. + void ReserializeAllFrames(const QuicPendingRetransmission& retransmission, + char* buffer, + size_t buffer_len); + + // Serializes all added frames into a single packet and invokes the delegate_ + // to further process the SerializedPacket. + void Flush(); + + // Optimized method to create a QuicStreamFrame and serialize it. Adds the + // QuicStreamFrame to the returned SerializedPacket. Sets + // |num_bytes_consumed| to the number of bytes consumed to create the + // QuicStreamFrame. + void CreateAndSerializeStreamFrame(QuicStreamId id, + size_t write_length, + QuicStreamOffset iov_offset, + QuicStreamOffset stream_offset, + bool fin, + TransmissionType transmission_type, + size_t* num_bytes_consumed); + + // Returns true if there are frames pending to be serialized. + bool HasPendingFrames() const; + + // Returns true if there are retransmittable frames pending to be serialized. + bool HasPendingRetransmittableFrames() const; + + // Returns true if there are stream frames for |id| pending to be serialized. + bool HasPendingStreamFramesOfStream(QuicStreamId id) const; + + // Returns the number of bytes which are available to be used by additional + // frames in the packet. Since stream frames are slightly smaller when they + // are the last frame in a packet, this method will return a different + // value than max_packet_size - PacketSize(), in this case. + size_t BytesFree(); + + // Returns the number of bytes that the packet will expand by if a new frame + // is added to the packet. If the last frame was a stream frame, it will + // expand slightly when a new frame is added, and this method returns the + // amount of expected expansion. + size_t ExpansionOnNewFrame() const; + + // Returns the number of bytes in the current packet, including the header, + // if serialized with the current frames. Adding a frame to the packet + // may change the serialized length of existing frames, as per the comment + // in BytesFree. + size_t PacketSize(); + + // Tries to add |frame| to the packet creator's list of frames to be + // serialized. If the frame does not fit into the current packet, flushes the + // packet and returns false. + bool AddSavedFrame(const QuicFrame& frame, + TransmissionType transmission_type); + + // Identical to AddSavedFrame, but allows the frame to be padded. + bool AddPaddedSavedFrame(const QuicFrame& frame, + TransmissionType transmission_type); + + // Creates a version negotiation packet which supports |supported_versions|. + std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket( + bool ietf_quic, + const ParsedQuicVersionVector& supported_versions); + + // Creates a connectivity probing packet for versions prior to version 99. + OwningSerializedPacketPointer SerializeConnectivityProbingPacket(); + + // Create connectivity probing request and response packets using PATH + // CHALLENGE and PATH RESPONSE frames, respectively, for version 99/IETF QUIC. + // SerializePathChallengeConnectivityProbingPacket will pad the packet to be + // MTU bytes long. + OwningSerializedPacketPointer SerializePathChallengeConnectivityProbingPacket( + QuicPathFrameBuffer* payload); + + // If |is_padded| is true then SerializePathResponseConnectivityProbingPacket + // will pad the packet to be MTU bytes long, else it will not pad the packet. + // |payloads| is cleared. + OwningSerializedPacketPointer SerializePathResponseConnectivityProbingPacket( + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded); + + // Returns a dummy packet that is valid but contains no useful information. + static SerializedPacket NoPacket(); + + // Returns length of destination connection ID to send over the wire. + QuicConnectionIdLength GetDestinationConnectionIdLength() const; + + // Returns length of source connection ID to send over the wire. + QuicConnectionIdLength GetSourceConnectionIdLength() const; + + // Sets whether the connection ID should be sent over the wire. + void SetConnectionIdIncluded(QuicConnectionIdIncluded connection_id_included); + + // Sets the encryption level that will be applied to new packets. + void set_encryption_level(EncryptionLevel level) { + packet_.encryption_level = level; + } + + // packet number of the last created packet, or 0 if no packets have been + // created. + QuicPacketNumber packet_number() const { return packet_.packet_number; } + + QuicByteCount max_packet_length() const { return max_packet_length_; } + + bool has_ack() const { return packet_.has_ack; } + + bool has_stop_waiting() const { return packet_.has_stop_waiting; } + + // Sets the encrypter to use for the encryption level and updates the max + // plaintext size. + void SetEncrypter(EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter); + + // Indicates whether the packet creator is in a state where it can change + // current maximum packet length. + bool CanSetMaxPacketLength() const; + + // Sets the maximum packet length. + void SetMaxPacketLength(QuicByteCount length); + + // Increases pending_padding_bytes by |size|. Pending padding will be sent by + // MaybeAddPadding(). + void AddPendingPadding(QuicByteCount size); + + // Sets transmission type of next constructed packets. + void SetTransmissionType(TransmissionType type); + + // Sets the retry token to be sent over the wire in v99 IETF Initial packets. + void SetRetryToken(QuicStringPiece retry_token); + + // Returns the largest payload that will fit into a single MESSAGE frame. + QuicPacketLength GetLargestMessagePayload() const; + + void set_debug_delegate(DebugDelegate* debug_delegate) { + debug_delegate_ = debug_delegate; + } + + void set_can_set_transmission_type(bool can_set_transmission_type) { + can_set_transmission_type_ = can_set_transmission_type; + } + + bool ShouldSetTransmissionTypeForNextFrame() const { + return can_set_transmission_type_ && set_transmission_type_for_next_frame_; + } + + QuicByteCount pending_padding_bytes() const { return pending_padding_bytes_; } + + QuicTransportVersion transport_version() const { + return framer_->transport_version(); + } + + private: + friend class test::QuicPacketCreatorPeer; + + // Creates a stream frame which fits into the current open packet. If + // |write_length| is 0 and fin is true, the expected behavior is to consume + // the fin but return 0. + void CreateStreamFrame(QuicStreamId id, + size_t write_length, + size_t iov_offset, + QuicStreamOffset offset, + bool fin, + QuicFrame* frame); + + // Creates a CRYPTO frame which fits into the current open packet. Returns + // false if there isn't enough room in the current open packet for a CRYPTO + // frame, and true if there is. + bool CreateCryptoFrame(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset, + QuicFrame* frame); + + void FillPacketHeader(QuicPacketHeader* header); + + // Adds a |frame| if there is space and returns false and flushes all pending + // frames if there isn't room. If |save_retransmittable_frames| is true, + // saves the |frame| in the next SerializedPacket. + bool AddFrame(const QuicFrame& frame, + bool save_retransmittable_frames, + TransmissionType transmission_type); + + // Adds a padding frame to the current packet (if there is space) when (1) + // current packet needs full padding or (2) there are pending paddings. + void MaybeAddPadding(); + + // Serializes all frames which have been added and adds any which should be + // retransmitted to packet_.retransmittable_frames. All frames must fit into + // a single packet. + // Fails if |buffer_len| isn't long enough for the encrypted packet. + void SerializePacket(char* encrypted_buffer, size_t buffer_len); + + // Called after a new SerialiedPacket is created to call the delegate's + // OnSerializedPacket and reset state. + void OnSerializedPacket(); + + // Clears all fields of packet_ that should be cleared between serializations. + void ClearPacket(); + + // Returns true if a diversification nonce should be included in the current + // packet's header. + bool IncludeNonceInPublicHeader() const; + + // Returns true if version should be included in current packet's header. + bool IncludeVersionInHeader() const; + + // Returns length of packet number to send over the wire. + // packet_.packet_number_length should never be read directly, use this + // function instead. + QuicPacketNumberLength GetPacketNumberLength() const; + + // Returns whether the destination connection ID is sent over the wire. + QuicConnectionIdIncluded GetDestinationConnectionIdIncluded() const; + + // Returns whether the source connection ID is sent over the wire. + QuicConnectionIdIncluded GetSourceConnectionIdIncluded() const; + + // Returns length of the retry token variable length integer to send over the + // wire. Is non-zero for v99 IETF Initial packets. + QuicVariableLengthIntegerLength GetRetryTokenLengthLength() const; + + // Returns the retry token to send over the wire, only sent in + // v99 IETF Initial packets. + QuicStringPiece GetRetryToken() const; + + // Returns length of the length variable length integer to send over the + // wire. Is non-zero for v99 IETF Initial, 0-RTT or Handshake packets. + QuicVariableLengthIntegerLength GetLengthLength() const; + + // Returns true if |frame| starts with CHLO. + bool StreamFrameStartsWithChlo(const QuicStreamFrame& frame) const; + + // Returns true if packet under construction has IETF long header. + bool HasIetfLongHeader() const; + + // Does not own these delegates or the framer. + DelegateInterface* delegate_; + DebugDelegate* debug_delegate_; + QuicFramer* framer_; + QuicRandom* random_; + + // Controls whether version should be included while serializing the packet. + // send_version_in_packet_ should never be read directly, use + // IncludeVersionInHeader() instead. + bool send_version_in_packet_; + // If true, then |diversification_nonce_| will be included in the header of + // all packets created at the initial encryption level. + bool have_diversification_nonce_; + DiversificationNonce diversification_nonce_; + // Maximum length including headers and encryption (UDP payload length.) + QuicByteCount max_packet_length_; + size_t max_plaintext_size_; + // Whether the connection_id is sent over the wire. + QuicConnectionIdIncluded connection_id_included_; + + // Frames to be added to the next SerializedPacket + QuicFrames queued_frames_; + + // packet_size should never be read directly, use PacketSize() instead. + // TODO(ianswett): Move packet_size_ into SerializedPacket once + // QuicEncryptedPacket has been flattened into SerializedPacket. + size_t packet_size_; + QuicConnectionId connection_id_; + + // Packet used to invoke OnSerializedPacket. + SerializedPacket packet_; + + // Retry token to send over the wire in v99 IETF Initial packets. + QuicString retry_token_; + + // Pending padding bytes to send. Pending padding bytes will be sent in next + // packet(s) (after all other frames) if current constructed packet does not + // have room to send all of them. + QuicByteCount pending_padding_bytes_; + + // Indicates whether current constructed packet needs full padding to max + // packet size. Please note, full padding does not consume pending padding + // bytes. + bool needs_full_padding_; + + // If true, packet_'s transmission type is only set by + // SetPacketTransmissionType and does not get cleared in ClearPacket. + bool can_set_transmission_type_; + + // Latched value of --quic_set_transmission_type_for_next_frame. Don't use + // this variable directly, use ShouldSetTransmissionTypeForNextFrame instead. + bool set_transmission_type_for_next_frame_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PACKET_CREATOR_H_
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc new file mode 100644 index 0000000..c15955c --- /dev/null +++ b/quic/core/quic_packet_creator_test.cc
@@ -0,0 +1,1668 @@ +// Copyright (c) 2012 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/quic_packet_creator.h" + +#include <cstdint> +#include <memory> +#include <ostream> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.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_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h" + +using testing::_; +using testing::DoAll; +using testing::InSequence; +using testing::Invoke; +using testing::Return; +using testing::SaveArg; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +// Run tests with combinations of {ParsedQuicVersion, +// ToggleVersionSerialization}. +struct TestParams { + TestParams(ParsedQuicVersion version, bool version_serialization) + : version(version), version_serialization(version_serialization) {} + + friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { + os << "{ version: " << ParsedQuicVersionToString(p.version) + << " include version: " << p.version_serialization << " }"; + return os; + } + + ParsedQuicVersion version; + bool version_serialization; +}; + +// Constructs various test permutations. +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + ParsedQuicVersionVector all_supported_versions = AllSupportedVersions(); + for (size_t i = 0; i < all_supported_versions.size(); ++i) { + params.push_back(TestParams(all_supported_versions[i], true)); + params.push_back(TestParams(all_supported_versions[i], false)); + } + return params; +} + +class TestPacketCreator : public QuicPacketCreator { + public: + TestPacketCreator(QuicConnectionId connection_id, + QuicFramer* framer, + DelegateInterface* delegate, + SimpleDataProducer* producer) + : QuicPacketCreator(connection_id, framer, delegate), + producer_(producer), + version_(framer->transport_version()) {} + + bool ConsumeData(QuicStreamId id, + const struct iovec* iov, + int iov_count, + size_t total_length, + size_t iov_offset, + QuicStreamOffset offset, + bool fin, + bool needs_full_padding, + TransmissionType transmission_type, + QuicFrame* frame) { + // Save data before data is consumed. + QuicByteCount data_length = total_length - iov_offset; + if (data_length > 0) { + producer_->SaveStreamData(id, iov, iov_count, iov_offset, data_length); + } + return QuicPacketCreator::ConsumeData(id, data_length, iov_offset, offset, + fin, needs_full_padding, + transmission_type, frame); + } + + void StopSendingVersion() { + if (version_ > QUIC_VERSION_43) { + set_encryption_level(ENCRYPTION_FORWARD_SECURE); + return; + } + QuicPacketCreator::StopSendingVersion(); + } + + SimpleDataProducer* producer_; + QuicTransportVersion version_; +}; + +class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { + public: + void ClearSerializedPacketForTests(SerializedPacket* serialized_packet) { + if (serialized_packet == nullptr) { + return; + } + ClearSerializedPacket(serialized_packet); + } + + void SaveSerializedPacket(SerializedPacket* serialized_packet) { + if (serialized_packet == nullptr) { + return; + } + delete[] serialized_packet_.encrypted_buffer; + serialized_packet_ = *serialized_packet; + serialized_packet_.encrypted_buffer = CopyBuffer(*serialized_packet); + serialized_packet->retransmittable_frames.clear(); + } + + void DeleteSerializedPacket() { + delete[] serialized_packet_.encrypted_buffer; + serialized_packet_.encrypted_buffer = nullptr; + ClearSerializedPacket(&serialized_packet_); + } + + protected: + QuicPacketCreatorTest() + : connection_id_(TestConnectionId(2)), + server_framer_(SupportedVersions(GetParam().version), + QuicTime::Zero(), + Perspective::IS_SERVER, + connection_id_.length()), + client_framer_(SupportedVersions(GetParam().version), + QuicTime::Zero(), + Perspective::IS_CLIENT, + connection_id_.length()), + data_("foo"), + creator_(connection_id_, &client_framer_, &delegate_, &producer_), + serialized_packet_(creator_.NoPacket()) { + EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr)); + creator_.SetEncrypter(ENCRYPTION_ZERO_RTT, QuicMakeUnique<NullEncrypter>( + Perspective::IS_CLIENT)); + creator_.SetEncrypter( + ENCRYPTION_FORWARD_SECURE, + QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT)); + client_framer_.set_visitor(&framer_visitor_); + server_framer_.set_visitor(&framer_visitor_); + client_framer_.set_data_producer(&producer_); + } + + ~QuicPacketCreatorTest() override { + delete[] serialized_packet_.encrypted_buffer; + ClearSerializedPacket(&serialized_packet_); + } + + SerializedPacket SerializeAllFrames(const QuicFrames& frames) { + SerializedPacket packet = QuicPacketCreatorPeer::SerializeAllFrames( + &creator_, frames, buffer_, kMaxPacketSize); + EXPECT_EQ(QuicPacketCreatorPeer::GetEncryptionLevel(&creator_), + packet.encryption_level); + return packet; + } + + void ProcessPacket(const SerializedPacket& packet) { + QuicEncryptedPacket encrypted_packet(packet.encrypted_buffer, + packet.encrypted_length); + server_framer_.ProcessPacket(encrypted_packet); + } + + void CheckStreamFrame(const QuicFrame& frame, + QuicStreamId stream_id, + const QuicString& data, + QuicStreamOffset offset, + bool fin) { + EXPECT_EQ(STREAM_FRAME, frame.type); + EXPECT_EQ(stream_id, frame.stream_frame.stream_id); + char buf[kMaxPacketSize]; + QuicDataWriter writer(kMaxPacketSize, buf, HOST_BYTE_ORDER); + if (frame.stream_frame.data_length > 0) { + producer_.WriteStreamData(stream_id, frame.stream_frame.offset, + frame.stream_frame.data_length, &writer); + } + EXPECT_EQ(data, QuicStringPiece(buf, frame.stream_frame.data_length)); + EXPECT_EQ(offset, frame.stream_frame.offset); + EXPECT_EQ(fin, frame.stream_frame.fin); + } + + // Returns the number of bytes consumed by the header of packet, including + // the version. + size_t GetPacketHeaderOverhead(QuicTransportVersion version) { + return GetPacketHeaderSize( + version, creator_.GetDestinationConnectionIdLength(), + creator_.GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), 0, + QuicPacketCreatorPeer::GetLengthLength(&creator_)); + } + + // Returns the number of bytes of overhead that will be added to a packet + // of maximum length. + size_t GetEncryptionOverhead() { + return creator_.max_packet_length() - + client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); + } + + // Returns the number of bytes consumed by the non-data fields of a stream + // frame, assuming it is the last frame in the packet + size_t GetStreamFrameOverhead(QuicTransportVersion version) { + return QuicFramer::GetMinStreamFrameSize( + version, GetNthClientInitiatedStreamId(1), kOffset, true, + /* data_length= */ 0); + } + + QuicPendingRetransmission CreateRetransmission( + const QuicFrames& retransmittable_frames, + bool has_crypto_handshake, + int num_padding_bytes, + EncryptionLevel encryption_level, + QuicPacketNumberLength packet_number_length) { + return QuicPendingRetransmission(QuicPacketNumber(1u), NOT_RETRANSMISSION, + retransmittable_frames, + has_crypto_handshake, num_padding_bytes, + encryption_level, packet_number_length); + } + + bool IsDefaultTestConfiguration() { + TestParams p = GetParam(); + return p.version == AllSupportedVersions()[0] && p.version_serialization; + } + + QuicStreamId GetNthClientInitiatedStreamId(int n) const { + return QuicUtils::GetHeadersStreamId(creator_.transport_version()) + n * 2; + } + + static const QuicStreamOffset kOffset = 0u; + + char buffer_[kMaxPacketSize]; + QuicConnectionId connection_id_; + QuicFrames frames_; + QuicFramer server_framer_; + QuicFramer client_framer_; + StrictMock<MockFramerVisitor> framer_visitor_; + StrictMock<MockPacketCreatorDelegate> delegate_; + QuicString data_; + struct iovec iov_; + TestPacketCreator creator_; + SerializedPacket serialized_packet_; + SimpleDataProducer producer_; + SimpleBufferAllocator allocator_; +}; + +// Run all packet creator tests with all supported versions of QUIC, and with +// and without version in the packet header, as well as doing a run for each +// length of truncated connection id. +INSTANTIATE_TEST_SUITE_P(QuicPacketCreatorTests, + QuicPacketCreatorTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QuicPacketCreatorTest, SerializeFrames) { + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + frames_.push_back(QuicFrame(new QuicAckFrame(InitAckFrame(1)))); + frames_.push_back(QuicFrame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), false, + 0u, QuicStringPiece()))); + frames_.push_back(QuicFrame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), true, + 0u, QuicStringPiece()))); + SerializedPacket serialized = SerializeAllFrames(frames_); + EXPECT_EQ(level, serialized.encryption_level); + delete frames_[0].ack_frame; + frames_.clear(); + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, + OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, OnAckFrameEnd(QuicPacketNumber(1))) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized); + } +} + +TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { + if (client_framer_.transport_version() > QUIC_VERSION_43) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + } + // If the original packet number length, the current packet number + // length, and the configured send packet number length are different, the + // retransmit must sent with the original length and the others do not change. + QuicPacketCreatorPeer::SetPacketNumberLength(&creator_, + PACKET_2BYTE_PACKET_NUMBER); + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + /*fin=*/false, 0u, QuicStringPiece()); + QuicFrames frames; + frames.push_back(QuicFrame(stream_frame)); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, true /* has_crypto_handshake */, -1 /* needs full padding */, + ENCRYPTION_NONE, PACKET_4BYTE_PACKET_NUMBER)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + // The packet number length is updated after every packet is sent, + // so there is no need to restore the old length after sending. + EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, + serialized_packet_.packet_number_length); + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized_packet_); +} + +TEST_P(QuicPacketCreatorTest, ReserializeCryptoFrameWithForwardSecurity) { + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + /*fin=*/false, 0u, QuicStringPiece()); + QuicFrames frames; + frames.push_back(QuicFrame(stream_frame)); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, true /* has_crypto_handshake */, -1 /* needs full padding */, + ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(ENCRYPTION_NONE, serialized_packet_.encryption_level); +} + +TEST_P(QuicPacketCreatorTest, ReserializeFrameWithForwardSecurity) { + QuicStreamFrame stream_frame(0u, /*fin=*/false, 0u, QuicStringPiece()); + QuicFrames frames; + frames.push_back(QuicFrame(stream_frame)); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, false /* has_crypto_handshake */, 0 /* no padding */, + ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, serialized_packet_.encryption_level); +} + +TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPadding) { + QuicFrame frame; + MakeIOVector("fake handshake message data", &iov_); + producer_.SaveStreamData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, 0u, iov_.iov_len); + QuicPacketCreatorPeer::CreateStreamFrame( + &creator_, + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + iov_.iov_len, 0u, 0u, false, &frame); + QuicFrames frames; + frames.push_back(frame); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, true /* has_crypto_handshake */, -1 /* needs full padding */, + ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length); +} + +TEST_P(QuicPacketCreatorTest, DoNotRetransmitPendingPadding) { + QuicFrame frame; + MakeIOVector("fake message data", &iov_); + producer_.SaveStreamData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, 0u, iov_.iov_len); + QuicPacketCreatorPeer::CreateStreamFrame( + &creator_, + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + iov_.iov_len, 0u, 0u, false, &frame); + + const int kNumPaddingBytes1 = 4; + int packet_size = 0; + { + QuicFrames frames; + frames.push_back(frame); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, false /* has_crypto_handshake */, + kNumPaddingBytes1 /* padding bytes */, ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + packet_size = serialized_packet_.encrypted_length; + } + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + // Pending paddings are not retransmitted. + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(0); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized_packet_); + + const int kNumPaddingBytes2 = 44; + QuicFrames frames; + frames.push_back(frame); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, false /* has_crypto_handshake */, + kNumPaddingBytes2 /* padding bytes */, ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + + EXPECT_EQ(packet_size, serialized_packet_.encrypted_length); +} + +TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPacketAndPadding) { + const size_t overhead = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.transport_version()); + size_t capacity = kDefaultMaxPacketSize - overhead; + for (int delta = -5; delta <= 0; ++delta) { + QuicString data(capacity + delta, 'A'); + size_t bytes_free = 0 - delta; + + QuicFrame frame; + MakeIOVector(data, &iov_); + SimpleDataProducer producer; + producer.SaveStreamData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, 0u, iov_.iov_len); + QuicPacketCreatorPeer::framer(&creator_)->set_data_producer(&producer); + QuicPacketCreatorPeer::CreateStreamFrame( + &creator_, + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + iov_.iov_len, 0, kOffset, false, &frame); + QuicFrames frames; + frames.push_back(frame); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, true /* has_crypto_handshake */, -1 /* needs full padding */, + ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + + // If there is not enough space in the packet to fit a padding frame + // (1 byte) and to expand the stream frame (another 2 bytes) the packet + // will not be padded. + if (bytes_free < 3) { + EXPECT_EQ(kDefaultMaxPacketSize - bytes_free, + serialized_packet_.encrypted_length); + } else { + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length); + } + + frames_.clear(); + } +} + +TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) { + QuicConnectionCloseFrame frame; + frame.error_code = QUIC_NO_ERROR; + frame.error_details = "error"; + + QuicFrames frames; + frames.push_back(QuicFrame(&frame)); + SerializedPacket serialized = SerializeAllFrames(frames); + EXPECT_EQ(ENCRYPTION_NONE, serialized.encryption_level); + ASSERT_EQ(QuicPacketNumber(1u), serialized.packet_number); + ASSERT_EQ(QuicPacketNumber(1u), creator_.packet_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPacket(serialized); +} + +TEST_P(QuicPacketCreatorTest, ConsumeCryptoData) { + QuicString data = "crypto data"; + QuicFrame frame; + ASSERT_TRUE(creator_.ConsumeCryptoData(ENCRYPTION_NONE, data.length(), 0, + NOT_RETRANSMISSION, &frame)); + EXPECT_EQ(frame.crypto_frame->data_length, data.length()); + EXPECT_TRUE(creator_.HasPendingFrames()); +} + +TEST_P(QuicPacketCreatorTest, ConsumeData) { + QuicFrame frame; + MakeIOVector("test", &iov_); + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame)); + size_t consumed = frame.stream_frame.data_length; + EXPECT_EQ(4u, consumed); + CheckStreamFrame( + frame, QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + "test", 0u, false); + EXPECT_TRUE(creator_.HasPendingFrames()); +} + +TEST_P(QuicPacketCreatorTest, ConsumeDataFin) { + QuicFrame frame; + MakeIOVector("test", &iov_); + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, true, false, NOT_RETRANSMISSION, &frame)); + size_t consumed = frame.stream_frame.data_length; + EXPECT_EQ(4u, consumed); + CheckStreamFrame( + frame, QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + "test", 0u, true); + EXPECT_TRUE(creator_.HasPendingFrames()); +} + +TEST_P(QuicPacketCreatorTest, ConsumeDataFinOnly) { + QuicFrame frame; + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), nullptr, + 0u, 0u, 0u, 0u, true, false, NOT_RETRANSMISSION, &frame)); + size_t consumed = frame.stream_frame.data_length; + EXPECT_EQ(0u, consumed); + CheckStreamFrame( + frame, QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + QuicString(), 0u, true); + EXPECT_TRUE(creator_.HasPendingFrames()); +} + +TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + const size_t overhead = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + GetEncryptionOverhead(); + for (size_t i = overhead; i < overhead + 100; ++i) { + creator_.SetMaxPacketLength(i); + const bool should_have_room = + i > + overhead + GetStreamFrameOverhead(client_framer_.transport_version()); + ASSERT_EQ(should_have_room, + creator_.HasRoomForStreamFrame(GetNthClientInitiatedStreamId(1), + kOffset, /* data_size=*/0xffff)); + if (should_have_room) { + QuicFrame frame; + MakeIOVector("testdata", &iov_); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke( + this, &QuicPacketCreatorTest::ClearSerializedPacketForTests)); + ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_, + 1u, iov_.iov_len, 0u, kOffset, false, + false, NOT_RETRANSMISSION, &frame)); + size_t bytes_consumed = frame.stream_frame.data_length; + EXPECT_LT(0u, bytes_consumed); + creator_.Flush(); + } + } +} + +TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + // Compute the total overhead for a single frame in packet. + const size_t overhead = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.transport_version()); + size_t capacity = kDefaultMaxPacketSize - overhead; + // Now, test various sizes around this size. + for (int delta = -5; delta <= 5; ++delta) { + QuicString data(capacity + delta, 'A'); + size_t bytes_free = delta > 0 ? 0 : 0 - delta; + QuicFrame frame; + MakeIOVector(data, &iov_); + ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_, + 1u, iov_.iov_len, 0u, kOffset, false, + false, NOT_RETRANSMISSION, &frame)); + + // BytesFree() returns bytes available for the next frame, which will + // be two bytes smaller since the stream frame would need to be grown. + EXPECT_EQ(2u, creator_.ExpansionOnNewFrame()); + size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2; + EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta; + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.Flush(); + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + DeleteSerializedPacket(); + } +} + +TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { + // Compute the total overhead for a single frame in packet. + const size_t overhead = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.transport_version()); + ASSERT_GT(kMaxPacketSize, overhead); + size_t capacity = kDefaultMaxPacketSize - overhead; + // Now, test various sizes around this size. + for (int delta = -5; delta <= 5; ++delta) { + QuicString data(capacity + delta, 'A'); + size_t bytes_free = delta > 0 ? 0 : 0 - delta; + + QuicFrame frame; + MakeIOVector(data, &iov_); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly( + Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION, + &frame)); + size_t bytes_consumed = frame.stream_frame.data_length; + EXPECT_LT(0u, bytes_consumed); + creator_.Flush(); + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + // If there is not enough space in the packet to fit a padding frame + // (1 byte) and to expand the stream frame (another 2 bytes) the packet + // will not be padded. + if (bytes_free < 3) { + EXPECT_EQ(kDefaultMaxPacketSize - bytes_free, + serialized_packet_.encrypted_length); + } else { + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length); + } + DeleteSerializedPacket(); + } +} + +TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + // Compute the total overhead for a single frame in packet. + const size_t overhead = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.transport_version()); + ASSERT_GT(kDefaultMaxPacketSize, overhead); + size_t capacity = kDefaultMaxPacketSize - overhead; + // Now, test various sizes around this size. + for (int delta = -5; delta <= 5; ++delta) { + QuicString data(capacity + delta, 'A'); + size_t bytes_free = delta > 0 ? 0 : 0 - delta; + + QuicFrame frame; + MakeIOVector(data, &iov_); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_, + 1u, iov_.iov_len, 0u, kOffset, false, + false, NOT_RETRANSMISSION, &frame)); + size_t bytes_consumed = frame.stream_frame.data_length; + EXPECT_LT(0u, bytes_consumed); + creator_.Flush(); + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + if (bytes_free > 0) { + EXPECT_EQ(kDefaultMaxPacketSize - bytes_free, + serialized_packet_.encrypted_length); + } else { + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length); + } + DeleteSerializedPacket(); + } +} + +TEST_P(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { + QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); + ParsedQuicVersionVector versions; + versions.push_back(test::QuicVersionMax()); + const bool ietf_quic = GetParam().version.transport_version > QUIC_VERSION_43; + std::unique_ptr<QuicEncryptedPacket> encrypted( + creator_.SerializeVersionNegotiationPacket(ietf_quic, versions)); + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_)); + } + QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_CLIENT); + client_framer_.ProcessPacket(*encrypted); +} + +TEST_P(QuicPacketCreatorTest, SerializeConnectivityProbingPacket) { + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + + creator_.set_encryption_level(level); + + OwningSerializedPacketPointer encrypted; + if (GetParam().version.transport_version == QUIC_VERSION_99) { + QuicPathFrameBuffer payload = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; + encrypted = + creator_.SerializePathChallengeConnectivityProbingPacket(&payload); + } else { + encrypted = creator_.SerializeConnectivityProbingPacket(); + } + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + if (GetParam().version.transport_version == QUIC_VERSION_99) { + EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + } else { + EXPECT_CALL(framer_visitor_, OnPingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + } + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, SerializePathChallengeProbePacket) { + if (GetParam().version.transport_version != QUIC_VERSION_99) { + return; + } + QuicPathFrameBuffer payload = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + + creator_.set_encryption_level(level); + + OwningSerializedPacketPointer encrypted( + creator_.SerializePathChallengeConnectivityProbingPacket(&payload)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadPadded) { + if (GetParam().version.transport_version != QUIC_VERSION_99) { + return; + } + QuicPathFrameBuffer payload0 = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + + OwningSerializedPacketPointer encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, + true)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, + SerializePathResponseProbePacket1PayloadUnPadded) { + if (GetParam().version.transport_version != QUIC_VERSION_99) { + return; + } + QuicPathFrameBuffer payload0 = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + + OwningSerializedPacketPointer encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, + false)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsPadded) { + if (GetParam().version.transport_version != QUIC_VERSION_99) { + return; + } + QuicPathFrameBuffer payload0 = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; + QuicPathFrameBuffer payload1 = { + {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + + OwningSerializedPacketPointer encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, + true)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, + SerializePathResponseProbePacket2PayloadsUnPadded) { + if (GetParam().version.transport_version != QUIC_VERSION_99) { + return; + } + QuicPathFrameBuffer payload0 = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; + QuicPathFrameBuffer payload1 = { + {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + + OwningSerializedPacketPointer encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, + false)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsPadded) { + if (GetParam().version.transport_version != QUIC_VERSION_99) { + return; + } + QuicPathFrameBuffer payload0 = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; + QuicPathFrameBuffer payload1 = { + {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; + QuicPathFrameBuffer payload2 = { + {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}}; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); + + OwningSerializedPacketPointer encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, + true)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, + SerializePathResponseProbePacket3PayloadsUnpadded) { + if (GetParam().version.transport_version != QUIC_VERSION_99) { + return; + } + QuicPathFrameBuffer payload0 = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; + QuicPathFrameBuffer payload1 = { + {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; + QuicPathFrameBuffer payload2 = { + {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}}; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) { + EncryptionLevel level = static_cast<EncryptionLevel>(i); + creator_.set_encryption_level(level); + + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); + + OwningSerializedPacketPointer encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, + false)); + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); + } +} + +TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { + if (GetParam().version.transport_version > QUIC_VERSION_43 && + GetParam().version.transport_version != QUIC_VERSION_99) { + EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + } else { + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + } + + QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64); + creator_.UpdatePacketNumberLength(QuicPacketNumber(2), + 10000 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + + QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256); + creator_.UpdatePacketNumberLength(QuicPacketNumber(2), + 10000 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + + QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256 * 256); + creator_.UpdatePacketNumberLength(QuicPacketNumber(2), + 10000 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + + QuicPacketCreatorPeer::SetPacketNumber(&creator_, + UINT64_C(64) * 256 * 256 * 256 * 256); + creator_.UpdatePacketNumberLength(QuicPacketNumber(2), + 10000 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); +} + +TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthCwnd) { + QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1); + if (GetParam().version.transport_version > QUIC_VERSION_43 && + GetParam().version.transport_version != QUIC_VERSION_99) { + EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + } else { + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + } + + creator_.UpdatePacketNumberLength(QuicPacketNumber(1), + 10000 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + + creator_.UpdatePacketNumberLength(QuicPacketNumber(1), + 10000 * 256 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + + creator_.UpdatePacketNumberLength(QuicPacketNumber(1), + 10000 * 256 * 256 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + + creator_.UpdatePacketNumberLength( + QuicPacketNumber(1), + UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize); + EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); +} + +TEST_P(QuicPacketCreatorTest, SerializeFrame) { + if (!GetParam().version_serialization) { + creator_.StopSendingVersion(); + } + frames_.push_back(QuicFrame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), false, + 0u, QuicStringPiece()))); + SerializedPacket serialized = SerializeAllFrames(frames_); + + QuicPacketHeader header; + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)) + .WillOnce(DoAll(SaveArg<0>(&header), Return(true))); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized); + EXPECT_EQ(GetParam().version_serialization, header.version_flag); +} + +TEST_P(QuicPacketCreatorTest, ConsumeDataLargerThanOneStreamFrame) { + if (!GetParam().version_serialization) { + creator_.StopSendingVersion(); + } + // A string larger than fits into a frame. + size_t payload_length; + creator_.SetMaxPacketLength(GetPacketLengthForOneStream( + client_framer_.transport_version(), + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + !kIncludeDiversificationNonce, + creator_.GetDestinationConnectionIdLength(), + creator_.GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), + QuicPacketCreatorPeer::GetLengthLength(&creator_), &payload_length)); + QuicFrame frame; + const QuicString too_long_payload(payload_length * 2, 'a'); + MakeIOVector(too_long_payload, &iov_); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, true, false, NOT_RETRANSMISSION, &frame)); + size_t consumed = frame.stream_frame.data_length; + EXPECT_EQ(payload_length, consumed); + const QuicString payload(payload_length, 'a'); + CheckStreamFrame( + frame, QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + payload, 0u, false); + creator_.Flush(); + DeleteSerializedPacket(); +} + +TEST_P(QuicPacketCreatorTest, AddFrameAndFlush) { + if (!GetParam().version_serialization) { + creator_.StopSendingVersion(); + } + const size_t max_plaintext_size = + client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); + EXPECT_FALSE(creator_.HasPendingFrames()); + EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()))); + EXPECT_EQ(max_plaintext_size - + GetPacketHeaderSize( + client_framer_.transport_version(), + creator_.GetDestinationConnectionIdLength(), + creator_.GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), + 0, QuicPacketCreatorPeer::GetLengthLength(&creator_)), + creator_.BytesFree()); + + // Add a variety of frame types and then a padding frame. + QuicAckFrame ack_frame(InitAckFrame(10u)); + EXPECT_TRUE( + creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()))); + + QuicFrame frame; + MakeIOVector("test", &iov_); + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame)); + size_t consumed = frame.stream_frame.data_length; + EXPECT_EQ(4u, consumed); + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_TRUE(creator_.HasPendingStreamFramesOfStream( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()))); + + QuicPaddingFrame padding_frame; + EXPECT_TRUE( + creator_.AddSavedFrame(QuicFrame(padding_frame), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_EQ(0u, creator_.BytesFree()); + + // Packet is full. Creator will flush. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + EXPECT_FALSE( + creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); + + // Ensure the packet is successfully created. + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + ASSERT_FALSE(serialized_packet_.retransmittable_frames.empty()); + const QuicFrames& retransmittable = serialized_packet_.retransmittable_frames; + ASSERT_EQ(1u, retransmittable.size()); + EXPECT_EQ(STREAM_FRAME, retransmittable[0].type); + EXPECT_TRUE(serialized_packet_.has_ack); + EXPECT_EQ(QuicPacketNumber(10u), serialized_packet_.largest_acked); + DeleteSerializedPacket(); + + EXPECT_FALSE(creator_.HasPendingFrames()); + EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()))); + EXPECT_EQ(max_plaintext_size - + GetPacketHeaderSize( + client_framer_.transport_version(), + creator_.GetDestinationConnectionIdLength(), + creator_.GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), + 0, QuicPacketCreatorPeer::GetLengthLength(&creator_)), + creator_.BytesFree()); +} + +TEST_P(QuicPacketCreatorTest, SerializeAndSendStreamFrame) { + if (!GetParam().version_serialization) { + creator_.StopSendingVersion(); + } + EXPECT_FALSE(creator_.HasPendingFrames()); + + MakeIOVector("test", &iov_); + producer_.SaveStreamData( + QuicUtils::GetHeadersStreamId(client_framer_.transport_version()), &iov_, + 1u, 0u, iov_.iov_len); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + size_t num_bytes_consumed; + creator_.CreateAndSerializeStreamFrame( + QuicUtils::GetHeadersStreamId(client_framer_.transport_version()), + iov_.iov_len, 0, 0, true, NOT_RETRANSMISSION, &num_bytes_consumed); + EXPECT_EQ(4u, num_bytes_consumed); + + // Ensure the packet is successfully created. + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + ASSERT_FALSE(serialized_packet_.retransmittable_frames.empty()); + const QuicFrames& retransmittable = serialized_packet_.retransmittable_frames; + ASSERT_EQ(1u, retransmittable.size()); + EXPECT_EQ(STREAM_FRAME, retransmittable[0].type); + DeleteSerializedPacket(); + + EXPECT_FALSE(creator_.HasPendingFrames()); +} + +TEST_P(QuicPacketCreatorTest, AddUnencryptedStreamDataClosesConnection) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (!IsDefaultTestConfiguration()) { + return; + } + + creator_.set_encryption_level(ENCRYPTION_NONE); + EXPECT_CALL(delegate_, OnUnrecoverableError(_, _, _)); + QuicStreamFrame stream_frame( + QuicUtils::GetHeadersStreamId(client_framer_.transport_version()), + /*fin=*/false, 0u, QuicStringPiece()); + EXPECT_QUIC_BUG( + creator_.AddSavedFrame(QuicFrame(stream_frame), NOT_RETRANSMISSION), + "Cannot send stream data without encryption."); +} + +TEST_P(QuicPacketCreatorTest, ChloTooLarge) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (!IsDefaultTestConfiguration()) { + return; + } + + CryptoHandshakeMessage message; + message.set_tag(kCHLO); + message.set_minimum_size(kMaxPacketSize); + CryptoFramer framer; + std::unique_ptr<QuicData> message_data; + message_data.reset(framer.ConstructHandshakeMessage(message)); + + struct iovec iov; + MakeIOVector(QuicStringPiece(message_data->data(), message_data->length()), + &iov); + QuicFrame frame; + EXPECT_CALL(delegate_, + OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, _, _)); + EXPECT_QUIC_BUG(creator_.ConsumeData(QuicUtils::GetCryptoStreamId( + client_framer_.transport_version()), + &iov, 1u, iov.iov_len, 0u, 0u, false, + false, NOT_RETRANSMISSION, &frame), + "Client hello won't fit in a single packet."); +} + +TEST_P(QuicPacketCreatorTest, PendingPadding) { + EXPECT_EQ(0u, creator_.pending_padding_bytes()); + creator_.AddPendingPadding(kMaxNumRandomPaddingBytes * 10); + EXPECT_EQ(kMaxNumRandomPaddingBytes * 10, creator_.pending_padding_bytes()); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly( + Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + // Flush all paddings. + while (creator_.pending_padding_bytes() > 0) { + creator_.Flush(); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + // Packet only contains padding. + ProcessPacket(serialized_packet_); + } + EXPECT_EQ(0u, creator_.pending_padding_bytes()); +} + +TEST_P(QuicPacketCreatorTest, FullPaddingDoesNotConsumePendingPadding) { + creator_.AddPendingPadding(kMaxNumRandomPaddingBytes); + QuicFrame frame; + MakeIOVector("test", &iov_); + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, false, + /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.Flush(); + EXPECT_EQ(kMaxNumRandomPaddingBytes, creator_.pending_padding_bytes()); +} + +TEST_P(QuicPacketCreatorTest, SendPendingPaddingInRetransmission) { + QuicStreamFrame stream_frame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + /*fin=*/false, 0u, QuicStringPiece()); + QuicFrames frames; + frames.push_back(QuicFrame(stream_frame)); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, true, /*num_padding_bytes=*/0, ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.AddPendingPadding(kMaxNumRandomPaddingBytes); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(0u, creator_.pending_padding_bytes()); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized_packet_); +} + +TEST_P(QuicPacketCreatorTest, SendPacketAfterFullPaddingRetransmission) { + // Making sure needs_full_padding gets reset after a full padding + // retransmission. + EXPECT_EQ(0u, creator_.pending_padding_bytes()); + QuicFrame frame; + MakeIOVector("fake handshake message data", &iov_); + producer_.SaveStreamData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, 0u, iov_.iov_len); + QuicPacketCreatorPeer::CreateStreamFrame( + &creator_, + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + iov_.iov_len, 0u, 0u, false, &frame); + QuicFrames frames; + frames.push_back(frame); + char buffer[kMaxPacketSize]; + QuicPendingRetransmission retransmission(CreateRetransmission( + frames, true, /*num_padding_bytes=*/-1, ENCRYPTION_NONE, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly( + Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + // Full padding. + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized_packet_); + + creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame); + creator_.Flush(); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + // needs_full_padding gets reset. + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(0); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized_packet_); +} + +TEST_P(QuicPacketCreatorTest, ConsumeDataAndRandomPadding) { + const QuicByteCount kStreamFramePayloadSize = 100u; + // Set the packet size be enough for one stream frame with 0 stream offset + + // 1. + size_t length = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + GetEncryptionOverhead() + + QuicFramer::GetMinStreamFrameSize( + client_framer_.transport_version(), + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), 0, + /*last_frame_in_packet=*/false, kStreamFramePayloadSize + 1) + + kStreamFramePayloadSize + 1; + creator_.SetMaxPacketLength(length); + creator_.AddPendingPadding(kMaxNumRandomPaddingBytes); + QuicByteCount pending_padding_bytes = creator_.pending_padding_bytes(); + QuicFrame frame; + char buf[kStreamFramePayloadSize + 1] = {}; + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly( + Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + // Send stream frame of size kStreamFramePayloadSize. + MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_); + creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame); + creator_.Flush(); + // 1 byte padding is sent. + EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes()); + // Send stream frame of size kStreamFramePayloadSize + 1. + MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize + 1), &iov_); + creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, kStreamFramePayloadSize, false, false, + NOT_RETRANSMISSION, &frame); + // No padding is sent. + creator_.Flush(); + EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes()); + // Flush all paddings. + while (creator_.pending_padding_bytes() > 0) { + creator_.Flush(); + } + EXPECT_EQ(0u, creator_.pending_padding_bytes()); +} + +TEST_P(QuicPacketCreatorTest, FlushWithExternalBuffer) { + char external_buffer[kMaxPacketSize]; + char* expected_buffer = external_buffer; + EXPECT_CALL(delegate_, GetPacketBuffer()).WillOnce(Return(expected_buffer)); + + QuicFrame frame; + MakeIOVector("test", &iov_); + ASSERT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, false, + /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke([expected_buffer](SerializedPacket* serialized_packet) { + EXPECT_EQ(expected_buffer, serialized_packet->encrypted_buffer); + ClearSerializedPacket(serialized_packet); + })); + creator_.Flush(); +} + +// Test for error found in +// https://bugs.chromium.org/p/chromium/issues/detail?id=859949 where a gap +// length that crosses an IETF VarInt length boundary would cause a +// failure. While this test is not applicable to versions other than version 99, +// it should still work. Hence, it is not made version-specific. +TEST_P(QuicPacketCreatorTest, IetfAckGapErrorRegression) { + QuicAckFrame ack_frame = + InitAckFrame({{QuicPacketNumber(60), QuicPacketNumber(61)}, + {QuicPacketNumber(125), QuicPacketNumber(126)}}); + frames_.push_back(QuicFrame(&ack_frame)); + SerializeAllFrames(frames_); +} + +TEST_P(QuicPacketCreatorTest, AddMessageFrame) { + if (client_framer_.transport_version() <= QUIC_VERSION_44) { + return; + } + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(3) + .WillRepeatedly( + Invoke(this, &QuicPacketCreatorTest::ClearSerializedPacketForTests)); + QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); + // Verify that there is enough room for the largest message payload. + EXPECT_TRUE( + creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload())); + QuicString message(creator_.GetLargestMessagePayload(), 'a'); + QuicMessageFrame* message_frame = new QuicMessageFrame(1); + MakeSpan(&allocator_, message, &storage) + .SaveMemSlicesAsMessageData(message_frame); + EXPECT_TRUE( + creator_.AddSavedFrame(QuicFrame(message_frame), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); + creator_.Flush(); + + QuicMessageFrame* frame2 = new QuicMessageFrame(2); + MakeSpan(&allocator_, "message", &storage).SaveMemSlicesAsMessageData(frame2); + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame2), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); + // Verify if a new frame is added, 1 byte message length will be added. + EXPECT_EQ(1u, creator_.ExpansionOnNewFrame()); + QuicMessageFrame* frame3 = new QuicMessageFrame(3); + MakeSpan(&allocator_, "message2", &storage) + .SaveMemSlicesAsMessageData(frame3); + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame3), NOT_RETRANSMISSION)); + EXPECT_EQ(1u, creator_.ExpansionOnNewFrame()); + creator_.Flush(); + + QuicFrame frame; + MakeIOVector("test", &iov_); + EXPECT_TRUE(creator_.ConsumeData( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame)); + QuicMessageFrame* frame4 = new QuicMessageFrame(4); + MakeSpan(&allocator_, "message", &storage).SaveMemSlicesAsMessageData(frame4); + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame4), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); + // Verify there is not enough room for largest payload. + EXPECT_FALSE( + creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload())); + // Add largest message will causes the flush of the stream frame. + QuicMessageFrame frame5(5); + MakeSpan(&allocator_, message, &storage).SaveMemSlicesAsMessageData(&frame5); + EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&frame5), NOT_RETRANSMISSION)); + EXPECT_FALSE(creator_.HasPendingFrames()); +} + +TEST_P(QuicPacketCreatorTest, MessageFrameConsumption) { + if (client_framer_.transport_version() <= QUIC_VERSION_44) { + return; + } + QuicString message_data(kDefaultMaxPacketSize, 'a'); + QuicStringPiece message_buffer(message_data); + QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); + // Test all possible size of message frames. + for (size_t message_size = 0; + message_size <= creator_.GetLargestMessagePayload(); ++message_size) { + QuicMessageFrame* frame = new QuicMessageFrame(0); + MakeSpan(&allocator_, QuicStringPiece(message_buffer.data(), message_size), + &storage) + .SaveMemSlicesAsMessageData(frame); + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); + + size_t expansion_bytes = message_size >= 64 ? 2 : 1; + EXPECT_EQ(expansion_bytes, creator_.ExpansionOnNewFrame()); + // Verify BytesFree returns bytes available for the next frame, which should + // subtract the message length. + size_t expected_bytes_free = + creator_.GetLargestMessagePayload() - message_size < expansion_bytes + ? 0 + : creator_.GetLargestMessagePayload() - expansion_bytes - + message_size; + EXPECT_EQ(expected_bytes_free, creator_.BytesFree()); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.Flush(); + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + DeleteSerializedPacket(); + } +} + +TEST_P(QuicPacketCreatorTest, PacketTransmissionType) { + creator_.set_can_set_transmission_type(true); + creator_.SetTransmissionType(NOT_RETRANSMISSION); + + QuicAckFrame temp_ack_frame = InitAckFrame(1); + QuicFrame ack_frame(&temp_ack_frame); + ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(ack_frame.type)); + + QuicFrame stream_frame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), + /*fin=*/false, 0u, QuicStringPiece())); + ASSERT_TRUE(QuicUtils::IsRetransmittableFrame(stream_frame.type)); + + QuicFrame padding_frame{QuicPaddingFrame()}; + ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(padding_frame.type)); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + + EXPECT_TRUE(creator_.AddSavedFrame(ack_frame, LOSS_RETRANSMISSION)); + ASSERT_FALSE(serialized_packet_.encrypted_buffer); + + EXPECT_TRUE(creator_.AddSavedFrame(stream_frame, RTO_RETRANSMISSION)); + ASSERT_FALSE(serialized_packet_.encrypted_buffer); + + EXPECT_TRUE(creator_.AddSavedFrame(padding_frame, TLP_RETRANSMISSION)); + creator_.Flush(); + ASSERT_TRUE(serialized_packet_.encrypted_buffer); + + if (creator_.ShouldSetTransmissionTypeForNextFrame()) { + // The last retransmittable frame on packet is a stream frame, the packet's + // transmission type should be the same as the stream frame's. + EXPECT_EQ(serialized_packet_.transmission_type, RTO_RETRANSMISSION); + } else { + EXPECT_EQ(serialized_packet_.transmission_type, NOT_RETRANSMISSION); + } + DeleteSerializedPacket(); +} + +TEST_P(QuicPacketCreatorTest, RetryToken) { + if (!GetParam().version_serialization || + !QuicVersionHasLongHeaderLengths(client_framer_.transport_version())) { + return; + } + + char retry_token_bytes[] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + + creator_.SetRetryToken( + QuicString(retry_token_bytes, sizeof(retry_token_bytes))); + + frames_.push_back(QuicFrame(QuicStreamFrame( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), false, + 0u, QuicStringPiece()))); + SerializedPacket serialized = SerializeAllFrames(frames_); + + QuicPacketHeader header; + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)) + .WillOnce(DoAll(SaveArg<0>(&header), Return(true))); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized); + ASSERT_TRUE(header.version_flag); + ASSERT_EQ(header.long_packet_type, INITIAL); + ASSERT_EQ(header.retry_token.length(), sizeof(retry_token_bytes)); + test::CompareCharArraysWithHexError( + "retry token", header.retry_token.data(), header.retry_token.length(), + retry_token_bytes, sizeof(retry_token_bytes)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc new file mode 100644 index 0000000..fd18064 --- /dev/null +++ b/quic/core/quic_packet_generator.cc
@@ -0,0 +1,538 @@ +// Copyright (c) 2012 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/quic_packet_generator.h" + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random_generator, + DelegateInterface* delegate) + : delegate_(delegate), + packet_creator_(connection_id, framer, random_generator, delegate), + next_transmission_type_(NOT_RETRANSMISSION), + flusher_attached_(false), + should_send_ack_(false), + should_send_stop_waiting_(false), + random_generator_(random_generator), + fully_pad_crypto_handshake_packets_(true), + deprecate_ack_bundling_mode_( + GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {} + +QuicPacketGenerator::~QuicPacketGenerator() { + DeleteFrames(&queued_control_frames_); +} + +void QuicPacketGenerator::SetShouldSendAck(bool also_send_stop_waiting) { + DCHECK(!deprecate_ack_bundling_mode_); + if (packet_creator_.has_ack()) { + // Ack already queued, nothing to do. + return; + } + + if (also_send_stop_waiting && packet_creator_.has_stop_waiting()) { + QUIC_BUG << "Should only ever be one pending stop waiting frame."; + return; + } + + should_send_ack_ = true; + should_send_stop_waiting_ = also_send_stop_waiting; + SendQueuedFrames(/*flush=*/false); +} + +void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) { + QUIC_BUG_IF(IsControlFrame(frame.type) && !GetControlFrameId(frame)) + << "Adding a control frame with no control frame id: " << frame; + if (deprecate_ack_bundling_mode_) { + MaybeBundleAckOpportunistically(); + } + queued_control_frames_.push_back(frame); + SendQueuedFrames(/*flush=*/false); +} + +size_t QuicPacketGenerator::ConsumeCryptoData(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset) { + QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " + "generator tries to write crypto data."; + if (deprecate_ack_bundling_mode_) { + MaybeBundleAckOpportunistically(); + } + // To make reasoning about crypto frames easier, we don't combine them with + // other retransmittable frames in a single packet. + // TODO(nharper): Once we have separate packet number spaces, everything + // should be driven by encryption level, and we should stop flushing in this + // spot. + const bool flush = packet_creator_.HasPendingRetransmittableFrames(); + SendQueuedFrames(flush); + + size_t total_bytes_consumed = 0; + + while (total_bytes_consumed < write_length) { + QuicFrame frame; + if (!packet_creator_.ConsumeCryptoData( + level, write_length - total_bytes_consumed, + offset + total_bytes_consumed, next_transmission_type_, &frame)) { + // The only pending data in the packet is non-retransmittable frames. I'm + // assuming here that they won't occupy so much of the packet that a + // CRYPTO frame won't fit. + QUIC_BUG << "Failed to ConsumeCryptoData at level " << level; + return 0; + } + total_bytes_consumed += frame.crypto_frame->data_length; + + // TODO(ianswett): Move to having the creator flush itself when it's full. + packet_creator_.Flush(); + } + + // Don't allow the handshake to be bundled with other retransmittable frames. + SendQueuedFrames(/*flush=*/true); + + return total_bytes_consumed; +} + +QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state) { + QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " + "generator tries to write stream data."; + bool has_handshake = + (id == QuicUtils::GetCryptoStreamId(packet_creator_.transport_version())); + if (deprecate_ack_bundling_mode_) { + MaybeBundleAckOpportunistically(); + } + bool fin = state != NO_FIN; + QUIC_BUG_IF(has_handshake && fin) + << "Handshake packets should never send a fin"; + // To make reasoning about crypto frames easier, we don't combine them with + // other retransmittable frames in a single packet. + const bool flush = + has_handshake && packet_creator_.HasPendingRetransmittableFrames(); + SendQueuedFrames(flush); + + size_t total_bytes_consumed = 0; + bool fin_consumed = false; + + if (!packet_creator_.HasRoomForStreamFrame(id, offset, write_length)) { + packet_creator_.Flush(); + } + + if (!fin && (write_length == 0)) { + QUIC_BUG << "Attempt to consume empty data without FIN."; + return QuicConsumedData(0, false); + } + // We determine if we can enter the fast path before executing + // the slow path loop. + bool run_fast_path = !has_handshake && state != FIN_AND_PADDING && + !HasQueuedFrames() && + write_length - total_bytes_consumed > kMaxPacketSize; + + while (!run_fast_path && delegate_->ShouldGeneratePacket( + HAS_RETRANSMITTABLE_DATA, + has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) { + QuicFrame frame; + bool needs_full_padding = + has_handshake && fully_pad_crypto_handshake_packets_; + + if (!packet_creator_.ConsumeData(id, write_length, total_bytes_consumed, + offset + total_bytes_consumed, fin, + needs_full_padding, + next_transmission_type_, &frame)) { + // The creator is always flushed if there's not enough room for a new + // stream frame before ConsumeData, so ConsumeData should always succeed. + QUIC_BUG << "Failed to ConsumeData, stream:" << id; + return QuicConsumedData(0, false); + } + + // A stream frame is created and added. + size_t bytes_consumed = frame.stream_frame.data_length; + total_bytes_consumed += bytes_consumed; + fin_consumed = fin && total_bytes_consumed == write_length; + if (fin_consumed && state == FIN_AND_PADDING) { + AddRandomPadding(); + } + DCHECK(total_bytes_consumed == write_length || + (bytes_consumed > 0 && packet_creator_.HasPendingFrames())); + + if (total_bytes_consumed == write_length) { + // We're done writing the data. Exit the loop. + // We don't make this a precondition because we could have 0 bytes of data + // if we're simply writing a fin. + break; + } + // TODO(ianswett): Move to having the creator flush itself when it's full. + packet_creator_.Flush(); + + run_fast_path = !has_handshake && state != FIN_AND_PADDING && + !HasQueuedFrames() && + write_length - total_bytes_consumed > kMaxPacketSize; + } + + if (run_fast_path) { + return ConsumeDataFastPath(id, write_length, offset, state != NO_FIN, + total_bytes_consumed); + } + + // Don't allow the handshake to be bundled with other retransmittable frames. + if (has_handshake) { + SendQueuedFrames(/*flush=*/true); + } + + return QuicConsumedData(total_bytes_consumed, fin_consumed); +} + +QuicConsumedData QuicPacketGenerator::ConsumeDataFastPath( + QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + bool fin, + size_t total_bytes_consumed) { + DCHECK_NE(id, + QuicUtils::GetCryptoStreamId(packet_creator_.transport_version())); + + while (total_bytes_consumed < write_length && + delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + // Serialize and encrypt the packet. + size_t bytes_consumed = 0; + packet_creator_.CreateAndSerializeStreamFrame( + id, write_length, total_bytes_consumed, offset + total_bytes_consumed, + fin, next_transmission_type_, &bytes_consumed); + total_bytes_consumed += bytes_consumed; + } + + return QuicConsumedData(total_bytes_consumed, + fin && (total_bytes_consumed == write_length)); +} + +void QuicPacketGenerator::GenerateMtuDiscoveryPacket(QuicByteCount target_mtu) { + // MTU discovery frames must be sent by themselves. + if (!packet_creator_.CanSetMaxPacketLength()) { + QUIC_BUG << "MTU discovery packets should only be sent when no other " + << "frames needs to be sent."; + return; + } + const QuicByteCount current_mtu = GetCurrentMaxPacketLength(); + + // The MTU discovery frame is allocated on the stack, since it is going to be + // serialized within this function. + QuicMtuDiscoveryFrame mtu_discovery_frame; + QuicFrame frame(mtu_discovery_frame); + + // Send the probe packet with the new length. + SetMaxPacketLength(target_mtu); + const bool success = + packet_creator_.AddPaddedSavedFrame(frame, next_transmission_type_); + packet_creator_.Flush(); + // The only reason AddFrame can fail is that the packet is too full to fit in + // a ping. This is not possible for any sane MTU. + DCHECK(success); + + // Reset the packet length back. + SetMaxPacketLength(current_mtu); +} + +bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const { + DCHECK(HasPendingFrames() || packet_creator_.pending_padding_bytes() > 0); + HasRetransmittableData retransmittable = + (should_send_ack_ || should_send_stop_waiting_ || + packet_creator_.pending_padding_bytes() > 0) + ? NO_RETRANSMITTABLE_DATA + : HAS_RETRANSMITTABLE_DATA; + if (retransmittable == HAS_RETRANSMITTABLE_DATA) { + DCHECK(!queued_control_frames_.empty()); // These are retransmittable. + } + return delegate_->ShouldGeneratePacket(retransmittable, NOT_HANDSHAKE); +} + +void QuicPacketGenerator::SendQueuedFrames(bool flush) { + // Only add pending frames if we are SURE we can then send the whole packet. + while (HasPendingFrames() && + (flush || CanSendWithNextPendingFrameAddition())) { + bool first_frame = packet_creator_.CanSetMaxPacketLength(); + if (!AddNextPendingFrame() && first_frame) { + // A single frame cannot fit into the packet, tear down the connection. + QUIC_BUG << "A single frame cannot fit into packet." + << " should_send_ack: " << should_send_ack_ + << " should_send_stop_waiting: " << should_send_stop_waiting_ + << " number of queued_control_frames: " + << queued_control_frames_.size(); + if (!queued_control_frames_.empty()) { + QUIC_LOG(INFO) << queued_control_frames_[0]; + } + delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, + "Single frame cannot fit into a packet", + ConnectionCloseSource::FROM_SELF); + return; + } + } + if (flush) { + packet_creator_.Flush(); + } +} + +bool QuicPacketGenerator::PacketFlusherAttached() const { + return flusher_attached_; +} + +void QuicPacketGenerator::AttachPacketFlusher() { + flusher_attached_ = true; +} + +void QuicPacketGenerator::Flush() { + SendQueuedFrames(/*flush=*/false); + packet_creator_.Flush(); + SendRemainingPendingPadding(); + flusher_attached_ = false; +} + +void QuicPacketGenerator::FlushAllQueuedFrames() { + SendQueuedFrames(/*flush=*/true); +} + +bool QuicPacketGenerator::HasQueuedFrames() const { + return packet_creator_.HasPendingFrames() || HasPendingFrames(); +} + +bool QuicPacketGenerator::IsPendingPacketEmpty() const { + return !packet_creator_.HasPendingFrames(); +} + +bool QuicPacketGenerator::HasPendingFrames() const { + return should_send_ack_ || should_send_stop_waiting_ || + !queued_control_frames_.empty(); +} + +bool QuicPacketGenerator::AddNextPendingFrame() { + QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " + "generator tries to write control frames."; + if (should_send_ack_) { + should_send_ack_ = !packet_creator_.AddSavedFrame( + delegate_->GetUpdatedAckFrame(), next_transmission_type_); + return !should_send_ack_; + } + + if (should_send_stop_waiting_) { + delegate_->PopulateStopWaitingFrame(&pending_stop_waiting_frame_); + // If we can't this add the frame now, then we still need to do so later. + should_send_stop_waiting_ = !packet_creator_.AddSavedFrame( + QuicFrame(pending_stop_waiting_frame_), next_transmission_type_); + // Return success if we have cleared out this flag (i.e., added the frame). + // If we still need to send, then the frame is full, and we have failed. + return !should_send_stop_waiting_; + } + + QUIC_BUG_IF(queued_control_frames_.empty()) + << "AddNextPendingFrame called with no queued control frames."; + + if (!packet_creator_.AddSavedFrame(queued_control_frames_.back(), + next_transmission_type_)) { + // Packet was full. + return false; + } + queued_control_frames_.pop_back(); + return true; +} + +void QuicPacketGenerator::StopSendingVersion() { + packet_creator_.StopSendingVersion(); +} + +void QuicPacketGenerator::SetDiversificationNonce( + const DiversificationNonce& nonce) { + packet_creator_.SetDiversificationNonce(nonce); +} + +QuicPacketNumber QuicPacketGenerator::packet_number() const { + return packet_creator_.packet_number(); +} + +QuicByteCount QuicPacketGenerator::GetCurrentMaxPacketLength() const { + return packet_creator_.max_packet_length(); +} + +void QuicPacketGenerator::SetMaxPacketLength(QuicByteCount length) { + DCHECK(packet_creator_.CanSetMaxPacketLength()); + packet_creator_.SetMaxPacketLength(length); +} + +std::unique_ptr<QuicEncryptedPacket> +QuicPacketGenerator::SerializeVersionNegotiationPacket( + bool ietf_quic, + const ParsedQuicVersionVector& supported_versions) { + return packet_creator_.SerializeVersionNegotiationPacket(ietf_quic, + supported_versions); +} + +OwningSerializedPacketPointer +QuicPacketGenerator::SerializeConnectivityProbingPacket() { + return packet_creator_.SerializeConnectivityProbingPacket(); +} + +OwningSerializedPacketPointer +QuicPacketGenerator::SerializePathChallengeConnectivityProbingPacket( + QuicPathFrameBuffer* payload) { + return packet_creator_.SerializePathChallengeConnectivityProbingPacket( + payload); +} + +OwningSerializedPacketPointer +QuicPacketGenerator::SerializePathResponseConnectivityProbingPacket( + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded) { + return packet_creator_.SerializePathResponseConnectivityProbingPacket( + payloads, is_padded); +} + +void QuicPacketGenerator::ReserializeAllFrames( + const QuicPendingRetransmission& retransmission, + char* buffer, + size_t buffer_len) { + packet_creator_.ReserializeAllFrames(retransmission, buffer, buffer_len); +} + +void QuicPacketGenerator::UpdatePacketNumberLength( + QuicPacketNumber least_packet_awaited_by_peer, + QuicPacketCount max_packets_in_flight) { + return packet_creator_.UpdatePacketNumberLength(least_packet_awaited_by_peer, + max_packets_in_flight); +} + +void QuicPacketGenerator::SetConnectionIdLength(uint32_t length) { + if (length == 0) { + packet_creator_.SetConnectionIdIncluded(CONNECTION_ID_ABSENT); + } else { + packet_creator_.SetConnectionIdIncluded(CONNECTION_ID_PRESENT); + } +} + +void QuicPacketGenerator::set_encryption_level(EncryptionLevel level) { + packet_creator_.set_encryption_level(level); +} + +void QuicPacketGenerator::SetEncrypter( + EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter) { + packet_creator_.SetEncrypter(level, std::move(encrypter)); +} + +void QuicPacketGenerator::AddRandomPadding() { + packet_creator_.AddPendingPadding( + random_generator_->RandUint64() % kMaxNumRandomPaddingBytes + 1); +} + +void QuicPacketGenerator::SendRemainingPendingPadding() { + while (packet_creator_.pending_padding_bytes() > 0 && !HasQueuedFrames() && + CanSendWithNextPendingFrameAddition()) { + packet_creator_.Flush(); + } +} + +bool QuicPacketGenerator::HasRetransmittableFrames() const { + return !queued_control_frames_.empty() || + packet_creator_.HasPendingRetransmittableFrames(); +} + +bool QuicPacketGenerator::HasPendingStreamFramesOfStream( + QuicStreamId id) const { + return packet_creator_.HasPendingStreamFramesOfStream(id); +} + +void QuicPacketGenerator::SetTransmissionType(TransmissionType type) { + packet_creator_.SetTransmissionType(type); + if (packet_creator_.ShouldSetTransmissionTypeForNextFrame()) { + next_transmission_type_ = type; + } +} + +void QuicPacketGenerator::SetCanSetTransmissionType( + bool can_set_transmission_type) { + packet_creator_.set_can_set_transmission_type(can_set_transmission_type); +} + +MessageStatus QuicPacketGenerator::AddMessageFrame(QuicMessageId message_id, + QuicMemSliceSpan message) { + QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " + "generator tries to add message frame."; + if (deprecate_ack_bundling_mode_) { + MaybeBundleAckOpportunistically(); + } + const QuicByteCount message_length = message.total_length(); + if (message_length > GetLargestMessagePayload()) { + return MESSAGE_STATUS_TOO_LARGE; + } + SendQueuedFrames(/*flush=*/false); + if (!packet_creator_.HasRoomForMessageFrame(message_length)) { + packet_creator_.Flush(); + } + QuicMessageFrame* frame = new QuicMessageFrame(message_id); + message.SaveMemSlicesAsMessageData(frame); + const bool success = + packet_creator_.AddSavedFrame(QuicFrame(frame), next_transmission_type_); + if (!success) { + QUIC_BUG << "Failed to send message " << message_id; + delete frame; + return MESSAGE_STATUS_INTERNAL_ERROR; + } + return MESSAGE_STATUS_SUCCESS; +} + +void QuicPacketGenerator::MaybeBundleAckOpportunistically() { + DCHECK(deprecate_ack_bundling_mode_); + if (packet_creator_.has_ack()) { + // Ack already queued, nothing to do. + return; + } + if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + return; + } + const bool flushed = + FlushAckFrame(delegate_->MaybeBundleAckOpportunistically()); + DCHECK(flushed); +} + +bool QuicPacketGenerator::FlushAckFrame(const QuicFrames& frames) { + QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " + "generator tries to send ACK frame."; + for (const auto& frame : frames) { + DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME); + if (packet_creator_.HasPendingFrames()) { + if (packet_creator_.AddSavedFrame(frame, next_transmission_type_)) { + // There is pending frames and current frame fits. + continue; + } + } + DCHECK(!packet_creator_.HasPendingFrames()); + // There is no pending frames, consult the delegate whether a packet can be + // generated. + if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + return false; + } + const bool success = + packet_creator_.AddSavedFrame(frame, next_transmission_type_); + QUIC_BUG_IF(!success) << "Failed to flush " << frame; + } + return true; +} + +QuicPacketLength QuicPacketGenerator::GetLargestMessagePayload() const { + return packet_creator_.GetLargestMessagePayload(); +} + +} // namespace quic
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h new file mode 100644 index 0000000..3a951f0 --- /dev/null +++ b/quic/core/quic_packet_generator.h
@@ -0,0 +1,313 @@ +// Copyright (c) 2012 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. + +// Responsible for generating packets on behalf of a QuicConnection. +// Packets are serialized just-in-time. Control frames are queued. +// Ack and Feedback frames will be requested from the Connection +// just-in-time. When a packet needs to be sent, the Generator +// will serialize a packet and pass it to QuicConnection::SendOrQueuePacket() +// +// The Generator's mode of operation is controlled by two conditions: +// +// 1) Is the Delegate writable? +// +// If the Delegate is not writable, then no operations will cause +// a packet to be serialized. In particular: +// * SetShouldSendAck will simply record that an ack is to be sent. +// * AddControlFrame will enqueue the control frame. +// * ConsumeData will do nothing. +// +// If the Delegate is writable, then the behavior depends on the second +// condition: +// +// 2) Is the Generator in batch mode? +// +// If the Generator is NOT in batch mode, then each call to a write +// operation will serialize one or more packets. The contents will +// include any previous queued frames. If an ack should be sent +// but has not been sent, then the Delegate will be asked to create +// an Ack frame which will then be included in the packet. When +// the write call completes, the current packet will be serialized +// and sent to the Delegate, even if it is not full. +// +// If the Generator is in batch mode, then each write operation will +// add data to the "current" packet. When the current packet becomes +// full, it will be serialized and sent to the packet. When batch +// mode is ended via |FinishBatchOperations|, the current packet +// will be serialzied, even if it is not full. + +#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_GENERATOR_H_ +#define QUICHE_QUIC_CORE_QUIC_PACKET_GENERATOR_H_ + +#include <cstddef> +#include <cstdint> +#include <list> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" +#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h" + +namespace quic { + +namespace test { +class QuicPacketGeneratorPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE QuicPacketGenerator { + public: + class QUIC_EXPORT_PRIVATE DelegateInterface + : public QuicPacketCreator::DelegateInterface { + public: + ~DelegateInterface() override {} + // Consults delegate whether a packet should be generated. + virtual bool ShouldGeneratePacket(HasRetransmittableData retransmittable, + IsHandshake handshake) = 0; + // Called when there is data to be sent. Retrieves updated ACK frame from + // the delegate. + virtual const QuicFrames MaybeBundleAckOpportunistically() = 0; + // TODO(fayang): Remove these two interfaces when deprecating + // quic_deprecate_ack_bundling_mode. + virtual const QuicFrame GetUpdatedAckFrame() = 0; + virtual void PopulateStopWaitingFrame( + QuicStopWaitingFrame* stop_waiting) = 0; + }; + + QuicPacketGenerator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random_generator, + DelegateInterface* delegate); + QuicPacketGenerator(const QuicPacketGenerator&) = delete; + QuicPacketGenerator& operator=(const QuicPacketGenerator&) = delete; + + ~QuicPacketGenerator(); + + // Indicates that an ACK frame should be sent. + // If |also_send_stop_waiting| is true, then it also indicates that a + // STOP_WAITING frame should be sent as well. + // The contents of the frame(s) will be generated via a call to the delegate + // CreateAckFrame() when the packet is serialized. + void SetShouldSendAck(bool also_send_stop_waiting); + + void AddControlFrame(const QuicFrame& frame); + + // Given some data, may consume part or all of it and pass it to the + // packet creator to be serialized into packets. If not in batch + // mode, these packets will also be sent during this call. + // When |state| is FIN_AND_PADDING, random padding of size [1, 256] will be + // added after stream frames. If current constructed packet cannot + // accommodate, the padding will overflow to the next packet(s). + QuicConsumedData ConsumeData(QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state); + + // Consumes data for CRYPTO frames sent at |level| starting at |offset| for a + // total of |write_length| bytes, and returns the number of bytes consumed. + // The data is passed into the packet creator and serialized into one or more + // packets. + size_t ConsumeCryptoData(EncryptionLevel level, + size_t write_length, + QuicStreamOffset offset); + + // Sends as many data only packets as allowed by the send algorithm and the + // available iov. + // This path does not support padding, or bundling pending frames. + // In case we access this method from ConsumeData, total_bytes_consumed + // keeps track of how many bytes have already been consumed. + QuicConsumedData ConsumeDataFastPath(QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + bool fin, + size_t total_bytes_consumed); + + // Generates an MTU discovery packet of specified size. + void GenerateMtuDiscoveryPacket(QuicByteCount target_mtu); + + // Indicates whether packet flusher is currently attached. + bool PacketFlusherAttached() const; + // Attaches packet flusher. + void AttachPacketFlusher(); + // Flushes everything, including all queued frames and pending padding. + void Flush(); + + // Flushes all queued frames, even frames which are not sendable. + void FlushAllQueuedFrames(); + + bool HasQueuedFrames() const; + + // Whether the pending packet has no frames in it at the moment. + bool IsPendingPacketEmpty() const; + + // Makes the framer not serialize the protocol version in sent packets. + void StopSendingVersion(); + + // SetDiversificationNonce sets the nonce that will be sent in each public + // header of packets encrypted at the initial encryption level. Should only + // be called by servers. + void SetDiversificationNonce(const DiversificationNonce& nonce); + + // Creates a version negotiation packet which supports |supported_versions|. + std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket( + bool ietf_quic, + const ParsedQuicVersionVector& supported_versions); + + // Creates a connectivity probing packet. + OwningSerializedPacketPointer SerializeConnectivityProbingPacket(); + + // Create connectivity probing request and response packets using PATH + // CHALLENGE and PATH RESPONSE frames, respectively. + // SerializePathChallengeConnectivityProbingPacket will pad the packet to be + // MTU bytes long. + OwningSerializedPacketPointer SerializePathChallengeConnectivityProbingPacket( + QuicPathFrameBuffer* payload); + + // If |is_padded| is true then SerializePathResponseConnectivityProbingPacket + // will pad the packet to be MTU bytes long, else it will not pad the packet. + // |payloads| is cleared. + OwningSerializedPacketPointer SerializePathResponseConnectivityProbingPacket( + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded); + + // Re-serializes frames with the original packet's packet number length. + // Used for retransmitting packets to ensure they aren't too long. + void ReserializeAllFrames(const QuicPendingRetransmission& retransmission, + char* buffer, + size_t buffer_len); + + // Update the packet number length to use in future packets as soon as it + // can be safely changed. + void UpdatePacketNumberLength(QuicPacketNumber least_packet_awaited_by_peer, + QuicPacketCount max_packets_in_flight); + + // Set the minimum number of bytes for the connection id length; + void SetConnectionIdLength(uint32_t length); + + // Sets the encrypter to use for the encryption level. + void SetEncrypter(EncryptionLevel level, + std::unique_ptr<QuicEncrypter> encrypter); + + // Returns true if there are control frames or current constructed packet has + // pending retransmittable frames. + bool HasRetransmittableFrames() const; + + // Returns true if current constructed packet has pending stream frames for + // stream |id|. + bool HasPendingStreamFramesOfStream(QuicStreamId id) const; + + // Sets the encryption level that will be applied to new packets. + void set_encryption_level(EncryptionLevel level); + + // packet number of the last created packet, or 0 if no packets have been + // created. + QuicPacketNumber packet_number() const; + + // Returns the maximum length a current packet can actually have. + QuicByteCount GetCurrentMaxPacketLength() const; + + // Set maximum packet length in the creator immediately. May not be called + // when there are frames queued in the creator. + void SetMaxPacketLength(QuicByteCount length); + + // Set transmission type of next constructed packets. + void SetTransmissionType(TransmissionType type); + + // Allow/Disallow setting transmission type of next constructed packets. + void SetCanSetTransmissionType(bool can_set_transmission_type); + + // Tries to add a message frame containing |message| and returns the status. + MessageStatus AddMessageFrame(QuicMessageId message_id, + QuicMemSliceSpan message); + + // Called to flush ACK and STOP_WAITING frames, returns false if the flush + // fails. + bool FlushAckFrame(const QuicFrames& frames); + + // Returns the largest payload that will fit into a single MESSAGE frame. + QuicPacketLength GetLargestMessagePayload() const; + + void set_debug_delegate(QuicPacketCreator::DebugDelegate* debug_delegate) { + packet_creator_.set_debug_delegate(debug_delegate); + } + + bool should_send_ack() const { return should_send_ack_; } + + void set_fully_pad_crypto_hadshake_packets(bool new_value) { + fully_pad_crypto_handshake_packets_ = new_value; + } + + bool fully_pad_crypto_handshake_packets() const { + return fully_pad_crypto_handshake_packets_; + } + + bool deprecate_ack_bundling_mode() const { + return deprecate_ack_bundling_mode_; + } + + private: + friend class test::QuicPacketGeneratorPeer; + + void SendQueuedFrames(bool flush); + + // Test to see if we have pending ack, or control frames. + bool HasPendingFrames() const; + // Returns true if addition of a pending frame (which might be + // retransmittable) would still allow the resulting packet to be sent now. + bool CanSendWithNextPendingFrameAddition() const; + // Add exactly one pending frame, preferring ack frames over control frames. + // Returns true if a pending frame is successfully added. + // Returns false and flushes current open packet if the pending frame cannot + // fit into current open packet. + bool AddNextPendingFrame(); + + // Adds a random amount of padding (between 1 to 256 bytes). + void AddRandomPadding(); + + // Sends remaining pending padding. + // Pending paddings should only be sent when there is nothing else to send. + void SendRemainingPendingPadding(); + + // Called when there is data to be sent, Retrieves updated ACK frame from + // delegate_ and flushes it. + void MaybeBundleAckOpportunistically(); + + DelegateInterface* delegate_; + + QuicPacketCreator packet_creator_; + QuicFrames queued_control_frames_; + + // Transmission type of the next serialized packet. + TransmissionType next_transmission_type_; + + // True if packet flusher is currently attached. + bool flusher_attached_; + + // Flags to indicate the need for just-in-time construction of a frame. + // TODO(fayang): Remove these two booleans when deprecating + // quic_deprecate_ack_bundling_mode. + bool should_send_ack_; + bool should_send_stop_waiting_; + // If we put a non-retransmittable frame in this packet, then we have to hold + // a reference to it until we flush (and serialize it). Retransmittable frames + // are referenced elsewhere so that they can later be (optionally) + // retransmitted. + // TODO(fayang): Remove this when deprecating + // quic_deprecate_ack_bundling_mode. + QuicStopWaitingFrame pending_stop_waiting_frame_; + + QuicRandom* random_generator_; + + // Whether crypto handshake packets should be fully padded. + bool fully_pad_crypto_handshake_packets_; + + // Latched value of quic_deprecate_ack_bundling_mode. + const bool deprecate_ack_bundling_mode_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PACKET_GENERATOR_H_
diff --git a/quic/core/quic_packet_generator_test.cc b/quic/core/quic_packet_generator_test.cc new file mode 100644 index 0000000..7b7d6ce --- /dev/null +++ b/quic/core/quic_packet_generator_test.cc
@@ -0,0 +1,1551 @@ +// Copyright (c) 2012 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/quic_packet_generator.h" + +#include <cstdint> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.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_simple_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h" + +using testing::_; +using testing::InSequence; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class MockDelegate : public QuicPacketGenerator::DelegateInterface { + public: + MockDelegate() {} + MockDelegate(const MockDelegate&) = delete; + MockDelegate& operator=(const MockDelegate&) = delete; + ~MockDelegate() override {} + + MOCK_METHOD2(ShouldGeneratePacket, + bool(HasRetransmittableData retransmittable, + IsHandshake handshake)); + MOCK_METHOD0(MaybeBundleAckOpportunistically, const QuicFrames()); + MOCK_METHOD0(GetUpdatedAckFrame, const QuicFrame()); + MOCK_METHOD1(PopulateStopWaitingFrame, void(QuicStopWaitingFrame*)); + MOCK_METHOD0(GetPacketBuffer, char*()); + MOCK_METHOD1(OnSerializedPacket, void(SerializedPacket* packet)); + MOCK_METHOD3(OnUnrecoverableError, + void(QuicErrorCode, const QuicString&, ConnectionCloseSource)); + + void SetCanWriteAnything() { + EXPECT_CALL(*this, ShouldGeneratePacket(_, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _)) + .WillRepeatedly(Return(true)); + } + + void SetCanNotWrite() { + EXPECT_CALL(*this, ShouldGeneratePacket(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _)) + .WillRepeatedly(Return(false)); + } + + // Use this when only ack frames should be allowed to be written. + void SetCanWriteOnlyNonRetransmittable() { + EXPECT_CALL(*this, ShouldGeneratePacket(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _)) + .WillRepeatedly(Return(true)); + } +}; + +// Simple struct for describing the contents of a packet. +// Useful in conjunction with a SimpleQuicFrame for validating that a packet +// contains the expected frames. +struct PacketContents { + PacketContents() + : num_ack_frames(0), + num_connection_close_frames(0), + num_goaway_frames(0), + num_rst_stream_frames(0), + num_stop_waiting_frames(0), + num_stream_frames(0), + num_crypto_frames(0), + num_ping_frames(0), + num_mtu_discovery_frames(0), + num_padding_frames(0) {} + + size_t num_ack_frames; + size_t num_connection_close_frames; + size_t num_goaway_frames; + size_t num_rst_stream_frames; + size_t num_stop_waiting_frames; + size_t num_stream_frames; + size_t num_crypto_frames; + size_t num_ping_frames; + size_t num_mtu_discovery_frames; + size_t num_padding_frames; +}; + +} // namespace + +class TestPacketGenerator : public QuicPacketGenerator { + public: + TestPacketGenerator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random_generator, + DelegateInterface* delegate, + SimpleDataProducer* producer) + : QuicPacketGenerator(connection_id, framer, random_generator, delegate), + ack_frame_(InitAckFrame(1)), + delegate_(static_cast<MockDelegate*>(delegate)), + producer_(producer) {} + + void AddControlFrame(const QuicFrame& frame, bool bundle_ack) { + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) && + !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack()) { + QuicFrames frames; + if (bundle_ack) { + frames.push_back(QuicFrame(&ack_frame_)); + } + if (delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()) + .WillOnce(Return(frames)); + } + } + QuicPacketGenerator::AddControlFrame(frame); + } + + QuicConsumedData ConsumeDataFastPath(QuicStreamId id, + const struct iovec* iov, + int iov_count, + size_t total_length, + QuicStreamOffset offset, + bool fin) { + // Save data before data is consumed. + if (total_length > 0) { + producer_->SaveStreamData(id, iov, iov_count, 0, total_length); + } + return QuicPacketGenerator::ConsumeDataFastPath(id, total_length, offset, + fin, 0); + } + + QuicConsumedData ConsumeData(QuicStreamId id, + const struct iovec* iov, + int iov_count, + size_t total_length, + QuicStreamOffset offset, + StreamSendingState state) { + // Save data before data is consumed. + if (total_length > 0) { + producer_->SaveStreamData(id, iov, iov_count, 0, total_length); + } + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) && + !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() && + delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1); + } + return QuicPacketGenerator::ConsumeData(id, total_length, offset, state); + } + + MessageStatus AddMessageFrame(QuicMessageId message_id, + QuicMemSliceSpan message) { + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) && + !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() && + delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1); + } + return QuicPacketGenerator::AddMessageFrame(message_id, message); + } + + size_t ConsumeCryptoData(EncryptionLevel level, + QuicStringPiece data, + QuicStreamOffset offset) { + producer_->SaveCryptoData(level, offset, data); + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) && + !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() && + delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1); + } + return QuicPacketGenerator::ConsumeCryptoData(level, data.length(), offset); + } + + QuicAckFrame ack_frame_; + MockDelegate* delegate_; + SimpleDataProducer* producer_; +}; + +class QuicPacketGeneratorTest : public QuicTest { + public: + QuicPacketGeneratorTest() + : framer_(AllSupportedVersions(), + QuicTime::Zero(), + Perspective::IS_CLIENT, + kQuicDefaultConnectionIdLength), + generator_(TestConnectionId(), + &framer_, + &random_generator_, + &delegate_, + &producer_), + creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)), + ack_frame_(InitAckFrame(1)) { + EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr)); + creator_->SetEncrypter( + ENCRYPTION_FORWARD_SECURE, + QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT)); + creator_->set_encryption_level(ENCRYPTION_FORWARD_SECURE); + framer_.set_data_producer(&producer_); + generator_.AttachPacketFlusher(); + } + + ~QuicPacketGeneratorTest() override { + for (SerializedPacket& packet : packets_) { + delete[] packet.encrypted_buffer; + ClearSerializedPacket(&packet); + } + } + + void SavePacket(SerializedPacket* packet) { + packet->encrypted_buffer = CopyBuffer(*packet); + packets_.push_back(*packet); + packet->encrypted_buffer = nullptr; + packet->retransmittable_frames.clear(); + } + + protected: + QuicRstStreamFrame* CreateRstStreamFrame() { + return new QuicRstStreamFrame(1, 1, QUIC_STREAM_NO_ERROR, 0); + } + + QuicGoAwayFrame* CreateGoAwayFrame() { + return new QuicGoAwayFrame(2, QUIC_NO_ERROR, 1, QuicString()); + } + + void CheckPacketContains(const PacketContents& contents, + size_t packet_index) { + ASSERT_GT(packets_.size(), packet_index); + const SerializedPacket& packet = packets_[packet_index]; + size_t num_retransmittable_frames = + contents.num_connection_close_frames + contents.num_goaway_frames + + contents.num_rst_stream_frames + contents.num_stream_frames + + contents.num_crypto_frames + contents.num_ping_frames; + size_t num_frames = + contents.num_ack_frames + contents.num_stop_waiting_frames + + contents.num_mtu_discovery_frames + contents.num_padding_frames + + num_retransmittable_frames; + + if (num_retransmittable_frames == 0) { + ASSERT_TRUE(packet.retransmittable_frames.empty()); + } else { + ASSERT_FALSE(packet.retransmittable_frames.empty()); + EXPECT_EQ(num_retransmittable_frames, + packet.retransmittable_frames.size()); + } + + ASSERT_TRUE(packet.encrypted_buffer != nullptr); + ASSERT_TRUE(simple_framer_.ProcessPacket( + QuicEncryptedPacket(packet.encrypted_buffer, packet.encrypted_length))); + EXPECT_EQ(num_frames, simple_framer_.num_frames()); + EXPECT_EQ(contents.num_ack_frames, simple_framer_.ack_frames().size()); + EXPECT_EQ(contents.num_connection_close_frames, + simple_framer_.connection_close_frames().size()); + EXPECT_EQ(contents.num_goaway_frames, + simple_framer_.goaway_frames().size()); + EXPECT_EQ(contents.num_rst_stream_frames, + simple_framer_.rst_stream_frames().size()); + EXPECT_EQ(contents.num_stream_frames, + simple_framer_.stream_frames().size()); + EXPECT_EQ(contents.num_crypto_frames, + simple_framer_.crypto_frames().size()); + EXPECT_EQ(contents.num_stop_waiting_frames, + simple_framer_.stop_waiting_frames().size()); + EXPECT_EQ(contents.num_padding_frames, + simple_framer_.padding_frames().size()); + + // From the receiver's perspective, MTU discovery frames are ping frames. + EXPECT_EQ(contents.num_ping_frames + contents.num_mtu_discovery_frames, + simple_framer_.ping_frames().size()); + } + + void CheckPacketHasSingleStreamFrame(size_t packet_index) { + ASSERT_GT(packets_.size(), packet_index); + const SerializedPacket& packet = packets_[packet_index]; + ASSERT_FALSE(packet.retransmittable_frames.empty()); + EXPECT_EQ(1u, packet.retransmittable_frames.size()); + ASSERT_TRUE(packet.encrypted_buffer != nullptr); + ASSERT_TRUE(simple_framer_.ProcessPacket( + QuicEncryptedPacket(packet.encrypted_buffer, packet.encrypted_length))); + EXPECT_EQ(1u, simple_framer_.num_frames()); + EXPECT_EQ(1u, simple_framer_.stream_frames().size()); + } + + void CheckAllPacketsHaveSingleStreamFrame() { + for (size_t i = 0; i < packets_.size(); i++) { + CheckPacketHasSingleStreamFrame(i); + } + } + + void CreateData(size_t len) { + data_array_.reset(new char[len]); + memset(data_array_.get(), '?', len); + iov_.iov_base = data_array_.get(); + iov_.iov_len = len; + } + + QuicFramer framer_; + MockRandom random_generator_; + StrictMock<MockDelegate> delegate_; + TestPacketGenerator generator_; + QuicPacketCreator* creator_; + SimpleQuicFramer simple_framer_; + std::vector<SerializedPacket> packets_; + QuicAckFrame ack_frame_; + struct iovec iov_; + SimpleBufferAllocator allocator_; + + private: + std::unique_ptr<char[]> data_array_; + SimpleDataProducer producer_; +}; + +class MockDebugDelegate : public QuicPacketCreator::DebugDelegate { + public: + MOCK_METHOD1(OnFrameAddedToPacket, void(const QuicFrame&)); +}; + +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) { + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + return; + } + delegate_.SetCanNotWrite(); + + generator_.SetShouldSendAck(false); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + return; + } + StrictMock<MockDebugDelegate> debug_delegate; + + generator_.set_debug_delegate(&debug_delegate); + delegate_.SetCanWriteOnlyNonRetransmittable(); + + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + EXPECT_CALL(debug_delegate, OnFrameAddedToPacket(_)).Times(1); + + generator_.SetShouldSendAck(false); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + return; + } + delegate_.SetCanWriteOnlyNonRetransmittable(); + + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + generator_.SetShouldSendAck(false); + generator_.Flush(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_ack_frames = 1; + CheckPacketContains(contents, 0); +} + +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_MultipleCalls) { + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + return; + } + // Make sure that calling SetShouldSendAck multiple times does not result in a + // crash. Previously this would result in multiple QuicFrames queued in the + // packet generator, with all but the last with internal pointers to freed + // memory. + delegate_.SetCanWriteAnything(); + + // Only one AckFrame should be created. + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(1) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + generator_.SetShouldSendAck(false); + generator_.SetShouldSendAck(false); + generator_.Flush(); +} + +TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) { + delegate_.SetCanNotWrite(); + + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/false); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, AddControlFrame_OnlyAckWritable) { + delegate_.SetCanWriteOnlyNonRetransmittable(); + + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/false); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) { + delegate_.SetCanWriteAnything(); + + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/false); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritableBatchThenFlush) { + delegate_.SetCanNotWrite(); + + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/false); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + generator_.Flush(); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + generator_.AttachPacketFlusher(); + generator_.FlushAllQueuedFrames(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_rst_stream_frames = 1; + CheckPacketContains(contents, 0); +} + +TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) { + delegate_.SetCanWriteAnything(); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/false); + generator_.Flush(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_rst_stream_frames = 1; + CheckPacketContains(contents, 0); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeCryptoData) { + delegate_.SetCanWriteAnything(); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicString data = "crypto data"; + size_t consumed_bytes = + generator_.ConsumeCryptoData(ENCRYPTION_NONE, data, 0); + generator_.Flush(); + EXPECT_EQ(data.length(), consumed_bytes); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_crypto_frames = 1; + contents.num_padding_frames = 1; + CheckPacketContains(contents, 0); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) { + delegate_.SetCanNotWrite(); + + MakeIOVector("foo", &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + EXPECT_EQ(0u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { + delegate_.SetCanWriteAnything(); + + MakeIOVector("foo", &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + EXPECT_EQ(3u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { + delegate_.SetCanWriteAnything(); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + MakeIOVector("foo", &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + generator_.Flush(); + EXPECT_EQ(3u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); +} + +// Test the behavior of ConsumeData when the data consumed is for the crypto +// handshake stream. Ensure that the packet is always sent and padded even if +// the generator operates in batch mode. +TEST_F(QuicPacketGeneratorTest, ConsumeData_Handshake) { + delegate_.SetCanWriteAnything(); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + MakeIOVector("foo", &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetCryptoStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, NO_FIN); + EXPECT_EQ(3u, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + contents.num_padding_frames = 1; + CheckPacketContains(contents, 0); + + ASSERT_EQ(1u, packets_.size()); + ASSERT_EQ(kDefaultMaxPacketSize, generator_.GetCurrentMaxPacketLength()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].encrypted_length); +} + +// Test the behavior of ConsumeData when the data is for the crypto handshake +// stream, but padding is disabled. +TEST_F(QuicPacketGeneratorTest, ConsumeData_Handshake_PaddingDisabled) { + generator_.set_fully_pad_crypto_hadshake_packets(false); + + delegate_.SetCanWriteAnything(); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + MakeIOVector("foo", &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetCryptoStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, NO_FIN); + EXPECT_EQ(3u, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + contents.num_padding_frames = 0; + CheckPacketContains(contents, 0); + + ASSERT_EQ(1u, packets_.size()); + + // Packet is not fully padded, but we want to future packets to be larger. + ASSERT_EQ(kDefaultMaxPacketSize, generator_.GetCurrentMaxPacketLength()); + EXPECT_EQ(27, packets_[0].encrypted_length); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeData_EmptyData) { + delegate_.SetCanWriteAnything(); + + EXPECT_QUIC_BUG(generator_.ConsumeData(QuicUtils::GetHeadersStreamId( + framer_.transport_version()), + nullptr, 0, 0, 0, NO_FIN), + "Attempt to consume empty data without FIN."); +} + +TEST_F(QuicPacketGeneratorTest, + ConsumeDataMultipleTimes_WritableAndShouldNotFlush) { + delegate_.SetCanWriteAnything(); + + MakeIOVector("foo", &iov_); + generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + MakeIOVector("quux", &iov_); + QuicConsumedData consumed = + generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 3, NO_FIN); + EXPECT_EQ(4u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { + delegate_.SetCanWriteAnything(); + + MakeIOVector("foo", &iov_); + generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + MakeIOVector("quux", &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 3, NO_FIN); + EXPECT_EQ(4u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + // Now both frames will be flushed out. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + generator_.Flush(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 2; + CheckPacketContains(contents, 0); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { + // Set the packet size be enough for two stream frames with 0 stream offset, + // but not enough for a stream frame of 0 offset and one with non-zero offset. + size_t length = + NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + + GetPacketHeaderSize( + framer_.transport_version(), + creator_->GetDestinationConnectionIdLength(), + creator_->GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::SendVersionInPacket(creator_), + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0, + QuicPacketCreatorPeer::GetLengthLength(creator_)) + + // Add an extra 3 bytes for the payload and 1 byte so + // BytesFree is larger than the GetMinStreamFrameSize. + QuicFramer::GetMinStreamFrameSize(framer_.transport_version(), 1, 0, + false, 3) + + 3 + + QuicFramer::GetMinStreamFrameSize(framer_.transport_version(), 1, 0, true, + 1) + + 1; + generator_.SetMaxPacketLength(length); + delegate_.SetCanWriteAnything(); + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } + // Queue enough data to prevent a stream frame with a non-zero offset from + // fitting. + MakeIOVector("foo", &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, NO_FIN); + EXPECT_EQ(3u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + // This frame will not fit with the existing frame, causing the queued frame + // to be serialized, and it will be added to a new open packet. + MakeIOVector("bar", &iov_); + consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 3, FIN); + EXPECT_EQ(3u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + creator_->Flush(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); + CheckPacketContains(contents, 1); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeDataFastPath) { + delegate_.SetCanWriteAnything(); + generator_.SetCanSetTransmissionType(true); + generator_.SetTransmissionType(LOSS_RETRANSMISSION); + + // Create a 10000 byte IOVector. + CreateData(10000); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = generator_.ConsumeDataFastPath( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, true); + EXPECT_EQ(10000u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(LOSS_RETRANSMISSION, packet.transmission_type); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + const QuicStreamFrame& stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeDataLarge) { + delegate_.SetCanWriteAnything(); + + // Create a 10000 byte IOVector. + CreateData(10000); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + EXPECT_EQ(10000u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + const QuicStreamFrame& stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckFalse) { + delegate_.SetCanNotWrite(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + generator_.SetShouldSendAck(false); + } + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/true); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + delegate_.SetCanWriteAnything(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + } + + // Create a 10000 byte IOVector. + CreateData(10000); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + generator_.Flush(); + + EXPECT_EQ(10000u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + const QuicStreamFrame& stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckTrue) { + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + delegate_.SetCanNotWrite(); + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + generator_.SetShouldSendAck(true /* stop_waiting */); + } + delegate_.SetCanWriteAnything(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + // Set up frames to write into the creator when control frames are written. + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_)); + // Generator should have queued control frames, and creator should be empty. + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + EXPECT_FALSE(creator_->HasPendingFrames()); + } + + // Create a 10000 byte IOVector. + CreateData(10000); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + generator_.Flush(); + + EXPECT_EQ(10000u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + const QuicStreamFrame& stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset); +} + +TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { + delegate_.SetCanNotWrite(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + generator_.SetShouldSendAck(false); + } + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/true); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + EXPECT_FALSE(generator_.HasPendingStreamFramesOfStream(3)); + + delegate_.SetCanWriteAnything(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + // When the first write operation is invoked, the ack frame will be + // returned. + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + } + + // Send some data and a control frame + MakeIOVector("quux", &iov_); + generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 0, NO_FIN); + if (framer_.transport_version() != QUIC_VERSION_99) { + generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()), + /*bundle_ack=*/false); + } + EXPECT_TRUE(generator_.HasPendingStreamFramesOfStream(3)); + + // All five frames will be flushed out in a single packet. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + generator_.Flush(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + EXPECT_FALSE(generator_.HasPendingStreamFramesOfStream(3)); + + PacketContents contents; + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + // ACK will be flushed by connection. + contents.num_ack_frames = 0; + } else { + contents.num_ack_frames = 1; + } + if (framer_.transport_version() != QUIC_VERSION_99) { + contents.num_goaway_frames = 1; + } else { + contents.num_goaway_frames = 0; + } + contents.num_rst_stream_frames = 1; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); +} + +TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { + delegate_.SetCanNotWrite(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + generator_.SetShouldSendAck(false); + } + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()), + /*bundle_ack=*/true); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + delegate_.SetCanWriteAnything(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + // When the first write operation is invoked, the ack frame will be + // returned. + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + } + + { + InSequence dummy; + // All five frames will be flushed out in a single packet + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } + + // Send enough data to exceed one packet + size_t data_len = kDefaultMaxPacketSize + 100; + CreateData(data_len); + QuicConsumedData consumed = + generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 0, FIN); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + if (framer_.transport_version() != QUIC_VERSION_99) { + generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()), + /*bundle_ack=*/false); + } + + generator_.Flush(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + // The first packet should have the queued data and part of the stream data. + PacketContents contents; + if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + // ACK will be sent by connection. + contents.num_ack_frames = 0; + } else { + contents.num_ack_frames = 1; + } + contents.num_rst_stream_frames = 1; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); + + // The second should have the remainder of the stream data. + PacketContents contents2; + if (framer_.transport_version() != QUIC_VERSION_99) { + contents2.num_goaway_frames = 1; + } else { + contents2.num_goaway_frames = 0; + } + contents2.num_stream_frames = 1; + CheckPacketContains(contents2, 1); +} + +// Regression test of b/120493795. +TEST_F(QuicPacketGeneratorTest, PacketTransmissionType) { + delegate_.SetCanWriteAnything(); + generator_.SetCanSetTransmissionType(true); + + // The first ConsumeData will fill the packet without flush. + generator_.SetTransmissionType(LOSS_RETRANSMISSION); + + size_t data_len = 1324; + CreateData(data_len); + QuicStreamId stream1_id = + QuicUtils::GetHeadersStreamId(framer_.transport_version()); + QuicConsumedData consumed = + generator_.ConsumeData(stream1_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN); + EXPECT_EQ(data_len, consumed.bytes_consumed); + ASSERT_EQ(0u, creator_->BytesFree()) + << "Test setup failed: Please increase data_len to " + << data_len + creator_->BytesFree() << " bytes."; + + // The second ConsumeData can not be added to the packet and will flush. + generator_.SetTransmissionType(NOT_RETRANSMISSION); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + QuicStreamId stream2_id = stream1_id + 4; + + consumed = + generator_.ConsumeData(stream2_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN); + EXPECT_EQ(data_len, consumed.bytes_consumed); + + // Ensure the packet is successfully created. + ASSERT_EQ(1u, packets_.size()); + ASSERT_TRUE(packets_[0].encrypted_buffer); + ASSERT_EQ(1u, packets_[0].retransmittable_frames.size()); + EXPECT_EQ(stream1_id, + packets_[0].retransmittable_frames[0].stream_frame.stream_id); + if (GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) { + // Since the second frame was not added, the packet's transmission type + // should be the first frame's type. + EXPECT_EQ(packets_[0].transmission_type, LOSS_RETRANSMISSION); + } else { + EXPECT_EQ(packets_[0].transmission_type, NOT_RETRANSMISSION); + } +} + +TEST_F(QuicPacketGeneratorTest, TestConnectionIdLength) { + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + generator_.SetConnectionIdLength(0); + EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID, + creator_->GetDestinationConnectionIdLength()); + + for (size_t i = 1; i < 10; i++) { + generator_.SetConnectionIdLength(i); + if (framer_.transport_version() > QUIC_VERSION_43) { + EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID, + creator_->GetDestinationConnectionIdLength()); + } else { + EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID, + creator_->GetDestinationConnectionIdLength()); + } + } +} + +// Test whether SetMaxPacketLength() works in the situation when the queue is +// empty, and we send three packets worth of data. +TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Initial) { + delegate_.SetCanWriteAnything(); + + // Send enough data for three packets. + size_t data_len = 3 * kDefaultMaxPacketSize + 1; + size_t packet_len = kDefaultMaxPacketSize + 100; + ASSERT_LE(packet_len, kMaxPacketSize); + generator_.SetMaxPacketLength(packet_len); + EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength()); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(3) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + CreateData(data_len); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, + /*offset=*/0, FIN); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + // We expect three packets, and first two of them have to be of packet_len + // size. We check multiple packets (instead of just one) because we want to + // ensure that |max_packet_length_| does not get changed incorrectly by the + // generator after first packet is serialized. + ASSERT_EQ(3u, packets_.size()); + EXPECT_EQ(packet_len, packets_[0].encrypted_length); + EXPECT_EQ(packet_len, packets_[1].encrypted_length); + CheckAllPacketsHaveSingleStreamFrame(); +} + +// Test whether SetMaxPacketLength() works in the situation when we first write +// data, then change packet size, then write data again. +TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Middle) { + delegate_.SetCanWriteAnything(); + + // We send enough data to overflow default packet length, but not the altered + // one. + size_t data_len = kDefaultMaxPacketSize; + size_t packet_len = kDefaultMaxPacketSize + 100; + ASSERT_LE(packet_len, kMaxPacketSize); + + // We expect to see three packets in total. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(3) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Send two packets before packet size change. + CreateData(data_len); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, + /*offset=*/0, NO_FIN); + generator_.Flush(); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + // Make sure we already have two packets. + ASSERT_EQ(2u, packets_.size()); + + // Increase packet size. + generator_.SetMaxPacketLength(packet_len); + EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength()); + + // Send a packet after packet size change. + CreateData(data_len); + generator_.AttachPacketFlusher(); + consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, data_len, FIN); + generator_.Flush(); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + // We expect first data chunk to get fragmented, but the second one to fit + // into a single packet. + ASSERT_EQ(3u, packets_.size()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].encrypted_length); + EXPECT_LE(kDefaultMaxPacketSize, packets_[2].encrypted_length); + CheckAllPacketsHaveSingleStreamFrame(); +} + +// Test whether SetMaxPacketLength() works correctly when we force the change of +// the packet size in the middle of the batched packet. +TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_MidpacketFlush) { + delegate_.SetCanWriteAnything(); + + size_t first_write_len = kDefaultMaxPacketSize / 2; + size_t packet_len = kDefaultMaxPacketSize + 100; + size_t second_write_len = packet_len + 1; + ASSERT_LE(packet_len, kMaxPacketSize); + + // First send half of the packet worth of data. We are in the batch mode, so + // should not cause packet serialization. + CreateData(first_write_len); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, + /*offset=*/0, NO_FIN); + EXPECT_EQ(first_write_len, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + // Make sure we have no packets so far. + ASSERT_EQ(0u, packets_.size()); + + // Expect a packet to be flushed. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Increase packet size after flushing all frames. + // Ensure it's immediately enacted. + generator_.FlushAllQueuedFrames(); + generator_.SetMaxPacketLength(packet_len); + EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength()); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + // We expect to see exactly one packet serialized after that, because we send + // a value somewhat exceeding new max packet size, and the tail data does not + // get serialized because we are still in the batch mode. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Send a more than a packet worth of data to the same stream. This should + // trigger serialization of one packet, and queue another one. + CreateData(second_write_len); + consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, + /*offset=*/first_write_len, FIN); + EXPECT_EQ(second_write_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + // We expect the first packet to be underfilled, and the second packet be up + // to the new max packet size. + ASSERT_EQ(2u, packets_.size()); + EXPECT_GT(kDefaultMaxPacketSize, packets_[0].encrypted_length); + EXPECT_EQ(packet_len, packets_[1].encrypted_length); + + CheckAllPacketsHaveSingleStreamFrame(); +} + +// Test sending a connectivity probing packet. +TEST_F(QuicPacketGeneratorTest, GenerateConnectivityProbingPacket) { + delegate_.SetCanWriteAnything(); + + OwningSerializedPacketPointer probing_packet; + if (framer_.transport_version() == QUIC_VERSION_99) { + QuicPathFrameBuffer payload = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; + probing_packet = + generator_.SerializePathChallengeConnectivityProbingPacket(&payload); + } else { + probing_packet = generator_.SerializeConnectivityProbingPacket(); + } + + ASSERT_TRUE(simple_framer_.ProcessPacket(QuicEncryptedPacket( + probing_packet->encrypted_buffer, probing_packet->encrypted_length))); + + EXPECT_EQ(2u, simple_framer_.num_frames()); + if (framer_.transport_version() == QUIC_VERSION_99) { + EXPECT_EQ(1u, simple_framer_.path_challenge_frames().size()); + } else { + EXPECT_EQ(1u, simple_framer_.ping_frames().size()); + } + EXPECT_EQ(1u, simple_framer_.padding_frames().size()); +} + +// Test sending an MTU probe, without any surrounding data. +TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_Simple) { + delegate_.SetCanWriteAnything(); + + const size_t target_mtu = kDefaultMaxPacketSize + 100; + static_assert(target_mtu < kMaxPacketSize, + "The MTU probe used by the test exceeds maximum packet size"); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + generator_.GenerateMtuDiscoveryPacket(target_mtu); + + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + ASSERT_EQ(1u, packets_.size()); + EXPECT_EQ(target_mtu, packets_[0].encrypted_length); + + PacketContents contents; + contents.num_mtu_discovery_frames = 1; + contents.num_padding_frames = 1; + CheckPacketContains(contents, 0); +} + +// Test sending an MTU probe. Surround it with data, to ensure that it resets +// the MTU to the value before the probe was sent. +TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_SurroundedByData) { + delegate_.SetCanWriteAnything(); + + const size_t target_mtu = kDefaultMaxPacketSize + 100; + static_assert(target_mtu < kMaxPacketSize, + "The MTU probe used by the test exceeds maximum packet size"); + + // Send enough data so it would always cause two packets to be sent. + const size_t data_len = target_mtu + 1; + + // Send a total of five packets: two packets before the probe, the probe + // itself, and two packets after the probe. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(5) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Send data before the MTU probe. + CreateData(data_len); + QuicConsumedData consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, + /*offset=*/0, NO_FIN); + generator_.Flush(); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + // Send the MTU probe. + generator_.GenerateMtuDiscoveryPacket(target_mtu); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + // Send data after the MTU probe. + CreateData(data_len); + generator_.AttachPacketFlusher(); + consumed = generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, + /*offset=*/data_len, FIN); + generator_.Flush(); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + ASSERT_EQ(5u, packets_.size()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].encrypted_length); + EXPECT_EQ(target_mtu, packets_[2].encrypted_length); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[3].encrypted_length); + + PacketContents probe_contents; + probe_contents.num_mtu_discovery_frames = 1; + probe_contents.num_padding_frames = 1; + + CheckPacketHasSingleStreamFrame(0); + CheckPacketHasSingleStreamFrame(1); + CheckPacketContains(probe_contents, 2); + CheckPacketHasSingleStreamFrame(3); + CheckPacketHasSingleStreamFrame(4); +} + +TEST_F(QuicPacketGeneratorTest, DontCrashOnInvalidStopWaiting) { + if (framer_.transport_version() > QUIC_VERSION_43) { + return; + } + // Test added to ensure the generator does not crash when an invalid frame is + // added. Because this is an indication of internal programming errors, + // DFATALs are expected. + // A 1 byte packet number length can't encode a gap of 1000. + QuicPacketCreatorPeer::SetPacketNumber(creator_, 1000); + + delegate_.SetCanNotWrite(); + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + generator_.SetShouldSendAck(true); + } + delegate_.SetCanWriteAnything(); + + if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) { + // Set up frames to write into the creator when control frames are written. + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_)); + // Generator should have queued control frames, and creator should be empty. + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + EXPECT_FALSE(creator_->HasPendingFrames()); + } + + // This will not serialize any packets, because of the invalid frame. + EXPECT_CALL(delegate_, + OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, _, + ConnectionCloseSource::FROM_SELF)); + EXPECT_QUIC_BUG(generator_.Flush(), + "packet_number_length 1 is too small " + "for least_unacked_delta: 1001"); +} + +// Regression test for b/31486443. +TEST_F(QuicPacketGeneratorTest, ConnectionCloseFrameLargerThanPacketSize) { + delegate_.SetCanWriteAnything(); + QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(); + frame->error_code = QUIC_PACKET_WRITE_ERROR; + char buf[2000] = {}; + QuicStringPiece error_details(buf, 2000); + frame->error_details = QuicString(error_details); + generator_.AddControlFrame(QuicFrame(frame), /*bundle_ack=*/false); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); +} + +TEST_F(QuicPacketGeneratorTest, RandomPaddingAfterFinSingleStreamSinglePacket) { + const QuicByteCount kStreamFramePayloadSize = 100u; + char buf[kStreamFramePayloadSize] = {}; + const QuicStreamId kDataStreamId = 5; + // Set the packet size be enough for one stream frame with 0 stream offset and + // max size of random padding. + size_t length = + NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + + GetPacketHeaderSize( + framer_.transport_version(), + creator_->GetDestinationConnectionIdLength(), + creator_->GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::SendVersionInPacket(creator_), + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0, + QuicPacketCreatorPeer::GetLengthLength(creator_)) + + QuicFramer::GetMinStreamFrameSize( + framer_.transport_version(), kDataStreamId, 0, + /*last_frame_in_packet=*/false, + kStreamFramePayloadSize + kMaxNumRandomPaddingBytes) + + kStreamFramePayloadSize + kMaxNumRandomPaddingBytes; + generator_.SetMaxPacketLength(length); + delegate_.SetCanWriteAnything(); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + kDataStreamId, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING); + generator_.Flush(); + EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + EXPECT_EQ(1u, packets_.size()); + PacketContents contents; + // The packet has both stream and padding frames. + contents.num_padding_frames = 1; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); +} + +TEST_F(QuicPacketGeneratorTest, + RandomPaddingAfterFinSingleStreamMultiplePackets) { + const QuicByteCount kStreamFramePayloadSize = 100u; + char buf[kStreamFramePayloadSize] = {}; + const QuicStreamId kDataStreamId = 5; + // Set the packet size be enough for one stream frame with 0 stream offset + + // 1. One or more packets will accommodate. + size_t length = + NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + + GetPacketHeaderSize( + framer_.transport_version(), + creator_->GetDestinationConnectionIdLength(), + creator_->GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::SendVersionInPacket(creator_), + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0, + QuicPacketCreatorPeer::GetLengthLength(creator_)) + + QuicFramer::GetMinStreamFrameSize( + framer_.transport_version(), kDataStreamId, 0, + /*last_frame_in_packet=*/false, kStreamFramePayloadSize + 1) + + kStreamFramePayloadSize + 1; + generator_.SetMaxPacketLength(length); + delegate_.SetCanWriteAnything(); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + kDataStreamId, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING); + generator_.Flush(); + EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + EXPECT_LE(1u, packets_.size()); + PacketContents contents; + // The first packet has both stream and padding frames. + contents.num_stream_frames = 1; + contents.num_padding_frames = 1; + CheckPacketContains(contents, 0); + + for (size_t i = 1; i < packets_.size(); ++i) { + // Following packets only have paddings. + contents.num_stream_frames = 0; + contents.num_padding_frames = 1; + CheckPacketContains(contents, i); + } +} + +TEST_F(QuicPacketGeneratorTest, + RandomPaddingAfterFinMultipleStreamsMultiplePackets) { + const QuicByteCount kStreamFramePayloadSize = 100u; + char buf[kStreamFramePayloadSize] = {}; + const QuicStreamId kDataStreamId1 = 5; + const QuicStreamId kDataStreamId2 = 6; + // Set the packet size be enough for first frame with 0 stream offset + second + // frame + 1 byte payload. two or more packets will accommodate. + size_t length = + NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + + GetPacketHeaderSize( + framer_.transport_version(), + creator_->GetDestinationConnectionIdLength(), + creator_->GetSourceConnectionIdLength(), + QuicPacketCreatorPeer::SendVersionInPacket(creator_), + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_), + QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0, + QuicPacketCreatorPeer::GetLengthLength(creator_)) + + QuicFramer::GetMinStreamFrameSize( + framer_.transport_version(), kDataStreamId1, 0, + /*last_frame_in_packet=*/false, kStreamFramePayloadSize) + + kStreamFramePayloadSize + + QuicFramer::GetMinStreamFrameSize(framer_.transport_version(), + kDataStreamId1, 0, + /*last_frame_in_packet=*/false, 1) + + 1; + generator_.SetMaxPacketLength(length); + delegate_.SetCanWriteAnything(); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_); + QuicConsumedData consumed = generator_.ConsumeData( + kDataStreamId1, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING); + EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed); + MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_); + consumed = generator_.ConsumeData(kDataStreamId2, &iov_, 1u, iov_.iov_len, 0, + FIN_AND_PADDING); + EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed); + generator_.Flush(); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + EXPECT_LE(2u, packets_.size()); + PacketContents contents; + // The first packet has two stream frames. + contents.num_stream_frames = 2; + CheckPacketContains(contents, 0); + + // The second packet has one stream frame and padding frames. + contents.num_stream_frames = 1; + contents.num_padding_frames = 1; + CheckPacketContains(contents, 1); + + for (size_t i = 2; i < packets_.size(); ++i) { + // Following packets only have paddings. + contents.num_stream_frames = 0; + contents.num_padding_frames = 1; + CheckPacketContains(contents, i); + } +} + +TEST_F(QuicPacketGeneratorTest, AddMessageFrame) { + if (framer_.transport_version() <= QUIC_VERSION_44) { + return; + } + quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); + delegate_.SetCanWriteAnything(); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + MakeIOVector("foo", &iov_); + generator_.ConsumeData( + QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u, + iov_.iov_len, 0, FIN); + EXPECT_EQ(MESSAGE_STATUS_SUCCESS, + generator_.AddMessageFrame( + 1, MakeSpan(&allocator_, "message", &storage))); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + // Add a message which causes the flush of current packet. + EXPECT_EQ( + MESSAGE_STATUS_SUCCESS, + generator_.AddMessageFrame( + 2, MakeSpan(&allocator_, + QuicString(generator_.GetLargestMessagePayload(), 'a'), + &storage))); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + // Failed to send messages which cannot fit into one packet. + EXPECT_EQ( + MESSAGE_STATUS_TOO_LARGE, + generator_.AddMessageFrame( + 3, + MakeSpan(&allocator_, + QuicString(generator_.GetLargestMessagePayload() + 10, 'a'), + &storage))); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_packet_number.cc b/quic/core/quic_packet_number.cc new file mode 100644 index 0000000..8a799c1 --- /dev/null +++ b/quic/core/quic_packet_number.cc
@@ -0,0 +1,132 @@ +// Copyright (c) 2019 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/quic_packet_number.h" + +namespace quic { + +QuicPacketNumber::QuicPacketNumber() + : packet_number_(UninitializedPacketNumber()) {} + +QuicPacketNumber::QuicPacketNumber(uint64_t packet_number) + : packet_number_(packet_number) { + DCHECK_NE(UninitializedPacketNumber(), packet_number) + << "Use default constructor for uninitialized packet number"; +} + +void QuicPacketNumber::Clear() { + packet_number_ = UninitializedPacketNumber(); +} + +uint64_t QuicPacketNumber::Hash() const { + DCHECK(IsInitialized()); + return packet_number_; +} + +uint64_t QuicPacketNumber::ToUint64() const { + DCHECK(IsInitialized()); + return packet_number_; +} + +bool QuicPacketNumber::IsInitialized() const { + return packet_number_ != UninitializedPacketNumber(); +} + +QuicPacketNumber& QuicPacketNumber::operator++() { +#ifndef NDEBUG + DCHECK(IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max() - 1); + } else { + DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max()); + } +#endif + packet_number_++; + return *this; +} + +QuicPacketNumber QuicPacketNumber::operator++(int) { +#ifndef NDEBUG + DCHECK(IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max() - 1); + } else { + DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max()); + } +#endif + QuicPacketNumber previous(*this); + packet_number_++; + return previous; +} + +QuicPacketNumber& QuicPacketNumber::operator--() { +#ifndef NDEBUG + DCHECK(IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_GE(ToUint64(), 1UL); + } else { + DCHECK_GT(ToUint64(), 1UL); + } +#endif + packet_number_--; + return *this; +} + +QuicPacketNumber QuicPacketNumber::operator--(int) { +#ifndef NDEBUG + DCHECK(IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_GE(ToUint64(), 1UL); + } else { + DCHECK_GT(ToUint64(), 1UL); + } +#endif + QuicPacketNumber previous(*this); + packet_number_--; + return previous; +} + +QuicPacketNumber& QuicPacketNumber::operator+=(uint64_t delta) { +#ifndef NDEBUG + DCHECK(IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_GT(std::numeric_limits<uint64_t>::max() - ToUint64(), delta); + } else { + DCHECK_GE(std::numeric_limits<uint64_t>::max() - ToUint64(), delta); + } +#endif + packet_number_ += delta; + return *this; +} + +QuicPacketNumber& QuicPacketNumber::operator-=(uint64_t delta) { +#ifndef NDEBUG + DCHECK(IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_GE(ToUint64(), delta); + } else { + DCHECK_GT(ToUint64(), delta); + } +#endif + packet_number_ -= delta; + return *this; +} + +std::ostream& operator<<(std::ostream& os, const QuicPacketNumber& p) { + if (p.IsInitialized()) { + os << p.packet_number_; + } else { + os << "uninitialized"; + } + return os; +} + +// static +uint64_t QuicPacketNumber::UninitializedPacketNumber() { + return GetQuicRestartFlag(quic_uint64max_uninitialized_pn) + ? std::numeric_limits<uint64_t>::max() + : 0; +} + +} // namespace quic
diff --git a/quic/core/quic_packet_number.h b/quic/core/quic_packet_number.h new file mode 100644 index 0000000..6797e3c --- /dev/null +++ b/quic/core/quic_packet_number.h
@@ -0,0 +1,154 @@ +// Copyright (c) 2019 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_QUIC_PACKET_NUMBER_H_ +#define QUICHE_QUIC_CORE_QUIC_PACKET_NUMBER_H_ + +#include <limits> +#include <ostream> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +// QuicPacketNumber can either initialized or uninitialized. An initialized +// packet number is simply an ordinal number. A sentinel value is used to +// represent an uninitialized packet number. +class QUIC_EXPORT_PRIVATE QuicPacketNumber { + public: + // Construct an uninitialized packet number. + QuicPacketNumber(); + // Construct a packet number from uint64_t. |packet_number| cannot equal the + // sentinel value. + explicit QuicPacketNumber(uint64_t packet_number); + + // Packet number becomes uninitialized after calling this function. + void Clear(); + + // REQUIRES: IsInitialized() == true. + uint64_t Hash() const; + + // Converts packet number to uint64_t. + // REQUIRES: IsInitialized() == true. + uint64_t ToUint64() const; + + // Returns true if packet number is considered initialized. + bool IsInitialized() const; + + // REQUIRES: IsInitialized() == true && ToUint64() < + // numeric_limits<uint64_t>::max() - 1. + QuicPacketNumber& operator++(); + QuicPacketNumber operator++(int); + // REQUIRES: IsInitialized() == true && ToUint64() >= 1. + QuicPacketNumber& operator--(); + QuicPacketNumber operator--(int); + + // REQUIRES: IsInitialized() == true && numeric_limits<uint64_t>::max() - + // ToUint64() > |delta|. + QuicPacketNumber& operator+=(uint64_t delta); + // REQUIRES: IsInitialized() == true && ToUint64() >= |delta|. + QuicPacketNumber& operator-=(uint64_t delta); + + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicPacketNumber& p); + + private: + // All following operators REQUIRE operands.Initialized() == true. + friend inline bool operator==(QuicPacketNumber lhs, QuicPacketNumber rhs); + friend inline bool operator!=(QuicPacketNumber lhs, QuicPacketNumber rhs); + friend inline bool operator<(QuicPacketNumber lhs, QuicPacketNumber rhs); + friend inline bool operator<=(QuicPacketNumber lhs, QuicPacketNumber rhs); + friend inline bool operator>(QuicPacketNumber lhs, QuicPacketNumber rhs); + friend inline bool operator>=(QuicPacketNumber lhs, QuicPacketNumber rhs); + + // REQUIRES: numeric_limits<uint64_t>::max() - lhs.ToUint64() > |delta|. + friend inline QuicPacketNumber operator+(QuicPacketNumber lhs, + uint64_t delta); + // REQUIRES: lhs.ToUint64() >= |delta|. + friend inline QuicPacketNumber operator-(QuicPacketNumber lhs, + uint64_t delta); + // REQUIRES: lhs >= rhs. + friend inline uint64_t operator-(QuicPacketNumber lhs, QuicPacketNumber rhs); + + // The sentinel value representing an uninitialized packet number. + static uint64_t UninitializedPacketNumber(); + + uint64_t packet_number_; +}; + +class QuicPacketNumberHash { + public: + uint64_t operator()(QuicPacketNumber packet_number) const noexcept { + return packet_number.Hash(); + } +}; + +inline bool operator==(QuicPacketNumber lhs, QuicPacketNumber rhs) { + DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs; + return lhs.packet_number_ == rhs.packet_number_; +} + +inline bool operator!=(QuicPacketNumber lhs, QuicPacketNumber rhs) { + DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs; + return lhs.packet_number_ != rhs.packet_number_; +} + +inline bool operator<(QuicPacketNumber lhs, QuicPacketNumber rhs) { + DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs; + return lhs.packet_number_ < rhs.packet_number_; +} + +inline bool operator<=(QuicPacketNumber lhs, QuicPacketNumber rhs) { + DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs; + return lhs.packet_number_ <= rhs.packet_number_; +} + +inline bool operator>(QuicPacketNumber lhs, QuicPacketNumber rhs) { + DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs; + return lhs.packet_number_ > rhs.packet_number_; +} + +inline bool operator>=(QuicPacketNumber lhs, QuicPacketNumber rhs) { + DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs; + return lhs.packet_number_ >= rhs.packet_number_; +} + +inline QuicPacketNumber operator+(QuicPacketNumber lhs, uint64_t delta) { +#ifndef NDEBUG + DCHECK(lhs.IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_GT(std::numeric_limits<uint64_t>::max() - lhs.ToUint64(), delta); + } else { + DCHECK_GE(std::numeric_limits<uint64_t>::max() - lhs.ToUint64(), delta); + } +#endif + return QuicPacketNumber(lhs.packet_number_ + delta); +} + +inline QuicPacketNumber operator-(QuicPacketNumber lhs, uint64_t delta) { +#ifndef NDEBUG + DCHECK(lhs.IsInitialized()); + if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + DCHECK_GE(lhs.ToUint64(), delta); + } else { + DCHECK_GT(lhs.ToUint64(), delta); + } +#endif + return QuicPacketNumber(lhs.packet_number_ - delta); +} + +inline uint64_t operator-(QuicPacketNumber lhs, QuicPacketNumber rhs) { + DCHECK(lhs.IsInitialized() && rhs.IsInitialized() && lhs >= rhs) + << lhs << " vs. " << rhs; + return lhs.packet_number_ - rhs.packet_number_; +} + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PACKET_NUMBER_H_
diff --git a/quic/core/quic_packet_number_test.cc b/quic/core/quic_packet_number_test.cc new file mode 100644 index 0000000..5e32b8c --- /dev/null +++ b/quic/core/quic_packet_number_test.cc
@@ -0,0 +1,83 @@ +// Copyright (c) 2019 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/quic_packet_number.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { + +namespace test { + +namespace { + +TEST(QuicPacketNumberTest, BasicTest) { + QuicPacketNumber num; + EXPECT_FALSE(num.IsInitialized()); + + QuicPacketNumber num2(10); + EXPECT_TRUE(num2.IsInitialized()); + EXPECT_EQ(10u, num2.ToUint64()); + EXPECT_EQ(10u, num2.Hash()); + num2.Clear(); + EXPECT_FALSE(num2.IsInitialized()); + + if (!GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + QuicPacketNumber num3(std::numeric_limits<uint64_t>::max()); + EXPECT_TRUE(num3.IsInitialized()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.ToUint64()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.Hash()); + num3.Clear(); + EXPECT_FALSE(num3.IsInitialized()); + return; + } + + QuicPacketNumber num4(0); + EXPECT_TRUE(num4.IsInitialized()); + EXPECT_EQ(0u, num4.ToUint64()); + EXPECT_EQ(0u, num4.Hash()); + num4.Clear(); + EXPECT_FALSE(num4.IsInitialized()); +} + +TEST(QuicPacketNumberTest, Operators) { + QuicPacketNumber num(100); + EXPECT_EQ(QuicPacketNumber(100), num++); + EXPECT_EQ(QuicPacketNumber(101), num); + EXPECT_EQ(QuicPacketNumber(101), num--); + EXPECT_EQ(QuicPacketNumber(100), num); + + EXPECT_EQ(QuicPacketNumber(101), ++num); + EXPECT_EQ(QuicPacketNumber(100), --num); + + if (!GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) { + QuicPacketNumber num2(std::numeric_limits<uint64_t>::max()); + EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max()), num2--); + EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 1), num2); + EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 2), + --num2); + + EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 2), + num2++); + EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 1), num2); + EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max()), ++num2); + return; + } + + QuicPacketNumber num3(0); + EXPECT_EQ(QuicPacketNumber(0), num3++); + EXPECT_EQ(QuicPacketNumber(1), num3); + EXPECT_EQ(QuicPacketNumber(2), ++num3); + + EXPECT_EQ(QuicPacketNumber(2), num3--); + EXPECT_EQ(QuicPacketNumber(1), num3); + EXPECT_EQ(QuicPacketNumber(0), --num3); +} + +} // namespace + +} // namespace test + +} // namespace quic
diff --git a/quic/core/quic_packet_reader.cc b/quic/core/quic_packet_reader.cc new file mode 100644 index 0000000..3216013 --- /dev/null +++ b/quic/core/quic_packet_reader.cc
@@ -0,0 +1,226 @@ +// Copyright 2015 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/quic_packet_reader.h" + +#include <errno.h> +#ifndef __APPLE__ +// This is a GNU header that is not present on Apple platforms +#include <features.h> +#endif +#include <string.h> +#include <sys/socket.h> + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_server_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/quic/platform/impl/quic_socket_utils.h" + +#ifndef SO_RXQ_OVFL +#define SO_RXQ_OVFL 40 +#endif + +namespace quic { + +QuicPacketReader::QuicPacketReader() { + Initialize(); +} + +void QuicPacketReader::Initialize() { +#if MMSG_MORE + // Zero initialize uninitialized memory. + memset(mmsg_hdr_, 0, sizeof(mmsg_hdr_)); + + for (int i = 0; i < kNumPacketsPerReadMmsgCall; ++i) { + packets_[i].iov.iov_base = packets_[i].buf; + packets_[i].iov.iov_len = sizeof(packets_[i].buf); + memset(&packets_[i].raw_address, 0, sizeof(packets_[i].raw_address)); + memset(packets_[i].cbuf, 0, sizeof(packets_[i].cbuf)); + memset(packets_[i].buf, 0, sizeof(packets_[i].buf)); + + msghdr* hdr = &mmsg_hdr_[i].msg_hdr; + hdr->msg_name = &packets_[i].raw_address; + hdr->msg_namelen = sizeof(sockaddr_storage); + hdr->msg_iov = &packets_[i].iov; + hdr->msg_iovlen = 1; + + hdr->msg_control = packets_[i].cbuf; + hdr->msg_controllen = kCmsgSpaceForReadPacket; + } +#endif +} + +QuicPacketReader::~QuicPacketReader() = default; + +bool QuicPacketReader::ReadAndDispatchPackets( + int fd, + int port, + const QuicClock& clock, + ProcessPacketInterface* processor, + QuicPacketCount* packets_dropped) { +#if MMSG_MORE_NO_ANDROID + return ReadAndDispatchManyPackets(fd, port, clock, processor, + packets_dropped); +#else + return ReadAndDispatchSinglePacket(fd, port, clock, processor, + packets_dropped); +#endif +} + +bool QuicPacketReader::ReadAndDispatchManyPackets( + int fd, + int port, + const QuicClock& clock, + ProcessPacketInterface* processor, + QuicPacketCount* packets_dropped) { +#if MMSG_MORE_NO_ANDROID + // Re-set the length fields in case recvmmsg has changed them. + for (int i = 0; i < kNumPacketsPerReadMmsgCall; ++i) { + DCHECK_LE(kMaxPacketSize, packets_[i].iov.iov_len); + msghdr* hdr = &mmsg_hdr_[i].msg_hdr; + hdr->msg_namelen = sizeof(sockaddr_storage); + DCHECK_EQ(1, hdr->msg_iovlen); + hdr->msg_controllen = kCmsgSpaceForReadPacket; + hdr->msg_flags = 0; + } + + int packets_read = + recvmmsg(fd, mmsg_hdr_, kNumPacketsPerReadMmsgCall, MSG_TRUNC, nullptr); + + if (packets_read <= 0) { + return false; // recvmmsg failed. + } + + bool use_quic_time = + GetQuicReloadableFlag(quic_use_quic_time_for_received_timestamp); + QuicTime fallback_timestamp(QuicTime::Zero()); + QuicWallTime fallback_walltimestamp = QuicWallTime::Zero(); + for (int i = 0; i < packets_read; ++i) { + if (mmsg_hdr_[i].msg_len == 0) { + continue; + } + + if (QUIC_PREDICT_FALSE(mmsg_hdr_[i].msg_hdr.msg_flags & MSG_CTRUNC)) { + QUIC_BUG << "Incorrectly set control length: " + << mmsg_hdr_[i].msg_hdr.msg_controllen << ", expected " + << kCmsgSpaceForReadPacket; + continue; + } + + if (QUIC_PREDICT_FALSE(mmsg_hdr_[i].msg_hdr.msg_flags & MSG_TRUNC)) { + QUIC_LOG_FIRST_N(WARNING, 100) + << "Dropping truncated QUIC packet: buffer size:" + << packets_[i].iov.iov_len << " packet size:" << mmsg_hdr_[i].msg_len; + QUIC_SERVER_HISTOGRAM_COUNTS( + "QuicPacketReader.DroppedPacketSize", mmsg_hdr_[i].msg_len, 1, 10000, + 20, "In QuicPacketReader, the size of big packets that are dropped."); + continue; + } + + QuicSocketAddress peer_address(packets_[i].raw_address); + QuicIpAddress self_ip; + QuicWallTime packet_walltimestamp = QuicWallTime::Zero(); + QuicSocketUtils::GetAddressAndTimestampFromMsghdr( + &mmsg_hdr_[i].msg_hdr, &self_ip, &packet_walltimestamp); + if (!self_ip.IsInitialized()) { + QUIC_BUG << "Unable to get self IP address."; + continue; + } + + // This isn't particularly desirable, but not all platforms support socket + // timestamping. + QuicTime timestamp(QuicTime::Zero()); + if (!use_quic_time) { + if (packet_walltimestamp.IsZero()) { + if (fallback_walltimestamp.IsZero()) { + fallback_walltimestamp = clock.WallNow(); + } + packet_walltimestamp = fallback_walltimestamp; + } + timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp); + + } else { + QUIC_RELOADABLE_FLAG_COUNT(quic_use_quic_time_for_received_timestamp); + if (packet_walltimestamp.IsZero()) { + if (!fallback_timestamp.IsInitialized()) { + fallback_timestamp = clock.Now(); + } + timestamp = fallback_timestamp; + } else { + timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp); + } + } + int ttl = 0; + bool has_ttl = + QuicSocketUtils::GetTtlFromMsghdr(&mmsg_hdr_[i].msg_hdr, &ttl); + char* headers = nullptr; + size_t headers_length = 0; + QuicSocketUtils::GetPacketHeadersFromMsghdr(&mmsg_hdr_[i].msg_hdr, &headers, + &headers_length); + QuicReceivedPacket packet(reinterpret_cast<char*>(packets_[i].iov.iov_base), + mmsg_hdr_[i].msg_len, timestamp, false, ttl, + has_ttl, headers, headers_length, false); + QuicSocketAddress self_address(self_ip, port); + processor->ProcessPacket(self_address, peer_address, packet); + } + + if (packets_dropped != nullptr) { + QuicSocketUtils::GetOverflowFromMsghdr(&mmsg_hdr_[0].msg_hdr, + packets_dropped); + } + + // We may not have read all of the packets available on the socket. + return packets_read == kNumPacketsPerReadMmsgCall; +#else + QUIC_LOG(FATAL) << "Unsupported"; + return false; +#endif +} + +/* static */ +bool QuicPacketReader::ReadAndDispatchSinglePacket( + int fd, + int port, + const QuicClock& clock, + ProcessPacketInterface* processor, + QuicPacketCount* packets_dropped) { + char buf[kMaxV4PacketSize]; + + QuicSocketAddress peer_address; + QuicIpAddress self_ip; + QuicWallTime walltimestamp = QuicWallTime::Zero(); + int bytes_read = + QuicSocketUtils::ReadPacket(fd, buf, QUIC_ARRAYSIZE(buf), packets_dropped, + &self_ip, &walltimestamp, &peer_address); + if (bytes_read < 0) { + return false; // ReadPacket failed. + } + + if (!self_ip.IsInitialized()) { + QUIC_BUG << "Unable to get self IP address."; + return false; + } + // This isn't particularly desirable, but not all platforms support socket + // timestamping. + if (walltimestamp.IsZero()) { + walltimestamp = clock.WallNow(); + } + QuicTime timestamp = clock.ConvertWallTimeToQuicTime(walltimestamp); + + QuicReceivedPacket packet(buf, bytes_read, timestamp, false); + QuicSocketAddress self_address(self_ip, port); + processor->ProcessPacket(self_address, peer_address, packet); + + // The socket read was successful, so return true even if packet dispatch + // failed. + return true; +} + +} // namespace quic
diff --git a/quic/core/quic_packet_reader.h b/quic/core/quic_packet_reader.h new file mode 100644 index 0000000..9767c39 --- /dev/null +++ b/quic/core/quic_packet_reader.h
@@ -0,0 +1,91 @@ +// Copyright 2015 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. + +// A class to read incoming QUIC packets from the UDP socket. + +#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_READER_H_ +#define QUICHE_QUIC_CORE_QUIC_PACKET_READER_H_ + +#include <netinet/in.h> +// Include here to guarantee this header gets included (for MSG_WAITFORONE) +// regardless of how the below transitive header include set may change. +#include <sys/socket.h> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/quic/platform/impl/quic_socket_utils.h" + +namespace quic { + +#if MMSG_MORE +// Read in larger batches to minimize recvmmsg overhead. +const int kNumPacketsPerReadMmsgCall = 16; +#endif + +class QuicPacketReader { + public: + QuicPacketReader(); + QuicPacketReader(const QuicPacketReader&) = delete; + QuicPacketReader& operator=(const QuicPacketReader&) = delete; + + virtual ~QuicPacketReader(); + + // Reads a number of packets from the given fd, and then passes them off to + // the PacketProcessInterface. Returns true if there may be additional + // packets available on the socket. + // Populates |packets_dropped| if it is non-null and the socket is configured + // to track dropped packets and some packets are read. + // If the socket has timestamping enabled, the per packet timestamps will be + // passed to the processor. Otherwise, |clock| will be used. + virtual bool ReadAndDispatchPackets(int fd, + int port, + const QuicClock& clock, + ProcessPacketInterface* processor, + QuicPacketCount* packets_dropped); + + private: + // Initialize the internal state of the reader. + void Initialize(); + + // Reads and dispatches many packets using recvmmsg. + bool ReadAndDispatchManyPackets(int fd, + int port, + const QuicClock& clock, + ProcessPacketInterface* processor, + QuicPacketCount* packets_dropped); + + // Reads and dispatches a single packet using recvmsg. + static bool ReadAndDispatchSinglePacket(int fd, + int port, + const QuicClock& clock, + ProcessPacketInterface* processor, + QuicPacketCount* packets_dropped); + +#if MMSG_MORE + // Storage only used when recvmmsg is available. + // TODO(danzh): change it to be a pointer to avoid the allocation on the stack + // from exceeding maximum allowed frame size. + // packets_ and mmsg_hdr_ are used to supply cbuf and buf to the recvmmsg + // call. + struct PacketData { + iovec iov; + // raw_address is used for address information provided by the recvmmsg + // call on the packets. + struct sockaddr_storage raw_address; + // cbuf is used for ancillary data from the kernel on recvmmsg. + char cbuf[kCmsgSpaceForReadPacket]; + // buf is used for the data read from the kernel on recvmmsg. + char buf[kMaxV4PacketSize]; + }; + PacketData packets_[kNumPacketsPerReadMmsgCall]; + mmsghdr mmsg_hdr_[kNumPacketsPerReadMmsgCall]; +#endif +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PACKET_READER_H_
diff --git a/quic/core/quic_packet_writer.h b/quic/core/quic_packet_writer.h new file mode 100644 index 0000000..c33e9b7 --- /dev/null +++ b/quic/core/quic_packet_writer.h
@@ -0,0 +1,132 @@ +// Copyright 2013 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_QUIC_PACKET_WRITER_H_ +#define QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_ + +#include <cstddef> + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +struct WriteResult; + +struct QUIC_EXPORT_PRIVATE PerPacketOptions { + virtual ~PerPacketOptions() {} + + // Returns a heap-allocated copy of |this|. + // + // The subclass implementation of this method should look like this: + // return QuicMakeUnique<MyAwesomePerPacketOptions>(*this); + // + // This method is declared pure virtual in order to ensure the subclasses + // would not forget to override it. + virtual std::unique_ptr<PerPacketOptions> Clone() const = 0; + + // Specifies release time delay for this packet. + QuicTime::Delta release_time_delay = QuicTime::Delta::Zero(); +}; + +// An interface between writers and the entity managing the +// socket (in our case the QuicDispatcher). This allows the Dispatcher to +// control writes, and manage any writers who end up write blocked. +// A concrete writer works in one of the two modes: +// - PassThrough mode. This is the default mode. Caller calls WritePacket with +// caller-allocated packet buffer. Unless the writer is blocked, each call to +// WritePacket triggers a write using the underlying socket API. +// +// - Batch mode. In this mode, a call to WritePacket may not cause a packet to +// be sent using the underlying socket API. Instead, multiple packets are +// saved in the writer's internal buffer until they are flushed. The flush can +// be explicit, by calling Flush, or implicit, e.g. by calling +// WritePacket when the internal buffer is near full. +// +// Buffer management: +// In Batch mode, a writer manages an internal buffer, which is large enough to +// hold multiple packets' data. If the caller calls WritePacket with a +// caller-allocated packet buffer, the writer will memcpy the buffer into the +// internal buffer. Caller can also avoid this memcpy by: +// 1. Call GetNextWriteLocation to get a pointer P into the internal buffer. +// 2. Serialize the packet directly to P. +// 3. Call WritePacket with P as the |buffer|. +class QUIC_EXPORT_PRIVATE QuicPacketWriter { + public: + virtual ~QuicPacketWriter() {} + + // PassThrough mode: + // Sends the packet out to the peer, with some optional per-packet options. + // If the write succeeded, the result's status is WRITE_STATUS_OK and + // bytes_written is populated. If the write failed, the result's status is + // WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and error_code is populated. + // + // Batch mode: + // If the writer is blocked, return WRITE_STATUS_BLOCKED immediately. + // If the packet can be batched with other buffered packets, save the packet + // to the internal buffer. + // If the packet can not be batched, or the internal buffer is near full after + // it is buffered, the internal buffer is flushed to free up space. + // Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) on success. When + // <bytes_flushed> is zero, it means the packet is buffered and not flushed. + // Return WRITE_STATUS_BLOCKED if the packet is not buffered and the socket is + // blocked while flushing. + // Otherwise return an error status. + // + // Options must be either null, or created for the particular QuicPacketWriter + // implementation. Options may be ignored, depending on the implementation. + virtual WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) = 0; + + // Returns true if the network socket is not writable. + virtual bool IsWriteBlocked() const = 0; + + // Records that the socket has become writable, for example when an EPOLLOUT + // is received or an asynchronous write completes. + virtual void SetWritable() = 0; + + // Returns the maximum size of the packet which can be written using this + // writer for the supplied peer address. This size may actually exceed the + // size of a valid QUIC packet. + virtual QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& peer_address) const = 0; + + // Returns true if the socket supports release timestamp. + virtual bool SupportsReleaseTime() const = 0; + + // True=Batch mode. False=PassThrough mode. + virtual bool IsBatchMode() const = 0; + + // PassThrough mode: Return nullptr. + // + // Batch mode: + // Return the starting address for the next packet's data. A minimum of + // kMaxPacketSize is guaranteed to be available from the returned address. If + // the internal buffer does not have enough space, nullptr is returned. + // All arguments should be identical to the follow-up call to |WritePacket|, + // they are here to allow advanced packet memory management in packet writers, + // e.g. one packet buffer pool per |peer_address|. + virtual char* GetNextWriteLocation(const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) = 0; + + // PassThrough mode: Return WriteResult(WRITE_STATUS_OK, 0). + // + // Batch mode: + // Try send all buffered packets. + // - Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) if all buffered + // packets were sent successfully. + // - Return WRITE_STATUS_BLOCKED, or an error status, if the underlying socket + // is blocked or returned an error while sending. Some packets may have been + // sent, packets not sent will stay in the internal buffer. + virtual WriteResult Flush() = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_
diff --git a/quic/core/quic_packet_writer_wrapper.cc b/quic/core/quic_packet_writer_wrapper.cc new file mode 100644 index 0000000..f1f25d8 --- /dev/null +++ b/quic/core/quic_packet_writer_wrapper.cc
@@ -0,0 +1,79 @@ +// Copyright 2014 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/quic_packet_writer_wrapper.h" + +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +QuicPacketWriterWrapper::QuicPacketWriterWrapper() = default; + +QuicPacketWriterWrapper::~QuicPacketWriterWrapper() { + unset_writer(); +} + +WriteResult QuicPacketWriterWrapper::WritePacket( + const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) { + return writer_->WritePacket(buffer, buf_len, self_address, peer_address, + options); +} + +bool QuicPacketWriterWrapper::IsWriteBlocked() const { + return writer_->IsWriteBlocked(); +} + +void QuicPacketWriterWrapper::SetWritable() { + writer_->SetWritable(); +} + +QuicByteCount QuicPacketWriterWrapper::GetMaxPacketSize( + const QuicSocketAddress& peer_address) const { + return writer_->GetMaxPacketSize(peer_address); +} + +bool QuicPacketWriterWrapper::SupportsReleaseTime() const { + return writer_->SupportsReleaseTime(); +} + +bool QuicPacketWriterWrapper::IsBatchMode() const { + return writer_->IsBatchMode(); +} + +char* QuicPacketWriterWrapper::GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) { + return writer_->GetNextWriteLocation(self_address, peer_address); +} + +WriteResult QuicPacketWriterWrapper::Flush() { + return writer_->Flush(); +} + +void QuicPacketWriterWrapper::set_writer(QuicPacketWriter* writer) { + unset_writer(); + writer_ = writer; + owns_writer_ = true; +} + +void QuicPacketWriterWrapper::set_non_owning_writer(QuicPacketWriter* writer) { + unset_writer(); + writer_ = writer; + owns_writer_ = false; +} + +void QuicPacketWriterWrapper::unset_writer() { + if (owns_writer_) { + delete writer_; + } + + owns_writer_ = false; + writer_ = nullptr; +} + +} // namespace quic
diff --git a/quic/core/quic_packet_writer_wrapper.h b/quic/core/quic_packet_writer_wrapper.h new file mode 100644 index 0000000..64b10f5 --- /dev/null +++ b/quic/core/quic_packet_writer_wrapper.h
@@ -0,0 +1,62 @@ +// Copyright 2014 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_QUIC_PACKET_WRITER_WRAPPER_H_ +#define QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_WRAPPER_H_ + +#include <cstddef> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" + +namespace quic { + +// Wraps a writer object to allow dynamically extending functionality. Use +// cases: replace writer while dispatcher and connections hold on to the +// wrapper; mix in monitoring; mix in mocks in unit tests. +class QuicPacketWriterWrapper : public QuicPacketWriter { + public: + QuicPacketWriterWrapper(); + QuicPacketWriterWrapper(const QuicPacketWriterWrapper&) = delete; + QuicPacketWriterWrapper& operator=(const QuicPacketWriterWrapper&) = delete; + ~QuicPacketWriterWrapper() override; + + // Default implementation of the QuicPacketWriter interface. Passes everything + // to |writer_|. + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) override; + bool IsWriteBlocked() const override; + void SetWritable() override; + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& peer_address) const override; + bool SupportsReleaseTime() const override; + bool IsBatchMode() const override; + char* GetNextWriteLocation(const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override; + WriteResult Flush() override; + + // Takes ownership of |writer|. + void set_writer(QuicPacketWriter* writer); + + // Does not take ownership of |writer|. + void set_non_owning_writer(QuicPacketWriter* writer); + + virtual void set_peer_address(const QuicSocketAddress& peer_address) {} + + QuicPacketWriter* writer() { return writer_; } + + private: + void unset_writer(); + + QuicPacketWriter* writer_ = nullptr; + bool owns_writer_ = false; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_WRAPPER_H_
diff --git a/quic/core/quic_packets.cc b/quic/core/quic_packets.cc new file mode 100644 index 0000000..e3ed6a7 --- /dev/null +++ b/quic/core/quic_packets.cc
@@ -0,0 +1,422 @@ +// Copyright (c) 2012 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/quic_packets.h" + +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.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 { + +QuicConnectionIdLength GetIncludedConnectionIdLength( + QuicConnectionId connection_id, + QuicConnectionIdIncluded connection_id_included) { + DCHECK(connection_id_included == CONNECTION_ID_PRESENT || + connection_id_included == CONNECTION_ID_ABSENT); + return connection_id_included == CONNECTION_ID_PRESENT + ? static_cast<QuicConnectionIdLength>(connection_id.length()) + : PACKET_0BYTE_CONNECTION_ID; +} + +QuicConnectionIdLength GetIncludedDestinationConnectionIdLength( + const QuicPacketHeader& header) { + return GetIncludedConnectionIdLength( + header.destination_connection_id, + header.destination_connection_id_included); +} + +QuicConnectionIdLength GetIncludedSourceConnectionIdLength( + const QuicPacketHeader& header) { + return GetIncludedConnectionIdLength(header.source_connection_id, + header.source_connection_id_included); +} + +size_t GetPacketHeaderSize(QuicTransportVersion version, + const QuicPacketHeader& header) { + return GetPacketHeaderSize( + version, GetIncludedDestinationConnectionIdLength(header), + GetIncludedSourceConnectionIdLength(header), header.version_flag, + header.nonce != nullptr, header.packet_number_length, + header.retry_token_length_length, header.retry_token.length(), + header.length_length); +} + +size_t GetPacketHeaderSize( + QuicTransportVersion version, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool include_version, + bool include_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicByteCount retry_token_length, + QuicVariableLengthIntegerLength length_length) { + if (version > QUIC_VERSION_43) { + if (include_version) { + // Long header. + return kPacketHeaderTypeSize + kConnectionIdLengthSize + + destination_connection_id_length + source_connection_id_length + + (version > QUIC_VERSION_44 ? packet_number_length + : PACKET_4BYTE_PACKET_NUMBER) + + kQuicVersionSize + + (include_diversification_nonce ? kDiversificationNonceSize : 0) + + retry_token_length_length + retry_token_length + length_length; + } + // Short header. + return kPacketHeaderTypeSize + destination_connection_id_length + + packet_number_length; + } + return kPublicFlagsSize + destination_connection_id_length + + (include_version ? kQuicVersionSize : 0) + packet_number_length + + (include_diversification_nonce ? kDiversificationNonceSize : 0); +} + +size_t GetStartOfEncryptedData(QuicTransportVersion version, + const QuicPacketHeader& header) { + return GetPacketHeaderSize(version, header); +} + +size_t GetStartOfEncryptedData( + QuicTransportVersion version, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool include_version, + bool include_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicByteCount retry_token_length, + QuicVariableLengthIntegerLength length_length) { + // Encryption starts before private flags. + return GetPacketHeaderSize( + version, destination_connection_id_length, source_connection_id_length, + include_version, include_diversification_nonce, packet_number_length, + retry_token_length_length, retry_token_length, length_length); +} + +QuicPacketHeader::QuicPacketHeader() + : destination_connection_id(EmptyQuicConnectionId()), + destination_connection_id_included(CONNECTION_ID_PRESENT), + source_connection_id(EmptyQuicConnectionId()), + source_connection_id_included(CONNECTION_ID_ABSENT), + reset_flag(false), + version_flag(false), + has_possible_stateless_reset_token(false), + packet_number_length(PACKET_4BYTE_PACKET_NUMBER), + version(UnsupportedQuicVersion()), + nonce(nullptr), + form(GOOGLE_QUIC_PACKET), + long_packet_type(INITIAL), + possible_stateless_reset_token(0), + retry_token_length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0), + retry_token(QuicStringPiece()), + length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0), + remaining_packet_length(0) {} + +QuicPacketHeader::QuicPacketHeader(const QuicPacketHeader& other) = default; + +QuicPacketHeader::~QuicPacketHeader() {} + +QuicPublicResetPacket::QuicPublicResetPacket() + : connection_id(EmptyQuicConnectionId()), nonce_proof(0) {} + +QuicPublicResetPacket::QuicPublicResetPacket(QuicConnectionId connection_id) + : connection_id(connection_id), nonce_proof(0) {} + +QuicVersionNegotiationPacket::QuicVersionNegotiationPacket() + : connection_id(EmptyQuicConnectionId()) {} + +QuicVersionNegotiationPacket::QuicVersionNegotiationPacket( + QuicConnectionId connection_id) + : connection_id(connection_id) {} + +QuicVersionNegotiationPacket::QuicVersionNegotiationPacket( + const QuicVersionNegotiationPacket& other) = default; + +QuicVersionNegotiationPacket::~QuicVersionNegotiationPacket() {} + +QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket() + : stateless_reset_token(0) {} + +QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket( + const QuicPacketHeader& header, + QuicUint128 token) + : header(header), stateless_reset_token(token) {} + +QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& other) = default; + +QuicIetfStatelessResetPacket::~QuicIetfStatelessResetPacket() {} + +std::ostream& operator<<(std::ostream& os, const QuicPacketHeader& header) { + os << "{ destination_connection_id: " << header.destination_connection_id + << " (" + << (header.destination_connection_id_included == CONNECTION_ID_PRESENT + ? "present" + : "absent") + << "), source_connection_id: " << header.source_connection_id << " (" + << (header.source_connection_id_included == CONNECTION_ID_PRESENT + ? "present" + : "absent") + << "), packet_number_length: " << header.packet_number_length + << ", reset_flag: " << header.reset_flag + << ", version_flag: " << header.version_flag; + if (header.version_flag) { + os << ", version: " << ParsedQuicVersionToString(header.version); + if (header.long_packet_type != INVALID_PACKET_TYPE) { + os << ", long_packet_type: " + << QuicUtils::QuicLongHeaderTypetoString(header.long_packet_type); + } + if (header.retry_token_length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0) { + os << ", retry_token_length_length: " + << static_cast<int>(header.retry_token_length_length); + } + if (header.retry_token.length() != 0) { + os << ", retry_token_length: " << header.retry_token.length(); + } + if (header.length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0) { + os << ", length_length: " << static_cast<int>(header.length_length); + } + if (header.remaining_packet_length != 0) { + os << ", remaining_packet_length: " << header.remaining_packet_length; + } + } + if (header.nonce != nullptr) { + os << ", diversification_nonce: " + << QuicTextUtils::HexEncode( + QuicStringPiece(header.nonce->data(), header.nonce->size())); + } + os << ", packet_number: " << header.packet_number << " }\n"; + return os; +} + +QuicData::QuicData(const char* buffer, size_t length) + : buffer_(buffer), length_(length), owns_buffer_(false) {} + +QuicData::QuicData(const char* buffer, size_t length, bool owns_buffer) + : buffer_(buffer), length_(length), owns_buffer_(owns_buffer) {} + +QuicData::~QuicData() { + if (owns_buffer_) { + delete[] const_cast<char*>(buffer_); + } +} + +QuicPacket::QuicPacket( + char* buffer, + size_t length, + bool owns_buffer, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool includes_version, + bool includes_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicByteCount retry_token_length, + QuicVariableLengthIntegerLength length_length) + : QuicData(buffer, length, owns_buffer), + buffer_(buffer), + destination_connection_id_length_(destination_connection_id_length), + source_connection_id_length_(source_connection_id_length), + includes_version_(includes_version), + includes_diversification_nonce_(includes_diversification_nonce), + packet_number_length_(packet_number_length), + retry_token_length_length_(retry_token_length_length), + retry_token_length_(retry_token_length), + length_length_(length_length) {} + +QuicPacket::QuicPacket(QuicTransportVersion version, + char* buffer, + size_t length, + bool owns_buffer, + const QuicPacketHeader& header) + : QuicPacket(buffer, + length, + owns_buffer, + GetIncludedDestinationConnectionIdLength(header), + GetIncludedSourceConnectionIdLength(header), + header.version_flag, + header.nonce != nullptr, + header.packet_number_length, + header.retry_token_length_length, + header.retry_token.length(), + header.length_length) {} + +QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, size_t length) + : QuicData(buffer, length) {} + +QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, + size_t length, + bool owns_buffer) + : QuicData(buffer, length, owns_buffer) {} + +std::unique_ptr<QuicEncryptedPacket> QuicEncryptedPacket::Clone() const { + char* buffer = new char[this->length()]; + memcpy(buffer, this->data(), this->length()); + return QuicMakeUnique<QuicEncryptedPacket>(buffer, this->length(), true); +} + +std::ostream& operator<<(std::ostream& os, const QuicEncryptedPacket& s) { + os << s.length() << "-byte data"; + return os; +} + +QuicReceivedPacket::QuicReceivedPacket(const char* buffer, + size_t length, + QuicTime receipt_time) + : QuicReceivedPacket(buffer, + length, + receipt_time, + false /* owns_buffer */) {} + +QuicReceivedPacket::QuicReceivedPacket(const char* buffer, + size_t length, + QuicTime receipt_time, + bool owns_buffer) + : QuicReceivedPacket(buffer, + length, + receipt_time, + owns_buffer, + 0 /* ttl */, + true /* ttl_valid */) {} + +QuicReceivedPacket::QuicReceivedPacket(const char* buffer, + size_t length, + QuicTime receipt_time, + bool owns_buffer, + int ttl, + bool ttl_valid) + : quic::QuicReceivedPacket(buffer, + length, + receipt_time, + owns_buffer, + ttl, + ttl_valid, + nullptr /* packet_headers */, + 0 /* headers_length */, + false /* owns_header_buffer */) {} + +QuicReceivedPacket::QuicReceivedPacket(const char* buffer, + size_t length, + QuicTime receipt_time, + bool owns_buffer, + int ttl, + bool ttl_valid, + char* packet_headers, + size_t headers_length, + bool owns_header_buffer) + : QuicEncryptedPacket(buffer, length, owns_buffer), + receipt_time_(receipt_time), + ttl_(ttl_valid ? ttl : -1), + packet_headers_(packet_headers), + headers_length_(headers_length), + owns_header_buffer_(owns_header_buffer) {} + +QuicReceivedPacket::~QuicReceivedPacket() { + if (owns_header_buffer_) { + delete[] static_cast<char*>(packet_headers_); + } +} + +std::unique_ptr<QuicReceivedPacket> QuicReceivedPacket::Clone() const { + char* buffer = new char[this->length()]; + memcpy(buffer, this->data(), this->length()); + if (this->packet_headers()) { + char* headers_buffer = new char[this->headers_length()]; + memcpy(headers_buffer, this->packet_headers(), this->headers_length()); + return QuicMakeUnique<QuicReceivedPacket>( + buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0, + headers_buffer, this->headers_length(), true); + } + + return QuicMakeUnique<QuicReceivedPacket>( + buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0); +} + +std::ostream& operator<<(std::ostream& os, const QuicReceivedPacket& s) { + os << s.length() << "-byte data"; + return os; +} + +QuicStringPiece QuicPacket::AssociatedData(QuicTransportVersion version) const { + return QuicStringPiece( + data(), + GetStartOfEncryptedData(version, destination_connection_id_length_, + source_connection_id_length_, includes_version_, + includes_diversification_nonce_, + packet_number_length_, retry_token_length_length_, + retry_token_length_, length_length_)); +} + +QuicStringPiece QuicPacket::Plaintext(QuicTransportVersion version) const { + const size_t start_of_encrypted_data = GetStartOfEncryptedData( + version, destination_connection_id_length_, source_connection_id_length_, + includes_version_, includes_diversification_nonce_, packet_number_length_, + retry_token_length_length_, retry_token_length_, length_length_); + return QuicStringPiece(data() + start_of_encrypted_data, + length() - start_of_encrypted_data); +} + +SerializedPacket::SerializedPacket(QuicPacketNumber packet_number, + QuicPacketNumberLength packet_number_length, + const char* encrypted_buffer, + QuicPacketLength encrypted_length, + bool has_ack, + bool has_stop_waiting) + : encrypted_buffer(encrypted_buffer), + encrypted_length(encrypted_length), + has_crypto_handshake(NOT_HANDSHAKE), + num_padding_bytes(0), + packet_number(packet_number), + packet_number_length(packet_number_length), + encryption_level(ENCRYPTION_NONE), + has_ack(has_ack), + has_stop_waiting(has_stop_waiting), + transmission_type(NOT_RETRANSMISSION) {} + +SerializedPacket::SerializedPacket(const SerializedPacket& other) = default; + +SerializedPacket& SerializedPacket::operator=(const SerializedPacket& other) = + default; + +SerializedPacket::SerializedPacket(SerializedPacket&& other) + : encrypted_buffer(other.encrypted_buffer), + encrypted_length(other.encrypted_length), + has_crypto_handshake(other.has_crypto_handshake), + num_padding_bytes(other.num_padding_bytes), + packet_number(other.packet_number), + packet_number_length(other.packet_number_length), + encryption_level(other.encryption_level), + has_ack(other.has_ack), + has_stop_waiting(other.has_stop_waiting), + transmission_type(other.transmission_type), + original_packet_number(other.original_packet_number), + largest_acked(other.largest_acked) { + retransmittable_frames.swap(other.retransmittable_frames); +} + +SerializedPacket::~SerializedPacket() {} + +void ClearSerializedPacket(SerializedPacket* serialized_packet) { + if (!serialized_packet->retransmittable_frames.empty()) { + DeleteFrames(&serialized_packet->retransmittable_frames); + } + serialized_packet->encrypted_buffer = nullptr; + serialized_packet->encrypted_length = 0; + serialized_packet->largest_acked.Clear(); +} + +char* CopyBuffer(const SerializedPacket& packet) { + char* dst_buffer = new char[packet.encrypted_length]; + memcpy(dst_buffer, packet.encrypted_buffer, packet.encrypted_length); + return dst_buffer; +} + +} // namespace quic
diff --git a/quic/core/quic_packets.h b/quic/core/quic_packets.h new file mode 100644 index 0000000..d2aaf34 --- /dev/null +++ b/quic/core/quic_packets.h
@@ -0,0 +1,369 @@ +// Copyright (c) 2012 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_QUIC_PACKETS_H_ +#define QUICHE_QUIC_CORE_QUIC_PACKETS_H_ + +#include <cstddef> +#include <cstdint> +#include <limits> +#include <list> +#include <memory> +#include <ostream> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +class QuicPacket; +struct QuicPacketHeader; + +// Number of connection ID bytes that are actually included over the wire. +QUIC_EXPORT_PRIVATE QuicConnectionIdLength +GetIncludedConnectionIdLength(QuicConnectionId connection_id, + QuicConnectionIdIncluded connection_id_included); + +// Number of destination connection ID bytes that are actually included over the +// wire for this particular header. +QUIC_EXPORT_PRIVATE QuicConnectionIdLength +GetIncludedDestinationConnectionIdLength(const QuicPacketHeader& header); + +// Number of source connection ID bytes that are actually included over the +// wire for this particular header. +QUIC_EXPORT_PRIVATE QuicConnectionIdLength +GetIncludedSourceConnectionIdLength(const QuicPacketHeader& header); + +// Size in bytes of the data packet header. +QUIC_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicTransportVersion version, + const QuicPacketHeader& header); + +QUIC_EXPORT_PRIVATE size_t +GetPacketHeaderSize(QuicTransportVersion version, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool include_version, + bool include_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicByteCount retry_token_length, + QuicVariableLengthIntegerLength length_length); + +// Index of the first byte in a QUIC packet of encrypted data. +QUIC_EXPORT_PRIVATE size_t +GetStartOfEncryptedData(QuicTransportVersion version, + const QuicPacketHeader& header); + +QUIC_EXPORT_PRIVATE size_t GetStartOfEncryptedData( + QuicTransportVersion version, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool include_version, + bool include_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicByteCount retry_token_length, + QuicVariableLengthIntegerLength length_length); + +struct QUIC_EXPORT_PRIVATE QuicPacketHeader { + QuicPacketHeader(); + QuicPacketHeader(const QuicPacketHeader& other); + ~QuicPacketHeader(); + + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicPacketHeader& header); + + // Universal header. All QuicPacket headers will have a connection_id and + // public flags. + QuicConnectionId destination_connection_id; + QuicConnectionIdIncluded destination_connection_id_included; + QuicConnectionId source_connection_id; + QuicConnectionIdIncluded source_connection_id_included; + // This is only used for Google QUIC. + bool reset_flag; + // For Google QUIC, version flag in packets from the server means version + // negotiation packet. For IETF QUIC, version flag means long header. + bool version_flag; + // Indicates whether |possible_stateless_reset_token| contains a valid value + // parsed from the packet buffer. IETF QUIC only, always false for GQUIC. + bool has_possible_stateless_reset_token; + QuicPacketNumberLength packet_number_length; + ParsedQuicVersion version; + // nonce contains an optional, 32-byte nonce value. If not included in the + // packet, |nonce| will be empty. + DiversificationNonce* nonce; + QuicPacketNumber packet_number; + // Format of this header. + PacketHeaderFormat form; + // Short packet type is reflected in packet_number_length. + QuicLongHeaderType long_packet_type; + // Only valid if |has_possible_stateless_reset_token| is true. + // Stores last 16 bytes of a this packet, used to check whether this packet is + // a stateless reset packet on decryption failure. + QuicUint128 possible_stateless_reset_token; + // Length of the retry token length variable length integer field, + // carried only by v99 IETF Initial packets. + QuicVariableLengthIntegerLength retry_token_length_length; + // Retry token, carried only by v99 IETF Initial packets. + QuicStringPiece retry_token; + // Length of the length variable length integer field, + // carried only by v99 IETF Initial, 0-RTT and Handshake packets. + QuicVariableLengthIntegerLength length_length; + // Length of the packet number and payload, carried only by v99 IETF Initial, + // 0-RTT and Handshake packets. Also includes the length of the + // diversification nonce in server to client 0-RTT packets. + QuicByteCount remaining_packet_length; +}; + +struct QUIC_EXPORT_PRIVATE QuicPublicResetPacket { + QuicPublicResetPacket(); + explicit QuicPublicResetPacket(QuicConnectionId connection_id); + + QuicConnectionId connection_id; + QuicPublicResetNonceProof nonce_proof; + QuicSocketAddress client_address; + // An arbitrary string to identify an endpoint. Used by clients to + // differentiate traffic from Google servers vs Non-google servers. + // Will not be used if empty(). + QuicString endpoint_id; +}; + +struct QUIC_EXPORT_PRIVATE QuicVersionNegotiationPacket { + QuicVersionNegotiationPacket(); + explicit QuicVersionNegotiationPacket(QuicConnectionId connection_id); + QuicVersionNegotiationPacket(const QuicVersionNegotiationPacket& other); + ~QuicVersionNegotiationPacket(); + + QuicConnectionId connection_id; + ParsedQuicVersionVector versions; +}; + +struct QUIC_EXPORT_PRIVATE QuicIetfStatelessResetPacket { + QuicIetfStatelessResetPacket(); + QuicIetfStatelessResetPacket(const QuicPacketHeader& header, + QuicUint128 token); + QuicIetfStatelessResetPacket(const QuicIetfStatelessResetPacket& other); + ~QuicIetfStatelessResetPacket(); + + QuicPacketHeader header; + QuicUint128 stateless_reset_token; +}; + +class QUIC_EXPORT_PRIVATE QuicData { + public: + QuicData(const char* buffer, size_t length); + QuicData(const char* buffer, size_t length, bool owns_buffer); + QuicData(const QuicData&) = delete; + QuicData& operator=(const QuicData&) = delete; + virtual ~QuicData(); + + QuicStringPiece AsStringPiece() const { + return QuicStringPiece(data(), length()); + } + + const char* data() const { return buffer_; } + size_t length() const { return length_; } + + private: + const char* buffer_; + size_t length_; + bool owns_buffer_; +}; + +class QUIC_EXPORT_PRIVATE QuicPacket : public QuicData { + public: + QuicPacket(char* buffer, + size_t length, + bool owns_buffer, + QuicConnectionIdLength destination_connection_id_length, + QuicConnectionIdLength source_connection_id_length, + bool includes_version, + bool includes_diversification_nonce, + QuicPacketNumberLength packet_number_length, + QuicVariableLengthIntegerLength retry_token_length_length, + QuicByteCount retry_token_length, + QuicVariableLengthIntegerLength length_length); + QuicPacket(QuicTransportVersion version, + char* buffer, + size_t length, + bool owns_buffer, + const QuicPacketHeader& header); + QuicPacket(const QuicPacket&) = delete; + QuicPacket& operator=(const QuicPacket&) = delete; + + QuicStringPiece AssociatedData(QuicTransportVersion version) const; + QuicStringPiece Plaintext(QuicTransportVersion version) const; + + char* mutable_data() { return buffer_; } + + private: + char* buffer_; + const QuicConnectionIdLength destination_connection_id_length_; + const QuicConnectionIdLength source_connection_id_length_; + const bool includes_version_; + const bool includes_diversification_nonce_; + const QuicPacketNumberLength packet_number_length_; + const QuicVariableLengthIntegerLength retry_token_length_length_; + const QuicByteCount retry_token_length_; + const QuicVariableLengthIntegerLength length_length_; +}; + +class QUIC_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData { + public: + QuicEncryptedPacket(const char* buffer, size_t length); + QuicEncryptedPacket(const char* buffer, size_t length, bool owns_buffer); + QuicEncryptedPacket(const QuicEncryptedPacket&) = delete; + QuicEncryptedPacket& operator=(const QuicEncryptedPacket&) = delete; + + // Clones the packet into a new packet which owns the buffer. + std::unique_ptr<QuicEncryptedPacket> Clone() const; + + // By default, gtest prints the raw bytes of an object. The bool data + // member (in the base class QuicData) causes this object to have padding + // bytes, which causes the default gtest object printer to read + // uninitialize memory. So we need to teach gtest how to print this object. + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicEncryptedPacket& s); +}; + +// A received encrypted QUIC packet, with a recorded time of receipt. +class QUIC_EXPORT_PRIVATE QuicReceivedPacket : public QuicEncryptedPacket { + public: + QuicReceivedPacket(const char* buffer, size_t length, QuicTime receipt_time); + QuicReceivedPacket(const char* buffer, + size_t length, + QuicTime receipt_time, + bool owns_buffer); + QuicReceivedPacket(const char* buffer, + size_t length, + QuicTime receipt_time, + bool owns_buffer, + int ttl, + bool ttl_valid); + QuicReceivedPacket(const char* buffer, + size_t length, + QuicTime receipt_time, + bool owns_buffer, + int ttl, + bool ttl_valid, + char* packet_headers, + size_t headers_length, + bool owns_header_buffer); + ~QuicReceivedPacket(); + QuicReceivedPacket(const QuicReceivedPacket&) = delete; + QuicReceivedPacket& operator=(const QuicReceivedPacket&) = delete; + + // Clones the packet into a new packet which owns the buffer. + std::unique_ptr<QuicReceivedPacket> Clone() const; + + // Returns the time at which the packet was received. + QuicTime receipt_time() const { return receipt_time_; } + + // This is the TTL of the packet, assuming ttl_vaild_ is true. + int ttl() const { return ttl_; } + + // Start of packet headers. + char* packet_headers() const { return packet_headers_; } + + // Length of packet headers. + int headers_length() const { return headers_length_; } + + // By default, gtest prints the raw bytes of an object. The bool data + // member (in the base class QuicData) causes this object to have padding + // bytes, which causes the default gtest object printer to read + // uninitialize memory. So we need to teach gtest how to print this object. + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicReceivedPacket& s); + + private: + const QuicTime receipt_time_; + int ttl_; + // Points to the start of packet headers. + char* packet_headers_; + // Length of packet headers. + int headers_length_; + // Whether owns the buffer for packet headers. + bool owns_header_buffer_; +}; + +struct QUIC_EXPORT_PRIVATE SerializedPacket { + SerializedPacket(QuicPacketNumber packet_number, + QuicPacketNumberLength packet_number_length, + const char* encrypted_buffer, + QuicPacketLength encrypted_length, + bool has_ack, + bool has_stop_waiting); + SerializedPacket(const SerializedPacket& other); + SerializedPacket& operator=(const SerializedPacket& other); + SerializedPacket(SerializedPacket&& other); + ~SerializedPacket(); + + // Not owned. + const char* encrypted_buffer; + QuicPacketLength encrypted_length; + QuicFrames retransmittable_frames; + IsHandshake has_crypto_handshake; + // -1: full padding to the end of a max-sized packet + // 0: no padding + // otherwise: only pad up to num_padding_bytes bytes + int16_t num_padding_bytes; + QuicPacketNumber packet_number; + QuicPacketNumberLength packet_number_length; + EncryptionLevel encryption_level; + bool has_ack; + bool has_stop_waiting; + TransmissionType transmission_type; + QuicPacketNumber original_packet_number; + // The largest acked of the AckFrame in this packet if has_ack is true, + // 0 otherwise. + QuicPacketNumber largest_acked; +}; + +// Deletes and clears all the frames and the packet from serialized packet. +QUIC_EXPORT_PRIVATE void ClearSerializedPacket( + SerializedPacket* serialized_packet); + +// Allocates a new char[] of size |packet.encrypted_length| and copies in +// |packet.encrypted_buffer|. +QUIC_EXPORT_PRIVATE char* CopyBuffer(const SerializedPacket& packet); + +struct QUIC_EXPORT_PRIVATE SerializedPacketDeleter { + void operator()(SerializedPacket* packet) { + if (packet->encrypted_buffer != nullptr) { + delete[] packet->encrypted_buffer; + } + delete packet; + } +}; + +// On destruction, OwningSerializedPacketPointer deletes a packet's (on-heap) +// encrypted_buffer before deleting the (also on-heap) packet itself. +// TODO(wub): Maybe delete retransmittable_frames too? +typedef std::unique_ptr<SerializedPacket, SerializedPacketDeleter> + OwningSerializedPacketPointer; + +// Context for an incoming packet. +struct QUIC_EXPORT_PRIVATE QuicPerPacketContext { + virtual ~QuicPerPacketContext() {} +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PACKETS_H_
diff --git a/quic/core/quic_pending_retransmission.h b/quic/core/quic_pending_retransmission.h new file mode 100644 index 0000000..d1e9657 --- /dev/null +++ b/quic/core/quic_pending_retransmission.h
@@ -0,0 +1,54 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_QUIC_PENDING_RETRANSMISSION_H_ +#define QUICHE_QUIC_CORE_QUIC_PENDING_RETRANSMISSION_H_ + +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Struct to store the pending retransmission information. +struct QUIC_EXPORT_PRIVATE QuicPendingRetransmission { + QuicPendingRetransmission(QuicPacketNumber packet_number, + TransmissionType transmission_type, + const QuicFrames& retransmittable_frames, + bool has_crypto_handshake, + int num_padding_bytes, + EncryptionLevel encryption_level, + QuicPacketNumberLength packet_number_length) + : packet_number(packet_number), + retransmittable_frames(retransmittable_frames), + transmission_type(transmission_type), + has_crypto_handshake(has_crypto_handshake), + num_padding_bytes(num_padding_bytes), + encryption_level(encryption_level), + packet_number_length(packet_number_length) {} + + QuicPendingRetransmission(QuicPacketNumber packet_number, + TransmissionType transmission_type, + const QuicTransmissionInfo& tranmission_info) + : packet_number(packet_number), + retransmittable_frames(tranmission_info.retransmittable_frames), + transmission_type(transmission_type), + has_crypto_handshake(tranmission_info.has_crypto_handshake), + num_padding_bytes(tranmission_info.num_padding_bytes), + encryption_level(tranmission_info.encryption_level), + packet_number_length(tranmission_info.packet_number_length) {} + + QuicPacketNumber packet_number; + const QuicFrames& retransmittable_frames; + TransmissionType transmission_type; + bool has_crypto_handshake; + int num_padding_bytes; + EncryptionLevel encryption_level; + QuicPacketNumberLength packet_number_length; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PENDING_RETRANSMISSION_H_
diff --git a/quic/core/quic_process_packet_interface.h b/quic/core/quic_process_packet_interface.h new file mode 100644 index 0000000..cefce2d --- /dev/null +++ b/quic/core/quic_process_packet_interface.h
@@ -0,0 +1,25 @@ +// Copyright 2015 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_QUIC_PROCESS_PACKET_INTERFACE_H_ +#define QUICHE_QUIC_CORE_QUIC_PROCESS_PACKET_INTERFACE_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +// A class to process each incoming packet. +class ProcessPacketInterface { + public: + virtual ~ProcessPacketInterface() {} + virtual void ProcessPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_PROCESS_PACKET_INTERFACE_H_
diff --git a/quic/core/quic_received_packet_manager.cc b/quic/core/quic_received_packet_manager.cc new file mode 100644 index 0000000..921c674 --- /dev/null +++ b/quic/core/quic_received_packet_manager.cc
@@ -0,0 +1,356 @@ +// Copyright 2013 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/quic_received_packet_manager.h" + +#include <algorithm> +#include <limits> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +namespace { + +// The maximum number of packets to ack immediately after a missing packet for +// fast retransmission to kick in at the sender. This limit is created to +// reduce the number of acks sent that have no benefit for fast retransmission. +// Set to the number of nacks needed for fast retransmit plus one for protection +// against an ack loss +const size_t kMaxPacketsAfterNewMissing = 4; + +// Maximum number of retransmittable packets received before sending an ack. +const QuicPacketCount kDefaultRetransmittablePacketsBeforeAck = 2; +// Minimum number of packets received before ack decimation is enabled. +// This intends to avoid the beginning of slow start, when CWNDs may be +// rapidly increasing. +const QuicPacketCount kMinReceivedBeforeAckDecimation = 100; +// Wait for up to 10 retransmittable packets before sending an ack. +const QuicPacketCount kMaxRetransmittablePacketsBeforeAck = 10; +// One quarter RTT delay when doing ack decimation. +const float kAckDecimationDelay = 0.25; +// One eighth RTT delay when doing ack decimation. +const float kShortAckDecimationDelay = 0.125; +} // namespace + +QuicReceivedPacketManager::QuicReceivedPacketManager(QuicConnectionStats* stats) + : ack_frame_updated_(false), + max_ack_ranges_(0), + time_largest_observed_(QuicTime::Zero()), + save_timestamps_(false), + stats_(stats), + ack_mode_(GetQuicReloadableFlag(quic_enable_ack_decimation) + ? ACK_DECIMATION + : TCP_ACKING), + num_retransmittable_packets_received_since_last_ack_sent_(0), + min_received_before_ack_decimation_(kMinReceivedBeforeAckDecimation), + ack_frequency_before_ack_decimation_( + kDefaultRetransmittablePacketsBeforeAck), + ack_decimation_delay_(kAckDecimationDelay), + unlimited_ack_decimation_(false), + fast_ack_after_quiescence_(false), + ack_timeout_(QuicTime::Zero()), + time_of_previous_received_packet_(QuicTime::Zero()), + was_last_packet_missing_(false), + decide_when_to_send_acks_( + GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) && + GetQuicReloadableFlag(quic_rpm_decides_when_to_send_acks)) {} + +QuicReceivedPacketManager::~QuicReceivedPacketManager() {} + +void QuicReceivedPacketManager::SetFromConfig(const QuicConfig& config, + Perspective perspective) { + DCHECK(decide_when_to_send_acks_); + if (GetQuicReloadableFlag(quic_enable_ack_decimation) && + config.HasClientSentConnectionOption(kACD0, perspective)) { + ack_mode_ = TCP_ACKING; + } + if (config.HasClientSentConnectionOption(kACKD, perspective)) { + ack_mode_ = ACK_DECIMATION; + } + if (config.HasClientSentConnectionOption(kAKD2, perspective)) { + ack_mode_ = ACK_DECIMATION_WITH_REORDERING; + } + if (config.HasClientSentConnectionOption(kAKD3, perspective)) { + ack_mode_ = ACK_DECIMATION; + ack_decimation_delay_ = kShortAckDecimationDelay; + } + if (config.HasClientSentConnectionOption(kAKD4, perspective)) { + ack_mode_ = ACK_DECIMATION_WITH_REORDERING; + ack_decimation_delay_ = kShortAckDecimationDelay; + } + if (config.HasClientSentConnectionOption(kAKDU, perspective)) { + unlimited_ack_decimation_ = true; + } + if (config.HasClientSentConnectionOption(kACKQ, perspective)) { + fast_ack_after_quiescence_ = true; + } +} + +void QuicReceivedPacketManager::RecordPacketReceived( + const QuicPacketHeader& header, + QuicTime receipt_time) { + const QuicPacketNumber packet_number = header.packet_number; + DCHECK(IsAwaitingPacket(packet_number)) << " packet_number:" << packet_number; + if (decide_when_to_send_acks_) { + was_last_packet_missing_ = IsMissing(packet_number); + } + if (!ack_frame_updated_) { + ack_frame_.received_packet_times.clear(); + } + ack_frame_updated_ = true; + + if (LargestAcked(ack_frame_).IsInitialized() && + LargestAcked(ack_frame_) > packet_number) { + // Record how out of order stats. + ++stats_->packets_reordered; + stats_->max_sequence_reordering = + std::max(stats_->max_sequence_reordering, + LargestAcked(ack_frame_) - packet_number); + int64_t reordering_time_us = + (receipt_time - time_largest_observed_).ToMicroseconds(); + stats_->max_time_reordering_us = + std::max(stats_->max_time_reordering_us, reordering_time_us); + } + if (!LargestAcked(ack_frame_).IsInitialized() || + packet_number > LargestAcked(ack_frame_)) { + ack_frame_.largest_acked = packet_number; + time_largest_observed_ = receipt_time; + } + ack_frame_.packets.Add(packet_number); + + if (save_timestamps_) { + // The timestamp format only handles packets in time order. + if (!ack_frame_.received_packet_times.empty() && + ack_frame_.received_packet_times.back().second > receipt_time) { + LOG(WARNING) + << "Receive time went backwards from: " + << ack_frame_.received_packet_times.back().second.ToDebuggingValue() + << " to " << receipt_time.ToDebuggingValue(); + } else { + ack_frame_.received_packet_times.push_back( + std::make_pair(packet_number, receipt_time)); + } + } + + if (least_received_packet_number_.IsInitialized()) { + least_received_packet_number_ = + std::min(least_received_packet_number_, packet_number); + } else { + least_received_packet_number_ = packet_number; + } +} + +bool QuicReceivedPacketManager::IsMissing(QuicPacketNumber packet_number) { + return LargestAcked(ack_frame_).IsInitialized() && + packet_number < LargestAcked(ack_frame_) && + !ack_frame_.packets.Contains(packet_number); +} + +bool QuicReceivedPacketManager::IsAwaitingPacket( + QuicPacketNumber packet_number) { + return quic::IsAwaitingPacket(ack_frame_, packet_number, + peer_least_packet_awaiting_ack_); +} + +const QuicFrame QuicReceivedPacketManager::GetUpdatedAckFrame( + QuicTime approximate_now) { + if (!decide_when_to_send_acks_) { + ack_frame_updated_ = false; + } + if (time_largest_observed_ == QuicTime::Zero()) { + // We have received no packets. + ack_frame_.ack_delay_time = QuicTime::Delta::Infinite(); + } else { + // Ensure the delta is zero if approximate now is "in the past". + ack_frame_.ack_delay_time = approximate_now < time_largest_observed_ + ? QuicTime::Delta::Zero() + : approximate_now - time_largest_observed_; + } + while (max_ack_ranges_ > 0 && + ack_frame_.packets.NumIntervals() > max_ack_ranges_) { + ack_frame_.packets.RemoveSmallestInterval(); + } + // Clear all packet times if any are too far from largest observed. + // It's expected this is extremely rare. + for (auto it = ack_frame_.received_packet_times.begin(); + it != ack_frame_.received_packet_times.end();) { + if (LargestAcked(ack_frame_) - it->first >= + std::numeric_limits<uint8_t>::max()) { + it = ack_frame_.received_packet_times.erase(it); + } else { + ++it; + } + } + + return QuicFrame(&ack_frame_); +} + +void QuicReceivedPacketManager::DontWaitForPacketsBefore( + QuicPacketNumber least_unacked) { + if (!least_unacked.IsInitialized()) { + return; + } + // ValidateAck() should fail if peer_least_packet_awaiting_ack shrinks. + DCHECK(!peer_least_packet_awaiting_ack_.IsInitialized() || + peer_least_packet_awaiting_ack_ <= least_unacked); + if (!peer_least_packet_awaiting_ack_.IsInitialized() || + least_unacked > peer_least_packet_awaiting_ack_) { + peer_least_packet_awaiting_ack_ = least_unacked; + bool packets_updated = ack_frame_.packets.RemoveUpTo(least_unacked); + if (packets_updated) { + // Ack frame gets updated because packets set is updated because of stop + // waiting frame. + ack_frame_updated_ = true; + } + } + DCHECK(ack_frame_.packets.Empty() || + !peer_least_packet_awaiting_ack_.IsInitialized() || + ack_frame_.packets.Min() >= peer_least_packet_awaiting_ack_); +} + +void QuicReceivedPacketManager::MaybeUpdateAckTimeout( + bool should_last_packet_instigate_acks, + QuicPacketNumber last_received_packet_number, + QuicTime time_of_last_received_packet, + QuicTime now, + const RttStats* rtt_stats, + QuicTime::Delta delayed_ack_time) { + DCHECK(decide_when_to_send_acks_); + if (!ack_frame_updated_) { + // ACK frame has not been updated, nothing to do. + return; + } + + if (was_last_packet_missing_ && last_sent_largest_acked_.IsInitialized() && + last_received_packet_number < last_sent_largest_acked_) { + // Only ack immediately if an ACK frame was sent with a larger largest acked + // than the newly received packet number. + ack_timeout_ = now; + return; + } + + if (!should_last_packet_instigate_acks) { + return; + } + + ++num_retransmittable_packets_received_since_last_ack_sent_; + if (ack_mode_ != TCP_ACKING && + last_received_packet_number >= PeerFirstSendingPacketNumber() + + min_received_before_ack_decimation_) { + // Ack up to 10 packets at once unless ack decimation is unlimited. + if (!unlimited_ack_decimation_ && + num_retransmittable_packets_received_since_last_ack_sent_ >= + kMaxRetransmittablePacketsBeforeAck) { + ack_timeout_ = now; + return; + } + // Wait for the minimum of the ack decimation delay or the delayed ack time + // before sending an ack. + QuicTime::Delta ack_delay = std::min( + delayed_ack_time, rtt_stats->min_rtt() * ack_decimation_delay_); + if (fast_ack_after_quiescence_ && now - time_of_previous_received_packet_ > + rtt_stats->SmoothedOrInitialRtt()) { + // Ack the first packet out of queiscence faster, because QUIC does + // not pace the first few packets and commonly these may be handshake + // or TLP packets, which we'd like to acknowledge quickly. + ack_delay = QuicTime::Delta::FromMilliseconds(1); + } + MaybeUpdateAckTimeoutTo(now + ack_delay); + } else { + // Ack with a timer or every 2 packets by default. + if (num_retransmittable_packets_received_since_last_ack_sent_ >= + ack_frequency_before_ack_decimation_) { + ack_timeout_ = now; + } else if (fast_ack_after_quiescence_ && + (now - time_of_previous_received_packet_) > + rtt_stats->SmoothedOrInitialRtt()) { + // Ack the first packet out of queiscence faster, because QUIC does + // not pace the first few packets and commonly these may be handshake + // or TLP packets, which we'd like to acknowledge quickly. + MaybeUpdateAckTimeoutTo(now + QuicTime::Delta::FromMilliseconds(1)); + } else { + MaybeUpdateAckTimeoutTo(now + delayed_ack_time); + } + } + + // If there are new missing packets to report, send an ack immediately. + if (HasNewMissingPackets()) { + if (ack_mode_ == ACK_DECIMATION_WITH_REORDERING) { + // Wait the minimum of an eighth min_rtt and the existing ack time. + QuicTime ack_time = now + kShortAckDecimationDelay * rtt_stats->min_rtt(); + MaybeUpdateAckTimeoutTo(ack_time); + } else { + ack_timeout_ = now; + } + } + + if (fast_ack_after_quiescence_) { + time_of_previous_received_packet_ = time_of_last_received_packet; + } +} + +void QuicReceivedPacketManager::ResetAckStates() { + DCHECK(decide_when_to_send_acks_); + ack_frame_updated_ = false; + ack_timeout_ = QuicTime::Zero(); + num_retransmittable_packets_received_since_last_ack_sent_ = 0; + last_sent_largest_acked_ = LargestAcked(ack_frame_); +} + +void QuicReceivedPacketManager::MaybeUpdateAckTimeoutTo(QuicTime time) { + DCHECK(decide_when_to_send_acks_); + if (!ack_timeout_.IsInitialized() || ack_timeout_ > time) { + ack_timeout_ = time; + } +} + +bool QuicReceivedPacketManager::HasMissingPackets() const { + if (ack_frame_.packets.Empty()) { + return false; + } + if (ack_frame_.packets.NumIntervals() > 1) { + return true; + } + if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + return ack_frame_.packets.Min() > + (peer_least_packet_awaiting_ack_.IsInitialized() + ? peer_least_packet_awaiting_ack_ + : QuicPacketNumber(1)); + } + return peer_least_packet_awaiting_ack_.IsInitialized() && + ack_frame_.packets.Min() > peer_least_packet_awaiting_ack_; +} + +bool QuicReceivedPacketManager::HasNewMissingPackets() const { + return HasMissingPackets() && + ack_frame_.packets.LastIntervalLength() <= kMaxPacketsAfterNewMissing; +} + +bool QuicReceivedPacketManager::ack_frame_updated() const { + return ack_frame_updated_; +} + +QuicPacketNumber QuicReceivedPacketManager::GetLargestObserved() const { + return LargestAcked(ack_frame_); +} + +QuicPacketNumber QuicReceivedPacketManager::PeerFirstSendingPacketNumber() + const { + if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + return QuicPacketNumber(1); + } + if (!least_received_packet_number_.IsInitialized()) { + QUIC_BUG << "No packets have been received yet"; + return QuicPacketNumber(1); + } + return least_received_packet_number_; +} + +} // namespace quic
diff --git a/quic/core/quic_received_packet_manager.h b/quic/core/quic_received_packet_manager.h new file mode 100644 index 0000000..719aa44 --- /dev/null +++ b/quic/core/quic_received_packet_manager.h
@@ -0,0 +1,193 @@ +// Copyright 2013 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_QUIC_RECEIVED_PACKET_MANAGER_H_ +#define QUICHE_QUIC_CORE_QUIC_RECEIVED_PACKET_MANAGER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_config.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/platform/api/quic_export.h" + +namespace quic { + +class RttStats; + +namespace test { +class QuicConnectionPeer; +class QuicReceivedPacketManagerPeer; +} // namespace test + +struct QuicConnectionStats; + +// Records all received packets by a connection. +class QUIC_EXPORT_PRIVATE QuicReceivedPacketManager { + public: + explicit QuicReceivedPacketManager(QuicConnectionStats* stats); + QuicReceivedPacketManager(const QuicReceivedPacketManager&) = delete; + QuicReceivedPacketManager& operator=(const QuicReceivedPacketManager&) = + delete; + virtual ~QuicReceivedPacketManager(); + + void SetFromConfig(const QuicConfig& config, Perspective perspective); + + // Updates the internal state concerning which packets have been received. + // header: the packet header. + // timestamp: the arrival time of the packet. + virtual void RecordPacketReceived(const QuicPacketHeader& header, + QuicTime receipt_time); + + // Checks whether |packet_number| is missing and less than largest observed. + virtual bool IsMissing(QuicPacketNumber packet_number); + + // Checks if we're still waiting for the packet with |packet_number|. + virtual bool IsAwaitingPacket(QuicPacketNumber packet_number); + + // Retrieves a frame containing a QuicAckFrame. The ack frame may not be + // changed outside QuicReceivedPacketManager and must be serialized before + // another packet is received, or it will change. + const QuicFrame GetUpdatedAckFrame(QuicTime approximate_now); + + // Deletes all missing packets before least unacked. The connection won't + // process any packets with packet number before |least_unacked| that it + // received after this call. + void DontWaitForPacketsBefore(QuicPacketNumber least_unacked); + + // Called to update ack_timeout_ to the time when an ACK needs to be sent. A + // caller can decide whether and when to send an ACK by retrieving + // ack_timeout_. If ack_timeout_ is not initialized, no ACK needs to be sent. + // Otherwise, ACK needs to be sent by the specified time. + void MaybeUpdateAckTimeout(bool should_last_packet_instigate_acks, + QuicPacketNumber last_received_packet_number, + QuicTime time_of_last_received_packet, + QuicTime now, + const RttStats* rtt_stats, + QuicTime::Delta delayed_ack_time); + + // Resets ACK related states, called after an ACK is successfully sent. + void ResetAckStates(); + + // Returns true if there are any missing packets. + bool HasMissingPackets() const; + + // Returns true when there are new missing packets to be reported within 3 + // packets of the largest observed. + virtual bool HasNewMissingPackets() const; + + QuicPacketNumber peer_least_packet_awaiting_ack() { + return peer_least_packet_awaiting_ack_; + } + + virtual bool ack_frame_updated() const; + + QuicPacketNumber GetLargestObserved() const; + + // Returns peer first sending packet number to our best knowledge. If + // GetQuicRestartFlag(quic_enable_accept_random_ipn) is false, returns 1. + // Otherwise considers least_received_packet_number_ as peer first sending + // packet number. Please note, this function should only be called when at + // least one packet has been received. + QuicPacketNumber PeerFirstSendingPacketNumber() const; + + // For logging purposes. + const QuicAckFrame& ack_frame() const { return ack_frame_; } + + void set_max_ack_ranges(size_t max_ack_ranges) { + max_ack_ranges_ = max_ack_ranges; + } + + void set_save_timestamps(bool save_timestamps) { + save_timestamps_ = save_timestamps; + } + + size_t min_received_before_ack_decimation() const { + return min_received_before_ack_decimation_; + } + void set_min_received_before_ack_decimation(size_t new_value) { + min_received_before_ack_decimation_ = new_value; + } + + size_t ack_frequency_before_ack_decimation() const { + return ack_frequency_before_ack_decimation_; + } + void set_ack_frequency_before_ack_decimation(size_t new_value) { + DCHECK_GT(new_value, 0u); + ack_frequency_before_ack_decimation_ = new_value; + } + + QuicTime ack_timeout() const { return ack_timeout_; } + + bool decide_when_to_send_acks() const { return decide_when_to_send_acks_; } + + private: + friend class test::QuicConnectionPeer; + friend class test::QuicReceivedPacketManagerPeer; + + // Sets ack_timeout_ to |time| if ack_timeout_ is not initialized or > time. + void MaybeUpdateAckTimeoutTo(QuicTime time); + + // Least packet number of the the packet sent by the peer for which it + // hasn't received an ack. + QuicPacketNumber peer_least_packet_awaiting_ack_; + + // Received packet information used to produce acks. + QuicAckFrame ack_frame_; + + // True if |ack_frame_| has been updated since UpdateReceivedPacketInfo was + // last called. + bool ack_frame_updated_; + + // Maximum number of ack ranges allowed to be stored in the ack frame. + size_t max_ack_ranges_; + + // The time we received the largest_observed packet number, or zero if + // no packet numbers have been received since UpdateReceivedPacketInfo. + // Needed for calculating ack_delay_time. + QuicTime time_largest_observed_; + + // If true, save timestamps in the ack_frame_. + bool save_timestamps_; + + // Least packet number received from peer. + QuicPacketNumber least_received_packet_number_; + + QuicConnectionStats* stats_; + + AckMode ack_mode_; + // How many retransmittable packets have arrived without sending an ack. + QuicPacketCount num_retransmittable_packets_received_since_last_ack_sent_; + // Ack decimation will start happening after this many packets are received. + size_t min_received_before_ack_decimation_; + // Before ack decimation starts (if enabled), we ack every n-th packet. + size_t ack_frequency_before_ack_decimation_; + // The max delay in fraction of min_rtt to use when sending decimated acks. + float ack_decimation_delay_; + // When true, removes ack decimation's max number of packets(10) before + // sending an ack. + bool unlimited_ack_decimation_; + // When true, use a 1ms delayed ack timer if it's been an SRTT since a packet + // was received. + bool fast_ack_after_quiescence_; + + // Time that an ACK needs to be sent. 0 means no ACK is pending. Used when + // decide_when_to_send_acks_ is true. + QuicTime ack_timeout_; + + // The time the previous ack-instigating packet was received and processed. + QuicTime time_of_previous_received_packet_; + // Whether the most recent packet was missing before it was received. + bool was_last_packet_missing_; + + // Last sent largest acked, which gets updated when ACK was successfully sent. + QuicPacketNumber last_sent_largest_acked_; + + // Latched value of quic_deprecate_ack_bundling_mode and + // quic_rpm_decides_when_to_send_acks. + const bool decide_when_to_send_acks_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_RECEIVED_PACKET_MANAGER_H_
diff --git a/quic/core/quic_received_packet_manager_test.cc b/quic/core/quic_received_packet_manager_test.cc new file mode 100644 index 0000000..0512cb7 --- /dev/null +++ b/quic/core/quic_received_packet_manager_test.cc
@@ -0,0 +1,807 @@ +// Copyright 2013 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/quic_received_packet_manager.h" + +#include <algorithm> +#include <ostream> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" + +namespace quic { +namespace test { + +class QuicReceivedPacketManagerPeer { + public: + static void SetAckMode(QuicReceivedPacketManager* manager, AckMode ack_mode) { + manager->ack_mode_ = ack_mode; + } + + static void SetFastAckAfterQuiescence(QuicReceivedPacketManager* manager, + bool fast_ack_after_quiescence) { + manager->fast_ack_after_quiescence_ = fast_ack_after_quiescence; + } + + static void SetAckDecimationDelay(QuicReceivedPacketManager* manager, + float ack_decimation_delay) { + manager->ack_decimation_delay_ = ack_decimation_delay; + } +}; + +namespace { + +const bool kInstigateAck = true; +const QuicTime::Delta kMinRttMs = QuicTime::Delta::FromMilliseconds(40); +const QuicTime::Delta kDelayedAckTime = + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + +struct TestParams { + explicit TestParams(QuicTransportVersion version) : version(version) {} + + friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { + os << "{ version: " << QuicVersionToString(p.version) << " }"; + return os; + } + + QuicTransportVersion version; +}; + +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + QuicTransportVersionVector all_supported_versions = + AllSupportedTransportVersions(); + for (size_t i = 0; i < all_supported_versions.size(); ++i) { + params.push_back(TestParams(all_supported_versions[i])); + } + return params; +} + +class QuicReceivedPacketManagerTest : public QuicTestWithParam<TestParams> { + protected: + QuicReceivedPacketManagerTest() : received_manager_(&stats_) { + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + rtt_stats_.UpdateRtt(kMinRttMs, QuicTime::Delta::Zero(), QuicTime::Zero()); + received_manager_.set_save_timestamps(true); + } + + void RecordPacketReceipt(uint64_t packet_number) { + RecordPacketReceipt(packet_number, QuicTime::Zero()); + } + + void RecordPacketReceipt(uint64_t packet_number, QuicTime receipt_time) { + QuicPacketHeader header; + header.packet_number = QuicPacketNumber(packet_number); + received_manager_.RecordPacketReceived(header, receipt_time); + } + + bool HasPendingAck() { + DCHECK(received_manager_.decide_when_to_send_acks()); + return received_manager_.ack_timeout().IsInitialized(); + } + + void MaybeUpdateAckTimeout(bool should_last_packet_instigate_acks, + uint64_t last_received_packet_number) { + DCHECK(received_manager_.decide_when_to_send_acks()); + received_manager_.MaybeUpdateAckTimeout( + should_last_packet_instigate_acks, + QuicPacketNumber(last_received_packet_number), clock_.ApproximateNow(), + clock_.ApproximateNow(), &rtt_stats_, kDelayedAckTime); + } + + void CheckAckTimeout(QuicTime time) { + DCHECK(HasPendingAck() && received_manager_.ack_timeout() == time); + if (time <= clock_.ApproximateNow()) { + // ACK timeout expires, send an ACK. + received_manager_.ResetAckStates(); + DCHECK(!HasPendingAck()); + } + } + + MockClock clock_; + RttStats rtt_stats_; + QuicConnectionStats stats_; + QuicReceivedPacketManager received_manager_; +}; + +INSTANTIATE_TEST_SUITE_P(QuicReceivedPacketManagerTest, + QuicReceivedPacketManagerTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) { + QuicPacketHeader header; + header.packet_number = QuicPacketNumber(2u); + received_manager_.RecordPacketReceived(header, QuicTime::Zero()); + header.packet_number = QuicPacketNumber(7u); + received_manager_.RecordPacketReceived(header, QuicTime::Zero()); + EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(3u))); + EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(6u))); + received_manager_.DontWaitForPacketsBefore(QuicPacketNumber(4)); + EXPECT_FALSE(received_manager_.IsAwaitingPacket(QuicPacketNumber(3u))); + EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(6u))); +} + +TEST_P(QuicReceivedPacketManagerTest, GetUpdatedAckFrame) { + QuicPacketHeader header; + header.packet_number = QuicPacketNumber(2u); + QuicTime two_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2); + EXPECT_FALSE(received_manager_.ack_frame_updated()); + received_manager_.RecordPacketReceived(header, two_ms); + EXPECT_TRUE(received_manager_.ack_frame_updated()); + + QuicFrame ack = received_manager_.GetUpdatedAckFrame(QuicTime::Zero()); + if (received_manager_.decide_when_to_send_acks()) { + received_manager_.ResetAckStates(); + } + EXPECT_FALSE(received_manager_.ack_frame_updated()); + // When UpdateReceivedPacketInfo with a time earlier than the time of the + // largest observed packet, make sure that the delta is 0, not negative. + EXPECT_EQ(QuicTime::Delta::Zero(), ack.ack_frame->ack_delay_time); + EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size()); + + QuicTime four_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(4); + ack = received_manager_.GetUpdatedAckFrame(four_ms); + if (received_manager_.decide_when_to_send_acks()) { + received_manager_.ResetAckStates(); + } + EXPECT_FALSE(received_manager_.ack_frame_updated()); + // When UpdateReceivedPacketInfo after not having received a new packet, + // the delta should still be accurate. + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2), + ack.ack_frame->ack_delay_time); + // And received packet times won't have change. + EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size()); + + header.packet_number = QuicPacketNumber(999u); + received_manager_.RecordPacketReceived(header, two_ms); + header.packet_number = QuicPacketNumber(4u); + received_manager_.RecordPacketReceived(header, two_ms); + header.packet_number = QuicPacketNumber(1000u); + received_manager_.RecordPacketReceived(header, two_ms); + EXPECT_TRUE(received_manager_.ack_frame_updated()); + ack = received_manager_.GetUpdatedAckFrame(two_ms); + if (received_manager_.decide_when_to_send_acks()) { + received_manager_.ResetAckStates(); + } + EXPECT_FALSE(received_manager_.ack_frame_updated()); + // UpdateReceivedPacketInfo should discard any times which can't be + // expressed on the wire. + EXPECT_EQ(2u, ack.ack_frame->received_packet_times.size()); +} + +TEST_P(QuicReceivedPacketManagerTest, UpdateReceivedConnectionStats) { + EXPECT_FALSE(received_manager_.ack_frame_updated()); + RecordPacketReceipt(1); + EXPECT_TRUE(received_manager_.ack_frame_updated()); + RecordPacketReceipt(6); + RecordPacketReceipt(2, + QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1)); + + EXPECT_EQ(4u, stats_.max_sequence_reordering); + EXPECT_EQ(1000, stats_.max_time_reordering_us); + EXPECT_EQ(1u, stats_.packets_reordered); +} + +TEST_P(QuicReceivedPacketManagerTest, LimitAckRanges) { + received_manager_.set_max_ack_ranges(10); + EXPECT_FALSE(received_manager_.ack_frame_updated()); + for (int i = 0; i < 100; ++i) { + RecordPacketReceipt(1 + 2 * i); + EXPECT_TRUE(received_manager_.ack_frame_updated()); + received_manager_.GetUpdatedAckFrame(QuicTime::Zero()); + EXPECT_GE(10u, received_manager_.ack_frame().packets.NumIntervals()); + EXPECT_EQ(QuicPacketNumber(1u + 2 * i), + received_manager_.ack_frame().packets.Max()); + for (int j = 0; j < std::min(10, i + 1); ++j) { + ASSERT_GE(i, j); + EXPECT_TRUE(received_manager_.ack_frame().packets.Contains( + QuicPacketNumber(1 + (i - j) * 2))); + if (i > j) { + EXPECT_FALSE(received_manager_.ack_frame().packets.Contains( + QuicPacketNumber((i - j) * 2))); + } + } + } +} + +TEST_P(QuicReceivedPacketManagerTest, IgnoreOutOfOrderTimestamps) { + EXPECT_FALSE(received_manager_.ack_frame_updated()); + RecordPacketReceipt(1, QuicTime::Zero()); + EXPECT_TRUE(received_manager_.ack_frame_updated()); + EXPECT_EQ(1u, received_manager_.ack_frame().received_packet_times.size()); + RecordPacketReceipt(2, + QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1)); + EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size()); + RecordPacketReceipt(3, QuicTime::Zero()); + EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size()); +} + +TEST_P(QuicReceivedPacketManagerTest, HasMissingPackets) { + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_QUIC_BUG(received_manager_.PeerFirstSendingPacketNumber(), + "No packets have been received yet"); + } else { + EXPECT_EQ(QuicPacketNumber(1), + received_manager_.PeerFirstSendingPacketNumber()); + } + RecordPacketReceipt(4, QuicTime::Zero()); + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_EQ(QuicPacketNumber(4), + received_manager_.PeerFirstSendingPacketNumber()); + EXPECT_FALSE(received_manager_.HasMissingPackets()); + } else { + EXPECT_TRUE(received_manager_.HasMissingPackets()); + EXPECT_EQ(QuicPacketNumber(1), + received_manager_.PeerFirstSendingPacketNumber()); + } + RecordPacketReceipt(3, QuicTime::Zero()); + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + EXPECT_FALSE(received_manager_.HasMissingPackets()); + EXPECT_EQ(QuicPacketNumber(3), + received_manager_.PeerFirstSendingPacketNumber()); + } else { + EXPECT_TRUE(received_manager_.HasMissingPackets()); + EXPECT_EQ(QuicPacketNumber(1), + received_manager_.PeerFirstSendingPacketNumber()); + } + RecordPacketReceipt(1, QuicTime::Zero()); + EXPECT_EQ(QuicPacketNumber(1), + received_manager_.PeerFirstSendingPacketNumber()); + EXPECT_TRUE(received_manager_.HasMissingPackets()); + RecordPacketReceipt(2, QuicTime::Zero()); + EXPECT_EQ(QuicPacketNumber(1), + received_manager_.PeerFirstSendingPacketNumber()); + EXPECT_FALSE(received_manager_.HasMissingPackets()); +} + +TEST_P(QuicReceivedPacketManagerTest, OutOfOrderReceiptCausesAckSent) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + + RecordPacketReceipt(3, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 3); + if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) { + // Delayed ack is scheduled. + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } else { + // Should ack immediately since we have missing packets. + CheckAckTimeout(clock_.ApproximateNow()); + } + + RecordPacketReceipt(2, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 2); + CheckAckTimeout(clock_.ApproximateNow()); + + RecordPacketReceipt(1, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 1); + // Should ack immediately, since this fills the last hole. + CheckAckTimeout(clock_.ApproximateNow()); + + RecordPacketReceipt(4, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 4); + // Delayed ack is scheduled. + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); +} + +TEST_P(QuicReceivedPacketManagerTest, OutOfOrderAckReceiptCausesNoAck) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + + RecordPacketReceipt(2, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(!kInstigateAck, 2); + EXPECT_FALSE(HasPendingAck()); + + RecordPacketReceipt(1, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(!kInstigateAck, 1); + EXPECT_FALSE(HasPendingAck()); +} + +TEST_P(QuicReceivedPacketManagerTest, AckReceiptCausesAckSend) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + + RecordPacketReceipt(1, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(!kInstigateAck, 1); + EXPECT_FALSE(HasPendingAck()); + + RecordPacketReceipt(2, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(!kInstigateAck, 2); + EXPECT_FALSE(HasPendingAck()); + + RecordPacketReceipt(3, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 3); + // Delayed ack is scheduled. + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + clock_.AdvanceTime(kDelayedAckTime); + CheckAckTimeout(clock_.ApproximateNow()); + + RecordPacketReceipt(4, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(!kInstigateAck, 4); + EXPECT_FALSE(HasPendingAck()); + + RecordPacketReceipt(5, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(!kInstigateAck, 5); + EXPECT_FALSE(HasPendingAck()); +} + +TEST_P(QuicReceivedPacketManagerTest, AckSentEveryNthPacket) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + received_manager_.set_ack_frequency_before_ack_decimation(3); + + // Receives packets 1 - 39. + for (size_t i = 1; i <= 39; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 3 == 0) { + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } +} + +TEST_P(QuicReceivedPacketManagerTest, AckDecimationReducesAcks) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, + ACK_DECIMATION_WITH_REORDERING); + + // Start ack decimation from 10th packet. + received_manager_.set_min_received_before_ack_decimation(10); + + // Receives packets 1 - 29. + for (size_t i = 1; i <= 29; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i <= 10) { + // For packets 1-10, ack every 2 packets. + if (i % 2 == 0) { + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + continue; + } + // ack at 20. + if (i == 20) { + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kMinRttMs * 0.25); + } + } + + // We now receive the 30th packet, and so we send an ack. + RecordPacketReceipt(30, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 30); + CheckAckTimeout(clock_.ApproximateNow()); +} + +TEST_P(QuicReceivedPacketManagerTest, SendDelayedAfterQuiescence) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetFastAckAfterQuiescence(&received_manager_, + true); + // The beginning of the connection counts as quiescence. + QuicTime ack_time = + clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + + RecordPacketReceipt(1, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 1); + CheckAckTimeout(ack_time); + // Simulate delayed ack alarm firing. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + CheckAckTimeout(clock_.ApproximateNow()); + + // Process another packet immediately after sending the ack and expect the + // ack timeout to be set delayed ack time in the future. + ack_time = clock_.ApproximateNow() + kDelayedAckTime; + RecordPacketReceipt(2, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 2); + CheckAckTimeout(ack_time); + // Simulate delayed ack alarm firing. + clock_.AdvanceTime(kDelayedAckTime); + CheckAckTimeout(clock_.ApproximateNow()); + + // Wait 1 second and enesure the ack timeout is set to 1ms in the future. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + RecordPacketReceipt(3, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 3); + CheckAckTimeout(ack_time); +} + +TEST_P(QuicReceivedPacketManagerTest, SendDelayedAckDecimation) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, ACK_DECIMATION); + // The ack time should be based on min_rtt * 1/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25; + + // Process all the packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + + RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket); + CheckAckTimeout(ack_time); + + // The 10th received packet causes an ack to be sent. + for (uint64_t i = 1; i < 10; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i); + } + CheckAckTimeout(clock_.ApproximateNow()); +} + +TEST_P(QuicReceivedPacketManagerTest, + SendDelayedAckAckDecimationAfterQuiescence) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, ACK_DECIMATION); + QuicReceivedPacketManagerPeer::SetFastAckAfterQuiescence(&received_manager_, + true); + // The beginning of the connection counts as quiescence. + QuicTime ack_time = + clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + RecordPacketReceipt(1, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 1); + CheckAckTimeout(ack_time); + // Simulate delayed ack alarm firing. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + CheckAckTimeout(clock_.ApproximateNow()); + + // Process another packet immedately after sending the ack and expect the + // ack timeout to be set delayed ack time in the future. + ack_time = clock_.ApproximateNow() + kDelayedAckTime; + RecordPacketReceipt(2, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 2); + CheckAckTimeout(ack_time); + // Simulate delayed ack alarm firing. + clock_.AdvanceTime(kDelayedAckTime); + CheckAckTimeout(clock_.ApproximateNow()); + + // Wait 1 second and enesure the ack timeout is set to 1ms in the future. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + RecordPacketReceipt(3, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, 3); + CheckAckTimeout(ack_time); + // Process enough packets to get into ack decimation behavior. + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25; + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 4; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + EXPECT_FALSE(HasPendingAck()); + RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket); + CheckAckTimeout(ack_time); + + // The 10th received packet causes an ack to be sent. + for (uint64_t i = 1; i < 10; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i); + } + CheckAckTimeout(clock_.ApproximateNow()); + + // Wait 1 second and enesure the ack timeout is set to 1ms in the future. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); + RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10); + CheckAckTimeout(ack_time); +} + +TEST_P(QuicReceivedPacketManagerTest, + SendDelayedAckDecimationUnlimitedAggregation) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kACKD); + // No limit on the number of packets received before sending an ack. + connection_options.push_back(kAKDU); + config.SetConnectionOptionsToSend(connection_options); + received_manager_.SetFromConfig(config, Perspective::IS_CLIENT); + + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25; + + // Process all the initial packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + + RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket); + CheckAckTimeout(ack_time); + + // 18 packets will not cause an ack to be sent. 19 will because when + // stop waiting frames are in use, we ack every 20 packets no matter what. + for (int i = 1; i <= 18; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i); + } + CheckAckTimeout(ack_time); +} + +TEST_P(QuicReceivedPacketManagerTest, SendDelayedAckDecimationEighthRtt) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, ACK_DECIMATION); + QuicReceivedPacketManagerPeer::SetAckDecimationDelay(&received_manager_, + 0.125); + + // The ack time should be based on min_rtt/8, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125; + + // Process all the packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + + RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket); + CheckAckTimeout(ack_time); + + // The 10th received packet causes an ack to be sent. + for (uint64_t i = 1; i < 10; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i); + } + CheckAckTimeout(clock_.ApproximateNow()); +} + +TEST_P(QuicReceivedPacketManagerTest, SendDelayedAckDecimationWithReordering) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, + ACK_DECIMATION_WITH_REORDERING); + + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25; + // Process all the packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + + // Receive one packet out of order and then the rest in order. + // The loop leaves a one packet gap between acks sent to simulate some loss. + for (int j = 0; j < 3; ++j) { + // Process packet 10 first and ensure the timeout is one eighth min_rtt. + RecordPacketReceipt(kFirstDecimatedPacket + 9 + (j * 11), + clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 9 + (j * 11)); + ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); + CheckAckTimeout(ack_time); + + // The 10th received packet causes an ack to be sent. + for (int i = 0; i < 9; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i + (j * 11), + clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, + kFirstDecimatedPacket + i + (j * 11)); + } + CheckAckTimeout(clock_.ApproximateNow()); + } +} + +TEST_P(QuicReceivedPacketManagerTest, + SendDelayedAckDecimationWithLargeReordering) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, + ACK_DECIMATION_WITH_REORDERING); + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25; + + // Process all the packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + + RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket); + CheckAckTimeout(ack_time); + + RecordPacketReceipt(kFirstDecimatedPacket + 19, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 19); + ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125; + CheckAckTimeout(ack_time); + + // The 10th received packet causes an ack to be sent. + for (int i = 1; i < 9; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i); + } + CheckAckTimeout(clock_.ApproximateNow()); + + // The next packet received in order will cause an immediate ack, because it + // fills a hole. + RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10); + CheckAckTimeout(clock_.ApproximateNow()); +} + +TEST_P(QuicReceivedPacketManagerTest, + SendDelayedAckDecimationWithReorderingEighthRtt) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, + ACK_DECIMATION_WITH_REORDERING); + QuicReceivedPacketManagerPeer::SetAckDecimationDelay(&received_manager_, + 0.125); + // The ack time should be based on min_rtt/8, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125; + + // Process all the packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + + RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket); + CheckAckTimeout(ack_time); + + // Process packet 10 first and ensure the timeout is one eighth min_rtt. + RecordPacketReceipt(kFirstDecimatedPacket + 9, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 9); + CheckAckTimeout(ack_time); + + // The 10th received packet causes an ack to be sent. + for (int i = 1; i < 9; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck + i, kFirstDecimatedPacket); + } + CheckAckTimeout(clock_.ApproximateNow()); +} + +TEST_P(QuicReceivedPacketManagerTest, + SendDelayedAckDecimationWithLargeReorderingEighthRtt) { + if (!received_manager_.decide_when_to_send_acks()) { + return; + } + EXPECT_FALSE(HasPendingAck()); + QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, + ACK_DECIMATION_WITH_REORDERING); + QuicReceivedPacketManagerPeer::SetAckDecimationDelay(&received_manager_, + 0.125); + + // The ack time should be based on min_rtt/8, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125; + // Process all the packets in order so there aren't missing packets. + uint64_t kFirstDecimatedPacket = 101; + for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) { + RecordPacketReceipt(i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, i); + if (i % 2 == 0) { + // Ack every 2 packets by default. + CheckAckTimeout(clock_.ApproximateNow()); + } else { + CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime); + } + } + + RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket); + CheckAckTimeout(ack_time); + + RecordPacketReceipt(kFirstDecimatedPacket + 19, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 19); + CheckAckTimeout(ack_time); + + // The 10th received packet causes an ack to be sent. + for (int i = 1; i < 9; ++i) { + RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i); + } + CheckAckTimeout(clock_.ApproximateNow()); + + // The next packet received in order will cause an immediate ack, because it + // fills a hole. + RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow()); + MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10); + CheckAckTimeout(clock_.ApproximateNow()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc new file mode 100644 index 0000000..6d6710f --- /dev/null +++ b/quic/core/quic_sent_packet_manager.cc
@@ -0,0 +1,1202 @@ +// Copyright 2013 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/quic_sent_packet_manager.h" + +#include <algorithm> + +#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace { +static const int64_t kDefaultRetransmissionTimeMs = 500; +static const int64_t kMaxRetransmissionTimeMs = 60000; +// Maximum number of exponential backoffs used for RTO timeouts. +static const size_t kMaxRetransmissions = 10; +// Maximum number of packets retransmitted upon an RTO. +static const size_t kMaxRetransmissionsOnTimeout = 2; +// The path degrading delay is the sum of this number of consecutive RTO delays. +const size_t kNumRetransmissionDelaysForPathDegradingDelay = 2; + +// Ensure the handshake timer isnt't faster than 10ms. +// This limits the tenth retransmitted packet to 10s after the initial CHLO. +static const int64_t kMinHandshakeTimeoutMs = 10; + +// Sends up to two tail loss probes before firing an RTO, +// per draft RFC draft-dukkipati-tcpm-tcp-loss-probe. +static const size_t kDefaultMaxTailLossProbes = 2; + +inline bool HasCryptoHandshake(const QuicTransmissionInfo& transmission_info) { + DCHECK(!transmission_info.has_crypto_handshake || + !transmission_info.retransmittable_frames.empty()); + return transmission_info.has_crypto_handshake; +} + +// Returns true if retransmissions the specified type leave the data in flight. +inline bool RetransmissionLeavesBytesInFlight( + TransmissionType transmission_type) { + // Both TLP and the new RTO leave the packets in flight and let the loss + // detection decide if packets are lost. + return transmission_type == TLP_RETRANSMISSION || + transmission_type == PROBING_RETRANSMISSION || + transmission_type == RTO_RETRANSMISSION; +} + +// Returns true of retransmissions of the specified type should retransmit +// the frames directly (as opposed to resulting in a loss notification). +inline bool ShouldForceRetransmission(TransmissionType transmission_type) { + return transmission_type == HANDSHAKE_RETRANSMISSION || + transmission_type == TLP_RETRANSMISSION || + transmission_type == PROBING_RETRANSMISSION || + transmission_type == RTO_RETRANSMISSION; +} + +} // namespace + +#define ENDPOINT \ + (unacked_packets_.perspective() == Perspective::IS_SERVER ? "Server: " \ + : "Client: ") + +QuicSentPacketManager::QuicSentPacketManager( + Perspective perspective, + const QuicClock* clock, + QuicConnectionStats* stats, + CongestionControlType congestion_control_type, + LossDetectionType loss_type) + : unacked_packets_(perspective), + clock_(clock), + stats_(stats), + debug_delegate_(nullptr), + network_change_visitor_(nullptr), + initial_congestion_window_(kInitialCongestionWindow), + loss_algorithm_( + unacked_packets_.use_uber_loss_algorithm() + ? dynamic_cast<LossDetectionInterface*>(&uber_loss_algorithm_) + : dynamic_cast<LossDetectionInterface*>( + &general_loss_algorithm_)), + general_loss_algorithm_(loss_type), + uber_loss_algorithm_(loss_type), + consecutive_rto_count_(0), + consecutive_tlp_count_(0), + consecutive_crypto_retransmission_count_(0), + pending_timer_transmission_count_(0), + max_tail_loss_probes_(kDefaultMaxTailLossProbes), + max_rto_packets_(kMaxRetransmissionsOnTimeout), + enable_half_rtt_tail_loss_probe_(false), + using_pacing_(false), + use_new_rto_(false), + conservative_handshake_retransmits_(false), + min_tlp_timeout_( + QuicTime::Delta::FromMilliseconds(kMinTailLossProbeTimeoutMs)), + min_rto_timeout_( + QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs)), + ietf_style_tlp_(false), + ietf_style_2x_tlp_(false), + largest_mtu_acked_(0), + handshake_confirmed_(false), + delayed_ack_time_( + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)), + rtt_updated_(false), + acked_packets_iter_(last_ack_frame_.packets.rbegin()) { + SetSendAlgorithm(congestion_control_type); +} + +QuicSentPacketManager::~QuicSentPacketManager() {} + +void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { + const Perspective perspective = unacked_packets_.perspective(); + if (config.HasReceivedInitialRoundTripTimeUs() && + config.ReceivedInitialRoundTripTimeUs() > 0) { + if (!config.HasClientSentConnectionOption(kNRTT, perspective)) { + SetInitialRtt(QuicTime::Delta::FromMicroseconds( + config.ReceivedInitialRoundTripTimeUs())); + } + } else if (config.HasInitialRoundTripTimeUsToSend() && + config.GetInitialRoundTripTimeUsToSend() > 0) { + SetInitialRtt(QuicTime::Delta::FromMicroseconds( + config.GetInitialRoundTripTimeUsToSend())); + } + if (config.HasClientSentConnectionOption(kMAD0, perspective)) { + rtt_stats_.set_ignore_max_ack_delay(true); + } + if (config.HasClientSentConnectionOption(kMAD1, perspective)) { + rtt_stats_.set_initial_max_ack_delay(delayed_ack_time_); + } + if (config.HasClientSentConnectionOption(kMAD2, perspective)) { + min_tlp_timeout_ = QuicTime::Delta::Zero(); + } + if (config.HasClientSentConnectionOption(kMAD3, perspective)) { + min_rto_timeout_ = QuicTime::Delta::Zero(); + } + if (config.HasClientSentConnectionOption(kMAD4, perspective)) { + ietf_style_tlp_ = true; + } + if (config.HasClientSentConnectionOption(kMAD5, perspective)) { + ietf_style_2x_tlp_ = true; + } + + // Configure congestion control. + if (config.HasClientRequestedIndependentOption(kTBBR, perspective)) { + SetSendAlgorithm(kBBR); + } + if (config.HasClientRequestedIndependentOption(kRENO, perspective)) { + SetSendAlgorithm(kRenoBytes); + } else if (config.HasClientRequestedIndependentOption(kBYTE, perspective) || + (GetQuicReloadableFlag(quic_default_to_bbr) && + config.HasClientRequestedIndependentOption(kQBIC, perspective))) { + SetSendAlgorithm(kCubicBytes); + } else if (GetQuicReloadableFlag(quic_enable_pcc3) && + config.HasClientRequestedIndependentOption(kTPCC, perspective)) { + SetSendAlgorithm(kPCC); + } + // Initial window. + if (GetQuicReloadableFlag(quic_unified_iw_options)) { + if (config.HasClientRequestedIndependentOption(kIW03, perspective)) { + initial_congestion_window_ = 3; + send_algorithm_->SetInitialCongestionWindowInPackets(3); + } + if (config.HasClientRequestedIndependentOption(kIW10, perspective)) { + initial_congestion_window_ = 10; + send_algorithm_->SetInitialCongestionWindowInPackets(10); + } + if (config.HasClientRequestedIndependentOption(kIW20, perspective)) { + initial_congestion_window_ = 20; + send_algorithm_->SetInitialCongestionWindowInPackets(20); + } + if (config.HasClientRequestedIndependentOption(kIW50, perspective)) { + initial_congestion_window_ = 50; + send_algorithm_->SetInitialCongestionWindowInPackets(50); + } + } + + using_pacing_ = !FLAGS_quic_disable_pacing_for_perf_tests; + + if (config.HasClientSentConnectionOption(k1CON, perspective)) { + send_algorithm_->SetNumEmulatedConnections(1); + } + if (config.HasClientSentConnectionOption(kNTLP, perspective)) { + max_tail_loss_probes_ = 0; + } + if (config.HasClientSentConnectionOption(k1TLP, perspective)) { + max_tail_loss_probes_ = 1; + } + if (config.HasClientSentConnectionOption(k1RTO, perspective)) { + max_rto_packets_ = 1; + } + if (config.HasClientSentConnectionOption(kTLPR, perspective)) { + enable_half_rtt_tail_loss_probe_ = true; + } + if (config.HasClientSentConnectionOption(kNRTO, perspective)) { + use_new_rto_ = true; + } + // Configure loss detection. + if (config.HasClientRequestedIndependentOption(kTIME, perspective)) { + if (unacked_packets_.use_uber_loss_algorithm()) { + uber_loss_algorithm_.SetLossDetectionType(kTime); + } else { + general_loss_algorithm_.SetLossDetectionType(kTime); + } + } + if (config.HasClientRequestedIndependentOption(kATIM, perspective)) { + if (unacked_packets_.use_uber_loss_algorithm()) { + uber_loss_algorithm_.SetLossDetectionType(kAdaptiveTime); + } else { + general_loss_algorithm_.SetLossDetectionType(kAdaptiveTime); + } + } + if (config.HasClientRequestedIndependentOption(kLFAK, perspective)) { + if (unacked_packets_.use_uber_loss_algorithm()) { + uber_loss_algorithm_.SetLossDetectionType(kLazyFack); + } else { + general_loss_algorithm_.SetLossDetectionType(kLazyFack); + } + } + if (config.HasClientSentConnectionOption(kCONH, perspective)) { + conservative_handshake_retransmits_ = true; + } + send_algorithm_->SetFromConfig(config, perspective); + + if (network_change_visitor_ != nullptr) { + network_change_visitor_->OnCongestionChange(); + } +} + +void QuicSentPacketManager::ResumeConnectionState( + const CachedNetworkParameters& cached_network_params, + bool max_bandwidth_resumption) { + QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond( + max_bandwidth_resumption + ? cached_network_params.max_bandwidth_estimate_bytes_per_second() + : cached_network_params.bandwidth_estimate_bytes_per_second()); + QuicTime::Delta rtt = + QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms()); + AdjustNetworkParameters(bandwidth, rtt); +} + +void QuicSentPacketManager::AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + if (!rtt.IsZero()) { + SetInitialRtt(rtt); + } + send_algorithm_->AdjustNetworkParameters(bandwidth, rtt); + if (debug_delegate_ != nullptr) { + debug_delegate_->OnAdjustNetworkParameters(bandwidth, rtt); + } +} + +void QuicSentPacketManager::SetHandshakeConfirmed() { + handshake_confirmed_ = true; + if (unacked_packets_.use_uber_loss_algorithm()) { + NeuterHandshakePackets(); + } +} + +void QuicSentPacketManager::PostProcessAfterMarkingPacketHandled( + const QuicAckFrame& ack_frame, + QuicTime ack_receive_time, + bool rtt_updated, + QuicByteCount prior_bytes_in_flight) { + if (session_decides_what_to_write()) { + unacked_packets_.NotifyAggregatedStreamFrameAcked( + last_ack_frame_.ack_delay_time); + } + InvokeLossDetection(ack_receive_time); + // Ignore losses in RTO mode. + if (consecutive_rto_count_ > 0 && !use_new_rto_) { + packets_lost_.clear(); + } + MaybeInvokeCongestionEvent(rtt_updated, prior_bytes_in_flight, + ack_receive_time); + unacked_packets_.RemoveObsoletePackets(); + + sustained_bandwidth_recorder_.RecordEstimate( + send_algorithm_->InRecovery(), send_algorithm_->InSlowStart(), + send_algorithm_->BandwidthEstimate(), ack_receive_time, clock_->WallNow(), + rtt_stats_.smoothed_rtt()); + + // Anytime we are making forward progress and have a new RTT estimate, reset + // the backoff counters. + if (rtt_updated) { + if (consecutive_rto_count_ > 0) { + // If the ack acknowledges data sent prior to the RTO, + // the RTO was spurious. + if (LargestAcked(ack_frame) < first_rto_transmission_) { + // Replace SRTT with latest_rtt and increase the variance to prevent + // a spurious RTO from happening again. + rtt_stats_.ExpireSmoothedMetrics(); + } else { + if (!use_new_rto_) { + send_algorithm_->OnRetransmissionTimeout(true); + } + } + } + // Reset all retransmit counters any time a new packet is acked. + consecutive_rto_count_ = 0; + consecutive_tlp_count_ = 0; + consecutive_crypto_retransmission_count_ = 0; + } + + if (debug_delegate_ != nullptr) { + debug_delegate_->OnIncomingAck(ack_frame, ack_receive_time, + LargestAcked(ack_frame), rtt_updated, + GetLeastUnacked()); + } + // Remove packets below least unacked from all_packets_acked_ and + // last_ack_frame_. + last_ack_frame_.packets.RemoveUpTo(unacked_packets_.GetLeastUnacked()); + last_ack_frame_.received_packet_times.clear(); +} + +void QuicSentPacketManager::MaybeInvokeCongestionEvent( + bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time) { + if (!rtt_updated && packets_acked_.empty() && packets_lost_.empty()) { + return; + } + if (using_pacing_) { + pacing_sender_.OnCongestionEvent(rtt_updated, prior_in_flight, event_time, + packets_acked_, packets_lost_); + } else { + send_algorithm_->OnCongestionEvent(rtt_updated, prior_in_flight, event_time, + packets_acked_, packets_lost_); + } + packets_acked_.clear(); + packets_lost_.clear(); + if (network_change_visitor_ != nullptr) { + network_change_visitor_->OnCongestionChange(); + } +} + +void QuicSentPacketManager::RetransmitUnackedPackets( + TransmissionType retransmission_type) { + DCHECK(retransmission_type == ALL_UNACKED_RETRANSMISSION || + retransmission_type == ALL_INITIAL_RETRANSMISSION); + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + if ((retransmission_type == ALL_UNACKED_RETRANSMISSION || + it->encryption_level == ENCRYPTION_ZERO_RTT) && + unacked_packets_.HasRetransmittableFrames(*it)) { + MarkForRetransmission(packet_number, retransmission_type); + } + } +} + +void QuicSentPacketManager::NeuterUnencryptedPackets() { + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + if (session_decides_what_to_write()) { + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + if (!it->retransmittable_frames.empty() && + it->encryption_level == ENCRYPTION_NONE) { + // Once the connection swithes to forward secure, no unencrypted packets + // will be sent. The data has been abandoned in the cryto stream. Remove + // it from in flight. + unacked_packets_.RemoveFromInFlight(packet_number); + } + } + return; + } + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + if (it->encryption_level == ENCRYPTION_NONE && + unacked_packets_.HasRetransmittableFrames(*it)) { + // Once you're forward secure, no unencrypted packets will be sent, crypto + // or otherwise. Unencrypted packets are neutered and abandoned, to ensure + // they are not retransmitted or considered lost from a congestion control + // perspective. + pending_retransmissions_.erase(packet_number); + unacked_packets_.RemoveFromInFlight(packet_number); + unacked_packets_.RemoveRetransmittability(packet_number); + } + } +} + +void QuicSentPacketManager::NeuterHandshakePackets() { + DCHECK(unacked_packets_.use_uber_loss_algorithm()); + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + if (session_decides_what_to_write()) { + if (!it->retransmittable_frames.empty() && + unacked_packets_.GetPacketNumberSpace(it->encryption_level) == + HANDSHAKE_DATA) { + unacked_packets_.RemoveFromInFlight(packet_number); + } + continue; + } + if (unacked_packets_.GetPacketNumberSpace(it->encryption_level) == + HANDSHAKE_DATA && + unacked_packets_.HasRetransmittableFrames(*it)) { + pending_retransmissions_.erase(packet_number); + unacked_packets_.RemoveFromInFlight(packet_number); + unacked_packets_.RemoveRetransmittability(packet_number); + } + } +} + +void QuicSentPacketManager::MarkForRetransmission( + QuicPacketNumber packet_number, + TransmissionType transmission_type) { + QuicTransmissionInfo* transmission_info = + unacked_packets_.GetMutableTransmissionInfo(packet_number); + // When session decides what to write, a previous RTO retransmission may cause + // connection close; packets without retransmittable frames can be marked for + // loss retransmissions. + QUIC_BUG_IF((transmission_type != LOSS_RETRANSMISSION && + (!session_decides_what_to_write() || + transmission_type != RTO_RETRANSMISSION)) && + !unacked_packets_.HasRetransmittableFrames(*transmission_info)) + << "transmission_type: " + << QuicUtils::TransmissionTypeToString(transmission_type); + // Handshake packets should never be sent as probing retransmissions. + DCHECK(!transmission_info->has_crypto_handshake || + transmission_type != PROBING_RETRANSMISSION); + if (!RetransmissionLeavesBytesInFlight(transmission_type)) { + unacked_packets_.RemoveFromInFlight(transmission_info); + } + + if (!session_decides_what_to_write()) { + if (!unacked_packets_.HasRetransmittableFrames(*transmission_info)) { + return; + } + if (!QuicContainsKey(pending_retransmissions_, packet_number)) { + pending_retransmissions_[packet_number] = transmission_type; + } + return; + } + + HandleRetransmission(transmission_type, transmission_info); + + // Update packet state according to transmission type. + transmission_info->state = + QuicUtils::RetransmissionTypeToPacketState(transmission_type); +} + +void QuicSentPacketManager::HandleRetransmission( + TransmissionType transmission_type, + QuicTransmissionInfo* transmission_info) { + DCHECK(session_decides_what_to_write()); + if (ShouldForceRetransmission(transmission_type)) { + // TODO(fayang): Consider to make RTO and PROBING retransmission + // strategies be configurable by applications. Today, TLP, RTO and PROBING + // retransmissions are handled similarly, i.e., always retranmist the + // oldest outstanding data. This is not ideal in general because different + // applications may want different strategies. For example, some + // applications may want to use higher priority stream data for bandwidth + // probing, and some applications want to consider RTO is an indication of + // loss, etc. + unacked_packets_.RetransmitFrames(*transmission_info, transmission_type); + return; + } + + unacked_packets_.NotifyFramesLost(*transmission_info, transmission_type); + if (transmission_info->retransmittable_frames.empty()) { + return; + } + + if (transmission_type == LOSS_RETRANSMISSION) { + // Record the first packet sent after loss, which allows to wait 1 + // more RTT before giving up on this lost packet. + transmission_info->retransmission = + unacked_packets_.largest_sent_packet() + 1; + } else { + // Clear the recorded first packet sent after loss when version or + // encryption changes. + transmission_info->retransmission.Clear(); + } +} + +void QuicSentPacketManager::RecordOneSpuriousRetransmission( + const QuicTransmissionInfo& info) { + stats_->bytes_spuriously_retransmitted += info.bytes_sent; + ++stats_->packets_spuriously_retransmitted; + if (debug_delegate_ != nullptr) { + debug_delegate_->OnSpuriousPacketRetransmission(info.transmission_type, + info.bytes_sent); + } +} + +void QuicSentPacketManager::RecordSpuriousRetransmissions( + const QuicTransmissionInfo& info, + QuicPacketNumber acked_packet_number) { + if (session_decides_what_to_write()) { + RecordOneSpuriousRetransmission(info); + if (info.transmission_type == LOSS_RETRANSMISSION) { + // Only inform the loss detection of spurious retransmits it caused. + loss_algorithm_->SpuriousRetransmitDetected( + unacked_packets_, clock_->Now(), rtt_stats_, acked_packet_number); + } + return; + } + QuicPacketNumber retransmission = info.retransmission; + while (retransmission.IsInitialized()) { + const QuicTransmissionInfo& retransmit_info = + unacked_packets_.GetTransmissionInfo(retransmission); + retransmission = retransmit_info.retransmission; + RecordOneSpuriousRetransmission(retransmit_info); + } + // Only inform the loss detection of spurious retransmits it caused. + if (unacked_packets_.GetTransmissionInfo(info.retransmission) + .transmission_type == LOSS_RETRANSMISSION) { + loss_algorithm_->SpuriousRetransmitDetected( + unacked_packets_, clock_->Now(), rtt_stats_, info.retransmission); + } +} + +QuicPendingRetransmission QuicSentPacketManager::NextPendingRetransmission() { + QUIC_BUG_IF(pending_retransmissions_.empty()) + << "Unexpected call to NextPendingRetransmission() with empty pending " + << "retransmission list. Corrupted memory usage imminent."; + QUIC_BUG_IF(session_decides_what_to_write()) + << "Unexpected call to NextPendingRetransmission() when session handles " + "retransmissions"; + QuicPacketNumber packet_number = pending_retransmissions_.begin()->first; + TransmissionType transmission_type = pending_retransmissions_.begin()->second; + if (unacked_packets_.HasPendingCryptoPackets()) { + // Ensure crypto packets are retransmitted before other packets. + for (const auto& pair : pending_retransmissions_) { + if (HasCryptoHandshake( + unacked_packets_.GetTransmissionInfo(pair.first))) { + packet_number = pair.first; + transmission_type = pair.second; + break; + } + } + } + DCHECK(unacked_packets_.IsUnacked(packet_number)) << packet_number; + const QuicTransmissionInfo& transmission_info = + unacked_packets_.GetTransmissionInfo(packet_number); + DCHECK(unacked_packets_.HasRetransmittableFrames(transmission_info)); + + return QuicPendingRetransmission(packet_number, transmission_type, + transmission_info); +} + +QuicPacketNumber QuicSentPacketManager::GetNewestRetransmission( + QuicPacketNumber packet_number, + const QuicTransmissionInfo& transmission_info) const { + if (session_decides_what_to_write()) { + return packet_number; + } + QuicPacketNumber retransmission = transmission_info.retransmission; + while (retransmission.IsInitialized()) { + packet_number = retransmission; + retransmission = + unacked_packets_.GetTransmissionInfo(retransmission).retransmission; + } + return packet_number; +} + +void QuicSentPacketManager::MarkPacketHandled(QuicPacketNumber packet_number, + QuicTransmissionInfo* info, + QuicTime::Delta ack_delay_time) { + QuicPacketNumber newest_transmission = + GetNewestRetransmission(packet_number, *info); + // Remove the most recent packet, if it is pending retransmission. + pending_retransmissions_.erase(newest_transmission); + + if (newest_transmission == packet_number) { + // Try to aggregate acked stream frames if acked packet is not a + // retransmission. + const bool fast_path = session_decides_what_to_write() && + info->transmission_type == NOT_RETRANSMISSION; + if (fast_path) { + unacked_packets_.MaybeAggregateAckedStreamFrame(*info, ack_delay_time); + } else { + if (session_decides_what_to_write()) { + unacked_packets_.NotifyAggregatedStreamFrameAcked(ack_delay_time); + } + const bool new_data_acked = + unacked_packets_.NotifyFramesAcked(*info, ack_delay_time); + if (session_decides_what_to_write() && !new_data_acked && + info->transmission_type != NOT_RETRANSMISSION) { + // Record as a spurious retransmission if this packet is a + // retransmission and no new data gets acked. + QUIC_DVLOG(1) << "Detect spurious retransmitted packet " + << packet_number << " transmission type: " + << QuicUtils::TransmissionTypeToString( + info->transmission_type); + RecordSpuriousRetransmissions(*info, packet_number); + } + } + } else { + DCHECK(!session_decides_what_to_write()); + RecordSpuriousRetransmissions(*info, packet_number); + // Remove the most recent packet from flight if it's a crypto handshake + // packet, since they won't be acked now that one has been processed. + // Other crypto handshake packets won't be in flight, only the newest + // transmission of a crypto packet is in flight at once. + // TODO(ianswett): Instead of handling all crypto packets special, + // only handle nullptr encrypted packets in a special way. + const QuicTransmissionInfo& newest_transmission_info = + unacked_packets_.GetTransmissionInfo(newest_transmission); + unacked_packets_.NotifyFramesAcked(newest_transmission_info, + ack_delay_time); + if (HasCryptoHandshake(newest_transmission_info)) { + unacked_packets_.RemoveFromInFlight(newest_transmission); + } + } + + if (network_change_visitor_ != nullptr && + info->bytes_sent > largest_mtu_acked_) { + largest_mtu_acked_ = info->bytes_sent; + network_change_visitor_->OnPathMtuIncreased(largest_mtu_acked_); + } + unacked_packets_.RemoveFromInFlight(info); + unacked_packets_.RemoveRetransmittability(info); + info->state = ACKED; +} + +bool QuicSentPacketManager::OnPacketSent( + SerializedPacket* serialized_packet, + QuicPacketNumber original_packet_number, + QuicTime sent_time, + TransmissionType transmission_type, + HasRetransmittableData has_retransmittable_data) { + QuicPacketNumber packet_number = serialized_packet->packet_number; + DCHECK_LE(FirstSendingPacketNumber(), packet_number); + DCHECK(!unacked_packets_.IsUnacked(packet_number)); + QUIC_BUG_IF(serialized_packet->encrypted_length == 0) + << "Cannot send empty packets."; + + if (original_packet_number.IsInitialized()) { + pending_retransmissions_.erase(original_packet_number); + } + + if (pending_timer_transmission_count_ > 0) { + --pending_timer_transmission_count_; + } + + bool in_flight = has_retransmittable_data == HAS_RETRANSMITTABLE_DATA; + if (using_pacing_) { + pacing_sender_.OnPacketSent( + sent_time, unacked_packets_.bytes_in_flight(), packet_number, + serialized_packet->encrypted_length, has_retransmittable_data); + } else { + send_algorithm_->OnPacketSent( + sent_time, unacked_packets_.bytes_in_flight(), packet_number, + serialized_packet->encrypted_length, has_retransmittable_data); + } + + unacked_packets_.AddSentPacket(serialized_packet, original_packet_number, + transmission_type, sent_time, in_flight); + // Reset the retransmission timer anytime a pending packet is sent. + return in_flight; +} + +void QuicSentPacketManager::OnRetransmissionTimeout() { + DCHECK(unacked_packets_.HasInFlightPackets()); + DCHECK_EQ(0u, pending_timer_transmission_count_); + // Handshake retransmission, timer based loss detection, TLP, and RTO are + // implemented with a single alarm. The handshake alarm is set when the + // handshake has not completed, the loss alarm is set when the loss detection + // algorithm says to, and the TLP and RTO alarms are set after that. + // The TLP alarm is always set to run for under an RTO. + switch (GetRetransmissionMode()) { + case HANDSHAKE_MODE: + ++stats_->crypto_retransmit_count; + RetransmitCryptoPackets(); + return; + case LOSS_MODE: { + ++stats_->loss_timeout_count; + QuicByteCount prior_in_flight = unacked_packets_.bytes_in_flight(); + const QuicTime now = clock_->Now(); + InvokeLossDetection(now); + MaybeInvokeCongestionEvent(false, prior_in_flight, now); + return; + } + case TLP_MODE: + ++stats_->tlp_count; + ++consecutive_tlp_count_; + pending_timer_transmission_count_ = 1; + // TLPs prefer sending new data instead of retransmitting data, so + // give the connection a chance to write before completing the TLP. + return; + case RTO_MODE: + ++stats_->rto_count; + RetransmitRtoPackets(); + return; + } +} + +void QuicSentPacketManager::RetransmitCryptoPackets() { + DCHECK_EQ(HANDSHAKE_MODE, GetRetransmissionMode()); + ++consecutive_crypto_retransmission_count_; + bool packet_retransmitted = false; + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + std::vector<QuicPacketNumber> crypto_retransmissions; + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + // Only retransmit frames which are in flight, and therefore have been sent. + if (!it->in_flight || + (session_decides_what_to_write() && it->state != OUTSTANDING) || + !it->has_crypto_handshake || + !unacked_packets_.HasRetransmittableFrames(*it)) { + continue; + } + packet_retransmitted = true; + if (session_decides_what_to_write()) { + crypto_retransmissions.push_back(packet_number); + } else { + MarkForRetransmission(packet_number, HANDSHAKE_RETRANSMISSION); + } + ++pending_timer_transmission_count_; + } + DCHECK(packet_retransmitted) << "No crypto packets found to retransmit."; + if (session_decides_what_to_write()) { + for (QuicPacketNumber retransmission : crypto_retransmissions) { + MarkForRetransmission(retransmission, HANDSHAKE_RETRANSMISSION); + } + } +} + +bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() { + if (pending_timer_transmission_count_ == 0) { + return false; + } + if (!MaybeRetransmitOldestPacket(TLP_RETRANSMISSION)) { + // If no tail loss probe can be sent, because there are no retransmittable + // packets, execute a conventional RTO to abandon old packets. + if (GetQuicReloadableFlag(quic_optimize_inflight_check)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_optimize_inflight_check); + pending_timer_transmission_count_ = 0; + RetransmitRtoPackets(); + } + return false; + } + return true; +} + +bool QuicSentPacketManager::MaybeRetransmitOldestPacket(TransmissionType type) { + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + // Only retransmit frames which are in flight, and therefore have been sent. + if (!it->in_flight || + (session_decides_what_to_write() && it->state != OUTSTANDING) || + !unacked_packets_.HasRetransmittableFrames(*it)) { + continue; + } + MarkForRetransmission(packet_number, type); + return true; + } + QUIC_DVLOG(1) + << "No retransmittable packets, so RetransmitOldestPacket failed."; + return false; +} + +void QuicSentPacketManager::RetransmitRtoPackets() { + QUIC_BUG_IF(pending_timer_transmission_count_ > 0) + << "Retransmissions already queued:" << pending_timer_transmission_count_; + // Mark two packets for retransmission. + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + std::vector<QuicPacketNumber> retransmissions; + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + if ((!session_decides_what_to_write() || it->state == OUTSTANDING) && + unacked_packets_.HasRetransmittableFrames(*it) && + pending_timer_transmission_count_ < max_rto_packets_) { + if (session_decides_what_to_write()) { + retransmissions.push_back(packet_number); + } else { + MarkForRetransmission(packet_number, RTO_RETRANSMISSION); + } + ++pending_timer_transmission_count_; + } + // Abandon non-retransmittable data that's in flight to ensure it doesn't + // fill up the congestion window. + bool has_retransmissions = it->retransmission.IsInitialized(); + if (session_decides_what_to_write()) { + has_retransmissions = it->state != OUTSTANDING; + } + if (it->in_flight && !has_retransmissions && + !unacked_packets_.HasRetransmittableFrames(*it)) { + // Log only for non-retransmittable data. + // Retransmittable data is marked as lost during loss detection, and will + // be logged later. + unacked_packets_.RemoveFromInFlight(packet_number); + if (debug_delegate_ != nullptr) { + debug_delegate_->OnPacketLoss(packet_number, RTO_RETRANSMISSION, + clock_->Now()); + } + } + } + if (pending_timer_transmission_count_ > 0) { + if (consecutive_rto_count_ == 0) { + first_rto_transmission_ = unacked_packets_.largest_sent_packet() + 1; + } + ++consecutive_rto_count_; + } + if (session_decides_what_to_write()) { + for (QuicPacketNumber retransmission : retransmissions) { + MarkForRetransmission(retransmission, RTO_RETRANSMISSION); + } + } +} + +QuicSentPacketManager::RetransmissionTimeoutMode +QuicSentPacketManager::GetRetransmissionMode() const { + DCHECK(unacked_packets_.HasInFlightPackets()); + if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) { + return HANDSHAKE_MODE; + } + if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) { + return LOSS_MODE; + } + if (consecutive_tlp_count_ < max_tail_loss_probes_) { + if (GetQuicReloadableFlag(quic_optimize_inflight_check) || + unacked_packets_.HasUnackedRetransmittableFrames()) { + return TLP_MODE; + } + } + return RTO_MODE; +} + +void QuicSentPacketManager::InvokeLossDetection(QuicTime time) { + if (!packets_acked_.empty()) { + DCHECK_LE(packets_acked_.front().packet_number, + packets_acked_.back().packet_number); + largest_newly_acked_ = packets_acked_.back().packet_number; + } + loss_algorithm_->DetectLosses(unacked_packets_, time, rtt_stats_, + largest_newly_acked_, packets_acked_, + &packets_lost_); + for (const LostPacket& packet : packets_lost_) { + ++stats_->packets_lost; + if (debug_delegate_ != nullptr) { + debug_delegate_->OnPacketLoss(packet.packet_number, LOSS_RETRANSMISSION, + time); + } + + MarkForRetransmission(packet.packet_number, LOSS_RETRANSMISSION); + } +} + +bool QuicSentPacketManager::MaybeUpdateRTT(QuicPacketNumber largest_acked, + QuicTime::Delta ack_delay_time, + QuicTime ack_receive_time) { + // We rely on ack_delay_time to compute an RTT estimate, so we + // only update rtt when the largest observed gets acked. + if (!unacked_packets_.IsUnacked(largest_acked)) { + return false; + } + // We calculate the RTT based on the highest ACKed packet number, the lower + // packet numbers will include the ACK aggregation delay. + const QuicTransmissionInfo& transmission_info = + unacked_packets_.GetTransmissionInfo(largest_acked); + // Ensure the packet has a valid sent time. + if (transmission_info.sent_time == QuicTime::Zero()) { + QUIC_BUG << "Acked packet has zero sent time, largest_acked:" + << largest_acked; + return false; + } + if (transmission_info.sent_time > ack_receive_time) { + QUIC_CODE_COUNT(quic_receive_acked_before_sending); + } + + QuicTime::Delta send_delta = ack_receive_time - transmission_info.sent_time; + rtt_stats_.UpdateRtt(send_delta, ack_delay_time, ack_receive_time); + + return true; +} + +QuicTime::Delta QuicSentPacketManager::TimeUntilSend(QuicTime now) const { + // The TLP logic is entirely contained within QuicSentPacketManager, so the + // send algorithm does not need to be consulted. + if (pending_timer_transmission_count_ > 0) { + return QuicTime::Delta::Zero(); + } + + if (using_pacing_) { + return pacing_sender_.TimeUntilSend(now, + unacked_packets_.bytes_in_flight()); + } + + return send_algorithm_->CanSend(unacked_packets_.bytes_in_flight()) + ? QuicTime::Delta::Zero() + : QuicTime::Delta::Infinite(); +} + +const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { + // Don't set the timer if there is nothing to retransmit or we've already + // queued a tlp transmission and it hasn't been sent yet. + if (!unacked_packets_.HasInFlightPackets() || + pending_timer_transmission_count_ > 0) { + return QuicTime::Zero(); + } + if (!GetQuicReloadableFlag(quic_optimize_inflight_check) && + !unacked_packets_.HasUnackedRetransmittableFrames()) { + return QuicTime::Zero(); + } + switch (GetRetransmissionMode()) { + case HANDSHAKE_MODE: + return unacked_packets_.GetLastCryptoPacketSentTime() + + GetCryptoRetransmissionDelay(); + case LOSS_MODE: + return loss_algorithm_->GetLossTimeout(); + case TLP_MODE: { + // TODO(ianswett): When CWND is available, it would be preferable to + // set the timer based on the earliest retransmittable packet. + // Base the updated timer on the send time of the last packet. + const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime(); + const QuicTime tlp_time = sent_time + GetTailLossProbeDelay(); + // Ensure the TLP timer never gets set to a time in the past. + return std::max(clock_->ApproximateNow(), tlp_time); + } + case RTO_MODE: { + // The RTO is based on the first outstanding packet. + const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime(); + QuicTime rto_time = sent_time + GetRetransmissionDelay(); + // Wait for TLP packets to be acked before an RTO fires. + QuicTime tlp_time = + unacked_packets_.GetLastPacketSentTime() + GetTailLossProbeDelay(); + return std::max(tlp_time, rto_time); + } + } + DCHECK(false); + return QuicTime::Zero(); +} + +const QuicTime::Delta QuicSentPacketManager::GetPathDegradingDelay() const { + QuicTime::Delta delay = QuicTime::Delta::Zero(); + for (size_t i = 0; i < max_tail_loss_probes_; ++i) { + delay = delay + GetTailLossProbeDelay(i); + } + for (size_t i = 0; i < kNumRetransmissionDelaysForPathDegradingDelay; ++i) { + delay = delay + GetRetransmissionDelay(i); + } + return delay; +} + +const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay() + const { + // This is equivalent to the TailLossProbeDelay, but slightly more aggressive + // because crypto handshake messages don't incur a delayed ack time. + QuicTime::Delta srtt = rtt_stats_.SmoothedOrInitialRtt(); + int64_t delay_ms; + if (conservative_handshake_retransmits_) { + // Using the delayed ack time directly could cause conservative handshake + // retransmissions to actually be more aggressive than the default. + delay_ms = std::max(delayed_ack_time_.ToMilliseconds(), + static_cast<int64_t>(2 * srtt.ToMilliseconds())); + } else { + delay_ms = std::max(kMinHandshakeTimeoutMs, + static_cast<int64_t>(1.5 * srtt.ToMilliseconds())); + } + return QuicTime::Delta::FromMilliseconds( + delay_ms << consecutive_crypto_retransmission_count_); +} + +const QuicTime::Delta QuicSentPacketManager::GetTailLossProbeDelay( + size_t consecutive_tlp_count) const { + QuicTime::Delta srtt = rtt_stats_.SmoothedOrInitialRtt(); + if (enable_half_rtt_tail_loss_probe_ && consecutive_tlp_count == 0u) { + return std::max(min_tlp_timeout_, srtt * 0.5); + } + if (ietf_style_tlp_) { + return std::max(min_tlp_timeout_, 1.5 * srtt + rtt_stats_.max_ack_delay()); + } + if (ietf_style_2x_tlp_) { + return std::max(min_tlp_timeout_, 2 * srtt + rtt_stats_.max_ack_delay()); + } + if (!unacked_packets_.HasMultipleInFlightPackets()) { + // This expression really should be using the delayed ack time, but in TCP + // MinRTO was traditionally set to 2x the delayed ack timer and this + // expression assumed QUIC did the same. + return std::max(2 * srtt, 1.5 * srtt + (min_rto_timeout_ * 0.5)); + } + return std::max(min_tlp_timeout_, 2 * srtt); +} + +const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay( + size_t consecutive_rto_count) const { + QuicTime::Delta retransmission_delay = QuicTime::Delta::Zero(); + if (rtt_stats_.smoothed_rtt().IsZero()) { + // We are in the initial state, use default timeout values. + retransmission_delay = + QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); + } else { + retransmission_delay = + rtt_stats_.smoothed_rtt() + 4 * rtt_stats_.mean_deviation(); + if (retransmission_delay < min_rto_timeout_) { + retransmission_delay = min_rto_timeout_; + } + } + + // Calculate exponential back off. + retransmission_delay = + retransmission_delay * + (1 << std::min<size_t>(consecutive_rto_count, kMaxRetransmissions)); + + if (retransmission_delay.ToMilliseconds() > kMaxRetransmissionTimeMs) { + return QuicTime::Delta::FromMilliseconds(kMaxRetransmissionTimeMs); + } + return retransmission_delay; +} + +QuicString QuicSentPacketManager::GetDebugState() const { + return send_algorithm_->GetDebugState(); +} + +void QuicSentPacketManager::CancelRetransmissionsForStream( + QuicStreamId stream_id) { + if (session_decides_what_to_write()) { + return; + } + unacked_packets_.CancelRetransmissionsForStream(stream_id); + auto it = pending_retransmissions_.begin(); + while (it != pending_retransmissions_.end()) { + if (unacked_packets_.HasRetransmittableFrames(it->first)) { + ++it; + continue; + } + it = pending_retransmissions_.erase(it); + } +} + +void QuicSentPacketManager::SetSendAlgorithm( + CongestionControlType congestion_control_type) { + SetSendAlgorithm(SendAlgorithmInterface::Create( + clock_, &rtt_stats_, &unacked_packets_, congestion_control_type, + QuicRandom::GetInstance(), stats_, initial_congestion_window_)); +} + +void QuicSentPacketManager::SetSendAlgorithm( + SendAlgorithmInterface* send_algorithm) { + send_algorithm_.reset(send_algorithm); + pacing_sender_.set_sender(send_algorithm); +} + +void QuicSentPacketManager::OnConnectionMigration(AddressChangeType type) { + if (type == PORT_CHANGE || type == IPV4_SUBNET_CHANGE) { + // Rtt and cwnd do not need to be reset when the peer address change is + // considered to be caused by NATs. + return; + } + consecutive_rto_count_ = 0; + consecutive_tlp_count_ = 0; + rtt_stats_.OnConnectionMigration(); + send_algorithm_->OnConnectionMigration(); +} + +void QuicSentPacketManager::OnAckFrameStart(QuicPacketNumber largest_acked, + QuicTime::Delta ack_delay_time, + QuicTime ack_receive_time) { + DCHECK(packets_acked_.empty()); + DCHECK_LE(largest_acked, unacked_packets_.largest_sent_packet()); + rtt_updated_ = + MaybeUpdateRTT(largest_acked, ack_delay_time, ack_receive_time); + DCHECK(!unacked_packets_.largest_acked().IsInitialized() || + largest_acked >= unacked_packets_.largest_acked()); + last_ack_frame_.ack_delay_time = ack_delay_time; + acked_packets_iter_ = last_ack_frame_.packets.rbegin(); +} + +void QuicSentPacketManager::OnAckRange(QuicPacketNumber start, + QuicPacketNumber end) { + if (!last_ack_frame_.largest_acked.IsInitialized() || + end > last_ack_frame_.largest_acked + 1) { + // Largest acked increases. + unacked_packets_.IncreaseLargestAcked(end - 1); + last_ack_frame_.largest_acked = end - 1; + } + // Drop ack ranges which ack packets below least_unacked. + QuicPacketNumber least_unacked = unacked_packets_.GetLeastUnacked(); + if (least_unacked.IsInitialized() && end <= least_unacked) { + return; + } + start = std::max(start, least_unacked); + do { + QuicPacketNumber newly_acked_start = start; + if (acked_packets_iter_ != last_ack_frame_.packets.rend()) { + newly_acked_start = std::max(start, acked_packets_iter_->max()); + } + for (QuicPacketNumber acked = end - 1; acked >= newly_acked_start; + --acked) { + // Check if end is above the current range. If so add newly acked packets + // in descending order. + packets_acked_.push_back(AckedPacket(acked, 0, QuicTime::Zero())); + if (acked == FirstSendingPacketNumber()) { + break; + } + } + if (acked_packets_iter_ == last_ack_frame_.packets.rend() || + start > acked_packets_iter_->min()) { + // Finish adding all newly acked packets. + return; + } + end = std::min(end, acked_packets_iter_->min()); + ++acked_packets_iter_; + } while (start < end); +} + +void QuicSentPacketManager::OnAckTimestamp(QuicPacketNumber packet_number, + QuicTime timestamp) { + last_ack_frame_.received_packet_times.push_back({packet_number, timestamp}); + for (AckedPacket& packet : packets_acked_) { + if (packet.packet_number == packet_number) { + packet.receive_timestamp = timestamp; + return; + } + } +} + +bool QuicSentPacketManager::OnAckFrameEnd(QuicTime ack_receive_time) { + QuicByteCount prior_bytes_in_flight = unacked_packets_.bytes_in_flight(); + // Reverse packets_acked_ so that it is in ascending order. + reverse(packets_acked_.begin(), packets_acked_.end()); + for (AckedPacket& acked_packet : packets_acked_) { + QuicTransmissionInfo* info = + unacked_packets_.GetMutableTransmissionInfo(acked_packet.packet_number); + if (!QuicUtils::IsAckable(info->state)) { + if (info->state == ACKED) { + QUIC_BUG << "Trying to ack an already acked packet: " + << acked_packet.packet_number + << ", last_ack_frame_: " << last_ack_frame_ + << ", least_unacked: " << unacked_packets_.GetLeastUnacked() + << ", packets_acked_: " << packets_acked_; + } else { + QUIC_PEER_BUG << "Received ack for unackable packet: " + << acked_packet.packet_number << " with state: " + << QuicUtils::SentPacketStateToString(info->state); + } + continue; + } + QUIC_DVLOG(1) << ENDPOINT << "Got an ack for packet " + << acked_packet.packet_number; + last_ack_frame_.packets.Add(acked_packet.packet_number); + if (info->largest_acked.IsInitialized()) { + if (largest_packet_peer_knows_is_acked_.IsInitialized()) { + largest_packet_peer_knows_is_acked_ = + std::max(largest_packet_peer_knows_is_acked_, info->largest_acked); + } else { + largest_packet_peer_knows_is_acked_ = info->largest_acked; + } + } + // If data is associated with the most recent transmission of this + // packet, then inform the caller. + if (info->in_flight) { + acked_packet.bytes_acked = info->bytes_sent; + } else { + // Unackable packets are skipped earlier. + largest_newly_acked_ = acked_packet.packet_number; + } + if (unacked_packets_.use_uber_loss_algorithm()) { + unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace( + info->encryption_level, acked_packet.packet_number); + } + MarkPacketHandled(acked_packet.packet_number, info, + last_ack_frame_.ack_delay_time); + } + const bool acked_new_packet = !packets_acked_.empty(); + PostProcessAfterMarkingPacketHandled(last_ack_frame_, ack_receive_time, + rtt_updated_, prior_bytes_in_flight); + + return acked_new_packet; +} + +void QuicSentPacketManager::SetDebugDelegate(DebugDelegate* debug_delegate) { + debug_delegate_ = debug_delegate; +} + +void QuicSentPacketManager::OnApplicationLimited() { + if (using_pacing_) { + pacing_sender_.OnApplicationLimited(); + } + send_algorithm_->OnApplicationLimited(unacked_packets_.bytes_in_flight()); + if (debug_delegate_ != nullptr) { + debug_delegate_->OnApplicationLimited(); + } +} + +QuicTime QuicSentPacketManager::GetNextReleaseTime() const { + return using_pacing_ ? pacing_sender_.ideal_next_packet_send_time() + : QuicTime::Zero(); +} + +void QuicSentPacketManager::SetInitialRtt(QuicTime::Delta rtt) { + const QuicTime::Delta min_rtt = + QuicTime::Delta::FromMicroseconds(kMinInitialRoundTripTimeUs); + const QuicTime::Delta max_rtt = + QuicTime::Delta::FromMicroseconds(kMaxInitialRoundTripTimeUs); + rtt_stats_.set_initial_rtt(std::max(min_rtt, std::min(max_rtt, rtt))); +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h new file mode 100644 index 0000000..aacdc34 --- /dev/null +++ b/quic/core/quic_sent_packet_manager.h
@@ -0,0 +1,587 @@ +// Copyright 2013 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_QUIC_SENT_PACKET_MANAGER_H_ +#define QUICHE_QUIC_CORE_QUIC_SENT_PACKET_MANAGER_H_ + +#include <cstddef> +#include <map> +#include <memory> +#include <set> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h" +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h" +#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace test { +class QuicConnectionPeer; +class QuicSentPacketManagerPeer; +} // namespace test + +class QuicClock; +class QuicConfig; +struct QuicConnectionStats; + +// Class which tracks the set of packets sent on a QUIC connection and contains +// a send algorithm to decide when to send new packets. It keeps track of any +// retransmittable data associated with each packet. If a packet is +// retransmitted, it will keep track of each version of a packet so that if a +// previous transmission is acked, the data will not be retransmitted. +class QUIC_EXPORT_PRIVATE QuicSentPacketManager { + public: + // Interface which gets callbacks from the QuicSentPacketManager at + // interesting points. Implementations must not mutate the state of + // the packet manager or connection as a result of these callbacks. + class QUIC_EXPORT_PRIVATE DebugDelegate { + public: + virtual ~DebugDelegate() {} + + // Called when a spurious retransmission is detected. + virtual void OnSpuriousPacketRetransmission( + TransmissionType transmission_type, + QuicByteCount byte_size) {} + + virtual void OnIncomingAck(const QuicAckFrame& ack_frame, + QuicTime ack_receive_time, + QuicPacketNumber largest_observed, + bool rtt_updated, + QuicPacketNumber least_unacked_sent_packet) {} + + virtual void OnPacketLoss(QuicPacketNumber lost_packet_number, + TransmissionType transmission_type, + QuicTime detection_time) {} + + virtual void OnApplicationLimited() {} + + virtual void OnAdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) {} + }; + + // Interface which gets callbacks from the QuicSentPacketManager when + // network-related state changes. Implementations must not mutate the + // state of the packet manager as a result of these callbacks. + class QUIC_EXPORT_PRIVATE NetworkChangeVisitor { + public: + virtual ~NetworkChangeVisitor() {} + + // Called when congestion window or RTT may have changed. + virtual void OnCongestionChange() = 0; + + // Called when the Path MTU may have increased. + virtual void OnPathMtuIncreased(QuicPacketLength packet_size) = 0; + }; + + QuicSentPacketManager(Perspective perspective, + const QuicClock* clock, + QuicConnectionStats* stats, + CongestionControlType congestion_control_type, + LossDetectionType loss_type); + QuicSentPacketManager(const QuicSentPacketManager&) = delete; + QuicSentPacketManager& operator=(const QuicSentPacketManager&) = delete; + virtual ~QuicSentPacketManager(); + + virtual void SetFromConfig(const QuicConfig& config); + + // Pass the CachedNetworkParameters to the send algorithm. + void ResumeConnectionState( + const CachedNetworkParameters& cached_network_params, + bool max_bandwidth_resumption); + + void SetMaxPacingRate(QuicBandwidth max_pacing_rate) { + pacing_sender_.set_max_pacing_rate(max_pacing_rate); + } + + QuicBandwidth MaxPacingRate() const { + return pacing_sender_.max_pacing_rate(); + } + + // Set handshake_confirmed_ to true and neuter packets in HANDSHAKE packet + // number space. + void SetHandshakeConfirmed(); + + // Requests retransmission of all unacked packets of |retransmission_type|. + // The behavior of this method depends on the value of |retransmission_type|: + // ALL_UNACKED_RETRANSMISSION - All unacked packets will be retransmitted. + // This can happen, for example, after a version negotiation packet has been + // received and all packets needs to be retransmitted with the new version. + // ALL_INITIAL_RETRANSMISSION - Only initially encrypted packets will be + // retransmitted. This can happen, for example, when a CHLO has been rejected + // and the previously encrypted data needs to be encrypted with a new key. + void RetransmitUnackedPackets(TransmissionType retransmission_type); + + // Notify the sent packet manager of an external network measurement or + // prediction for either |bandwidth| or |rtt|; either can be empty. + void AdjustNetworkParameters(QuicBandwidth bandwidth, QuicTime::Delta rtt); + + // Retransmits the oldest pending packet there is still a tail loss probe + // pending. Invoked after OnRetransmissionTimeout. + bool MaybeRetransmitTailLossProbe(); + + // Retransmits the oldest pending packet. + bool MaybeRetransmitOldestPacket(TransmissionType type); + + // Removes the retransmittable frames from all unencrypted packets to ensure + // they don't get retransmitted. + // TODO(fayang): Consider remove this function when deprecating + // quic_use_uber_loss_algorithm. + void NeuterUnencryptedPackets(); + + // Returns true if there are pending retransmissions. + // Not const because retransmissions may be cancelled before returning. + bool HasPendingRetransmissions() const { + return !pending_retransmissions_.empty(); + } + + // Retrieves the next pending retransmission. You must ensure that + // there are pending retransmissions prior to calling this function. + QuicPendingRetransmission NextPendingRetransmission(); + + // Returns true if there's outstanding crypto data. + bool HasUnackedCryptoPackets() const { + return unacked_packets_.HasPendingCryptoPackets(); + } + + // Returns true if there are packets in flight expecting to be acknowledged. + bool HasInFlightPackets() const { + return unacked_packets_.HasInFlightPackets(); + } + + // Returns the smallest packet number of a serialized packet which has not + // been acked by the peer. + QuicPacketNumber GetLeastUnacked() const { + return unacked_packets_.GetLeastUnacked(); + } + + // Called when we have sent bytes to the peer. This informs the manager both + // the number of bytes sent and if they were retransmitted. Returns true if + // the sender should reset the retransmission timer. + bool OnPacketSent(SerializedPacket* serialized_packet, + QuicPacketNumber original_packet_number, + QuicTime sent_time, + TransmissionType transmission_type, + HasRetransmittableData has_retransmittable_data); + + // Called when the retransmission timer expires. + void OnRetransmissionTimeout(); + + // Calculate the time until we can send the next packet to the wire. + // Note 1: When kUnknownWaitTime is returned, there is no need to poll + // TimeUntilSend again until we receive an OnIncomingAckFrame event. + // Note 2: Send algorithms may or may not use |retransmit| in their + // calculations. + QuicTime::Delta TimeUntilSend(QuicTime now) const; + + // Returns the current delay for the retransmission timer, which may send + // either a tail loss probe or do a full RTO. Returns QuicTime::Zero() if + // there are no retransmittable packets. + const QuicTime GetRetransmissionTime() const; + + // Returns the current delay for the path degrading timer, which is used to + // notify the session that this connection is degrading. + const QuicTime::Delta GetPathDegradingDelay() const; + + const RttStats* GetRttStats() const { return &rtt_stats_; } + + // Returns the estimated bandwidth calculated by the congestion algorithm. + QuicBandwidth BandwidthEstimate() const { + return send_algorithm_->BandwidthEstimate(); + } + + const QuicSustainedBandwidthRecorder* SustainedBandwidthRecorder() const { + return &sustained_bandwidth_recorder_; + } + + // Returns the size of the current congestion window in number of + // kDefaultTCPMSS-sized segments. Note, this is not the *available* window. + // Some send algorithms may not use a congestion window and will return 0. + QuicPacketCount GetCongestionWindowInTcpMss() const { + return send_algorithm_->GetCongestionWindow() / kDefaultTCPMSS; + } + + // Returns the number of packets of length |max_packet_length| which fit in + // the current congestion window. More packets may end up in flight if the + // congestion window has been recently reduced, of if non-full packets are + // sent. + QuicPacketCount EstimateMaxPacketsInFlight( + QuicByteCount max_packet_length) const { + return send_algorithm_->GetCongestionWindow() / max_packet_length; + } + + // Returns the size of the current congestion window size in bytes. + QuicByteCount GetCongestionWindowInBytes() const { + return send_algorithm_->GetCongestionWindow(); + } + + // Returns the size of the slow start congestion window in nume of 1460 byte + // TCP segments, aka ssthresh. Some send algorithms do not define a slow + // start threshold and will return 0. + QuicPacketCount GetSlowStartThresholdInTcpMss() const { + return send_algorithm_->GetSlowStartThreshold() / kDefaultTCPMSS; + } + + // Returns debugging information about the state of the congestion controller. + QuicString GetDebugState() const; + + // Returns the number of bytes that are considered in-flight, i.e. not lost or + // acknowledged. + QuicByteCount GetBytesInFlight() const { + return unacked_packets_.bytes_in_flight(); + } + + // No longer retransmit data for |stream_id|. + void CancelRetransmissionsForStream(QuicStreamId stream_id); + + // Called when peer address changes and the connection migrates. + void OnConnectionMigration(AddressChangeType type); + + // Called when an ack frame is initially parsed. + void OnAckFrameStart(QuicPacketNumber largest_acked, + QuicTime::Delta ack_delay_time, + QuicTime ack_receive_time); + + // Called when ack range [start, end) is received. Populates packets_acked_ + // with newly acked packets. + void OnAckRange(QuicPacketNumber start, QuicPacketNumber end); + + // Called when a timestamp is processed. If it's present in packets_acked_, + // the timestamp field is set. Otherwise, the timestamp is ignored. + void OnAckTimestamp(QuicPacketNumber packet_number, QuicTime timestamp); + + // Called when an ack frame is parsed completely. Returns true if a previously + // -unacked packet is acked. + bool OnAckFrameEnd(QuicTime ack_receive_time); + + // Called to enable/disable letting session decide what to write. + void SetSessionDecideWhatToWrite(bool session_decides_what_to_write) { + unacked_packets_.SetSessionDecideWhatToWrite(session_decides_what_to_write); + } + + void SetDebugDelegate(DebugDelegate* debug_delegate); + + void SetPacingAlarmGranularity(QuicTime::Delta alarm_granularity) { + pacing_sender_.set_alarm_granularity(alarm_granularity); + } + + QuicPacketNumber GetLargestObserved() const { + return unacked_packets_.largest_acked(); + } + + QuicPacketNumber GetLargestSentPacket() const { + return unacked_packets_.largest_sent_packet(); + } + + void SetNetworkChangeVisitor(NetworkChangeVisitor* visitor) { + DCHECK(!network_change_visitor_); + DCHECK(visitor); + network_change_visitor_ = visitor; + } + + bool InSlowStart() const { return send_algorithm_->InSlowStart(); } + + size_t GetConsecutiveRtoCount() const { return consecutive_rto_count_; } + + size_t GetConsecutiveTlpCount() const { return consecutive_tlp_count_; } + + void OnApplicationLimited(); + + const SendAlgorithmInterface* GetSendAlgorithm() const { + return send_algorithm_.get(); + } + + void SetSessionNotifier(SessionNotifierInterface* session_notifier) { + unacked_packets_.SetSessionNotifier(session_notifier); + } + + QuicTime GetNextReleaseTime() const; + + QuicPacketCount initial_congestion_window() const { + return initial_congestion_window_; + } + + QuicPacketNumber largest_packet_peer_knows_is_acked() const { + return largest_packet_peer_knows_is_acked_; + } + + bool handshake_confirmed() const { return handshake_confirmed_; } + + bool session_decides_what_to_write() const { + return unacked_packets_.session_decides_what_to_write(); + } + + size_t pending_timer_transmission_count() const { + return pending_timer_transmission_count_; + } + + QuicTime::Delta delayed_ack_time() const { return delayed_ack_time_; } + + void set_delayed_ack_time(QuicTime::Delta delayed_ack_time) { + // The delayed ack time should never be more than one half the min RTO time. + DCHECK_LE(delayed_ack_time, (min_rto_timeout_ * 0.5)); + delayed_ack_time_ = delayed_ack_time; + } + + const QuicUnackedPacketMap& unacked_packets() const { + return unacked_packets_; + } + + // Sets the send algorithm to the given congestion control type and points the + // pacing sender at |send_algorithm_|. Can be called any number of times. + void SetSendAlgorithm(CongestionControlType congestion_control_type); + + // Sets the send algorithm to |send_algorithm| and points the pacing sender at + // |send_algorithm_|. Takes ownership of |send_algorithm|. Can be called any + // number of times. + // Setting the send algorithm once the connection is underway is dangerous. + void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm); + + private: + friend class test::QuicConnectionPeer; + friend class test::QuicSentPacketManagerPeer; + + // The retransmission timer is a single timer which switches modes depending + // upon connection state. + enum RetransmissionTimeoutMode { + // A conventional TCP style RTO. + RTO_MODE, + // A tail loss probe. By default, QUIC sends up to two before RTOing. + TLP_MODE, + // Retransmission of handshake packets prior to handshake completion. + HANDSHAKE_MODE, + // Re-invoke the loss detection when a packet is not acked before the + // loss detection algorithm expects. + LOSS_MODE, + }; + + typedef QuicLinkedHashMap<QuicPacketNumber, + TransmissionType, + QuicPacketNumberHash> + PendingRetransmissionMap; + + // Returns the current retransmission mode. + RetransmissionTimeoutMode GetRetransmissionMode() const; + + // Retransmits all crypto stream packets. + void RetransmitCryptoPackets(); + + // Retransmits two packets for an RTO and removes any non-retransmittable + // packets from flight. + void RetransmitRtoPackets(); + + // Returns the timeout for retransmitting crypto handshake packets. + const QuicTime::Delta GetCryptoRetransmissionDelay() const; + + // Returns the timeout for a new tail loss probe. |consecutive_tlp_count| is + // the number of consecutive tail loss probes that have already been sent. + const QuicTime::Delta GetTailLossProbeDelay( + size_t consecutive_tlp_count) const; + + // Calls GetTailLossProbeDelay() with values from the current state of this + // packet manager as its params. + const QuicTime::Delta GetTailLossProbeDelay() const { + return GetTailLossProbeDelay(consecutive_tlp_count_); + } + + // Returns the retransmission timeout, after which a full RTO occurs. + // |consecutive_rto_count| is the number of consecutive RTOs that have already + // occurred. + const QuicTime::Delta GetRetransmissionDelay( + size_t consecutive_rto_count) const; + + // Calls GetRetransmissionDelay() with values from the current state of this + // packet manager as its params. + const QuicTime::Delta GetRetransmissionDelay() const { + return GetRetransmissionDelay(consecutive_rto_count_); + } + + // Returns the newest transmission associated with a packet. + QuicPacketNumber GetNewestRetransmission( + QuicPacketNumber packet_number, + const QuicTransmissionInfo& transmission_info) const; + + // Update the RTT if the ack is for the largest acked packet number. + // Returns true if the rtt was updated. + bool MaybeUpdateRTT(QuicPacketNumber largest_acked, + QuicTime::Delta ack_delay_time, + QuicTime ack_receive_time); + + // Invokes the loss detection algorithm and loses and retransmits packets if + // necessary. + void InvokeLossDetection(QuicTime time); + + // Invokes OnCongestionEvent if |rtt_updated| is true, there are pending acks, + // or pending losses. Clears pending acks and pending losses afterwards. + // |prior_in_flight| is the number of bytes in flight before the losses or + // acks, |event_time| is normally the timestamp of the ack packet which caused + // the event, although it can be the time at which loss detection was + // triggered. + void MaybeInvokeCongestionEvent(bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time); + + // Removes the retransmittability and in flight properties from the packet at + // |info| due to receipt by the peer. + void MarkPacketHandled(QuicPacketNumber packet_number, + QuicTransmissionInfo* info, + QuicTime::Delta ack_delay_time); + + // Request that |packet_number| be retransmitted after the other pending + // retransmissions. Does not add it to the retransmissions if it's already + // a pending retransmission. + void MarkForRetransmission(QuicPacketNumber packet_number, + TransmissionType transmission_type); + + // Performs whatever work is need to retransmit the data correctly, either + // by retransmitting the frames directly or by notifying that the frames + // are lost. + void HandleRetransmission(TransmissionType transmission_type, + QuicTransmissionInfo* transmission_info); + + // Called after packets have been marked handled with last received ack frame. + void PostProcessAfterMarkingPacketHandled( + const QuicAckFrame& ack_frame, + QuicTime ack_receive_time, + bool rtt_updated, + QuicByteCount prior_bytes_in_flight); + + // Notify observers that packet with QuicTransmissionInfo |info| is a spurious + // retransmission. It is caller's responsibility to guarantee the packet with + // QuicTransmissionInfo |info| is a spurious retransmission before calling + // this function. + void RecordOneSpuriousRetransmission(const QuicTransmissionInfo& info); + + // Notify observers about spurious retransmits of packet with + // QuicTransmissionInfo |info|. + void RecordSpuriousRetransmissions(const QuicTransmissionInfo& info, + QuicPacketNumber acked_packet_number); + + // Sets the initial RTT of the connection. + void SetInitialRtt(QuicTime::Delta rtt); + + // Called when handshake is confirmed to remove the retransmittable frames + // from all packets of HANDSHAKE_DATA packet number space to ensure they don't + // get retransmitted and will eventually be removed from unacked packets map. + // Only used when quic_use_uber_loss_algorithm is true. Please note, this only + // applies to QUIC Crypto and needs to be changed when switches to IETF QUIC + // with QUIC TLS. + void NeuterHandshakePackets(); + + // Newly serialized retransmittable packets are added to this map, which + // contains owning pointers to any contained frames. If a packet is + // retransmitted, this map will contain entries for both the old and the new + // packet. The old packet's retransmittable frames entry will be nullptr, + // while the new packet's entry will contain the frames to retransmit. + // If the old packet is acked before the new packet, then the old entry will + // be removed from the map and the new entry's retransmittable frames will be + // set to nullptr. + QuicUnackedPacketMap unacked_packets_; + + // Pending retransmissions which have not been packetized and sent yet. + PendingRetransmissionMap pending_retransmissions_; + + const QuicClock* clock_; + QuicConnectionStats* stats_; + + DebugDelegate* debug_delegate_; + NetworkChangeVisitor* network_change_visitor_; + QuicPacketCount initial_congestion_window_; + RttStats rtt_stats_; + std::unique_ptr<SendAlgorithmInterface> send_algorithm_; + // Not owned. Always points to |general_loss_algorithm_| outside of tests. + LossDetectionInterface* loss_algorithm_; + // TODO(fayang): Remove general_loss_algorithm_ when deprecating + // quic_use_uber_loss_algorithm. + GeneralLossAlgorithm general_loss_algorithm_; + UberLossAlgorithm uber_loss_algorithm_; + + // Tracks the first RTO packet. If any packet before that packet gets acked, + // it indicates the RTO was spurious and should be reversed(F-RTO). + QuicPacketNumber first_rto_transmission_; + // Number of times the RTO timer has fired in a row without receiving an ack. + size_t consecutive_rto_count_; + // Number of times the tail loss probe has been sent. + size_t consecutive_tlp_count_; + // Number of times the crypto handshake has been retransmitted. + size_t consecutive_crypto_retransmission_count_; + // Number of pending transmissions of TLP, RTO, or crypto packets. + size_t pending_timer_transmission_count_; + // Maximum number of tail loss probes to send before firing an RTO. + size_t max_tail_loss_probes_; + // Maximum number of packets to send upon RTO. + QuicPacketCount max_rto_packets_; + // If true, send the TLP at 0.5 RTT. + bool enable_half_rtt_tail_loss_probe_; + bool using_pacing_; + // If true, use the new RTO with loss based CWND reduction instead of the send + // algorithms's OnRetransmissionTimeout to reduce the congestion window. + bool use_new_rto_; + // If true, use a more conservative handshake retransmission policy. + bool conservative_handshake_retransmits_; + // The minimum TLP timeout. + QuicTime::Delta min_tlp_timeout_; + // The minimum RTO. + QuicTime::Delta min_rto_timeout_; + // Whether to use IETF style TLP that includes the max ack delay. + bool ietf_style_tlp_; + // IETF style TLP, but with a 2x multiplier instead of 1.5x. + bool ietf_style_2x_tlp_; + + // Vectors packets acked and lost as a result of the last congestion event. + AckedPacketVector packets_acked_; + LostPacketVector packets_lost_; + // Largest newly acknowledged packet. + QuicPacketNumber largest_newly_acked_; + // Largest packet in bytes ever acknowledged. + QuicPacketLength largest_mtu_acked_; + + // Replaces certain calls to |send_algorithm_| when |using_pacing_| is true. + // Calls into |send_algorithm_| for the underlying congestion control. + PacingSender pacing_sender_; + + // Set to true after the crypto handshake has successfully completed. After + // this is true we no longer use HANDSHAKE_MODE, and further frames sent on + // the crypto stream (i.e. SCUP messages) are treated like normal + // retransmittable frames. + bool handshake_confirmed_; + + // Records bandwidth from server to client in normal operation, over periods + // of time with no loss events. + QuicSustainedBandwidthRecorder sustained_bandwidth_recorder_; + + // The largest acked value that was sent in an ack, which has then been acked. + QuicPacketNumber largest_packet_peer_knows_is_acked_; + + // The maximum amount of time to wait before sending an acknowledgement. + // The recovery code assumes the delayed ack time is the same on both sides. + QuicTime::Delta delayed_ack_time_; + + // Latest received ack frame. + QuicAckFrame last_ack_frame_; + + // Record whether RTT gets updated by last largest acked.. + bool rtt_updated_; + + // A reverse iterator of last_ack_frame_.packets. This is reset in + // OnAckRangeStart, and gradually moves in OnAckRange.. + PacketNumberQueue::const_reverse_iterator acked_packets_iter_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_SENT_PACKET_MANAGER_H_
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc new file mode 100644 index 0000000..eb3c6a8 --- /dev/null +++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -0,0 +1,2438 @@ +// Copyright 2013 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/quic_sent_packet_manager.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::AnyNumber; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::IsEmpty; +using testing::Not; +using testing::Pointwise; +using testing::Return; +using testing::StrictMock; +using testing::WithArgs; + +namespace quic { +namespace test { +namespace { +// Default packet length. +const uint32_t kDefaultLength = 1000; + +// Stream ID for data sent in CreatePacket(). +const QuicStreamId kStreamId = 7; + +// Matcher to check that the packet number matches the second argument. +MATCHER(PacketNumberEq, "") { + return ::testing::get<0>(arg).packet_number == + QuicPacketNumber(::testing::get<1>(arg)); +} + +class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate { + public: + MOCK_METHOD2(OnSpuriousPacketRetransmission, + void(TransmissionType transmission_type, + QuicByteCount byte_size)); + MOCK_METHOD3(OnPacketLoss, + void(QuicPacketNumber lost_packet_number, + TransmissionType transmission_type, + QuicTime detection_time)); +}; + +class QuicSentPacketManagerTest : public QuicTestWithParam<bool> { + public: + void RetransmitCryptoPacket(uint64_t packet_number) { + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number), + kDefaultLength, HAS_RETRANSMITTABLE_DATA)); + SerializedPacket packet(CreatePacket(packet_number, false)); + packet.retransmittable_frames.push_back( + QuicFrame(QuicStreamFrame(1, false, 0, QuicStringPiece()))); + packet.has_crypto_handshake = IS_HANDSHAKE; + manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), + HANDSHAKE_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + } + + void RetransmitDataPacket(uint64_t packet_number, TransmissionType type) { + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number), + kDefaultLength, HAS_RETRANSMITTABLE_DATA)); + SerializedPacket packet(CreatePacket(packet_number, true)); + manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), type, + HAS_RETRANSMITTABLE_DATA); + } + + protected: + QuicSentPacketManagerTest() + : manager_(Perspective::IS_SERVER, &clock_, &stats_, kCubicBytes, kNack), + send_algorithm_(new StrictMock<MockSendAlgorithm>), + network_change_visitor_(new StrictMock<MockNetworkChangeVisitor>) { + QuicSentPacketManagerPeer::SetSendAlgorithm(&manager_, send_algorithm_); + // Disable tail loss probes for most tests. + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 0); + // Advance the time 1s so the send times are never QuicTime::Zero. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); + manager_.SetNetworkChangeVisitor(network_change_visitor_.get()); + manager_.SetSessionNotifier(¬ifier_); + manager_.SetSessionDecideWhatToWrite(GetParam()); + + EXPECT_CALL(*send_algorithm_, HasReliableBandwidthEstimate()) + .Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()) + .Times(AnyNumber()) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber()); + EXPECT_CALL(*network_change_visitor_, OnPathMtuIncreased(1000)) + .Times(AnyNumber()); + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(notifier_, HasUnackedCryptoData()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(notifier_, OnStreamFrameRetransmitted(_)).Times(AnyNumber()); + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillRepeatedly(Return(true)); + } + + ~QuicSentPacketManagerTest() override {} + + QuicByteCount BytesInFlight() { + return QuicSentPacketManagerPeer::GetBytesInFlight(&manager_); + } + void VerifyUnackedPackets(uint64_t* packets, size_t num_packets) { + if (num_packets == 0) { + EXPECT_TRUE(manager_.unacked_packets().empty()); + EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets( + &manager_)); + return; + } + + EXPECT_FALSE(manager_.unacked_packets().empty()); + EXPECT_EQ(QuicPacketNumber(packets[0]), manager_.GetLeastUnacked()); + for (size_t i = 0; i < num_packets; ++i) { + EXPECT_TRUE(QuicSentPacketManagerPeer::IsUnacked(&manager_, packets[i])) + << packets[i]; + } + } + + void VerifyRetransmittablePackets(uint64_t* packets, size_t num_packets) { + EXPECT_EQ( + num_packets, + QuicSentPacketManagerPeer::GetNumRetransmittablePackets(&manager_)); + for (size_t i = 0; i < num_packets; ++i) { + EXPECT_TRUE(QuicSentPacketManagerPeer::HasRetransmittableFrames( + &manager_, packets[i])) + << " packets[" << i << "]:" << packets[i]; + } + } + + void ExpectAck(uint64_t largest_observed) { + EXPECT_CALL( + *send_algorithm_, + // Ensure the AckedPacketVector argument contains largest_observed. + OnCongestionEvent(true, _, _, + Pointwise(PacketNumberEq(), {largest_observed}), + IsEmpty())); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + } + + void ExpectUpdatedRtt(uint64_t largest_observed) { + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent(true, _, _, IsEmpty(), IsEmpty())); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + } + + void ExpectAckAndLoss(bool rtt_updated, + uint64_t largest_observed, + uint64_t lost_packet) { + EXPECT_CALL( + *send_algorithm_, + OnCongestionEvent(rtt_updated, _, _, + Pointwise(PacketNumberEq(), {largest_observed}), + Pointwise(PacketNumberEq(), {lost_packet}))); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + } + + // |packets_acked| and |packets_lost| should be in packet number order. + void ExpectAcksAndLosses(bool rtt_updated, + uint64_t* packets_acked, + size_t num_packets_acked, + uint64_t* packets_lost, + size_t num_packets_lost) { + std::vector<QuicPacketNumber> ack_vector; + for (size_t i = 0; i < num_packets_acked; ++i) { + ack_vector.push_back(QuicPacketNumber(packets_acked[i])); + } + std::vector<QuicPacketNumber> lost_vector; + for (size_t i = 0; i < num_packets_lost; ++i) { + lost_vector.push_back(QuicPacketNumber(packets_lost[i])); + } + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent(rtt_updated, _, _, + Pointwise(PacketNumberEq(), ack_vector), + Pointwise(PacketNumberEq(), lost_vector))); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()) + .Times(AnyNumber()); + } + + void RetransmitAndSendPacket(uint64_t old_packet_number, + uint64_t new_packet_number) { + RetransmitAndSendPacket(old_packet_number, new_packet_number, + TLP_RETRANSMISSION); + } + + void RetransmitAndSendPacket(uint64_t old_packet_number, + uint64_t new_packet_number, + TransmissionType transmission_type) { + bool is_lost = false; + if (manager_.session_decides_what_to_write()) { + if (transmission_type == HANDSHAKE_RETRANSMISSION || + transmission_type == TLP_RETRANSMISSION || + transmission_type == RTO_RETRANSMISSION || + transmission_type == PROBING_RETRANSMISSION) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>( + Invoke([this, new_packet_number](TransmissionType type) { + RetransmitDataPacket(new_packet_number, type); + }))); + } else { + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1); + is_lost = true; + } + } + QuicSentPacketManagerPeer::MarkForRetransmission( + &manager_, old_packet_number, transmission_type); + if (manager_.session_decides_what_to_write()) { + if (!is_lost) { + return; + } + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, BytesInFlight(), QuicPacketNumber(new_packet_number), + kDefaultLength, HAS_RETRANSMITTABLE_DATA)); + SerializedPacket packet(CreatePacket(new_packet_number, true)); + manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), + transmission_type, HAS_RETRANSMITTABLE_DATA); + return; + } + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + QuicPendingRetransmission next_retransmission = + manager_.NextPendingRetransmission(); + EXPECT_EQ(QuicPacketNumber(old_packet_number), + next_retransmission.packet_number); + EXPECT_EQ(transmission_type, next_retransmission.transmission_type); + + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, BytesInFlight(), QuicPacketNumber(new_packet_number), + kDefaultLength, HAS_RETRANSMITTABLE_DATA)); + SerializedPacket packet(CreatePacket(new_packet_number, false)); + manager_.OnPacketSent(&packet, QuicPacketNumber(old_packet_number), + clock_.Now(), transmission_type, + HAS_RETRANSMITTABLE_DATA); + EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, + new_packet_number)); + } + + SerializedPacket CreateDataPacket(uint64_t packet_number) { + return CreatePacket(packet_number, true); + } + + SerializedPacket CreatePacket(uint64_t packet_number, bool retransmittable) { + SerializedPacket packet(QuicPacketNumber(packet_number), + PACKET_4BYTE_PACKET_NUMBER, nullptr, kDefaultLength, + false, false); + if (retransmittable) { + packet.retransmittable_frames.push_back( + QuicFrame(QuicStreamFrame(kStreamId, false, 0, QuicStringPiece()))); + } + return packet; + } + + void SendDataPacket(uint64_t packet_number) { + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, BytesInFlight(), + QuicPacketNumber(packet_number), _, _)); + SerializedPacket packet(CreateDataPacket(packet_number)); + manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + } + + void SendCryptoPacket(uint64_t packet_number) { + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number), + kDefaultLength, HAS_RETRANSMITTABLE_DATA)); + SerializedPacket packet(CreatePacket(packet_number, false)); + packet.retransmittable_frames.push_back( + QuicFrame(QuicStreamFrame(1, false, 0, QuicStringPiece()))); + packet.has_crypto_handshake = IS_HANDSHAKE; + manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, HasUnackedCryptoData()) + .WillRepeatedly(Return(true)); + } + } + + void SendAckPacket(uint64_t packet_number, uint64_t largest_acked) { + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number), + kDefaultLength, NO_RETRANSMITTABLE_DATA)); + SerializedPacket packet(CreatePacket(packet_number, false)); + packet.largest_acked = QuicPacketNumber(largest_acked); + manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), + NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA); + } + + // Based on QuicConnection's WritePendingRetransmissions. + void RetransmitNextPacket(uint64_t retransmission_packet_number) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_CALL( + *send_algorithm_, + OnPacketSent(_, _, QuicPacketNumber(retransmission_packet_number), + kDefaultLength, HAS_RETRANSMITTABLE_DATA)); + const QuicPendingRetransmission pending = + manager_.NextPendingRetransmission(); + SerializedPacket packet(CreatePacket(retransmission_packet_number, false)); + manager_.OnPacketSent(&packet, pending.packet_number, clock_.Now(), + pending.transmission_type, HAS_RETRANSMITTABLE_DATA); + } + + QuicSentPacketManager manager_; + MockClock clock_; + QuicConnectionStats stats_; + MockSendAlgorithm* send_algorithm_; + std::unique_ptr<MockNetworkChangeVisitor> network_change_visitor_; + StrictMock<MockSessionNotifier> notifier_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, QuicSentPacketManagerTest, testing::Bool()); + +TEST_P(QuicSentPacketManagerTest, IsUnacked) { + VerifyUnackedPackets(nullptr, 0); + SendDataPacket(1); + + uint64_t unacked[] = {1}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + uint64_t retransmittable[] = {1}; + VerifyRetransmittablePackets(retransmittable, + QUIC_ARRAYSIZE(retransmittable)); +} + +TEST_P(QuicSentPacketManagerTest, IsUnAckedRetransmit) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + + EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, 2)); + uint64_t unacked[] = {1, 2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + std::vector<uint64_t> retransmittable; + if (manager_.session_decides_what_to_write()) { + retransmittable = {1, 2}; + } else { + retransmittable = {2}; + } + VerifyRetransmittablePackets(&retransmittable[0], retransmittable.size()); +} + +TEST_P(QuicSentPacketManagerTest, RetransmitThenAck) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + + // Ack 2 but not 1. + ExpectAck(2); + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + } + // Packet 1 is unacked, pending, but not retransmittable. + uint64_t unacked[] = {1}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(nullptr, 0); +} + +TEST_P(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) { + SendDataPacket(1); + if (manager_.session_decides_what_to_write()) { + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(2, type); + }))); + } + } + QuicSentPacketManagerPeer::MarkForRetransmission(&manager_, 1, + TLP_RETRANSMISSION); + if (!manager_.session_decides_what_to_write()) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + } + // Ack 1. + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + // There should no longer be a pending retransmission. + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + uint64_t unacked[] = {2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + // We do not know packet 2 is a spurious retransmission until it gets acked. + } else { + // No unacked packets remain. + VerifyUnackedPackets(nullptr, 0); + } + VerifyRetransmittablePackets(nullptr, 0); + EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted); +} + +TEST_P(QuicSentPacketManagerTest, RetransmitThenStopRetransmittingBeforeSend) { + SendDataPacket(1); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)); + } + QuicSentPacketManagerPeer::MarkForRetransmission(&manager_, 1, + TLP_RETRANSMISSION); + if (!manager_.session_decides_what_to_write()) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + } + + manager_.CancelRetransmissionsForStream(kStreamId); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + } + + // There should no longer be a pending retransmission. + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + uint64_t unacked[] = {1}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); + EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted); +} + +TEST_P(QuicSentPacketManagerTest, RetransmitThenAckPrevious) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15); + clock_.AdvanceTime(rtt); + + // Ack 1 but not 2. + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + } + // 2 remains unacked, but no packets have retransmittable data. + uint64_t unacked[] = {2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(nullptr, 0); + if (manager_.session_decides_what_to_write()) { + // Ack 2 causes 2 be considered as spurious retransmission. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillOnce(Return(false)); + ExpectAck(2); + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + } + + EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted); +} + +TEST_P(QuicSentPacketManagerTest, RetransmitThenAckPreviousThenNackRetransmit) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15); + clock_.AdvanceTime(rtt); + + // First, ACK packet 1 which makes packet 2 non-retransmittable. + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + SendDataPacket(3); + SendDataPacket(4); + SendDataPacket(5); + clock_.AdvanceTime(rtt); + + // Next, NACK packet 2 three times. + ExpectAck(3); + manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + ExpectAck(4); + manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + ExpectAckAndLoss(true, 5, 2); + if (manager_.session_decides_what_to_write()) { + // Frames in all packets are acked. + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + // Notify session that stream frame in packet 2 gets lost although it is + // not outstanding. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1); + } + manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + if (manager_.session_decides_what_to_write()) { + uint64_t unacked[] = {2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + } else { + // No packets remain unacked. + VerifyUnackedPackets(nullptr, 0); + } + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(nullptr, 0); + + // Verify that the retransmission alarm would not fire, + // since there is no retransmittable data outstanding. + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, + DISABLED_RetransmitTwiceThenAckPreviousBeforeSend) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + + // Fire the RTO, which will mark 2 for retransmission (but will not send it). + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.OnRetransmissionTimeout(); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + + // Ack 1 but not 2, before 2 is able to be sent. + // Since 1 has been retransmitted, it has already been lost, and so the + // send algorithm is not informed that it has been ACK'd. + ExpectUpdatedRtt(1); + EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout()); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + // Since 2 was marked for retransmit, when 1 is acked, 2 is kept for RTT. + uint64_t unacked[] = {2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(nullptr, 0); + + // Verify that the retransmission alarm would not fire, + // since there is no retransmittable data outstanding. + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, RetransmitTwiceThenAckFirst) { + StrictMock<MockDebugDelegate> debug_delegate; + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmission( + TLP_RETRANSMISSION, kDefaultLength)) + .Times(1); + } else { + EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmission( + TLP_RETRANSMISSION, kDefaultLength)) + .Times(2); + } + manager_.SetDebugDelegate(&debug_delegate); + + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + RetransmitAndSendPacket(2, 3); + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15); + clock_.AdvanceTime(rtt); + + // Ack 1 but not 2 or 3. + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + if (manager_.session_decides_what_to_write()) { + // Frames in packets 2 and 3 are acked. + EXPECT_CALL(notifier_, IsFrameOutstanding(_)) + .Times(2) + .WillRepeatedly(Return(false)); + } + + // 2 and 3 remain unacked, but no packets have retransmittable data. + uint64_t unacked[] = {2, 3}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(nullptr, 0); + + // Ensure packet 2 is lost when 4 is sent and 3 and 4 are acked. + SendDataPacket(4); + if (manager_.session_decides_what_to_write()) { + // No new data gets acked in packet 3. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true)); + } + uint64_t acked[] = {3, 4}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + uint64_t unacked2[] = {2}; + VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + + SendDataPacket(5); + ExpectAckAndLoss(true, 5, 2); + EXPECT_CALL(debug_delegate, + OnPacketLoss(QuicPacketNumber(2), LOSS_RETRANSMISSION, _)); + if (manager_.session_decides_what_to_write()) { + // Frames in all packets are acked. + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + // Notify session that stream frame in packet 2 gets lost although it is + // not outstanding. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1); + } + manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + if (manager_.session_decides_what_to_write()) { + uint64_t unacked[] = {2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + } else { + VerifyUnackedPackets(nullptr, 0); + } + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + if (manager_.session_decides_what_to_write()) { + // Spurious retransmission is detected when packet 3 gets acked. We cannot + // know packet 2 is a spurious until it gets acked. + EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted); + } else { + EXPECT_EQ(2u, stats_.packets_spuriously_retransmitted); + } +} + +TEST_P(QuicSentPacketManagerTest, AckOriginalTransmission) { + auto loss_algorithm = QuicMakeUnique<MockLossAlgorithm>(); + QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm.get()); + + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + + // Ack original transmission, but that wasn't lost via fast retransmit, + // so no call on OnSpuriousRetransmission is expected. + { + ExpectAck(1); + EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _)); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + } + + SendDataPacket(3); + SendDataPacket(4); + // Ack 4, which causes 3 to be retransmitted. + { + ExpectAck(4); + EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _)); + manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(5)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION); + } + + // Ack 3, which causes SpuriousRetransmitDetected to be called. + { + uint64_t acked[] = {3}; + ExpectAcksAndLosses(false, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(*loss_algorithm, + SpuriousRetransmitDetected(_, _, _, QuicPacketNumber(5))); + manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + if (manager_.session_decides_what_to_write()) { + // Ack 3 will not cause 5 be considered as a spurious retransmission. Ack + // 5 will cause 5 be considered as a spurious retransmission as no new + // data gets acked. + ExpectAck(5); + EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _)); + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillOnce(Return(false)); + manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6)); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + } + } +} + +TEST_P(QuicSentPacketManagerTest, GetLeastUnacked) { + EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked()); +} + +TEST_P(QuicSentPacketManagerTest, GetLeastUnackedUnacked) { + SendDataPacket(1); + EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked()); +} + +TEST_P(QuicSentPacketManagerTest, AckAckAndUpdateRtt) { + EXPECT_FALSE(manager_.largest_packet_peer_knows_is_acked().IsInitialized()); + SendDataPacket(1); + SendAckPacket(2, 1); + + // Now ack the ack and expect an RTT update. + uint64_t acked[] = {1, 2}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(2), + QuicTime::Delta::FromMilliseconds(5), clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + EXPECT_EQ(QuicPacketNumber(1), manager_.largest_packet_peer_knows_is_acked()); + + SendAckPacket(3, 3); + + // Now ack the ack and expect only an RTT update. + uint64_t acked2[] = {3}; + ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(4)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + EXPECT_EQ(QuicPacketNumber(3u), + manager_.largest_packet_peer_knows_is_acked()); +} + +TEST_P(QuicSentPacketManagerTest, Rtt) { + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(20); + SendDataPacket(1); + clock_.AdvanceTime(expected_rtt); + + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt()); +} + +TEST_P(QuicSentPacketManagerTest, RttWithInvalidDelta) { + // Expect that the RTT is equal to the local time elapsed, since the + // ack_delay_time is larger than the local time elapsed + // and is hence invalid. + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); + SendDataPacket(1); + clock_.AdvanceTime(expected_rtt); + + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), + QuicTime::Delta::FromMilliseconds(11), clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt()); +} + +TEST_P(QuicSentPacketManagerTest, RttWithInfiniteDelta) { + // Expect that the RTT is equal to the local time elapsed, since the + // ack_delay_time is infinite, and is hence invalid. + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); + SendDataPacket(1); + clock_.AdvanceTime(expected_rtt); + + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt()); +} + +TEST_P(QuicSentPacketManagerTest, RttZeroDelta) { + // Expect that the RTT is the time between send and receive since the + // ack_delay_time is zero. + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); + SendDataPacket(1); + clock_.AdvanceTime(expected_rtt); + + ExpectAck(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Zero(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt()); +} + +TEST_P(QuicSentPacketManagerTest, TailLossProbeTimeout) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2); + + // Send 1 packet. + SendDataPacket(1); + + // The first tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(2, type); }))); + } + manager_.MaybeRetransmitTailLossProbe(); + if (!manager_.session_decides_what_to_write()) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(2); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + // The second tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(3, type); }))); + } + manager_.MaybeRetransmitTailLossProbe(); + if (!manager_.session_decides_what_to_write()) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(3); + } + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false)); + EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + // Ack the third and ensure the first two are still pending. + ExpectAck(3); + + manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + + // Acking two more packets will lose both of them due to nacks. + SendDataPacket(4); + SendDataPacket(5); + uint64_t acked[] = {4, 5}; + uint64_t lost[] = {1, 2}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), lost, + QUIC_ARRAYSIZE(lost)); + if (manager_.session_decides_what_to_write()) { + // Frames in all packets are acked. + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + // Notify session that stream frame in packets 1 and 2 get lost although + // they are not outstanding. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2); + } + manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + EXPECT_EQ(2u, stats_.tlp_count); + EXPECT_EQ(0u, stats_.rto_count); +} + +TEST_P(QuicSentPacketManagerTest, TailLossProbeThenRTO) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2); + + // Send 100 packets. + const size_t kNumSentPackets = 100; + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + QuicTime rto_packet_time = clock_.Now(); + // Advance the time. + clock_.AdvanceTime(manager_.GetRetransmissionTime() - clock_.Now()); + + // The first tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(101, type); + }))); + } + manager_.MaybeRetransmitTailLossProbe(); + if (!manager_.session_decides_what_to_write()) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(101); + } + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false)); + EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + clock_.AdvanceTime(manager_.GetRetransmissionTime() - clock_.Now()); + + // The second tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(102, type); + }))); + } + EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe()); + if (!manager_.session_decides_what_to_write()) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(102); + } + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false)); + EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now())); + + // Ensure the RTO is set based on the correct packet. + rto_packet_time = clock_.Now(); + EXPECT_EQ(rto_packet_time + QuicTime::Delta::FromMilliseconds(500), + manager_.GetRetransmissionTime()); + + // Advance the time enough to ensure all packets are RTO'd. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); + + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(103, type); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(104, type); + }))); + } + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(2u, stats_.tlp_count); + EXPECT_EQ(1u, stats_.rto_count); + if (manager_.session_decides_what_to_write()) { + // There are 2 RTO retransmissions. + EXPECT_EQ(104 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } + if (!manager_.session_decides_what_to_write()) { + // Send and Ack the RTO and ensure OnRetransmissionTimeout is called. + EXPECT_EQ(102 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(103); + } + QuicPacketNumber largest_acked = QuicPacketNumber(103); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent( + true, _, _, Pointwise(PacketNumberEq(), {largest_acked}), _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + if (manager_.session_decides_what_to_write()) { + // Although frames in packet 3 gets acked, it would be kept for another + // RTT. + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true)); + // Packets [1, 102] are lost, although stream frame in packet 3 is not + // outstanding. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(102); + } + manager_.OnAckFrameStart(QuicPacketNumber(103), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(103), QuicPacketNumber(104)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + // All packets before 103 should be lost. + if (manager_.session_decides_what_to_write()) { + // Packet 104 is still in flight. + EXPECT_EQ(1000u, QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } +} + +TEST_P(QuicSentPacketManagerTest, CryptoHandshakeTimeout) { + // Send 2 crypto packets and 3 data packets. + const size_t kNumSentCryptoPackets = 2; + for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) { + SendCryptoPacket(i); + } + const size_t kNumSentDataPackets = 3; + for (size_t i = 1; i <= kNumSentDataPackets; ++i) { + SendDataPacket(kNumSentCryptoPackets + i); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // The first retransmits 2 packets. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(6); })) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(7); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + RetransmitNextPacket(6); + RetransmitNextPacket(7); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // The second retransmits 2 packets. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(8); })) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(9); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + RetransmitNextPacket(8); + RetransmitNextPacket(9); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Now ack the two crypto packets and the speculatively encrypted request, + // and ensure the first four crypto packets get abandoned, but not lost. + uint64_t acked[] = {3, 4, 5, 8, 9}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, HasUnackedCryptoData()) + .WillRepeatedly(Return(false)); + } + manager_.OnAckFrameStart(QuicPacketNumber(9), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(8), QuicPacketNumber(10)); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, CryptoHandshakeTimeoutVersionNegotiation) { + // Send 2 crypto packets and 3 data packets. + const size_t kNumSentCryptoPackets = 2; + for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) { + SendCryptoPacket(i); + } + const size_t kNumSentDataPackets = 3; + for (size_t i = 1; i <= kNumSentDataPackets; ++i) { + SendDataPacket(kNumSentCryptoPackets + i); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(6); })) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(7); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(6); + RetransmitNextPacket(7); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Now act like a version negotiation packet arrived, which would cause all + // unacked packets to be retransmitted. + if (manager_.session_decides_what_to_write()) { + // Mark packets [1, 7] lost. And the frames in 6 and 7 are same as packets 1 + // and 2, respectively. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(7); + } + manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); + + // Ensure the first two pending packets are the crypto retransmits. + if (manager_.session_decides_what_to_write()) { + RetransmitCryptoPacket(8); + RetransmitCryptoPacket(9); + RetransmitDataPacket(10, ALL_UNACKED_RETRANSMISSION); + RetransmitDataPacket(11, ALL_UNACKED_RETRANSMISSION); + RetransmitDataPacket(12, ALL_UNACKED_RETRANSMISSION); + } else { + ASSERT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(QuicPacketNumber(6u), + manager_.NextPendingRetransmission().packet_number); + RetransmitNextPacket(8); + EXPECT_EQ(QuicPacketNumber(7u), + manager_.NextPendingRetransmission().packet_number); + RetransmitNextPacket(9); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + // Send 3 more data packets and ensure the least unacked is raised. + RetransmitNextPacket(10); + RetransmitNextPacket(11); + RetransmitNextPacket(12); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked()); + // Least unacked isn't raised until an ack is received, so ack the + // crypto packets. + uint64_t acked[] = {8, 9}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(9), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(8), QuicPacketNumber(10)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, HasUnackedCryptoData()) + .WillRepeatedly(Return(false)); + } + EXPECT_EQ(QuicPacketNumber(10u), manager_.GetLeastUnacked()); +} + +TEST_P(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) { + // Send 1 crypto packet. + SendCryptoPacket(1); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Retransmit the crypto packet as 2. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(2); + } + + // Retransmit the crypto packet as 3. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(3); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(3); + } + + // Now ack the second crypto packet, and ensure the first gets removed, but + // the third does not. + uint64_t acked[] = {2}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, HasUnackedCryptoData()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + } + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + uint64_t unacked[] = {3}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); +} + +TEST_P(QuicSentPacketManagerTest, CryptoHandshakeTimeoutUnsentDataPacket) { + // Send 2 crypto packets and 1 data packet. + const size_t kNumSentCryptoPackets = 2; + for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) { + SendCryptoPacket(i); + } + SendDataPacket(3); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Retransmit 2 crypto packets, but not the serialized packet. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(4); })) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(5); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(4); + RetransmitNextPacket(5); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, + CryptoHandshakeRetransmissionThenRetransmitAll) { + // Send 1 crypto packet. + SendCryptoPacket(1); + + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Retransmit the crypto packet as 2. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(2); + } + // Now retransmit all the unacked packets, which occurs when there is a + // version negotiation. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2); + } + manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); + if (manager_.session_decides_what_to_write()) { + // Both packets 1 and 2 are unackable. + EXPECT_FALSE(QuicSentPacketManagerPeer::IsUnacked(&manager_, 1)); + EXPECT_FALSE(QuicSentPacketManagerPeer::IsUnacked(&manager_, 2)); + } else { + // Packet 2 is useful because it does not get retransmitted and still has + // retransmittable frames. + uint64_t unacked[] = {1, 2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, + CryptoHandshakeRetransmissionThenNeuterAndAck) { + // Send 1 crypto packet. + SendCryptoPacket(1); + + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Retransmit the crypto packet as 2. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(2); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Retransmit the crypto packet as 3. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(3); })); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(3); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Now neuter all unacked unencrypted packets, which occurs when the + // connection goes forward secure. + manager_.NeuterUnencryptedPackets(); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, HasUnackedCryptoData()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + } + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + uint64_t unacked[] = {1, 2, 3}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + + // Ensure both packets get discarded when packet 2 is acked. + uint64_t acked[] = {3}; + ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + VerifyUnackedPackets(nullptr, 0); + VerifyRetransmittablePackets(nullptr, 0); +} + +TEST_P(QuicSentPacketManagerTest, RetransmissionTimeout) { + StrictMock<MockDebugDelegate> debug_delegate; + manager_.SetDebugDelegate(&debug_delegate); + + // Send 100 packets. + const size_t kNumSentPackets = 100; + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + + EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(101, type); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(102, type); + }))); + } + manager_.OnRetransmissionTimeout(); + if (manager_.session_decides_what_to_write()) { + EXPECT_EQ(102 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + ASSERT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(100 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(101); + ASSERT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(102); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + // Ack a retransmission. + // Ensure no packets are lost. + QuicPacketNumber largest_acked = QuicPacketNumber(102); + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent(true, _, _, + Pointwise(PacketNumberEq(), {largest_acked}), + /*lost_packets=*/IsEmpty())); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + // RTO's use loss detection instead of immediately declaring retransmitted + // packets lost. + for (int i = 1; i <= 99; ++i) { + EXPECT_CALL(debug_delegate, + OnPacketLoss(QuicPacketNumber(i), LOSS_RETRANSMISSION, _)); + } + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true)); + // Packets [1, 99] are considered as lost, although stream frame in packet + // 2 is not outstanding. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99); + } + manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(102), QuicPacketNumber(103)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); +} + +TEST_P(QuicSentPacketManagerTest, RetransmissionTimeoutOnePacket) { + // Set the 1RTO connection option. + QuicConfig client_config; + QuicTagVector options; + options.push_back(k1RTO); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + manager_.SetFromConfig(client_config); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + + StrictMock<MockDebugDelegate> debug_delegate; + manager_.SetDebugDelegate(&debug_delegate); + + // Send 100 packets. + const size_t kNumSentPackets = 100; + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + + EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(1) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(101, type); + }))); + } + manager_.OnRetransmissionTimeout(); + if (manager_.session_decides_what_to_write()) { + EXPECT_EQ(101 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + ASSERT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(100 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(101); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } +} + +TEST_P(QuicSentPacketManagerTest, NewRetransmissionTimeout) { + QuicConfig client_config; + QuicTagVector options; + options.push_back(kNRTO); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + manager_.SetFromConfig(client_config); + EXPECT_TRUE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_)); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + + // Send 100 packets. + const size_t kNumSentPackets = 100; + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + + EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(101, type); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(102, type); + }))); + } + manager_.OnRetransmissionTimeout(); + if (manager_.session_decides_what_to_write()) { + EXPECT_EQ(102 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(100 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(101); + RetransmitNextPacket(102); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + // Ack a retransmission and expect no call to OnRetransmissionTimeout. + // This will include packets in the lost packet map. + QuicPacketNumber largest_acked = QuicPacketNumber(102); + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent(true, _, _, + Pointwise(PacketNumberEq(), {largest_acked}), + /*lost_packets=*/Not(IsEmpty()))); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true)); + // Packets [1, 99] are considered as lost, although stream frame in packet + // 2 is not outstanding. + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99); + } + manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(102), QuicPacketNumber(103)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); +} + +TEST_P(QuicSentPacketManagerTest, TwoRetransmissionTimeoutsAckSecond) { + // Send 1 packet. + SendDataPacket(1); + + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(2, type); }))); + } + manager_.OnRetransmissionTimeout(); + if (manager_.session_decides_what_to_write()) { + EXPECT_EQ(2 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(2); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + // Rto a second time. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(3, type); }))); + } + manager_.OnRetransmissionTimeout(); + if (manager_.session_decides_what_to_write()) { + EXPECT_EQ(3 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(2 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(3); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + // Ack a retransmission and ensure OnRetransmissionTimeout is called. + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + ExpectAck(2); + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Zero(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + // The original packet and newest should be outstanding. + EXPECT_EQ(2 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, TwoRetransmissionTimeoutsAckFirst) { + // Send 1 packet. + SendDataPacket(1); + + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(2, type); }))); + } + manager_.OnRetransmissionTimeout(); + if (manager_.session_decides_what_to_write()) { + EXPECT_EQ(2 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(2); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + // Rto a second time. + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(3, type); }))); + } + manager_.OnRetransmissionTimeout(); + if (manager_.session_decides_what_to_write()) { + EXPECT_EQ(3 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + } else { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(2 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(3); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + } + + // Ack a retransmission and ensure OnRetransmissionTimeout is called. + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + ExpectAck(3); + manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Zero(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + // The first two packets should still be outstanding. + EXPECT_EQ(2 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, GetTransmissionTime) { + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, GetTransmissionTimeCryptoHandshake) { + QuicTime crypto_packet_send_time = clock_.Now(); + SendCryptoPacket(1); + + // Check the min. + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(1)); + EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromMilliseconds(10), + manager_.GetRetransmissionTime()); + + // Test with a standard smoothed RTT. + rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100)); + + QuicTime::Delta srtt = rtt_stats->initial_rtt(); + QuicTime expected_time = clock_.Now() + 1.5 * srtt; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet by invoking the retransmission timeout. + clock_.AdvanceTime(1.5 * srtt); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); })); + // When session decides what to write, crypto_packet_send_time gets updated. + crypto_packet_send_time = clock_.Now(); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(2); + } + + // The retransmission time should now be twice as far in the future. + expected_time = crypto_packet_send_time + srtt * 2 * 1.5; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet for the 2nd time. + clock_.AdvanceTime(2 * 1.5 * srtt); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(3); })); + // When session decides what to write, crypto_packet_send_time gets updated. + crypto_packet_send_time = clock_.Now(); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(3); + } + + // Verify exponential backoff of the retransmission timeout. + expected_time = crypto_packet_send_time + srtt * 4 * 1.5; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, + GetConservativeTransmissionTimeCryptoHandshake) { + QuicConfig config; + QuicTagVector options; + options.push_back(kCONH); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + // Calling SetFromConfig requires mocking out some send algorithm methods. + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + + QuicTime crypto_packet_send_time = clock_.Now(); + SendCryptoPacket(1); + + // Check the min. + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(1)); + EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromMilliseconds(25), + manager_.GetRetransmissionTime()); + + // Test with a standard smoothed RTT. + rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100)); + + QuicTime::Delta srtt = rtt_stats->initial_rtt(); + QuicTime expected_time = clock_.Now() + 2 * srtt; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet by invoking the retransmission timeout. + clock_.AdvanceTime(2 * srtt); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); })); + crypto_packet_send_time = clock_.Now(); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(2); + } + + // The retransmission time should now be twice as far in the future. + expected_time = crypto_packet_send_time + srtt * 2 * 2; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, GetTransmissionTimeTailLossProbe) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2); + SendDataPacket(1); + SendDataPacket(2); + + // Check the min. + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(1)); + EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromMilliseconds(10), + manager_.GetRetransmissionTime()); + + // Test with a standard smoothed RTT. + rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100)); + QuicTime::Delta srtt = rtt_stats->initial_rtt(); + QuicTime::Delta expected_tlp_delay = 2 * srtt; + QuicTime expected_time = clock_.Now() + expected_tlp_delay; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet by invoking the retransmission timeout. + clock_.AdvanceTime(expected_tlp_delay); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(3, type); }))); + } + EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe()); + if (!manager_.session_decides_what_to_write()) { + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(3); + } + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false)); + EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + expected_time = clock_.Now() + expected_tlp_delay; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, GetTransmissionTimeSpuriousRTO) { + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + SendDataPacket(1); + SendDataPacket(2); + SendDataPacket(3); + SendDataPacket(4); + + QuicTime::Delta expected_rto_delay = + rtt_stats->smoothed_rtt() + 4 * rtt_stats->mean_deviation(); + QuicTime expected_time = clock_.Now() + expected_rto_delay; + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet by invoking the retransmission timeout. + clock_.AdvanceTime(expected_rto_delay); + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(5, type); }))) + .WillOnce(WithArgs<1>(Invoke( + [this](TransmissionType type) { RetransmitDataPacket(6, type); }))); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + // All packets are still considered inflight. + EXPECT_EQ(4 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + RetransmitNextPacket(5); + RetransmitNextPacket(6); + } + // All previous packets are inflight, plus two rto retransmissions. + EXPECT_EQ(6 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + // The delay should double the second time. + expected_time = clock_.Now() + expected_rto_delay + expected_rto_delay; + // Once we always base the timer on the right edge, leaving the older packets + // in flight doesn't change the timeout. + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Ack a packet before the first RTO and ensure the RTO timeout returns to the + // original value and OnRetransmissionTimeout is not called or reverted. + ExpectAck(2); + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(5 * kDefaultLength, + QuicSentPacketManagerPeer::GetBytesInFlight(&manager_)); + + // Wait 2RTTs from now for the RTO, since it's the max of the RTO time + // and the TLP time. In production, there would always be two TLP's first. + // Since retransmission was spurious, smoothed_rtt_ is expired, and replaced + // by the latest RTT sample of 500ms. + expected_time = clock_.Now() + QuicTime::Delta::FromMilliseconds(1000); + // Once we always base the timer on the right edge, leaving the older packets + // in flight doesn't change the timeout. + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); +} + +TEST_P(QuicSentPacketManagerTest, GetTransmissionDelayMin) { + SendDataPacket(1); + // Provide a 1ms RTT sample. + const_cast<RttStats*>(manager_.GetRttStats()) + ->UpdateRtt(QuicTime::Delta::FromMilliseconds(1), QuicTime::Delta::Zero(), + QuicTime::Zero()); + QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(200); + + // If the delay is smaller than the min, ensure it exponentially backs off + // from the min. + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); + EXPECT_EQ(delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, i)); + delay = delay + delay; + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this, i](TransmissionType type) { + RetransmitDataPacket(i + 2, type); + }))); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(i + 2); + } + } +} + +TEST_P(QuicSentPacketManagerTest, GetTransmissionDelayMax) { + SendDataPacket(1); + // Provide a 60s RTT sample. + const_cast<RttStats*>(manager_.GetRttStats()) + ->UpdateRtt(QuicTime::Delta::FromSeconds(60), QuicTime::Delta::Zero(), + QuicTime::Zero()); + + EXPECT_EQ(QuicTime::Delta::FromSeconds(60), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromSeconds(60), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0)); +} + +TEST_P(QuicSentPacketManagerTest, GetTransmissionDelayExponentialBackoff) { + SendDataPacket(1); + QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(500); + + // Delay should back off exponentially. + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); + EXPECT_EQ(delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, i)); + delay = delay + delay; + if (manager_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this, i](TransmissionType type) { + RetransmitDataPacket(i + 2, type); + }))); + } + manager_.OnRetransmissionTimeout(); + if (!manager_.session_decides_what_to_write()) { + RetransmitNextPacket(i + 2); + } + } +} + +TEST_P(QuicSentPacketManagerTest, RetransmissionDelay) { + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + const int64_t kRttMs = 250; + const int64_t kDeviationMs = 5; + + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRttMs), + QuicTime::Delta::Zero(), clock_.Now()); + + // Initial value is to set the median deviation to half of the initial rtt, + // the median in then multiplied by a factor of 4 and finally the smoothed rtt + // is added which is the initial rtt. + QuicTime::Delta expected_delay = + QuicTime::Delta::FromMilliseconds(kRttMs + kRttMs / 2 * 4); + EXPECT_EQ(expected_delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); + EXPECT_EQ(expected_delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0)); + + for (int i = 0; i < 100; ++i) { + // Run to make sure that we converge. + rtt_stats->UpdateRtt( + QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs), + QuicTime::Delta::Zero(), clock_.Now()); + rtt_stats->UpdateRtt( + QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs), + QuicTime::Delta::Zero(), clock_.Now()); + } + expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4); + + EXPECT_NEAR(kRttMs, rtt_stats->smoothed_rtt().ToMilliseconds(), 1); + EXPECT_NEAR(expected_delay.ToMilliseconds(), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_) + .ToMilliseconds(), + 1); + EXPECT_EQ(QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, GetLossDelay) { + auto loss_algorithm = QuicMakeUnique<MockLossAlgorithm>(); + QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm.get()); + + EXPECT_CALL(*loss_algorithm, GetLossTimeout()) + .WillRepeatedly(Return(QuicTime::Zero())); + SendDataPacket(1); + SendDataPacket(2); + + // Handle an ack which causes the loss algorithm to be evaluated and + // set the loss timeout. + ExpectAck(2); + EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _)); + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + QuicTime timeout(clock_.Now() + QuicTime::Delta::FromMilliseconds(10)); + EXPECT_CALL(*loss_algorithm, GetLossTimeout()) + .WillRepeatedly(Return(timeout)); + EXPECT_EQ(timeout, manager_.GetRetransmissionTime()); + + // Fire the retransmission timeout and ensure the loss detection algorithm + // is invoked. + EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _)); + manager_.OnRetransmissionTimeout(); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateTimeLossDetectionFromOptions) { + EXPECT_EQ(kNack, QuicSentPacketManagerPeer::GetLossAlgorithm(&manager_) + ->GetLossDetectionType()); + + QuicConfig config; + QuicTagVector options; + options.push_back(kTIME); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + + EXPECT_EQ(kTime, QuicSentPacketManagerPeer::GetLossAlgorithm(&manager_) + ->GetLossDetectionType()); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateCongestionControlFromOptions) { + QuicConfig config; + QuicTagVector options; + + options.push_back(kRENO); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); + + options.clear(); + options.push_back(kTBBR); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kBBR, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); + + options.clear(); + options.push_back(kBYTE); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kCubicBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); + options.clear(); + options.push_back(kRENO); + options.push_back(kBYTE); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateClientCongestionControlFromOptions) { + QuicConfig config; + QuicTagVector options; + + // No change if the server receives client options. + const SendAlgorithmInterface* mock_sender = + QuicSentPacketManagerPeer::GetSendAlgorithm(manager_); + options.push_back(kRENO); + config.SetClientConnectionOptions(options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(mock_sender, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)); + + // Change the congestion control on the client with client options. + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); + + options.clear(); + options.push_back(kTBBR); + config.SetClientConnectionOptions(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kBBR, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); + + options.clear(); + options.push_back(kBYTE); + config.SetClientConnectionOptions(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kCubicBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); + + options.clear(); + options.push_back(kRENO); + options.push_back(kBYTE); + config.SetClientConnectionOptions(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_) + ->GetCongestionControlType()); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNumConnectionsFromOptions) { + QuicConfig config; + QuicTagVector options; + + options.push_back(k1CON); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetNumEmulatedConnections(1)); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + QuicConfig client_config; + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetNumEmulatedConnections(1)); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(client_config); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtServer) { + QuicConfig config; + QuicTagVector options; + + options.push_back(kMAD2); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillOnce(Return(10 * kDefaultTCPMSS)); + manager_.SetFromConfig(config); + // Set the initial RTT to 1us. + QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt( + QuicTime::Delta::FromMicroseconds(1)); + // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(200ms). + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); + + // Send two packets, and the TLP should be 2 us. + SendDataPacket(1); + SendDataPacket(2); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtClient) { + QuicConfig client_config; + QuicTagVector options; + + options.push_back(kMAD2); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillOnce(Return(10 * kDefaultTCPMSS)); + manager_.SetFromConfig(client_config); + // Set the initial RTT to 1us. + QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt( + QuicTime::Delta::FromMicroseconds(1)); + // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(200ms). + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); + // Send two packets, and the TLP should be 2 us. + SendDataPacket(1); + SendDataPacket(2); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtServer) { + QuicConfig config; + QuicTagVector options; + + options.push_back(kMAD4); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + // Provide an RTT measurement of 100ms. + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // Expect 1.5x * SRTT + 0ms MAD + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); + // Expect 1.5x * SRTT + 50ms MAD + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(150), + QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats->smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtClient) { + QuicConfig client_config; + QuicTagVector options; + + options.push_back(kMAD4); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(client_config); + // Provide an RTT measurement of 100ms. + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // Expect 1.5x * SRTT + 0ms MAD + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); + // Expect 1.5x * SRTT + 50ms MAD + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(150), + QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats->smoothed_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtServer) { + QuicConfig config; + QuicTagVector options; + + options.push_back(kMAD3); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + // Provide one RTT measurement, because otherwise we use the default of 500ms. + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMicroseconds(1), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0)); + // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(0ms). + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtClient) { + QuicConfig client_config; + QuicTagVector options; + + options.push_back(kMAD3); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(client_config); + // Provide one RTT measurement, because otherwise we use the default of 500ms. + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMicroseconds(1), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0)); + // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(0ms). + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNoTLPFromOptionsAtServer) { + QuicConfig config; + QuicTagVector options; + + options.push_back(kNTLP); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNoTLPFromOptionsAtClient) { + QuicConfig client_config; + QuicTagVector options; + + options.push_back(kNTLP); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(client_config); + EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, Negotiate1TLPFromOptionsAtServer) { + QuicConfig config; + QuicTagVector options; + + options.push_back(k1TLP); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + EXPECT_EQ(1u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, Negotiate1TLPFromOptionsAtClient) { + QuicConfig client_config; + QuicTagVector options; + + options.push_back(k1TLP); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(client_config); + EXPECT_EQ(1u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateTLPRttFromOptionsAtServer) { + QuicConfig config; + QuicTagVector options; + + options.push_back(kTLPR); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + EXPECT_TRUE( + QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateTLPRttFromOptionsAtClient) { + QuicConfig client_config; + QuicTagVector options; + + options.push_back(kTLPR); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(client_config); + EXPECT_TRUE( + QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNewRTOFromOptionsAtServer) { + EXPECT_FALSE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_)); + QuicConfig config; + QuicTagVector options; + + options.push_back(kNRTO); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + EXPECT_TRUE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, NegotiateNewRTOFromOptionsAtClient) { + EXPECT_FALSE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_)); + QuicConfig client_config; + QuicTagVector options; + + options.push_back(kNRTO); + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + client_config.SetConnectionOptionsToSend(options); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(client_config); + EXPECT_TRUE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_)); +} + +TEST_P(QuicSentPacketManagerTest, UseInitialRoundTripTimeToSend) { + QuicTime::Delta initial_rtt = QuicTime::Delta::FromMilliseconds(325); + EXPECT_NE(initial_rtt, manager_.GetRttStats()->smoothed_rtt()); + + QuicConfig config; + config.SetInitialRoundTripTimeUsToSend(initial_rtt.ToMicroseconds()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.GetRttStats()->smoothed_rtt()); + EXPECT_EQ(initial_rtt, manager_.GetRttStats()->initial_rtt()); +} + +TEST_P(QuicSentPacketManagerTest, ResumeConnectionState) { + // The sent packet manager should use the RTT from CachedNetworkParameters if + // it is provided. + const QuicTime::Delta kRtt = QuicTime::Delta::FromMilliseconds(1234); + CachedNetworkParameters cached_network_params; + cached_network_params.set_min_rtt_ms(kRtt.ToMilliseconds()); + + EXPECT_CALL(*send_algorithm_, + AdjustNetworkParameters(QuicBandwidth::Zero(), kRtt)); + manager_.ResumeConnectionState(cached_network_params, false); + EXPECT_EQ(kRtt, manager_.GetRttStats()->initial_rtt()); +} + +TEST_P(QuicSentPacketManagerTest, ConnectionMigrationUnspecifiedChange) { + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + QuicTime::Delta default_init_rtt = rtt_stats->initial_rtt(); + rtt_stats->set_initial_rtt(default_init_rtt * 2); + EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt()); + + QuicSentPacketManagerPeer::SetConsecutiveRtoCount(&manager_, 1); + EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount()); + QuicSentPacketManagerPeer::SetConsecutiveTlpCount(&manager_, 2); + EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount()); + + EXPECT_CALL(*send_algorithm_, OnConnectionMigration()); + manager_.OnConnectionMigration(IPV4_TO_IPV4_CHANGE); + + EXPECT_EQ(default_init_rtt, rtt_stats->initial_rtt()); + EXPECT_EQ(0u, manager_.GetConsecutiveRtoCount()); + EXPECT_EQ(0u, manager_.GetConsecutiveTlpCount()); +} + +TEST_P(QuicSentPacketManagerTest, ConnectionMigrationIPSubnetChange) { + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + QuicTime::Delta default_init_rtt = rtt_stats->initial_rtt(); + rtt_stats->set_initial_rtt(default_init_rtt * 2); + EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt()); + + QuicSentPacketManagerPeer::SetConsecutiveRtoCount(&manager_, 1); + EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount()); + QuicSentPacketManagerPeer::SetConsecutiveTlpCount(&manager_, 2); + EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount()); + + manager_.OnConnectionMigration(IPV4_SUBNET_CHANGE); + + EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt()); + EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount()); + EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount()); +} + +TEST_P(QuicSentPacketManagerTest, ConnectionMigrationPortChange) { + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + QuicTime::Delta default_init_rtt = rtt_stats->initial_rtt(); + rtt_stats->set_initial_rtt(default_init_rtt * 2); + EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt()); + + QuicSentPacketManagerPeer::SetConsecutiveRtoCount(&manager_, 1); + EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount()); + QuicSentPacketManagerPeer::SetConsecutiveTlpCount(&manager_, 2); + EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount()); + + manager_.OnConnectionMigration(PORT_CHANGE); + + EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt()); + EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount()); + EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount()); +} + +TEST_P(QuicSentPacketManagerTest, PathMtuIncreased) { + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, BytesInFlight(), QuicPacketNumber(1), _, _)); + SerializedPacket packet(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER, + nullptr, kDefaultLength + 100, false, false); + manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + + // Ack the large packet and expect the path MTU to increase. + ExpectAck(1); + EXPECT_CALL(*network_change_visitor_, + OnPathMtuIncreased(kDefaultLength + 100)); + QuicAckFrame ack_frame = InitAckFrame(1); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); +} + +TEST_P(QuicSentPacketManagerTest, OnAckRangeSlowPath) { + // Send packets 1 - 20. + for (size_t i = 1; i <= 20; ++i) { + SendDataPacket(i); + } + // Ack [5, 7), [10, 12), [15, 17). + uint64_t acked1[] = {5, 6, 10, 11, 15, 16}; + uint64_t lost1[] = {1, 2, 3, 4, 7, 8, 9, 12, 13}; + ExpectAcksAndLosses(true, acked1, QUIC_ARRAYSIZE(acked1), lost1, + QUIC_ARRAYSIZE(lost1)); + EXPECT_CALL(notifier_, OnFrameLost(_)).Times(AnyNumber()); + manager_.OnAckFrameStart(QuicPacketNumber(16), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(15), QuicPacketNumber(17)); + manager_.OnAckRange(QuicPacketNumber(10), QuicPacketNumber(12)); + manager_.OnAckRange(QuicPacketNumber(5), QuicPacketNumber(7)); + // Make sure empty range does not harm. + manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(4)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); + + // Ack [4, 8), [9, 13), [14, 21). + uint64_t acked2[] = {4, 7, 9, 12, 14, 17, 18, 19, 20}; + ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(20), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(14), QuicPacketNumber(21)); + manager_.OnAckRange(QuicPacketNumber(9), QuicPacketNumber(13)); + manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(8)); + EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now())); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_server_id.cc b/quic/core/quic_server_id.cc new file mode 100644 index 0000000..ab35d6e --- /dev/null +++ b/quic/core/quic_server_id.cc
@@ -0,0 +1,41 @@ +// Copyright 2014 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/quic_server_id.h" + +#include <tuple> + +#include "net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicServerId::QuicServerId() : QuicServerId("", 0, false) {} + +QuicServerId::QuicServerId(const QuicString& host, uint16_t port) + : QuicServerId(host, port, false) {} + +QuicServerId::QuicServerId(const QuicString& host, + uint16_t port, + bool privacy_mode_enabled) + : host_(host), port_(port), privacy_mode_enabled_(privacy_mode_enabled) {} + +QuicServerId::~QuicServerId() {} + +bool QuicServerId::operator<(const QuicServerId& other) const { + return std::tie(port_, host_, privacy_mode_enabled_) < + std::tie(other.port_, other.host_, other.privacy_mode_enabled_); +} + +bool QuicServerId::operator==(const QuicServerId& other) const { + return privacy_mode_enabled_ == other.privacy_mode_enabled_ && + host_ == other.host_ && port_ == other.port_; +} + +size_t QuicServerId::EstimateMemoryUsage() const { + return QuicEstimateMemoryUsage(host_); +} + +} // namespace quic
diff --git a/quic/core/quic_server_id.h b/quic/core/quic_server_id.h new file mode 100644 index 0000000..62c0fda --- /dev/null +++ b/quic/core/quic_server_id.h
@@ -0,0 +1,46 @@ +// Copyright 2014 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_QUIC_SERVER_ID_H_ +#define QUICHE_QUIC_CORE_QUIC_SERVER_ID_H_ + +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// The id used to identify sessions. Includes the hostname, port, scheme and +// privacy_mode. +class QUIC_EXPORT_PRIVATE QuicServerId { + public: + QuicServerId(); + QuicServerId(const QuicString& host, uint16_t port); + QuicServerId(const QuicString& host, + uint16_t port, + bool privacy_mode_enabled); + ~QuicServerId(); + + // Needed to be an element of std::set. + bool operator<(const QuicServerId& other) const; + bool operator==(const QuicServerId& other) const; + + const QuicString& host() const { return host_; } + + uint16_t port() const { return port_; } + + bool privacy_mode_enabled() const { return privacy_mode_enabled_; } + + size_t EstimateMemoryUsage() const; + + private: + QuicString host_; + uint16_t port_; + bool privacy_mode_enabled_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_SERVER_ID_H_
diff --git a/quic/core/quic_server_id_test.cc b/quic/core/quic_server_id_test.cc new file mode 100644 index 0000000..e7a2abe --- /dev/null +++ b/quic/core/quic_server_id_test.cc
@@ -0,0 +1,128 @@ +// Copyright 2014 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/quic_server_id.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { + +namespace { + +class QuicServerIdTest : public QuicTest {}; + +TEST_F(QuicServerIdTest, Constructor) { + QuicServerId google_server_id("google.com", 10, false); + EXPECT_EQ("google.com", google_server_id.host()); + EXPECT_EQ(10, google_server_id.port()); + EXPECT_FALSE(google_server_id.privacy_mode_enabled()); + + QuicServerId private_server_id("mail.google.com", 12, true); + EXPECT_EQ("mail.google.com", private_server_id.host()); + EXPECT_EQ(12, private_server_id.port()); + EXPECT_TRUE(private_server_id.privacy_mode_enabled()); +} + +TEST_F(QuicServerIdTest, LessThan) { + QuicServerId a_10_https("a.com", 10, false); + QuicServerId a_11_https("a.com", 11, false); + QuicServerId b_10_https("b.com", 10, false); + QuicServerId b_11_https("b.com", 11, false); + + QuicServerId a_10_https_private("a.com", 10, true); + QuicServerId a_11_https_private("a.com", 11, true); + QuicServerId b_10_https_private("b.com", 10, true); + QuicServerId b_11_https_private("b.com", 11, true); + + // Test combinations of host, port, and privacy being same on left and + // right side of less than. + EXPECT_FALSE(a_10_https < a_10_https); + EXPECT_TRUE(a_10_https < a_10_https_private); + EXPECT_FALSE(a_10_https_private < a_10_https); + EXPECT_FALSE(a_10_https_private < a_10_https_private); + + // Test with either host, port or https being different on left and right side + // of less than. + bool left_privacy; + bool right_privacy; + for (int i = 0; i < 4; i++) { + left_privacy = (i / 2 == 0); + right_privacy = (i % 2 == 0); + QuicServerId a_10_https_left_private("a.com", 10, left_privacy); + QuicServerId a_10_https_right_private("a.com", 10, right_privacy); + QuicServerId a_11_https_left_private("a.com", 11, left_privacy); + QuicServerId a_11_https_right_private("a.com", 11, right_privacy); + + QuicServerId b_10_https_left_private("b.com", 10, left_privacy); + QuicServerId b_10_https_right_private("b.com", 10, right_privacy); + QuicServerId b_11_https_left_private("b.com", 11, left_privacy); + QuicServerId b_11_https_right_private("b.com", 11, right_privacy); + + EXPECT_TRUE(a_10_https_left_private < a_11_https_right_private); + EXPECT_TRUE(a_10_https_left_private < b_10_https_right_private); + EXPECT_TRUE(a_10_https_left_private < b_11_https_right_private); + EXPECT_FALSE(a_11_https_left_private < a_10_https_right_private); + EXPECT_FALSE(a_11_https_left_private < b_10_https_right_private); + EXPECT_TRUE(a_11_https_left_private < b_11_https_right_private); + EXPECT_FALSE(b_10_https_left_private < a_10_https_right_private); + EXPECT_TRUE(b_10_https_left_private < a_11_https_right_private); + EXPECT_TRUE(b_10_https_left_private < b_11_https_right_private); + EXPECT_FALSE(b_11_https_left_private < a_10_https_right_private); + EXPECT_FALSE(b_11_https_left_private < a_11_https_right_private); + EXPECT_FALSE(b_11_https_left_private < b_10_https_right_private); + } +} + +TEST_F(QuicServerIdTest, Equals) { + bool left_privacy; + bool right_privacy; + for (int i = 0; i < 2; i++) { + left_privacy = right_privacy = (i == 0); + QuicServerId a_10_https_right_private("a.com", 10, right_privacy); + QuicServerId a_11_https_right_private("a.com", 11, right_privacy); + QuicServerId b_10_https_right_private("b.com", 10, right_privacy); + QuicServerId b_11_https_right_private("b.com", 11, right_privacy); + + QuicServerId new_a_10_https_left_private("a.com", 10, left_privacy); + QuicServerId new_a_11_https_left_private("a.com", 11, left_privacy); + QuicServerId new_b_10_https_left_private("b.com", 10, left_privacy); + QuicServerId new_b_11_https_left_private("b.com", 11, left_privacy); + + EXPECT_EQ(new_a_10_https_left_private, a_10_https_right_private); + EXPECT_EQ(new_a_11_https_left_private, a_11_https_right_private); + EXPECT_EQ(new_b_10_https_left_private, b_10_https_right_private); + EXPECT_EQ(new_b_11_https_left_private, b_11_https_right_private); + } + + for (int i = 0; i < 2; i++) { + right_privacy = (i == 0); + QuicServerId a_10_https_right_private("a.com", 10, right_privacy); + QuicServerId a_11_https_right_private("a.com", 11, right_privacy); + QuicServerId b_10_https_right_private("b.com", 10, right_privacy); + QuicServerId b_11_https_right_private("b.com", 11, right_privacy); + + QuicServerId new_a_10_https_left_private("a.com", 10, false); + + EXPECT_FALSE(new_a_10_https_left_private == a_11_https_right_private); + EXPECT_FALSE(new_a_10_https_left_private == b_10_https_right_private); + EXPECT_FALSE(new_a_10_https_left_private == b_11_https_right_private); + } + QuicServerId a_10_https_private("a.com", 10, true); + QuicServerId new_a_10_https_no_private("a.com", 10, false); + EXPECT_FALSE(new_a_10_https_no_private == a_10_https_private); +} + +TEST_F(QuicServerIdTest, EstimateMemoryUsage) { + QuicString host = "this is a rather very quite long hostname"; + uint16_t port = 10; + bool privacy_mode_enabled = true; + QuicServerId server_id(host, port, privacy_mode_enabled); + EXPECT_EQ(QuicEstimateMemoryUsage(host), QuicEstimateMemoryUsage(server_id)); +} + +} // namespace + +} // namespace quic
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc new file mode 100644 index 0000000..09e3865 --- /dev/null +++ b/quic/core/quic_session.cc
@@ -0,0 +1,1675 @@ +// Copyright (c) 2012 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/quic_session.h" + +#include <cstdint> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +using spdy::SpdyPriority; + +namespace quic { + +namespace { + +class ClosedStreamsCleanUpDelegate : public QuicAlarm::Delegate { + public: + explicit ClosedStreamsCleanUpDelegate(QuicSession* session) + : session_(session) {} + ClosedStreamsCleanUpDelegate(const ClosedStreamsCleanUpDelegate&) = delete; + ClosedStreamsCleanUpDelegate& operator=(const ClosedStreamsCleanUpDelegate&) = + delete; + + void OnAlarm() override { session_->CleanUpClosedStreams(); } + + private: + QuicSession* session_; +}; + +} // namespace + +#define ENDPOINT \ + (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") + +QuicSession::QuicSession(QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions) + : connection_(connection), + visitor_(owner), + write_blocked_streams_(), + config_(config), + stream_id_manager_(this, + kDefaultMaxStreamsPerConnection, + config_.GetMaxIncomingDynamicStreamsToSend()), + v99_streamid_manager_(this, + kDefaultMaxStreamsPerConnection, + config_.GetMaxIncomingDynamicStreamsToSend()), + num_dynamic_incoming_streams_(0), + num_draining_incoming_streams_(0), + num_locally_closed_incoming_streams_highest_offset_(0), + error_(QUIC_NO_ERROR), + flow_controller_( + this, + QuicUtils::GetInvalidStreamId(connection->transport_version()), + /*is_connection_flow_controller*/ true, + kMinimumFlowControlSendWindow, + config_.GetInitialSessionFlowControlWindowToSend(), + kSessionReceiveWindowLimit, + perspective() == Perspective::IS_SERVER, + nullptr), + currently_writing_stream_id_(0), + largest_static_stream_id_(0), + is_handshake_confirmed_(false), + goaway_sent_(false), + goaway_received_(false), + control_frame_manager_(this), + last_message_id_(0), + closed_streams_clean_up_alarm_(nullptr), + supported_versions_(supported_versions) { + closed_streams_clean_up_alarm_ = + QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm( + new ClosedStreamsCleanUpDelegate(this))); +} + +void QuicSession::Initialize() { + connection_->set_visitor(this); + connection_->SetSessionNotifier(this); + connection_->SetDataProducer(this); + connection_->SetFromConfig(config_); + + DCHECK_EQ(QuicUtils::GetCryptoStreamId(connection_->transport_version()), + GetMutableCryptoStream()->id()); + RegisterStaticStream( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + GetMutableCryptoStream()); +} + +QuicSession::~QuicSession() { + QUIC_LOG_IF(WARNING, !zombie_streams_.empty()) << "Still have zombie streams"; +} + +void QuicSession::RegisterStaticStream(QuicStreamId id, QuicStream* stream) { + static_stream_map_[id] = stream; + + QUIC_BUG_IF(id > + largest_static_stream_id_ + + QuicUtils::StreamIdDelta(connection_->transport_version())) + << ENDPOINT << "Static stream registered out of order: " << id + << " vs: " << largest_static_stream_id_; + largest_static_stream_id_ = std::max(id, largest_static_stream_id_); + + if (connection_->transport_version() == QUIC_VERSION_99) { + v99_streamid_manager_.RegisterStaticStream(id); + } +} + +void QuicSession::OnStreamFrame(const QuicStreamFrame& frame) { + // TODO(rch) deal with the error case of stream id 0. + QuicStreamId stream_id = frame.stream_id; + if (stream_id == + QuicUtils::GetInvalidStreamId(connection()->transport_version())) { + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Recevied data for an invalid stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + if (frame.fin && QuicContainsKey(static_stream_map_, stream_id)) { + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Attempt to close a static stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + StreamHandler handler = GetOrCreateStreamImpl(stream_id, frame.offset != 0); + if (handler.is_pending) { + handler.pending->OnStreamFrame(frame); + return; + } + + if (!handler.stream) { + // The stream no longer exists, but we may still be interested in the + // final stream byte offset sent by the peer. A frame with a FIN can give + // us this offset. + if (frame.fin) { + QuicStreamOffset final_byte_offset = frame.offset + frame.data_length; + OnFinalByteOffsetReceived(stream_id, final_byte_offset); + } + return; + } + handler.stream->OnStreamFrame(frame); +} + +void QuicSession::OnCryptoFrame(const QuicCryptoFrame& frame) { + GetMutableCryptoStream()->OnCryptoFrame(frame); +} + +bool QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) { + // We are not version 99. In theory, if not in version 99 then the framer + // could not call OnStopSending... This is just a check that is good when + // both a new protocol and a new implementation of that protocol are both + // being developed. + DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version()); + + QuicStreamId stream_id = frame.stream_id; + // If Stream ID is invalid then close the connection. + if (stream_id == + QuicUtils::GetInvalidStreamId(connection()->transport_version())) { + QUIC_DVLOG(1) << ENDPOINT + << "Received STOP_SENDING with invalid stream_id: " + << stream_id << " Closing connection"; + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Received STOP_SENDING for an invalid stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + // Ignore STOP_SENDING for static streams. + // TODO(fkastenholz): IETF Quic does not have static streams and does not + // make exceptions for them with respect to processing things like + // STOP_SENDING. + if (QuicContainsKey(static_stream_map_, stream_id)) { + QUIC_DVLOG(1) << ENDPOINT + << "Received STOP_SENDING for a static stream, id: " + << stream_id << " Closing connection"; + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Received STOP_SENDING for a static stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + if (visitor_) { + visitor_->OnStopSendingReceived(frame); + } + + // If stream is closed, ignore the frame + if (IsClosedStream(stream_id)) { + QUIC_DVLOG(1) + << ENDPOINT + << "Received STOP_SENDING for closed or non-existent stream, id: " + << stream_id << " Ignoring."; + return true; // Continue processing the packet. + } + // If stream is non-existent, close the connection + DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id); + if (it == dynamic_stream_map_.end()) { + QUIC_DVLOG(1) << ENDPOINT + << "Received STOP_SENDING for non-existent stream, id: " + << stream_id << " Closing connection"; + connection()->CloseConnection( + IETF_QUIC_PROTOCOL_VIOLATION, + "Received STOP_SENDING for a non-existent stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + // Get the QuicStream for this stream. Ignore the STOP_SENDING + // if the QuicStream pointer is NULL + // QUESTION: IS THIS THE RIGHT THING TO DO? (that is, this would happen IFF + // there was an entry in the map, but the pointer is null. sounds more like a + // deep programming error rather than a simple protocol problem). + QuicStream* stream = it->second.get(); + if (stream == nullptr) { + QUIC_DVLOG(1) << ENDPOINT + << "Received STOP_SENDING for NULL QuicStream, stream_id: " + << stream_id << ". Ignoring."; + return true; + } + stream->OnStopSending(frame.application_error_code); + + stream->set_stream_error( + static_cast<QuicRstStreamErrorCode>(frame.application_error_code)); + SendRstStreamInner( + stream->id(), + static_cast<quic::QuicRstStreamErrorCode>(frame.application_error_code), + stream->stream_bytes_written(), + /*close_write_side_only=*/true); + + return true; +} + +void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { + QuicStreamId stream_id = frame.stream_id; + if (stream_id == + QuicUtils::GetInvalidStreamId(connection()->transport_version())) { + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Recevied data for an invalid stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + if (QuicContainsKey(static_stream_map_, stream_id)) { + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + + if (visitor_) { + visitor_->OnRstStreamReceived(frame); + } + + // may_buffer is true here to allow subclasses to buffer streams until the + // first byte of payload arrives which would allow sessions to delay + // creation of the stream until the type is known. + StreamHandler handler = GetOrCreateStreamImpl(stream_id, /*may_buffer=*/true); + if (handler.is_pending) { + handler.pending->OnRstStreamFrame(frame); + ClosePendingStream(stream_id); + return; + } + if (!handler.stream) { + HandleRstOnValidNonexistentStream(frame); + return; // Errors are handled by GetOrCreateStream. + } + handler.stream->OnStreamReset(frame); +} + +void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) { + goaway_received_ = true; +} + +void QuicSession::OnMessageReceived(QuicStringPiece message) { + QUIC_DVLOG(1) << ENDPOINT << "Received message, length: " << message.length() + << ", " << message; +} + +void QuicSession::OnConnectionClosed(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) { + DCHECK(!connection_->connected()); + if (error_ == QUIC_NO_ERROR) { + error_ = error; + } + + while (!dynamic_stream_map_.empty()) { + DynamicStreamMap::iterator it = dynamic_stream_map_.begin(); + QuicStreamId id = it->first; + it->second->OnConnectionClosed(error, source); + // The stream should call CloseStream as part of OnConnectionClosed. + if (dynamic_stream_map_.find(id) != dynamic_stream_map_.end()) { + QUIC_BUG << ENDPOINT << "Stream failed to close under OnConnectionClosed"; + CloseStream(id); + } + } + + // Cleanup zombie stream map on connection close. + while (!zombie_streams_.empty()) { + ZombieStreamMap::iterator it = zombie_streams_.begin(); + closed_streams_.push_back(std::move(it->second)); + zombie_streams_.erase(it); + } + + closed_streams_clean_up_alarm_->Cancel(); + + if (visitor_) { + visitor_->OnConnectionClosed(connection_->connection_id(), error, + error_details, source); + } +} + +void QuicSession::OnWriteBlocked() { + if (GetQuicReloadableFlag( + quic_connection_do_not_add_to_write_blocked_list_if_disconnected) && + !connection_->connected()) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_connection_do_not_add_to_write_blocked_list_if_disconnected, 1, 2); + return; + } + if (visitor_) { + visitor_->OnWriteBlocked(connection_); + } +} + +void QuicSession::OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) { + GetMutableCryptoStream()->OnSuccessfulVersionNegotiation(version); +} + +void QuicSession::OnConnectivityProbeReceived( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address) { + if (perspective() == Perspective::IS_SERVER) { + // Server only sends back a connectivity probe after received a + // connectivity probe from a new peer address. + connection_->SendConnectivityProbingResponsePacket(peer_address); + } +} + +void QuicSession::OnPathDegrading() {} + +bool QuicSession::AllowSelfAddressChange() const { + return false; +} + +void QuicSession::OnForwardProgressConfirmed() {} + +void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { + // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't + // assume that it still exists. + QuicStreamId stream_id = frame.stream_id; + if (stream_id == + QuicUtils::GetInvalidStreamId(connection_->transport_version())) { + // This is a window update that applies to the connection, rather than an + // individual stream. + QUIC_DLOG(INFO) << ENDPOINT + << "Received connection level flow control window " + "update with byte offset: " + << frame.byte_offset; + flow_controller_.UpdateSendWindowOffset(frame.byte_offset); + return; + } + QuicStream* stream = GetOrCreateStream(stream_id); + if (stream != nullptr) { + stream->OnWindowUpdateFrame(frame); + } +} + +void QuicSession::OnBlockedFrame(const QuicBlockedFrame& frame) { + // TODO(rjshade): Compare our flow control receive windows for specified + // streams: if we have a large window then maybe something + // had gone wrong with the flow control accounting. + QUIC_DLOG(INFO) << ENDPOINT << "Received BLOCKED frame with stream id: " + << frame.stream_id; +} + +bool QuicSession::CheckStreamNotBusyLooping(QuicStream* stream, + uint64_t previous_bytes_written, + bool previous_fin_sent) { + if ( // Stream should not be closed. + !stream->write_side_closed() && + // Not connection flow control blocked. + !flow_controller_.IsBlocked() && + // Detect lack of forward progress. + previous_bytes_written == stream->stream_bytes_written() && + previous_fin_sent == stream->fin_sent()) { + stream->set_busy_counter(stream->busy_counter() + 1); + QUIC_DVLOG(1) << "Suspected busy loop on stream id " << stream->id() + << " stream_bytes_written " << stream->stream_bytes_written() + << " fin " << stream->fin_sent() << " count " + << stream->busy_counter(); + // Wait a few iterations before firing, the exact count is + // arbitrary, more than a few to cover a few test-only false + // positives. + if (stream->busy_counter() > 20) { + QUIC_LOG(ERROR) << "Detected busy loop on stream id " << stream->id() + << " stream_bytes_written " + << stream->stream_bytes_written() << " fin " + << stream->fin_sent(); + return false; + } + } else { + stream->set_busy_counter(0); + } + return true; +} + +bool QuicSession::CheckStreamWriteBlocked(QuicStream* stream) const { + if (!stream->write_side_closed() && stream->HasBufferedData() && + !stream->flow_controller()->IsBlocked() && + !write_blocked_streams_.IsStreamBlocked(stream->id())) { + QUIC_DLOG(ERROR) << "stream " << stream->id() << " has buffered " + << stream->BufferedDataBytes() + << " bytes, and is not flow control blocked, " + "but it is not in the write block list."; + return false; + } + return true; +} + +void QuicSession::OnCanWrite() { + if (!RetransmitLostData()) { + // Cannot finish retransmitting lost data, connection is write blocked. + QUIC_DVLOG(1) << ENDPOINT + << "Cannot finish retransmitting lost data, connection is " + "write blocked."; + return; + } + if (session_decides_what_to_write()) { + SetTransmissionType(NOT_RETRANSMISSION); + } + // We limit the number of writes to the number of pending streams. If more + // streams become pending, WillingAndAbleToWrite will be true, which will + // cause the connection to request resumption before yielding to other + // connections. + // If we are connection level flow control blocked, then only allow the + // crypto and headers streams to try writing as all other streams will be + // blocked. + size_t num_writes = flow_controller_.IsBlocked() + ? write_blocked_streams_.NumBlockedSpecialStreams() + : write_blocked_streams_.NumBlockedStreams(); + if (num_writes == 0 && !control_frame_manager_.WillingToWrite()) { + return; + } + + QuicConnection::ScopedPacketFlusher flusher( + connection_, QuicConnection::SEND_ACK_IF_QUEUED); + if (control_frame_manager_.WillingToWrite()) { + control_frame_manager_.OnCanWrite(); + } + for (size_t i = 0; i < num_writes; ++i) { + if (!(write_blocked_streams_.HasWriteBlockedSpecialStream() || + write_blocked_streams_.HasWriteBlockedDataStreams())) { + // Writing one stream removed another!? Something's broken. + QUIC_BUG << "WriteBlockedStream is missing"; + connection_->CloseConnection(QUIC_INTERNAL_ERROR, + "WriteBlockedStream is missing", + ConnectionCloseBehavior::SILENT_CLOSE); + return; + } + if (!connection_->CanWriteStreamData()) { + return; + } + currently_writing_stream_id_ = write_blocked_streams_.PopFront(); + QuicStream* stream = GetOrCreateStream(currently_writing_stream_id_); + if (stream != nullptr && !stream->flow_controller()->IsBlocked()) { + // If the stream can't write all bytes it'll re-add itself to the blocked + // list. + uint64_t previous_bytes_written = stream->stream_bytes_written(); + bool previous_fin_sent = stream->fin_sent(); + QUIC_DVLOG(1) << "stream " << stream->id() << " bytes_written " + << previous_bytes_written << " fin " << previous_fin_sent; + stream->OnCanWrite(); + DCHECK(CheckStreamWriteBlocked(stream)); + DCHECK(CheckStreamNotBusyLooping(stream, previous_bytes_written, + previous_fin_sent)); + } + currently_writing_stream_id_ = 0; + } +} + +bool QuicSession::WillingAndAbleToWrite() const { + // Schedule a write when: + // 1) control frame manager has pending or new control frames, or + // 2) any stream has pending retransmissions, or + // 3) If the crypto or headers streams are blocked, or + // 4) connection is not flow control blocked and there are write blocked + // streams. + return control_frame_manager_.WillingToWrite() || + !streams_with_pending_retransmission_.empty() || + write_blocked_streams_.HasWriteBlockedSpecialStream() || + (!flow_controller_.IsBlocked() && + write_blocked_streams_.HasWriteBlockedDataStreams()); +} + +bool QuicSession::HasPendingHandshake() const { + return QuicContainsKey( + streams_with_pending_retransmission_, + QuicUtils::GetCryptoStreamId(connection_->transport_version())) || + write_blocked_streams_.IsStreamBlocked( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); +} + +uint64_t QuicSession::GetNumOpenDynamicStreams() const { + return dynamic_stream_map_.size() - draining_streams_.size() + + locally_closed_streams_highest_offset_.size(); +} + +void QuicSession::ProcessUdpPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet) { + connection_->ProcessUdpPacket(self_address, peer_address, packet); +} + +QuicConsumedData QuicSession::WritevData(QuicStream* stream, + QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state) { + // This check is an attempt to deal with potential memory corruption + // in which |id| ends up set to 1 (the crypto stream id). If this happen + // it might end up resulting in unencrypted stream data being sent. + // While this is impossible to avoid given sufficient corruption, this + // seems like a reasonable mitigation. + if (id == QuicUtils::GetCryptoStreamId(connection_->transport_version()) && + stream != GetMutableCryptoStream()) { + QUIC_BUG << "Stream id mismatch"; + connection_->CloseConnection( + QUIC_INTERNAL_ERROR, + "Non-crypto stream attempted to write data as crypto stream.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return QuicConsumedData(0, false); + } + if (!IsEncryptionEstablished() && + id != QuicUtils::GetCryptoStreamId(connection_->transport_version())) { + // Do not let streams write without encryption. The calling stream will end + // up write blocked until OnCanWrite is next called. + return QuicConsumedData(0, false); + } + + QuicConsumedData data = + connection_->SendStreamData(id, write_length, offset, state); + if (offset >= stream->stream_bytes_written()) { + // This is new stream data. + write_blocked_streams_.UpdateBytesForStream(id, data.bytes_consumed); + } + return data; +} + +bool QuicSession::WriteControlFrame(const QuicFrame& frame) { + return connection_->SendControlFrame(frame); +} + +void QuicSession::SendRstStream(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) { + SendRstStreamInner(id, error, bytes_written, /*close_write_side_only=*/false); +} + +void QuicSession::SendRstStreamInner(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written, + bool close_write_side_only) { + if (connection()->connected()) { + // Only send if still connected. + if (close_write_side_only) { + DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version()); + // Send a RST_STREAM frame. + control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written); + } else { + // Send a RST_STREAM frame plus, if version 99, an IETF + // QUIC STOP_SENDING frame. Both sre sent to emulate + // the two-way close that Google QUIC's RST_STREAM does. + if (connection_->transport_version() == QUIC_VERSION_99) { + QuicConnection::ScopedPacketFlusher flusher( + connection(), QuicConnection::SEND_ACK_IF_QUEUED); + control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written); + control_frame_manager_.WriteOrBufferStopSending(error, id); + } else { + control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written); + } + } + connection_->OnStreamReset(id, error); + } + if (error != QUIC_STREAM_NO_ERROR && QuicContainsKey(zombie_streams_, id)) { + OnStreamDoneWaitingForAcks(id); + return; + } + + if (!close_write_side_only) { + CloseStreamInner(id, true); + return; + } + DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version()); + + DynamicStreamMap::iterator it = dynamic_stream_map_.find(id); + if (it != dynamic_stream_map_.end()) { + QuicStream* stream = it->second.get(); + if (stream) { + stream->set_rst_sent(true); + stream->CloseWriteSide(); + } + } +} + +void QuicSession::SendGoAway(QuicErrorCode error_code, + const QuicString& reason) { + // GOAWAY frame is not supported in v99. + DCHECK_NE(QUIC_VERSION_99, connection_->transport_version()); + if (goaway_sent_) { + return; + } + goaway_sent_ = true; + control_frame_manager_.WriteOrBufferGoAway( + error_code, stream_id_manager_.largest_peer_created_stream_id(), reason); +} + +void QuicSession::SendBlocked(QuicStreamId id) { + control_frame_manager_.WriteOrBufferBlocked(id); +} + +void QuicSession::SendWindowUpdate(QuicStreamId id, + QuicStreamOffset byte_offset) { + control_frame_manager_.WriteOrBufferWindowUpdate(id, byte_offset); +} + +void QuicSession::SendMaxStreamId(QuicStreamId max_allowed_incoming_id) { + control_frame_manager_.WriteOrBufferMaxStreamId(max_allowed_incoming_id); +} + +void QuicSession::SendStreamIdBlocked(QuicStreamId max_allowed_outgoing_id) { + control_frame_manager_.WriteOrBufferStreamIdBlocked(max_allowed_outgoing_id); +} + +void QuicSession::CloseStream(QuicStreamId stream_id) { + CloseStreamInner(stream_id, false); +} + +void QuicSession::InsertLocallyClosedStreamsHighestOffset( + const QuicStreamId id, + QuicStreamOffset offset) { + locally_closed_streams_highest_offset_[id] = offset; + if (IsIncomingStream(id)) { + ++num_locally_closed_incoming_streams_highest_offset_; + } +} + +void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) { + QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << stream_id; + + DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id); + if (it == dynamic_stream_map_.end()) { + // When CloseStreamInner has been called recursively (via + // QuicStream::OnClose), the stream will already have been deleted + // from stream_map_, so return immediately. + QUIC_DVLOG(1) << ENDPOINT << "Stream is already closed: " << stream_id; + return; + } + QuicStream* stream = it->second.get(); + + // Tell the stream that a RST has been sent. + if (locally_reset) { + stream->set_rst_sent(true); + } + + if (stream->IsWaitingForAcks()) { + zombie_streams_[stream->id()] = std::move(it->second); + } else { + closed_streams_.push_back(std::move(it->second)); + // Do not retransmit data of a closed stream. + streams_with_pending_retransmission_.erase(stream_id); + if (!closed_streams_clean_up_alarm_->IsSet()) { + closed_streams_clean_up_alarm_->Set( + connection_->clock()->ApproximateNow()); + } + } + + // If we haven't received a FIN or RST for this stream, we need to keep track + // of the how many bytes the stream's flow controller believes it has + // received, for accurate connection level flow control accounting. + const bool had_fin_or_rst = stream->HasFinalReceivedByteOffset(); + if (!had_fin_or_rst) { + InsertLocallyClosedStreamsHighestOffset( + stream_id, stream->flow_controller()->highest_received_byte_offset()); + } + dynamic_stream_map_.erase(it); + if (IsIncomingStream(stream_id)) { + --num_dynamic_incoming_streams_; + } + + const bool stream_was_draining = + draining_streams_.find(stream_id) != draining_streams_.end(); + if (stream_was_draining) { + if (IsIncomingStream(stream_id)) { + --num_draining_incoming_streams_; + } + draining_streams_.erase(stream_id); + } else if (connection_->transport_version() == QUIC_VERSION_99) { + // Stream was not draining, but we did have a fin or rst, so we can now + // free the stream ID if version 99. + if (had_fin_or_rst) { + v99_streamid_manager_.OnStreamClosed(stream_id); + } + } + + stream->OnClose(); + + if (!stream_was_draining && !IsIncomingStream(stream_id) && had_fin_or_rst && + connection_->transport_version() != QUIC_VERSION_99) { + // Streams that first became draining already called OnCanCreate... + // This covers the case where the stream went directly to being closed. + OnCanCreateNewOutgoingStream(); + } +} + +void QuicSession::ClosePendingStream(QuicStreamId stream_id) { + QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << stream_id; + + if (pending_stream_map_.find(stream_id) == pending_stream_map_.end()) { + QUIC_BUG << ENDPOINT << "Stream is already closed: " << stream_id; + return; + } + + SendRstStream(stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0); + + // The pending stream may have been deleted and removed during SendRstStream. + // Remove the stream from pending stream map iff it is still in the map. + if (pending_stream_map_.find(stream_id) != pending_stream_map_.end()) { + pending_stream_map_.erase(stream_id); + } + + --num_dynamic_incoming_streams_; + + if (connection_->transport_version() == QUIC_VERSION_99) { + v99_streamid_manager_.OnStreamClosed(stream_id); + } + + OnCanCreateNewOutgoingStream(); +} + +void QuicSession::OnFinalByteOffsetReceived( + QuicStreamId stream_id, + QuicStreamOffset final_byte_offset) { + auto it = locally_closed_streams_highest_offset_.find(stream_id); + if (it == locally_closed_streams_highest_offset_.end()) { + return; + } + + QUIC_DVLOG(1) << ENDPOINT << "Received final byte offset " + << final_byte_offset << " for stream " << stream_id; + QuicByteCount offset_diff = final_byte_offset - it->second; + if (flow_controller_.UpdateHighestReceivedOffset( + flow_controller_.highest_received_byte_offset() + offset_diff)) { + // If the final offset violates flow control, close the connection now. + if (flow_controller_.FlowControlViolation()) { + connection_->CloseConnection( + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, + "Connection level flow control violation", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + } + + flow_controller_.AddBytesConsumed(offset_diff); + locally_closed_streams_highest_offset_.erase(it); + if (IsIncomingStream(stream_id)) { + --num_locally_closed_incoming_streams_highest_offset_; + if (connection_->transport_version() == QUIC_VERSION_99) { + v99_streamid_manager_.OnStreamClosed(stream_id); + } + } else if (connection_->transport_version() != QUIC_VERSION_99) { + OnCanCreateNewOutgoingStream(); + } +} + +bool QuicSession::IsEncryptionEstablished() const { + // Once the handshake is confirmed, it never becomes un-confirmed. + if (is_handshake_confirmed_) { + return true; + } + return GetCryptoStream()->encryption_established(); +} + +bool QuicSession::IsCryptoHandshakeConfirmed() const { + return GetCryptoStream()->handshake_confirmed(); +} + +void QuicSession::OnConfigNegotiated() { + connection_->SetFromConfig(config_); + + uint32_t max_streams = 0; + if (config_.HasReceivedMaxIncomingDynamicStreams()) { + max_streams = config_.ReceivedMaxIncomingDynamicStreams(); + } + QUIC_DVLOG(1) << "Setting max_open_outgoing_streams_ to " << max_streams; + if (connection_->transport_version() == QUIC_VERSION_99) { + v99_streamid_manager_.SetMaxOpenOutgoingStreams(max_streams); + } else { + stream_id_manager_.set_max_open_outgoing_streams(max_streams); + } + if (perspective() == Perspective::IS_SERVER) { + if (config_.HasReceivedConnectionOptions()) { + // The following variations change the initial receive flow control + // window sizes. + if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW6)) { + AdjustInitialFlowControlWindows(64 * 1024); + } + if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW7)) { + AdjustInitialFlowControlWindows(128 * 1024); + } + if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW8)) { + AdjustInitialFlowControlWindows(256 * 1024); + } + if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW9)) { + AdjustInitialFlowControlWindows(512 * 1024); + } + if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFWA)) { + AdjustInitialFlowControlWindows(1024 * 1024); + } + } + + config_.SetStatelessResetTokenToSend(GetStatelessResetToken()); + } + + // A small number of additional incoming streams beyond the limit should be + // allowed. This helps avoid early connection termination when FIN/RSTs for + // old streams are lost or arrive out of order. + // Use a minimum number of additional streams, or a percentage increase, + // whichever is larger. + uint32_t max_incoming_streams_to_send = + config_.GetMaxIncomingDynamicStreamsToSend(); + if (connection_->transport_version() == QUIC_VERSION_99) { + v99_streamid_manager_.SetMaxOpenIncomingStreams( + max_incoming_streams_to_send); + } else { + uint32_t max_incoming_streams = + std::max(max_incoming_streams_to_send + kMaxStreamsMinimumIncrement, + static_cast<uint32_t>(max_incoming_streams_to_send * + kMaxStreamsMultiplier)); + stream_id_manager_.set_max_open_incoming_streams(max_incoming_streams); + } + + if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) { + // Streams which were created before the SHLO was received (0-RTT + // requests) are now informed of the peer's initial flow control window. + OnNewStreamFlowControlWindow( + config_.ReceivedInitialStreamFlowControlWindowBytes()); + } + if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) { + OnNewSessionFlowControlWindow( + config_.ReceivedInitialSessionFlowControlWindowBytes()); + } +} + +void QuicSession::AdjustInitialFlowControlWindows(size_t stream_window) { + const float session_window_multiplier = + config_.GetInitialStreamFlowControlWindowToSend() + ? static_cast<float>( + config_.GetInitialSessionFlowControlWindowToSend()) / + config_.GetInitialStreamFlowControlWindowToSend() + : 1.5; + + QUIC_DVLOG(1) << ENDPOINT << "Set stream receive window to " << stream_window; + config_.SetInitialStreamFlowControlWindowToSend(stream_window); + + size_t session_window = session_window_multiplier * stream_window; + QUIC_DVLOG(1) << ENDPOINT << "Set session receive window to " + << session_window; + config_.SetInitialSessionFlowControlWindowToSend(session_window); + flow_controller_.UpdateReceiveWindowSize(session_window); + // Inform all existing streams about the new window. + for (auto const& kv : static_stream_map_) { + kv.second->flow_controller()->UpdateReceiveWindowSize(stream_window); + } + for (auto const& kv : dynamic_stream_map_) { + kv.second->flow_controller()->UpdateReceiveWindowSize(stream_window); + } +} + +void QuicSession::HandleFrameOnNonexistentOutgoingStream( + QuicStreamId stream_id) { + DCHECK(!IsClosedStream(stream_id)); + // Received a frame for a locally-created stream that is not currently + // active. This is an error. + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, "Data for nonexistent stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +void QuicSession::HandleRstOnValidNonexistentStream( + const QuicRstStreamFrame& frame) { + // If the stream is neither originally in active streams nor created in + // GetOrCreateDynamicStream(), it could be a closed stream in which case its + // final received byte offset need to be updated. + if (IsClosedStream(frame.stream_id)) { + // The RST frame contains the final byte offset for the stream: we can now + // update the connection level flow controller if needed. + OnFinalByteOffsetReceived(frame.stream_id, frame.byte_offset); + } +} + +void QuicSession::OnNewStreamFlowControlWindow(QuicStreamOffset new_window) { + if (new_window < kMinimumFlowControlSendWindow) { + QUIC_LOG_FIRST_N(ERROR, 1) + << "Peer sent us an invalid stream flow control send window: " + << new_window << ", below default: " << kMinimumFlowControlSendWindow; + if (connection_->connected()) { + connection_->CloseConnection( + QUIC_FLOW_CONTROL_INVALID_WINDOW, "New stream window too low", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } + return; + } + + // Inform all existing streams about the new window. + for (auto const& kv : static_stream_map_) { + kv.second->UpdateSendWindowOffset(new_window); + } + for (auto const& kv : dynamic_stream_map_) { + kv.second->UpdateSendWindowOffset(new_window); + } +} + +void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) { + if (new_window < kMinimumFlowControlSendWindow) { + QUIC_LOG_FIRST_N(ERROR, 1) + << "Peer sent us an invalid session flow control send window: " + << new_window << ", below default: " << kMinimumFlowControlSendWindow; + if (connection_->connected()) { + connection_->CloseConnection( + QUIC_FLOW_CONTROL_INVALID_WINDOW, "New connection window too low", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } + return; + } + + flow_controller_.UpdateSendWindowOffset(new_window); +} + +void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { + switch (event) { + // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter + // to QuicSession since it is the glue. + case ENCRYPTION_FIRST_ESTABLISHED: + // Given any streams blocked by encryption a chance to write. + OnCanWrite(); + break; + + case ENCRYPTION_REESTABLISHED: + // Retransmit originally packets that were sent, since they can't be + // decrypted by the peer. + connection_->RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION); + // Given any streams blocked by encryption a chance to write. + OnCanWrite(); + break; + + case HANDSHAKE_CONFIRMED: + QUIC_BUG_IF(!config_.negotiated()) + << ENDPOINT << "Handshake confirmed without parameter negotiation."; + // Discard originally encrypted packets, since they can't be decrypted by + // the peer. + NeuterUnencryptedData(); + is_handshake_confirmed_ = true; + break; + + default: + QUIC_LOG(ERROR) << ENDPOINT << "Got unknown handshake event: " << event; + } +} + +void QuicSession::OnCryptoHandshakeMessageSent( + const CryptoHandshakeMessage& /*message*/) {} + +void QuicSession::OnCryptoHandshakeMessageReceived( + const CryptoHandshakeMessage& /*message*/) {} + +void QuicSession::RegisterStreamPriority(QuicStreamId id, + bool is_static, + SpdyPriority priority) { + write_blocked_streams()->RegisterStream(id, is_static, priority); +} + +void QuicSession::UnregisterStreamPriority(QuicStreamId id, bool is_static) { + write_blocked_streams()->UnregisterStream(id, is_static); +} + +void QuicSession::UpdateStreamPriority(QuicStreamId id, + SpdyPriority new_priority) { + write_blocked_streams()->UpdateStreamPriority(id, new_priority); +} + +QuicConfig* QuicSession::config() { + return &config_; +} + +void QuicSession::ActivateStream(std::unique_ptr<QuicStream> stream) { + QuicStreamId stream_id = stream->id(); + QUIC_DVLOG(1) << ENDPOINT << "num_streams: " << dynamic_stream_map_.size() + << ". activating " << stream_id; + DCHECK(!QuicContainsKey(dynamic_stream_map_, stream_id)); + DCHECK(!QuicContainsKey(static_stream_map_, stream_id)); + dynamic_stream_map_[stream_id] = std::move(stream); + if (IsIncomingStream(stream_id)) { + ++num_dynamic_incoming_streams_; + } +} + +QuicStreamId QuicSession::GetNextOutgoingBidirectionalStreamId() { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.GetNextOutgoingBidirectionalStreamId(); + } + return stream_id_manager_.GetNextOutgoingStreamId(); +} + +QuicStreamId QuicSession::GetNextOutgoingUnidirectionalStreamId() { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.GetNextOutgoingUnidirectionalStreamId(); + } + return stream_id_manager_.GetNextOutgoingStreamId(); +} + +bool QuicSession::CanOpenNextOutgoingBidirectionalStream() { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.CanOpenNextOutgoingBidirectionalStream(); + } + return stream_id_manager_.CanOpenNextOutgoingStream( + GetNumOpenOutgoingStreams()); +} + +bool QuicSession::CanOpenNextOutgoingUnidirectionalStream() { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.CanOpenNextOutgoingUnidirectionalStream(); + } + return stream_id_manager_.CanOpenNextOutgoingStream( + GetNumOpenOutgoingStreams()); +} + +QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) { + StreamHandler handler = + GetOrCreateStreamImpl(stream_id, /*may_buffer=*/false); + DCHECK(!handler.is_pending); + return handler.stream; +} + +QuicSession::StreamHandler QuicSession::GetOrCreateStreamImpl( + QuicStreamId stream_id, + bool may_buffer) { + StaticStreamMap::iterator it = static_stream_map_.find(stream_id); + if (it != static_stream_map_.end()) { + return StreamHandler(it->second); + } + return GetOrCreateDynamicStreamImpl(stream_id, may_buffer); +} + +void QuicSession::StreamDraining(QuicStreamId stream_id) { + DCHECK(QuicContainsKey(dynamic_stream_map_, stream_id)); + if (!QuicContainsKey(draining_streams_, stream_id)) { + draining_streams_.insert(stream_id); + if (IsIncomingStream(stream_id)) { + ++num_draining_incoming_streams_; + } + if (connection_->transport_version() == QUIC_VERSION_99) { + v99_streamid_manager_.OnStreamClosed(stream_id); + } + } + if (!IsIncomingStream(stream_id)) { + // Inform application that a stream is available. + OnCanCreateNewOutgoingStream(); + } +} + +bool QuicSession::MaybeIncreaseLargestPeerStreamId( + const QuicStreamId stream_id) { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.MaybeIncreaseLargestPeerStreamId(stream_id); + } + return stream_id_manager_.MaybeIncreaseLargestPeerStreamId(stream_id); +} + +bool QuicSession::ShouldYield(QuicStreamId stream_id) { + if (stream_id == currently_writing_stream_id_) { + return false; + } + return write_blocked_streams()->ShouldYield(stream_id); +} + +QuicStream* QuicSession::GetOrCreateDynamicStream( + const QuicStreamId stream_id) { + StreamHandler handler = + GetOrCreateDynamicStreamImpl(stream_id, /*may_buffer=*/false); + DCHECK(!handler.is_pending); + return handler.stream; +} + +QuicSession::StreamHandler QuicSession::GetOrCreateDynamicStreamImpl( + QuicStreamId stream_id, + bool may_buffer) { + DCHECK(!QuicContainsKey(static_stream_map_, stream_id)) + << "Attempt to call GetOrCreateDynamicStream for a static stream"; + + DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id); + if (it != dynamic_stream_map_.end()) { + return StreamHandler(it->second.get()); + } + + if (IsClosedStream(stream_id)) { + return StreamHandler(); + } + + if (!IsIncomingStream(stream_id)) { + HandleFrameOnNonexistentOutgoingStream(stream_id); + return StreamHandler(); + } + + auto pending_it = pending_stream_map_.find(stream_id); + if (pending_it != pending_stream_map_.end()) { + DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version()); + if (may_buffer) { + return StreamHandler(pending_it->second.get()); + } + // The stream limit accounting has already been taken care of + // when the PendingStream was created, so there is no need to + // do so here. Now we can create the actual stream from the + // PendingStream. + StreamHandler handler(CreateIncomingStream(std::move(*pending_it->second))); + pending_stream_map_.erase(pending_it); + return handler; + } + + // TODO(fkastenholz): If we are creating a new stream and we have + // sent a goaway, we should ignore the stream creation. Need to + // add code to A) test if goaway was sent ("if (goaway_sent_)") and + // B) reject stream creation ("return nullptr") + + if (!MaybeIncreaseLargestPeerStreamId(stream_id)) { + return StreamHandler(); + } + + if (connection_->transport_version() != QUIC_VERSION_99) { + // TODO(fayang): Let LegacyQuicStreamIdManager count open streams and make + // CanOpenIncomingStream interface cosistent with that of v99. + if (!stream_id_manager_.CanOpenIncomingStream( + GetNumOpenIncomingStreams())) { + // Refuse to open the stream. + SendRstStream(stream_id, QUIC_REFUSED_STREAM, 0); + return StreamHandler(); + } + } + + if (connection_->transport_version() == QUIC_VERSION_99 && may_buffer && + ShouldBufferIncomingStream(stream_id)) { + ++num_dynamic_incoming_streams_; + // Since STREAM frames may arrive out of order, delay creating the + // stream object until the first byte arrives. Buffer the frames and + // handle flow control accounting in the PendingStream. + auto pending = QuicMakeUnique<PendingStream>(stream_id, this); + StreamHandler handler(pending.get()); + pending_stream_map_[stream_id] = std::move(pending); + return handler; + } + + return StreamHandler(CreateIncomingStream(stream_id)); +} + +void QuicSession::set_largest_peer_created_stream_id( + QuicStreamId largest_peer_created_stream_id) { + if (connection_->transport_version() == QUIC_VERSION_99) { + v99_streamid_manager_.SetLargestPeerCreatedStreamId( + largest_peer_created_stream_id); + return; + } + stream_id_manager_.set_largest_peer_created_stream_id( + largest_peer_created_stream_id); +} + +bool QuicSession::IsClosedStream(QuicStreamId id) { + DCHECK_NE(QuicUtils::GetInvalidStreamId(connection_->transport_version()), + id); + if (IsOpenStream(id)) { + // Stream is active + return false; + } + + if (connection_->transport_version() == QUIC_VERSION_99) { + return !v99_streamid_manager_.IsAvailableStream(id); + } + + return !stream_id_manager_.IsAvailableStream(id); +} + +bool QuicSession::IsOpenStream(QuicStreamId id) { + DCHECK_NE(QuicUtils::GetInvalidStreamId(connection_->transport_version()), + id); + if (QuicContainsKey(static_stream_map_, id) || + QuicContainsKey(dynamic_stream_map_, id) || + QuicContainsKey(pending_stream_map_, id)) { + // Stream is active + return true; + } + return false; +} + +size_t QuicSession::GetNumOpenIncomingStreams() const { + return num_dynamic_incoming_streams_ - num_draining_incoming_streams_ + + num_locally_closed_incoming_streams_highest_offset_; +} + +size_t QuicSession::GetNumOpenOutgoingStreams() const { + DCHECK_GE(GetNumDynamicOutgoingStreams() + + GetNumLocallyClosedOutgoingStreamsHighestOffset(), + GetNumDrainingOutgoingStreams()); + return GetNumDynamicOutgoingStreams() + + GetNumLocallyClosedOutgoingStreamsHighestOffset() - + GetNumDrainingOutgoingStreams(); +} + +size_t QuicSession::GetNumActiveStreams() const { + return dynamic_stream_map_.size() - draining_streams_.size(); +} + +size_t QuicSession::GetNumDrainingStreams() const { + return draining_streams_.size(); +} + +void QuicSession::MarkConnectionLevelWriteBlocked(QuicStreamId id) { + if (GetOrCreateStream(id) == nullptr) { + QUIC_BUG << "Marking unknown stream " << id << " blocked."; + QUIC_LOG_FIRST_N(ERROR, 2) << QuicStackTrace(); + } + + write_blocked_streams_.AddStream(id); +} + +bool QuicSession::HasDataToWrite() const { + return write_blocked_streams_.HasWriteBlockedSpecialStream() || + write_blocked_streams_.HasWriteBlockedDataStreams() || + connection_->HasQueuedData() || + !streams_with_pending_retransmission_.empty() || + control_frame_manager_.WillingToWrite(); +} + +void QuicSession::OnAckNeedsRetransmittableFrame() { + flow_controller_.SendWindowUpdate(); +} + +void QuicSession::SendPing() { + control_frame_manager_.WritePing(); +} + +size_t QuicSession::GetNumDynamicOutgoingStreams() const { + DCHECK_GE(dynamic_stream_map_.size() + pending_stream_map_.size(), + num_dynamic_incoming_streams_); + return dynamic_stream_map_.size() + pending_stream_map_.size() - + num_dynamic_incoming_streams_; +} + +size_t QuicSession::GetNumDrainingOutgoingStreams() const { + DCHECK_GE(draining_streams_.size(), num_draining_incoming_streams_); + return draining_streams_.size() - num_draining_incoming_streams_; +} + +size_t QuicSession::GetNumLocallyClosedOutgoingStreamsHighestOffset() const { + DCHECK_GE(locally_closed_streams_highest_offset_.size(), + num_locally_closed_incoming_streams_highest_offset_); + return locally_closed_streams_highest_offset_.size() - + num_locally_closed_incoming_streams_highest_offset_; +} + +bool QuicSession::IsConnectionFlowControlBlocked() const { + return flow_controller_.IsBlocked(); +} + +bool QuicSession::IsStreamFlowControlBlocked() { + for (auto const& kv : static_stream_map_) { + if (kv.second->flow_controller()->IsBlocked()) { + return true; + } + } + for (auto const& kv : dynamic_stream_map_) { + if (kv.second->flow_controller()->IsBlocked()) { + return true; + } + } + return false; +} + +size_t QuicSession::MaxAvailableBidirectionalStreams() const { + if (connection()->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.GetMaxAllowdIncomingBidirectionalStreams(); + } + return stream_id_manager_.MaxAvailableStreams(); +} + +size_t QuicSession::MaxAvailableUnidirectionalStreams() const { + if (connection()->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.GetMaxAllowdIncomingUnidirectionalStreams(); + } + return stream_id_manager_.MaxAvailableStreams(); +} + +bool QuicSession::IsIncomingStream(QuicStreamId id) const { + if (connection()->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.IsIncomingStream(id); + } + return stream_id_manager_.IsIncomingStream(id); +} + +void QuicSession::OnStreamDoneWaitingForAcks(QuicStreamId id) { + auto it = zombie_streams_.find(id); + if (it == zombie_streams_.end()) { + return; + } + + closed_streams_.push_back(std::move(it->second)); + if (!closed_streams_clean_up_alarm_->IsSet()) { + closed_streams_clean_up_alarm_->Set(connection_->clock()->ApproximateNow()); + } + zombie_streams_.erase(it); + // Do not retransmit data of a closed stream. + streams_with_pending_retransmission_.erase(id); +} + +QuicStream* QuicSession::GetStream(QuicStreamId id) const { + if (id <= largest_static_stream_id_) { + auto static_stream = static_stream_map_.find(id); + if (static_stream != static_stream_map_.end()) { + return static_stream->second; + } + } + + auto active_stream = dynamic_stream_map_.find(id); + if (active_stream != dynamic_stream_map_.end()) { + return active_stream->second.get(); + } + auto zombie_stream = zombie_streams_.find(id); + if (zombie_stream != zombie_streams_.end()) { + return zombie_stream->second.get(); + } + return nullptr; +} + +bool QuicSession::OnFrameAcked(const QuicFrame& frame, + QuicTime::Delta ack_delay_time) { + if (frame.type == MESSAGE_FRAME) { + OnMessageAcked(frame.message_frame->message_id); + return true; + } + if (frame.type == CRYPTO_FRAME) { + return GetMutableCryptoStream()->OnCryptoFrameAcked(*frame.crypto_frame, + ack_delay_time); + } + if (frame.type != STREAM_FRAME) { + return control_frame_manager_.OnControlFrameAcked(frame); + } + bool new_stream_data_acked = false; + QuicStream* stream = GetStream(frame.stream_frame.stream_id); + // Stream can already be reset when sent frame gets acked. + if (stream != nullptr) { + QuicByteCount newly_acked_length = 0; + new_stream_data_acked = stream->OnStreamFrameAcked( + frame.stream_frame.offset, frame.stream_frame.data_length, + frame.stream_frame.fin, ack_delay_time, &newly_acked_length); + if (!stream->HasPendingRetransmission()) { + streams_with_pending_retransmission_.erase(stream->id()); + } + } + return new_stream_data_acked; +} + +void QuicSession::OnStreamFrameRetransmitted(const QuicStreamFrame& frame) { + QuicStream* stream = GetStream(frame.stream_id); + if (stream == nullptr) { + QUIC_BUG << "Stream: " << frame.stream_id << " is closed when " << frame + << " is retransmitted."; + connection()->CloseConnection( + QUIC_INTERNAL_ERROR, "Attempt to retransmit frame of a closed stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + stream->OnStreamFrameRetransmitted(frame.offset, frame.data_length, + frame.fin); +} + +void QuicSession::OnFrameLost(const QuicFrame& frame) { + if (frame.type == MESSAGE_FRAME) { + OnMessageLost(frame.message_frame->message_id); + return; + } + if (frame.type == CRYPTO_FRAME) { + GetMutableCryptoStream()->OnCryptoFrameLost(frame.crypto_frame); + return; + } + if (frame.type != STREAM_FRAME) { + control_frame_manager_.OnControlFrameLost(frame); + return; + } + QuicStream* stream = GetStream(frame.stream_frame.stream_id); + if (stream == nullptr) { + return; + } + stream->OnStreamFrameLost(frame.stream_frame.offset, + frame.stream_frame.data_length, + frame.stream_frame.fin); + if (stream->HasPendingRetransmission() && + !QuicContainsKey(streams_with_pending_retransmission_, + frame.stream_frame.stream_id)) { + streams_with_pending_retransmission_.insert( + std::make_pair(frame.stream_frame.stream_id, true)); + } +} + +void QuicSession::RetransmitFrames(const QuicFrames& frames, + TransmissionType type) { + QuicConnection::ScopedPacketFlusher retransmission_flusher( + connection_, QuicConnection::NO_ACK); + SetTransmissionType(type); + for (const QuicFrame& frame : frames) { + if (frame.type == MESSAGE_FRAME) { + // Do not retransmit MESSAGE frames. + continue; + } + if (frame.type == CRYPTO_FRAME) { + GetMutableCryptoStream()->RetransmitData(frame.crypto_frame); + continue; + } + if (frame.type != STREAM_FRAME) { + if (!control_frame_manager_.RetransmitControlFrame(frame)) { + break; + } + continue; + } + QuicStream* stream = GetStream(frame.stream_frame.stream_id); + if (stream != nullptr && + !stream->RetransmitStreamData(frame.stream_frame.offset, + frame.stream_frame.data_length, + frame.stream_frame.fin)) { + break; + } + } +} + +bool QuicSession::IsFrameOutstanding(const QuicFrame& frame) const { + if (frame.type == MESSAGE_FRAME) { + return false; + } + if (frame.type == CRYPTO_FRAME) { + return GetCryptoStream()->IsFrameOutstanding( + frame.crypto_frame->level, frame.crypto_frame->offset, + frame.crypto_frame->data_length); + } + if (frame.type != STREAM_FRAME) { + return control_frame_manager_.IsControlFrameOutstanding(frame); + } + QuicStream* stream = GetStream(frame.stream_frame.stream_id); + return stream != nullptr && + stream->IsStreamFrameOutstanding(frame.stream_frame.offset, + frame.stream_frame.data_length, + frame.stream_frame.fin); +} + +bool QuicSession::HasUnackedCryptoData() const { + const QuicCryptoStream* crypto_stream = GetCryptoStream(); + if (crypto_stream->IsWaitingForAcks()) { + return true; + } + if (GetQuicReloadableFlag(quic_fix_has_pending_crypto_data) && + crypto_stream->HasBufferedData()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_has_pending_crypto_data); + return true; + } + return false; +} + +WriteStreamDataResult QuicSession::WriteStreamData(QuicStreamId id, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) { + QuicStream* stream = GetStream(id); + if (stream == nullptr) { + // This causes the connection to be closed because of failed to serialize + // packet. + QUIC_BUG << "Stream " << id << " does not exist when trying to write data."; + return STREAM_MISSING; + } + if (stream->WriteStreamData(offset, data_length, writer)) { + return WRITE_SUCCESS; + } + return WRITE_FAILED; +} + +bool QuicSession::WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) { + return GetMutableCryptoStream()->WriteCryptoFrame(level, offset, data_length, + writer); +} + +QuicUint128 QuicSession::GetStatelessResetToken() const { + return QuicUtils::GenerateStatelessResetToken(connection_->connection_id()); +} + +bool QuicSession::RetransmitLostData() { + QuicConnection::ScopedPacketFlusher retransmission_flusher( + connection_, QuicConnection::SEND_ACK_IF_QUEUED); + // Retransmit crypto data first. + bool uses_crypto_frames = connection_->transport_version() >= QUIC_VERSION_47; + QuicCryptoStream* crypto_stream = GetMutableCryptoStream(); + if (uses_crypto_frames && crypto_stream->HasPendingCryptoRetransmission()) { + SetTransmissionType(HANDSHAKE_RETRANSMISSION); + crypto_stream->WritePendingCryptoRetransmission(); + } + // Retransmit crypto data in stream 1 frames (version < 47). + if (!uses_crypto_frames && + QuicContainsKey( + streams_with_pending_retransmission_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()))) { + SetTransmissionType(HANDSHAKE_RETRANSMISSION); + // Retransmit crypto data first. + QuicStream* crypto_stream = GetStream( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); + crypto_stream->OnCanWrite(); + DCHECK(CheckStreamWriteBlocked(crypto_stream)); + if (crypto_stream->HasPendingRetransmission()) { + // Connection is write blocked. + return false; + } else { + streams_with_pending_retransmission_.erase( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); + } + } + if (control_frame_manager_.HasPendingRetransmission()) { + SetTransmissionType(LOSS_RETRANSMISSION); + control_frame_manager_.OnCanWrite(); + if (control_frame_manager_.HasPendingRetransmission()) { + return false; + } + } + while (!streams_with_pending_retransmission_.empty()) { + if (!connection_->CanWriteStreamData()) { + break; + } + // Retransmit lost data on headers and data streams. + const QuicStreamId id = streams_with_pending_retransmission_.begin()->first; + QuicStream* stream = GetStream(id); + if (stream != nullptr) { + SetTransmissionType(LOSS_RETRANSMISSION); + stream->OnCanWrite(); + DCHECK(CheckStreamWriteBlocked(stream)); + if (stream->HasPendingRetransmission()) { + // Connection is write blocked. + break; + } else if (!streams_with_pending_retransmission_.empty() && + streams_with_pending_retransmission_.begin()->first == id) { + // Retransmit lost data may cause connection close. If this stream + // has not yet sent fin, a RST_STREAM will be sent and it will be + // removed from streams_with_pending_retransmission_. + streams_with_pending_retransmission_.pop_front(); + } + } else { + QUIC_BUG << "Try to retransmit data of a closed stream"; + streams_with_pending_retransmission_.pop_front(); + } + } + + return streams_with_pending_retransmission_.empty(); +} + +void QuicSession::NeuterUnencryptedData() { + if (connection_->session_decides_what_to_write()) { + QuicCryptoStream* crypto_stream = GetMutableCryptoStream(); + crypto_stream->NeuterUnencryptedStreamData(); + if (!crypto_stream->HasPendingRetransmission()) { + streams_with_pending_retransmission_.erase( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); + } + } + connection_->NeuterUnencryptedPackets(); +} + +void QuicSession::SetTransmissionType(TransmissionType type) { + connection_->SetTransmissionType(type); +} + +MessageResult QuicSession::SendMessage(QuicMemSliceSpan message) { + if (!IsEncryptionEstablished()) { + return {MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0}; + } + MessageStatus result = + connection_->SendMessage(last_message_id_ + 1, message); + if (result == MESSAGE_STATUS_SUCCESS) { + return {result, ++last_message_id_}; + } + return {result, 0}; +} + +void QuicSession::OnMessageAcked(QuicMessageId message_id) { + QUIC_DVLOG(1) << ENDPOINT << "message " << message_id << " gets acked."; +} + +void QuicSession::OnMessageLost(QuicMessageId message_id) { + QUIC_DVLOG(1) << ENDPOINT << "message " << message_id + << " is considered lost"; +} + +void QuicSession::CleanUpClosedStreams() { + closed_streams_.clear(); +} + +bool QuicSession::session_decides_what_to_write() const { + return connection_->session_decides_what_to_write(); +} + +QuicPacketLength QuicSession::GetLargestMessagePayload() const { + return connection_->GetLargestMessagePayload(); +} + +void QuicSession::SendStopSending(uint16_t code, QuicStreamId stream_id) { + control_frame_manager_.WriteOrBufferStopSending(code, stream_id); +} + +void QuicSession::OnCanCreateNewOutgoingStream() {} + +QuicStreamId QuicSession::next_outgoing_bidirectional_stream_id() const { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.next_outgoing_bidirectional_stream_id(); + } + return stream_id_manager_.next_outgoing_stream_id(); +} + +QuicStreamId QuicSession::next_outgoing_unidirectional_stream_id() const { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.next_outgoing_unidirectional_stream_id(); + } + return stream_id_manager_.next_outgoing_stream_id(); +} + +bool QuicSession::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) { + return v99_streamid_manager_.OnMaxStreamIdFrame(frame); +} + +bool QuicSession::OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& frame) { + return v99_streamid_manager_.OnStreamIdBlockedFrame(frame); +} + +size_t QuicSession::max_open_incoming_bidirectional_streams() const { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.GetMaxAllowdIncomingBidirectionalStreams(); + } + return stream_id_manager_.max_open_incoming_streams(); +} + +size_t QuicSession::max_open_incoming_unidirectional_streams() const { + if (connection_->transport_version() == QUIC_VERSION_99) { + return v99_streamid_manager_.GetMaxAllowdIncomingUnidirectionalStreams(); + } + return stream_id_manager_.max_open_incoming_streams(); +} + +#undef ENDPOINT // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h new file mode 100644 index 0000000..8976842 --- /dev/null +++ b/quic/core/quic_session.h
@@ -0,0 +1,692 @@ +// Copyright (c) 2012 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. + +// A QuicSession, which demuxes a single connection to individual streams. + +#ifndef QUICHE_QUIC_CORE_QUIC_SESSION_H_ +#define QUICHE_QUIC_CORE_QUIC_SESSION_H_ + +#include <cstddef> +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_control_frame_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h" +#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" +#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h" +#include "net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class QuicCryptoStream; +class QuicFlowController; +class QuicStream; +class QuicStreamIdManager; + +namespace test { +class QuicSessionPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, + public SessionNotifierInterface, + public QuicStreamFrameDataProducer { + public: + // An interface from the session to the entity owning the session. + // This lets the session notify its owner (the Dispatcher) when the connection + // is closed, blocked, or added/removed from the time-wait list. + class Visitor { + public: + virtual ~Visitor() {} + + // Called when the connection is closed after the streams have been closed. + virtual void OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) = 0; + + // Called when the session has become write blocked. + virtual void OnWriteBlocked(QuicBlockedWriterInterface* blocked_writer) = 0; + + // Called when the session receives reset on a stream from the peer. + virtual void OnRstStreamReceived(const QuicRstStreamFrame& frame) = 0; + + // Called when the session receives a STOP_SENDING for a stream from the + // peer. + virtual void OnStopSendingReceived(const QuicStopSendingFrame& frame) = 0; + }; + + // CryptoHandshakeEvent enumerates the events generated by a QuicCryptoStream. + enum CryptoHandshakeEvent { + // ENCRYPTION_FIRST_ESTABLISHED indicates that a full client hello has been + // sent by a client and that subsequent packets will be encrypted. (Client + // only.) + ENCRYPTION_FIRST_ESTABLISHED, + // ENCRYPTION_REESTABLISHED indicates that a client hello was rejected by + // the server and thus the encryption key has been updated. Therefore the + // connection should resend any packets that were sent under + // ENCRYPTION_ZERO_RTT. (Client only.) + ENCRYPTION_REESTABLISHED, + // HANDSHAKE_CONFIRMED, in a client, indicates the server has accepted + // our handshake. In a server it indicates that a full, valid client hello + // has been received. (Client and server.) + HANDSHAKE_CONFIRMED, + }; + + // Does not take ownership of |connection| or |visitor|. + QuicSession(QuicConnection* connection, + Visitor* owner, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions); + QuicSession(const QuicSession&) = delete; + QuicSession& operator=(const QuicSession&) = delete; + + ~QuicSession() override; + + virtual void Initialize(); + + // QuicConnectionVisitorInterface methods: + void OnStreamFrame(const QuicStreamFrame& frame) override; + void OnCryptoFrame(const QuicCryptoFrame& frame) override; + void OnRstStream(const QuicRstStreamFrame& frame) override; + void OnGoAway(const QuicGoAwayFrame& frame) override; + void OnMessageReceived(QuicStringPiece message) override; + void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override; + void OnBlockedFrame(const QuicBlockedFrame& frame) override; + void OnConnectionClosed(QuicErrorCode error, + const QuicString& error_details, + ConnectionCloseSource source) override; + void OnWriteBlocked() override; + void OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) override; + void OnConnectivityProbeReceived( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address) override; + void OnCanWrite() override; + void OnCongestionWindowChange(QuicTime /*now*/) override {} + void OnConnectionMigration(AddressChangeType type) override {} + // Adds a connection level WINDOW_UPDATE frame. + void OnAckNeedsRetransmittableFrame() override; + void SendPing() override; + bool WillingAndAbleToWrite() const override; + bool HasPendingHandshake() const override; + void OnPathDegrading() override; + bool AllowSelfAddressChange() const override; + void OnForwardProgressConfirmed() override; + bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override; + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override; + bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override; + + // QuicStreamFrameDataProducer + WriteStreamDataResult WriteStreamData(QuicStreamId id, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override; + bool WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override; + + // SessionNotifierInterface methods: + bool OnFrameAcked(const QuicFrame& frame, + QuicTime::Delta ack_delay_time) override; + void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override; + void OnFrameLost(const QuicFrame& frame) override; + void RetransmitFrames(const QuicFrames& frames, + TransmissionType type) override; + bool IsFrameOutstanding(const QuicFrame& frame) const override; + bool HasUnackedCryptoData() const override; + + // Called on every incoming packet. Passes |packet| through to |connection_|. + virtual void ProcessUdpPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + const QuicReceivedPacket& packet); + + // Called by streams when they want to write data to the peer. + // Returns a pair with the number of bytes consumed from data, and a boolean + // indicating if the fin bit was consumed. This does not indicate the data + // has been sent on the wire: it may have been turned into a packet and queued + // if the socket was unexpectedly blocked. + virtual QuicConsumedData WritevData(QuicStream* stream, + QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state); + + // Called by application to send |message|. Data copy can be avoided if + // |message| is provided in reference counted memory. + // Please note, |message| provided in reference counted memory would be moved + // internally when message is successfully sent. Thereafter, it would be + // undefined behavior if callers try to access the slices through their own + // copy of the span object. + // Returns the message result which includes the message status and message ID + // (valid if the write succeeds). SendMessage flushes a message packet even it + // is not full. If the application wants to bundle other data in the same + // packet, please consider adding a packet flusher around the SendMessage + // and/or WritevData calls. + // + // OnMessageAcked and OnMessageLost are called when a particular message gets + // acked or lost. + // + // Note that SendMessage will fail with status = MESSAGE_STATUS_BLOCKED + // if connection is congestion control blocked or underlying socket is write + // blocked. In this case the caller can retry sending message again when + // connection becomes available, for example after getting OnCanWrite() + // callback. + MessageResult SendMessage(QuicMemSliceSpan message); + + // Called when message with |message_id| gets acked. + virtual void OnMessageAcked(QuicMessageId message_id); + + // Called when message with |message_id| is considered as lost. + virtual void OnMessageLost(QuicMessageId message_id); + + // Called by control frame manager when it wants to write control frames to + // the peer. Returns true if |frame| is consumed, false otherwise. + virtual bool WriteControlFrame(const QuicFrame& frame); + + // Called by streams when they want to close the stream in both directions. + virtual void SendRstStream(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written); + + // Called when the session wants to go away and not accept any new streams. + virtual void SendGoAway(QuicErrorCode error_code, const QuicString& reason); + + // Sends a BLOCKED frame. + virtual void SendBlocked(QuicStreamId id); + + // Sends a WINDOW_UPDATE frame. + virtual void SendWindowUpdate(QuicStreamId id, QuicStreamOffset byte_offset); + + // Send a MAX_STREAM_ID frame. + void SendMaxStreamId(QuicStreamId max_allowed_incoming_id); + + // Send a STREAM_ID_BLOCKED frame. + void SendStreamIdBlocked(QuicStreamId max_allowed_outgoing_id); + + // Create and transmit a STOP_SENDING frame + virtual void SendStopSending(uint16_t code, QuicStreamId stream_id); + + // Removes the stream associated with 'stream_id' from the active stream map. + virtual void CloseStream(QuicStreamId stream_id); + + // Returns true if outgoing packets will be encrypted, even if the server + // hasn't confirmed the handshake yet. + virtual bool IsEncryptionEstablished() const; + + // For a client, returns true if the server has confirmed our handshake. For + // a server, returns true if a full, valid client hello has been received. + virtual bool IsCryptoHandshakeConfirmed() const; + + // Called by the QuicCryptoStream when a new QuicConfig has been negotiated. + virtual void OnConfigNegotiated(); + + // Called by the QuicCryptoStream when the handshake enters a new state. + // + // Clients will call this function in the order: + // ENCRYPTION_FIRST_ESTABLISHED + // zero or more ENCRYPTION_REESTABLISHED + // HANDSHAKE_CONFIRMED + // + // Servers will simply call it once with HANDSHAKE_CONFIRMED. + virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event); + + // Called by the QuicCryptoStream when a handshake message is sent. + virtual void OnCryptoHandshakeMessageSent( + const CryptoHandshakeMessage& message); + + // Called by the QuicCryptoStream when a handshake message is received. + virtual void OnCryptoHandshakeMessageReceived( + const CryptoHandshakeMessage& message); + + // Called by the stream on creation to set priority in the write blocked list. + virtual void RegisterStreamPriority(QuicStreamId id, + bool is_static, + spdy::SpdyPriority priority); + // Called by the stream on deletion to clear priority from the write blocked + // list. + virtual void UnregisterStreamPriority(QuicStreamId id, bool is_static); + // Called by the stream on SetPriority to update priority on the write blocked + // list. + virtual void UpdateStreamPriority(QuicStreamId id, + spdy::SpdyPriority new_priority); + + // Returns mutable config for this session. Returned config is owned + // by QuicSession. + QuicConfig* config(); + + // Returns true if the stream existed previously and has been closed. + // Returns false if the stream is still active or if the stream has + // not yet been created. + bool IsClosedStream(QuicStreamId id); + + QuicConnection* connection() { return connection_; } + const QuicConnection* connection() const { return connection_; } + size_t num_active_requests() const { return dynamic_stream_map_.size(); } + const QuicSocketAddress& peer_address() const { + return connection_->peer_address(); + } + const QuicSocketAddress& self_address() const { + return connection_->self_address(); + } + QuicConnectionId connection_id() const { + return connection_->connection_id(); + } + + // Returns the number of currently open streams, excluding the reserved + // headers and crypto streams, and never counting unfinished streams. + size_t GetNumActiveStreams() const; + + // Returns the number of currently draining streams. + size_t GetNumDrainingStreams() const; + + // Returns the number of currently open peer initiated streams, excluding the + // reserved headers and crypto streams. + size_t GetNumOpenIncomingStreams() const; + + // Returns the number of currently open self initiated streams, excluding the + // reserved headers and crypto streams. + size_t GetNumOpenOutgoingStreams() const; + + // Add the stream to the session's write-blocked list because it is blocked by + // connection-level flow control but not by its own stream-level flow control. + // The stream will be given a chance to write when a connection-level + // WINDOW_UPDATE arrives. + void MarkConnectionLevelWriteBlocked(QuicStreamId id); + + // Called when stream |id| is done waiting for acks either because all data + // gets acked or is not interested in data being acked (which happens when + // a stream is reset because of an error). + void OnStreamDoneWaitingForAcks(QuicStreamId id); + + // Called to cancel retransmission of unencypted crypto stream data. + void NeuterUnencryptedData(); + + // Returns true if the session has data to be sent, either queued in the + // connection, or in a write-blocked stream. + bool HasDataToWrite() const; + + // Returns the largest payload that will fit into a single MESSAGE frame. + // Because overhead can vary during a connection, this method should be + // checked for every message. + QuicPacketLength GetLargestMessagePayload() const; + + bool goaway_sent() const { return goaway_sent_; } + + bool goaway_received() const { return goaway_received_; } + + QuicErrorCode error() const { return error_; } + + Perspective perspective() const { return connection_->perspective(); } + + QuicFlowController* flow_controller() { return &flow_controller_; } + + // Returns true if connection is flow controller blocked. + bool IsConnectionFlowControlBlocked() const; + + // Returns true if any stream is flow controller blocked. + bool IsStreamFlowControlBlocked(); + + size_t max_open_incoming_bidirectional_streams() const; + size_t max_open_incoming_unidirectional_streams() const; + + size_t MaxAvailableBidirectionalStreams() const; + size_t MaxAvailableUnidirectionalStreams() const; + + // Returns existing static or dynamic stream with id = |stream_id|. If no + // such stream exists, and |stream_id| is a peer-created dynamic stream id, + // then a new stream is created and returned. In all other cases, nullptr is + // returned. + QuicStream* GetOrCreateStream(const QuicStreamId stream_id); + + // Mark a stream as draining. + virtual void StreamDraining(QuicStreamId id); + + // Returns true if this stream should yield writes to another blocked stream. + bool ShouldYield(QuicStreamId stream_id); + + // Set transmission type of next sending packets. + void SetTransmissionType(TransmissionType type); + + // Clean up closed_streams_. + void CleanUpClosedStreams(); + + bool session_decides_what_to_write() const; + + const ParsedQuicVersionVector& supported_versions() const { + return supported_versions_; + } + + // Called when new outgoing streams are available to be opened. This occurs + // when an extant, open, stream is moved to draining or closed. The default + // implementation does nothing. + virtual void OnCanCreateNewOutgoingStream(); + + QuicStreamId next_outgoing_bidirectional_stream_id() const; + QuicStreamId next_outgoing_unidirectional_stream_id() const; + + // Return true if given stream is peer initiated. + bool IsIncomingStream(QuicStreamId id) const; + + size_t GetNumLocallyClosedOutgoingStreamsHighestOffset() const; + + size_t num_locally_closed_incoming_streams_highest_offset() const { + return num_locally_closed_incoming_streams_highest_offset_; + } + + // Does actual work of sending reset-stream or reset-stream&stop-sending + // If the connection is not version 99/IETF QUIC, will always send a + // RESET_STREAM and close_write_side_only is ignored. If the connection is + // IETF QUIC/Version 99 then will send a RESET_STREAM and STOP_SENDING if + // close_write_side_only is false, just a RESET_STREAM if + // close_write_side_only is true. + virtual void SendRstStreamInner(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written, + bool close_write_side_only); + + protected: + using StaticStreamMap = QuicSmallMap<QuicStreamId, QuicStream*, 2>; + + using DynamicStreamMap = + QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>; + + using PendingStreamMap = + QuicSmallMap<QuicStreamId, std::unique_ptr<PendingStream>, 10>; + + using ClosedStreams = std::vector<std::unique_ptr<QuicStream>>; + + using ZombieStreamMap = + QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>; + + // Creates a new stream to handle a peer-initiated stream. + // Caller does not own the returned stream. + // Returns nullptr and does error handling if the stream can not be created. + virtual QuicStream* CreateIncomingStream(QuicStreamId id) = 0; + virtual QuicStream* CreateIncomingStream(PendingStream pending) = 0; + + // Return the reserved crypto stream. + virtual QuicCryptoStream* GetMutableCryptoStream() = 0; + + // Return the reserved crypto stream as a constant pointer. + virtual const QuicCryptoStream* GetCryptoStream() const = 0; + + // Adds |stream| to the dynamic stream map. + virtual void ActivateStream(std::unique_ptr<QuicStream> stream); + + // Returns the stream ID for a new outgoing bidirectional/unidirectional + // stream, and increments the underlying counter. + QuicStreamId GetNextOutgoingBidirectionalStreamId(); + QuicStreamId GetNextOutgoingUnidirectionalStreamId(); + + // Indicates whether the next outgoing bidirectional/unidirectional stream ID + // can be allocated or not. The test for version-99/IETF QUIC is whether it + // will exceed the maximum-stream-id or not. For non-version-99 (Google) QUIC + // it checks whether the next stream would exceed the limit on the number of + // open streams. + bool CanOpenNextOutgoingBidirectionalStream(); + bool CanOpenNextOutgoingUnidirectionalStream(); + + // Returns the number of open dynamic streams. + uint64_t GetNumOpenDynamicStreams() const; + + // Returns existing stream with id = |stream_id|. If no such stream exists, + // and |stream_id| is a peer-created id, then a new stream is created and + // returned. However if |stream_id| is a locally-created id and no such stream + // exists, the connection is closed. + // Caller does not own the returned stream. + QuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id); + + // Performs the work required to close |stream_id|. If |locally_reset| + // then the stream has been reset by this endpoint, not by the peer. + virtual void CloseStreamInner(QuicStreamId stream_id, bool locally_reset); + + // When a stream is closed locally, it may not yet know how many bytes the + // peer sent on that stream. + // When this data arrives (via stream frame w. FIN, trailing headers, or RST) + // this method is called, and correctly updates the connection level flow + // controller. + virtual void OnFinalByteOffsetReceived(QuicStreamId id, + QuicStreamOffset final_byte_offset); + + // Returns true if incoming streams should be buffered until the first + // byte of the stream arrives. + virtual bool ShouldBufferIncomingStream(QuicStreamId id) const { + return false; + } + + // Register (|id|, |stream|) with the static stream map. Override previous + // registrations with the same id. + void RegisterStaticStream(QuicStreamId id, QuicStream* stream); + const StaticStreamMap& static_streams() const { return static_stream_map_; } + + DynamicStreamMap& dynamic_streams() { return dynamic_stream_map_; } + const DynamicStreamMap& dynamic_streams() const { + return dynamic_stream_map_; + } + + ClosedStreams* closed_streams() { return &closed_streams_; } + + const ZombieStreamMap& zombie_streams() const { return zombie_streams_; } + + void set_largest_peer_created_stream_id( + QuicStreamId largest_peer_created_stream_id); + + void set_error(QuicErrorCode error) { error_ = error; } + QuicWriteBlockedList* write_blocked_streams() { + return &write_blocked_streams_; + } + + size_t GetNumDynamicOutgoingStreams() const; + + size_t GetNumDrainingOutgoingStreams() const; + + // Returns true if the stream is still active. + bool IsOpenStream(QuicStreamId id); + + // Close connection when receive a frame for a locally-created nonexistant + // stream. + // Prerequisite: IsClosedStream(stream_id) == false + // Server session might need to override this method to allow server push + // stream to be promised before creating an active stream. + virtual void HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id); + + virtual bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id); + + void InsertLocallyClosedStreamsHighestOffset(const QuicStreamId id, + QuicStreamOffset offset); + // If stream is a locally closed stream, this RST will update FIN offset. + // Otherwise stream is a preserved stream and the behavior of it depends on + // derived class's own implementation. + virtual void HandleRstOnValidNonexistentStream( + const QuicRstStreamFrame& frame); + + // Returns a stateless reset token which will be included in the public reset + // packet. + virtual QuicUint128 GetStatelessResetToken() const; + + QuicControlFrameManager& control_frame_manager() { + return control_frame_manager_; + } + + const LegacyQuicStreamIdManager& stream_id_manager() const { + return stream_id_manager_; + } + + // A StreamHandler represents an object which can receive a STREAM or + // or RST_STREAM frame. + struct StreamHandler { + StreamHandler() : is_pending(false), stream(nullptr) {} + + // Creates a StreamHandler wrapping a QuicStream. + explicit StreamHandler(QuicStream* stream) + : is_pending(false), stream(stream) {} + + // Creates a StreamHandler wrapping a PendingStream. + explicit StreamHandler(PendingStream* pending) + : is_pending(true), pending(pending) { + DCHECK(pending != nullptr); + } + + // True if this handler contains a non-null PendingStream, false otherwise. + bool is_pending; + union { + QuicStream* stream; + PendingStream* pending; + }; + }; + + StreamHandler GetOrCreateStreamImpl(QuicStreamId stream_id, bool may_buffer); + + private: + friend class test::QuicSessionPeer; + + // Called in OnConfigNegotiated when we receive a new stream level flow + // control window in a negotiated config. Closes the connection if invalid. + void OnNewStreamFlowControlWindow(QuicStreamOffset new_window); + + // Called in OnConfigNegotiated when we receive a new connection level flow + // control window in a negotiated config. Closes the connection if invalid. + void OnNewSessionFlowControlWindow(QuicStreamOffset new_window); + + // Debug helper for |OnCanWrite()|, check that OnStreamWrite() makes + // forward progress. Returns false if busy loop detected. + bool CheckStreamNotBusyLooping(QuicStream* stream, + uint64_t previous_bytes_written, + bool previous_fin_sent); + + // Debug helper for OnCanWrite. Check that after QuicStream::OnCanWrite(), + // if stream has buffered data and is not stream level flow control blocked, + // it has to be in the write blocked list. + bool CheckStreamWriteBlocked(QuicStream* stream) const; + + // Called in OnConfigNegotiated for Finch trials to measure performance of + // starting with larger flow control receive windows. + void AdjustInitialFlowControlWindows(size_t stream_window); + + // Find stream with |id|, returns nullptr if the stream does not exist or + // closed. + QuicStream* GetStream(QuicStreamId id) const; + + StreamHandler GetOrCreateDynamicStreamImpl(QuicStreamId stream_id, + bool may_buffer); + + // Let streams and control frame managers retransmit lost data, returns true + // if all lost data is retransmitted. Returns false otherwise. + bool RetransmitLostData(); + + // Closes the pending stream |stream_id| before it has been created. + void ClosePendingStream(QuicStreamId stream_id); + + // Keep track of highest received byte offset of locally closed streams, while + // waiting for a definitive final highest offset from the peer. + std::map<QuicStreamId, QuicStreamOffset> + locally_closed_streams_highest_offset_; + + QuicConnection* connection_; + + // May be null. + Visitor* visitor_; + + // A list of streams which need to write more data. Stream register + // themselves in their constructor, and unregisterm themselves in their + // destructors, so the write blocked list must outlive all streams. + QuicWriteBlockedList write_blocked_streams_; + + ClosedStreams closed_streams_; + // Streams which are closed, but need to be kept alive. Currently, the only + // reason is the stream's sent data (including FIN) does not get fully acked. + ZombieStreamMap zombie_streams_; + + QuicConfig config_; + + // Static streams, such as crypto and header streams. Owned by child classes + // that create these streams. + StaticStreamMap static_stream_map_; + + // Map from StreamId to pointers to streams. Owns the streams. + DynamicStreamMap dynamic_stream_map_; + + // Map from StreamId to PendingStreams for peer-created unidirectional streams + // which are waiting for the first byte of payload to arrive. + PendingStreamMap pending_stream_map_; + + // Set of stream ids that are "draining" -- a FIN has been sent and received, + // but the stream object still exists because not all the received data has + // been consumed. + QuicUnorderedSet<QuicStreamId> draining_streams_; + + // TODO(fayang): Consider moving LegacyQuicStreamIdManager into + // UberQuicStreamIdManager. + // Manages stream IDs for Google QUIC. + LegacyQuicStreamIdManager stream_id_manager_; + + // Manages stream IDs for version99/IETF QUIC + UberQuicStreamIdManager v99_streamid_manager_; + + // A counter for peer initiated streams which are in the dynamic_stream_map_. + size_t num_dynamic_incoming_streams_; + + // A counter for peer initiated streams which are in the draining_streams_. + size_t num_draining_incoming_streams_; + + // A counter for peer initiated streams which are in the + // locally_closed_streams_highest_offset_. + size_t num_locally_closed_incoming_streams_highest_offset_; + + // The latched error with which the connection was closed. + QuicErrorCode error_; + + // Used for connection-level flow control. + QuicFlowController flow_controller_; + + // The stream id which was last popped in OnCanWrite, or 0, if not under the + // call stack of OnCanWrite. + QuicStreamId currently_writing_stream_id_; + + // The largest stream id in |static_stream_map_|. + QuicStreamId largest_static_stream_id_; + + // Cached value of whether the crypto handshake has been confirmed. + bool is_handshake_confirmed_; + + // Whether a GoAway has been sent. + bool goaway_sent_; + + // Whether a GoAway has been received. + bool goaway_received_; + + QuicControlFrameManager control_frame_manager_; + + // Id of latest successfully sent message. + QuicMessageId last_message_id_; + + // TODO(fayang): switch to linked_hash_set when chromium supports it. The bool + // is not used here. + // List of streams with pending retransmissions. + QuicLinkedHashMap<QuicStreamId, bool> streams_with_pending_retransmission_; + + // Clean up closed_streams_ when this alarm fires. + std::unique_ptr<QuicAlarm> closed_streams_clean_up_alarm_; + + // Supported version list used by the crypto handshake only. Please note, this + // list may be a superset of the connection framer's supported versions. + ParsedQuicVersionVector supported_versions_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_SESSION_H_
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc new file mode 100644 index 0000000..928f690 --- /dev/null +++ b/quic/core/quic_session_test.cc
@@ -0,0 +1,2347 @@ +// Copyright (c) 2012 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/quic_session.h" + +#include <cstdint> +#include <set> +#include <utility> + +#include "testing/gmock/include/gmock/gmock.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using spdy::kV3HighestPriority; +using spdy::SpdyPriority; +using testing::_; +using testing::AtLeast; +using testing::InSequence; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::StrictMock; +using testing::WithArg; + +namespace quic { +namespace test { +namespace { + +class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { + public: + explicit TestCryptoStream(QuicSession* session) + : QuicCryptoStream(session), + QuicCryptoHandshaker(this, session), + encryption_established_(false), + handshake_confirmed_(false), + params_(new QuicCryptoNegotiatedParameters) {} + + void OnHandshakeMessage(const CryptoHandshakeMessage& /*message*/) override { + encryption_established_ = true; + handshake_confirmed_ = true; + CryptoHandshakeMessage msg; + QuicString error_details; + session()->config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session()->config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + session()->config()->ToHandshakeMessage(&msg); + const QuicErrorCode error = + session()->config()->ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + session()->OnConfigNegotiated(); + session()->connection()->SetDefaultEncryptionLevel( + ENCRYPTION_FORWARD_SECURE); + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + } + + // QuicCryptoStream implementation + bool encryption_established() const override { + return encryption_established_; + } + bool handshake_confirmed() const override { return handshake_confirmed_; } + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override { + return *params_; + } + CryptoMessageParser* crypto_message_parser() override { + return QuicCryptoHandshaker::crypto_message_parser(); + } + + MOCK_METHOD0(OnCanWrite, void()); + bool HasPendingCryptoRetransmission() override { return false; } + + MOCK_CONST_METHOD0(HasPendingRetransmission, bool()); + + private: + using QuicCryptoStream::session; + + bool encryption_established_; + bool handshake_confirmed_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; +}; + +class TestStream : public QuicStream { + public: + TestStream(QuicStreamId id, QuicSession* session, StreamType type) + : QuicStream(id, session, /*is_static=*/false, type) {} + + TestStream(PendingStream pending, StreamType type) + : QuicStream(std::move(pending), type) {} + + using QuicStream::CloseReadSide; + using QuicStream::CloseWriteSide; + using QuicStream::WriteMemSlices; + using QuicStream::WritevData; + + void OnDataAvailable() override {} + + MOCK_METHOD0(OnCanWrite, void()); + MOCK_METHOD3(RetransmitStreamData, + bool(QuicStreamOffset, QuicByteCount, bool)); + + MOCK_CONST_METHOD0(HasPendingRetransmission, bool()); + MOCK_METHOD1(OnStopSending, void(uint16_t code)); +}; + +class TestSession : public QuicSession { + public: + explicit TestSession(QuicConnection* connection, + MockQuicSessionVisitor* session_visitor) + : QuicSession(connection, + session_visitor, + DefaultQuicConfig(), + CurrentSupportedVersions()), + crypto_stream_(this), + writev_consumes_all_data_(false), + should_buffer_incoming_streams_(false), + num_incoming_streams_created_(0) { + Initialize(); + this->connection()->SetEncrypter( + ENCRYPTION_FORWARD_SECURE, + QuicMakeUnique<NullEncrypter>(connection->perspective())); + } + + ~TestSession() override { delete connection(); } + + TestCryptoStream* GetMutableCryptoStream() override { + return &crypto_stream_; + } + + const TestCryptoStream* GetCryptoStream() const override { + return &crypto_stream_; + } + + TestStream* CreateOutgoingBidirectionalStream() { + QuicStreamId id = GetNextOutgoingBidirectionalStreamId(); + if (id == + QuicUtils::GetInvalidStreamId(connection()->transport_version())) { + return nullptr; + } + TestStream* stream = new TestStream(id, this, BIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + TestStream* CreateOutgoingUnidirectionalStream() { + TestStream* stream = new TestStream(GetNextOutgoingUnidirectionalStreamId(), + this, WRITE_UNIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + TestStream* CreateIncomingStream(QuicStreamId id) override { + // Enforce the limit on the number of open streams. + if (GetNumOpenIncomingStreams() + 1 > + max_open_incoming_bidirectional_streams() && + connection()->transport_version() != QUIC_VERSION_99) { + // No need to do this test for version 99; it's done by + // QuicSession::GetOrCreateDynamicStream. + connection()->CloseConnection( + QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams!", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return nullptr; + } + + TestStream* stream = + new TestStream(id, this, + DetermineStreamType( + id, connection()->transport_version(), perspective(), + /*is_incoming=*/true, BIDIRECTIONAL)); + ActivateStream(QuicWrapUnique(stream)); + ++num_incoming_streams_created_; + return stream; + } + + TestStream* CreateIncomingStream(PendingStream pending) override { + QuicStreamId id = pending.id(); + TestStream* stream = + new TestStream(std::move(pending), + DetermineStreamType( + id, connection()->transport_version(), perspective(), + /*is_incoming=*/true, BIDIRECTIONAL)); + ActivateStream(QuicWrapUnique(stream)); + ++num_incoming_streams_created_; + return stream; + } + + bool IsClosedStream(QuicStreamId id) { + return QuicSession::IsClosedStream(id); + } + + QuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id) { + return QuicSession::GetOrCreateDynamicStream(stream_id); + } + + bool ShouldKeepConnectionAlive() const override { + return GetNumOpenDynamicStreams() > 0; + } + + QuicConsumedData WritevData(QuicStream* stream, + QuicStreamId id, + size_t write_length, + QuicStreamOffset offset, + StreamSendingState state) override { + bool fin = state != NO_FIN; + QuicConsumedData consumed(write_length, fin); + if (!writev_consumes_all_data_) { + consumed = + QuicSession::WritevData(stream, id, write_length, offset, state); + } + if (fin && consumed.fin_consumed) { + stream->set_fin_sent(true); + } + QuicSessionPeer::GetWriteBlockedStreams(this)->UpdateBytesForStream( + id, consumed.bytes_consumed); + return consumed; + } + + MOCK_METHOD0(OnCanCreateNewOutgoingStream, void()); + + void set_writev_consumes_all_data(bool val) { + writev_consumes_all_data_ = val; + } + + QuicConsumedData SendStreamData(QuicStream* stream) { + struct iovec iov; + if (stream->id() != + QuicUtils::GetCryptoStreamId(connection()->transport_version()) && + this->connection()->encryption_level() != ENCRYPTION_FORWARD_SECURE) { + this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + } + MakeIOVector("not empty", &iov); + QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9); + QuicConsumedData consumed = WritevData(stream, stream->id(), 9, 0, FIN); + QuicStreamPeer::SendBuffer(stream).OnStreamDataConsumed( + consumed.bytes_consumed); + return consumed; + } + + bool ClearControlFrame(const QuicFrame& frame) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + bool SaveFrame(const QuicFrame& frame) { + save_frame_ = frame; + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + const QuicFrame& save_frame() { return save_frame_; } + + QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) { + DCHECK(writev_consumes_all_data_); + return WritevData(stream, stream->id(), bytes, 0, FIN); + } + + bool ShouldBufferIncomingStream(QuicStreamId id) const override { + return should_buffer_incoming_streams_; + } + + void set_should_buffer_incoming_streams(bool should_buffer_incoming_streams) { + should_buffer_incoming_streams_ = should_buffer_incoming_streams; + } + + int num_incoming_streams_created() const { + return num_incoming_streams_created_; + } + + using QuicSession::ActivateStream; + using QuicSession::closed_streams; + using QuicSession::zombie_streams; + + private: + StrictMock<TestCryptoStream> crypto_stream_; + + bool writev_consumes_all_data_; + bool should_buffer_incoming_streams_; + QuicFrame save_frame_; + int num_incoming_streams_created_; +}; + +class QuicSessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { + protected: + explicit QuicSessionTestBase(Perspective perspective) + : connection_( + new StrictMock<MockQuicConnection>(&helper_, + &alarm_factory_, + perspective, + SupportedVersions(GetParam()))), + session_(connection_, &session_visitor_) { + session_.config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session_.config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) + .Times(testing::AnyNumber()); + } + + void CheckClosedStreams() { + for (QuicStreamId i = + QuicUtils::GetCryptoStreamId(connection_->transport_version()); + i < 100; i++) { + if (!QuicContainsKey(closed_streams_, i)) { + EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i; + } else { + EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i; + } + } + } + + void CloseStream(QuicStreamId id) { + if (session_.connection()->transport_version() == QUIC_VERSION_99 && + QuicUtils::GetStreamType(id, session_.perspective(), + session_.IsIncomingStream(id)) == + READ_UNIDIRECTIONAL) { + // Verify reset is not sent for READ_UNIDIRECTIONAL streams. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(0); + } else { + // Verify reset IS sent for BIDIRECTIONAL streams. + if (session_.connection()->transport_version() == QUIC_VERSION_99) { + // Once for the RST_STREAM, Once for the STOP_SENDING + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + } else { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + } + EXPECT_CALL(*connection_, OnStreamReset(id, _)); + } + session_.CloseStream(id); + closed_streams_.insert(id); + } + + QuicTransportVersion transport_version() const { + return connection_->transport_version(); + } + + QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { + return QuicUtils::GetFirstBidirectionalStreamId( + connection_->transport_version(), Perspective::IS_CLIENT) + + QuicUtils::StreamIdDelta(connection_->transport_version()) * n; + } + + QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) { + return QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_CLIENT) + + QuicUtils::StreamIdDelta(connection_->transport_version()) * n; + } + + QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { + return QuicUtils::GetFirstBidirectionalStreamId( + connection_->transport_version(), Perspective::IS_SERVER) + + QuicUtils::StreamIdDelta(connection_->transport_version()) * n; + } + + QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) { + return QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_SERVER) + + QuicUtils::StreamIdDelta(connection_->transport_version()) * n; + } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + NiceMock<MockQuicSessionVisitor> session_visitor_; + StrictMock<MockQuicConnection>* connection_; + TestSession session_; + std::set<QuicStreamId> closed_streams_; +}; + +class QuicSessionTestServer : public QuicSessionTestBase { + public: + // CheckMultiPathResponse validates that a written packet + // contains both expected path responses. + WriteResult CheckMultiPathResponse(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) { + QuicEncryptedPacket packet(buffer, buf_len); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)) + .WillOnce( + WithArg<0>(Invoke([this](const QuicPathResponseFrame& frame) { + EXPECT_EQ(path_frame_buffer1_, frame.data_buffer); + return true; + }))); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)) + .WillOnce( + WithArg<0>(Invoke([this](const QuicPathResponseFrame& frame) { + EXPECT_EQ(path_frame_buffer2_, frame.data_buffer); + return true; + }))); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + client_framer_.ProcessPacket(packet); + return WriteResult(WRITE_STATUS_OK, 0); + } + + protected: + QuicSessionTestServer() + : QuicSessionTestBase(Perspective::IS_SERVER), + path_frame_buffer1_({0, 1, 2, 3, 4, 5, 6, 7}), + path_frame_buffer2_({8, 9, 10, 11, 12, 13, 14, 15}), + client_framer_(SupportedVersions(GetParam()), + QuicTime::Zero(), + Perspective::IS_CLIENT, + kQuicDefaultConnectionIdLength) { + client_framer_.set_visitor(&framer_visitor_); + } + + QuicPathFrameBuffer path_frame_buffer1_; + QuicPathFrameBuffer path_frame_buffer2_; + StrictMock<MockFramerVisitor> framer_visitor_; + // Framer used to process packets sent by server. + QuicFramer client_framer_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicSessionTestServer, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicSessionTestServer, PeerAddress) { + EXPECT_EQ(QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort), + session_.peer_address()); +} + +TEST_P(QuicSessionTestServer, SelfAddress) { + EXPECT_TRUE(session_.self_address().IsInitialized()); +} + +TEST_P(QuicSessionTestServer, DontCallOnWriteBlockedForDisconnectedConnection) { + EXPECT_CALL(*connection_, CloseConnection(_, _, _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + connection_->CloseConnection(QUIC_NO_ERROR, "Everything is fine.", + ConnectionCloseBehavior::SILENT_CLOSE); + ASSERT_FALSE(connection_->connected()); + + if (GetQuicReloadableFlag( + quic_connection_do_not_add_to_write_blocked_list_if_disconnected)) { + EXPECT_CALL(session_visitor_, OnWriteBlocked(_)).Times(0); + } else { + EXPECT_CALL(session_visitor_, OnWriteBlocked(_)).Times(1); + } + session_.OnWriteBlocked(); +} + +TEST_P(QuicSessionTestServer, IsCryptoHandshakeConfirmed) { + EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); + CryptoHandshakeMessage message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); + EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed()); +} + +TEST_P(QuicSessionTestServer, IsClosedStreamDefault) { + // Ensure that no streams are initially closed. + for (QuicStreamId i = + QuicUtils::GetCryptoStreamId(connection_->transport_version()); + i < 100; i++) { + EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i; + } +} + +TEST_P(QuicSessionTestServer, AvailableBidirectionalStreams) { + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(3)) != nullptr); + // Smaller bidirectional streams should be available. + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedBidirectionalId(1))); + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedBidirectionalId(2))); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(2)) != nullptr); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(1)) != nullptr); +} + +TEST_P(QuicSessionTestServer, AvailableUnidirectionalStreams) { + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedUnidirectionalId(3)) != nullptr); + // Smaller unidirectional streams should be available. + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedUnidirectionalId(1))); + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedUnidirectionalId(2))); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedUnidirectionalId(2)) != nullptr); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthClientInitiatedUnidirectionalId(1)) != nullptr); +} + +TEST_P(QuicSessionTestServer, MaxAvailableBidirectionalStreams) { + if (transport_version() == QUIC_VERSION_99) { + EXPECT_EQ(session_.max_open_incoming_bidirectional_streams(), + session_.MaxAvailableBidirectionalStreams()); + } else { + // The protocol specification requires that there can be at least 10 times + // as many available streams as the connection's maximum open streams. + EXPECT_EQ(session_.max_open_incoming_bidirectional_streams() * + kMaxAvailableStreamsMultiplier, + session_.MaxAvailableBidirectionalStreams()); + } +} + +TEST_P(QuicSessionTestServer, MaxAvailableUnidirectionalStreams) { + if (transport_version() == QUIC_VERSION_99) { + EXPECT_EQ(session_.max_open_incoming_unidirectional_streams(), + session_.MaxAvailableUnidirectionalStreams()); + } else { + // The protocol specification requires that there can be at least 10 times + // as many available streams as the connection's maximum open streams. + EXPECT_EQ(session_.max_open_incoming_unidirectional_streams() * + kMaxAvailableStreamsMultiplier, + session_.MaxAvailableUnidirectionalStreams()); + } +} + +TEST_P(QuicSessionTestServer, IsClosedBidirectionalStreamLocallyCreated) { + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0), stream2->id()); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1), stream4->id()); + + CheckClosedStreams(); + CloseStream(GetNthServerInitiatedBidirectionalId(0)); + CheckClosedStreams(); + CloseStream(GetNthServerInitiatedBidirectionalId(1)); + CheckClosedStreams(); +} + +TEST_P(QuicSessionTestServer, IsClosedUnidirectionalStreamLocallyCreated) { + TestStream* stream2 = session_.CreateOutgoingUnidirectionalStream(); + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0), stream2->id()); + TestStream* stream4 = session_.CreateOutgoingUnidirectionalStream(); + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(1), stream4->id()); + + CheckClosedStreams(); + CloseStream(GetNthServerInitiatedUnidirectionalId(0)); + CheckClosedStreams(); + CloseStream(GetNthServerInitiatedUnidirectionalId(1)); + CheckClosedStreams(); +} + +TEST_P(QuicSessionTestServer, IsClosedBidirectionalStreamPeerCreated) { + QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0); + QuicStreamId stream_id2 = GetNthClientInitiatedBidirectionalId(1); + session_.GetOrCreateDynamicStream(stream_id1); + session_.GetOrCreateDynamicStream(stream_id2); + + CheckClosedStreams(); + CloseStream(stream_id1); + CheckClosedStreams(); + CloseStream(stream_id2); + // Create a stream, and make another available. + QuicStream* stream3 = session_.GetOrCreateDynamicStream( + stream_id2 + + 2 * QuicUtils::StreamIdDelta(connection_->transport_version())); + CheckClosedStreams(); + // Close one, but make sure the other is still not closed + CloseStream(stream3->id()); + CheckClosedStreams(); +} + +TEST_P(QuicSessionTestServer, IsClosedUnidirectionalStreamPeerCreated) { + QuicStreamId stream_id1 = GetNthClientInitiatedUnidirectionalId(0); + QuicStreamId stream_id2 = GetNthClientInitiatedUnidirectionalId(1); + session_.GetOrCreateDynamicStream(stream_id1); + session_.GetOrCreateDynamicStream(stream_id2); + + CheckClosedStreams(); + CloseStream(stream_id1); + CheckClosedStreams(); + CloseStream(stream_id2); + // Create a stream, and make another available. + QuicStream* stream3 = session_.GetOrCreateDynamicStream( + stream_id2 + + 2 * QuicUtils::StreamIdDelta(connection_->transport_version())); + CheckClosedStreams(); + // Close one, but make sure the other is still not closed + CloseStream(stream3->id()); + CheckClosedStreams(); +} + +TEST_P(QuicSessionTestServer, MaximumAvailableOpenedBidirectionalStreams) { + QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); + session_.GetOrCreateDynamicStream(stream_id); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_NE( + nullptr, + session_.GetOrCreateDynamicStream(GetNthClientInitiatedBidirectionalId( + session_.max_open_incoming_bidirectional_streams() - 1))); +} + +TEST_P(QuicSessionTestServer, MaximumAvailableOpenedUnidirectionalStreams) { + QuicStreamId stream_id = GetNthClientInitiatedUnidirectionalId(0); + session_.GetOrCreateDynamicStream(stream_id); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_NE( + nullptr, + session_.GetOrCreateDynamicStream(GetNthClientInitiatedUnidirectionalId( + session_.max_open_incoming_unidirectional_streams() - 1))); +} + +TEST_P(QuicSessionTestServer, TooManyAvailableBidirectionalStreams) { + QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0); + QuicStreamId stream_id2; + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id1)); + // A stream ID which is too large to create. + stream_id2 = GetNthClientInitiatedBidirectionalId( + session_.MaxAvailableBidirectionalStreams() + 2); + if (transport_version() == QUIC_VERSION_99) { + // V99 terminates the connection with invalid stream id + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + } else { + // other versions terminate the connection with + // QUIC_TOO_MANY_AVAILABLE_STREAMS. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _)); + } + EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(stream_id2)); +} + +TEST_P(QuicSessionTestServer, TooManyAvailableUnidirectionalStreams) { + QuicStreamId stream_id1 = GetNthClientInitiatedUnidirectionalId(0); + QuicStreamId stream_id2; + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id1)); + // A stream ID which is too large to create. + stream_id2 = GetNthClientInitiatedUnidirectionalId( + session_.MaxAvailableUnidirectionalStreams() + 2); + if (transport_version() == QUIC_VERSION_99) { + // V99 terminates the connection with invalid stream id + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + } else { + // other versions terminate the connection with + // QUIC_TOO_MANY_AVAILABLE_STREAMS. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _)); + } + EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(stream_id2)); +} + +TEST_P(QuicSessionTestServer, ManyAvailableBidirectionalStreams) { + // When max_open_streams_ is 200, should be able to create 200 streams + // out-of-order, that is, creating the one with the largest stream ID first. + QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200); + QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); + // Create one stream. + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id)); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + + // Create the largest stream ID of a threatened total of 200 streams. + // GetNth... starts at 0, so for 200 streams, get the 199th. + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream( + GetNthClientInitiatedBidirectionalId(199))); +} + +TEST_P(QuicSessionTestServer, ManyAvailableUnidirectionalStreams) { + // When max_open_streams_ is 200, should be able to create 200 streams + // out-of-order, that is, creating the one with the largest stream ID first. + QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200); + QuicStreamId stream_id = GetNthClientInitiatedUnidirectionalId(0); + // Create one stream. + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id)); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + + // Create the largest stream ID of a threatened total of 200 streams. + // GetNth... starts at 0, so for 200 streams, get the 199th. + EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream( + GetNthClientInitiatedUnidirectionalId(199))); +} + +TEST_P(QuicSessionTestServer, DebugDFatalIfMarkingClosedStreamWriteBlocked) { + // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. + if (GetParam() != AllSupportedVersions()[0]) { + return; + } + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicStreamId closed_stream_id = stream2->id(); + // Close the stream. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(closed_stream_id, _)); + stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD); + QuicString msg = + QuicStrCat("Marking unknown stream ", closed_stream_id, " blocked."); + EXPECT_QUIC_BUG(session_.MarkConnectionLevelWriteBlocked(closed_stream_id), + msg); +} + +TEST_P(QuicSessionTestServer, OnCanWrite) { + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + InSequence s; + + // Reregister, to test the loop limit. + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + // 2 will get called a second time as it didn't finish its block + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { + session_.SendStreamData(stream6); + })); + // 4 will not get called, as we exceeded the loop limit. + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTestServer, TestBatchedWrites) { + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.set_writev_consumes_all_data(true); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + // With two sessions blocked, we should get two write calls. They should both + // go to the first stream as it will only write 6k and mark itself blocked + // again. + InSequence s; + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + session_.OnCanWrite(); + + // We should get one more call for stream2, at which point it has used its + // write quota and we move over to stream 4. + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendLargeFakeData(stream4, 6000); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + })); + session_.OnCanWrite(); + + // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high + // priority stream 6. 4 should be preempted. 6 will write but *not* block so + // will cede back to 4. + stream6->SetPriority(kV3HighestPriority); + EXPECT_CALL(*stream4, OnCanWrite()) + .WillOnce(Invoke([this, stream4, stream6]() { + session_.SendLargeFakeData(stream4, 6000); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + })); + EXPECT_CALL(*stream6, OnCanWrite()) + .WillOnce(Invoke([this, stream4, stream6]() { + session_.SendStreamData(stream6); + session_.SendLargeFakeData(stream4, 6000); + })); + session_.OnCanWrite(); + + // Stream4 alread did 6k worth of writes, so after doing another 12k it should + // cede and 2 should resume. + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendLargeFakeData(stream4, 12000); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + })); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendLargeFakeData(stream2, 6000); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + })); + session_.OnCanWrite(); +} + +TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) { + // Encryption needs to be established before data can be sent. + CryptoHandshakeMessage msg; + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*send_algorithm, GetCongestionWindow()) + .WillRepeatedly(Return(kMaxPacketSize * 10)); + EXPECT_CALL(*send_algorithm, InRecovery()).WillRepeatedly(Return(false)); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + })); + EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { + session_.SendStreamData(stream6); + })); + + // Expect that we only send one packet, the writes from different streams + // should be bundled together. + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _)); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTestServer, OnCanWriteCongestionControlBlocks) { + session_.set_writev_consumes_all_data(true); + InSequence s; + + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { + session_.SendStreamData(stream6); + })); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false)); + // stream4->OnCanWrite is not called. + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // Still congestion-control blocked. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false)); + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // stream4->OnCanWrite is called once the connection stops being + // congestion-control blocked. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + })); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTestServer, OnCanWriteWriterBlocks) { + // Drive congestion control manually in order to ensure that + // application-limited signaling is handled correctly. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); + + // Drive packet writer manually. + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true)); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)).Times(0); + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + + EXPECT_CALL(*stream2, OnCanWrite()).Times(0); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)).Times(0); + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTestServer, BufferedHandshake) { + session_.set_writev_consumes_all_data(true); + EXPECT_FALSE(session_.HasPendingHandshake()); // Default value. + + // Test that blocking other streams does not change our status. + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + EXPECT_FALSE(session_.HasPendingHandshake()); + + TestStream* stream3 = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream3->id()); + EXPECT_FALSE(session_.HasPendingHandshake()); + + // Blocking (due to buffering of) the Crypto stream is detected. + session_.MarkConnectionLevelWriteBlocked( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); + EXPECT_TRUE(session_.HasPendingHandshake()); + + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + EXPECT_TRUE(session_.HasPendingHandshake()); + + InSequence s; + // Force most streams to re-register, which is common scenario when we block + // the Crypto stream, and only the crypto stream can "really" write. + + // Due to prioritization, we *should* be asked to write the crypto stream + // first. + // Don't re-register the crypto stream (which signals complete writing). + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_CALL(*crypto_stream, OnCanWrite()); + + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(Invoke([this, stream3]() { + session_.SendStreamData(stream3); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + })); + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote. +} + +TEST_P(QuicSessionTestServer, OnCanWriteWithClosedStream) { + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + CloseStream(stream6->id()); + + InSequence s; + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { + session_.SendStreamData(stream2); + })); + EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { + session_.SendStreamData(stream4); + })); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTestServer, OnCanWriteLimitsNumWritesIfFlowControlBlocked) { + // Drive congestion control manually in order to ensure that + // application-limited signaling is handled correctly. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); + + // Ensure connection level flow control blockage. + QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); + EXPECT_TRUE(session_.flow_controller()->IsBlocked()); + EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + + // Mark the crypto and headers streams as write blocked, we expect them to be + // allowed to write later. + session_.MarkConnectionLevelWriteBlocked( + QuicUtils::GetCryptoStreamId(connection_->transport_version())); + + // Create a data stream, and although it is write blocked we never expect it + // to be allowed to write as we are connection level flow control blocked. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + session_.MarkConnectionLevelWriteBlocked(stream->id()); + EXPECT_CALL(*stream, OnCanWrite()).Times(0); + + // The crypto and headers streams should be called even though we are + // connection flow control blocked. + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_CALL(*crypto_stream, OnCanWrite()); + + // After the crypto and header streams perform a write, the connection will be + // blocked by the flow control, hence it should become application-limited. + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTestServer, SendGoAway) { + if (transport_version() == QUIC_VERSION_99) { + // GoAway frames are not in version 99 + return; + } + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallySendControlFrame)); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); + EXPECT_TRUE(session_.goaway_sent()); + + const QuicStreamId kTestStreamId = 5u; + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + EXPECT_CALL(*connection_, + OnStreamReset(kTestStreamId, QUIC_STREAM_PEER_GOING_AWAY)) + .Times(0); + EXPECT_TRUE(session_.GetOrCreateDynamicStream(kTestStreamId)); +} + +TEST_P(QuicSessionTestServer, DoNotSendGoAwayTwice) { + if (transport_version() == QUIC_VERSION_99) { + // TODO(b/118808809): Enable this test for version 99 when GOAWAY is + // supported. + return; + } + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); + EXPECT_TRUE(session_.goaway_sent()); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); +} + +TEST_P(QuicSessionTestServer, InvalidGoAway) { + if (transport_version() == QUIC_VERSION_99) { + // TODO(b/118808809): Enable this test for version 99 when GOAWAY is + // supported. + return; + } + QuicGoAwayFrame go_away(kInvalidControlFrameId, QUIC_PEER_GOING_AWAY, + session_.next_outgoing_bidirectional_stream_id(), ""); + session_.OnGoAway(go_away); +} + +// Test that server session will send a connectivity probe in response to a +// connectivity probe on the same path. +TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbe) { + QuicSocketAddress old_peer_address = + QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort); + EXPECT_EQ(old_peer_address, session_.peer_address()); + + QuicSocketAddress new_peer_address = + QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort + 1); + + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, new_peer_address, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + EXPECT_CALL(*connection_, SendConnectivityProbingResponsePacket(_)) + .WillOnce(Invoke( + connection_, + &MockQuicConnection::ReallySendConnectivityProbingResponsePacket)); + if (transport_version() == QUIC_VERSION_99) { + // Need to explicitly do this to emulate the reception of a PathChallenge, + // which stores its payload for use in generating the response. + connection_->OnPathChallengeFrame( + QuicPathChallengeFrame(0, path_frame_buffer1_)); + } + session_.OnConnectivityProbeReceived(session_.self_address(), + new_peer_address); + EXPECT_EQ(old_peer_address, session_.peer_address()); +} + +// Same as above, but check that if there are two PATH_CHALLENGE frames in the +// packet, the response has both of them AND we do not do migration. This for +// V99 only. +TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbes) { + if (transport_version() != QUIC_VERSION_99) { + return; + } + QuicSocketAddress old_peer_address = + QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort); + EXPECT_EQ(old_peer_address, session_.peer_address()); + + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + // CheckMultiPathResponse validates that the written packet + // contains both path responses. + EXPECT_CALL(*writer, WritePacket(_, _, _, old_peer_address, _)) + .WillOnce(Invoke(this, &QuicSessionTestServer::CheckMultiPathResponse)); + + EXPECT_CALL(*connection_, SendConnectivityProbingResponsePacket(_)) + .WillOnce(Invoke( + connection_, + &MockQuicConnection::ReallySendConnectivityProbingResponsePacket)); + // Need to explicitly do this to emulate the reception of a PathChallenge, + // which stores its payload for use in generating the response. + connection_->OnPathChallengeFrame( + QuicPathChallengeFrame(0, path_frame_buffer1_)); + connection_->OnPathChallengeFrame( + QuicPathChallengeFrame(0, path_frame_buffer2_)); + session_.OnConnectivityProbeReceived(session_.self_address(), + old_peer_address); +} + +TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { + EXPECT_EQ(kInitialIdleTimeoutSecs + 3, + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); + CryptoHandshakeMessage msg; + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + EXPECT_EQ(kMaximumIdleTimeoutSecs + 3, + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); +} + +TEST_P(QuicSessionTestServer, OnStreamFrameFinStaticStreamId) { + // Send two bytes of payload. + QuicStreamFrame data1( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), true, 0, + QuicStringPiece("HT")); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Attempt to close a static stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnStreamFrame(data1); +} + +TEST_P(QuicSessionTestServer, OnRstStreamStaticStreamId) { + // Send two bytes of payload. + QuicRstStreamFrame rst1( + kInvalidControlFrameId, + QuicUtils::GetCryptoStreamId(connection_->transport_version()), + QUIC_ERROR_PROCESSING_STREAM, 0); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnRstStream(rst1); +} + +TEST_P(QuicSessionTestServer, OnStreamFrameInvalidStreamId) { + // Send two bytes of payload. + QuicStreamFrame data1( + QuicUtils::GetInvalidStreamId(connection_->transport_version()), true, 0, + QuicStringPiece("HT")); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Recevied data for an invalid stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnStreamFrame(data1); +} + +TEST_P(QuicSessionTestServer, OnRstStreamInvalidStreamId) { + // Send two bytes of payload. + QuicRstStreamFrame rst1( + kInvalidControlFrameId, + QuicUtils::GetInvalidStreamId(connection_->transport_version()), + QUIC_ERROR_PROCESSING_STREAM, 0); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_INVALID_STREAM_ID, "Recevied data for an invalid stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + session_.OnRstStream(rst1); +} + +TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedStream) { + // Test that if a stream is flow control blocked, then on receipt of the SHLO + // containing a suitable send window offset, the stream becomes unblocked. + + // Ensure that Writev consumes all the data it is given (simulate no socket + // blocking). + session_.set_writev_consumes_all_data(true); + + // Create a stream, and send enough data to make it flow control blocked. + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicString body(kMinimumFlowControlSendWindow, '.'); + EXPECT_FALSE(stream2->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AtLeast(1)); + stream2->WriteOrBufferData(body, false, nullptr); + EXPECT_TRUE(stream2->flow_controller()->IsBlocked()); + EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); + EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); + + // Now complete the crypto handshake, resulting in an increased flow control + // send window. + CryptoHandshakeMessage msg; + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(&session_, stream2->id())); + // Stream is now unblocked. + EXPECT_FALSE(stream2->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); +} + +TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedCryptoStream) { + if (GetParam().transport_version >= QUIC_VERSION_47) { + // QUIC version 47 onwards uses CRYPTO frames for the handshake, so this + // test doesn't make sense for those versions since CRYPTO frames aren't + // flow controlled. + return; + } + // Test that if the crypto stream is flow control blocked, then if the SHLO + // contains a larger send window offset, the stream becomes unblocked. + session_.set_writev_consumes_all_data(true); + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + for (QuicStreamId i = 0; + !crypto_stream->flow_controller()->IsBlocked() && i < 1000u; i++) { + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); + QuicStreamOffset offset = crypto_stream->stream_bytes_written(); + QuicConfig config; + CryptoHandshakeMessage crypto_message; + config.ToHandshakeMessage(&crypto_message); + crypto_stream->SendHandshakeMessage(crypto_message); + char buf[1000]; + QuicDataWriter writer(1000, buf, NETWORK_BYTE_ORDER); + crypto_stream->WriteStreamData(offset, crypto_message.size(), &writer); + } + EXPECT_TRUE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); + EXPECT_FALSE(session_.HasDataToWrite()); + EXPECT_TRUE(crypto_stream->HasBufferedData()); + + // Now complete the crypto handshake, resulting in an increased flow control + // send window. + CryptoHandshakeMessage msg; + session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); + EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked( + &session_, + QuicUtils::GetCryptoStreamId(connection_->transport_version()))); + // Stream is now unblocked and will no longer have buffered data. + EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked()); + EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); + EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); +} + +TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingRstOutOfOrder) { + // Test that when we receive an out of order stream RST we correctly adjust + // our connection level flow control receive window. + // On close, the stream should mark as consumed all bytes between the highest + // byte consumed so far and the final byte offset from the RST frame. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + + const QuicStreamOffset kByteOffset = + 1 + kInitialSessionFlowControlWindowForTest / 2; + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(), + QUIC_STREAM_CANCELLED, kByteOffset); + session_.OnRstStream(rst_frame); + if (transport_version() == QUIC_VERSION_99) { + // The test is predicated on the stream being fully closed. For V99, the + // RST_STREAM only does one side (the read side from the perspective of the + // node receiving the RST_STREAM). This is needed to fully close the + // stream and therefore fulfill all of the expects. + QuicStopSendingFrame frame(kInvalidControlFrameId, stream->id(), + QUIC_STREAM_CANCELLED); + EXPECT_TRUE(session_.OnStopSendingFrame(frame)); + } + EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); +} + +TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingFinAndLocalReset) { + // Test the situation where we receive a FIN on a stream, and before we fully + // consume all the data from the sequencer buffer we locally RST the stream. + // The bytes between highest consumed byte, and the final byte offset that we + // determined when the FIN arrived, should be marked as consumed at the + // connection level flow controller when the stream is reset. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + + const QuicStreamOffset kByteOffset = + kInitialSessionFlowControlWindowForTest / 2 - 1; + QuicStreamFrame frame(stream->id(), true, kByteOffset, "."); + session_.OnStreamFrame(frame); + EXPECT_TRUE(connection_->connected()); + + EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed()); + EXPECT_EQ(kByteOffset + frame.data_length, + stream->flow_controller()->highest_received_byte_offset()); + + // Reset stream locally. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + EXPECT_EQ(kByteOffset + frame.data_length, + session_.flow_controller()->bytes_consumed()); +} + +TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingFinAfterRst) { + // Test that when we RST the stream (and tear down stream state), and then + // receive a FIN from the peer, we correctly adjust our connection level flow + // control receive window. + + // Connection starts with some non-zero highest received byte offset, + // due to other active streams. + const uint64_t kInitialConnectionBytesConsumed = 567; + const uint64_t kInitialConnectionHighestReceivedOffset = 1234; + EXPECT_LT(kInitialConnectionBytesConsumed, + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->UpdateHighestReceivedOffset( + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); + + // Reset our stream: this results in the stream being closed locally. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + + // Now receive a response from the peer with a FIN. We should handle this by + // adjusting the connection level flow control receive window to take into + // account the total number of bytes sent by the peer. + const QuicStreamOffset kByteOffset = 5678; + QuicString body = "hello"; + QuicStreamFrame frame(stream->id(), true, kByteOffset, QuicStringPiece(body)); + session_.OnStreamFrame(frame); + + QuicStreamOffset total_stream_bytes_sent_by_peer = + kByteOffset + body.length(); + EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer, + session_.flow_controller()->bytes_consumed()); + EXPECT_EQ( + kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer, + session_.flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingRstAfterRst) { + // Test that when we RST the stream (and tear down stream state), and then + // receive a RST from the peer, we correctly adjust our connection level flow + // control receive window. + + // Connection starts with some non-zero highest received byte offset, + // due to other active streams. + const uint64_t kInitialConnectionBytesConsumed = 567; + const uint64_t kInitialConnectionHighestReceivedOffset = 1234; + EXPECT_LT(kInitialConnectionBytesConsumed, + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->UpdateHighestReceivedOffset( + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); + + // Reset our stream: this results in the stream being closed locally. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream)); + + // Now receive a RST from the peer. We should handle this by adjusting the + // connection level flow control receive window to take into account the total + // number of bytes sent by the peer. + const QuicStreamOffset kByteOffset = 5678; + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(), + QUIC_STREAM_CANCELLED, kByteOffset); + session_.OnRstStream(rst_frame); + + EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset, + session_.flow_controller()->bytes_consumed()); + EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset, + session_.flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicSessionTestServer, InvalidStreamFlowControlWindowInHandshake) { + // Test that receipt of an invalid (< default) stream flow control window from + // the peer results in the connection being torn down. + const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1; + QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(), + kInvalidWindow); + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _)); + session_.OnConfigNegotiated(); +} + +TEST_P(QuicSessionTestServer, InvalidSessionFlowControlWindowInHandshake) { + // Test that receipt of an invalid (< default) session flow control window + // from the peer results in the connection being torn down. + const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1; + QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(), + kInvalidWindow); + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _)); + session_.OnConfigNegotiated(); +} + +// Test negotiation of custom server initial flow control window. +TEST_P(QuicSessionTestServer, CustomFlowControlWindow) { + QuicTagVector copt; + copt.push_back(kIFW7); + QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); + + session_.OnConfigNegotiated(); + EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize( + session_.flow_controller())); +} + +TEST_P(QuicSessionTestServer, FlowControlWithInvalidFinalOffset) { + // Test that if we receive a stream RST with a highest byte offset that + // violates flow control, that we close the connection. + const uint64_t kLargeOffset = kInitialSessionFlowControlWindowForTest + 1; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)) + .Times(2); + + // Check that stream frame + FIN results in connection close. + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + QuicStreamFrame frame(stream->id(), true, kLargeOffset, QuicStringPiece()); + session_.OnStreamFrame(frame); + + // Check that RST results in connection close. + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(), + QUIC_STREAM_CANCELLED, kLargeOffset); + session_.OnRstStream(rst_frame); +} + +TEST_P(QuicSessionTestServer, TooManyUnfinishedStreamsCauseServerRejectStream) { + // If a buggy/malicious peer creates too many streams that are not ended + // with a FIN or RST then we send an RST to refuse streams. For V99 the + // connection is closed. + const QuicStreamId kMaxStreams = 5; + QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams); + const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0); + const QuicStreamId kFinalStreamId = + GetNthClientInitiatedBidirectionalId(kMaxStreams); + // Create kMaxStreams data streams, and close them all without receiving a + // FIN or a RST_STREAM from the client. + for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; + i += QuicUtils::StreamIdDelta(connection_->transport_version())) { + QuicStreamFrame data1(i, false, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + // EXPECT_EQ(1u, session_.GetNumOpenStreams()); + if (transport_version() == QUIC_VERSION_99) { + // Expect two control frames, RST STREAM and STOP SENDING + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + } else { + // Expect one control frame, just RST STREAM + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + } + // Close stream. Should not make new streams available since + // the stream is not finished. + EXPECT_CALL(*connection_, OnStreamReset(i, _)); + session_.CloseStream(i); + } + + if (transport_version() == QUIC_VERSION_99) { + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, + "Stream id 24 above 20", _)); + } else { + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + EXPECT_CALL(*connection_, + OnStreamReset(kFinalStreamId, QUIC_REFUSED_STREAM)) + .Times(1); + } + // Create one more data streams to exceed limit of open stream. + QuicStreamFrame data1(kFinalStreamId, false, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); +} + +TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpenedOutgoing) { + // Verify that a draining stream (which has received a FIN but not consumed + // it) does not count against the open quota (because it is closed from the + // protocol point of view). + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + QuicStreamId stream_id = stream->id(); + QuicStreamFrame data1(stream_id, true, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(1); + session_.StreamDraining(stream_id); +} + +TEST_P(QuicSessionTestServer, NoPendingStreams) { + session_.set_should_buffer_incoming_streams(false); + + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + EXPECT_EQ(1, session_.num_incoming_streams_created()); + + QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data2); + EXPECT_EQ(1, session_.num_incoming_streams_created()); +} + +TEST_P(QuicSessionTestServer, PendingStreams) { + if (connection_->transport_version() != QUIC_VERSION_99) { + return; + } + session_.set_should_buffer_incoming_streams(true); + + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data2); + EXPECT_EQ(1, session_.num_incoming_streams_created()); +} + +TEST_P(QuicSessionTestServer, RstPendingStreams) { + if (connection_->transport_version() != QUIC_VERSION_99) { + return; + } + session_.set_should_buffer_incoming_streams(true); + + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(1); + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + EXPECT_CALL(*connection_, OnStreamReset(stream_id, QUIC_RST_ACKNOWLEDGEMENT)) + .Times(1); + QuicRstStreamFrame rst1(kInvalidControlFrameId, stream_id, + QUIC_ERROR_PROCESSING_STREAM, 12); + session_.OnRstStream(rst1); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data2); + EXPECT_EQ(0, session_.num_incoming_streams_created()); +} + +TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpened) { + // Verify that a draining stream (which has received a FIN but not consumed + // it) does not count against the open quota (because it is closed from the + // protocol point of view). + if (transport_version() == QUIC_VERSION_99) { + // On v99, we will expect to see a MAX_STREAM_ID go out when there are not + // enough streams to create the next one. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + } else { + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + } + EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)).Times(0); + const QuicStreamId kMaxStreams = 5; + QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams); + + // Create kMaxStreams + 1 data streams, and mark them draining. + const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0); + const QuicStreamId kFinalStreamId = + GetNthClientInitiatedBidirectionalId(2 * kMaxStreams + 1); + for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; + i += QuicUtils::StreamIdDelta(connection_->transport_version())) { + QuicStreamFrame data1(i, true, 0, QuicStringPiece("HT")); + session_.OnStreamFrame(data1); + EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams()); + session_.StreamDraining(i); + EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams()); + } +} + +class QuicSessionTestClient : public QuicSessionTestBase { + protected: + QuicSessionTestClient() : QuicSessionTestBase(Perspective::IS_CLIENT) {} +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicSessionTestClient, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicSessionTestClient, AvailableBidirectionalStreamsClient) { + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedBidirectionalId(2)) != nullptr); + // Smaller bidirectional streams should be available. + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthServerInitiatedBidirectionalId(0))); + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthServerInitiatedBidirectionalId(1))); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedBidirectionalId(0)) != nullptr); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedBidirectionalId(1)) != nullptr); + // And 5 should be not available. + EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedBidirectionalId(1))); +} + +TEST_P(QuicSessionTestClient, AvailableUnidirectionalStreamsClient) { + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedUnidirectionalId(2)) != nullptr); + // Smaller unidirectional streams should be available. + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthServerInitiatedUnidirectionalId(0))); + EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthServerInitiatedUnidirectionalId(1))); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedUnidirectionalId(0)) != nullptr); + ASSERT_TRUE(session_.GetOrCreateDynamicStream( + GetNthServerInitiatedUnidirectionalId(1)) != nullptr); + // And 5 should be not available. + EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable( + &session_, GetNthClientInitiatedUnidirectionalId(1))); +} + +TEST_P(QuicSessionTestClient, RecordFinAfterReadSideClosed) { + // Verify that an incoming FIN is recorded in a stream object even if the read + // side has been closed. This prevents an entry from being made in + // locally_closed_streams_highest_offset_ (which will never be deleted). + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + QuicStreamId stream_id = stream->id(); + + // Close the read side manually. + QuicStreamPeer::CloseReadSide(stream); + + // Receive a stream data frame with FIN. + QuicStreamFrame frame(stream_id, true, 0, QuicStringPiece()); + session_.OnStreamFrame(frame); + EXPECT_TRUE(stream->fin_received()); + + // Reset stream locally. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + stream->Reset(QUIC_STREAM_CANCELLED); + EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream)); + + EXPECT_TRUE(connection_->connected()); + EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id)); + EXPECT_FALSE(QuicSessionPeer::IsStreamCreated(&session_, stream_id)); + + // The stream is not waiting for the arrival of the peer's final offset as it + // was received with the FIN earlier. + EXPECT_EQ( + 0u, + QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(&session_).size()); +} + +TEST_P(QuicSessionTestServer, ZombieStreams) { + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicStreamPeer::SetStreamBytesWritten(3, stream2); + EXPECT_TRUE(stream2->IsWaitingForAcks()); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _)); + session_.CloseStream(stream2->id()); + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + ASSERT_EQ(1u, session_.closed_streams()->size()); + EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id()); + session_.OnStreamDoneWaitingForAcks(stream2->id()); + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + EXPECT_EQ(1u, session_.closed_streams()->size()); + EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id()); +} + +TEST_P(QuicSessionTestServer, RstStreamReceivedAfterRstStreamSent) { + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicStreamPeer::SetStreamBytesWritten(3, stream2); + EXPECT_TRUE(stream2->IsWaitingForAcks()); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _)); + EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(0); + stream2->Reset(quic::QUIC_STREAM_CANCELLED); + + QuicRstStreamFrame rst1(kInvalidControlFrameId, stream2->id(), + QUIC_ERROR_PROCESSING_STREAM, 0); + if (transport_version() != QUIC_VERSION_99) { + EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(1); + } + session_.OnRstStream(rst1); +} + +// Regression test of b/71548958. +TEST_P(QuicSessionTestServer, TestZombieStreams) { + session_.set_writev_consumes_all_data(true); + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicString body(100, '.'); + stream2->WriteOrBufferData(body, false, nullptr); + EXPECT_TRUE(stream2->IsWaitingForAcks()); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream2).size()); + + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream2->id(), + QUIC_STREAM_CANCELLED, 1234); + // Just for the RST_STREAM + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + if (transport_version() == QUIC_VERSION_99) { + EXPECT_CALL(*connection_, + OnStreamReset(stream2->id(), QUIC_STREAM_CANCELLED)); + } else { + EXPECT_CALL(*connection_, + OnStreamReset(stream2->id(), QUIC_RST_ACKNOWLEDGEMENT)); + } + stream2->OnStreamReset(rst_frame); + + if (transport_version() == QUIC_VERSION_99) { + // The test is predicated on the stream being fully closed. For V99, the + // RST_STREAM only does one side (the read side from the perspective of the + // node receiving the RST_STREAM). This is needed to fully close the + // stream and therefore fulfill all of the expects. + QuicStopSendingFrame frame(kInvalidControlFrameId, stream2->id(), + QUIC_STREAM_CANCELLED); + EXPECT_TRUE(session_.OnStopSendingFrame(frame)); + } + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + ASSERT_EQ(1u, session_.closed_streams()->size()); + EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id()); + + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + if (transport_version() == QUIC_VERSION_99) { + // Once for the RST_STREAM, once for the STOP_SENDING + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + } else { + // Just for the RST_STREAM + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); + } + EXPECT_CALL(*connection_, + OnStreamReset(stream4->id(), QUIC_STREAM_CANCELLED)); + stream4->WriteOrBufferData(body, false, nullptr); + // Note well: Reset() actually closes the stream in both directions. For + // GOOGLE QUIC it sends a RST_STREAM (which does a 2-way close), for IETF + // QUIC/V99 it sends both a RST_STREAM and a STOP_SENDING (each of which + // closes in only one direction). + stream4->Reset(QUIC_STREAM_CANCELLED); + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream4->id())); + EXPECT_EQ(2u, session_.closed_streams()->size()); +} + +TEST_P(QuicSessionTestServer, OnStreamFrameLost) { + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + InSequence s; + + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + + QuicStreamFrame frame1( + QuicUtils::GetCryptoStreamId(connection_->transport_version()), false, 0, + 1300); + QuicStreamFrame frame2(stream2->id(), false, 0, 9); + QuicStreamFrame frame3(stream4->id(), false, 0, 9); + + // Lost data on cryption stream, streams 2 and 4. + EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true)); + if (connection_->transport_version() < QUIC_VERSION_47) { + EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) + .WillOnce(Return(true)); + } + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true)); + session_.OnFrameLost(QuicFrame(frame3)); + if (connection_->transport_version() < QUIC_VERSION_47) { + session_.OnFrameLost(QuicFrame(frame1)); + } else { + QuicCryptoFrame crypto_frame(ENCRYPTION_NONE, 0, 1300); + session_.OnFrameLost(QuicFrame(&crypto_frame)); + } + session_.OnFrameLost(QuicFrame(frame2)); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // Mark streams 2 and 4 write blocked. + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + + // Lost data is retransmitted before new data, and retransmissions for crypto + // stream go first. + // Do not check congestion window when crypto stream has lost data. + EXPECT_CALL(*send_algorithm, CanSend(_)).Times(0); + if (connection_->transport_version() < QUIC_VERSION_47) { + EXPECT_CALL(*crypto_stream, OnCanWrite()); + EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) + .WillOnce(Return(false)); + } + // Check congestion window for non crypto streams. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream4, OnCanWrite()); + EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(false)); + // Connection is blocked. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false)); + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // Unblock connection. + // Stream 2 retransmits lost data. + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false)); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + // Stream 2 sends new data. + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); + EXPECT_CALL(*stream4, OnCanWrite()); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTestServer, DonotRetransmitDataOfClosedStreams) { + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + InSequence s; + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + + QuicStreamFrame frame1(stream2->id(), false, 0, 9); + QuicStreamFrame frame2(stream4->id(), false, 0, 9); + QuicStreamFrame frame3(stream6->id(), false, 0, 9); + + EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(true)); + EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true)); + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true)); + session_.OnFrameLost(QuicFrame(frame3)); + session_.OnFrameLost(QuicFrame(frame2)); + session_.OnFrameLost(QuicFrame(frame1)); + + session_.MarkConnectionLevelWriteBlocked(stream2->id()); + session_.MarkConnectionLevelWriteBlocked(stream4->id()); + session_.MarkConnectionLevelWriteBlocked(stream6->id()); + + // Reset stream 4 locally. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream4->id(), _)); + stream4->Reset(QUIC_STREAM_CANCELLED); + + // Verify stream 4 is removed from streams with lost data list. + EXPECT_CALL(*stream6, OnCanWrite()); + EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(false)); + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false)); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream6, OnCanWrite()); + session_.OnCanWrite(); +} + +TEST_P(QuicSessionTestServer, RetransmitFrames) { + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + InSequence s; + + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); + TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + session_.SendWindowUpdate(stream2->id(), 9); + + QuicStreamFrame frame1(stream2->id(), false, 0, 9); + QuicStreamFrame frame2(stream4->id(), false, 0, 9); + QuicStreamFrame frame3(stream6->id(), false, 0, 9); + QuicWindowUpdateFrame window_update(1, stream2->id(), 9); + QuicFrames frames; + frames.push_back(QuicFrame(frame1)); + frames.push_back(QuicFrame(&window_update)); + frames.push_back(QuicFrame(frame2)); + frames.push_back(QuicFrame(frame3)); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); + + EXPECT_CALL(*stream2, RetransmitStreamData(_, _, _)).WillOnce(Return(true)); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*stream4, RetransmitStreamData(_, _, _)).WillOnce(Return(true)); + EXPECT_CALL(*stream6, RetransmitStreamData(_, _, _)).WillOnce(Return(true)); + EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); + session_.RetransmitFrames(frames, TLP_RETRANSMISSION); +} + +// Regression test of b/110082001. +TEST_P(QuicSessionTestServer, RetransmitLostDataCausesConnectionClose) { + // This test mimics the scenario when a dynamic stream retransmits lost data + // and causes connection close. + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + QuicStreamFrame frame(stream->id(), false, 0, 9); + + EXPECT_CALL(*stream, HasPendingRetransmission()) + .Times(2) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + session_.OnFrameLost(QuicFrame(frame)); + // Retransmit stream data causes connection close. Stream has not sent fin + // yet, so an RST is sent. + EXPECT_CALL(*stream, OnCanWrite()) + .WillOnce(Invoke(stream, &QuicStream::OnClose)); + if (transport_version() == QUIC_VERSION_99) { + // Once for the RST_STREAM, once for the STOP_SENDING + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(2) + .WillRepeatedly(Invoke(&session_, &TestSession::SaveFrame)); + } else { + // Just for the RST_STREAM + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(&session_, &TestSession::SaveFrame)); + } + EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); + session_.OnCanWrite(); +} + +TEST_P(QuicSessionTestServer, SendMessage) { + // Cannot send message when encryption is not established. + EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); + quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); + EXPECT_EQ(MessageResult(MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0), + session_.SendMessage( + MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(), + "", &storage))); + + // Finish handshake. + CryptoHandshakeMessage handshake_message; + session_.GetMutableCryptoStream()->OnHandshakeMessage(handshake_message); + EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed()); + + QuicStringPiece message; + EXPECT_CALL(*connection_, SendMessage(1, _)) + .WillOnce(Return(MESSAGE_STATUS_SUCCESS)); + EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1), + session_.SendMessage( + MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(), + message, &storage))); + // Verify message_id increases. + EXPECT_CALL(*connection_, SendMessage(2, _)) + .WillOnce(Return(MESSAGE_STATUS_TOO_LARGE)); + EXPECT_EQ(MessageResult(MESSAGE_STATUS_TOO_LARGE, 0), + session_.SendMessage( + MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(), + message, &storage))); + // Verify unsent message does not consume a message_id. + EXPECT_CALL(*connection_, SendMessage(2, _)) + .WillOnce(Return(MESSAGE_STATUS_SUCCESS)); + EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 2), + session_.SendMessage( + MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(), + message, &storage))); + + QuicMessageFrame frame(1); + QuicMessageFrame frame2(2); + EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame))); + EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame2))); + + // Lost message 2. + session_.OnMessageLost(2); + EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame2))); + + // message 1 gets acked. + session_.OnMessageAcked(1); + EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame))); +} + +// Regression test of b/115323618. +TEST_P(QuicSessionTestServer, LocallyResetZombieStreams) { + QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_); + + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + QuicString body(100, '.'); + stream2->CloseReadSide(); + stream2->WriteOrBufferData(body, true, nullptr); + EXPECT_TRUE(stream2->IsWaitingForAcks()); + // Verify stream2 is a zombie streams. + EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + + QuicStreamFrame frame(stream2->id(), true, 0, 100); + EXPECT_CALL(*stream2, HasPendingRetransmission()) + .WillRepeatedly(Return(true)); + session_.OnFrameLost(QuicFrame(frame)); + + // Reset stream2 locally. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame)); + EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _)); + stream2->Reset(QUIC_STREAM_CANCELLED); + + // Verify stream 2 gets closed. + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + EXPECT_TRUE(session_.IsClosedStream(stream2->id())); + EXPECT_CALL(*stream2, OnCanWrite()).Times(0); + session_.OnCanWrite(); +} + +TEST_P(QuicSessionTestServer, CleanUpClosedStreamsAlarm) { + EXPECT_FALSE( + QuicSessionPeer::GetCleanUpClosedStreamsAlarm(&session_)->IsSet()); + + session_.set_writev_consumes_all_data(true); + TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); + EXPECT_FALSE(stream2->IsWaitingForAcks()); + + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _)); + session_.CloseStream(stream2->id()); + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id())); + EXPECT_EQ(1u, session_.closed_streams()->size()); + EXPECT_TRUE( + QuicSessionPeer::GetCleanUpClosedStreamsAlarm(&session_)->IsSet()); + + alarm_factory_.FireAlarm( + QuicSessionPeer::GetCleanUpClosedStreamsAlarm(&session_)); + EXPECT_TRUE(session_.closed_streams()->empty()); +} + +TEST_P(QuicSessionTestServer, WriteUnidirectionalStream) { + session_.set_writev_consumes_all_data(true); + TestStream* stream4 = new TestStream(GetNthServerInitiatedUnidirectionalId(1), + &session_, WRITE_UNIDIRECTIONAL); + session_.ActivateStream(QuicWrapUnique(stream4)); + QuicString body(100, '.'); + stream4->WriteOrBufferData(body, false, nullptr); + EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream4->id())); + stream4->WriteOrBufferData(body, true, nullptr); + EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), stream4->id())); +} + +TEST_P(QuicSessionTestServer, ReceivedDataOnWriteUnidirectionalStream) { + TestStream* stream4 = new TestStream(GetNthServerInitiatedUnidirectionalId(1), + &session_, WRITE_UNIDIRECTIONAL); + session_.ActivateStream(QuicWrapUnique(stream4)); + + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM, _, _)) + .Times(1); + QuicStreamFrame stream_frame(GetNthServerInitiatedUnidirectionalId(1), false, + 0, 2); + session_.OnStreamFrame(stream_frame); +} + +TEST_P(QuicSessionTestServer, ReadUnidirectionalStream) { + TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), + &session_, READ_UNIDIRECTIONAL); + session_.ActivateStream(QuicWrapUnique(stream4)); + EXPECT_FALSE(stream4->IsWaitingForAcks()); + // Discard all incoming data. + stream4->StopReading(); + + QuicString data(100, '.'); + QuicStreamFrame stream_frame(GetNthClientInitiatedUnidirectionalId(1), false, + 0, data); + stream4->OnStreamFrame(stream_frame); + EXPECT_TRUE(session_.closed_streams()->empty()); + + QuicStreamFrame stream_frame2(GetNthClientInitiatedUnidirectionalId(1), true, + 100, data); + stream4->OnStreamFrame(stream_frame2); + EXPECT_EQ(1u, session_.closed_streams()->size()); +} + +TEST_P(QuicSessionTestServer, WriteOrBufferDataOnReadUnidirectionalStream) { + TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), + &session_, READ_UNIDIRECTIONAL); + session_.ActivateStream(QuicWrapUnique(stream4)); + + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, _, _)) + .Times(1); + QuicString body(100, '.'); + stream4->WriteOrBufferData(body, false, nullptr); +} + +TEST_P(QuicSessionTestServer, WritevDataOnReadUnidirectionalStream) { + TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), + &session_, READ_UNIDIRECTIONAL); + session_.ActivateStream(QuicWrapUnique(stream4)); + + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, _, _)) + .Times(1); + QuicString body(100, '.'); + struct iovec iov = {const_cast<char*>(body.data()), body.length()}; + QuicMemSliceStorage storage( + &iov, 1, session_.connection()->helper()->GetStreamSendBufferAllocator(), + 1024); + stream4->WriteMemSlices(storage.ToSpan(), false); +} + +TEST_P(QuicSessionTestServer, WriteMemSlicesOnReadUnidirectionalStream) { + TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), + &session_, READ_UNIDIRECTIONAL); + session_.ActivateStream(QuicWrapUnique(stream4)); + + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, _, _)) + .Times(1); + char data[1024]; + std::vector<std::pair<char*, size_t>> buffers; + buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data))); + buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data))); + QuicTestMemSliceVector vector(buffers); + stream4->WriteMemSlices(vector.span(), false); +} + +// Test code that tests that an incoming stream frame with a new (not previously +// seen) stream id is acceptable. The ID must not be larger than has been +// advertised. It may be equal to what has been advertised. These tests +// invoke QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId by calling +// QuicSession::OnStreamFrame in order to check that all the steps are connected +// properly and that nothing in the call path interferes with the check. +// First test make sure that streams with ids below the limit are accepted. +TEST_P(QuicSessionTestServer, NewStreamIdBelowLimit) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + QuicStreamId bidirectional_stream_id = + QuicSessionPeer::v99_streamid_manager(&session_) + ->advertised_max_allowed_incoming_bidirectional_stream_id() - + kV99StreamIdIncrement; + QuicStreamFrame bidirectional_stream_frame(bidirectional_stream_id, false, 0, + "Random String"); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + session_.OnStreamFrame(bidirectional_stream_frame); + + QuicStreamId unidirectional_stream_id = + QuicSessionPeer::v99_streamid_manager(&session_) + ->advertised_max_allowed_incoming_unidirectional_stream_id() - + kV99StreamIdIncrement; + QuicStreamFrame unidirectional_stream_frame(unidirectional_stream_id, false, + 0, "Random String"); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + session_.OnStreamFrame(unidirectional_stream_frame); +} + +// Accept a stream with an ID that equals the limit. +TEST_P(QuicSessionTestServer, NewStreamIdAtLimit) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + QuicStreamId bidirectional_stream_id = + QuicSessionPeer::v99_streamid_manager(&session_) + ->advertised_max_allowed_incoming_bidirectional_stream_id(); + QuicStreamFrame bidirectional_stream_frame(bidirectional_stream_id, false, 0, + "Random String"); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + session_.OnStreamFrame(bidirectional_stream_frame); + + QuicStreamId unidirectional_stream_id = + QuicSessionPeer::v99_streamid_manager(&session_) + ->advertised_max_allowed_incoming_unidirectional_stream_id(); + QuicStreamFrame unidirectional_stream_frame(unidirectional_stream_id, false, + 0, "Random String"); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + session_.OnStreamFrame(unidirectional_stream_frame); +} + +// Close the connection if the id exceeds the limit. +TEST_P(QuicSessionTestServer, NewStreamIdAboveLimit) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + QuicStreamId bidirectional_stream_id = + QuicSessionPeer::v99_streamid_manager(&session_) + ->advertised_max_allowed_incoming_bidirectional_stream_id() + + kV99StreamIdIncrement; + QuicStreamFrame bidirectional_stream_frame(bidirectional_stream_id, false, 0, + "Random String"); + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, + "Stream id 404 above 400", _)); + session_.OnStreamFrame(bidirectional_stream_frame); + + QuicStreamId unidirectional_stream_id = + QuicSessionPeer::v99_streamid_manager(&session_) + ->advertised_max_allowed_incoming_unidirectional_stream_id() + + kV99StreamIdIncrement; + QuicStreamFrame unidirectional_stream_frame(unidirectional_stream_id, false, + 0, "Random String"); + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, + "Stream id 402 above 398", _)); + session_.OnStreamFrame(unidirectional_stream_frame); +} + +// Check that the OnStopSendingFrame upcall handles bad input properly +// First test checks that invalid stream ids are handled. +TEST_P(QuicSessionTestServer, OnStopSendingInputInvalidStreamId) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + // Check that "invalid" stream ids are rejected. + // Note that the notion of an invalid stream id is Google-specific. + QuicStopSendingFrame frame(1, -1, 123); + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Received STOP_SENDING for an invalid stream", _)); + EXPECT_FALSE(session_.OnStopSendingFrame(frame)); +} + +// Second test, streams in the static stream map are not subject to +// STOP_SENDING; it's ignored. +TEST_P(QuicSessionTestServer, OnStopSendingInputStaticStreams) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + // Check that a stream id in the static stream map is ignored. + // Note that the notion of a static stream is Google-specific. + QuicStopSendingFrame frame(1, 0, 123); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Received STOP_SENDING for a static stream", _)); + EXPECT_FALSE(session_.OnStopSendingFrame(frame)); +} + +// Third test, if stream id specifies a closed stream: +// return true and do not close the connection. +TEST_P(QuicSessionTestServer, OnStopSendingInputClosedStream) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + QuicStreamId stream_id = stream->id(); + // Expect these as side effect of the close operations. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, OnStreamReset(_, _)); + stream->CloseWriteSide(); + stream->CloseReadSide(); + QuicStopSendingFrame frame(1, stream_id, 123); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_TRUE(session_.OnStopSendingFrame(frame)); +} + +// Fourth test, if stream id specifies a nonexistent stream, return false and +// close the connection +TEST_P(QuicSessionTestServer, OnStopSendingInputNonExistentStream) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + + QuicStopSendingFrame frame(1, GetNthServerInitiatedBidirectionalId(123456), + 123); + EXPECT_CALL( + *connection_, + CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, + "Received STOP_SENDING for a non-existent stream", _)) + .Times(1); + EXPECT_FALSE(session_.OnStopSendingFrame(frame)); +} + +// For a valid stream, ensure that all works +TEST_P(QuicSessionTestServer, OnStopSendingInputValidStream) { + if (transport_version() != QUIC_VERSION_99) { + // Applicable only to V99 + return; + } + + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + + // Ensure that the stream starts out open in both directions. + EXPECT_FALSE(QuicStreamPeer::write_side_closed(stream)); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream)); + + QuicStreamId stream_id = stream->id(); + QuicStopSendingFrame frame(1, stream_id, 123); + EXPECT_CALL(*stream, OnStopSending(123)); + // Expect a reset to come back out. + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL( + *connection_, + OnStreamReset(stream_id, static_cast<QuicRstStreamErrorCode>(123))); + EXPECT_TRUE(session_.OnStopSendingFrame(frame)); + // When the STOP_SENDING is received, the node generates a RST_STREAM, + // which closes the stream in the write direction. Ensure this. + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream)); + EXPECT_TRUE(QuicStreamPeer::write_side_closed(stream)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_simple_buffer_allocator.cc b/quic/core/quic_simple_buffer_allocator.cc new file mode 100644 index 0000000..3056c33 --- /dev/null +++ b/quic/core/quic_simple_buffer_allocator.cc
@@ -0,0 +1,21 @@ +// Copyright (c) 2012 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/quic_simple_buffer_allocator.h" + +namespace quic { + +char* SimpleBufferAllocator::New(size_t size) { + return new char[size]; +} + +char* SimpleBufferAllocator::New(size_t size, bool /* flag_enable */) { + return New(size); +} + +void SimpleBufferAllocator::Delete(char* buffer) { + delete[] buffer; +} + +} // namespace quic
diff --git a/quic/core/quic_simple_buffer_allocator.h b/quic/core/quic_simple_buffer_allocator.h new file mode 100644 index 0000000..e681e80 --- /dev/null +++ b/quic/core/quic_simple_buffer_allocator.h
@@ -0,0 +1,22 @@ +// Copyright (c) 2012 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_QUIC_SIMPLE_BUFFER_ALLOCATOR_H_ +#define QUICHE_QUIC_CORE_QUIC_SIMPLE_BUFFER_ALLOCATOR_H_ + +#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE SimpleBufferAllocator : public QuicBufferAllocator { + public: + char* New(size_t size) override; + char* New(size_t size, bool flag_enable) override; + void Delete(char* buffer) override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_SIMPLE_BUFFER_ALLOCATOR_H_
diff --git a/quic/core/quic_simple_buffer_allocator_test.cc b/quic/core/quic_simple_buffer_allocator_test.cc new file mode 100644 index 0000000..747d733 --- /dev/null +++ b/quic/core/quic_simple_buffer_allocator_test.cc
@@ -0,0 +1,28 @@ +// Copyright (c) 2012 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/quic_simple_buffer_allocator.h" + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace { + +class SimpleBufferAllocatorTest : public QuicTest {}; + +TEST_F(SimpleBufferAllocatorTest, NewDelete) { + SimpleBufferAllocator alloc; + char* buf = alloc.New(4); + EXPECT_NE(nullptr, buf); + alloc.Delete(buf); +} + +TEST_F(SimpleBufferAllocatorTest, DeleteNull) { + SimpleBufferAllocator alloc; + alloc.Delete(nullptr); +} + +} // namespace +} // namespace quic
diff --git a/quic/core/quic_socket_address_coder.cc b/quic/core/quic_socket_address_coder.cc new file mode 100644 index 0000000..bb1eaea --- /dev/null +++ b/quic/core/quic_socket_address_coder.cc
@@ -0,0 +1,87 @@ +// Copyright 2014 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/quic_socket_address_coder.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace { + +// For convenience, the values of these constants match the values of AF_INET +// and AF_INET6 on Linux. +const uint16_t kIPv4 = 2; +const uint16_t kIPv6 = 10; + +} // namespace + +QuicSocketAddressCoder::QuicSocketAddressCoder() {} + +QuicSocketAddressCoder::QuicSocketAddressCoder(const QuicSocketAddress& address) + : address_(address) {} + +QuicSocketAddressCoder::~QuicSocketAddressCoder() {} + +QuicString QuicSocketAddressCoder::Encode() const { + QuicString serialized; + uint16_t address_family; + switch (address_.host().address_family()) { + case IpAddressFamily::IP_V4: + address_family = kIPv4; + break; + case IpAddressFamily::IP_V6: + address_family = kIPv6; + break; + default: + return serialized; + } + serialized.append(reinterpret_cast<const char*>(&address_family), + sizeof(address_family)); + serialized.append(address_.host().ToPackedString()); + uint16_t port = address_.port(); + serialized.append(reinterpret_cast<const char*>(&port), sizeof(port)); + return serialized; +} + +bool QuicSocketAddressCoder::Decode(const char* data, size_t length) { + uint16_t address_family; + if (length < sizeof(address_family)) { + return false; + } + memcpy(&address_family, data, sizeof(address_family)); + data += sizeof(address_family); + length -= sizeof(address_family); + + size_t ip_length; + switch (address_family) { + case kIPv4: + ip_length = QuicIpAddress::kIPv4AddressSize; + break; + case kIPv6: + ip_length = QuicIpAddress::kIPv6AddressSize; + break; + default: + return false; + } + if (length < ip_length) { + return false; + } + std::vector<uint8_t> ip(ip_length); + memcpy(&ip[0], data, ip_length); + data += ip_length; + length -= ip_length; + + uint16_t port; + if (length != sizeof(port)) { + return false; + } + memcpy(&port, data, length); + + QuicIpAddress ip_address; + ip_address.FromPackedString(reinterpret_cast<const char*>(&ip[0]), ip_length); + address_ = QuicSocketAddress(ip_address, port); + return true; +} + +} // namespace quic
diff --git a/quic/core/quic_socket_address_coder.h b/quic/core/quic_socket_address_coder.h new file mode 100644 index 0000000..6ac5d49 --- /dev/null +++ b/quic/core/quic_socket_address_coder.h
@@ -0,0 +1,42 @@ +// Copyright 2014 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_QUIC_SOCKET_ADDRESS_CODER_H_ +#define QUICHE_QUIC_CORE_QUIC_SOCKET_ADDRESS_CODER_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// Serializes and parses a socket address (IP address and port), to be used in +// the kCADR tag in the ServerHello handshake message and the Public Reset +// packet. +class QUIC_EXPORT_PRIVATE QuicSocketAddressCoder { + public: + QuicSocketAddressCoder(); + explicit QuicSocketAddressCoder(const QuicSocketAddress& address); + QuicSocketAddressCoder(const QuicSocketAddressCoder&) = delete; + QuicSocketAddressCoder& operator=(const QuicSocketAddressCoder&) = delete; + ~QuicSocketAddressCoder(); + + QuicString Encode() const; + + bool Decode(const char* data, size_t length); + + QuicIpAddress ip() const { return address_.host(); } + + uint16_t port() const { return address_.port(); } + + private: + QuicSocketAddress address_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_SOCKET_ADDRESS_CODER_H_
diff --git a/quic/core/quic_socket_address_coder_test.cc b/quic/core/quic_socket_address_coder_test.cc new file mode 100644 index 0000000..eb65efa --- /dev/null +++ b/quic/core/quic_socket_address_coder_test.cc
@@ -0,0 +1,127 @@ +// Copyright 2014 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/quic_socket_address_coder.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class QuicSocketAddressCoderTest : public QuicTest {}; + +TEST_F(QuicSocketAddressCoderTest, EncodeIPv4) { + QuicIpAddress ip; + ip.FromString("4.31.198.44"); + QuicSocketAddressCoder coder(QuicSocketAddress(ip, 0x1234)); + QuicString serialized = coder.Encode(); + QuicString expected("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8); + EXPECT_EQ(expected, serialized); +} + +TEST_F(QuicSocketAddressCoderTest, EncodeIPv6) { + QuicIpAddress ip; + ip.FromString("2001:700:300:1800::f"); + QuicSocketAddressCoder coder(QuicSocketAddress(ip, 0x5678)); + QuicString serialized = coder.Encode(); + QuicString expected( + "\x0a\x00" + "\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f" + "\x78\x56", + 20); + EXPECT_EQ(expected, serialized); +} + +TEST_F(QuicSocketAddressCoderTest, DecodeIPv4) { + QuicString serialized("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8); + QuicSocketAddressCoder coder; + ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length())); + EXPECT_EQ(IpAddressFamily::IP_V4, coder.ip().address_family()); + QuicString expected_addr("\x04\x1f\xc6\x2c"); + EXPECT_EQ(expected_addr, coder.ip().ToPackedString()); + EXPECT_EQ(0x1234, coder.port()); +} + +TEST_F(QuicSocketAddressCoderTest, DecodeIPv6) { + QuicString serialized( + "\x0a\x00" + "\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f" + "\x78\x56", + 20); + QuicSocketAddressCoder coder; + ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length())); + EXPECT_EQ(IpAddressFamily::IP_V6, coder.ip().address_family()); + QuicString expected_addr( + "\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f", + 16); + EXPECT_EQ(expected_addr, coder.ip().ToPackedString()); + EXPECT_EQ(0x5678, coder.port()); +} + +TEST_F(QuicSocketAddressCoderTest, DecodeBad) { + QuicString serialized( + "\x0a\x00" + "\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f" + "\x78\x56", + 20); + QuicSocketAddressCoder coder; + EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length())); + // Append junk. + serialized.push_back('\0'); + EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length())); + // Undo. + serialized.resize(20); + EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length())); + + // Set an unknown address family. + serialized[0] = '\x03'; + EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length())); + // Undo. + serialized[0] = '\x0a'; + EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length())); + + // Truncate. + size_t len = serialized.length(); + for (size_t i = 0; i < len; i++) { + ASSERT_FALSE(serialized.empty()); + serialized.erase(serialized.length() - 1); + EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length())); + } + EXPECT_TRUE(serialized.empty()); +} + +TEST_F(QuicSocketAddressCoderTest, EncodeAndDecode) { + struct { + const char* ip_literal; + uint16_t port; + } test_case[] = { + {"93.184.216.119", 0x1234}, + {"199.204.44.194", 80}, + {"149.20.4.69", 443}, + {"127.0.0.1", 8080}, + {"2001:700:300:1800::", 0x5678}, + {"::1", 65534}, + }; + + for (size_t i = 0; i < QUIC_ARRAYSIZE(test_case); i++) { + QuicIpAddress ip; + ASSERT_TRUE(ip.FromString(test_case[i].ip_literal)); + QuicSocketAddressCoder encoder(QuicSocketAddress(ip, test_case[i].port)); + QuicString serialized = encoder.Encode(); + + QuicSocketAddressCoder decoder; + ASSERT_TRUE(decoder.Decode(serialized.data(), serialized.length())); + EXPECT_EQ(encoder.ip(), decoder.ip()); + EXPECT_EQ(encoder.port(), decoder.port()); + } +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc new file mode 100644 index 0000000..86207b6 --- /dev/null +++ b/quic/core/quic_stream.cc
@@ -0,0 +1,1101 @@ +// Copyright (c) 2012 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/quic_stream.h" + +#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +using spdy::SpdyPriority; + +namespace quic { + +#define ENDPOINT \ + (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") + +namespace { + +size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) { + return session->config()->GetInitialStreamFlowControlWindowToSend(); +} + +size_t GetReceivedFlowControlWindow(QuicSession* session) { + if (session->config()->HasReceivedInitialStreamFlowControlWindowBytes()) { + return session->config()->ReceivedInitialStreamFlowControlWindowBytes(); + } + + return kMinimumFlowControlSendWindow; +} + +} // namespace + +// static +const SpdyPriority QuicStream::kDefaultPriority; + +PendingStream::PendingStream(QuicStreamId id, QuicSession* session) + : id_(id), + session_(session), + stream_bytes_read_(0), + fin_received_(false), + connection_flow_controller_(session->flow_controller()), + flow_controller_(session, + id, + /*is_connection_flow_controller*/ false, + GetReceivedFlowControlWindow(session), + GetInitialStreamFlowControlWindowToSend(session), + kStreamReceiveWindowLimit, + session_->flow_controller()->auto_tune_receive_window(), + session_->flow_controller()), + sequencer_(this) {} + +void PendingStream::OnDataAvailable() { + QUIC_BUG << "OnDataAvailable should not be called."; + CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected data available"); +} + +void PendingStream::OnFinRead() { + QUIC_BUG << "OnFinRead should not be called."; + CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected fin read"); +} + +void PendingStream::AddBytesConsumed(QuicByteCount bytes) { + QUIC_BUG << "AddBytesConsumed should not be called."; + CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected bytes consumed"); +} + +void PendingStream::Reset(QuicRstStreamErrorCode error) { + session_->SendRstStream(id_, error, 0); +} + +void PendingStream::CloseConnectionWithDetails(QuicErrorCode error, + const QuicString& details) { + session_->connection()->CloseConnection( + error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +QuicStreamId PendingStream::id() const { + return id_; +} + +const QuicSocketAddress& PendingStream::PeerAddressOfLatestPacket() const { + return session_->connection()->last_packet_source_address(); +} + +void PendingStream::OnStreamFrame(const QuicStreamFrame& frame) { + DCHECK_EQ(frame.stream_id, id_); + DCHECK_NE(0u, frame.offset); + + bool is_stream_too_long = + (frame.offset > kMaxStreamLength) || + (kMaxStreamLength - frame.offset < frame.data_length); + if (is_stream_too_long) { + // Close connection if stream becomes too long. + QUIC_PEER_BUG + << "Receive stream frame reaches max stream length. frame offset " + << frame.offset << " length " << frame.data_length; + CloseConnectionWithDetails( + QUIC_STREAM_LENGTH_OVERFLOW, + "Peer sends more data than allowed on this stream."); + return; + } + + if (frame.fin) { + fin_received_ = true; + } + + // This count includes duplicate data received. + size_t frame_payload_size = frame.data_length; + stream_bytes_read_ += frame_payload_size; + + // Flow control is interested in tracking highest received offset. + // Only interested in received frames that carry data. + if (frame_payload_size > 0 && + MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) { + // As the highest received offset has changed, check to see if this is a + // violation of flow control. + if (flow_controller_.FlowControlViolation() || + connection_flow_controller_->FlowControlViolation()) { + CloseConnectionWithDetails( + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, + "Flow control violation after increasing offset"); + return; + } + } + + sequencer_.OnStreamFrame(frame); +} + +void PendingStream::OnRstStreamFrame(const QuicRstStreamFrame& frame) { + DCHECK_EQ(frame.stream_id, id_); + + if (frame.byte_offset > kMaxStreamLength) { + // Peer are not suppose to write bytes more than maxium allowed. + CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW, + "Reset frame stream offset overflow."); + return; + } + MaybeIncreaseHighestReceivedOffset(frame.byte_offset); + if (flow_controller_.FlowControlViolation() || + connection_flow_controller_->FlowControlViolation()) { + CloseConnectionWithDetails( + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, + "Flow control violation after increasing offset"); + return; + } +} + +bool PendingStream::MaybeIncreaseHighestReceivedOffset( + QuicStreamOffset new_offset) { + uint64_t increment = + new_offset - flow_controller_.highest_received_byte_offset(); + if (!flow_controller_.UpdateHighestReceivedOffset(new_offset)) { + return false; + } + + // If |new_offset| increased the stream flow controller's highest received + // offset, increase the connection flow controller's value by the incremental + // difference. + connection_flow_controller_->UpdateHighestReceivedOffset( + connection_flow_controller_->highest_received_byte_offset() + increment); + return true; +} + +QuicStream::QuicStream(PendingStream pending, StreamType type) + : QuicStream(pending.id_, + pending.session_, + std::move(pending.sequencer_), + /*is_static=*/false, + type, + pending.stream_bytes_read_, + pending.fin_received_, + std::move(pending.flow_controller_), + pending.connection_flow_controller_) { + sequencer_.set_stream(this); +} + +QuicStream::QuicStream(QuicStreamId id, + QuicSession* session, + bool is_static, + StreamType type) + : QuicStream(id, + session, + QuicStreamSequencer(this), + is_static, + type, + 0, + false, + QuicFlowController( + session, + id, + /*is_connection_flow_controller*/ false, + GetReceivedFlowControlWindow(session), + GetInitialStreamFlowControlWindowToSend(session), + kStreamReceiveWindowLimit, + session->flow_controller()->auto_tune_receive_window(), + session->flow_controller()), + session->flow_controller()) {} + +QuicStream::QuicStream(QuicStreamId id, + QuicSession* session, + QuicStreamSequencer sequencer, + bool is_static, + StreamType type, + uint64_t stream_bytes_read, + bool fin_received, + QuicFlowController flow_controller, + QuicFlowController* connection_flow_controller) + : sequencer_(std::move(sequencer)), + id_(id), + session_(session), + priority_(kDefaultPriority), + stream_bytes_read_(stream_bytes_read), + stream_error_(QUIC_STREAM_NO_ERROR), + connection_error_(QUIC_NO_ERROR), + read_side_closed_(false), + write_side_closed_(false), + fin_buffered_(false), + fin_sent_(false), + fin_outstanding_(false), + fin_lost_(false), + fin_received_(fin_received), + rst_sent_(false), + rst_received_(false), + perspective_(session_->perspective()), + flow_controller_(std::move(flow_controller)), + connection_flow_controller_(connection_flow_controller), + stream_contributes_to_connection_flow_control_(true), + busy_counter_(0), + add_random_padding_after_fin_(false), + send_buffer_( + session->connection()->helper()->GetStreamSendBufferAllocator()), + buffered_data_threshold_(GetQuicFlag(FLAGS_quic_buffered_data_threshold)), + is_static_(is_static), + deadline_(QuicTime::Zero()), + type_(session->connection()->transport_version() == QUIC_VERSION_99 + ? QuicUtils::GetStreamType(id_, + perspective_, + session->IsIncomingStream(id_)) + : type) { + if (type_ == WRITE_UNIDIRECTIONAL) { + set_fin_received(true); + CloseReadSide(); + } else if (type_ == READ_UNIDIRECTIONAL) { + set_fin_sent(true); + CloseWriteSide(); + } + SetFromConfig(); + session_->RegisterStreamPriority(id, is_static_, priority_); +} + +QuicStream::~QuicStream() { + if (session_ != nullptr && IsWaitingForAcks()) { + QUIC_DVLOG(1) + << ENDPOINT << "Stream " << id_ + << " gets destroyed while waiting for acks. stream_bytes_outstanding = " + << send_buffer_.stream_bytes_outstanding() + << ", fin_outstanding: " << fin_outstanding_; + } + if (session_ != nullptr) { + session_->UnregisterStreamPriority(id(), is_static_); + } +} + +void QuicStream::SetFromConfig() {} + +void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) { + DCHECK_EQ(frame.stream_id, id_); + + DCHECK(!(read_side_closed_ && write_side_closed_)); + + if (type_ == WRITE_UNIDIRECTIONAL) { + CloseConnectionWithDetails( + QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM, + "Data received on write unidirectional stream"); + return; + } + + bool is_stream_too_long = + (frame.offset > kMaxStreamLength) || + (kMaxStreamLength - frame.offset < frame.data_length); + if (is_stream_too_long) { + // Close connection if stream becomes too long. + QUIC_PEER_BUG << "Receive stream frame on stream " << id_ + << " reaches max stream length. frame offset " << frame.offset + << " length " << frame.data_length << ". " + << sequencer_.DebugString(); + CloseConnectionWithDetails( + QUIC_STREAM_LENGTH_OVERFLOW, + QuicStrCat("Peer sends more data than allowed on stream ", id_, + ". frame: offset = ", frame.offset, ", length = ", + frame.data_length, ". ", sequencer_.DebugString())); + return; + } + if (frame.fin) { + fin_received_ = true; + if (fin_sent_) { + session_->StreamDraining(id_); + } + } + + if (read_side_closed_) { + QUIC_DLOG(INFO) + << ENDPOINT << "Stream " << frame.stream_id + << " is closed for reading. Ignoring newly received stream data."; + // The subclass does not want to read data: blackhole the data. + return; + } + + // This count includes duplicate data received. + size_t frame_payload_size = frame.data_length; + stream_bytes_read_ += frame_payload_size; + + // Flow control is interested in tracking highest received offset. + // Only interested in received frames that carry data. + if (frame_payload_size > 0 && + MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) { + // As the highest received offset has changed, check to see if this is a + // violation of flow control. + if (flow_controller_.FlowControlViolation() || + connection_flow_controller_->FlowControlViolation()) { + CloseConnectionWithDetails( + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, + "Flow control violation after increasing offset"); + return; + } + } + + sequencer_.OnStreamFrame(frame); +} + +int QuicStream::num_frames_received() const { + return sequencer_.num_frames_received(); +} + +int QuicStream::num_duplicate_frames_received() const { + return sequencer_.num_duplicate_frames_received(); +} + +void QuicStream::OnStreamReset(const QuicRstStreamFrame& frame) { + rst_received_ = true; + if (frame.byte_offset > kMaxStreamLength) { + // Peer are not suppose to write bytes more than maxium allowed. + CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW, + "Reset frame stream offset overflow."); + return; + } + MaybeIncreaseHighestReceivedOffset(frame.byte_offset); + if (flow_controller_.FlowControlViolation() || + connection_flow_controller_->FlowControlViolation()) { + CloseConnectionWithDetails( + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, + "Flow control violation after increasing offset"); + return; + } + + stream_error_ = frame.error_code; + // Google QUIC closes both sides of the stream in response to a + // RESET_STREAM, IETF QUIC closes only the read side. + if (transport_version() != QUIC_VERSION_99) { + CloseWriteSide(); + } + CloseReadSide(); +} + +void QuicStream::OnConnectionClosed(QuicErrorCode error, + ConnectionCloseSource /*source*/) { + if (read_side_closed_ && write_side_closed_) { + return; + } + if (error != QUIC_NO_ERROR) { + stream_error_ = QUIC_STREAM_CONNECTION_ERROR; + connection_error_ = error; + } + + CloseWriteSide(); + CloseReadSide(); +} + +void QuicStream::OnFinRead() { + DCHECK(sequencer_.IsClosed()); + // OnFinRead can be called due to a FIN flag in a headers block, so there may + // have been no OnStreamFrame call with a FIN in the frame. + fin_received_ = true; + // If fin_sent_ is true, then CloseWriteSide has already been called, and the + // stream will be destroyed by CloseReadSide, so don't need to call + // StreamDraining. + CloseReadSide(); +} + +void QuicStream::Reset(QuicRstStreamErrorCode error) { + stream_error_ = error; + // Sending a RstStream results in calling CloseStream. + session()->SendRstStream(id(), error, stream_bytes_written()); + rst_sent_ = true; +} + +void QuicStream::CloseConnectionWithDetails(QuicErrorCode error, + const QuicString& details) { + session()->connection()->CloseConnection( + error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +SpdyPriority QuicStream::priority() const { + return priority_; +} + +void QuicStream::SetPriority(SpdyPriority priority) { + priority_ = priority; + session_->UpdateStreamPriority(id(), priority); +} + +void QuicStream::WriteOrBufferData( + QuicStringPiece data, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + if (data.empty() && !fin) { + QUIC_BUG << "data.empty() && !fin"; + return; + } + + if (fin_buffered_) { + QUIC_BUG << "Fin already buffered"; + return; + } + if (write_side_closed_) { + QUIC_DLOG(ERROR) << ENDPOINT + << "Attempt to write when the write side is closed"; + if (type_ == READ_UNIDIRECTIONAL) { + CloseConnectionWithDetails( + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, + "Try to send data on read unidirectional stream"); + } + return; + } + + QuicConsumedData consumed_data(0, false); + fin_buffered_ = fin; + + bool had_buffered_data = HasBufferedData(); + // Do not respect buffered data upper limit as WriteOrBufferData guarantees + // all data to be consumed. + if (data.length() > 0) { + struct iovec iov(QuicUtils::MakeIovec(data)); + QuicStreamOffset offset = send_buffer_.stream_offset(); + if (kMaxStreamLength - offset < data.length()) { + QUIC_BUG << "Write too many data via stream " << id_; + CloseConnectionWithDetails( + QUIC_STREAM_LENGTH_OVERFLOW, + QuicStrCat("Write too many data via stream ", id_)); + return; + } + send_buffer_.SaveStreamData(&iov, 1, 0, data.length()); + OnDataBuffered(offset, data.length(), ack_listener); + } + if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) { + // Write data if there is no buffered data before. + WriteBufferedData(); + } +} + +void QuicStream::OnCanWrite() { + if (HasDeadlinePassed()) { + OnDeadlinePassed(); + return; + } + if (HasPendingRetransmission()) { + WritePendingRetransmission(); + // Exit early to allow other streams to write pending retransmissions if + // any. + return; + } + + if (write_side_closed_) { + QUIC_DLOG(ERROR) + << ENDPOINT << "Stream " << id() + << " attempting to write new data when the write side is closed"; + return; + } + if (HasBufferedData() || (fin_buffered_ && !fin_sent_)) { + WriteBufferedData(); + } + if (!fin_buffered_ && !fin_sent_ && CanWriteNewData()) { + // Notify upper layer to write new data when buffered data size is below + // low water mark. + OnCanWriteNewData(); + } +} + +void QuicStream::MaybeSendBlocked() { + if (flow_controller_.ShouldSendBlocked()) { + session_->SendBlocked(id_); + } + if (!stream_contributes_to_connection_flow_control_) { + return; + } + if (connection_flow_controller_->ShouldSendBlocked()) { + session_->SendBlocked(QuicUtils::GetInvalidStreamId(transport_version())); + } + // If the stream is blocked by connection-level flow control but not by + // stream-level flow control, add the stream to the write blocked list so that + // the stream will be given a chance to write when a connection-level + // WINDOW_UPDATE arrives. + if (connection_flow_controller_->IsBlocked() && + !flow_controller_.IsBlocked()) { + session_->MarkConnectionLevelWriteBlocked(id()); + } +} + +QuicConsumedData QuicStream::WritevData(const struct iovec* iov, + int iov_count, + bool fin) { + if (write_side_closed_) { + QUIC_DLOG(ERROR) << ENDPOINT << "Stream " << id() + << "attempting to write when the write side is closed"; + if (type_ == READ_UNIDIRECTIONAL) { + CloseConnectionWithDetails( + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, + "Try to send data on read unidirectional stream"); + } + return QuicConsumedData(0, false); + } + + // How much data was provided. + size_t write_length = 0; + if (iov != nullptr) { + for (int i = 0; i < iov_count; ++i) { + write_length += iov[i].iov_len; + } + } + + QuicConsumedData consumed_data(0, false); + if (fin_buffered_) { + QUIC_BUG << "Fin already buffered"; + return consumed_data; + } + + if (kMaxStreamLength - send_buffer_.stream_offset() < write_length) { + QUIC_BUG << "Write too many data via stream " << id_; + CloseConnectionWithDetails( + QUIC_STREAM_LENGTH_OVERFLOW, + QuicStrCat("Write too many data via stream ", id_)); + return consumed_data; + } + + bool had_buffered_data = HasBufferedData(); + if (CanWriteNewData()) { + // Save all data if buffered data size is below low water mark. + consumed_data.bytes_consumed = write_length; + if (consumed_data.bytes_consumed > 0) { + QuicStreamOffset offset = send_buffer_.stream_offset(); + send_buffer_.SaveStreamData(iov, iov_count, 0, write_length); + OnDataBuffered(offset, write_length, nullptr); + } + } + consumed_data.fin_consumed = + consumed_data.bytes_consumed == write_length && fin; + fin_buffered_ = consumed_data.fin_consumed; + + if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) { + // Write data if there is no buffered data before. + WriteBufferedData(); + } + + return consumed_data; +} + +QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) { + QuicConsumedData consumed_data(0, false); + if (span.empty() && !fin) { + QUIC_BUG << "span.empty() && !fin"; + return consumed_data; + } + + if (fin_buffered_) { + QUIC_BUG << "Fin already buffered"; + return consumed_data; + } + + if (write_side_closed_) { + QUIC_DLOG(ERROR) << ENDPOINT << "Stream " << id() + << "attempting to write when the write side is closed"; + if (type_ == READ_UNIDIRECTIONAL) { + CloseConnectionWithDetails( + QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, + "Try to send data on read unidirectional stream"); + } + return consumed_data; + } + + bool had_buffered_data = HasBufferedData(); + if (CanWriteNewData() || span.empty()) { + consumed_data.fin_consumed = fin; + if (!span.empty()) { + // Buffer all data if buffered data size is below limit. + QuicStreamOffset offset = send_buffer_.stream_offset(); + consumed_data.bytes_consumed = + span.SaveMemSlicesInSendBuffer(&send_buffer_); + if (offset > send_buffer_.stream_offset() || + kMaxStreamLength < send_buffer_.stream_offset()) { + QUIC_BUG << "Write too many data via stream " << id_; + CloseConnectionWithDetails( + QUIC_STREAM_LENGTH_OVERFLOW, + QuicStrCat("Write too many data via stream ", id_)); + return consumed_data; + } + OnDataBuffered(offset, consumed_data.bytes_consumed, nullptr); + } + } + fin_buffered_ = consumed_data.fin_consumed; + + if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) { + // Write data if there is no buffered data before. + WriteBufferedData(); + } + + return consumed_data; +} + +bool QuicStream::HasPendingRetransmission() const { + return send_buffer_.HasPendingRetransmission() || fin_lost_; +} + +bool QuicStream::IsStreamFrameOutstanding(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin) const { + return send_buffer_.IsStreamDataOutstanding(offset, data_length) || + (fin && fin_outstanding_); +} + +QuicConsumedData QuicStream::WritevDataInner(size_t write_length, + QuicStreamOffset offset, + bool fin) { + StreamSendingState state = fin ? FIN : NO_FIN; + if (fin && add_random_padding_after_fin_) { + state = FIN_AND_PADDING; + } + return session()->WritevData(this, id(), write_length, offset, state); +} + +void QuicStream::CloseReadSide() { + if (read_side_closed_) { + return; + } + QUIC_DVLOG(1) << ENDPOINT << "Done reading from stream " << id(); + + read_side_closed_ = true; + sequencer_.ReleaseBuffer(); + + if (write_side_closed_) { + QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << id(); + session_->CloseStream(id()); + } +} + +void QuicStream::CloseWriteSide() { + if (write_side_closed_) { + return; + } + QUIC_DVLOG(1) << ENDPOINT << "Done writing to stream " << id(); + + write_side_closed_ = true; + if (read_side_closed_) { + QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << id(); + session_->CloseStream(id()); + } +} + +bool QuicStream::HasBufferedData() const { + DCHECK_GE(send_buffer_.stream_offset(), stream_bytes_written()); + return send_buffer_.stream_offset() > stream_bytes_written(); +} + +QuicTransportVersion QuicStream::transport_version() const { + return session_->connection()->transport_version(); +} + +HandshakeProtocol QuicStream::handshake_protocol() const { + return session_->connection()->version().handshake_protocol; +} + +void QuicStream::StopReading() { + QUIC_DVLOG(1) << ENDPOINT << "Stop reading from stream " << id(); + sequencer_.StopReading(); +} + +const QuicSocketAddress& QuicStream::PeerAddressOfLatestPacket() const { + return session_->connection()->last_packet_source_address(); +} + +void QuicStream::OnClose() { + CloseReadSide(); + CloseWriteSide(); + + if (!fin_sent_ && !rst_sent_) { + // For flow control accounting, tell the peer how many bytes have been + // written on this stream before termination. Done here if needed, using a + // RST_STREAM frame. + QUIC_DLOG(INFO) << ENDPOINT << "Sending RST_STREAM in OnClose: " << id(); + session_->SendRstStream(id(), QUIC_RST_ACKNOWLEDGEMENT, + stream_bytes_written()); + session_->OnStreamDoneWaitingForAcks(id_); + rst_sent_ = true; + } + + if (flow_controller_.FlowControlViolation() || + connection_flow_controller_->FlowControlViolation()) { + return; + } + // The stream is being closed and will not process any further incoming bytes. + // As there may be more bytes in flight, to ensure that both endpoints have + // the same connection level flow control state, mark all unreceived or + // buffered bytes as consumed. + QuicByteCount bytes_to_consume = + flow_controller_.highest_received_byte_offset() - + flow_controller_.bytes_consumed(); + AddBytesConsumed(bytes_to_consume); +} + +void QuicStream::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { + if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) { + // Let session unblock this stream. + session_->MarkConnectionLevelWriteBlocked(id_); + } +} + +bool QuicStream::MaybeIncreaseHighestReceivedOffset( + QuicStreamOffset new_offset) { + uint64_t increment = + new_offset - flow_controller_.highest_received_byte_offset(); + if (!flow_controller_.UpdateHighestReceivedOffset(new_offset)) { + return false; + } + + // If |new_offset| increased the stream flow controller's highest received + // offset, increase the connection flow controller's value by the incremental + // difference. + if (stream_contributes_to_connection_flow_control_) { + connection_flow_controller_->UpdateHighestReceivedOffset( + connection_flow_controller_->highest_received_byte_offset() + + increment); + } + return true; +} + +void QuicStream::AddBytesSent(QuicByteCount bytes) { + flow_controller_.AddBytesSent(bytes); + if (stream_contributes_to_connection_flow_control_) { + connection_flow_controller_->AddBytesSent(bytes); + } +} + +void QuicStream::AddBytesConsumed(QuicByteCount bytes) { + // Only adjust stream level flow controller if still reading. + if (!read_side_closed_) { + flow_controller_.AddBytesConsumed(bytes); + } + + if (stream_contributes_to_connection_flow_control_) { + connection_flow_controller_->AddBytesConsumed(bytes); + } +} + +void QuicStream::UpdateSendWindowOffset(QuicStreamOffset new_window) { + if (flow_controller_.UpdateSendWindowOffset(new_window)) { + // Let session unblock this stream. + session_->MarkConnectionLevelWriteBlocked(id_); + } +} + +void QuicStream::AddRandomPaddingAfterFin() { + add_random_padding_after_fin_ = true; +} + +bool QuicStream::OnStreamFrameAcked(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_acked, + QuicTime::Delta ack_delay_time, + QuicByteCount* newly_acked_length) { + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " Acking " + << "[" << offset << ", " << offset + data_length << "]" + << " fin = " << fin_acked; + *newly_acked_length = 0; + if (!send_buffer_.OnStreamDataAcked(offset, data_length, + newly_acked_length)) { + CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "Trying to ack unsent data."); + return false; + } + if (!fin_sent_ && fin_acked) { + CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "Trying to ack unsent fin."); + return false; + } + // Indicates whether ack listener's OnPacketAcked should be called. + const bool new_data_acked = + *newly_acked_length > 0 || (fin_acked && fin_outstanding_); + if (fin_acked) { + fin_outstanding_ = false; + fin_lost_ = false; + } + if (!IsWaitingForAcks()) { + session_->OnStreamDoneWaitingForAcks(id_); + } + return new_data_acked; +} + +void QuicStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_retransmitted) { + send_buffer_.OnStreamDataRetransmitted(offset, data_length); + if (fin_retransmitted) { + fin_lost_ = false; + } +} + +void QuicStream::OnStreamFrameLost(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_lost) { + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " Losting " + << "[" << offset << ", " << offset + data_length << "]" + << " fin = " << fin_lost; + if (data_length > 0) { + send_buffer_.OnStreamDataLost(offset, data_length); + } + if (fin_lost && fin_outstanding_) { + fin_lost_ = true; + } +} + +bool QuicStream::RetransmitStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin) { + if (HasDeadlinePassed()) { + OnDeadlinePassed(); + return true; + } + QuicIntervalSet<QuicStreamOffset> retransmission(offset, + offset + data_length); + retransmission.Difference(bytes_acked()); + bool retransmit_fin = fin && fin_outstanding_; + if (retransmission.Empty() && !retransmit_fin) { + return true; + } + QuicConsumedData consumed(0, false); + for (const auto& interval : retransmission) { + QuicStreamOffset retransmission_offset = interval.min(); + QuicByteCount retransmission_length = interval.max() - interval.min(); + const bool can_bundle_fin = + retransmit_fin && (retransmission_offset + retransmission_length == + stream_bytes_written()); + consumed = session()->WritevData(this, id_, retransmission_length, + retransmission_offset, + can_bundle_fin ? FIN : NO_FIN); + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ + << " is forced to retransmit stream data [" + << retransmission_offset << ", " + << retransmission_offset + retransmission_length + << ") and fin: " << can_bundle_fin + << ", consumed: " << consumed; + OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed, + consumed.fin_consumed); + if (can_bundle_fin) { + retransmit_fin = !consumed.fin_consumed; + } + if (consumed.bytes_consumed < retransmission_length || + (can_bundle_fin && !consumed.fin_consumed)) { + // Connection is write blocked. + return false; + } + } + if (retransmit_fin) { + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ + << " retransmits fin only frame."; + consumed = session()->WritevData(this, id_, 0, stream_bytes_written(), FIN); + if (!consumed.fin_consumed) { + return false; + } + } + return true; +} + +bool QuicStream::IsWaitingForAcks() const { + return (!rst_sent_ || stream_error_ == QUIC_STREAM_NO_ERROR) && + (send_buffer_.stream_bytes_outstanding() || fin_outstanding_); +} + +size_t QuicStream::ReadableBytes() const { + return sequencer_.ReadableBytes(); +} + +bool QuicStream::WriteStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) { + DCHECK_LT(0u, data_length); + QUIC_DVLOG(2) << ENDPOINT << "Write stream " << id_ << " data from offset " + << offset << " length " << data_length; + return send_buffer_.WriteStreamData(offset, data_length, writer); +} + +void QuicStream::WriteBufferedData() { + DCHECK(!write_side_closed_ && (HasBufferedData() || fin_buffered_)); + + if (session_->ShouldYield(id())) { + session_->MarkConnectionLevelWriteBlocked(id()); + return; + } + + // Size of buffered data. + size_t write_length = BufferedDataBytes(); + + // A FIN with zero data payload should not be flow control blocked. + bool fin_with_zero_data = (fin_buffered_ && write_length == 0); + + bool fin = fin_buffered_; + + // How much data flow control permits to be written. + QuicByteCount send_window = flow_controller_.SendWindowSize(); + if (stream_contributes_to_connection_flow_control_) { + send_window = + std::min(send_window, connection_flow_controller_->SendWindowSize()); + } + + if (send_window == 0 && !fin_with_zero_data) { + // Quick return if nothing can be sent. + MaybeSendBlocked(); + return; + } + + if (write_length > send_window) { + // Don't send the FIN unless all the data will be sent. + fin = false; + + // Writing more data would be a violation of flow control. + write_length = static_cast<size_t>(send_window); + QUIC_DVLOG(1) << "stream " << id() << " shortens write length to " + << write_length << " due to flow control"; + } + if (session_->session_decides_what_to_write()) { + session_->SetTransmissionType(NOT_RETRANSMISSION); + } + QuicConsumedData consumed_data = + WritevDataInner(write_length, stream_bytes_written(), fin); + + OnStreamDataConsumed(consumed_data.bytes_consumed); + + AddBytesSent(consumed_data.bytes_consumed); + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " sends " + << stream_bytes_written() << " bytes " + << " and has buffered data " << BufferedDataBytes() << " bytes." + << " fin is sent: " << consumed_data.fin_consumed + << " fin is buffered: " << fin_buffered_; + + // The write may have generated a write error causing this stream to be + // closed. If so, simply return without marking the stream write blocked. + if (write_side_closed_) { + return; + } + + if (consumed_data.bytes_consumed == write_length) { + if (!fin_with_zero_data) { + MaybeSendBlocked(); + } + if (fin && consumed_data.fin_consumed) { + fin_sent_ = true; + fin_outstanding_ = true; + if (fin_received_) { + session_->StreamDraining(id_); + } + CloseWriteSide(); + } else if (fin && !consumed_data.fin_consumed) { + session_->MarkConnectionLevelWriteBlocked(id()); + } + } else { + session_->MarkConnectionLevelWriteBlocked(id()); + } + if (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed) { + busy_counter_ = 0; + } +} + +uint64_t QuicStream::BufferedDataBytes() const { + DCHECK_GE(send_buffer_.stream_offset(), stream_bytes_written()); + return send_buffer_.stream_offset() - stream_bytes_written(); +} + +bool QuicStream::CanWriteNewData() const { + return BufferedDataBytes() < buffered_data_threshold_; +} + +bool QuicStream::CanWriteNewDataAfterData(QuicByteCount length) const { + return (BufferedDataBytes() + length) < buffered_data_threshold_; +} + +uint64_t QuicStream::stream_bytes_written() const { + return send_buffer_.stream_bytes_written(); +} + +const QuicIntervalSet<QuicStreamOffset>& QuicStream::bytes_acked() const { + return send_buffer_.bytes_acked(); +} + +void QuicStream::OnStreamDataConsumed(size_t bytes_consumed) { + send_buffer_.OnStreamDataConsumed(bytes_consumed); +} + +void QuicStream::WritePendingRetransmission() { + while (HasPendingRetransmission()) { + QuicConsumedData consumed(0, false); + if (!send_buffer_.HasPendingRetransmission()) { + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ + << " retransmits fin only frame."; + consumed = + session()->WritevData(this, id_, 0, stream_bytes_written(), FIN); + fin_lost_ = !consumed.fin_consumed; + if (fin_lost_) { + // Connection is write blocked. + return; + } + } else { + StreamPendingRetransmission pending = + send_buffer_.NextPendingRetransmission(); + // Determine whether the lost fin can be bundled with the data. + const bool can_bundle_fin = + fin_lost_ && + (pending.offset + pending.length == stream_bytes_written()); + consumed = + session()->WritevData(this, id_, pending.length, pending.offset, + can_bundle_fin ? FIN : NO_FIN); + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ + << " tries to retransmit stream data [" << pending.offset + << ", " << pending.offset + pending.length + << ") and fin: " << can_bundle_fin + << ", consumed: " << consumed; + OnStreamFrameRetransmitted(pending.offset, consumed.bytes_consumed, + consumed.fin_consumed); + if (consumed.bytes_consumed < pending.length || + (can_bundle_fin && !consumed.fin_consumed)) { + // Connection is write blocked. + return; + } + } + } +} + +bool QuicStream::MaybeSetTtl(QuicTime::Delta ttl) { + if (is_static_) { + QUIC_BUG << "Cannot set TTL of a static stream."; + return false; + } + if (deadline_.IsInitialized()) { + QUIC_DLOG(WARNING) << "Deadline has already been set."; + return false; + } + if (!session()->session_decides_what_to_write()) { + QUIC_DLOG(WARNING) << "This session does not support stream TTL yet."; + return false; + } + QuicTime now = session()->connection()->clock()->ApproximateNow(); + deadline_ = now + ttl; + return true; +} + +bool QuicStream::HasDeadlinePassed() const { + if (!deadline_.IsInitialized()) { + // No deadline has been set. + return false; + } + DCHECK(session()->session_decides_what_to_write()); + QuicTime now = session()->connection()->clock()->ApproximateNow(); + if (now < deadline_) { + return false; + } + // TTL expired. + QUIC_DVLOG(1) << "stream " << id() << " deadline has passed"; + return true; +} + +void QuicStream::OnDeadlinePassed() { + Reset(QUIC_STREAM_TTL_EXPIRED); +} + +void QuicStream::SendStopSending(uint16_t code) { + if (transport_version() != QUIC_VERSION_99) { + // If the connection is not version 99, do nothing. + // Do not QUIC_BUG or anything; the application really does not need to know + // what version the connection is in. + return; + } + session_->SendStopSending(code, id_); +} + +void QuicStream::OnStopSending(uint16_t code) {} + +} // namespace quic
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h new file mode 100644 index 0000000..fe0a794 --- /dev/null +++ b/quic/core/quic_stream.h
@@ -0,0 +1,541 @@ +// Copyright (c) 2012 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. + +// The base class for client/server QUIC streams. + +// It does not contain the entire interface needed by an application to interact +// with a QUIC stream. Some parts of the interface must be obtained by +// accessing the owning session object. A subclass of QuicStream +// connects the object and the application that generates and consumes the data +// of the stream. + +// The QuicStream object has a dependent QuicStreamSequencer object, +// which is given the stream frames as they arrive, and provides stream data in +// order by invoking ProcessRawData(). + +#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_H_ +#define QUICHE_QUIC_CORE_QUIC_STREAM_H_ + +#include <cstddef> +#include <cstdint> +#include <list> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" + +namespace quic { + +namespace test { +class QuicStreamPeer; +} // namespace test + +class QuicSession; +class QuicStream; + +// Buffers frames for a stream until the first byte of that frame arrives. +class QUIC_EXPORT_PRIVATE PendingStream + : public QuicStreamSequencer::StreamInterface { + public: + PendingStream(QuicStreamId id, QuicSession* session); + PendingStream(const PendingStream&) = delete; + PendingStream(PendingStream&&) = default; + ~PendingStream() override = default; + + // QuicStreamSequencer::StreamInterface + void OnDataAvailable() override; + void OnFinRead() override; + void AddBytesConsumed(QuicByteCount bytes) override; + void Reset(QuicRstStreamErrorCode error) override; + void CloseConnectionWithDetails(QuicErrorCode error, + const QuicString& details) override; + QuicStreamId id() const override; + const QuicSocketAddress& PeerAddressOfLatestPacket() const override; + + // Buffers the contents of |frame|. Frame must have a non-zero offset. + // If the data violates flow control, the connection will be closed. + void OnStreamFrame(const QuicStreamFrame& frame); + + // Stores the final byte offset from |frame|. + // If the final offset violates flow control, the connection will be closed. + void OnRstStreamFrame(const QuicRstStreamFrame& frame); + + // Returns the number of bytes read on this stream. + uint64_t stream_bytes_read() { return stream_bytes_read_; } + + private: + friend class QuicStream; + + bool MaybeIncreaseHighestReceivedOffset(QuicStreamOffset new_offset); + + // ID of this stream. + QuicStreamId id_; + + // Session which owns this. + QuicSession* session_; + + // Bytes read refers to payload bytes only: they do not include framing, + // encryption overhead etc. + uint64_t stream_bytes_read_; + + // True if a frame containing a fin has been received. + bool fin_received_; + + // Connection-level flow controller. Owned by the session. + QuicFlowController* connection_flow_controller_; + // Stream-level flow controller. + QuicFlowController flow_controller_; + // Stores the buffered frames. + QuicStreamSequencer sequencer_; +}; + +class QUIC_EXPORT_PRIVATE QuicStream + : public QuicStreamSequencer::StreamInterface { + public: + // This is somewhat arbitrary. It's possible, but unlikely, we will either + // fail to set a priority client-side, or cancel a stream before stripping the + // priority from the wire server-side. In either case, start out with a + // priority in the middle. + static const spdy::SpdyPriority kDefaultPriority = 3; + static_assert(kDefaultPriority == + (spdy::kV3LowestPriority + spdy::kV3HighestPriority) / 2, + "Unexpected value of kDefaultPriority"); + + // Creates a new stream with stream_id |id| associated with |session|. If + // |is_static| is true, then the stream will be given precedence + // over other streams when determing what streams should write next. + // |type| indicates whether the stream is bidirectional, read unidirectional + // or write unidirectional. + // TODO(fayang): Remove |type| when IETF stream ID numbering fully kicks in. + QuicStream(QuicStreamId id, + QuicSession* session, + bool is_static, + StreamType type); + QuicStream(PendingStream pending, StreamType type); + QuicStream(const QuicStream&) = delete; + QuicStream& operator=(const QuicStream&) = delete; + + virtual ~QuicStream(); + + // Not in use currently. + void SetFromConfig(); + + // QuicStreamSequencer::StreamInterface implementation. + QuicStreamId id() const override { return id_; } + // Called by the stream subclass after it has consumed the final incoming + // data. + void OnFinRead() override; + + // Called by the subclass or the sequencer to reset the stream from this + // end. + void Reset(QuicRstStreamErrorCode error) override; + + // Called by the subclass or the sequencer to close the entire connection from + // this end. + void CloseConnectionWithDetails(QuicErrorCode error, + const QuicString& details) override; + + // Called by the stream sequencer as bytes are consumed from the buffer. + // If the receive window has dropped below the threshold, then send a + // WINDOW_UPDATE frame. + void AddBytesConsumed(QuicByteCount bytes) override; + + // Get peer IP of the lastest packet which connection is dealing/delt with. + const QuicSocketAddress& PeerAddressOfLatestPacket() const override; + + // Called by the session when a (potentially duplicate) stream frame has been + // received for this stream. + virtual void OnStreamFrame(const QuicStreamFrame& frame); + + // Called by the session when the connection becomes writeable to allow the + // stream to write any pending data. + virtual void OnCanWrite(); + + // Called by the session just before the object is destroyed. + // The object should not be accessed after OnClose is called. + // Sends a RST_STREAM with code QUIC_RST_ACKNOWLEDGEMENT if neither a FIN nor + // a RST_STREAM has been sent. + virtual void OnClose(); + + // Called by the session when the endpoint receives a RST_STREAM from the + // peer. + virtual void OnStreamReset(const QuicRstStreamFrame& frame); + + // Called by the session when the endpoint receives or sends a connection + // close, and should immediately close the stream. + virtual void OnConnectionClosed(QuicErrorCode error, + ConnectionCloseSource source); + + spdy::SpdyPriority priority() const; + + // Sets priority_ to priority. This should only be called before bytes are + // written to the server. + void SetPriority(spdy::SpdyPriority priority); + + // Returns true if this stream is still waiting for acks of sent data. + // This will return false if all data has been acked, or if the stream + // is no longer interested in data being acked (which happens when + // a stream is reset because of an error). + bool IsWaitingForAcks() const; + + // Number of bytes available to read. + size_t ReadableBytes() const; + + QuicRstStreamErrorCode stream_error() const { return stream_error_; } + QuicErrorCode connection_error() const { return connection_error_; } + + bool reading_stopped() const { + return sequencer_.ignore_read_data() || read_side_closed_; + } + bool write_side_closed() const { return write_side_closed_; } + + bool rst_received() const { return rst_received_; } + bool rst_sent() const { return rst_sent_; } + bool fin_received() const { return fin_received_; } + bool fin_sent() const { return fin_sent_; } + bool fin_outstanding() const { return fin_outstanding_; } + bool fin_lost() const { return fin_lost_; } + + uint64_t BufferedDataBytes() const; + + uint64_t stream_bytes_read() const { return stream_bytes_read_; } + uint64_t stream_bytes_written() const; + + size_t busy_counter() const { return busy_counter_; } + void set_busy_counter(size_t busy_counter) { busy_counter_ = busy_counter; } + + void set_fin_sent(bool fin_sent) { fin_sent_ = fin_sent; } + void set_fin_received(bool fin_received) { fin_received_ = fin_received; } + void set_rst_sent(bool rst_sent) { rst_sent_ = rst_sent; } + + void set_rst_received(bool rst_received) { rst_received_ = rst_received; } + void set_stream_error(QuicRstStreamErrorCode error) { stream_error_ = error; } + + // Adjust the flow control window according to new offset in |frame|. + virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame); + + int num_frames_received() const; + int num_duplicate_frames_received() const; + + QuicFlowController* flow_controller() { return &flow_controller_; } + + // Called when endpoint receives a frame which could increase the highest + // offset. + // Returns true if the highest offset did increase. + bool MaybeIncreaseHighestReceivedOffset(QuicStreamOffset new_offset); + // Called when bytes are sent to the peer. + void AddBytesSent(QuicByteCount bytes); + + // Updates the flow controller's send window offset and calls OnCanWrite if + // it was blocked before. + void UpdateSendWindowOffset(QuicStreamOffset new_offset); + + // Returns true if the stream has received either a RST_STREAM or a FIN - + // either of which gives a definitive number of bytes which the peer has + // sent. If this is not true on deletion of the stream object, the session + // must keep track of the stream's byte offset until a definitive final value + // arrives. + bool HasFinalReceivedByteOffset() const { + return fin_received_ || rst_received_; + } + + // Returns true if the stream has queued data waiting to write. + bool HasBufferedData() const; + + // Returns the version of QUIC being used for this stream. + QuicTransportVersion transport_version() const; + + // Returns the crypto handshake protocol that was used on this stream's + // connection. + HandshakeProtocol handshake_protocol() const; + + // Sets the sequencer to consume all incoming data itself and not call + // OnDataAvailable(). + // When the FIN is received, the stream will be notified automatically (via + // OnFinRead()) (which may happen during the call of StopReading()). + // TODO(dworley): There should be machinery to send a RST_STREAM/NO_ERROR and + // stop sending stream-level flow-control updates when this end sends FIN. + virtual void StopReading(); + + // Sends as much of 'data' to the connection as the connection will consume, + // and then buffers any remaining data in queued_data_. + // If fin is true: if it is immediately passed on to the session, + // write_side_closed() becomes true, otherwise fin_buffered_ becomes true. + void WriteOrBufferData( + QuicStringPiece data, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + + // Adds random padding after the fin is consumed for this stream. + void AddRandomPaddingAfterFin(); + + // Write |data_length| of data starts at |offset| from send buffer. + bool WriteStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer); + + // Called when data [offset, offset + data_length) is acked. |fin_acked| + // indicates whether the fin is acked. Returns true and updates + // |newly_acked_length| if any new stream data (including fin) gets acked. + virtual bool OnStreamFrameAcked(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_acked, + QuicTime::Delta ack_delay_time, + QuicByteCount* newly_acked_length); + + // Called when data [offset, offset + data_length) was retransmitted. + // |fin_retransmitted| indicates whether fin was retransmitted. + virtual void OnStreamFrameRetransmitted(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_retransmitted); + + // Called when data [offset, offset + data_length) is considered as lost. + // |fin_lost| indicates whether the fin is considered as lost. + virtual void OnStreamFrameLost(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin_lost); + + // Called to retransmit outstanding portion in data [offset, offset + + // data_length) and |fin|. Returns true if all data gets retransmitted. + virtual bool RetransmitStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin); + + // Sets deadline of this stream to be now + |ttl|, returns true if the setting + // succeeds. + bool MaybeSetTtl(QuicTime::Delta ttl); + + // Same as WritevData except data is provided in reference counted memory so + // that data copy is avoided. + QuicConsumedData WriteMemSlices(QuicMemSliceSpan span, bool fin); + + // Returns true if any stream data is lost (including fin) and needs to be + // retransmitted. + virtual bool HasPendingRetransmission() const; + + // Returns true if any portion of data [offset, offset + data_length) is + // outstanding or fin is outstanding (if |fin| is true). Returns false + // otherwise. + bool IsStreamFrameOutstanding(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin) const; + + StreamType type() const { return type_; } + + // Creates and sends a STOP_SENDING frame. This can be called regardless of + // the version that has been negotiated. If not IETF QUIC/Version 99 then the + // method is a noop, relieving the application of the necessity of + // understanding the connection's QUIC version and knowing whether it can call + // this method or not. + void SendStopSending(uint16_t code); + + // Invoked when QUIC receives a STOP_SENDING frame for this stream, informing + // the application that the peer has sent a STOP_SENDING. The default + // implementation is a noop. Is to be overridden by the application-specific + // QuicStream class. + virtual void OnStopSending(uint16_t code); + + // Close the write side of the socket. Further writes will fail. + // Can be called by the subclass or internally. + // Does not send a FIN. May cause the stream to be closed. + virtual void CloseWriteSide(); + + protected: + // Sends as many bytes in the first |count| buffers of |iov| to the connection + // as the connection will consume. If FIN is consumed, the write side is + // immediately closed. + // Returns the number of bytes consumed by the connection. + // Please note: Returned consumed data is the amount of data saved in send + // buffer. The data is not necessarily consumed by the connection. So write + // side is closed when FIN is sent. + // TODO(fayang): Let WritevData return boolean. + QuicConsumedData WritevData(const struct iovec* iov, int iov_count, bool fin); + + // Allows override of the session level writev, for the force HOL + // blocking experiment. + virtual QuicConsumedData WritevDataInner(size_t write_length, + QuicStreamOffset offset, + bool fin); + + // Close the read side of the socket. May cause the stream to be closed. + // Subclasses and consumers should use StopReading to terminate reading early + // if expecting a FIN. Can be used directly by subclasses if not expecting a + // FIN. + void CloseReadSide(); + + // Called when data of [offset, offset + data_length] is buffered in send + // buffer. + virtual void OnDataBuffered( + QuicStreamOffset offset, + QuicByteCount data_length, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& + ack_listener) {} + + // True if buffered data in send buffer is below buffered_data_threshold_. + bool CanWriteNewData() const; + + // True if buffered data in send buffer is still below + // buffered_data_threshold_ even after writing |length| bytes. + bool CanWriteNewDataAfterData(QuicByteCount length) const; + + // Called when upper layer can write new data. + virtual void OnCanWriteNewData() {} + + // Called when |bytes_consumed| bytes has been consumed. + virtual void OnStreamDataConsumed(size_t bytes_consumed); + + // Writes pending retransmissions if any. + virtual void WritePendingRetransmission(); + + // This is called when stream tries to retransmit data after deadline_. Make + // this virtual so that subclasses can implement their own logics. + virtual void OnDeadlinePassed(); + + bool fin_buffered() const { return fin_buffered_; } + + const QuicSession* session() const { return session_; } + QuicSession* session() { return session_; } + + const QuicStreamSequencer* sequencer() const { return &sequencer_; } + QuicStreamSequencer* sequencer() { return &sequencer_; } + + void DisableConnectionFlowControlForThisStream() { + stream_contributes_to_connection_flow_control_ = false; + } + + const QuicIntervalSet<QuicStreamOffset>& bytes_acked() const; + + const QuicStreamSendBuffer& send_buffer() const { return send_buffer_; } + + QuicStreamSendBuffer& send_buffer() { return send_buffer_; } + + private: + friend class test::QuicStreamPeer; + friend class QuicStreamUtils; + + QuicStream(QuicStreamId id, + QuicSession* session, + QuicStreamSequencer sequencer, + bool is_static, + StreamType type, + uint64_t stream_bytes_read, + bool fin_received, + QuicFlowController flow_controller, + QuicFlowController* connection_flow_controller); + + // Subclasses and consumers should use reading_stopped. + bool read_side_closed() const { return read_side_closed_; } + + // Calls MaybeSendBlocked on the stream's flow controller and the connection + // level flow controller. If the stream is flow control blocked by the + // connection-level flow controller but not by the stream-level flow + // controller, marks this stream as connection-level write blocked. + void MaybeSendBlocked(); + + // Write buffered data in send buffer. TODO(fayang): Consider combine + // WriteOrBufferData, Writev and WriteBufferedData. + void WriteBufferedData(); + + // Returns true if deadline_ has passed. + bool HasDeadlinePassed() const; + + QuicStreamSequencer sequencer_; + QuicStreamId id_; + // Pointer to the owning QuicSession object. + QuicSession* session_; + // The priority of the stream, once parsed. + spdy::SpdyPriority priority_; + // Bytes read refers to payload bytes only: they do not include framing, + // encryption overhead etc. + uint64_t stream_bytes_read_; + + // Stream error code received from a RstStreamFrame or error code sent by the + // visitor or sequencer in the RstStreamFrame. + QuicRstStreamErrorCode stream_error_; + // Connection error code due to which the stream was closed. |stream_error_| + // is set to |QUIC_STREAM_CONNECTION_ERROR| when this happens and consumers + // should check |connection_error_|. + QuicErrorCode connection_error_; + + // True if the read side is closed and further frames should be rejected. + bool read_side_closed_; + // True if the write side is closed, and further writes should fail. + bool write_side_closed_; + + // True if the subclass has written a FIN with WriteOrBufferData, but it was + // buffered in queued_data_ rather than being sent to the session. + bool fin_buffered_; + // True if a FIN has been sent to the session. + bool fin_sent_; + // True if a FIN is waiting to be acked. + bool fin_outstanding_; + // True if a FIN is lost. + bool fin_lost_; + + // True if this stream has received (and the sequencer has accepted) a + // StreamFrame with the FIN set. + bool fin_received_; + + // True if an RST_STREAM has been sent to the session. + // In combination with fin_sent_, used to ensure that a FIN and/or a + // RST_STREAM is always sent to terminate the stream. + bool rst_sent_; + + // True if this stream has received a RST_STREAM frame. + bool rst_received_; + + // Tracks if the session this stream is running under was created by a + // server or a client. + Perspective perspective_; + + QuicFlowController flow_controller_; + + // The connection level flow controller. Not owned. + QuicFlowController* connection_flow_controller_; + + // Special streams, such as the crypto and headers streams, do not respect + // connection level flow control limits (but are stream level flow control + // limited). + bool stream_contributes_to_connection_flow_control_; + + // A counter incremented when OnCanWrite() is called and no progress is made. + // For debugging only. + size_t busy_counter_; + + // Indicates whether paddings will be added after the fin is consumed for this + // stream. + bool add_random_padding_after_fin_; + + // Send buffer of this stream. Send buffer is cleaned up when data gets acked + // or discarded. + QuicStreamSendBuffer send_buffer_; + + // Latched value of FLAGS_quic_buffered_data_threshold. + const QuicByteCount buffered_data_threshold_; + + // If true, then this stream has precedence over other streams for write + // scheduling. + const bool is_static_; + + // If initialized, reset this stream at this deadline. + QuicTime deadline_; + + // Indicates whether this stream is bidirectional, read unidirectional or + // write unidirectional. + const StreamType type_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_STREAM_H_
diff --git a/quic/core/quic_stream_frame_data_producer.h b/quic/core/quic_stream_frame_data_producer.h new file mode 100644 index 0000000..ab67d54 --- /dev/null +++ b/quic/core/quic_stream_frame_data_producer.h
@@ -0,0 +1,39 @@ +// Copyright (c) 2017 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_QUIC_STREAM_FRAME_DATA_PRODUCER_H_ +#define QUICHE_QUIC_CORE_QUIC_STREAM_FRAME_DATA_PRODUCER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_types.h" + +namespace quic { + +class QuicDataWriter; + +// Pure virtual class to retrieve stream data. +class QUIC_EXPORT_PRIVATE QuicStreamFrameDataProducer { + public: + virtual ~QuicStreamFrameDataProducer() {} + + // Let |writer| write |data_length| data with |offset| of stream |id|. The + // write fails when either stream is closed or corresponding data is failed to + // be retrieved. This method allows writing a single stream frame from data + // that spans multiple buffers. + virtual WriteStreamDataResult WriteStreamData(QuicStreamId id, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) = 0; + + // Writes the data for a CRYPTO frame to |writer| for a frame at encryption + // level |level| starting at offset |offset| for |data_length| bytes. Returns + // whether writing the data was successful. + virtual bool WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_STREAM_FRAME_DATA_PRODUCER_H_
diff --git a/quic/core/quic_stream_id_manager.cc b/quic/core/quic_stream_id_manager.cc new file mode 100644 index 0000000..ce08e59 --- /dev/null +++ b/quic/core/quic_stream_id_manager.cc
@@ -0,0 +1,350 @@ +// Copyright (c) 2018 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/quic_stream_id_manager.h" + +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +#define ENDPOINT \ + (session_->perspective() == Perspective::IS_SERVER ? " Server: " \ + : " Client: ") + +QuicStreamIdManager::QuicStreamIdManager( + QuicSession* session, + QuicStreamId next_outgoing_stream_id, + QuicStreamId largest_peer_created_stream_id, + QuicStreamId first_incoming_dynamic_stream_id, + size_t max_allowed_outgoing_streams, + size_t max_allowed_incoming_streams) + : session_(session), + next_outgoing_stream_id_(next_outgoing_stream_id), + largest_peer_created_stream_id_(largest_peer_created_stream_id), + max_allowed_outgoing_stream_id_(0), + actual_max_allowed_incoming_stream_id_(0), + advertised_max_allowed_incoming_stream_id_(0), + max_stream_id_window_(max_allowed_incoming_streams / + kMaxStreamIdWindowDivisor), + max_allowed_incoming_streams_(max_allowed_incoming_streams), + first_incoming_dynamic_stream_id_(first_incoming_dynamic_stream_id), + first_outgoing_dynamic_stream_id_(next_outgoing_stream_id) { + available_incoming_streams_ = max_allowed_incoming_streams_; + SetMaxOpenOutgoingStreams(max_allowed_outgoing_streams); + SetMaxOpenIncomingStreams(max_allowed_incoming_streams); +} + +QuicStreamIdManager::~QuicStreamIdManager() { + QUIC_LOG_IF(WARNING, + session_->num_locally_closed_incoming_streams_highest_offset() > + max_allowed_incoming_streams_) + << "Surprisingly high number of locally closed peer initiated streams" + "still waiting for final byte offset: " + << session_->num_locally_closed_incoming_streams_highest_offset(); + QUIC_LOG_IF(WARNING, + session_->GetNumLocallyClosedOutgoingStreamsHighestOffset() > + max_allowed_outgoing_streams_) + << "Surprisingly high number of locally closed self initiated streams" + "still waiting for final byte offset: " + << session_->GetNumLocallyClosedOutgoingStreamsHighestOffset(); +} + +bool QuicStreamIdManager::OnMaxStreamIdFrame( + const QuicMaxStreamIdFrame& frame) { + DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(frame.max_stream_id), + QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_)); + // Need to determine whether the stream id matches our client/server + // perspective or not. If not, it's an error. If so, update appropriate + // maxima. + QUIC_CODE_COUNT_N(max_stream_id_received, 2, 2); + // TODO(fkastenholz): this test needs to be broader to handle uni- and bi- + // directional stream ids when that functionality is supported. + if (IsIncomingStream(frame.max_stream_id)) { + // TODO(fkastenholz): This, and following, closeConnection may + // need modification when proper support for IETF CONNECTION + // CLOSE is done. + QUIC_CODE_COUNT(max_stream_id_bad_direction); + session_->connection()->CloseConnection( + QUIC_MAX_STREAM_ID_ERROR, + "Recevied max stream ID with wrong initiator bit setting", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + // If a MAX_STREAM_ID advertises a stream ID that is smaller than previously + // advertised, it is to be ignored. + if (frame.max_stream_id < max_allowed_outgoing_stream_id_) { + QUIC_CODE_COUNT(max_stream_id_ignored); + return true; + } + max_allowed_outgoing_stream_id_ = frame.max_stream_id; + + // Outgoing stream limit has increased, tell the applications + session_->OnCanCreateNewOutgoingStream(); + + return true; +} + +bool QuicStreamIdManager::OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& frame) { + DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(frame.stream_id), + QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_)); + QUIC_CODE_COUNT_N(stream_id_blocked_received, 2, 2); + QuicStreamId id = frame.stream_id; + if (!IsIncomingStream(frame.stream_id)) { + // Client/server mismatch, close the connection + // TODO(fkastenholz): revise when proper IETF Connection Close support is + // done. + QUIC_CODE_COUNT(stream_id_blocked_bad_direction); + session_->connection()->CloseConnection( + QUIC_STREAM_ID_BLOCKED_ERROR, + "Invalid stream ID directionality specified", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + if (id > advertised_max_allowed_incoming_stream_id_) { + // Peer thinks it can send more streams that we've told it. + // This is a protocol error. + // TODO(fkastenholz): revise when proper IETF Connection Close support is + // done. + QUIC_CODE_COUNT(stream_id_blocked_id_too_big); + session_->connection()->CloseConnection( + QUIC_STREAM_ID_BLOCKED_ERROR, "Invalid stream ID specified", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + if (id < actual_max_allowed_incoming_stream_id_) { + // Peer thinks it's blocked on an ID that is less than our current + // max. Inform the peer of the correct stream ID. + SendMaxStreamIdFrame(); + return true; + } + // The peer's notion of the maximum ID is correct, + // there is nothing to do. + QUIC_CODE_COUNT(stream_id_blocked_id_correct); + return true; +} + +// TODO(fkastenholz): Many changes will be needed here: +// -- Use IETF QUIC server/client-initiation sense +// -- Support both BIDI and UNI streams. +// -- can not change the max number of streams after config negotiation has +// been done. +void QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_streams) { + max_allowed_outgoing_streams_ = max_streams; + max_allowed_outgoing_stream_id_ = + next_outgoing_stream_id_ + (max_streams - 1) * kV99StreamIdIncrement; +} + +// TODO(fkastenholz): Many changes will be needed here: +// -- can not change the max number of streams after config negotiation has +// been done. +// -- Currently uses the Google Client/server-initiation sense, needs to +// be IETF. +// -- Support both BIDI and UNI streams. +// -- Convert calculation of the maximum ID from Google-QUIC semantics to IETF +// QUIC semantics. +void QuicStreamIdManager::SetMaxOpenIncomingStreams(size_t max_streams) { + max_allowed_incoming_streams_ = max_streams; + // The peer should always believe that it has the negotiated + // number of stream ids available for use. + available_incoming_streams_ = max_allowed_incoming_streams_; + + // the window is a fraction of the peer's notion of its stream-id space. + max_stream_id_window_ = + available_incoming_streams_ / kMaxStreamIdWindowDivisor; + if (max_stream_id_window_ == 0) { + max_stream_id_window_ = 1; + } + + actual_max_allowed_incoming_stream_id_ = + first_incoming_dynamic_stream_id_ + + (max_allowed_incoming_streams_ - 1) * kV99StreamIdIncrement; + // To start, we can assume advertised and actual are the same. + advertised_max_allowed_incoming_stream_id_ = + actual_max_allowed_incoming_stream_id_; +} + +void QuicStreamIdManager::MaybeSendMaxStreamIdFrame() { + if (available_incoming_streams_ > max_stream_id_window_) { + // window too large, no advertisement + return; + } + // Calculate the number of streams that the peer will believe + // it has. The "/kV99StreamIdIncrement" converts from stream-id- + // values to number-of-stream-ids. + available_incoming_streams_ += (actual_max_allowed_incoming_stream_id_ - + advertised_max_allowed_incoming_stream_id_) / + kV99StreamIdIncrement; + SendMaxStreamIdFrame(); +} + +void QuicStreamIdManager::SendMaxStreamIdFrame() { + advertised_max_allowed_incoming_stream_id_ = + actual_max_allowed_incoming_stream_id_; + // And Advertise it. + session_->SendMaxStreamId(advertised_max_allowed_incoming_stream_id_); +} + +void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) { + DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id), + QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_)); + if (!IsIncomingStream(stream_id)) { + // Nothing to do for outbound streams with respect to the + // stream ID space management. + return; + } + // If the stream is inbound, we can increase the stream ID limit and maybe + // advertise the new limit to the peer. + if (actual_max_allowed_incoming_stream_id_ >= + (kMaxQuicStreamId - kV99StreamIdIncrement)) { + // Reached the maximum stream id value that the implementation + // supports. Nothing can be done here. + return; + } + actual_max_allowed_incoming_stream_id_ += kV99StreamIdIncrement; + MaybeSendMaxStreamIdFrame(); +} + +QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() { + QUIC_BUG_IF(next_outgoing_stream_id_ > max_allowed_outgoing_stream_id_) + << "Attempt allocate a new outgoing stream ID would exceed the limit"; + QuicStreamId id = next_outgoing_stream_id_; + next_outgoing_stream_id_ += kV99StreamIdIncrement; + return id; +} + +bool QuicStreamIdManager::CanOpenNextOutgoingStream() { + DCHECK_EQ(QUIC_VERSION_99, session_->connection()->transport_version()); + if (next_outgoing_stream_id_ > max_allowed_outgoing_stream_id_) { + // Next stream ID would exceed the limit, need to inform the peer. + session_->SendStreamIdBlocked(max_allowed_outgoing_stream_id_); + QUIC_CODE_COUNT(reached_outgoing_stream_id_limit); + return false; + } + return true; +} + +void QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id) { + DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id), + QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_)); + QuicStreamId first_dynamic_stream_id = stream_id + kV99StreamIdIncrement; + + if (IsIncomingStream(first_dynamic_stream_id)) { + // This code is predicated on static stream ids being allocated densely, in + // order, and starting with the first stream allowed. QUIC_BUG if this is + // not so. + QUIC_BUG_IF(stream_id > first_incoming_dynamic_stream_id_) + << "Error in incoming static stream allocation, expected to allocate " + << first_incoming_dynamic_stream_id_ << " got " << stream_id; + + // This is a stream id for a stream that is started by the peer, deal with + // the incoming stream ids. Increase the floor and adjust everything + // accordingly. + if (stream_id == first_incoming_dynamic_stream_id_) { + actual_max_allowed_incoming_stream_id_ += kV99StreamIdIncrement; + first_incoming_dynamic_stream_id_ = first_dynamic_stream_id; + } + return; + } + + // This code is predicated on static stream ids being allocated densely, in + // order, and starting with the first stream allowed. QUIC_BUG if this is + // not so. + QUIC_BUG_IF(stream_id > first_outgoing_dynamic_stream_id_) + << "Error in outgoing static stream allocation, expected to allocate " + << first_outgoing_dynamic_stream_id_ << " got " << stream_id; + // This is a stream id for a stream that is started by this node; deal with + // the outgoing stream ids. Increase the floor and adjust everything + // accordingly. + if (stream_id == first_outgoing_dynamic_stream_id_) { + max_allowed_outgoing_stream_id_ += kV99StreamIdIncrement; + first_outgoing_dynamic_stream_id_ = first_dynamic_stream_id; + } +} + +bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( + const QuicStreamId stream_id) { + DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id), + QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_)); + available_streams_.erase(stream_id); + + if (largest_peer_created_stream_id_ != + QuicUtils::GetInvalidStreamId( + session_->connection()->transport_version()) && + stream_id <= largest_peer_created_stream_id_) { + return true; + } + + if (stream_id > actual_max_allowed_incoming_stream_id_) { + // Desired stream ID is larger than the limit, do not increase. + QUIC_DLOG(INFO) << ENDPOINT + << "Failed to create a new incoming stream with id:" + << stream_id << ". Maximum allowed stream id is " + << actual_max_allowed_incoming_stream_id_ << "."; + session_->connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, + QuicStrCat("Stream id ", stream_id, " above ", + actual_max_allowed_incoming_stream_id_), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + available_incoming_streams_--; + + QuicStreamId id = largest_peer_created_stream_id_ + kV99StreamIdIncrement; + if (largest_peer_created_stream_id_ == + QuicUtils::GetInvalidStreamId( + session_->connection()->transport_version())) { + // Adjust id based on perspective and whether stream_id is bidirectional or + // unidirectional. + if (QuicUtils::IsBidirectionalStreamId(stream_id)) { + // This should only happen on client side because server bidirectional + // stream ID manager's largest_peer_created_stream_id_ is initialized to + // the crypto stream ID. + DCHECK_EQ(Perspective::IS_CLIENT, session_->perspective()); + id = 1; + } else { + id = session_->perspective() == Perspective::IS_SERVER ? 2 : 3; + } + } + for (; id < stream_id; id += kV99StreamIdIncrement) { + available_streams_.insert(id); + } + largest_peer_created_stream_id_ = stream_id; + return true; +} + +bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { + DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(id), + QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_)); + if (!IsIncomingStream(id)) { + // Stream IDs under next_ougoing_stream_id_ are either open or previously + // open but now closed. + return id >= next_outgoing_stream_id_; + } + // For peer created streams, we also need to consider available streams. + return largest_peer_created_stream_id_ == + QuicUtils::GetInvalidStreamId( + session_->connection()->transport_version()) || + id > largest_peer_created_stream_id_ || + QuicContainsKey(available_streams_, id); +} + +bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const { + DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(id), + QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_)); + return id % kV99StreamIdIncrement != + next_outgoing_stream_id_ % kV99StreamIdIncrement; +} + +} // namespace quic
diff --git a/quic/core/quic_stream_id_manager.h b/quic/core/quic_stream_id_manager.h new file mode 100644 index 0000000..95951c0 --- /dev/null +++ b/quic/core/quic_stream_id_manager.h
@@ -0,0 +1,239 @@ +// Copyright (c) 2018 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_QUIC_STREAM_ID_MANAGER_H_ +#define QUICHE_QUIC_CORE_QUIC_STREAM_ID_MANAGER_H_ + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" + +namespace quic { + +namespace test { +class QuicSessionPeer; +class QuicStreamIdManagerPeer; +} // namespace test + +class QuicSession; + +// Amount to increment a stream ID value to get the next stream ID in +// the stream ID space. +const QuicStreamId kV99StreamIdIncrement = 4; + +// This constant controls the size of the window when deciding whether +// to generate a MAX STREAM ID frame or not. See the discussion of the +// window, below, for more details. +const int kMaxStreamIdWindowDivisor = 2; + +// This class manages the stream ids for Version 99/IETF QUIC. +// TODO(fkastenholz): Expand to support bi- and uni-directional stream ids +// TODO(fkastenholz): Roll in pre-version-99 management +class QUIC_EXPORT_PRIVATE QuicStreamIdManager { + public: + QuicStreamIdManager(QuicSession* session, + QuicStreamId next_outgoing_stream_id, + QuicStreamId largest_peer_created_stream_id, + QuicStreamId first_incoming_dynamic_stream_id, + size_t max_allowed_outgoing_streams, + size_t max_allowed_incoming_streams); + + ~QuicStreamIdManager(); + + // Generate a string suitable for sending to the log/etc to show current state + // of the stream ID manager. + QuicString DebugString() const { + return QuicStrCat( + " { max_allowed_outgoing_stream_id: ", max_allowed_outgoing_stream_id_, + ", actual_max_allowed_incoming_stream_id_: ", + actual_max_allowed_incoming_stream_id_, + ", advertised_max_allowed_incoming_stream_id_: ", + advertised_max_allowed_incoming_stream_id_, + ", max_stream_id_window_: ", max_stream_id_window_, + ", max_allowed_outgoing_streams_: ", max_allowed_outgoing_streams_, + ", max_allowed_incoming_streams_: ", max_allowed_incoming_streams_, + ", available_incoming_streams_: ", available_incoming_streams_, + ", first_incoming_dynamic_stream_id_: ", + first_incoming_dynamic_stream_id_, + ", first_outgoing_dynamic_stream_id_: ", + first_outgoing_dynamic_stream_id_, " }"); + } + + // Processes the MAX STREAM ID frame, invoked from + // QuicSession::OnMaxStreamIdFrame. It has the same semantics as the + // QuicFramerVisitorInterface, returning true if the framer should continue + // processing the packet, false if not. + bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame); + + // Processes the STREAM ID BLOCKED frame, invoked from + // QuicSession::OnStreamIdBlockedFrame. It has the same semantics as the + // QuicFramerVisitorInterface, returning true if the framer should continue + // processing the packet, false if not. + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame); + + // Indicates whether the next outgoing stream ID can be allocated or not. The + // test is whether it will exceed the maximum-stream-id or not. + bool CanOpenNextOutgoingStream(); + + // Generate and send a MAX_STREAM_ID frame. + void SendMaxStreamIdFrame(); + + // Invoked to deal with releasing a stream ID. + void OnStreamClosed(QuicStreamId stream_id); + + // Returns the next outgoing stream id. If it fails (due to running into the + // max_allowed_outgoing_stream_id limit) then it returns an invalid stream id. + QuicStreamId GetNextOutgoingStreamId(); + + // Initialize the maximum allowed incoming stream id and number of streams. + void SetMaxOpenIncomingStreams(size_t max_streams); + + // Initialize the maximum allowed outgoing stream id, number of streams, and + // MAX_STREAM_ID advertisement window. + void SetMaxOpenOutgoingStreams(size_t max_streams); + + // Register a new stream as a static stream. This is used so that the + // advertised maximum stream ID can be calculated based on the start of the + // dynamic stream space. This method will take any stream ID, one that either + // this node or the peer will initiate. + void RegisterStaticStream(QuicStreamId stream_id); + + // Check that an incoming stream id is valid -- is below the maximum allowed + // stream ID. Note that this method uses the actual maximum, not the most + // recently advertised maximum this helps preserve the Google-QUIC semantic + // that we actually care about the number of open streams, not the maximum + // stream ID. Returns true if the stream ID is valid. If the stream ID fails + // the test, will close the connection (per the protocol specification) and + // return false. This method also maintains state with regard to the number of + // streams that the peer can open (used for generating MAX_STREAM_ID frames). + // This method should be called exactly once for each incoming stream + // creation. + bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id); + + // Returns true if |id| is still available. + bool IsAvailableStream(QuicStreamId id) const; + + // Return true if given stream is peer initiated. + bool IsIncomingStream(QuicStreamId id) const; + + size_t max_allowed_outgoing_streams() const { + return max_allowed_outgoing_streams_; + } + size_t max_allowed_incoming_streams() const { + return max_allowed_incoming_streams_; + } + QuicStreamId max_allowed_outgoing_stream_id() const { + return max_allowed_outgoing_stream_id_; + } + QuicStreamId advertised_max_allowed_incoming_stream_id() const { + return advertised_max_allowed_incoming_stream_id_; + } + QuicStreamId actual_max_allowed_incoming_stream_id() const { + return actual_max_allowed_incoming_stream_id_; + } + QuicStreamId max_stream_id_window() const { return max_stream_id_window_; } + + QuicStreamId next_outgoing_stream_id() const { + return next_outgoing_stream_id_; + } + + QuicStreamId first_incoming_dynamic_stream_id() { + return first_incoming_dynamic_stream_id_; + } + QuicStreamId first_outgoing_dynamic_stream_id() { + return first_outgoing_dynamic_stream_id_; + } + size_t available_incoming_streams() { return available_incoming_streams_; } + + void set_max_allowed_incoming_streams(size_t stream_count) { + max_allowed_incoming_streams_ = stream_count; + } + + void set_largest_peer_created_stream_id( + QuicStreamId largest_peer_created_stream_id) { + largest_peer_created_stream_id_ = largest_peer_created_stream_id; + } + + private: + friend class test::QuicSessionPeer; + friend class test::QuicStreamIdManagerPeer; + + // Check whether the MAX_STREAM_ID window has opened up enough and, if so, + // generate and send a MAX_STREAM_ID frame. + void MaybeSendMaxStreamIdFrame(); + + // Back reference to the session containing this Stream ID Manager. + // needed to access various session methods, such as perspective() + QuicSession* session_; + + // The ID to use for the next outgoing stream. + QuicStreamId next_outgoing_stream_id_; + + // Set of stream ids that are less than the largest stream id that has been + // received, but are nonetheless available to be created. + QuicUnorderedSet<QuicStreamId> available_streams_; + + QuicStreamId largest_peer_created_stream_id_; + + // The maximum stream ID value that we can use. This is initialized based on, + // first, the default number of open streams we can do, updated per the number + // of streams we receive in the transport parameters, and then finally is + // modified whenever a MAX_STREAM_ID frame is received from the peer. + QuicStreamId max_allowed_outgoing_stream_id_; + + // Unlike for streams this node initiates, for incoming streams, there are two + // maxima; the actual maximum which is the limit the peer must obey and the + // maximum that was most recently advertised to the peer in a MAX_STREAM_ID + // frame. + // + // The advertised maximum is never larger than the actual maximum. The actual + // maximum increases whenever an incoming stream is closed. The advertised + // maximum increases (to the actual maximum) whenever a MAX_STREAM_ID is sent. + // + // The peer is granted some leeway, incoming streams are accepted as long as + // their stream id is not greater than the actual maximum. The protocol + // specifies that the advertised maximum is the limit. This implmentation uses + // the actual maximum in order to support Google-QUIC semantics, where it's + // the number of open streams, not their ID number, that is the real limit. + QuicStreamId actual_max_allowed_incoming_stream_id_; + QuicStreamId advertised_max_allowed_incoming_stream_id_; + + // max_stream_id_window_ is set to max_allowed_outgoing_streams_ / 2 + // (half of the number of streams that are allowed). The local node + // does not send a MAX_STREAM_ID frame to the peer until the local node + // believes that the peer can open fewer than |max_stream_id_window_| + // streams. When that is so, the local node sends a MAX_STREAM_ID every time + // an inbound stream is closed. + QuicStreamId max_stream_id_window_; + + // Maximum number of outgoing and incoming streams that are allowed to be + // concurrently opened. Initialized as part of configuration. + size_t max_allowed_outgoing_streams_; + size_t max_allowed_incoming_streams_; + + // Keep track of the first dynamic stream id (which is the largest static + // stream id plus one id). For Google QUIC, static streams are not counted + // against the stream count limit. When the number of static streams + // increases, the maximum stream id has to increase by a corresponding amount. + // These are used as floors from which the relevant maximum is + // calculated. Keeping the "first dynamic" rather than the "last static" has + // some implementation advantages. + QuicStreamId first_incoming_dynamic_stream_id_; + QuicStreamId first_outgoing_dynamic_stream_id_; + + // Number of streams that that this node believes that the + // peer can open. It is initialized to the same value as + // max_allowed_incoming_streams_. It is decremented every + // time a new incoming stream is detected. A MAX_STREAM_ID + // is sent whenver a stream closes and this counter is less + // than the window. When that happens, it is incremented by + // the number of streams we make available (the actual max + // stream ID - the most recently advertised one) + size_t available_incoming_streams_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_STREAM_ID_MANAGER_H_
diff --git a/quic/core/quic_stream_id_manager_test.cc b/quic/core/quic_stream_id_manager_test.cc new file mode 100644 index 0000000..51799e2 --- /dev/null +++ b/quic/core/quic_stream_id_manager_test.cc
@@ -0,0 +1,844 @@ +// Copyright (c) 2018 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/quic_stream_id_manager.h" + +#include <cstdint> +#include <set> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::Invoke; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class TestQuicStream : public QuicStream { + // TestQuicStream exists simply as a place to hang OnDataAvailable(). + public: + TestQuicStream(QuicStreamId id, QuicSession* session, StreamType type) + : QuicStream(id, session, /*is_static=*/false, type) {} + + void OnDataAvailable() override {} +}; + +class TestQuicSession : public MockQuicSession { + public: + TestQuicSession(QuicConnection* connection) + : MockQuicSession(connection, /*create_mock_crypto_stream=*/true) { + Initialize(); + } + + TestQuicStream* CreateIncomingStream(QuicStreamId id) override { + TestQuicStream* stream = new TestQuicStream( + id, this, + DetermineStreamType(id, connection()->transport_version(), + perspective(), + /*is_incoming=*/true, BIDIRECTIONAL)); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + bool SaveFrame(const QuicFrame& frame) { + save_frame_ = frame; + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + const QuicFrame& save_frame() { return save_frame_; } + + bool ClearControlFrame(const QuicFrame& frame) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + TestQuicStream* CreateOutgoingBidirectionalStream() { + if (!CanOpenNextOutgoingBidirectionalStream()) { + return nullptr; + } + QuicStreamId id = GetNextOutgoingBidirectionalStreamId(); + TestQuicStream* stream = new TestQuicStream(id, this, BIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + TestQuicStream* CreateOutgoingUnidirectionalStream() { + if (!CanOpenNextOutgoingUnidirectionalStream()) { + return nullptr; + } + QuicStreamId id = GetNextOutgoingUnidirectionalStreamId(); + TestQuicStream* stream = new TestQuicStream(id, this, WRITE_UNIDIRECTIONAL); + ActivateStream(QuicWrapUnique(stream)); + return stream; + } + + private: + QuicFrame save_frame_; +}; + +class QuicStreamIdManagerTestBase : public QuicTestWithParam<bool> { + protected: + explicit QuicStreamIdManagerTestBase(Perspective perspective) + : connection_(new StrictMock<MockQuicConnection>( + &helper_, + &alarm_factory_, + perspective, + ParsedQuicVersionVector( + {{PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99}}))) { + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + session_ = QuicMakeUnique<TestQuicSession>(connection_); + stream_id_manager_ = + GetParam() ? QuicSessionPeer::v99_bidirectional_stream_id_manager( + session_.get()) + : QuicSessionPeer::v99_unidirectional_stream_id_manager( + session_.get()); + } + + QuicTransportVersion transport_version() const { + return connection_->transport_version(); + } + + void CloseStream(QuicStreamId id) { session_->CloseStream(id); } + + QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { + return QuicUtils::GetFirstBidirectionalStreamId( + connection_->transport_version(), Perspective::IS_CLIENT) + + kV99StreamIdIncrement * n; + } + + QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) { + return QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_CLIENT) + + kV99StreamIdIncrement * n; + } + + QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { + return QuicUtils::GetFirstBidirectionalStreamId( + connection_->transport_version(), Perspective::IS_SERVER) + + kV99StreamIdIncrement * n; + } + + QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) { + return QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_SERVER) + + kV99StreamIdIncrement * n; + } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + std::unique_ptr<TestQuicSession> session_; + QuicStreamIdManager* stream_id_manager_; +}; + +// Following tests are either client-specific (they depend, in some way, on +// client-specific attributes, such as the initial stream ID) or are +// server/client independent (arbitrarily all such tests have been placed here). + +class QuicStreamIdManagerTestClient : public QuicStreamIdManagerTestBase { + protected: + QuicStreamIdManagerTestClient() + : QuicStreamIdManagerTestBase(Perspective::IS_CLIENT) {} +}; + +INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestClient, testing::Bool()); + +// Check that the parameters used by the stream ID manager are properly +// initialized. +TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientInitialization) { + // These fields are inited via the QuicSession constructor to default + // values defined as a constant. + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + stream_id_manager_->max_allowed_incoming_streams()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + stream_id_manager_->max_allowed_outgoing_streams()); + + // The window for advertising updates to the MAX STREAM ID is half the number + // of streams allowed. + EXPECT_EQ(kDefaultMaxStreamsPerConnection / kMaxStreamIdWindowDivisor, + stream_id_manager_->max_stream_id_window()); + + // This test runs as a client, so it initiates (that is to say, outgoing) + // even-numbered stream IDs. Also, our implementation starts allocating + // stream IDs at 0 (for clients) 1 (for servers) -- before taking statically + // allocated streams into account. The -1 in the calculation is + // because the value being tested is the maximum allowed stream ID, not the + // first unallowed stream id. + const QuicStreamId kExpectedMaxOutgoingStreamId = + (GetParam() ? session_->next_outgoing_bidirectional_stream_id() + : session_->next_outgoing_unidirectional_stream_id()) + + ((kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement); + EXPECT_EQ(kExpectedMaxOutgoingStreamId, + stream_id_manager_->max_allowed_outgoing_stream_id()); + + // Same for IDs of incoming streams... + const QuicStreamId kExpectedMaxIncomingStreamId = + stream_id_manager_->first_incoming_dynamic_stream_id() + + (kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement; + EXPECT_EQ(kExpectedMaxIncomingStreamId, + stream_id_manager_->actual_max_allowed_incoming_stream_id()); + EXPECT_EQ(kExpectedMaxIncomingStreamId, + stream_id_manager_->advertised_max_allowed_incoming_stream_id()); +} + +// This test checks that the initialization for the maximum allowed outgoing +// stream id is correct. +TEST_P(QuicStreamIdManagerTestClient, CheckMaxAllowedOutgoing) { + const size_t kNumOutgoingStreams = 124; + stream_id_manager_->SetMaxOpenOutgoingStreams(kNumOutgoingStreams); + EXPECT_EQ(kNumOutgoingStreams, + stream_id_manager_->max_allowed_outgoing_streams()); + + // Check that the maximum available stream is properly set. + size_t expected_max_outgoing_id = + (GetParam() ? session_->next_outgoing_bidirectional_stream_id() + : session_->next_outgoing_unidirectional_stream_id()) + + ((kNumOutgoingStreams - 1) * kV99StreamIdIncrement); + EXPECT_EQ(expected_max_outgoing_id, + stream_id_manager_->max_allowed_outgoing_stream_id()); +} + +// This test checks that the initialization for the maximum allowed incoming +// stream id is correct. +TEST_P(QuicStreamIdManagerTestClient, CheckMaxAllowedIncoming) { + const size_t kStreamCount = 245; + stream_id_manager_->SetMaxOpenIncomingStreams(kStreamCount); + EXPECT_EQ(kStreamCount, stream_id_manager_->max_allowed_incoming_streams()); + // Check that the window is 1/2 (integer math) of the stream count. + EXPECT_EQ(kStreamCount / 2, stream_id_manager_->max_stream_id_window()); + + // Actual- and advertised- maxima start out equal. + EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(), + stream_id_manager_->advertised_max_allowed_incoming_stream_id()); + + // Check that the maximum stream ID is properly calculated. + EXPECT_EQ(stream_id_manager_->first_incoming_dynamic_stream_id() + + ((kStreamCount - 1) * kV99StreamIdIncrement), + stream_id_manager_->actual_max_allowed_incoming_stream_id()); +} + +// This test checks that the stream advertisement window is set to 1 +// if the number of stream ids is 1. This is a special case in the code. +TEST_P(QuicStreamIdManagerTestClient, CheckMaxStreamIdWindow1) { + stream_id_manager_->SetMaxOpenIncomingStreams(1); + EXPECT_EQ(1u, stream_id_manager_->max_allowed_incoming_streams()); + // If streamid_count/2==0 (integer math) force it to 1. + EXPECT_EQ(1u, stream_id_manager_->max_stream_id_window()); +} + +// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is less than the +// stream ID most recently advertised in a MAX_STREAM_ID frame. This should +// cause a MAX_STREAM_ID frame with the most recently advertised stream id to be +// sent. +TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedOk) { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame)); + QuicStreamId stream_id = + stream_id_manager_->advertised_max_allowed_incoming_stream_id() - + kV99StreamIdIncrement; + QuicStreamIdBlockedFrame frame(0, stream_id); + session_->OnStreamIdBlockedFrame(frame); + + // We should see a MAX_STREAM_ID frame. + EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type); + + // and it should advertise the current max-allowed value. + EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(), + session_->save_frame().max_stream_id_frame.max_stream_id); +} + +// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is equal to +// stream ID most recently advertised in a MAX_STREAM_ID frame. No +// MAX_STREAM_ID should be generated. +TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedNoOp) { + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + QuicStreamId stream_id = + stream_id_manager_->advertised_max_allowed_incoming_stream_id(); + QuicStreamIdBlockedFrame frame(0, stream_id); + session_->OnStreamIdBlockedFrame(frame); +} + +// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is greater than +// the stream ID most recently advertised in a MAX_STREAM_ID frame. Expect a +// connection close with an error. +TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedTooBig) { + EXPECT_CALL(*connection_, + CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _)); + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + QuicStreamId stream_id = + stream_id_manager_->advertised_max_allowed_incoming_stream_id() + + kV99StreamIdIncrement; + QuicStreamIdBlockedFrame frame(0, stream_id); + session_->OnStreamIdBlockedFrame(frame); +} + +// Same basic tests as above, but calls +// QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId directly, avoiding the +// call chain. The intent is that if there is a problem, the following tests +// will point to either the stream ID manager or the call chain. They also +// provide specific, small scale, tests of a public QuicStreamIdManager method. +// First test make sure that streams with ids below the limit are accepted. +TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdValidBelowLimit) { + QuicStreamId stream_id = + stream_id_manager_->actual_max_allowed_incoming_stream_id() - + kV99StreamIdIncrement; + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); +} + +// Accept a stream with an ID that equals the limit. +TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdValidAtLimit) { + QuicStreamId stream_id = + stream_id_manager_->actual_max_allowed_incoming_stream_id(); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); +} + +// Close the connection if the id exceeds the limit. +TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdInValidAboveLimit) { + QuicStreamId stream_id = + stream_id_manager_->actual_max_allowed_incoming_stream_id() + + kV99StreamIdIncrement; + QuicString error_details = + GetParam() ? "Stream id 401 above 397" : "Stream id 403 above 399"; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _)); + EXPECT_FALSE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); +} + +// Test that a client will reject a MAX_STREAM_ID that specifies a +// server-initiated stream ID. +TEST_P(QuicStreamIdManagerTestClient, RejectServerMaxStreamId) { + QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id(); + + // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the + // current MAX. + id += (kV99StreamIdIncrement * 2); + + // Make it an odd (server-initiated) ID. + id |= 0x1; + EXPECT_FALSE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id)); + + // Make the frame and process it; should result in the connection being + // closed. + QuicMaxStreamIdFrame frame(0, id); + EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAM_ID_ERROR, _, _)); + session_->OnMaxStreamIdFrame(frame); +} + +// Test that a client will reject a STREAM_ID_BLOCKED that specifies a +// client-initiated stream ID. STREAM_ID_BLOCKED from a server should specify an +// odd (server-initiated_ ID). Generate one with an odd ID and check that the +// connection is closed. +TEST_P(QuicStreamIdManagerTestClient, RejectServerStreamIdBlocked) { + QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id(); + + // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the + // current MAX. + id += (kV99StreamIdIncrement * 2); + // Make sure it's odd, like a client-initiated ID. + id &= ~0x01; + EXPECT_TRUE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id)); + + // Generate and process the frame; connection should be closed. + QuicStreamIdBlockedFrame frame(0, id); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _)); + session_->OnStreamIdBlockedFrame(frame); +} + +// Test functionality for reception of a MAX STREAM ID frame. This code is +// client/server-agnostic. +TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientOnMaxStreamIdFrame) { + // Get the current maximum allowed outgoing stream ID. + QuicStreamId initial_stream_id = + stream_id_manager_->max_allowed_outgoing_stream_id(); + QuicMaxStreamIdFrame frame; + + // If the stream ID in the frame is < the current maximum then + // the frame should be ignored. + frame.max_stream_id = initial_stream_id - kV99StreamIdIncrement; + EXPECT_TRUE(stream_id_manager_->OnMaxStreamIdFrame(frame)); + EXPECT_EQ(initial_stream_id, + stream_id_manager_->max_allowed_outgoing_stream_id()); + + // A stream ID greater than the current limit should increase the limit. + frame.max_stream_id = initial_stream_id + kV99StreamIdIncrement; + EXPECT_TRUE(stream_id_manager_->OnMaxStreamIdFrame(frame)); + EXPECT_EQ(initial_stream_id + kV99StreamIdIncrement, + stream_id_manager_->max_allowed_outgoing_stream_id()); +} + +// Test functionality for reception of a STREAM ID BLOCKED frame. +// This code is client/server-agnostic. +TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerOnStreamIdBlockedFrame) { + // Get the current maximum allowed incoming stream ID. + QuicStreamId advertised_stream_id = + stream_id_manager_->advertised_max_allowed_incoming_stream_id(); + QuicStreamIdBlockedFrame frame; + + // If the peer is saying it's blocked on the stream ID that + // we've advertised, it's a noop since the peer has the correct information. + frame.stream_id = advertised_stream_id; + EXPECT_TRUE(stream_id_manager_->OnStreamIdBlockedFrame(frame)); + + // If the peer is saying it's blocked on a stream ID that is larger + // than what we've advertised, the connection should get closed. + frame.stream_id = advertised_stream_id + kV99StreamIdIncrement; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _)); + EXPECT_FALSE(stream_id_manager_->OnStreamIdBlockedFrame(frame)); + + // If the peer is saying it's blocked on a stream ID that is less than + // what we've advertised, we send a MAX STREAM ID frame and update + // the advertised value. + // First, need to bump up the actual max so there is room for the MAX + // STREAM_ID frame to send a larger ID. + QuicStreamId actual_stream_id = + stream_id_manager_->actual_max_allowed_incoming_stream_id(); + stream_id_manager_->OnStreamClosed( + stream_id_manager_->first_incoming_dynamic_stream_id()); + EXPECT_EQ(actual_stream_id + kV99StreamIdIncrement, + stream_id_manager_->actual_max_allowed_incoming_stream_id()); + EXPECT_GT(stream_id_manager_->actual_max_allowed_incoming_stream_id(), + stream_id_manager_->advertised_max_allowed_incoming_stream_id()); + + // Now simulate receiving a STTREAM_ID_BLOCKED frame... + // Changing the actual maximum, above, forces a MAX STREAM ID frame to be + // sent, so the logic for that (SendMaxStreamIdFrame(), etc) is tested. + frame.stream_id = advertised_stream_id; + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(1) + .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame)); + EXPECT_TRUE(stream_id_manager_->OnStreamIdBlockedFrame(frame)); + EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(), + stream_id_manager_->advertised_max_allowed_incoming_stream_id()); + EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type); + EXPECT_EQ(stream_id_manager_->advertised_max_allowed_incoming_stream_id(), + session_->save_frame().max_stream_id_frame.max_stream_id); + + // Ensure a client initiated stream ID is rejected. + frame.stream_id = GetParam() ? GetNthClientInitiatedBidirectionalId(1) + : GetNthClientInitiatedUnidirectionalId(1); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _)); + EXPECT_FALSE(stream_id_manager_->OnStreamIdBlockedFrame(frame)); +} + +// Test GetNextOutgoingStream. This is client/server agnostic. +TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerGetNextOutgoingFrame) { + // Number of streams we can open and the first one we should get when + // opening... + int number_of_streams = kDefaultMaxStreamsPerConnection; + QuicStreamId stream_id = + GetParam() ? session_->next_outgoing_bidirectional_stream_id() + : session_->next_outgoing_unidirectional_stream_id(); + + while (number_of_streams) { + EXPECT_TRUE(stream_id_manager_->CanOpenNextOutgoingStream()); + EXPECT_EQ(stream_id, stream_id_manager_->GetNextOutgoingStreamId()); + stream_id += kV99StreamIdIncrement; + number_of_streams--; + } + EXPECT_EQ(stream_id - kV99StreamIdIncrement, + stream_id_manager_->max_allowed_outgoing_stream_id()); + + // If we try to check that the next outgoing stream id is available it should + // A) fail and B) generate a STREAM_ID_BLOCKED frame. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(1) + .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame)); + EXPECT_FALSE(stream_id_manager_->CanOpenNextOutgoingStream()); + EXPECT_EQ(STREAM_ID_BLOCKED_FRAME, session_->save_frame().type); + EXPECT_EQ(stream_id_manager_->max_allowed_outgoing_stream_id(), + session_->save_frame().max_stream_id_frame.max_stream_id); + // If we try to get the next id (above the limit), it should cause a quic-bug. + EXPECT_QUIC_BUG( + stream_id_manager_->GetNextOutgoingStreamId(), + "Attempt allocate a new outgoing stream ID would exceed the limit"); +} + +// Ensure that MaybeIncreaseLargestPeerStreamId works properly. This is +// server/client agnostic. +TEST_P(QuicStreamIdManagerTestClient, + StreamIdManagerServerMaybeIncreaseLargestPeerStreamId) { + EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId( + stream_id_manager_->actual_max_allowed_incoming_stream_id())); + QuicStreamId server_initiated_stream_id = + GetParam() ? GetNthServerInitiatedBidirectionalId(0) + : GetNthServerInitiatedUnidirectionalId(0); + EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId( + server_initiated_stream_id)); + // A bad stream ID results in a closed connection. + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + EXPECT_FALSE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId( + stream_id_manager_->actual_max_allowed_incoming_stream_id() + + kV99StreamIdIncrement)); +} + +// Test the MAX STREAM ID Window functionality. +// Free up Stream ID space. Do not expect to see a MAX_STREAM_ID +// until |window| stream ids are available. +TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerServerMaxStreamId) { + // Test that a MAX_STREAM_ID frame is generated when the peer has less than + // |max_stream_id_window_| streams left that it can initiate. + + // First, open, and then close, max_stream_id_window_ streams. This will + // max_stream_id_window_ streams available for the peer -- no MAX_STREAM_ID + // should be sent. The -1 is because the check in + // QuicStreamIdManager::MaybeSendMaxStreamIdFrame sends a MAX_STREAM_ID if the + // number of available streams at the peer is <= |max_stream_id_window_| + int stream_count = stream_id_manager_->max_stream_id_window() - 1; + + QuicStreamId advertised_max = + stream_id_manager_->advertised_max_allowed_incoming_stream_id(); + QuicStreamId expected_actual_max_id = + stream_id_manager_->actual_max_allowed_incoming_stream_id(); + + // Should not get a control-frame transmission since the peer should have + // "plenty" of stream IDs to use. + EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); + // This test runs as a client, so the first stream to release is 2, a + // server-initiated stream. + QuicStreamId stream_id = GetParam() + ? GetNthServerInitiatedBidirectionalId(0) + : GetNthServerInitiatedUnidirectionalId(0); + size_t old_available_incoming_streams = + stream_id_manager_->available_incoming_streams(); + + while (stream_count) { + EXPECT_TRUE( + stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); + + old_available_incoming_streams--; + EXPECT_EQ(old_available_incoming_streams, + stream_id_manager_->available_incoming_streams()); + + stream_count--; + stream_id += kV99StreamIdIncrement; + } + + // Now close them, still should get no MAX_STREAM_ID + stream_count = stream_id_manager_->max_stream_id_window(); + stream_id = GetParam() ? GetNthServerInitiatedBidirectionalId(0) + : GetNthServerInitiatedUnidirectionalId(0); + while (stream_count) { + stream_id_manager_->OnStreamClosed(stream_id); + stream_count--; + stream_id += kV99StreamIdIncrement; + expected_actual_max_id += kV99StreamIdIncrement; + EXPECT_EQ(expected_actual_max_id, + stream_id_manager_->actual_max_allowed_incoming_stream_id()); + // Advertised maximum should remain the same. + EXPECT_EQ(advertised_max, + stream_id_manager_->advertised_max_allowed_incoming_stream_id()); + } + + // This should not change. + EXPECT_EQ(old_available_incoming_streams, + stream_id_manager_->available_incoming_streams()); + + // Now whenever we close a stream we should get a MAX_STREAM_ID frame. + // Above code closed all the open streams, so we have to open/close + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(1) + .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame)); + EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id)); + stream_id_manager_->OnStreamClosed(stream_id); + stream_id += kV99StreamIdIncrement; + + // Check that the MAX STREAM ID was sent and has the correct values. + EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type); + EXPECT_EQ(stream_id_manager_->advertised_max_allowed_incoming_stream_id(), + session_->save_frame().max_stream_id_frame.max_stream_id); +} + +// Test that registering static stream IDs causes the stream ID limit to rise +// accordingly. This is server/client agnostic. +TEST_P(QuicStreamIdManagerTestClient, TestStaticStreamAdjustment) { + QuicStreamId first_dynamic = + stream_id_manager_->first_incoming_dynamic_stream_id(); + QuicStreamId expected_max_incoming = + stream_id_manager_->actual_max_allowed_incoming_stream_id(); + + // First test will register the first dynamic stream id as being for a static + // stream. This takes one stream ID out of the low-end of the dynamic range + // so therefore the high end should go up by 1 ID. + expected_max_incoming += kV99StreamIdIncrement; + stream_id_manager_->RegisterStaticStream(first_dynamic); + EXPECT_EQ(expected_max_incoming, + stream_id_manager_->actual_max_allowed_incoming_stream_id()); + + // Now be extreme, increase static by 100 stream ids. A discontinuous + // jump is not allowed; make sure. + first_dynamic += kV99StreamIdIncrement * 100; + expected_max_incoming += kV99StreamIdIncrement * 100; + QuicString bug_detail = + GetParam() ? "allocate 5 got 401" : "allocate 7 got 403"; + EXPECT_QUIC_BUG( + stream_id_manager_->RegisterStaticStream(first_dynamic), + "Error in incoming static stream allocation, expected to " + bug_detail); +} + +// Following tests all are server-specific. They depend, in some way, on +// server-specific attributes, such as the initial stream ID. + +class QuicStreamIdManagerTestServer : public QuicStreamIdManagerTestBase { + protected: + QuicStreamIdManagerTestServer() + : QuicStreamIdManagerTestBase(Perspective::IS_SERVER) {} +}; + +INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestServer, testing::Bool()); + +// This test checks that the initialization for the maximum allowed outgoing +// stream id is correct. +TEST_P(QuicStreamIdManagerTestServer, CheckMaxAllowedOutgoing) { + const size_t kIncomingStreamCount = 123; + stream_id_manager_->SetMaxOpenOutgoingStreams(kIncomingStreamCount); + EXPECT_EQ(kIncomingStreamCount, + stream_id_manager_->max_allowed_outgoing_streams()); + + // Check that the max outgoing stream id is properly calculated + EXPECT_EQ(stream_id_manager_->GetNextOutgoingStreamId() + + ((kIncomingStreamCount - 1) * kV99StreamIdIncrement), + stream_id_manager_->max_allowed_outgoing_stream_id()); +} + +// This test checks that the initialization for the maximum allowed incoming +// stream id is correct. +TEST_P(QuicStreamIdManagerTestServer, CheckMaxAllowedIncoming) { + const size_t kIncomingStreamCount = 245; + stream_id_manager_->SetMaxOpenIncomingStreams(kIncomingStreamCount); + EXPECT_EQ(kIncomingStreamCount, + stream_id_manager_->max_allowed_incoming_streams()); + + // Check that the window is 1/2 (integer math) of the stream count. + EXPECT_EQ((kIncomingStreamCount / 2), + stream_id_manager_->max_stream_id_window()); + + // Actual- and advertised- maxima start out equal. + EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(), + stream_id_manager_->advertised_max_allowed_incoming_stream_id()); + + // First stream ID the client should use should be 3, this means that the max + // stream id is 491 -- ((number of stream ids-1) * 2) + first available id. + EXPECT_EQ(stream_id_manager_->first_incoming_dynamic_stream_id() + + ((kIncomingStreamCount - 1) * kV99StreamIdIncrement), + stream_id_manager_->actual_max_allowed_incoming_stream_id()); +} + +// Test that a MAX_STREAM_ID frame is generated when half the stream ids become +// available. This has a useful side effect of testing that when streams are +// closed, the number of available stream ids increases. +TEST_P(QuicStreamIdManagerTestServer, MaxStreamIdSlidingWindow) { + // Ignore OnStreamReset calls. + EXPECT_CALL(*connection_, OnStreamReset(_, _)).WillRepeatedly(Return()); + // Capture control frames for analysis. + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame)); + // Simulate config being negotiated, causing the limits all to be initialized. + session_->OnConfigNegotiated(); + QuicStreamId first_advert = + stream_id_manager_->advertised_max_allowed_incoming_stream_id(); + + // Open/close enough streams to shrink the window without causing a MAX STREAM + // ID to be generated. The window will open (and a MAX STREAM ID generated) + // when max_stream_id_window() stream IDs have been made available. The loop + // will make that many stream IDs available, so the last CloseStream should + // cause a MAX STREAM ID frame to be generated. + int i = static_cast<int>(stream_id_manager_->max_stream_id_window()); + QuicStreamId id = stream_id_manager_->first_incoming_dynamic_stream_id(); + while (i) { + QuicStream* stream = session_->GetOrCreateStream(id); + EXPECT_NE(nullptr, stream); + // have to set the stream's fin-received flag to true so that it + // does not go into the has-not-received-byte-offset state, leading + // to the stream being added to the locally_closed_streams_highest_offset_ + // map, and therefore not counting as truly being closed. The test requires + // that the stream truly close, so that new streams become available, + // causing the MAX_STREAM_ID to be sent. + stream->set_fin_received(true); + EXPECT_EQ(id, stream->id()); + if (GetParam()) { + // Only send reset for incoming bidirectional streams. + EXPECT_CALL(*session_, SendRstStream(_, _, _)); + } + CloseStream(stream->id()); + i--; + id += kV99StreamIdIncrement; + } + EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type); + QuicStreamId second_advert = + session_->save_frame().max_stream_id_frame.max_stream_id; + EXPECT_EQ(first_advert + (stream_id_manager_->max_stream_id_window() * + kV99StreamIdIncrement), + second_advert); +} + +// Tast that an attempt to create an outgoing stream does not exceed the limit +// and that it generates an appropriate STREAM_ID_BLOCKED frame. +TEST_P(QuicStreamIdManagerTestServer, NewStreamDoesNotExceedLimit) { + size_t stream_count = stream_id_manager_->max_allowed_outgoing_streams(); + EXPECT_NE(0u, stream_count); + TestQuicStream* stream; + while (stream_count) { + stream = GetParam() ? session_->CreateOutgoingBidirectionalStream() + : session_->CreateOutgoingUnidirectionalStream(); + EXPECT_NE(stream, nullptr); + stream_count--; + } + // Quis Custodiet Ipsos Custodes. + EXPECT_EQ(stream->id(), stream_id_manager_->max_allowed_outgoing_stream_id()); + // Create another, it should fail. Should also send a STREAM_ID_BLOCKED + // control frame. + EXPECT_CALL(*connection_, SendControlFrame(_)); + stream = GetParam() ? session_->CreateOutgoingBidirectionalStream() + : session_->CreateOutgoingUnidirectionalStream(); + EXPECT_EQ(nullptr, stream); +} + +// Test that a server will reject a MAX_STREAM_ID that specifies a +// client-initiated stream ID. +TEST_P(QuicStreamIdManagerTestServer, RejectClientMaxStreamId) { + QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id(); + + // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the + // current MAX. + id += (kV99StreamIdIncrement * 2); + + // Turn it into a client-initiated ID (even). + id &= ~0x1; + EXPECT_TRUE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id)); + + // Generate a MAX_STREAM_ID frame and process it; the connection should close. + QuicMaxStreamIdFrame frame(0, id); + EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAM_ID_ERROR, _, _)); + session_->OnMaxStreamIdFrame(frame); +} + +// Test that a server will reject a STREAM_ID_BLOCKED that specifies a +// server-initiated stream ID. STREAM_ID_BLOCKED from a client should specify an +// even (client-initiated_ ID) generate one with an odd ID and check that the +// connection is closed. +TEST_P(QuicStreamIdManagerTestServer, RejectClientStreamIdBlocked) { + QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id(); + + // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the + // current MAX. + id += (kV99StreamIdIncrement * 2); + + // Make the ID odd, so it looks like the client is trying to specify a + // server-initiated ID. + id |= 0x1; + EXPECT_FALSE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id)); + + // Generate a STREAM_ID_BLOCKED frame and process it; the connection should + // close. + QuicStreamIdBlockedFrame frame(0, id); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _)); + session_->OnStreamIdBlockedFrame(frame); +} + +// Check that the parameters used by the stream ID manager are properly +// initialized +TEST_P(QuicStreamIdManagerTestServer, StreamIdManagerServerInitialization) { + // These fields are inited via the QuicSession constructor to default + // values defined as a constant. + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + stream_id_manager_->max_allowed_incoming_streams()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + stream_id_manager_->max_allowed_outgoing_streams()); + + // The window for advertising updates to the MAX STREAM ID is half the number + // of stream allowed. + EXPECT_EQ(kDefaultMaxStreamsPerConnection / kMaxStreamIdWindowDivisor, + stream_id_manager_->max_stream_id_window()); + + // This test runs as a server, so it initiates (that is to say, outgoing) + // even-numbered stream IDs. The -1 in the calculation is because the value + // being tested is the maximum allowed stream ID, not the first unallowed + // stream id. + const QuicStreamId kExpectedMaxOutgoingStreamId = + (GetParam() ? session_->next_outgoing_bidirectional_stream_id() + : session_->next_outgoing_unidirectional_stream_id()) + + ((kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement); + EXPECT_EQ(kExpectedMaxOutgoingStreamId, + stream_id_manager_->max_allowed_outgoing_stream_id()); + + // Same for IDs of incoming streams... But they are client initiated, so are + // even. + const QuicStreamId kExpectedMaxIncomingStreamId = + GetParam() ? GetNthClientInitiatedBidirectionalId( + kDefaultMaxStreamsPerConnection - 1) + : GetNthClientInitiatedUnidirectionalId( + kDefaultMaxStreamsPerConnection - 1); + EXPECT_EQ(kExpectedMaxIncomingStreamId, + stream_id_manager_->actual_max_allowed_incoming_stream_id()); + EXPECT_EQ(kExpectedMaxIncomingStreamId, + stream_id_manager_->advertised_max_allowed_incoming_stream_id()); +} + +TEST_P(QuicStreamIdManagerTestServer, AvailableStreams) { + stream_id_manager_->MaybeIncreaseLargestPeerStreamId( + GetParam() ? GetNthClientInitiatedBidirectionalId(3) + : GetNthClientInitiatedUnidirectionalId(3)); + EXPECT_TRUE(stream_id_manager_->IsAvailableStream( + GetParam() ? GetNthClientInitiatedBidirectionalId(1) + : GetNthClientInitiatedUnidirectionalId(1))); + EXPECT_TRUE(stream_id_manager_->IsAvailableStream( + GetParam() ? GetNthClientInitiatedBidirectionalId(2) + : GetNthClientInitiatedUnidirectionalId(2))); +} + +// Tests that if MaybeIncreaseLargestPeerStreamId is given an extremely +// large stream ID (larger than the limit) it is rejected. +// This is a regression for Chromium bugs 909987 and 910040 +TEST_P(QuicStreamIdManagerTestServer, ExtremeMaybeIncreaseLargestPeerStreamId) { + QuicStreamId too_big_stream_id = + stream_id_manager_->actual_max_allowed_incoming_stream_id() + + kV99StreamIdIncrement * 20; + + QuicString error_details = + GetParam() ? "Stream id 480 above 400" : "Stream id 478 above 398"; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _)); + + EXPECT_FALSE( + stream_id_manager_->MaybeIncreaseLargestPeerStreamId(too_big_stream_id)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_stream_send_buffer.cc b/quic/core/quic_stream_send_buffer.cc new file mode 100644 index 0000000..efe6240 --- /dev/null +++ b/quic/core/quic_stream_send_buffer.cc
@@ -0,0 +1,304 @@ +// Copyright (c) 2017 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 <algorithm> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +namespace { + +struct CompareOffset { + bool operator()(const BufferedSlice& slice, QuicStreamOffset offset) const { + return slice.offset + slice.slice.length() < offset; + } +}; + +} // namespace + +BufferedSlice::BufferedSlice(QuicMemSlice mem_slice, QuicStreamOffset offset) + : slice(std::move(mem_slice)), offset(offset) {} + +BufferedSlice::BufferedSlice(BufferedSlice&& other) = default; + +BufferedSlice& BufferedSlice::operator=(BufferedSlice&& other) = default; + +BufferedSlice::~BufferedSlice() {} + +bool StreamPendingRetransmission::operator==( + const StreamPendingRetransmission& other) const { + return offset == other.offset && length == other.length; +} + +QuicStreamSendBuffer::QuicStreamSendBuffer(QuicBufferAllocator* allocator) + : stream_offset_(0), + allocator_(allocator), + stream_bytes_written_(0), + stream_bytes_outstanding_(0), + write_index_(-1) {} + +QuicStreamSendBuffer::~QuicStreamSendBuffer() {} + +void QuicStreamSendBuffer::SaveStreamData(const struct iovec* iov, + int iov_count, + size_t iov_offset, + QuicByteCount data_length) { + DCHECK_LT(0u, data_length); + // Latch the maximum data slice size. + const QuicByteCount max_data_slice_size = + GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size); + while (data_length > 0) { + size_t slice_len = std::min(data_length, max_data_slice_size); + QuicMemSlice slice(allocator_, slice_len); + QuicUtils::CopyToBuffer(iov, iov_count, iov_offset, slice_len, + const_cast<char*>(slice.data())); + SaveMemSlice(std::move(slice)); + data_length -= slice_len; + iov_offset += slice_len; + } +} + +void QuicStreamSendBuffer::SaveMemSlice(QuicMemSlice slice) { + QUIC_DVLOG(2) << "Save slice offset " << stream_offset_ << " length " + << slice.length(); + if (slice.empty()) { + QUIC_BUG << "Try to save empty MemSlice to send buffer."; + return; + } + size_t length = slice.length(); + buffered_slices_.emplace_back(std::move(slice), stream_offset_); + if (write_index_ == -1) { + write_index_ = buffered_slices_.size() - 1; + } + stream_offset_ += length; +} + +void QuicStreamSendBuffer::OnStreamDataConsumed(size_t bytes_consumed) { + stream_bytes_written_ += bytes_consumed; + stream_bytes_outstanding_ += bytes_consumed; +} + +bool QuicStreamSendBuffer::WriteStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) { + bool write_index_hit = false; + QuicDeque<BufferedSlice>::iterator slice_it = + write_index_ == -1 + ? buffered_slices_.begin() + // Assume with write_index, write mostly starts from indexed slice. + : buffered_slices_.begin() + write_index_; + if (write_index_ != -1) { + if (offset >= slice_it->offset + slice_it->slice.length()) { + QUIC_BUG << "Tried to write data out of sequence."; + return false; + } + // Determine if write actually happens at indexed slice. + if (offset >= slice_it->offset) { + write_index_hit = true; + } else { + // Write index missed, move iterator to the beginning. + slice_it = buffered_slices_.begin(); + } + } + + for (; slice_it != buffered_slices_.end(); ++slice_it) { + if (data_length == 0 || offset < slice_it->offset) { + break; + } + if (offset >= slice_it->offset + slice_it->slice.length()) { + continue; + } + QuicByteCount slice_offset = offset - slice_it->offset; + QuicByteCount available_bytes_in_slice = + slice_it->slice.length() - slice_offset; + QuicByteCount copy_length = std::min(data_length, available_bytes_in_slice); + if (!writer->WriteBytes(slice_it->slice.data() + slice_offset, + copy_length)) { + QUIC_BUG << "Writer fails to write."; + return false; + } + offset += copy_length; + data_length -= copy_length; + + if (write_index_hit && copy_length == available_bytes_in_slice) { + // Finished writing all data in current slice, advance write index for + // next write. + ++write_index_; + } + } + + if (write_index_hit && + static_cast<size_t>(write_index_) == buffered_slices_.size()) { + // Already write to the end off buffer. + DVLOG(2) << "Finish writing out all buffered data."; + write_index_ = -1; + } + + return data_length == 0; +} + +bool QuicStreamSendBuffer::OnStreamDataAcked( + QuicStreamOffset offset, + QuicByteCount data_length, + QuicByteCount* newly_acked_length) { + *newly_acked_length = 0; + if (data_length == 0) { + return true; + } + if (bytes_acked_.Empty() || offset >= bytes_acked_.rbegin()->max() || + bytes_acked_.IsDisjoint( + QuicInterval<QuicStreamOffset>(offset, offset + data_length))) { + // Optimization for the typical case, when all data is newly acked. + if (stream_bytes_outstanding_ < data_length) { + return false; + } + bytes_acked_.Add(offset, offset + data_length); + *newly_acked_length = data_length; + stream_bytes_outstanding_ -= data_length; + pending_retransmissions_.Difference(offset, offset + data_length); + if (!FreeMemSlices(offset, offset + data_length)) { + return false; + } + CleanUpBufferedSlices(); + return true; + } + // Exit if no new data gets acked. + if (bytes_acked_.Contains(offset, offset + data_length)) { + return true; + } + // Execute the slow path if newly acked data fill in existing holes. + QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); + newly_acked.Difference(bytes_acked_); + for (const auto& interval : newly_acked) { + *newly_acked_length += (interval.max() - interval.min()); + } + if (stream_bytes_outstanding_ < *newly_acked_length) { + return false; + } + stream_bytes_outstanding_ -= *newly_acked_length; + bytes_acked_.Add(offset, offset + data_length); + pending_retransmissions_.Difference(offset, offset + data_length); + if (newly_acked.Empty()) { + return true; + } + if (!FreeMemSlices(newly_acked.begin()->min(), newly_acked.rbegin()->max())) { + return false; + } + CleanUpBufferedSlices(); + return true; +} + +void QuicStreamSendBuffer::OnStreamDataLost(QuicStreamOffset offset, + QuicByteCount data_length) { + if (data_length == 0) { + return; + } + QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length); + bytes_lost.Difference(bytes_acked_); + if (bytes_lost.Empty()) { + return; + } + for (const auto& lost : bytes_lost) { + pending_retransmissions_.Add(lost.min(), lost.max()); + } +} + +void QuicStreamSendBuffer::OnStreamDataRetransmitted( + QuicStreamOffset offset, + QuicByteCount data_length) { + if (data_length == 0) { + return; + } + pending_retransmissions_.Difference(offset, offset + data_length); +} + +bool QuicStreamSendBuffer::HasPendingRetransmission() const { + return !pending_retransmissions_.Empty(); +} + +StreamPendingRetransmission QuicStreamSendBuffer::NextPendingRetransmission() + const { + if (HasPendingRetransmission()) { + const auto pending = pending_retransmissions_.begin(); + return {pending->min(), pending->max() - pending->min()}; + } + QUIC_BUG << "NextPendingRetransmission is called unexpected with no " + "pending retransmissions."; + return {0, 0}; +} + +bool QuicStreamSendBuffer::FreeMemSlices(QuicStreamOffset start, + QuicStreamOffset end) { + auto it = buffered_slices_.begin(); + // Find it, such that buffered_slices_[it - 1].end < start <= + // buffered_slices_[it].end. + if (it == buffered_slices_.end() || it->slice.empty()) { + QUIC_BUG << "Trying to ack stream data [" << start << ", " << end << "), " + << (it == buffered_slices_.end() + ? "and there is no outstanding data." + : "and the first slice is empty."); + return false; + } + if (start >= it->offset + it->slice.length() || start < it->offset) { + // Slow path that not the earliest outstanding data gets acked. + it = std::lower_bound(buffered_slices_.begin(), buffered_slices_.end(), + start, CompareOffset()); + } + if (it == buffered_slices_.end() || it->slice.empty()) { + QUIC_BUG << "Offset " << start + << " does not exist or it has already been acked."; + return false; + } + for (; it != buffered_slices_.end(); ++it) { + if (it->offset >= end) { + break; + } + if (!it->slice.empty() && + bytes_acked_.Contains(it->offset, it->offset + it->slice.length())) { + it->slice.Reset(); + } + } + return true; +} + +void QuicStreamSendBuffer::CleanUpBufferedSlices() { + while (!buffered_slices_.empty() && buffered_slices_.front().slice.empty()) { + // Remove data which stops waiting for acks. Please note, mem slices can + // be released out of order, but send buffer is cleaned up in order. + QUIC_BUG_IF(write_index_ == 0) + << "Fail to advance current_write_slice_. It points to the slice " + "whose data has all be written and ACK'ed or ignored. " + "current_write_slice_ offset " + << buffered_slices_[write_index_].offset << " length " + << buffered_slices_[write_index_].slice.length(); + if (write_index_ > 0) { + // If write index is pointing to any slice, reduce the index as the + // slices are all shifted to the left by one. + --write_index_; + } + buffered_slices_.pop_front(); + } +} + +bool QuicStreamSendBuffer::IsStreamDataOutstanding( + QuicStreamOffset offset, + QuicByteCount data_length) const { + return data_length > 0 && + !bytes_acked_.Contains(offset, offset + data_length); +} + +size_t QuicStreamSendBuffer::size() const { + return buffered_slices_.size(); +} + +} // namespace quic
diff --git a/quic/core/quic_stream_send_buffer.h b/quic/core/quic_stream_send_buffer.h new file mode 100644 index 0000000..4bcac34 --- /dev/null +++ b/quic/core/quic_stream_send_buffer.h
@@ -0,0 +1,165 @@ +// Copyright (c) 2017 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_QUIC_STREAM_SEND_BUFFER_H_ +#define QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_ + +#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_interval_set.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h" + +namespace quic { + +namespace test { +class QuicStreamSendBufferPeer; +class QuicStreamPeer; +} // namespace test + +class QuicDataWriter; + +// BufferedSlice comprises information of a piece of stream data stored in +// contiguous memory space. Please note, BufferedSlice is constructed when +// stream data is saved in send buffer and is removed when stream data is fully +// acked. It is move-only. +struct BufferedSlice { + BufferedSlice(QuicMemSlice mem_slice, QuicStreamOffset offset); + BufferedSlice(BufferedSlice&& other); + BufferedSlice& operator=(BufferedSlice&& other); + + BufferedSlice(const BufferedSlice& other) = delete; + BufferedSlice& operator=(const BufferedSlice& other) = delete; + ~BufferedSlice(); + + // Stream data of this data slice. + QuicMemSlice slice; + // Location of this data slice in the stream. + QuicStreamOffset offset; +}; + +struct StreamPendingRetransmission { + StreamPendingRetransmission(QuicStreamOffset offset, QuicByteCount length) + : offset(offset), length(length) {} + + // Starting offset of this pending retransmission. + QuicStreamOffset offset; + // Length of this pending retransmission. + QuicByteCount length; + + QUIC_EXPORT_PRIVATE bool operator==( + const StreamPendingRetransmission& other) const; +}; + +// QuicStreamSendBuffer contains a list of QuicStreamDataSlices. New data slices +// are added to the tail of the list. Data slices are removed from the head of +// the list when they get fully acked. Stream data can be retrieved and acked +// across slice boundaries. +class QUIC_EXPORT_PRIVATE QuicStreamSendBuffer { + public: + explicit QuicStreamSendBuffer(QuicBufferAllocator* allocator); + QuicStreamSendBuffer(const QuicStreamSendBuffer& other) = delete; + QuicStreamSendBuffer(QuicStreamSendBuffer&& other) = delete; + ~QuicStreamSendBuffer(); + + // Save |data_length| of data starts at |iov_offset| in |iov| to send buffer. + void SaveStreamData(const struct iovec* iov, + int iov_count, + size_t iov_offset, + QuicByteCount data_length); + + // Save |slice| to send buffer. + void SaveMemSlice(QuicMemSlice slice); + + // Called when |bytes_consumed| bytes has been consumed by the stream. + void OnStreamDataConsumed(size_t bytes_consumed); + + // Write |data_length| of data starts at |offset|. + bool WriteStreamData(QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer); + + // Called when data [offset, offset + data_length) is acked or removed as + // stream is canceled. Removes fully acked data slice from send buffer. Set + // |newly_acked_length|. Returns false if trying to ack unsent data. + bool OnStreamDataAcked(QuicStreamOffset offset, + QuicByteCount data_length, + QuicByteCount* newly_acked_length); + + // Called when data [offset, offset + data_length) is considered as lost. + void OnStreamDataLost(QuicStreamOffset offset, QuicByteCount data_length); + + // Called when data [offset, offset + length) was retransmitted. + void OnStreamDataRetransmitted(QuicStreamOffset offset, + QuicByteCount data_length); + + // Returns true if there is pending retransmissions. + bool HasPendingRetransmission() const; + + // Returns next pending retransmissions. + StreamPendingRetransmission NextPendingRetransmission() const; + + // Returns true if data [offset, offset + data_length) is outstanding and + // waiting to be acked. Returns false otherwise. + bool IsStreamDataOutstanding(QuicStreamOffset offset, + QuicByteCount data_length) const; + + // Number of data slices in send buffer. + size_t size() const; + + QuicStreamOffset stream_offset() const { return stream_offset_; } + + uint64_t stream_bytes_written() const { return stream_bytes_written_; } + + uint64_t stream_bytes_outstanding() const { + return stream_bytes_outstanding_; + } + + const QuicIntervalSet<QuicStreamOffset>& bytes_acked() const { + return bytes_acked_; + } + + const QuicIntervalSet<QuicStreamOffset>& pending_retransmissions() const { + return pending_retransmissions_; + } + + private: + friend class test::QuicStreamSendBufferPeer; + friend class test::QuicStreamPeer; + + // Called when data within offset [start, end) gets acked. Frees fully + // acked buffered slices if any. Returns false if the corresponding data does + // not exist or has been acked. + bool FreeMemSlices(QuicStreamOffset start, QuicStreamOffset end); + + // Cleanup empty slices in order from buffered_slices_. + void CleanUpBufferedSlices(); + + QuicDeque<BufferedSlice> buffered_slices_; + + // Offset of next inserted byte. + QuicStreamOffset stream_offset_; + + QuicBufferAllocator* allocator_; + + // Bytes that have been consumed by the stream. + uint64_t stream_bytes_written_; + + // Bytes that have been consumed and are waiting to be acked. + uint64_t stream_bytes_outstanding_; + + // Offsets of data that has been acked. + QuicIntervalSet<QuicStreamOffset> bytes_acked_; + + // Data considered as lost and needs to be retransmitted. + QuicIntervalSet<QuicStreamOffset> pending_retransmissions_; + + // Index of slice which contains data waiting to be written for the first + // time. -1 if send buffer is empty or all data has been written. + int32_t write_index_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_
diff --git a/quic/core/quic_stream_send_buffer_test.cc b/quic/core/quic_stream_send_buffer_test.cc new file mode 100644 index 0000000..a58f872 --- /dev/null +++ b/quic/core/quic_stream_send_buffer_test.cc
@@ -0,0 +1,291 @@ +// Copyright (c) 2017 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/quic_stream_send_buffer.h" + +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +struct iovec MakeIovec(QuicStringPiece data) { + struct iovec iov = {const_cast<char*>(data.data()), + static_cast<size_t>(data.size())}; + return iov; +} + +class QuicStreamSendBufferTest : public QuicTest { + public: + QuicStreamSendBufferTest() : send_buffer_(&allocator_) { + EXPECT_EQ(0u, send_buffer_.size()); + EXPECT_EQ(0u, send_buffer_.stream_bytes_written()); + EXPECT_EQ(0u, send_buffer_.stream_bytes_outstanding()); + QuicString data1(1536, 'a'); + QuicString data2 = QuicString(256, 'b') + QuicString(256, 'c'); + struct iovec iov[2]; + iov[0] = MakeIovec(QuicStringPiece(data1)); + iov[1] = MakeIovec(QuicStringPiece(data2)); + + QuicMemSlice slice1(&allocator_, 1024); + memset(const_cast<char*>(slice1.data()), 'c', 1024); + QuicMemSlice slice2(&allocator_, 768); + memset(const_cast<char*>(slice2.data()), 'd', 768); + + // Index starts from not pointing to any slice. + EXPECT_EQ(nullptr, + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)); + + // Save all data. + SetQuicFlag(&FLAGS_quic_send_buffer_max_data_slice_size, 1024); + send_buffer_.SaveStreamData(iov, 2, 0, 2048); + send_buffer_.SaveMemSlice(std::move(slice1)); + EXPECT_TRUE(slice1.empty()); + send_buffer_.SaveMemSlice(std::move(slice2)); + EXPECT_TRUE(slice2.empty()); + + EXPECT_EQ(4u, send_buffer_.size()); + // At this point, the whole buffer looks like: + // | a * 1536 |b * 256| c * 1280 | d * 768 | + // | slice1 | slice2 | slice3 | slice4 | + } + + void WriteAllData() { + // Write all data. + char buf[4000]; + QuicDataWriter writer(4000, buf, HOST_BYTE_ORDER); + send_buffer_.WriteStreamData(0, 3840u, &writer); + + send_buffer_.OnStreamDataConsumed(3840u); + EXPECT_EQ(3840u, send_buffer_.stream_bytes_written()); + EXPECT_EQ(3840u, send_buffer_.stream_bytes_outstanding()); + } + + SimpleBufferAllocator allocator_; + QuicStreamSendBuffer send_buffer_; +}; + +TEST_F(QuicStreamSendBufferTest, CopyDataToBuffer) { + char buf[4000]; + QuicDataWriter writer(4000, buf, HOST_BYTE_ORDER); + QuicString copy1(1024, 'a'); + QuicString copy2 = + QuicString(512, 'a') + QuicString(256, 'b') + QuicString(256, 'c'); + QuicString copy3(1024, 'c'); + QuicString copy4(768, 'd'); + + ASSERT_TRUE(send_buffer_.WriteStreamData(0, 1024, &writer)); + EXPECT_EQ(copy1, QuicStringPiece(buf, 1024)); + ASSERT_TRUE(send_buffer_.WriteStreamData(1024, 1024, &writer)); + EXPECT_EQ(copy2, QuicStringPiece(buf + 1024, 1024)); + ASSERT_TRUE(send_buffer_.WriteStreamData(2048, 1024, &writer)); + EXPECT_EQ(copy3, QuicStringPiece(buf + 2048, 1024)); + ASSERT_TRUE(send_buffer_.WriteStreamData(3072, 768, &writer)); + EXPECT_EQ(copy4, QuicStringPiece(buf + 3072, 768)); + + // Test data piece across boundries. + QuicDataWriter writer2(4000, buf, HOST_BYTE_ORDER); + QuicString copy5 = + QuicString(536, 'a') + QuicString(256, 'b') + QuicString(232, 'c'); + ASSERT_TRUE(send_buffer_.WriteStreamData(1000, 1024, &writer2)); + EXPECT_EQ(copy5, QuicStringPiece(buf, 1024)); + ASSERT_TRUE(send_buffer_.WriteStreamData(2500, 1024, &writer2)); + QuicString copy6 = QuicString(572, 'c') + QuicString(452, 'd'); + EXPECT_EQ(copy6, QuicStringPiece(buf + 1024, 1024)); + + // Invalid data copy. + QuicDataWriter writer3(4000, buf, HOST_BYTE_ORDER); + EXPECT_FALSE(send_buffer_.WriteStreamData(3000, 1024, &writer3)); + EXPECT_QUIC_BUG(send_buffer_.WriteStreamData(0, 4000, &writer3), + "Writer fails to write."); + + send_buffer_.OnStreamDataConsumed(3840); + EXPECT_EQ(3840u, send_buffer_.stream_bytes_written()); + EXPECT_EQ(3840u, send_buffer_.stream_bytes_outstanding()); +} + +TEST_F(QuicStreamSendBufferTest, RemoveStreamFrame) { + WriteAllData(); + + QuicByteCount newly_acked_length; + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1024, 1024, &newly_acked_length)); + EXPECT_EQ(1024u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2048, 1024, &newly_acked_length)); + EXPECT_EQ(1024u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1024, &newly_acked_length)); + EXPECT_EQ(1024u, newly_acked_length); + + // Send buffer is cleaned up in order. + EXPECT_EQ(1u, send_buffer_.size()); + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(3072, 768, &newly_acked_length)); + EXPECT_EQ(768u, newly_acked_length); + EXPECT_EQ(0u, send_buffer_.size()); +} + +TEST_F(QuicStreamSendBufferTest, RemoveStreamFrameAcrossBoundries) { + WriteAllData(); + + QuicByteCount newly_acked_length; + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2024, 576, &newly_acked_length)); + EXPECT_EQ(576u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1000, &newly_acked_length)); + EXPECT_EQ(1000u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1000, 1024, &newly_acked_length)); + EXPECT_EQ(1024u, newly_acked_length); + // Send buffer is cleaned up in order. + EXPECT_EQ(2u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2600, 1024, &newly_acked_length)); + EXPECT_EQ(1024u, newly_acked_length); + EXPECT_EQ(1u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(3624, 216, &newly_acked_length)); + EXPECT_EQ(216u, newly_acked_length); + EXPECT_EQ(0u, send_buffer_.size()); +} + +TEST_F(QuicStreamSendBufferTest, AckStreamDataMultipleTimes) { + WriteAllData(); + QuicByteCount newly_acked_length; + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(100, 1500, &newly_acked_length)); + EXPECT_EQ(1500u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2000, 500, &newly_acked_length)); + EXPECT_EQ(500u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 2600, &newly_acked_length)); + EXPECT_EQ(600u, newly_acked_length); + // Send buffer is cleaned up in order. + EXPECT_EQ(2u, send_buffer_.size()); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2200, 1640, &newly_acked_length)); + EXPECT_EQ(1240u, newly_acked_length); + EXPECT_EQ(0u, send_buffer_.size()); + + EXPECT_FALSE(send_buffer_.OnStreamDataAcked(4000, 100, &newly_acked_length)); +} + +TEST_F(QuicStreamSendBufferTest, AckStreamDataOutOfOrder) { + WriteAllData(); + QuicByteCount newly_acked_length; + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(500, 1000, &newly_acked_length)); + EXPECT_EQ(1000u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + EXPECT_EQ(3840u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_)); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1200, 1000, &newly_acked_length)); + EXPECT_EQ(700u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + // Slice 2 gets fully acked. + EXPECT_EQ(2816u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_)); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2000, 1840, &newly_acked_length)); + EXPECT_EQ(1640u, newly_acked_length); + EXPECT_EQ(4u, send_buffer_.size()); + // Slices 3 and 4 get fully acked. + EXPECT_EQ(1024u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_)); + + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1000, &newly_acked_length)); + EXPECT_EQ(500u, newly_acked_length); + EXPECT_EQ(0u, send_buffer_.size()); + EXPECT_EQ(0u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_)); +} + +TEST_F(QuicStreamSendBufferTest, PendingRetransmission) { + WriteAllData(); + EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(0, 3840)); + EXPECT_FALSE(send_buffer_.HasPendingRetransmission()); + // Lost data [0, 1200). + send_buffer_.OnStreamDataLost(0, 1200); + // Lost data [1500, 2000). + send_buffer_.OnStreamDataLost(1500, 500); + EXPECT_TRUE(send_buffer_.HasPendingRetransmission()); + + EXPECT_EQ(StreamPendingRetransmission(0, 1200), + send_buffer_.NextPendingRetransmission()); + // Retransmit data [0, 500). + send_buffer_.OnStreamDataRetransmitted(0, 500); + EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(0, 500)); + EXPECT_EQ(StreamPendingRetransmission(500, 700), + send_buffer_.NextPendingRetransmission()); + // Ack data [500, 1200). + QuicByteCount newly_acked_length = 0; + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(500, 700, &newly_acked_length)); + EXPECT_FALSE(send_buffer_.IsStreamDataOutstanding(500, 700)); + EXPECT_TRUE(send_buffer_.HasPendingRetransmission()); + EXPECT_EQ(StreamPendingRetransmission(1500, 500), + send_buffer_.NextPendingRetransmission()); + // Retransmit data [1500, 2000). + send_buffer_.OnStreamDataRetransmitted(1500, 500); + EXPECT_FALSE(send_buffer_.HasPendingRetransmission()); + + // Lost [200, 800). + send_buffer_.OnStreamDataLost(200, 600); + EXPECT_TRUE(send_buffer_.HasPendingRetransmission()); + // Verify [200, 500) is considered as lost, as [500, 800) has been acked. + EXPECT_EQ(StreamPendingRetransmission(200, 300), + send_buffer_.NextPendingRetransmission()); + + // Verify 0 length data is not outstanding. + EXPECT_FALSE(send_buffer_.IsStreamDataOutstanding(100, 0)); + // Verify partially acked data is outstanding. + EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(400, 800)); +} + +TEST_F(QuicStreamSendBufferTest, CurrentWriteIndex) { + char buf[4000]; + QuicDataWriter writer(4000, buf, HOST_BYTE_ORDER); + // With data buffered, index points to the 1st slice of data. + EXPECT_EQ(0u, + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset); + ASSERT_TRUE(send_buffer_.WriteStreamData(0, 1024, &writer)); + // Wrote all data on 1st slice, index points to next slice. + EXPECT_EQ(1024u, + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset); + ASSERT_TRUE(send_buffer_.WriteStreamData(1024, 512, &writer)); + // Last write didn't finish a whole slice. Index remains. + EXPECT_EQ(1024u, + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset); + send_buffer_.OnStreamDataConsumed(1024); + + // If data in 1st slice gets ACK'ed, it shouldn't change the indexed slice + QuicByteCount newly_acked_length; + EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1024, &newly_acked_length)); + EXPECT_EQ(1024u, + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset); + + ASSERT_TRUE( + send_buffer_.WriteStreamData(1024 + 512, 3840 - 1024 - 512, &writer)); + // After writing all buffered data, index become invalid again. + EXPECT_EQ(nullptr, + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)); + QuicMemSlice slice(&allocator_, 60); + memset(const_cast<char*>(slice.data()), 'e', 60); + send_buffer_.SaveMemSlice(std::move(slice)); + // With new data, index points to the new data. + EXPECT_EQ(3840u, + QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_stream_sequencer.cc b/quic/core/quic_stream_sequencer.cc new file mode 100644 index 0000000..6311dcf --- /dev/null +++ b/quic/core/quic_stream_sequencer.cc
@@ -0,0 +1,274 @@ +// Copyright (c) 2012 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/quic_stream_sequencer.h" + +#include <algorithm> +#include <limits> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +QuicStreamSequencer::QuicStreamSequencer(StreamInterface* quic_stream) + : stream_(quic_stream), + buffered_frames_(kStreamReceiveWindowLimit), + close_offset_(std::numeric_limits<QuicStreamOffset>::max()), + blocked_(false), + num_frames_received_(0), + num_duplicate_frames_received_(0), + ignore_read_data_(false), + level_triggered_(false), + stop_reading_when_level_triggered_( + GetQuicReloadableFlag(quic_stop_reading_when_level_triggered)) {} + +QuicStreamSequencer::~QuicStreamSequencer() {} + +void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { + ++num_frames_received_; + const QuicStreamOffset byte_offset = frame.offset; + const size_t data_len = frame.data_length; + + if (frame.fin) { + CloseStreamAtOffset(frame.offset + data_len); + if (data_len == 0) { + return; + } + } + OnFrameData(byte_offset, data_len, frame.data_buffer); +} + +void QuicStreamSequencer::OnCryptoFrame(const QuicCryptoFrame& frame) { + ++num_frames_received_; + OnFrameData(frame.offset, frame.data_length, frame.data_buffer); +} + +void QuicStreamSequencer::OnFrameData(QuicStreamOffset byte_offset, + size_t data_len, + const char* data_buffer) { + const size_t previous_readable_bytes = buffered_frames_.ReadableBytes(); + size_t bytes_written; + QuicString error_details; + QuicErrorCode result = buffered_frames_.OnStreamData( + byte_offset, QuicStringPiece(data_buffer, data_len), &bytes_written, + &error_details); + if (result != QUIC_NO_ERROR) { + QuicString details = QuicStrCat( + "Stream ", stream_->id(), ": ", QuicErrorCodeToString(result), ": ", + error_details, + "\nPeer Address: ", stream_->PeerAddressOfLatestPacket().ToString()); + QUIC_LOG_FIRST_N(WARNING, 50) << QuicErrorCodeToString(result); + QUIC_LOG_FIRST_N(WARNING, 50) << details; + stream_->CloseConnectionWithDetails(result, details); + return; + } + + if (bytes_written == 0) { + ++num_duplicate_frames_received_; + // Silently ignore duplicates. + return; + } + + if (blocked_) { + return; + } + + if (level_triggered_) { + if (buffered_frames_.ReadableBytes() > previous_readable_bytes) { + // Readable bytes has changed, let stream decide if to inform application + // or not. + if (stop_reading_when_level_triggered_ && ignore_read_data_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_stop_reading_when_level_triggered); + FlushBufferedFrames(); + } else { + stream_->OnDataAvailable(); + } + } + return; + } + const bool stream_unblocked = + previous_readable_bytes == 0 && buffered_frames_.ReadableBytes() > 0; + if (stream_unblocked) { + if (ignore_read_data_) { + FlushBufferedFrames(); + } else { + stream_->OnDataAvailable(); + } + } +} + +void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) { + const QuicStreamOffset kMaxOffset = + std::numeric_limits<QuicStreamOffset>::max(); + + // If there is a scheduled close, the new offset should match it. + if (close_offset_ != kMaxOffset && offset != close_offset_) { + stream_->Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS); + return; + } + + close_offset_ = offset; + + MaybeCloseStream(); +} + +bool QuicStreamSequencer::MaybeCloseStream() { + if (blocked_ || !IsClosed()) { + return false; + } + + QUIC_DVLOG(1) << "Passing up termination, as we've processed " + << buffered_frames_.BytesConsumed() << " of " << close_offset_ + << " bytes."; + // This will cause the stream to consume the FIN. + // Technically it's an error if |num_bytes_consumed| isn't exactly + // equal to |close_offset|, but error handling seems silly at this point. + if (ignore_read_data_) { + // The sequencer is discarding stream data and must notify the stream on + // receipt of a FIN because the consumer won't. + stream_->OnFinRead(); + } else { + stream_->OnDataAvailable(); + } + buffered_frames_.Clear(); + return true; +} + +int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) const { + DCHECK(!blocked_); + return buffered_frames_.GetReadableRegions(iov, iov_len); +} + +bool QuicStreamSequencer::GetReadableRegion(iovec* iov) const { + DCHECK(!blocked_); + return buffered_frames_.GetReadableRegion(iov); +} + +bool QuicStreamSequencer::PrefetchNextRegion(iovec* iov) { + DCHECK(!blocked_); + return buffered_frames_.PrefetchNextRegion(iov); +} + +void QuicStreamSequencer::Read(QuicString* buffer) { + DCHECK(!blocked_); + buffer->resize(buffer->size() + ReadableBytes()); + iovec iov; + iov.iov_len = ReadableBytes(); + iov.iov_base = &(*buffer)[buffer->size() - iov.iov_len]; + Readv(&iov, 1); +} + +int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) { + DCHECK(!blocked_); + QuicString error_details; + size_t bytes_read; + QuicErrorCode read_error = + buffered_frames_.Readv(iov, iov_len, &bytes_read, &error_details); + if (read_error != QUIC_NO_ERROR) { + QuicString details = + QuicStrCat("Stream ", stream_->id(), ": ", error_details); + stream_->CloseConnectionWithDetails(read_error, details); + return static_cast<int>(bytes_read); + } + + stream_->AddBytesConsumed(bytes_read); + return static_cast<int>(bytes_read); +} + +bool QuicStreamSequencer::HasBytesToRead() const { + return buffered_frames_.HasBytesToRead(); +} + +size_t QuicStreamSequencer::ReadableBytes() const { + return buffered_frames_.ReadableBytes(); +} + +bool QuicStreamSequencer::IsClosed() const { + return buffered_frames_.BytesConsumed() >= close_offset_; +} + +void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { + DCHECK(!blocked_); + bool result = buffered_frames_.MarkConsumed(num_bytes_consumed); + if (!result) { + QUIC_BUG << "Invalid argument to MarkConsumed." + << " expect to consume: " << num_bytes_consumed + << ", but not enough bytes available. " << DebugString(); + stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); + return; + } + stream_->AddBytesConsumed(num_bytes_consumed); +} + +void QuicStreamSequencer::SetBlockedUntilFlush() { + blocked_ = true; +} + +void QuicStreamSequencer::SetUnblocked() { + blocked_ = false; + if (IsClosed() || HasBytesToRead()) { + stream_->OnDataAvailable(); + } +} + +void QuicStreamSequencer::StopReading() { + if (ignore_read_data_) { + return; + } + ignore_read_data_ = true; + FlushBufferedFrames(); +} + +void QuicStreamSequencer::ReleaseBuffer() { + buffered_frames_.ReleaseWholeBuffer(); +} + +void QuicStreamSequencer::ReleaseBufferIfEmpty() { + if (buffered_frames_.Empty()) { + buffered_frames_.ReleaseWholeBuffer(); + } +} + +void QuicStreamSequencer::FlushBufferedFrames() { + DCHECK(ignore_read_data_); + size_t bytes_flushed = buffered_frames_.FlushBufferedFrames(); + QUIC_DVLOG(1) << "Flushing buffered data at offset " + << buffered_frames_.BytesConsumed() << " length " + << bytes_flushed << " for stream " << stream_->id(); + stream_->AddBytesConsumed(bytes_flushed); + MaybeCloseStream(); +} + +size_t QuicStreamSequencer::NumBytesBuffered() const { + return buffered_frames_.BytesBuffered(); +} + +QuicStreamOffset QuicStreamSequencer::NumBytesConsumed() const { + return buffered_frames_.BytesConsumed(); +} + +const QuicString QuicStreamSequencer::DebugString() const { + // clang-format off + return QuicStrCat("QuicStreamSequencer:", + "\n bytes buffered: ", NumBytesBuffered(), + "\n bytes consumed: ", NumBytesConsumed(), + "\n has bytes to read: ", HasBytesToRead() ? "true" : "false", + "\n frames received: ", num_frames_received(), + "\n close offset bytes: ", close_offset_, + "\n is closed: ", IsClosed() ? "true" : "false"); + // clang-format on +} + +} // namespace quic
diff --git a/quic/core/quic_stream_sequencer.h b/quic/core/quic_stream_sequencer.h new file mode 100644 index 0000000..3feee5e --- /dev/null +++ b/quic/core/quic_stream_sequencer.h
@@ -0,0 +1,212 @@ +// Copyright (c) 2012 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_QUIC_STREAM_SEQUENCER_H_ +#define QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_H_ + +#include <cstddef> +#include <map> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +namespace test { +class QuicStreamSequencerPeer; +} // namespace test + +// Buffers frames until we have something which can be passed +// up to the next layer. +class QUIC_EXPORT_PRIVATE QuicStreamSequencer { + public: + // Interface that thie Sequencer uses to communicate with the Stream. + class StreamInterface { + public: + virtual ~StreamInterface() = default; + + // Called when new data is available to be read from the sequencer. + virtual void OnDataAvailable() = 0; + // Called when the end of the stream has been read. + virtual void OnFinRead() = 0; + // Called when bytes have been consumed from the sequencer. + virtual void AddBytesConsumed(QuicByteCount bytes) = 0; + // TODO(rch): Clean up this interface via OnUnrecoverableError and + // remove PeerAddressOfLatestPacket(). + // Called when an error has occurred which should result in the stream + // being reset. + virtual void Reset(QuicRstStreamErrorCode error) = 0; + // Called when an error has occurred which should result in the connection + // being closed. + virtual void CloseConnectionWithDetails(QuicErrorCode error, + const QuicString& details) = 0; + + // Returns the stream id of this stream. + virtual QuicStreamId id() const = 0; + // Returns the peer address of the last packet received for this stream. + virtual const QuicSocketAddress& PeerAddressOfLatestPacket() const = 0; + }; + + explicit QuicStreamSequencer(StreamInterface* quic_stream); + QuicStreamSequencer(const QuicStreamSequencer&) = delete; + QuicStreamSequencer(QuicStreamSequencer&&) = default; + QuicStreamSequencer& operator=(const QuicStreamSequencer&) = delete; + virtual ~QuicStreamSequencer(); + + // If the frame is the next one we need in order to process in-order data, + // ProcessData will be immediately called on the stream until all buffered + // data is processed or the stream fails to consume data. Any unconsumed + // data will be buffered. If the frame is not the next in line, it will be + // buffered. + void OnStreamFrame(const QuicStreamFrame& frame); + + // If the frame is the next one we need in order to process in-order data, + // ProcessData will be immediately called on the crypto stream until all + // buffered data is processed or the crypto stream fails to consume data. Any + // unconsumed data will be buffered. If the frame is not the next in line, it + // will be buffered. + void OnCryptoFrame(const QuicCryptoFrame& frame); + + // Once data is buffered, it's up to the stream to read it when the stream + // can handle more data. The following three functions make that possible. + + // Fills in up to iov_len iovecs with the next readable regions. Returns the + // number of iovs used. Non-destructive of the underlying data. + int GetReadableRegions(iovec* iov, size_t iov_len) const; + + // Fills in one iovec with the next readable region. Returns false if there + // is no readable region available. + bool GetReadableRegion(iovec* iov) const; + + // Fill in one iovec with the next unread region for the quic spdy stream. + // Returns false if no readable region is available. + bool PrefetchNextRegion(iovec* iov); + + // Copies the data into the iov_len buffers provided. Returns the number of + // bytes read. Any buffered data no longer in use will be released. + // TODO(rch): remove this method and instead implement it as a helper method + // based on GetReadableRegions and MarkConsumed. + int Readv(const struct iovec* iov, size_t iov_len); + + // Consumes |num_bytes| data. Used in conjunction with |GetReadableRegions| + // to do zero-copy reads. + void MarkConsumed(size_t num_bytes); + + // Appends all of the readable data to |buffer| and marks all of the appended + // data as consumed. + void Read(QuicString* buffer); + + // Returns true if the sequncer has bytes available for reading. + bool HasBytesToRead() const; + + // Number of bytes available to read. + size_t ReadableBytes() const; + + // Returns true if the sequencer has delivered the fin. + bool IsClosed() const; + + // Calls |OnDataAvailable| on |stream_| if there is buffered data that can + // be processed, and causes |OnDataAvailable| to be called as new data + // arrives. + void SetUnblocked(); + + // Blocks processing of frames until |SetUnblocked| is called. + void SetBlockedUntilFlush(); + + // Sets the sequencer to discard all incoming data itself and not call + // |stream_->OnDataAvailable()|. |stream_->OnFinRead()| will be called + // automatically when the FIN is consumed (which may be immediately). + void StopReading(); + + // Free the memory of underlying buffer. + void ReleaseBuffer(); + + // Free the memory of underlying buffer when no bytes remain in it. + void ReleaseBufferIfEmpty(); + + // Number of bytes in the buffer right now. + size_t NumBytesBuffered() const; + + // Number of bytes has been consumed. + QuicStreamOffset NumBytesConsumed() const; + + QuicStreamOffset close_offset() const { return close_offset_; } + + int num_frames_received() const { return num_frames_received_; } + + int num_duplicate_frames_received() const { + return num_duplicate_frames_received_; + } + + bool ignore_read_data() const { return ignore_read_data_; } + + void set_level_triggered(bool level_triggered) { + level_triggered_ = level_triggered; + } + + bool level_triggered() const { return level_triggered_; } + + void set_stream(StreamInterface* stream) { stream_ = stream; } + + // Returns string describing internal state. + const QuicString DebugString() const; + + private: + friend class test::QuicStreamSequencerPeer; + + // Deletes and records as consumed any buffered data that is now in-sequence. + // (To be called only after StopReading has been called.) + void FlushBufferedFrames(); + + // Wait until we've seen 'offset' bytes, and then terminate the stream. + void CloseStreamAtOffset(QuicStreamOffset offset); + + // If we've received a FIN and have processed all remaining data, then inform + // the stream of FIN, and clear buffers. + bool MaybeCloseStream(); + + // Shared implementation between OnStreamFrame and OnCryptoFrame. + void OnFrameData(QuicStreamOffset byte_offset, + size_t data_len, + const char* data_buffer); + + // The stream which owns this sequencer. + StreamInterface* stream_; + + // Stores received data in offset order. + QuicStreamSequencerBuffer buffered_frames_; + + // The offset, if any, we got a stream termination for. When this many bytes + // have been processed, the sequencer will be closed. + QuicStreamOffset close_offset_; + + // If true, the sequencer is blocked from passing data to the stream and will + // buffer all new incoming data until FlushBufferedFrames is called. + bool blocked_; + + // Count of the number of frames received. + int num_frames_received_; + + // Count of the number of duplicate frames received. + int num_duplicate_frames_received_; + + // If true, all incoming data will be discarded. + bool ignore_read_data_; + + // If false, only call OnDataAvailable() when it becomes newly unblocked. + // Otherwise, call OnDataAvailable() when number of readable bytes changes. + bool level_triggered_; + + // Latched value of quic_stop_reading_when_level_triggered flag. When true, + // the sequencer will discard incoming data (but not FIN bits) after + // StopReading is called, even in level_triggered_ mode. + const bool stop_reading_when_level_triggered_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_H_
diff --git a/quic/core/quic_stream_sequencer_buffer.cc b/quic/core/quic_stream_sequencer_buffer.cc new file mode 100644 index 0000000..485c9dc --- /dev/null +++ b/quic/core/quic_stream_sequencer_buffer.cc
@@ -0,0 +1,527 @@ +// Copyright (c) 2015 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/quic_stream_sequencer_buffer.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { +namespace { + +size_t CalculateBlockCount(size_t max_capacity_bytes) { + return (max_capacity_bytes + QuicStreamSequencerBuffer::kBlockSizeBytes - 1) / + QuicStreamSequencerBuffer::kBlockSizeBytes; +} + +// Upper limit of how many gaps allowed in buffer, which ensures a reasonable +// number of iterations needed to find the right gap to fill when a frame +// arrives. +const size_t kMaxNumDataIntervalsAllowed = 2 * kMaxPacketGap; + +} // namespace + +QuicStreamSequencerBuffer::QuicStreamSequencerBuffer(size_t max_capacity_bytes) + : max_buffer_capacity_bytes_(max_capacity_bytes), + blocks_count_(CalculateBlockCount(max_capacity_bytes)), + total_bytes_read_(0), + blocks_(nullptr), + total_bytes_prefetched_(0), + faster_interval_add_in_sequence_buffer_( + GetQuicReloadableFlag(quic_faster_interval_add_in_sequence_buffer)) { + Clear(); +} + +QuicStreamSequencerBuffer::~QuicStreamSequencerBuffer() { + Clear(); +} + +void QuicStreamSequencerBuffer::Clear() { + if (blocks_ != nullptr) { + for (size_t i = 0; i < blocks_count_; ++i) { + if (blocks_[i] != nullptr) { + RetireBlock(i); + } + } + } + num_bytes_buffered_ = 0; + bytes_received_.Clear(); + bytes_received_.Add(0, total_bytes_read_); +} + +bool QuicStreamSequencerBuffer::RetireBlock(size_t idx) { + if (blocks_[idx] == nullptr) { + QUIC_BUG << "Try to retire block twice"; + return false; + } + delete blocks_[idx]; + blocks_[idx] = nullptr; + QUIC_DVLOG(1) << "Retired block with index: " << idx; + return true; +} + +QuicErrorCode QuicStreamSequencerBuffer::OnStreamData( + QuicStreamOffset starting_offset, + QuicStringPiece data, + size_t* const bytes_buffered, + QuicString* error_details) { + *bytes_buffered = 0; + size_t size = data.size(); + if (size == 0) { + *error_details = "Received empty stream frame without FIN."; + return QUIC_EMPTY_STREAM_FRAME_NO_FIN; + } + // Write beyond the current range this buffer is covering. + if (starting_offset + size > total_bytes_read_ + max_buffer_capacity_bytes_ || + starting_offset + size < starting_offset) { + *error_details = "Received data beyond available range."; + return QUIC_INTERNAL_ERROR; + } + if (bytes_received_.Empty() || + starting_offset >= bytes_received_.rbegin()->max() || + bytes_received_.IsDisjoint(QuicInterval<QuicStreamOffset>( + starting_offset, starting_offset + size))) { + // Optimization for the typical case, when all data is newly received. + if (faster_interval_add_in_sequence_buffer_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_faster_interval_add_in_sequence_buffer); + bytes_received_.AddOptimizedForAppend(starting_offset, + starting_offset + size); + if (bytes_received_.Size() >= kMaxNumDataIntervalsAllowed) { + // This frame is going to create more intervals than allowed. Stop + // processing. + *error_details = "Too many data intervals received for this stream."; + return QUIC_TOO_MANY_STREAM_DATA_INTERVALS; + } + } else { + if (!bytes_received_.Empty() && + starting_offset == bytes_received_.rbegin()->max()) { + // Extend the right edge of last interval. + // TODO(fayang): Encapsulate this into a future version of + // QuicIntervalSet if this is more efficient than Add. + const_cast<QuicInterval<QuicStreamOffset>*>( + &(*bytes_received_.rbegin())) + ->SetMax(starting_offset + size); + } else { + bytes_received_.Add(starting_offset, starting_offset + size); + if (bytes_received_.Size() >= kMaxNumDataIntervalsAllowed) { + // This frame is going to create more intervals than allowed. Stop + // processing. + *error_details = "Too many data intervals received for this stream."; + return QUIC_TOO_MANY_STREAM_DATA_INTERVALS; + } + } + } + size_t bytes_copy = 0; + if (!CopyStreamData(starting_offset, data, &bytes_copy, error_details)) { + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } + *bytes_buffered += bytes_copy; + num_bytes_buffered_ += *bytes_buffered; + return QUIC_NO_ERROR; + } + // Slow path, received data overlaps with received data. + QuicIntervalSet<QuicStreamOffset> newly_received(starting_offset, + starting_offset + size); + newly_received.Difference(bytes_received_); + if (newly_received.Empty()) { + return QUIC_NO_ERROR; + } + bytes_received_.Add(starting_offset, starting_offset + size); + if (bytes_received_.Size() >= kMaxNumDataIntervalsAllowed) { + // This frame is going to create more intervals than allowed. Stop + // processing. + *error_details = "Too many data intervals received for this stream."; + return QUIC_TOO_MANY_STREAM_DATA_INTERVALS; + } + for (const auto& interval : newly_received) { + const QuicStreamOffset copy_offset = interval.min(); + const QuicByteCount copy_length = interval.max() - interval.min(); + size_t bytes_copy = 0; + if (!CopyStreamData(copy_offset, + data.substr(copy_offset - starting_offset, copy_length), + &bytes_copy, error_details)) { + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } + *bytes_buffered += bytes_copy; + } + num_bytes_buffered_ += *bytes_buffered; + return QUIC_NO_ERROR; +} + +bool QuicStreamSequencerBuffer::CopyStreamData(QuicStreamOffset offset, + QuicStringPiece data, + size_t* bytes_copy, + QuicString* error_details) { + *bytes_copy = 0; + size_t source_remaining = data.size(); + if (source_remaining == 0) { + return true; + } + const char* source = data.data(); + // Write data block by block. If corresponding block has not created yet, + // create it first. + // Stop when all data are written or reaches the logical end of the buffer. + while (source_remaining > 0) { + const size_t write_block_num = GetBlockIndex(offset); + const size_t write_block_offset = GetInBlockOffset(offset); + DCHECK_GT(blocks_count_, write_block_num); + + size_t block_capacity = GetBlockCapacity(write_block_num); + size_t bytes_avail = block_capacity - write_block_offset; + + // If this write meets the upper boundary of the buffer, + // reduce the available free bytes. + if (offset + bytes_avail > total_bytes_read_ + max_buffer_capacity_bytes_) { + bytes_avail = total_bytes_read_ + max_buffer_capacity_bytes_ - offset; + } + + if (blocks_ == nullptr) { + blocks_.reset(new BufferBlock*[blocks_count_]()); + for (size_t i = 0; i < blocks_count_; ++i) { + blocks_[i] = nullptr; + } + } + + if (write_block_num >= blocks_count_) { + *error_details = QuicStrCat( + "QuicStreamSequencerBuffer error: OnStreamData() exceed array bounds." + "write offset = ", + offset, " write_block_num = ", write_block_num, + " blocks_count_ = ", blocks_count_); + return false; + } + if (blocks_ == nullptr) { + *error_details = + "QuicStreamSequencerBuffer error: OnStreamData() blocks_ is null"; + return false; + } + if (blocks_[write_block_num] == nullptr) { + // TODO(danzh): Investigate if using a freelist would improve performance. + // Same as RetireBlock(). + blocks_[write_block_num] = new BufferBlock(); + } + + const size_t bytes_to_copy = + std::min<size_t>(bytes_avail, source_remaining); + char* dest = blocks_[write_block_num]->buffer + write_block_offset; + QUIC_DVLOG(1) << "Write at offset: " << offset + << " length: " << bytes_to_copy; + + if (dest == nullptr || source == nullptr) { + *error_details = QuicStrCat( + "QuicStreamSequencerBuffer error: OnStreamData()" + " dest == nullptr: ", + (dest == nullptr), " source == nullptr: ", (source == nullptr), + " Writing at offset ", offset, " Gaps: ", GapsDebugString(), + " Remaining frames: ", ReceivedFramesDebugString(), + " total_bytes_read_ = ", total_bytes_read_); + return false; + } + memcpy(dest, source, bytes_to_copy); + source += bytes_to_copy; + source_remaining -= bytes_to_copy; + offset += bytes_to_copy; + *bytes_copy += bytes_to_copy; + } + return true; +} + +QuicErrorCode QuicStreamSequencerBuffer::Readv(const iovec* dest_iov, + size_t dest_count, + size_t* bytes_read, + QuicString* error_details) { + *bytes_read = 0; + for (size_t i = 0; i < dest_count && ReadableBytes() > 0; ++i) { + char* dest = reinterpret_cast<char*>(dest_iov[i].iov_base); + DCHECK(dest != nullptr); + size_t dest_remaining = dest_iov[i].iov_len; + while (dest_remaining > 0 && ReadableBytes() > 0) { + size_t block_idx = NextBlockToRead(); + size_t start_offset_in_block = ReadOffset(); + size_t block_capacity = GetBlockCapacity(block_idx); + size_t bytes_available_in_block = std::min<size_t>( + ReadableBytes(), block_capacity - start_offset_in_block); + size_t bytes_to_copy = + std::min<size_t>(bytes_available_in_block, dest_remaining); + DCHECK_GT(bytes_to_copy, 0u); + if (blocks_[block_idx] == nullptr || dest == nullptr) { + *error_details = QuicStrCat( + "QuicStreamSequencerBuffer error:" + " Readv() dest == nullptr: ", + (dest == nullptr), " blocks_[", block_idx, + "] == nullptr: ", (blocks_[block_idx] == nullptr), + " Gaps: ", GapsDebugString(), + " Remaining frames: ", ReceivedFramesDebugString(), + " total_bytes_read_ = ", total_bytes_read_); + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } + memcpy(dest, blocks_[block_idx]->buffer + start_offset_in_block, + bytes_to_copy); + dest += bytes_to_copy; + dest_remaining -= bytes_to_copy; + num_bytes_buffered_ -= bytes_to_copy; + total_bytes_read_ += bytes_to_copy; + *bytes_read += bytes_to_copy; + + // Retire the block if all the data is read out and no other data is + // stored in this block. + // In case of failing to retire a block which is ready to retire, return + // immediately. + if (bytes_to_copy == bytes_available_in_block) { + bool retire_successfully = RetireBlockIfEmpty(block_idx); + if (!retire_successfully) { + *error_details = QuicStrCat( + "QuicStreamSequencerBuffer error: fail to retire block ", + block_idx, + " as the block is already released, total_bytes_read_ = ", + total_bytes_read_, " Gaps: ", GapsDebugString()); + return QUIC_STREAM_SEQUENCER_INVALID_STATE; + } + } + } + } + total_bytes_prefetched_ = + std::max(total_bytes_prefetched_, total_bytes_read_); + + return QUIC_NO_ERROR; +} + +int QuicStreamSequencerBuffer::GetReadableRegions(struct iovec* iov, + int iov_count) const { + DCHECK(iov != nullptr); + DCHECK_GT(iov_count, 0); + + if (ReadableBytes() == 0) { + iov[0].iov_base = nullptr; + iov[0].iov_len = 0; + return 0; + } + + size_t start_block_idx = NextBlockToRead(); + QuicStreamOffset readable_offset_end = FirstMissingByte() - 1; + DCHECK_GE(readable_offset_end + 1, total_bytes_read_); + size_t end_block_offset = GetInBlockOffset(readable_offset_end); + size_t end_block_idx = GetBlockIndex(readable_offset_end); + + // If readable region is within one block, deal with it seperately. + if (start_block_idx == end_block_idx && ReadOffset() <= end_block_offset) { + iov[0].iov_base = blocks_[start_block_idx]->buffer + ReadOffset(); + iov[0].iov_len = ReadableBytes(); + QUIC_DVLOG(1) << "Got only a single block with index: " << start_block_idx; + return 1; + } + + // Get first block + iov[0].iov_base = blocks_[start_block_idx]->buffer + ReadOffset(); + iov[0].iov_len = GetBlockCapacity(start_block_idx) - ReadOffset(); + QUIC_DVLOG(1) << "Got first block " << start_block_idx << " with len " + << iov[0].iov_len; + DCHECK_GT(readable_offset_end + 1, total_bytes_read_ + iov[0].iov_len) + << "there should be more available data"; + + // Get readable regions of the rest blocks till either 2nd to last block + // before gap is met or |iov| is filled. For these blocks, one whole block is + // a region. + int iov_used = 1; + size_t block_idx = (start_block_idx + iov_used) % blocks_count_; + while (block_idx != end_block_idx && iov_used < iov_count) { + DCHECK(nullptr != blocks_[block_idx]); + iov[iov_used].iov_base = blocks_[block_idx]->buffer; + iov[iov_used].iov_len = GetBlockCapacity(block_idx); + QUIC_DVLOG(1) << "Got block with index: " << block_idx; + ++iov_used; + block_idx = (start_block_idx + iov_used) % blocks_count_; + } + + // Deal with last block if |iov| can hold more. + if (iov_used < iov_count) { + DCHECK(nullptr != blocks_[block_idx]); + iov[iov_used].iov_base = blocks_[end_block_idx]->buffer; + iov[iov_used].iov_len = end_block_offset + 1; + QUIC_DVLOG(1) << "Got last block with index: " << end_block_idx; + ++iov_used; + } + return iov_used; +} + +bool QuicStreamSequencerBuffer::GetReadableRegion(iovec* iov) const { + return GetReadableRegions(iov, 1) == 1; +} + +bool QuicStreamSequencerBuffer::PrefetchNextRegion(iovec* iov) { + DCHECK(iov != nullptr); + + if (total_bytes_prefetched_ == FirstMissingByte()) { + return false; + } + + size_t start_block_idx = GetBlockIndex(total_bytes_prefetched_); + size_t start_block_offset = GetInBlockOffset(total_bytes_prefetched_); + QuicStreamOffset readable_offset_end = FirstMissingByte() - 1; + size_t end_block_offset = GetInBlockOffset(readable_offset_end); + size_t end_block_idx = GetBlockIndex(readable_offset_end); + + if (start_block_idx != end_block_idx) { + iov->iov_base = blocks_[start_block_idx]->buffer + start_block_offset; + iov->iov_len = GetBlockCapacity(start_block_idx) - start_block_offset; + total_bytes_prefetched_ += iov->iov_len; + return true; + } + iov->iov_base = blocks_[end_block_idx]->buffer + start_block_offset; + iov->iov_len = end_block_offset - start_block_offset + 1; + total_bytes_prefetched_ += iov->iov_len; + return true; +} + +bool QuicStreamSequencerBuffer::MarkConsumed(size_t bytes_used) { + if (bytes_used > ReadableBytes()) { + return false; + } + size_t bytes_to_consume = bytes_used; + while (bytes_to_consume > 0) { + size_t block_idx = NextBlockToRead(); + size_t offset_in_block = ReadOffset(); + size_t bytes_available = std::min<size_t>( + ReadableBytes(), GetBlockCapacity(block_idx) - offset_in_block); + size_t bytes_read = std::min<size_t>(bytes_to_consume, bytes_available); + total_bytes_read_ += bytes_read; + num_bytes_buffered_ -= bytes_read; + bytes_to_consume -= bytes_read; + // If advanced to the end of current block and end of buffer hasn't wrapped + // to this block yet. + if (bytes_available == bytes_read) { + RetireBlockIfEmpty(block_idx); + } + } + total_bytes_prefetched_ = + std::max(total_bytes_read_, total_bytes_prefetched_); + return true; +} + +size_t QuicStreamSequencerBuffer::FlushBufferedFrames() { + size_t prev_total_bytes_read = total_bytes_read_; + total_bytes_read_ = NextExpectedByte(); + Clear(); + return total_bytes_read_ - prev_total_bytes_read; +} + +void QuicStreamSequencerBuffer::ReleaseWholeBuffer() { + Clear(); + blocks_.reset(nullptr); +} + +size_t QuicStreamSequencerBuffer::ReadableBytes() const { + return FirstMissingByte() - total_bytes_read_; +} + +bool QuicStreamSequencerBuffer::HasBytesToRead() const { + return ReadableBytes() > 0; +} + +QuicStreamOffset QuicStreamSequencerBuffer::BytesConsumed() const { + return total_bytes_read_; +} + +size_t QuicStreamSequencerBuffer::BytesBuffered() const { + return num_bytes_buffered_; +} + +size_t QuicStreamSequencerBuffer::GetBlockIndex(QuicStreamOffset offset) const { + return (offset % max_buffer_capacity_bytes_) / kBlockSizeBytes; +} + +size_t QuicStreamSequencerBuffer::GetInBlockOffset( + QuicStreamOffset offset) const { + return (offset % max_buffer_capacity_bytes_) % kBlockSizeBytes; +} + +size_t QuicStreamSequencerBuffer::ReadOffset() const { + return GetInBlockOffset(total_bytes_read_); +} + +size_t QuicStreamSequencerBuffer::NextBlockToRead() const { + return GetBlockIndex(total_bytes_read_); +} + +bool QuicStreamSequencerBuffer::RetireBlockIfEmpty(size_t block_index) { + DCHECK(ReadableBytes() == 0 || GetInBlockOffset(total_bytes_read_) == 0) + << "RetireBlockIfEmpty() should only be called when advancing to next " + << "block or a gap has been reached."; + // If the whole buffer becomes empty, the last piece of data has been read. + if (Empty()) { + return RetireBlock(block_index); + } + + // Check where the logical end of this buffer is. + // Not empty if the end of circular buffer has been wrapped to this block. + if (GetBlockIndex(NextExpectedByte() - 1) == block_index) { + return true; + } + + // Read index remains in this block, which means a gap has been reached. + if (NextBlockToRead() == block_index) { + if (bytes_received_.Size() > 1) { + auto it = bytes_received_.begin(); + ++it; + if (GetBlockIndex(it->min()) == block_index) { + // Do not retire the block if next data interval is in this block. + return true; + } + } else { + QUIC_BUG << "Read stopped at where it shouldn't."; + return false; + } + } + return RetireBlock(block_index); +} + +bool QuicStreamSequencerBuffer::Empty() const { + return bytes_received_.Empty() || + (bytes_received_.Size() == 1 && total_bytes_read_ > 0 && + bytes_received_.begin()->max() == total_bytes_read_); +} + +size_t QuicStreamSequencerBuffer::GetBlockCapacity(size_t block_index) const { + if ((block_index + 1) == blocks_count_) { + size_t result = max_buffer_capacity_bytes_ % kBlockSizeBytes; + if (result == 0) { // whole block + result = kBlockSizeBytes; + } + return result; + } else { + return kBlockSizeBytes; + } +} + +QuicString QuicStreamSequencerBuffer::GapsDebugString() { + // TODO(vasilvv): this should return the complement of |bytes_received_|. + return bytes_received_.ToString(); +} + +QuicString QuicStreamSequencerBuffer::ReceivedFramesDebugString() { + return bytes_received_.ToString(); +} + +QuicStreamOffset QuicStreamSequencerBuffer::FirstMissingByte() const { + if (bytes_received_.Empty() || bytes_received_.begin()->min() > 0) { + // Offset 0 is not received yet. + return 0; + } + return bytes_received_.begin()->max(); +} + +QuicStreamOffset QuicStreamSequencerBuffer::NextExpectedByte() const { + if (bytes_received_.Empty()) { + return 0; + } + return bytes_received_.rbegin()->max(); +} + +} // namespace quic
diff --git a/quic/core/quic_stream_sequencer_buffer.h b/quic/core/quic_stream_sequencer_buffer.h new file mode 100644 index 0000000..7e547a9 --- /dev/null +++ b/quic/core/quic_stream_sequencer_buffer.h
@@ -0,0 +1,249 @@ +// Copyright (c) 2015 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_QUIC_STREAM_SEQUENCER_BUFFER_H_ +#define QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_ + +// QuicStreamSequencerBuffer is a circular stream buffer with random write and +// in-sequence read. It consists of a vector of pointers pointing +// to memory blocks created as needed and an interval set recording received +// data. +// - Data are written in with offset indicating where it should be in the +// stream, and the buffer grown as needed (up to the maximum buffer capacity), +// without expensive copying (extra blocks are allocated). +// - Data can be read from the buffer if there is no gap before it, +// and the buffer shrinks as the data are consumed. +// - An upper limit on the number of blocks in the buffer provides an upper +// bound on memory use. +// +// This class is thread-unsafe. +// +// QuicStreamSequencerBuffer maintains a concept of the readable region, which +// contains all written data that has not been read. +// It promises stability of the underlying memory addresses in the readable +// region, so pointers into it can be maintained, and the offset of a pointer +// from the start of the read region can be calculated. +// +// Expected Use: +// QuicStreamSequencerBuffer buffer(2.5 * 8 * 1024); +// QuicString source(1024, 'a'); +// QuicStringPiece string_piece(source.data(), source.size()); +// size_t written = 0; +// buffer.OnStreamData(800, string_piece, GetEpollClockNow(), &written); +// source = QuicString{800, 'b'}; +// QuicStringPiece string_piece1(source.data(), 800); +// // Try to write to [1, 801), but should fail due to overlapping, +// // res should be QUIC_INVALID_STREAM_DATA +// auto res = buffer.OnStreamData(1, string_piece1, &written)); +// // write to [0, 800), res should be QUIC_NO_ERROR +// auto res = buffer.OnStreamData(0, string_piece1, GetEpollClockNow(), +// &written); +// +// // Read into a iovec array with total capacity of 120 bytes. +// char dest[120]; +// iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, +// iovec{dest + 80, 40}}; +// size_t read = buffer.Readv(iovecs, 3); +// +// // Get single readable region. +// iovec iov; +// buffer.GetReadableRegion(iov); +// +// // Get readable regions from [256, 1024) and consume some of it. +// iovec iovs[2]; +// int iov_count = buffer.GetReadableRegions(iovs, 2); +// // Consume some bytes in iovs, returning number of bytes having been +// consumed. +// size_t consumed = consume_iovs(iovs, iov_count); +// buffer.MarkConsumed(consumed); + +#include <cstddef> +#include <functional> +#include <list> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_interval_set.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" + +namespace quic { + +namespace test { +class QuicStreamSequencerBufferPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer { + public: + // Size of blocks used by this buffer. + // Choose 8K to make block large enough to hold multiple frames, each of + // which could be up to 1.5 KB. + static const size_t kBlockSizeBytes = 8 * 1024; // 8KB + + // The basic storage block used by this buffer. + struct BufferBlock { + char buffer[kBlockSizeBytes]; + }; + + explicit QuicStreamSequencerBuffer(size_t max_capacity_bytes); + QuicStreamSequencerBuffer(const QuicStreamSequencerBuffer&) = delete; + QuicStreamSequencerBuffer(QuicStreamSequencerBuffer&&) = default; + QuicStreamSequencerBuffer& operator=(const QuicStreamSequencerBuffer&) = + delete; + ~QuicStreamSequencerBuffer(); + + // Free the space used to buffer data. + void Clear(); + + // Returns true if there is nothing to read in this buffer. + bool Empty() const; + + // Called to buffer new data received for this stream. If the data was + // successfully buffered, returns QUIC_NO_ERROR and stores the number of + // bytes buffered in |bytes_buffered|. Returns an error otherwise. + QuicErrorCode OnStreamData(QuicStreamOffset offset, + QuicStringPiece data, + size_t* bytes_buffered, + QuicString* error_details); + + // Reads from this buffer into given iovec array, up to number of iov_len + // iovec objects and returns the number of bytes read. + QuicErrorCode Readv(const struct iovec* dest_iov, + size_t dest_count, + size_t* bytes_read, + QuicString* error_details); + + // Returns the readable region of valid data in iovec format. The readable + // region is the buffer region where there is valid data not yet read by + // client. + // Returns the number of iovec entries in |iov| which were populated. + // If the region is empty, one iovec entry with 0 length + // is returned, and the function returns 0. If there are more readable + // regions than |iov_size|, the function only processes the first + // |iov_size| of them. + int GetReadableRegions(struct iovec* iov, int iov_len) const; + + // Fills in one iovec with data from the next readable region. + // Returns false if there is no readable region available. + bool GetReadableRegion(iovec* iov) const; + + // Called to return the next region that has not been returned by this method + // previously. + // If this method is to be used along with Readv() or MarkConsumed(), make + // sure that they are consuming less data than is read by this method. + // This method only returns reference of underlying data. The caller is + // responsible for copying and consuming the data. + // Returns true if the data is read, false otherwise. + bool PrefetchNextRegion(iovec* iov); + + // Called after GetReadableRegions() to free up |bytes_used| space if these + // bytes are processed. + // Pre-requisite: bytes_used <= available bytes to read. + bool MarkConsumed(size_t bytes_buffered); + + // Deletes and records as consumed any buffered data and clear the buffer. + // (To be called only after sequencer's StopReading has been called.) + size_t FlushBufferedFrames(); + + // Free the memory of buffered data. + void ReleaseWholeBuffer(); + + // Whether there are bytes can be read out. + bool HasBytesToRead() const; + + // Count how many bytes have been consumed (read out of buffer). + QuicStreamOffset BytesConsumed() const; + + // Count how many bytes are in buffer at this moment. + size_t BytesBuffered() const; + + // Returns number of bytes available to be read out. + size_t ReadableBytes() const; + + private: + friend class test::QuicStreamSequencerBufferPeer; + + // Copies |data| to blocks_, sets |bytes_copy|. Returns true if the copy is + // successful. Otherwise, sets |error_details| and returns false. + bool CopyStreamData(QuicStreamOffset offset, + QuicStringPiece data, + size_t* bytes_copy, + QuicString* error_details); + + // Dispose the given buffer block. + // After calling this method, blocks_[index] is set to nullptr + // in order to indicate that no memory set is allocated for that block. + // Returns true on success, false otherwise. + bool RetireBlock(size_t index); + + // Should only be called after the indexed block is read till the end of the + // block or missing data has been reached. + // If the block at |block_index| contains no buffered data, the block + // should be retired. + // Returns true on success, or false otherwise. + bool RetireBlockIfEmpty(size_t block_index); + + // Calculate the capacity of block at specified index. + // Return value should be either kBlockSizeBytes for non-trailing blocks and + // max_buffer_capacity % kBlockSizeBytes for trailing block. + size_t GetBlockCapacity(size_t index) const; + + // Does not check if offset is within reasonable range. + size_t GetBlockIndex(QuicStreamOffset offset) const; + + // Given an offset in the stream, return the offset from the beginning of the + // block which contains this data. + size_t GetInBlockOffset(QuicStreamOffset offset) const; + + // Get offset relative to index 0 in logical 1st block to start next read. + size_t ReadOffset() const; + + // Get the index of the logical 1st block to start next read. + size_t NextBlockToRead() const; + + // Returns offset of first missing byte. + QuicStreamOffset FirstMissingByte() const; + + // Returns offset of highest received byte + 1. + QuicStreamOffset NextExpectedByte() const; + + // Return |gaps_| as a string: [1024, 1500) [1800, 2048)... for debugging. + QuicString GapsDebugString(); + + // Return all received frames as a string in same format as GapsDebugString(); + QuicString ReceivedFramesDebugString(); + + // The maximum total capacity of this buffer in byte, as constructed. + const size_t max_buffer_capacity_bytes_; + + // How many blocks this buffer would need when it reaches full capacity. + const size_t blocks_count_; + + // Number of bytes read out of buffer. + QuicStreamOffset total_bytes_read_; + + // An ordered, variable-length list of blocks, with the length limited + // such that the number of blocks never exceeds blocks_count_. + // Each list entry can hold up to kBlockSizeBytes bytes. + std::unique_ptr<BufferBlock*[]> blocks_; + + // Number of bytes in buffer. + size_t num_bytes_buffered_; + + // Currently received data. + QuicIntervalSet<QuicStreamOffset> bytes_received_; + + // Total number of bytes that have been prefetched. + QuicStreamOffset total_bytes_prefetched_; + + // Latched value of --quic_faster_interval_add_in_sequence_buffer. + const bool faster_interval_add_in_sequence_buffer_; +}; +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_
diff --git a/quic/core/quic_stream_sequencer_buffer_test.cc b/quic/core/quic_stream_sequencer_buffer_test.cc new file mode 100644 index 0000000..7d5947c --- /dev/null +++ b/quic/core/quic_stream_sequencer_buffer_test.cc
@@ -0,0 +1,1086 @@ +// Copyright (c) 2015 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/quic_stream_sequencer_buffer.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <map> +#include <utility> + +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { + +namespace test { + +char GetCharFromIOVecs(size_t offset, iovec iov[], size_t count) { + size_t start_offset = 0; + for (size_t i = 0; i < count; i++) { + if (iov[i].iov_len == 0) { + continue; + } + size_t end_offset = start_offset + iov[i].iov_len - 1; + if (offset >= start_offset && offset <= end_offset) { + const char* buf = reinterpret_cast<const char*>(iov[i].iov_base); + return buf[offset - start_offset]; + } + start_offset += iov[i].iov_len; + } + LOG(ERROR) << "Could not locate char at offset " << offset << " in " << count + << " iovecs"; + for (size_t i = 0; i < count; ++i) { + LOG(ERROR) << " iov[" << i << "].iov_len = " << iov[i].iov_len; + } + return '\0'; +} + +const size_t kMaxNumGapsAllowed = 2 * kMaxPacketGap; + +static const size_t kBlockSizeBytes = + QuicStreamSequencerBuffer::kBlockSizeBytes; +typedef QuicStreamSequencerBuffer::BufferBlock BufferBlock; + +namespace { + +class QuicStreamSequencerBufferTest : public QuicTest { + public: + void SetUp() override { Initialize(); } + + void ResetMaxCapacityBytes(size_t max_capacity_bytes) { + max_capacity_bytes_ = max_capacity_bytes; + Initialize(); + } + + protected: + void Initialize() { + buffer_ = QuicMakeUnique<QuicStreamSequencerBuffer>((max_capacity_bytes_)); + helper_ = QuicMakeUnique<QuicStreamSequencerBufferPeer>((buffer_.get())); + } + + // Use 2.5 here to make sure the buffer has more than one block and its end + // doesn't align with the end of a block in order to test all the offset + // calculation. + size_t max_capacity_bytes_ = 2.5 * kBlockSizeBytes; + + std::unique_ptr<QuicStreamSequencerBuffer> buffer_; + std::unique_ptr<QuicStreamSequencerBufferPeer> helper_; + QuicString error_details_; +}; + +TEST_F(QuicStreamSequencerBufferTest, InitializeWithMaxRecvWindowSize) { + ResetMaxCapacityBytes(16 * 1024 * 1024); // 16MB + EXPECT_EQ(2 * 1024u, // 16MB / 8KB = 2K + helper_->block_count()); + EXPECT_EQ(max_capacity_bytes_, helper_->max_buffer_capacity()); + EXPECT_TRUE(helper_->CheckInitialState()); +} + +TEST_F(QuicStreamSequencerBufferTest, InitializationWithDifferentSizes) { + const size_t kCapacity = 2 * QuicStreamSequencerBuffer::kBlockSizeBytes; + ResetMaxCapacityBytes(kCapacity); + EXPECT_EQ(max_capacity_bytes_, helper_->max_buffer_capacity()); + EXPECT_TRUE(helper_->CheckInitialState()); + + const size_t kCapacity1 = 8 * QuicStreamSequencerBuffer::kBlockSizeBytes; + ResetMaxCapacityBytes(kCapacity1); + EXPECT_EQ(kCapacity1, helper_->max_buffer_capacity()); + EXPECT_TRUE(helper_->CheckInitialState()); +} + +TEST_F(QuicStreamSequencerBufferTest, ClearOnEmpty) { + buffer_->Clear(); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamData0length) { + size_t written; + QuicErrorCode error = + buffer_->OnStreamData(800, "", &written, &error_details_); + EXPECT_EQ(error, QUIC_EMPTY_STREAM_FRAME_NO_FIN); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataWithinBlock) { + EXPECT_FALSE(helper_->IsBufferAllocated()); + QuicString source(1024, 'a'); + size_t written; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(800, source, &written, &error_details_)); + BufferBlock* block_ptr = helper_->GetBlock(0); + for (size_t i = 0; i < source.size(); ++i) { + ASSERT_EQ('a', block_ptr->buffer[helper_->GetInBlockOffset(800) + i]); + } + EXPECT_EQ(2, helper_->IntervalSize()); + EXPECT_EQ(0u, helper_->ReadableBytes()); + EXPECT_EQ(1u, helper_->bytes_received().Size()); + EXPECT_EQ(800u, helper_->bytes_received().begin()->min()); + EXPECT_EQ(1824u, helper_->bytes_received().begin()->max()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + EXPECT_TRUE(helper_->IsBufferAllocated()); +} + +TEST_F(QuicStreamSequencerBufferTest, Move) { + EXPECT_FALSE(helper_->IsBufferAllocated()); + QuicString source(1024, 'a'); + size_t written; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(800, source, &written, &error_details_)); + BufferBlock* block_ptr = helper_->GetBlock(0); + for (size_t i = 0; i < source.size(); ++i) { + ASSERT_EQ('a', block_ptr->buffer[helper_->GetInBlockOffset(800) + i]); + } + + QuicStreamSequencerBuffer buffer2(std::move(*buffer_)); + QuicStreamSequencerBufferPeer helper2(&buffer2); + + EXPECT_FALSE(helper_->IsBufferAllocated()); + + EXPECT_EQ(2, helper2.IntervalSize()); + EXPECT_EQ(0u, helper2.ReadableBytes()); + EXPECT_EQ(1u, helper2.bytes_received().Size()); + EXPECT_EQ(800u, helper2.bytes_received().begin()->min()); + EXPECT_EQ(1824u, helper2.bytes_received().begin()->max()); + EXPECT_TRUE(helper2.CheckBufferInvariants()); + EXPECT_TRUE(helper2.IsBufferAllocated()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataInvalidSource) { + // Pass in an invalid source, expects to return error. + QuicStringPiece source; + source = QuicStringPiece(nullptr, 1024); + size_t written; + EXPECT_EQ(QUIC_STREAM_SEQUENCER_INVALID_STATE, + buffer_->OnStreamData(800, source, &written, &error_details_)); + EXPECT_EQ(0u, error_details_.find(QuicStrCat( + "QuicStreamSequencerBuffer error: OnStreamData() " + "dest == nullptr: ", + false, " source == nullptr: ", true))); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataWithOverlap) { + QuicString source(1024, 'a'); + // Write something into [800, 1824) + size_t written; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(800, source, &written, &error_details_)); + // Try to write to [0, 1024) and [1024, 2048). + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(0, source, &written, &error_details_)); + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(1024, source, &written, &error_details_)); +} + +TEST_F(QuicStreamSequencerBufferTest, + OnStreamDataOverlapAndDuplicateCornerCases) { + QuicString source(1024, 'a'); + // Write something into [800, 1824) + size_t written; + buffer_->OnStreamData(800, source, &written, &error_details_); + source = QuicString(800, 'b'); + QuicString one_byte = "c"; + // Write [1, 801). + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(1, source, &written, &error_details_)); + // Write [0, 800). + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(0, source, &written, &error_details_)); + // Write [1823, 1824). + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(1823, one_byte, &written, &error_details_)); + EXPECT_EQ(0u, written); + // write one byte to [1824, 1825) + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(1824, one_byte, &written, &error_details_)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataWithoutOverlap) { + QuicString source(1024, 'a'); + // Write something into [800, 1824). + size_t written; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(800, source, &written, &error_details_)); + source = QuicString(100, 'b'); + // Write something into [kBlockSizeBytes * 2 - 20, kBlockSizeBytes * 2 + 80). + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(kBlockSizeBytes * 2 - 20, source, &written, + &error_details_)); + EXPECT_EQ(3, helper_->IntervalSize()); + EXPECT_EQ(1024u + 100u, buffer_->BytesBuffered()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataInLongStreamWithOverlap) { + // Assume a stream has already buffered almost 4GB. + uint64_t total_bytes_read = pow(2, 32) - 1; + helper_->set_total_bytes_read(total_bytes_read); + helper_->AddBytesReceived(0, total_bytes_read); + + // Three new out of order frames arrive. + const size_t kBytesToWrite = 100; + QuicString source(kBytesToWrite, 'a'); + size_t written; + // Frame [2^32 + 500, 2^32 + 600). + QuicStreamOffset offset = pow(2, 32) + 500; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(offset, source, &written, &error_details_)); + EXPECT_EQ(2, helper_->IntervalSize()); + + // Frame [2^32 + 700, 2^32 + 800). + offset = pow(2, 32) + 700; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(offset, source, &written, &error_details_)); + EXPECT_EQ(3, helper_->IntervalSize()); + + // Another frame [2^32 + 300, 2^32 + 400). + offset = pow(2, 32) + 300; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(offset, source, &written, &error_details_)); + EXPECT_EQ(4, helper_->IntervalSize()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataTillEnd) { + // Write 50 bytes to the end. + const size_t kBytesToWrite = 50; + QuicString source(kBytesToWrite, 'a'); + size_t written; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(max_capacity_bytes_ - kBytesToWrite, source, + &written, &error_details_)); + EXPECT_EQ(50u, buffer_->BytesBuffered()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataTillEndCorner) { + // Write 1 byte to the end. + const size_t kBytesToWrite = 1; + QuicString source(kBytesToWrite, 'a'); + size_t written; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->OnStreamData(max_capacity_bytes_ - kBytesToWrite, source, + &written, &error_details_)); + EXPECT_EQ(1u, buffer_->BytesBuffered()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, OnStreamDataBeyondCapacity) { + QuicString source(60, 'a'); + size_t written; + EXPECT_EQ(QUIC_INTERNAL_ERROR, + buffer_->OnStreamData(max_capacity_bytes_ - 50, source, &written, + &error_details_)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + + source = "b"; + EXPECT_EQ(QUIC_INTERNAL_ERROR, + buffer_->OnStreamData(max_capacity_bytes_, source, &written, + &error_details_)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + + EXPECT_EQ(QUIC_INTERNAL_ERROR, + buffer_->OnStreamData(max_capacity_bytes_ * 1000, source, &written, + &error_details_)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + + // Disallow current_gap != gaps_.end() + EXPECT_EQ(QUIC_INTERNAL_ERROR, + buffer_->OnStreamData(static_cast<QuicStreamOffset>(-1), source, + &written, &error_details_)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + + // Disallow offset + size overflow + source = "bbb"; + EXPECT_EQ(QUIC_INTERNAL_ERROR, + buffer_->OnStreamData(static_cast<QuicStreamOffset>(-2), source, + &written, &error_details_)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + EXPECT_EQ(0u, buffer_->BytesBuffered()); +} + +TEST_F(QuicStreamSequencerBufferTest, Readv100Bytes) { + QuicString source(1024, 'a'); + // Write something into [kBlockSizeBytes, kBlockSizeBytes + 1024). + size_t written; + buffer_->OnStreamData(kBlockSizeBytes, source, &written, &error_details_); + EXPECT_FALSE(buffer_->HasBytesToRead()); + source = QuicString(100, 'b'); + // Write something into [0, 100). + buffer_->OnStreamData(0, source, &written, &error_details_); + EXPECT_TRUE(buffer_->HasBytesToRead()); + // Read into a iovec array with total capacity of 120 bytes. + char dest[120]; + iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, iovec{dest + 80, 40}}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 3, &read, &error_details_)); + QUIC_LOG(ERROR) << error_details_; + EXPECT_EQ(100u, read); + EXPECT_EQ(100u, buffer_->BytesConsumed()); + EXPECT_EQ(source, QuicString(dest, read)); + // The first block should be released as its data has been read out. + EXPECT_EQ(nullptr, helper_->GetBlock(0)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, ReadvAcrossBlocks) { + QuicString source(kBlockSizeBytes + 50, 'a'); + // Write 1st block to full and extand 50 bytes to next block. + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + EXPECT_EQ(source.size(), helper_->ReadableBytes()); + // Iteratively read 512 bytes from buffer_-> Overwrite dest[] each time. + char dest[512]; + while (helper_->ReadableBytes()) { + std::fill(dest, dest + 512, 0); + iovec iovecs[2]{iovec{dest, 256}, iovec{dest + 256, 256}}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 2, &read, &error_details_)); + } + // The last read only reads the rest 50 bytes in 2nd block. + EXPECT_EQ(QuicString(50, 'a'), QuicString(dest, 50)); + EXPECT_EQ(0, dest[50]) << "Dest[50] shouln't be filled."; + EXPECT_EQ(source.size(), buffer_->BytesConsumed()); + EXPECT_TRUE(buffer_->Empty()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, ClearAfterRead) { + QuicString source(kBlockSizeBytes + 50, 'a'); + // Write 1st block to full with 'a'. + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + // Read first 512 bytes from buffer to make space at the beginning. + char dest[512]{0}; + const iovec iov{dest, 512}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); + // Clear() should make buffer empty while preserving BytesConsumed() + buffer_->Clear(); + EXPECT_TRUE(buffer_->Empty()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, + OnStreamDataAcrossLastBlockAndFillCapacity) { + QuicString source(kBlockSizeBytes + 50, 'a'); + // Write 1st block to full with 'a'. + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + // Read first 512 bytes from buffer to make space at the beginning. + char dest[512]{0}; + const iovec iov{dest, 512}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); + EXPECT_EQ(source.size(), written); + + // Write more than half block size of bytes in the last block with 'b', which + // will wrap to the beginning and reaches the full capacity. + source = QuicString(0.5 * kBlockSizeBytes + 512, 'b'); + EXPECT_EQ(QUIC_NO_ERROR, buffer_->OnStreamData(2 * kBlockSizeBytes, source, + &written, &error_details_)); + EXPECT_EQ(source.size(), written); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, + OnStreamDataAcrossLastBlockAndExceedCapacity) { + QuicString source(kBlockSizeBytes + 50, 'a'); + // Write 1st block to full. + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + // Read first 512 bytes from buffer to make space at the beginning. + char dest[512]{0}; + const iovec iov{dest, 512}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); + + // Try to write from [max_capacity_bytes_ - 0.5 * kBlockSizeBytes, + // max_capacity_bytes_ + 512 + 1). But last bytes exceeds current capacity. + source = QuicString(0.5 * kBlockSizeBytes + 512 + 1, 'b'); + EXPECT_EQ(QUIC_INTERNAL_ERROR, + buffer_->OnStreamData(2 * kBlockSizeBytes, source, &written, + &error_details_)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, ReadvAcrossLastBlock) { + // Write to full capacity and read out 512 bytes at beginning and continue + // appending 256 bytes. + QuicString source(max_capacity_bytes_, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[512]{0}; + const iovec iov{dest, 512}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); + source = QuicString(256, 'b'); + buffer_->OnStreamData(max_capacity_bytes_, source, &written, &error_details_); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + + // Read all data out. + std::unique_ptr<char[]> dest1{new char[max_capacity_bytes_]}; + dest1[0] = 0; + const iovec iov1{dest1.get(), max_capacity_bytes_}; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov1, 1, &read, &error_details_)); + EXPECT_EQ(max_capacity_bytes_ - 512 + 256, read); + EXPECT_EQ(max_capacity_bytes_ + 256, buffer_->BytesConsumed()); + EXPECT_TRUE(buffer_->Empty()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, ReadvEmpty) { + char dest[512]{0}; + iovec iov{dest, 512}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_)); + EXPECT_EQ(0u, read); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsEmpty) { + iovec iovs[2]; + int iov_count = buffer_->GetReadableRegions(iovs, 2); + EXPECT_EQ(0, iov_count); + EXPECT_EQ(nullptr, iovs[iov_count].iov_base); + EXPECT_EQ(0u, iovs[iov_count].iov_len); +} + +TEST_F(QuicStreamSequencerBufferTest, ReleaseWholeBuffer) { + // Tests that buffer is not deallocated unless ReleaseWholeBuffer() is called. + QuicString source(100, 'b'); + // Write something into [0, 100). + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + EXPECT_TRUE(buffer_->HasBytesToRead()); + char dest[120]; + iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, iovec{dest + 80, 40}}; + size_t read; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 3, &read, &error_details_)); + EXPECT_EQ(100u, read); + EXPECT_EQ(100u, buffer_->BytesConsumed()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + EXPECT_TRUE(helper_->IsBufferAllocated()); + buffer_->ReleaseWholeBuffer(); + EXPECT_FALSE(helper_->IsBufferAllocated()); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsBlockedByGap) { + // Write into [1, 1024). + QuicString source(1023, 'a'); + size_t written; + buffer_->OnStreamData(1, source, &written, &error_details_); + // Try to get readable regions, but none is there. + iovec iovs[2]; + int iov_count = buffer_->GetReadableRegions(iovs, 2); + EXPECT_EQ(0, iov_count); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsTillEndOfBlock) { + // Write first block to full with [0, 256) 'a' and the rest 'b' then read out + // [0, 256) + QuicString source(kBlockSizeBytes, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[256]; + helper_->Read(dest, 256); + // Get readable region from [256, 1024) + iovec iovs[2]; + int iov_count = buffer_->GetReadableRegions(iovs, 2); + EXPECT_EQ(1, iov_count); + EXPECT_EQ(QuicString(kBlockSizeBytes - 256, 'a'), + QuicString(reinterpret_cast<const char*>(iovs[0].iov_base), + iovs[0].iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsWithinOneBlock) { + // Write into [0, 1024) and then read out [0, 256) + QuicString source(1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[256]; + helper_->Read(dest, 256); + // Get readable region from [256, 1024) + iovec iovs[2]; + int iov_count = buffer_->GetReadableRegions(iovs, 2); + EXPECT_EQ(1, iov_count); + EXPECT_EQ(QuicString(1024 - 256, 'a'), + QuicString(reinterpret_cast<const char*>(iovs[0].iov_base), + iovs[0].iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, + GetReadableRegionsAcrossBlockWithLongIOV) { + // Write into [0, 2 * kBlockSizeBytes + 1024) and then read out [0, 1024) + QuicString source(2 * kBlockSizeBytes + 1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[1024]; + helper_->Read(dest, 1024); + + iovec iovs[4]; + int iov_count = buffer_->GetReadableRegions(iovs, 4); + EXPECT_EQ(3, iov_count); + EXPECT_EQ(kBlockSizeBytes - 1024, iovs[0].iov_len); + EXPECT_EQ(kBlockSizeBytes, iovs[1].iov_len); + EXPECT_EQ(1024u, iovs[2].iov_len); +} + +TEST_F(QuicStreamSequencerBufferTest, + GetReadableRegionsWithMultipleIOVsAcrossEnd) { + // Write into [0, 2 * kBlockSizeBytes + 1024) and then read out [0, 1024) + // and then append 1024 + 512 bytes. + QuicString source(2.5 * kBlockSizeBytes - 1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[1024]; + helper_->Read(dest, 1024); + // Write across the end. + source = QuicString(1024 + 512, 'b'); + buffer_->OnStreamData(2.5 * kBlockSizeBytes - 1024, source, &written, + &error_details_); + // Use short iovec's. + iovec iovs[2]; + int iov_count = buffer_->GetReadableRegions(iovs, 2); + EXPECT_EQ(2, iov_count); + EXPECT_EQ(kBlockSizeBytes - 1024, iovs[0].iov_len); + EXPECT_EQ(kBlockSizeBytes, iovs[1].iov_len); + // Use long iovec's and wrap the end of buffer. + iovec iovs1[5]; + EXPECT_EQ(4, buffer_->GetReadableRegions(iovs1, 5)); + EXPECT_EQ(0.5 * kBlockSizeBytes, iovs1[2].iov_len); + EXPECT_EQ(512u, iovs1[3].iov_len); + EXPECT_EQ(QuicString(512, 'b'), + QuicString(reinterpret_cast<const char*>(iovs1[3].iov_base), + iovs1[3].iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionEmpty) { + iovec iov; + EXPECT_FALSE(buffer_->GetReadableRegion(&iov)); + EXPECT_EQ(nullptr, iov.iov_base); + EXPECT_EQ(0u, iov.iov_len); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionBeforeGap) { + // Write into [1, 1024). + QuicString source(1023, 'a'); + size_t written; + buffer_->OnStreamData(1, source, &written, &error_details_); + // GetReadableRegion should return false because range [0,1) hasn't been + // filled yet. + iovec iov; + EXPECT_FALSE(buffer_->GetReadableRegion(&iov)); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionTillEndOfBlock) { + // Write into [0, kBlockSizeBytes + 1) and then read out [0, 256) + QuicString source(kBlockSizeBytes + 1, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[256]; + helper_->Read(dest, 256); + // Get readable region from [256, 1024) + iovec iov; + EXPECT_TRUE(buffer_->GetReadableRegion(&iov)); + EXPECT_EQ( + QuicString(kBlockSizeBytes - 256, 'a'), + QuicString(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionTillGap) { + // Write into [0, kBlockSizeBytes - 1) and then read out [0, 256) + QuicString source(kBlockSizeBytes - 1, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[256]; + helper_->Read(dest, 256); + // Get readable region from [256, 1023) + iovec iov; + EXPECT_TRUE(buffer_->GetReadableRegion(&iov)); + EXPECT_EQ( + QuicString(kBlockSizeBytes - 1 - 256, 'a'), + QuicString(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, PrefetchEmptyBuffer) { + iovec iov; + EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); +} + +TEST_F(QuicStreamSequencerBufferTest, PrefetchInitialBuffer) { + QuicString source(kBlockSizeBytes, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithOffset) { + QuicString source(1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); + // The second frame goes into the same bucket. + QuicString source2(800, 'a'); + buffer_->OnStreamData(1024, source2, &written, &error_details_); + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source2, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithMultipleBucket) { + const size_t data_size = 1024; + QuicString source(data_size, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); + QuicString source2(kBlockSizeBytes, 'b'); + buffer_->OnStreamData(data_size, source2, &written, &error_details_); + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ( + QuicString(kBlockSizeBytes - data_size, 'b'), + QuicString(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ( + QuicString(data_size, 'b'), + QuicString(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferAfterBlockRetired) { + QuicString source(kBlockSizeBytes, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); + // Read the whole block so it's retired. + char dest[kBlockSizeBytes]; + helper_->Read(dest, kBlockSizeBytes); + + QuicString source2(300, 'b'); + buffer_->OnStreamData(kBlockSizeBytes, source2, &written, &error_details_); + + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source2, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, PrefetchContinously) { + QuicString source(kBlockSizeBytes, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); + QuicString source2(kBlockSizeBytes, 'b'); + buffer_->OnStreamData(kBlockSizeBytes, source2, &written, &error_details_); + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(source2, QuicString(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, ConsumeMoreThanPrefetch) { + QuicString source(100, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[30]; + helper_->Read(dest, 30); + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ( + QuicString(70, 'a'), + QuicString(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); + QuicString source2(100, 'b'); + buffer_->OnStreamData(100, source2, &written, &error_details_); + buffer_->MarkConsumed(100); + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ( + QuicString(70, 'b'), + QuicString(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); +} + +TEST_F(QuicStreamSequencerBufferTest, PrefetchMoreThanBufferHas) { + QuicString source(100, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ( + QuicString(100, 'a'), + QuicString(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len)); + EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); +} + +TEST_F(QuicStreamSequencerBufferTest, MarkConsumedInOneBlock) { + // Write into [0, 1024) and then read out [0, 256) + QuicString source(1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[256]; + helper_->Read(dest, 256); + + EXPECT_TRUE(buffer_->MarkConsumed(512)); + EXPECT_EQ(256u + 512u, buffer_->BytesConsumed()); + EXPECT_EQ(256u, helper_->ReadableBytes()); + buffer_->MarkConsumed(256); + EXPECT_TRUE(buffer_->Empty()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, MarkConsumedNotEnoughBytes) { + // Write into [0, 1024) and then read out [0, 256) + QuicString source(1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[256]; + helper_->Read(dest, 256); + + // Consume 1st 512 bytes + EXPECT_TRUE(buffer_->MarkConsumed(512)); + EXPECT_EQ(256u + 512u, buffer_->BytesConsumed()); + EXPECT_EQ(256u, helper_->ReadableBytes()); + // Try to consume one bytes more than available. Should return false. + EXPECT_FALSE(buffer_->MarkConsumed(257)); + EXPECT_EQ(256u + 512u, buffer_->BytesConsumed()); + iovec iov; + EXPECT_TRUE(buffer_->GetReadableRegion(&iov)); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, MarkConsumedAcrossBlock) { + // Write into [0, 2 * kBlockSizeBytes + 1024) and then read out [0, 1024) + QuicString source(2 * kBlockSizeBytes + 1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[1024]; + helper_->Read(dest, 1024); + + buffer_->MarkConsumed(2 * kBlockSizeBytes); + EXPECT_EQ(source.size(), buffer_->BytesConsumed()); + EXPECT_TRUE(buffer_->Empty()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, MarkConsumedAcrossEnd) { + // Write into [0, 2.5 * kBlockSizeBytes - 1024) and then read out [0, 1024) + // and then append 1024 + 512 bytes. + QuicString source(2.5 * kBlockSizeBytes - 1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[1024]; + helper_->Read(dest, 1024); + source = QuicString(1024 + 512, 'b'); + buffer_->OnStreamData(2.5 * kBlockSizeBytes - 1024, source, &written, + &error_details_); + EXPECT_EQ(1024u, buffer_->BytesConsumed()); + + // Consume to the end of 2nd block. + buffer_->MarkConsumed(2 * kBlockSizeBytes - 1024); + EXPECT_EQ(2 * kBlockSizeBytes, buffer_->BytesConsumed()); + // Consume across the physical end of buffer + buffer_->MarkConsumed(0.5 * kBlockSizeBytes + 500); + EXPECT_EQ(max_capacity_bytes_ + 500, buffer_->BytesConsumed()); + EXPECT_EQ(12u, helper_->ReadableBytes()); + // Consume to the logical end of buffer + buffer_->MarkConsumed(12); + EXPECT_EQ(max_capacity_bytes_ + 512, buffer_->BytesConsumed()); + EXPECT_TRUE(buffer_->Empty()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, FlushBufferedFrames) { + // Write into [0, 2.5 * kBlockSizeBytes - 1024) and then read out [0, 1024). + QuicString source(max_capacity_bytes_ - 1024, 'a'); + size_t written; + buffer_->OnStreamData(0, source, &written, &error_details_); + char dest[1024]; + helper_->Read(dest, 1024); + EXPECT_EQ(1024u, buffer_->BytesConsumed()); + // Write [1024, 512) to the physical beginning. + source = QuicString(512, 'b'); + buffer_->OnStreamData(max_capacity_bytes_, source, &written, &error_details_); + EXPECT_EQ(512u, written); + EXPECT_EQ(max_capacity_bytes_ - 1024 + 512, buffer_->FlushBufferedFrames()); + EXPECT_EQ(max_capacity_bytes_ + 512, buffer_->BytesConsumed()); + EXPECT_TRUE(buffer_->Empty()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); + // Clear buffer at this point should still preserve BytesConsumed(). + buffer_->Clear(); + EXPECT_EQ(max_capacity_bytes_ + 512, buffer_->BytesConsumed()); + EXPECT_TRUE(helper_->CheckBufferInvariants()); +} + +TEST_F(QuicStreamSequencerBufferTest, TooManyGaps) { + // Make sure max capacity is large enough that it is possible to have more + // than |kMaxNumGapsAllowed| number of gaps. + max_capacity_bytes_ = 3 * kBlockSizeBytes; + // Feed buffer with 1-byte discontiguous frames. e.g. [1,2), [3,4), [5,6)... + for (QuicStreamOffset begin = 1; begin <= max_capacity_bytes_; begin += 2) { + size_t written; + QuicErrorCode rs = + buffer_->OnStreamData(begin, "a", &written, &error_details_); + + QuicStreamOffset last_straw = 2 * kMaxNumGapsAllowed - 1; + if (begin == last_straw) { + EXPECT_EQ(QUIC_TOO_MANY_STREAM_DATA_INTERVALS, rs); + EXPECT_EQ("Too many data intervals received for this stream.", + error_details_); + break; + } + } +} + +class QuicStreamSequencerBufferRandomIOTest + : public QuicStreamSequencerBufferTest { + public: + typedef std::pair<QuicStreamOffset, size_t> OffsetSizePair; + + void SetUp() override { + // Test against a larger capacity then above tests. Also make sure the last + // block is partially available to use. + max_capacity_bytes_ = 6.25 * kBlockSizeBytes; + // Stream to be buffered should be larger than the capacity to test wrap + // around. + bytes_to_buffer_ = 2 * max_capacity_bytes_; + Initialize(); + + uint64_t seed = QuicRandom::GetInstance()->RandUint64(); + QUIC_LOG(INFO) << "**** The current seed is " << seed << " ****"; + rng_.set_seed(seed); + } + + // Create an out-of-order source stream with given size to populate + // shuffled_buf_. + void CreateSourceAndShuffle(size_t max_chunk_size_bytes) { + max_chunk_size_bytes_ = max_chunk_size_bytes; + std::unique_ptr<OffsetSizePair[]> chopped_stream( + new OffsetSizePair[bytes_to_buffer_]); + + // Split stream into small chunks with random length. chopped_stream will be + // populated with segmented stream chunks. + size_t start_chopping_offset = 0; + size_t iterations = 0; + while (start_chopping_offset < bytes_to_buffer_) { + size_t max_chunk = std::min<size_t>( + max_chunk_size_bytes_, bytes_to_buffer_ - start_chopping_offset); + size_t chunk_size = rng_.RandUint64() % max_chunk + 1; + chopped_stream[iterations] = + OffsetSizePair(start_chopping_offset, chunk_size); + start_chopping_offset += chunk_size; + ++iterations; + } + DCHECK(start_chopping_offset == bytes_to_buffer_); + size_t chunk_num = iterations; + + // Randomly change the sequence of in-ordered OffsetSizePairs to make a + // out-of-order array of OffsetSizePairs. + for (int i = chunk_num - 1; i >= 0; --i) { + size_t random_idx = rng_.RandUint64() % (i + 1); + QUIC_DVLOG(1) << "chunk offset " << chopped_stream[random_idx].first + << " size " << chopped_stream[random_idx].second; + shuffled_buf_.push_front(chopped_stream[random_idx]); + chopped_stream[random_idx] = chopped_stream[i]; + } + } + + // Write the currently first chunk of data in the out-of-order stream into + // QuicStreamSequencerBuffer. If current chuck cannot be written into buffer + // because it goes beyond current capacity, move it to the end of + // shuffled_buf_ and write it later. + void WriteNextChunkToBuffer() { + OffsetSizePair& chunk = shuffled_buf_.front(); + QuicStreamOffset offset = chunk.first; + const size_t num_to_write = chunk.second; + std::unique_ptr<char[]> write_buf{new char[max_chunk_size_bytes_]}; + for (size_t i = 0; i < num_to_write; ++i) { + write_buf[i] = (offset + i) % 256; + } + QuicStringPiece string_piece_w(write_buf.get(), num_to_write); + size_t written; + auto result = buffer_->OnStreamData(offset, string_piece_w, &written, + &error_details_); + if (result == QUIC_NO_ERROR) { + shuffled_buf_.pop_front(); + total_bytes_written_ += num_to_write; + } else { + // This chunk offset exceeds window size. + shuffled_buf_.push_back(chunk); + shuffled_buf_.pop_front(); + } + QUIC_DVLOG(1) << " write at offset: " << offset + << " len to write: " << num_to_write + << " write result: " << result + << " left over: " << shuffled_buf_.size(); + } + + protected: + std::list<OffsetSizePair> shuffled_buf_; + size_t max_chunk_size_bytes_; + QuicStreamOffset bytes_to_buffer_; + size_t total_bytes_written_ = 0; + size_t total_bytes_read_ = 0; + SimpleRandom rng_; +}; + +TEST_F(QuicStreamSequencerBufferRandomIOTest, RandomWriteAndReadv) { + // Set kMaxReadSize larger than kBlockSizeBytes to test both small and large + // read. + const size_t kMaxReadSize = kBlockSizeBytes * 2; + // kNumReads is larger than 1 to test how multiple read destinations work. + const size_t kNumReads = 2; + // Since write and read operation have equal possibility to be called. Bytes + // to be written into and read out of should roughly the same. + const size_t kMaxWriteSize = kNumReads * kMaxReadSize; + size_t iterations = 0; + + CreateSourceAndShuffle(kMaxWriteSize); + + while ((!shuffled_buf_.empty() || total_bytes_read_ < bytes_to_buffer_) && + iterations <= 2 * bytes_to_buffer_) { + uint8_t next_action = + shuffled_buf_.empty() ? uint8_t{1} : rng_.RandUint64() % 2; + QUIC_DVLOG(1) << "iteration: " << iterations; + switch (next_action) { + case 0: { // write + WriteNextChunkToBuffer(); + ASSERT_TRUE(helper_->CheckBufferInvariants()); + break; + } + case 1: { // readv + std::unique_ptr<char[][kMaxReadSize]> read_buf{ + new char[kNumReads][kMaxReadSize]}; + iovec dest_iov[kNumReads]; + size_t num_to_read = 0; + for (size_t i = 0; i < kNumReads; ++i) { + dest_iov[i].iov_base = + reinterpret_cast<void*>(const_cast<char*>(read_buf[i])); + dest_iov[i].iov_len = rng_.RandUint64() % kMaxReadSize; + num_to_read += dest_iov[i].iov_len; + } + size_t actually_read; + EXPECT_EQ(QUIC_NO_ERROR, + buffer_->Readv(dest_iov, kNumReads, &actually_read, + &error_details_)); + ASSERT_LE(actually_read, num_to_read); + QUIC_DVLOG(1) << " read from offset: " << total_bytes_read_ + << " size: " << num_to_read + << " actual read: " << actually_read; + for (size_t i = 0; i < actually_read; ++i) { + char ch = (i + total_bytes_read_) % 256; + ASSERT_EQ(ch, GetCharFromIOVecs(i, dest_iov, kNumReads)) + << " at iteration " << iterations; + } + total_bytes_read_ += actually_read; + ASSERT_EQ(total_bytes_read_, buffer_->BytesConsumed()); + ASSERT_TRUE(helper_->CheckBufferInvariants()); + break; + } + } + ++iterations; + ASSERT_LE(total_bytes_read_, total_bytes_written_); + } + EXPECT_LT(iterations, bytes_to_buffer_) << "runaway test"; + EXPECT_LE(bytes_to_buffer_, total_bytes_read_) + << "iterations: " << iterations; + EXPECT_LE(bytes_to_buffer_, total_bytes_written_); +} + +TEST_F(QuicStreamSequencerBufferRandomIOTest, RandomWriteAndConsumeInPlace) { + // The value 4 is chosen such that the max write size is no larger than the + // maximum buffer capacity. + const size_t kMaxNumReads = 4; + // Adjust write amount be roughly equal to that GetReadableRegions() can get. + const size_t kMaxWriteSize = kMaxNumReads * kBlockSizeBytes; + ASSERT_LE(kMaxWriteSize, max_capacity_bytes_); + size_t iterations = 0; + + CreateSourceAndShuffle(kMaxWriteSize); + + while ((!shuffled_buf_.empty() || total_bytes_read_ < bytes_to_buffer_) && + iterations <= 2 * bytes_to_buffer_) { + uint8_t next_action = + shuffled_buf_.empty() ? uint8_t{1} : rng_.RandUint64() % 2; + QUIC_DVLOG(1) << "iteration: " << iterations; + switch (next_action) { + case 0: { // write + WriteNextChunkToBuffer(); + ASSERT_TRUE(helper_->CheckBufferInvariants()); + break; + } + case 1: { // GetReadableRegions and then MarkConsumed + size_t num_read = rng_.RandUint64() % kMaxNumReads + 1; + iovec dest_iov[kMaxNumReads]; + ASSERT_TRUE(helper_->CheckBufferInvariants()); + size_t actually_num_read = + buffer_->GetReadableRegions(dest_iov, num_read); + ASSERT_LE(actually_num_read, num_read); + size_t avail_bytes = 0; + for (size_t i = 0; i < actually_num_read; ++i) { + avail_bytes += dest_iov[i].iov_len; + } + // process random number of bytes (check the value of each byte). + size_t bytes_to_process = rng_.RandUint64() % (avail_bytes + 1); + size_t bytes_processed = 0; + for (size_t i = 0; i < actually_num_read; ++i) { + size_t bytes_in_block = std::min<size_t>( + bytes_to_process - bytes_processed, dest_iov[i].iov_len); + if (bytes_in_block == 0) { + break; + } + for (size_t j = 0; j < bytes_in_block; ++j) { + ASSERT_LE(bytes_processed, bytes_to_process); + char char_expected = + (buffer_->BytesConsumed() + bytes_processed) % 256; + ASSERT_EQ(char_expected, + reinterpret_cast<const char*>(dest_iov[i].iov_base)[j]) + << " at iteration " << iterations; + ++bytes_processed; + } + } + + buffer_->MarkConsumed(bytes_processed); + + QUIC_DVLOG(1) << "iteration " << iterations << ": try to get " + << num_read << " readable regions, actually get " + << actually_num_read + << " from offset: " << total_bytes_read_ + << "\nprocesse bytes: " << bytes_processed; + total_bytes_read_ += bytes_processed; + ASSERT_EQ(total_bytes_read_, buffer_->BytesConsumed()); + ASSERT_TRUE(helper_->CheckBufferInvariants()); + break; + } + } + ++iterations; + ASSERT_LE(total_bytes_read_, total_bytes_written_); + } + EXPECT_LT(iterations, bytes_to_buffer_) << "runaway test"; + EXPECT_LE(bytes_to_buffer_, total_bytes_read_) + << "iterations: " << iterations; + EXPECT_LE(bytes_to_buffer_, total_bytes_written_); +} + +} // anonymous namespace + +} // namespace test + +} // namespace quic
diff --git a/quic/core/quic_stream_sequencer_test.cc b/quic/core/quic_stream_sequencer_test.cc new file mode 100644 index 0000000..989b0f6 --- /dev/null +++ b/quic/core/quic_stream_sequencer_test.cc
@@ -0,0 +1,763 @@ +// Copyright (c) 2012 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/quic_stream_sequencer.h" + +#include <algorithm> +#include <cstdint> +#include <memory> +#include <utility> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::AnyNumber; +using testing::InSequence; + +namespace quic { +namespace test { + +class MockStream : public QuicStreamSequencer::StreamInterface { + public: + MOCK_METHOD0(OnFinRead, void()); + MOCK_METHOD0(OnDataAvailable, void()); + MOCK_METHOD2(CloseConnectionWithDetails, + void(QuicErrorCode error, const QuicString& details)); + MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error)); + MOCK_METHOD0(OnCanWrite, void()); + MOCK_METHOD1(AddBytesConsumed, void(QuicByteCount bytes)); + + QuicStreamId id() const override { return 1; } + + const QuicSocketAddress& PeerAddressOfLatestPacket() const override { + return peer_address_; + } + + protected: + QuicSocketAddress peer_address_ = + QuicSocketAddress(QuicIpAddress::Any4(), 65535); +}; + +namespace { + +static const char kPayload[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +class QuicStreamSequencerTest : public QuicTest { + public: + void ConsumeData(size_t num_bytes) { + char buffer[1024]; + ASSERT_GT(QUIC_ARRAYSIZE(buffer), num_bytes); + struct iovec iov; + iov.iov_base = buffer; + iov.iov_len = num_bytes; + ASSERT_EQ(static_cast<int>(num_bytes), sequencer_->Readv(&iov, 1)); + } + + protected: + QuicStreamSequencerTest() + : stream_(), sequencer_(new QuicStreamSequencer(&stream_)) {} + + // Verify that the data in first region match with the expected[0]. + bool VerifyReadableRegion(const std::vector<QuicString>& expected) { + return VerifyReadableRegion(*sequencer_, expected); + } + + // Verify that the data in each of currently readable regions match with each + // item given in |expected|. + bool VerifyReadableRegions(const std::vector<QuicString>& expected) { + return VerifyReadableRegions(*sequencer_, expected); + } + + bool VerifyIovecs(iovec* iovecs, + size_t num_iovecs, + const std::vector<QuicString>& expected) { + return VerifyIovecs(*sequencer_, iovecs, num_iovecs, expected); + } + + bool VerifyReadableRegion(const QuicStreamSequencer& sequencer, + const std::vector<QuicString>& expected) { + iovec iovecs[1]; + if (sequencer.GetReadableRegions(iovecs, 1)) { + return (VerifyIovecs(sequencer, iovecs, 1, + std::vector<QuicString>{expected[0]})); + } + return false; + } + + // Verify that the data in each of currently readable regions match with each + // item given in |expected|. + bool VerifyReadableRegions(const QuicStreamSequencer& sequencer, + const std::vector<QuicString>& expected) { + iovec iovecs[5]; + size_t num_iovecs = + sequencer.GetReadableRegions(iovecs, QUIC_ARRAYSIZE(iovecs)); + return VerifyReadableRegion(sequencer, expected) && + VerifyIovecs(sequencer, iovecs, num_iovecs, expected); + } + + bool VerifyIovecs(const QuicStreamSequencer& sequencer, + iovec* iovecs, + size_t num_iovecs, + const std::vector<QuicString>& expected) { + int start_position = 0; + for (size_t i = 0; i < num_iovecs; ++i) { + if (!VerifyIovec(iovecs[i], + expected[0].substr(start_position, iovecs[i].iov_len))) { + return false; + } + start_position += iovecs[i].iov_len; + } + return true; + } + + bool VerifyIovec(const iovec& iovec, QuicStringPiece expected) { + if (iovec.iov_len != expected.length()) { + QUIC_LOG(ERROR) << "Invalid length: " << iovec.iov_len << " vs " + << expected.length(); + return false; + } + if (memcmp(iovec.iov_base, expected.data(), expected.length()) != 0) { + QUIC_LOG(ERROR) << "Invalid data: " << static_cast<char*>(iovec.iov_base) + << " vs " << expected; + return false; + } + return true; + } + + void OnFinFrame(QuicStreamOffset byte_offset, const char* data) { + QuicStreamFrame frame; + frame.stream_id = 1; + frame.offset = byte_offset; + frame.data_buffer = data; + frame.data_length = strlen(data); + frame.fin = true; + sequencer_->OnStreamFrame(frame); + } + + void OnFrame(QuicStreamOffset byte_offset, const char* data) { + QuicStreamFrame frame; + frame.stream_id = 1; + frame.offset = byte_offset; + frame.data_buffer = data; + frame.data_length = strlen(data); + frame.fin = false; + sequencer_->OnStreamFrame(frame); + } + + size_t NumBufferedBytes() { + return QuicStreamSequencerPeer::GetNumBufferedBytes(sequencer_.get()); + } + + testing::StrictMock<MockStream> stream_; + std::unique_ptr<QuicStreamSequencer> sequencer_; +}; + +// TODO(rch): reorder these tests so they build on each other. + +TEST_F(QuicStreamSequencerTest, RejectOldFrame) { + EXPECT_CALL(stream_, AddBytesConsumed(3)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(3); + })); + + OnFrame(0, "abc"); + + EXPECT_EQ(0u, NumBufferedBytes()); + EXPECT_EQ(3u, sequencer_->NumBytesConsumed()); + // Ignore this - it matches a past packet number and we should not see it + // again. + OnFrame(0, "def"); + EXPECT_EQ(0u, NumBufferedBytes()); +} + +TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) { + EXPECT_CALL(stream_, OnDataAvailable()); + + OnFrame(0, "abc"); + EXPECT_EQ(3u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); + + // Ignore this - it matches a buffered frame. + // Right now there's no checking that the payload is consistent. + OnFrame(0, "def"); + EXPECT_EQ(3u, NumBufferedBytes()); +} + +TEST_F(QuicStreamSequencerTest, FullFrameConsumed) { + EXPECT_CALL(stream_, AddBytesConsumed(3)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(3); + })); + + OnFrame(0, "abc"); + EXPECT_EQ(0u, NumBufferedBytes()); + EXPECT_EQ(3u, sequencer_->NumBytesConsumed()); +} + +TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) { + sequencer_->SetBlockedUntilFlush(); + + OnFrame(0, "abc"); + EXPECT_EQ(3u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); + + EXPECT_CALL(stream_, AddBytesConsumed(3)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(3); + })); + sequencer_->SetUnblocked(); + EXPECT_EQ(0u, NumBufferedBytes()); + EXPECT_EQ(3u, sequencer_->NumBytesConsumed()); + + EXPECT_CALL(stream_, AddBytesConsumed(3)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(3); + })); + EXPECT_FALSE(sequencer_->IsClosed()); + OnFinFrame(3, "def"); + EXPECT_TRUE(sequencer_->IsClosed()); +} + +TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) { + sequencer_->SetBlockedUntilFlush(); + + OnFinFrame(0, "abc"); + EXPECT_EQ(3u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); + + EXPECT_CALL(stream_, AddBytesConsumed(3)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(3); + })); + EXPECT_FALSE(sequencer_->IsClosed()); + sequencer_->SetUnblocked(); + EXPECT_TRUE(sequencer_->IsClosed()); + EXPECT_EQ(0u, NumBufferedBytes()); + EXPECT_EQ(3u, sequencer_->NumBytesConsumed()); +} + +TEST_F(QuicStreamSequencerTest, EmptyFrame) { + EXPECT_CALL(stream_, + CloseConnectionWithDetails(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _)); + OnFrame(0, ""); + EXPECT_EQ(0u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); +} + +TEST_F(QuicStreamSequencerTest, EmptyFinFrame) { + EXPECT_CALL(stream_, OnDataAvailable()); + OnFinFrame(0, ""); + EXPECT_EQ(0u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); +} + +TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) { + EXPECT_CALL(stream_, AddBytesConsumed(2)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(2); + })); + + OnFrame(0, "abc"); + EXPECT_EQ(1u, NumBufferedBytes()); + EXPECT_EQ(2u, sequencer_->NumBytesConsumed()); +} + +TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) { + EXPECT_CALL(stream_, OnDataAvailable()); + + OnFrame(0, "abc"); + EXPECT_EQ(3u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); +} + +TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) { + OnFrame(3, "abc"); + EXPECT_EQ(3u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); +} + +TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { + // Buffer the first + OnFrame(6, "ghi"); + EXPECT_EQ(3u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); + EXPECT_EQ(3u, sequencer_->NumBytesBuffered()); + // Buffer the second + OnFrame(3, "def"); + EXPECT_EQ(6u, NumBufferedBytes()); + EXPECT_EQ(0u, sequencer_->NumBytesConsumed()); + EXPECT_EQ(6u, sequencer_->NumBytesBuffered()); + + EXPECT_CALL(stream_, AddBytesConsumed(9)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(9); + })); + + // Now process all of them at once. + OnFrame(0, "abc"); + EXPECT_EQ(9u, sequencer_->NumBytesConsumed()); + EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); + + EXPECT_EQ(0u, NumBufferedBytes()); +} + +TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) { + InSequence s; + + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(3); + })); + EXPECT_CALL(stream_, AddBytesConsumed(3)); + OnFinFrame(0, "abc"); + + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); +} + +TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) { + OnFinFrame(6, ""); + EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); + + OnFrame(3, "def"); + EXPECT_CALL(stream_, AddBytesConsumed(6)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(6); + })); + EXPECT_FALSE(sequencer_->IsClosed()); + OnFrame(0, "abc"); + EXPECT_TRUE(sequencer_->IsClosed()); +} + +TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) { + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); + + EXPECT_CALL(stream_, AddBytesConsumed(3)); + EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() { + ConsumeData(3); + })); + EXPECT_FALSE(sequencer_->IsClosed()); + OnFrame(0, "abc"); + EXPECT_TRUE(sequencer_->IsClosed()); +} + +TEST_F(QuicStreamSequencerTest, TerminateWithReadv) { + char buffer[3]; + + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); + + EXPECT_FALSE(sequencer_->IsClosed()); + + EXPECT_CALL(stream_, OnDataAvailable()); + OnFrame(0, "abc"); + + EXPECT_CALL(stream_, AddBytesConsumed(3)); + iovec iov = {&buffer[0], 3}; + int bytes_read = sequencer_->Readv(&iov, 1); + EXPECT_EQ(3, bytes_read); + EXPECT_TRUE(sequencer_->IsClosed()); +} + +TEST_F(QuicStreamSequencerTest, MutipleOffsets) { + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); + + EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS)); + OnFinFrame(5, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); + + EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS)); + OnFinFrame(1, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); + + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); +} + +class QuicSequencerRandomTest : public QuicStreamSequencerTest { + public: + typedef std::pair<int, QuicString> Frame; + typedef std::vector<Frame> FrameList; + + void CreateFrames() { + int payload_size = QUIC_ARRAYSIZE(kPayload) - 1; + int remaining_payload = payload_size; + while (remaining_payload != 0) { + int size = std::min(OneToN(6), remaining_payload); + int index = payload_size - remaining_payload; + list_.push_back( + std::make_pair(index, QuicString(kPayload + index, size))); + remaining_payload -= size; + } + } + + QuicSequencerRandomTest() { + uint64_t seed = QuicRandom::GetInstance()->RandUint64(); + QUIC_LOG(INFO) << "**** The current seed is " << seed << " ****"; + random_.set_seed(seed); + + CreateFrames(); + } + + int OneToN(int n) { return random_.RandUint64() % n + 1; } + + void ReadAvailableData() { + // Read all available data + char output[QUIC_ARRAYSIZE(kPayload) + 1]; + iovec iov; + iov.iov_base = output; + iov.iov_len = QUIC_ARRAYSIZE(output); + int bytes_read = sequencer_->Readv(&iov, 1); + EXPECT_NE(0, bytes_read); + output_.append(output, bytes_read); + } + + QuicString output_; + // Data which peek at using GetReadableRegion if we back up. + QuicString peeked_; + SimpleRandom random_; + FrameList list_; +}; + +// All frames are processed as soon as we have sequential data. +// Infinite buffering, so all frames are acked right away. +TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) { + EXPECT_CALL(stream_, OnDataAvailable()) + .Times(AnyNumber()) + .WillRepeatedly( + Invoke(this, &QuicSequencerRandomTest::ReadAvailableData)); + QuicByteCount total_bytes_consumed = 0; + EXPECT_CALL(stream_, AddBytesConsumed(_)) + .Times(AnyNumber()) + .WillRepeatedly( + testing::Invoke([&total_bytes_consumed](QuicByteCount bytes) { + total_bytes_consumed += bytes; + })); + + while (!list_.empty()) { + int index = OneToN(list_.size()) - 1; + QUIC_LOG(ERROR) << "Sending index " << index << " " << list_[index].second; + OnFrame(list_[index].first, list_[index].second.data()); + + list_.erase(list_.begin() + index); + } + + ASSERT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, output_.size()); + EXPECT_EQ(kPayload, output_); + EXPECT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, total_bytes_consumed); +} + +TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingBackup) { + char buffer[10]; + iovec iov[2]; + iov[0].iov_base = &buffer[0]; + iov[0].iov_len = 5; + iov[1].iov_base = &buffer[5]; + iov[1].iov_len = 5; + + EXPECT_CALL(stream_, OnDataAvailable()).Times(AnyNumber()); + QuicByteCount total_bytes_consumed = 0; + EXPECT_CALL(stream_, AddBytesConsumed(_)) + .Times(AnyNumber()) + .WillRepeatedly( + testing::Invoke([&total_bytes_consumed](QuicByteCount bytes) { + total_bytes_consumed += bytes; + })); + + while (output_.size() != QUIC_ARRAYSIZE(kPayload) - 1) { + if (!list_.empty() && OneToN(2) == 1) { // Send data + int index = OneToN(list_.size()) - 1; + OnFrame(list_[index].first, list_[index].second.data()); + list_.erase(list_.begin() + index); + } else { // Read data + bool has_bytes = sequencer_->HasBytesToRead(); + iovec peek_iov[20]; + int iovs_peeked = sequencer_->GetReadableRegions(peek_iov, 20); + if (has_bytes) { + ASSERT_LT(0, iovs_peeked); + ASSERT_TRUE(sequencer_->GetReadableRegion(peek_iov)); + } else { + ASSERT_EQ(0, iovs_peeked); + ASSERT_FALSE(sequencer_->GetReadableRegion(peek_iov)); + } + int total_bytes_to_peek = QUIC_ARRAYSIZE(buffer); + for (int i = 0; i < iovs_peeked; ++i) { + int bytes_to_peek = + std::min<int>(peek_iov[i].iov_len, total_bytes_to_peek); + peeked_.append(static_cast<char*>(peek_iov[i].iov_base), bytes_to_peek); + total_bytes_to_peek -= bytes_to_peek; + if (total_bytes_to_peek == 0) { + break; + } + } + int bytes_read = sequencer_->Readv(iov, 2); + output_.append(buffer, bytes_read); + ASSERT_EQ(output_.size(), peeked_.size()); + } + } + EXPECT_EQ(QuicString(kPayload), output_); + EXPECT_EQ(QuicString(kPayload), peeked_); + EXPECT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, total_bytes_consumed); +} + +// Same as above, just using a different method for reading. +TEST_F(QuicStreamSequencerTest, MarkConsumed) { + InSequence s; + EXPECT_CALL(stream_, OnDataAvailable()); + + OnFrame(0, "abc"); + OnFrame(3, "def"); + OnFrame(6, "ghi"); + + // abcdefghi buffered. + EXPECT_EQ(9u, sequencer_->NumBytesBuffered()); + + // Peek into the data. + std::vector<QuicString> expected = {"abcdefghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected)); + + // Consume 1 byte. + EXPECT_CALL(stream_, AddBytesConsumed(1)); + sequencer_->MarkConsumed(1); + // Verify data. + std::vector<QuicString> expected2 = {"bcdefghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected2)); + EXPECT_EQ(8u, sequencer_->NumBytesBuffered()); + + // Consume 2 bytes. + EXPECT_CALL(stream_, AddBytesConsumed(2)); + sequencer_->MarkConsumed(2); + // Verify data. + std::vector<QuicString> expected3 = {"defghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected3)); + EXPECT_EQ(6u, sequencer_->NumBytesBuffered()); + + // Consume 5 bytes. + EXPECT_CALL(stream_, AddBytesConsumed(5)); + sequencer_->MarkConsumed(5); + // Verify data. + std::vector<QuicString> expected4{"i"}; + ASSERT_TRUE(VerifyReadableRegions(expected4)); + EXPECT_EQ(1u, sequencer_->NumBytesBuffered()); +} + +TEST_F(QuicStreamSequencerTest, MarkConsumedError) { + EXPECT_CALL(stream_, OnDataAvailable()); + + OnFrame(0, "abc"); + OnFrame(9, "jklmnopqrstuvwxyz"); + + // Peek into the data. Only the first chunk should be readable because of the + // missing data. + std::vector<QuicString> expected{"abc"}; + ASSERT_TRUE(VerifyReadableRegions(expected)); + + // Now, attempt to mark consumed more data than was readable and expect the + // stream to be closed. + EXPECT_CALL(stream_, Reset(QUIC_ERROR_PROCESSING_STREAM)); + EXPECT_QUIC_BUG(sequencer_->MarkConsumed(4), + "Invalid argument to MarkConsumed." + " expect to consume: 4, but not enough bytes available."); +} + +TEST_F(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) { + InSequence s; + EXPECT_CALL(stream_, OnDataAvailable()); + + OnFrame(0, "abc"); + OnFrame(3, "def"); + // Missing packet: 6, ghi. + OnFrame(9, "jkl"); + + std::vector<QuicString> expected = {"abcdef"}; + ASSERT_TRUE(VerifyReadableRegions(expected)); + + EXPECT_CALL(stream_, AddBytesConsumed(6)); + sequencer_->MarkConsumed(6); +} + +TEST_F(QuicStreamSequencerTest, Move) { + InSequence s; + EXPECT_CALL(stream_, OnDataAvailable()); + + OnFrame(0, "abc"); + OnFrame(3, "def"); + OnFrame(6, "ghi"); + + // abcdefghi buffered. + EXPECT_EQ(9u, sequencer_->NumBytesBuffered()); + + // Peek into the data. + std::vector<QuicString> expected = {"abcdefghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected)); + + QuicStreamSequencer sequencer2(std::move(*sequencer_)); + ASSERT_TRUE(VerifyReadableRegions(sequencer2, expected)); +} + +TEST_F(QuicStreamSequencerTest, OverlappingFramesReceived) { + // The peer should never send us non-identical stream frames which contain + // overlapping byte ranges - if they do, we close the connection. + QuicStreamId id = 1; + + QuicStreamFrame frame1(id, false, 1, QuicStringPiece("hello")); + sequencer_->OnStreamFrame(frame1); + + QuicStreamFrame frame2(id, false, 2, QuicStringPiece("hello")); + EXPECT_CALL(stream_, + CloseConnectionWithDetails(QUIC_OVERLAPPING_STREAM_DATA, _)) + .Times(0); + sequencer_->OnStreamFrame(frame2); +} + +TEST_F(QuicStreamSequencerTest, DataAvailableOnOverlappingFrames) { + QuicStreamId id = 1; + const QuicString data(1000, '.'); + + // Received [0, 1000). + QuicStreamFrame frame1(id, false, 0, data); + EXPECT_CALL(stream_, OnDataAvailable()); + sequencer_->OnStreamFrame(frame1); + // Consume [0, 500). + EXPECT_CALL(stream_, AddBytesConsumed(500)); + QuicStreamSequencerTest::ConsumeData(500); + EXPECT_EQ(500u, sequencer_->NumBytesConsumed()); + EXPECT_EQ(500u, sequencer_->NumBytesBuffered()); + + // Received [500, 1500). + QuicStreamFrame frame2(id, false, 500, data); + // Do not call OnDataAvailable as there are readable bytes left in the buffer. + EXPECT_CALL(stream_, OnDataAvailable()).Times(0); + sequencer_->OnStreamFrame(frame2); + // Consume [1000, 1500). + EXPECT_CALL(stream_, AddBytesConsumed(1000)); + QuicStreamSequencerTest::ConsumeData(1000); + EXPECT_EQ(1500u, sequencer_->NumBytesConsumed()); + EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); + + // Received [1498, 1503). + QuicStreamFrame frame3(id, false, 1498, QuicStringPiece("hello")); + EXPECT_CALL(stream_, OnDataAvailable()); + sequencer_->OnStreamFrame(frame3); + EXPECT_CALL(stream_, AddBytesConsumed(3)); + QuicStreamSequencerTest::ConsumeData(3); + EXPECT_EQ(1503u, sequencer_->NumBytesConsumed()); + EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); + + // Received [1000, 1005). + QuicStreamFrame frame4(id, false, 1000, QuicStringPiece("hello")); + EXPECT_CALL(stream_, OnDataAvailable()).Times(0); + sequencer_->OnStreamFrame(frame4); + EXPECT_EQ(1503u, sequencer_->NumBytesConsumed()); + EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); +} + +TEST_F(QuicStreamSequencerTest, OnDataAvailableWhenReadableBytesIncrease) { + sequencer_->set_level_triggered(true); + QuicStreamId id = 1; + + // Received [0, 5). + QuicStreamFrame frame1(id, false, 0, "hello"); + EXPECT_CALL(stream_, OnDataAvailable()); + sequencer_->OnStreamFrame(frame1); + EXPECT_EQ(5u, sequencer_->NumBytesBuffered()); + + // Without consuming the buffer bytes, continue receiving [5, 11). + QuicStreamFrame frame2(id, false, 5, " world"); + // OnDataAvailable should still be called because there are more data to read. + EXPECT_CALL(stream_, OnDataAvailable()); + sequencer_->OnStreamFrame(frame2); + EXPECT_EQ(11u, sequencer_->NumBytesBuffered()); + + // Without consuming the buffer bytes, continue receiving [12, 13). + QuicStreamFrame frame3(id, false, 5, "a"); + // OnDataAvailable shouldn't be called becasue there are still only 11 bytes + // available. + EXPECT_CALL(stream_, OnDataAvailable()).Times(0); + sequencer_->OnStreamFrame(frame3); + EXPECT_EQ(11u, sequencer_->NumBytesBuffered()); +} + +TEST_F(QuicStreamSequencerTest, ReadSingleFrame) { + EXPECT_CALL(stream_, OnDataAvailable()); + OnFrame(0u, "abc"); + QuicString actual; + EXPECT_CALL(stream_, AddBytesConsumed(3)); + sequencer_->Read(&actual); + EXPECT_EQ("abc", actual); + EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); +} + +TEST_F(QuicStreamSequencerTest, ReadMultipleFramesWithMissingFrame) { + EXPECT_CALL(stream_, OnDataAvailable()); + OnFrame(0u, "abc"); + OnFrame(3u, "def"); + OnFrame(6u, "ghi"); + OnFrame(10u, "xyz"); // Byte 9 is missing. + QuicString actual; + EXPECT_CALL(stream_, AddBytesConsumed(9)); + sequencer_->Read(&actual); + EXPECT_EQ("abcdefghi", actual); + EXPECT_EQ(3u, sequencer_->NumBytesBuffered()); +} + +TEST_F(QuicStreamSequencerTest, ReadAndAppendToString) { + EXPECT_CALL(stream_, OnDataAvailable()); + OnFrame(0u, "def"); + OnFrame(3u, "ghi"); + QuicString actual = "abc"; + EXPECT_CALL(stream_, AddBytesConsumed(6)); + sequencer_->Read(&actual); + EXPECT_EQ("abcdefghi", actual); + EXPECT_EQ(0u, sequencer_->NumBytesBuffered()); +} + +TEST_F(QuicStreamSequencerTest, StopReading) { + EXPECT_CALL(stream_, OnDataAvailable()).Times(0); + EXPECT_CALL(stream_, OnFinRead()); + + EXPECT_CALL(stream_, AddBytesConsumed(0)); + sequencer_->StopReading(); + + EXPECT_CALL(stream_, AddBytesConsumed(3)); + OnFrame(0u, "abc"); + EXPECT_CALL(stream_, AddBytesConsumed(3)); + OnFrame(3u, "def"); + EXPECT_CALL(stream_, AddBytesConsumed(3)); + OnFinFrame(6u, "ghi"); +} + +TEST_F(QuicStreamSequencerTest, StopReadingWithLevelTriggered) { + if (GetQuicReloadableFlag(quic_stop_reading_when_level_triggered)) { + EXPECT_CALL(stream_, AddBytesConsumed(0)); + EXPECT_CALL(stream_, AddBytesConsumed(3)).Times(3); + EXPECT_CALL(stream_, OnDataAvailable()).Times(0); + EXPECT_CALL(stream_, OnFinRead()); + } else { + EXPECT_CALL(stream_, AddBytesConsumed(0)); + EXPECT_CALL(stream_, OnDataAvailable()).Times(3); + } + + sequencer_->set_level_triggered(true); + sequencer_->StopReading(); + + OnFrame(0u, "abc"); + OnFrame(3u, "def"); + OnFinFrame(6u, "ghi"); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_stream_test.cc b/quic/core/quic_stream_test.cc new file mode 100644 index 0000000..d019f71 --- /dev/null +++ b/quic/core/quic_stream_test.cc
@@ -0,0 +1,1548 @@ +// Copyright (c) 2012 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/quic_stream.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::AnyNumber; +using testing::AtLeast; +using testing::InSequence; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +const char kData1[] = "FooAndBar"; +const char kData2[] = "EepAndBaz"; +const size_t kDataLen = 9; + +class TestStream : public QuicStream { + public: + TestStream(QuicStreamId id, QuicSession* session, StreamType type) + : QuicStream(id, session, /*is_static=*/false, type) {} + + TestStream(PendingStream pending, StreamType type) + : QuicStream(std::move(pending), type) {} + + void OnDataAvailable() override {} + + MOCK_METHOD0(OnCanWriteNewData, void()); + + using QuicStream::CanWriteNewData; + using QuicStream::CanWriteNewDataAfterData; + using QuicStream::CloseWriteSide; + using QuicStream::fin_buffered; + using QuicStream::OnClose; + using QuicStream::WriteMemSlices; + using QuicStream::WriteOrBufferData; + using QuicStream::WritevData; + + private: + QuicString data_; +}; + +class QuicStreamTestBase : public QuicTestWithParam<ParsedQuicVersion> { + public: + QuicStreamTestBase() + : initial_flow_control_window_bytes_(kMaxPacketSize), + zero_(QuicTime::Delta::Zero()), + supported_versions_(AllSupportedVersions()) {} + + void Initialize() { + ParsedQuicVersionVector version_vector; + version_vector.push_back(GetParam()); + connection_ = new StrictMock<MockQuicConnection>( + &helper_, &alarm_factory_, Perspective::IS_SERVER, version_vector); + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_); + + // New streams rely on having the peer's flow control receive window + // negotiated in the config. + QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow( + session_->config(), initial_flow_control_window_bytes_); + + stream_ = new TestStream(kTestStreamId, session_.get(), BIDIRECTIONAL); + EXPECT_NE(nullptr, stream_); + // session_ now owns stream_. + session_->ActivateStream(QuicWrapUnique(stream_)); + // Ignore resetting when session_ is terminated. + EXPECT_CALL(*session_, SendRstStream(kTestStreamId, _, _)) + .Times(AnyNumber()); + write_blocked_list_ = + QuicSessionPeer::GetWriteBlockedStreams(session_.get()); + } + + bool fin_sent() { return QuicStreamPeer::FinSent(stream_); } + bool rst_sent() { return QuicStreamPeer::RstSent(stream_); } + + void set_initial_flow_control_window_bytes(uint32_t val) { + initial_flow_control_window_bytes_ = val; + } + + bool HasWriteBlockedStreams() { + return write_blocked_list_->HasWriteBlockedSpecialStream() || + write_blocked_list_->HasWriteBlockedDataStreams(); + } + + QuicConsumedData CloseStreamOnWriteError(QuicStream* /*stream*/, + QuicStreamId id, + size_t /*write_length*/, + QuicStreamOffset /*offset*/, + StreamSendingState /*state*/) { + session_->CloseStream(id); + return QuicConsumedData(1, false); + } + + bool ClearControlFrame(const QuicFrame& frame) { + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + bool ClearResetStreamFrame(const QuicFrame& frame) { + EXPECT_EQ(RST_STREAM_FRAME, frame.type); + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + bool ClearStopSendingFrame(const QuicFrame& frame) { + EXPECT_EQ(STOP_SENDING_FRAME, frame.type); + DeleteFrame(&const_cast<QuicFrame&>(frame)); + return true; + } + + protected: + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* connection_; + std::unique_ptr<MockQuicSession> session_; + TestStream* stream_; + QuicWriteBlockedList* write_blocked_list_; + uint32_t initial_flow_control_window_bytes_; + QuicTime::Delta zero_; + ParsedQuicVersionVector supported_versions_; + const QuicStreamId kTestStreamId = + QuicUtils::GetHeadersStreamId(GetParam().transport_version) + + QuicUtils::StreamIdDelta(GetParam().transport_version); +}; + +// Non parameterized QuicStreamTest used for tests that do not +// have any dependencies on the quic version. +class QuicStreamTest : public QuicStreamTestBase {}; + +// Index value of 1 has the test run with supported-version[1], which is some +// version OTHER than 99. +INSTANTIATE_TEST_SUITE_P( + QuicStreamTests, QuicStreamTest, + ::testing::ValuesIn(ParsedVersionOfIndex(AllSupportedVersions(), 1))); + +// Make a parameterized version of the QuicStreamTest for those tests +// that need to differentiate based on version number. +class QuicParameterizedStreamTest : public QuicStreamTestBase {}; +INSTANTIATE_TEST_SUITE_P(QuicParameterizedStreamTests, + QuicParameterizedStreamTest, + ::testing::ValuesIn(AllSupportedVersions())); + +TEST_P(QuicStreamTest, PendingStreamTooMuchData) { + Initialize(); + + PendingStream pending(kTestStreamId + 2, session_.get()); + // Receive a stream frame that violates flow control: the byte offset is + // higher than the receive window offset. + QuicStreamFrame frame(kTestStreamId + 2, false, + kInitialSessionFlowControlWindowForTest + 1, + QuicStringPiece(".")); + + // Stream should not accept the frame, and the connection should be closed. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + pending.OnStreamFrame(frame); +} + +TEST_P(QuicStreamTest, PendingStreamTooMuchDataInRstStream) { + Initialize(); + + PendingStream pending(kTestStreamId + 2, session_.get()); + // Receive a rst stream frame that violates flow control: the byte offset is + // higher than the receive window offset. + QuicRstStreamFrame frame(kInvalidControlFrameId, kTestStreamId + 2, + QUIC_STREAM_CANCELLED, + kInitialSessionFlowControlWindowForTest + 1); + + // Pending stream should not accept the frame, and the connection should be + // closed. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + pending.OnRstStreamFrame(frame); +} + +TEST_P(QuicStreamTest, PendingStreamRstStream) { + Initialize(); + + PendingStream pending(kTestStreamId + 2, session_.get()); + QuicStreamOffset final_byte_offset = 7; + QuicRstStreamFrame frame(kInvalidControlFrameId, kTestStreamId + 2, + QUIC_STREAM_CANCELLED, final_byte_offset); + + // Pending stream should accept the frame and not close the connection. + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + pending.OnRstStreamFrame(frame); +} + +TEST_P(QuicStreamTest, FromPendingStream) { + Initialize(); + + PendingStream pending(kTestStreamId + 2, session_.get()); + + QuicStreamFrame frame(kTestStreamId + 2, false, 2, QuicStringPiece(".")); + pending.OnStreamFrame(frame); + pending.OnStreamFrame(frame); + QuicStreamFrame frame2(kTestStreamId + 2, true, 3, QuicStringPiece(".")); + pending.OnStreamFrame(frame2); + + TestStream stream(std::move(pending), StreamType::READ_UNIDIRECTIONAL); + EXPECT_EQ(3, stream.num_frames_received()); + EXPECT_EQ(3u, stream.stream_bytes_read()); + EXPECT_EQ(1, stream.num_duplicate_frames_received()); + EXPECT_EQ(true, stream.fin_received()); + EXPECT_EQ(frame2.offset + 1, + stream.flow_controller()->highest_received_byte_offset()); + EXPECT_EQ(frame2.offset + 1, + session_->flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicStreamTest, FromPendingStreamThenData) { + Initialize(); + + PendingStream pending(kTestStreamId + 2, session_.get()); + + QuicStreamFrame frame(kTestStreamId + 2, false, 2, QuicStringPiece(".")); + pending.OnStreamFrame(frame); + + auto stream = + new TestStream(std::move(pending), StreamType::READ_UNIDIRECTIONAL); + session_->ActivateStream(QuicWrapUnique(stream)); + + QuicStreamFrame frame2(kTestStreamId + 2, true, 3, QuicStringPiece(".")); + stream->OnStreamFrame(frame2); + + EXPECT_EQ(2, stream->num_frames_received()); + EXPECT_EQ(2u, stream->stream_bytes_read()); + EXPECT_EQ(true, stream->fin_received()); + EXPECT_EQ(frame2.offset + 1, + stream->flow_controller()->highest_received_byte_offset()); + EXPECT_EQ(frame2.offset + 1, + session_->flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicStreamTest, WriteAllData) { + Initialize(); + + size_t length = + 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->transport_version(), PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0u); + connection_->SetMaxPacketLength(length); + + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(Invoke(&(MockQuicSession::ConsumeData))); + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_FALSE(HasWriteBlockedStreams()); +} + +TEST_P(QuicStreamTest, NoBlockingIfNoDataOrFin) { + Initialize(); + + // Write no data and no fin. If we consume nothing we should not be write + // blocked. + EXPECT_QUIC_BUG(stream_->WriteOrBufferData(QuicStringPiece(), false, nullptr), + ""); + EXPECT_FALSE(HasWriteBlockedStreams()); +} + +TEST_P(QuicStreamTest, BlockIfOnlySomeDataConsumed) { + Initialize(); + + // Write some data and no fin. If we consume some but not all of the data, + // we should be write blocked a not all the data was consumed. + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 1u, 0u, + NO_FIN); + })); + stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), false, nullptr); + ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); + EXPECT_EQ(1u, stream_->BufferedDataBytes()); +} + +TEST_P(QuicStreamTest, BlockIfFinNotConsumedWithData) { + Initialize(); + + // Write some data and no fin. If we consume all the data but not the fin, + // we should be write blocked because the fin was not consumed. + // (This should never actually happen as the fin should be sent out with the + // last data) + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u, 0u, + NO_FIN); + })); + stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), true, nullptr); + ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); +} + +TEST_P(QuicStreamTest, BlockIfSoloFinNotConsumed) { + Initialize(); + + // Write no data and a fin. If we consume nothing we should be write blocked, + // as the fin was not consumed. + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, false))); + stream_->WriteOrBufferData(QuicStringPiece(), true, nullptr); + ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); +} + +TEST_P(QuicStreamTest, CloseOnPartialWrite) { + Initialize(); + + // Write some data and no fin. However, while writing the data + // close the stream and verify that MarkConnectionLevelWriteBlocked does not + // crash with an unknown stream. + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(Invoke(this, &QuicStreamTest::CloseStreamOnWriteError)); + stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), false, nullptr); + ASSERT_EQ(0u, write_blocked_list_->NumBlockedStreams()); +} + +TEST_P(QuicStreamTest, WriteOrBufferData) { + Initialize(); + + EXPECT_FALSE(HasWriteBlockedStreams()); + size_t length = + 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->transport_version(), PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, + !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, + VARIABLE_LENGTH_INTEGER_LENGTH_0, 0u); + connection_->SetMaxPacketLength(length); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), + kDataLen - 1, 0u, NO_FIN); + })); + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_EQ(1u, stream_->BufferedDataBytes()); + EXPECT_TRUE(HasWriteBlockedStreams()); + + // Queue a bytes_consumed write. + stream_->WriteOrBufferData(kData2, false, nullptr); + EXPECT_EQ(10u, stream_->BufferedDataBytes()); + // Make sure we get the tail of the first write followed by the bytes_consumed + InSequence s; + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), + kDataLen - 1, kDataLen - 1, NO_FIN); + })); + stream_->OnCanWrite(); + + // And finally the end of the bytes_consumed. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u, + 2 * kDataLen - 2, NO_FIN); + })); + stream_->OnCanWrite(); +} + +TEST_P(QuicStreamTest, WriteOrBufferDataReachStreamLimit) { + Initialize(); + QuicString data("aaaaa"); + QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - data.length(), + stream_); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(&(MockQuicSession::ConsumeData))); + stream_->WriteOrBufferData(data, false, nullptr); + EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _)); + EXPECT_QUIC_BUG(stream_->WriteOrBufferData("a", false, nullptr), + "Write too many data via stream"); +} + +TEST_P(QuicStreamTest, ConnectionCloseAfterStreamClose) { + Initialize(); + + QuicStreamPeer::CloseReadSide(stream_); + stream_->CloseWriteSide(); + EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error()); + EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error()); + stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR, + ConnectionCloseSource::FROM_SELF); + EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error()); + EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error()); +} + +TEST_P(QuicStreamTest, RstAlwaysSentIfNoFinSent) { + // For flow control accounting, a stream must send either a FIN or a RST frame + // before termination. + // Test that if no FIN has been sent, we send a RST. + + Initialize(); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Write some data, with no FIN. + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 1u, 0u, + NO_FIN); + })); + stream_->WriteOrBufferData(QuicStringPiece(kData1, 1), false, nullptr); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Now close the stream, and expect that we send a RST. + EXPECT_CALL(*session_, SendRstStream(_, _, _)); + stream_->OnClose(); + EXPECT_FALSE(fin_sent()); + EXPECT_TRUE(rst_sent()); +} + +TEST_P(QuicStreamTest, RstNotSentIfFinSent) { + // For flow control accounting, a stream must send either a FIN or a RST frame + // before termination. + // Test that if a FIN has been sent, we don't also send a RST. + + Initialize(); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Write some data, with FIN. + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 1u, 0u, + FIN); + })); + stream_->WriteOrBufferData(QuicStringPiece(kData1, 1), true, nullptr); + EXPECT_TRUE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Now close the stream, and expect that we do not send a RST. + stream_->OnClose(); + EXPECT_TRUE(fin_sent()); + EXPECT_FALSE(rst_sent()); +} + +TEST_P(QuicStreamTest, OnlySendOneRst) { + // For flow control accounting, a stream must send either a FIN or a RST frame + // before termination. + // Test that if a stream sends a RST, it doesn't send an additional RST during + // OnClose() (this shouldn't be harmful, but we shouldn't do it anyway...) + + Initialize(); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Reset the stream. + const int expected_resets = 1; + EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(expected_resets); + stream_->Reset(QUIC_STREAM_CANCELLED); + EXPECT_FALSE(fin_sent()); + EXPECT_TRUE(rst_sent()); + + // Now close the stream (any further resets being sent would break the + // expectation above). + stream_->OnClose(); + EXPECT_FALSE(fin_sent()); + EXPECT_TRUE(rst_sent()); +} + +TEST_P(QuicStreamTest, StreamFlowControlMultipleWindowUpdates) { + set_initial_flow_control_window_bytes(1000); + + Initialize(); + + // If we receive multiple WINDOW_UPDATES (potentially out of order), then we + // want to make sure we latch the largest offset we see. + + // Initially should be default. + EXPECT_EQ( + initial_flow_control_window_bytes_, + QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller())); + + // Check a single WINDOW_UPDATE results in correct offset. + QuicWindowUpdateFrame window_update_1(kInvalidControlFrameId, stream_->id(), + 1234); + stream_->OnWindowUpdateFrame(window_update_1); + EXPECT_EQ( + window_update_1.byte_offset, + QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller())); + + // Now send a few more WINDOW_UPDATES and make sure that only the largest is + // remembered. + QuicWindowUpdateFrame window_update_2(kInvalidControlFrameId, stream_->id(), + 1); + QuicWindowUpdateFrame window_update_3(kInvalidControlFrameId, stream_->id(), + 9999); + QuicWindowUpdateFrame window_update_4(kInvalidControlFrameId, stream_->id(), + 5678); + stream_->OnWindowUpdateFrame(window_update_2); + stream_->OnWindowUpdateFrame(window_update_3); + stream_->OnWindowUpdateFrame(window_update_4); + EXPECT_EQ( + window_update_3.byte_offset, + QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller())); +} + +TEST_P(QuicStreamTest, FrameStats) { + Initialize(); + + EXPECT_EQ(0, stream_->num_frames_received()); + EXPECT_EQ(0, stream_->num_duplicate_frames_received()); + QuicStreamFrame frame(stream_->id(), false, 0, QuicStringPiece(".")); + stream_->OnStreamFrame(frame); + EXPECT_EQ(1, stream_->num_frames_received()); + EXPECT_EQ(0, stream_->num_duplicate_frames_received()); + stream_->OnStreamFrame(frame); + EXPECT_EQ(2, stream_->num_frames_received()); + EXPECT_EQ(1, stream_->num_duplicate_frames_received()); +} + +// Verify that when we receive a packet which violates flow control (i.e. sends +// too much data on the stream) that the stream sequencer never sees this frame, +// as we check for violation and close the connection early. +TEST_P(QuicStreamTest, StreamSequencerNeverSeesPacketsViolatingFlowControl) { + Initialize(); + + // Receive a stream frame that violates flow control: the byte offset is + // higher than the receive window offset. + QuicStreamFrame frame(stream_->id(), false, + kInitialSessionFlowControlWindowForTest + 1, + QuicStringPiece(".")); + EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Stream should not accept the frame, and the connection should be closed. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + stream_->OnStreamFrame(frame); +} + +// Verify that after the consumer calls StopReading(), the stream still sends +// flow control updates. +TEST_P(QuicStreamTest, StopReadingSendsFlowControl) { + Initialize(); + + stream_->StopReading(); + + // Connection should not get terminated due to flow control errors. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)) + .Times(0); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(AtLeast(1)) + .WillRepeatedly(Invoke(this, &QuicStreamTest::ClearControlFrame)); + + QuicString data(1000, 'x'); + for (QuicStreamOffset offset = 0; + offset < 2 * kInitialStreamFlowControlWindowForTest; + offset += data.length()) { + QuicStreamFrame frame(stream_->id(), false, offset, data); + stream_->OnStreamFrame(frame); + } + EXPECT_LT( + kInitialStreamFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowOffset(stream_->flow_controller())); +} + +TEST_P(QuicStreamTest, FinalByteOffsetFromFin) { + Initialize(); + + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + + QuicStreamFrame stream_frame_no_fin(stream_->id(), false, 1234, + QuicStringPiece(".")); + stream_->OnStreamFrame(stream_frame_no_fin); + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + + QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234, + QuicStringPiece(".")); + stream_->OnStreamFrame(stream_frame_with_fin); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); +} + +TEST_P(QuicStreamTest, FinalByteOffsetFromRst) { + Initialize(); + + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + stream_->OnStreamReset(rst_frame); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); +} + +TEST_P(QuicStreamTest, InvalidFinalByteOffsetFromRst) { + Initialize(); + + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 0xFFFFFFFFFFFF); + // Stream should not accept the frame, and the connection should be closed. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + stream_->OnStreamReset(rst_frame); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); + stream_->OnClose(); +} + +TEST_P(QuicStreamTest, FinalByteOffsetFromZeroLengthStreamFrame) { + // When receiving Trailers, an empty stream frame is created with the FIN set, + // and is passed to OnStreamFrame. The Trailers may be sent in advance of + // queued body bytes being sent, and thus the final byte offset may exceed + // current flow control limits. Flow control should only be concerned with + // data that has actually been sent/received, so verify that flow control + // ignores such a stream frame. + Initialize(); + + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + const QuicStreamOffset kByteOffsetExceedingFlowControlWindow = + kInitialSessionFlowControlWindowForTest + 1; + const QuicStreamOffset current_stream_flow_control_offset = + QuicFlowControllerPeer::ReceiveWindowOffset(stream_->flow_controller()); + const QuicStreamOffset current_connection_flow_control_offset = + QuicFlowControllerPeer::ReceiveWindowOffset(session_->flow_controller()); + ASSERT_GT(kByteOffsetExceedingFlowControlWindow, + current_stream_flow_control_offset); + ASSERT_GT(kByteOffsetExceedingFlowControlWindow, + current_connection_flow_control_offset); + QuicStreamFrame zero_length_stream_frame_with_fin( + stream_->id(), /*fin=*/true, kByteOffsetExceedingFlowControlWindow, + QuicStringPiece()); + EXPECT_EQ(0, zero_length_stream_frame_with_fin.data_length); + + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + stream_->OnStreamFrame(zero_length_stream_frame_with_fin); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); + + // The flow control receive offset values should not have changed. + EXPECT_EQ( + current_stream_flow_control_offset, + QuicFlowControllerPeer::ReceiveWindowOffset(stream_->flow_controller())); + EXPECT_EQ( + current_connection_flow_control_offset, + QuicFlowControllerPeer::ReceiveWindowOffset(session_->flow_controller())); +} + +TEST_P(QuicStreamTest, OnStreamResetOffsetOverflow) { + Initialize(); + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, kMaxStreamLength + 1); + EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _)); + stream_->OnStreamReset(rst_frame); +} + +TEST_P(QuicStreamTest, OnStreamFrameUpperLimit) { + Initialize(); + + // Modify receive window offset and sequencer buffer total_bytes_read_ to + // avoid flow control violation. + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kMaxStreamLength + 5u); + QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(), + kMaxStreamLength + 5u); + QuicStreamSequencerPeer::SetFrameBufferTotalBytesRead( + QuicStreamPeer::sequencer(stream_), kMaxStreamLength - 10u); + + EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _)) + .Times(0); + QuicStreamFrame stream_frame(stream_->id(), false, kMaxStreamLength - 1, + QuicStringPiece(".")); + stream_->OnStreamFrame(stream_frame); + QuicStreamFrame stream_frame2(stream_->id(), true, kMaxStreamLength, + QuicStringPiece("")); + stream_->OnStreamFrame(stream_frame2); +} + +TEST_P(QuicStreamTest, StreamTooLong) { + Initialize(); + EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _)) + .Times(1); + QuicStreamFrame stream_frame(stream_->id(), false, kMaxStreamLength, + QuicStringPiece(".")); + EXPECT_QUIC_PEER_BUG(stream_->OnStreamFrame(stream_frame), + QuicStrCat("Receive stream frame on stream ", + stream_->id(), " reaches max stream length")); +} + +TEST_P(QuicParameterizedStreamTest, SetDrainingIncomingOutgoing) { + // Don't have incoming data consumed. + Initialize(); + + // Incoming data with FIN. + QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234, + QuicStringPiece(".")); + stream_->OnStreamFrame(stream_frame_with_fin); + // The FIN has been received but not consumed. + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + EXPECT_FALSE(stream_->reading_stopped()); + + EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams()); + + // Outgoing data with FIN. + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u, 0u, + FIN); + })); + stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), true, nullptr); + EXPECT_TRUE(stream_->write_side_closed()); + + EXPECT_EQ(1u, QuicSessionPeer::GetDrainingStreams(session_.get()) + ->count(kTestStreamId)); + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); +} + +TEST_P(QuicParameterizedStreamTest, SetDrainingOutgoingIncoming) { + // Don't have incoming data consumed. + Initialize(); + + // Outgoing data with FIN. + EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u, 0u, + FIN); + })); + stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), true, nullptr); + EXPECT_TRUE(stream_->write_side_closed()); + + EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams()); + + // Incoming data with FIN. + QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234, + QuicStringPiece(".")); + stream_->OnStreamFrame(stream_frame_with_fin); + // The FIN has been received but not consumed. + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + EXPECT_FALSE(stream_->reading_stopped()); + + EXPECT_EQ(1u, QuicSessionPeer::GetDrainingStreams(session_.get()) + ->count(kTestStreamId)); + EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams()); +} + +TEST_P(QuicStreamTest, EarlyResponseFinHandling) { + // Verify that if the server completes the response before reading the end of + // the request, the received FIN is recorded. + + Initialize(); + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + + // Receive data for the request. + QuicStreamFrame frame1(stream_->id(), false, 0, QuicStringPiece("Start")); + stream_->OnStreamFrame(frame1); + // When QuicSimpleServerStream sends the response, it calls + // QuicStream::CloseReadSide() first. + QuicStreamPeer::CloseReadSide(stream_); + // Send data and FIN for the response. + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_)); + // Receive remaining data and FIN for the request. + QuicStreamFrame frame2(stream_->id(), true, 0, QuicStringPiece("End")); + stream_->OnStreamFrame(frame2); + EXPECT_TRUE(stream_->fin_received()); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); +} + +TEST_P(QuicStreamTest, StreamWaitsForAcks) { + Initialize(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + // Stream is not waiting for acks initially. + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + // Send kData1. + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + QuicByteCount newly_acked_length = 0; + EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(9u, newly_acked_length); + // Stream is not waiting for acks as all sent data is acked. + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + // Send kData2. + stream_->WriteOrBufferData(kData2, false, nullptr); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Send FIN. + stream_->WriteOrBufferData("", true, nullptr); + // Fin only frame is not stored in send buffer. + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + + // kData2 is retransmitted. + stream_->OnStreamFrameRetransmitted(9, 9, false); + + // kData2 is acked. + EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(9u, newly_acked_length); + // Stream is waiting for acks as FIN is not acked. + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + // FIN is acked. + EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(0u, newly_acked_length); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); +} + +TEST_P(QuicStreamTest, StreamDataGetAckedOutOfOrder) { + Initialize(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + // Send data. + stream_->WriteOrBufferData(kData1, false, nullptr); + stream_->WriteOrBufferData(kData1, false, nullptr); + stream_->WriteOrBufferData(kData1, false, nullptr); + stream_->WriteOrBufferData("", true, nullptr); + EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + QuicByteCount newly_acked_length = 0; + EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(9u, newly_acked_length); + EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(9u, newly_acked_length); + EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(9u, newly_acked_length); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + // FIN is not acked yet. + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(0u, newly_acked_length); + EXPECT_FALSE(stream_->IsWaitingForAcks()); +} + +TEST_P(QuicStreamTest, CancelStream) { + Initialize(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Cancel stream. + stream_->Reset(QUIC_STREAM_NO_ERROR); + // stream still waits for acks as the error code is QUIC_STREAM_NO_ERROR, and + // data is going to be retransmitted. + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 9)); + stream_->Reset(QUIC_STREAM_CANCELLED); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Stream stops waiting for acks as data is not going to be retransmitted. + EXPECT_FALSE(stream_->IsWaitingForAcks()); +} + +TEST_P(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) { + Initialize(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + + // RST_STREAM received. + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 9); + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9)); + stream_->OnStreamReset(rst_frame); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Stream stops waiting for acks as it does not finish sending and rst is + // sent. + EXPECT_FALSE(stream_->IsWaitingForAcks()); +} + +TEST_P(QuicStreamTest, RstFrameReceivedStreamFinishSending) { + Initialize(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + stream_->WriteOrBufferData(kData1, true, nullptr); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + // RST_STREAM received. + EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(0); + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + stream_->OnStreamReset(rst_frame); + // Stream still waits for acks as it finishes sending and has unacked data. + EXPECT_TRUE(stream_->IsWaitingForAcks()); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); +} + +TEST_P(QuicStreamTest, ConnectionClosed) { + Initialize(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9)); + stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR, + ConnectionCloseSource::FROM_SELF); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Stream stops waiting for acks as connection is going to close. + EXPECT_FALSE(stream_->IsWaitingForAcks()); +} + +TEST_P(QuicStreamTest, CanWriteNewDataAfterData) { + SetQuicFlag(&FLAGS_quic_buffered_data_threshold, 100); + Initialize(); + EXPECT_TRUE(stream_->CanWriteNewDataAfterData(99)); + EXPECT_FALSE(stream_->CanWriteNewDataAfterData(100)); +} + +TEST_P(QuicStreamTest, WriteBufferedData) { + // Set buffered data low water mark to be 100. + SetQuicFlag(&FLAGS_quic_buffered_data_threshold, 100); + // Do not stream level flow control block this stream. + set_initial_flow_control_window_bytes(500000); + + Initialize(); + QuicString data(1024, 'a'); + EXPECT_TRUE(stream_->CanWriteNewData()); + + // Testing WriteOrBufferData. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 100u, 0u, + NO_FIN); + })); + stream_->WriteOrBufferData(data, false, nullptr); + stream_->WriteOrBufferData(data, false, nullptr); + stream_->WriteOrBufferData(data, false, nullptr); + // Verify all data is saved. + EXPECT_EQ(3 * data.length() - 100, stream_->BufferedDataBytes()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 100, 100u, + NO_FIN); + })); + // Buffered data size > threshold, do not ask upper layer for more data. + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(0); + stream_->OnCanWrite(); + EXPECT_EQ(3 * data.length() - 200, stream_->BufferedDataBytes()); + EXPECT_FALSE(stream_->CanWriteNewData()); + + // Send buffered data to make buffered data size < threshold. + size_t data_to_write = 3 * data.length() - 200 - + GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1; + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this, data_to_write]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), + data_to_write, 200u, NO_FIN); + })); + // Buffered data size < threshold, ask upper layer for more data. + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); + stream_->OnCanWrite(); + EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1, + stream_->BufferedDataBytes()); + EXPECT_TRUE(stream_->CanWriteNewData()); + + // Flush all buffered data. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); + stream_->OnCanWrite(); + EXPECT_EQ(0u, stream_->BufferedDataBytes()); + EXPECT_FALSE(stream_->HasBufferedData()); + EXPECT_TRUE(stream_->CanWriteNewData()); + + // Testing Writev. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, false))); + struct iovec iov = {const_cast<char*>(data.data()), data.length()}; + QuicMemSliceStorage storage( + &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(), + 1024); + QuicConsumedData consumed = stream_->WriteMemSlices(storage.ToSpan(), false); + + // There is no buffered data before, all data should be consumed without + // respecting buffered data upper limit. + EXPECT_EQ(data.length(), consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(data.length(), stream_->BufferedDataBytes()); + EXPECT_FALSE(stream_->CanWriteNewData()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0); + QuicMemSliceStorage storage2( + &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(), + 1024); + consumed = stream_->WriteMemSlices(storage2.ToSpan(), false); + // No Data can be consumed as buffered data is beyond upper limit. + EXPECT_EQ(0u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(data.length(), stream_->BufferedDataBytes()); + + data_to_write = + data.length() - GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1; + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this, data_to_write]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), + data_to_write, 0u, NO_FIN); + })); + + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); + stream_->OnCanWrite(); + EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1, + stream_->BufferedDataBytes()); + EXPECT_TRUE(stream_->CanWriteNewData()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0); + // All data can be consumed as buffered data is below upper limit. + QuicMemSliceStorage storage3( + &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(), + 1024); + consumed = stream_->WriteMemSlices(storage3.ToSpan(), false); + EXPECT_EQ(data.length(), consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(data.length() + GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1, + stream_->BufferedDataBytes()); + EXPECT_FALSE(stream_->CanWriteNewData()); +} + +TEST_P(QuicStreamTest, WritevDataReachStreamLimit) { + Initialize(); + QuicString data("aaaaa"); + QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - data.length(), + stream_); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(&(MockQuicSession::ConsumeData))); + struct iovec iov = {const_cast<char*>(data.data()), 5u}; + QuicMemSliceStorage storage( + &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(), + 1024); + QuicConsumedData consumed = stream_->WriteMemSlices(storage.ToSpan(), false); + EXPECT_EQ(data.length(), consumed.bytes_consumed); + struct iovec iov2 = {const_cast<char*>(data.data()), 1u}; + QuicMemSliceStorage storage2( + &iov2, 1, + session_->connection()->helper()->GetStreamSendBufferAllocator(), 1024); + EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _)); + EXPECT_QUIC_BUG(stream_->WriteMemSlices(storage2.ToSpan(), false), + "Write too many data via stream"); +} + +TEST_P(QuicStreamTest, WriteMemSlices) { + // Set buffered data low water mark to be 100. + SetQuicFlag(&FLAGS_quic_buffered_data_threshold, 100); + // Do not flow control block this stream. + set_initial_flow_control_window_bytes(500000); + + Initialize(); + char data[1024]; + std::vector<std::pair<char*, size_t>> buffers; + buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data))); + buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data))); + QuicTestMemSliceVector vector1(buffers); + QuicTestMemSliceVector vector2(buffers); + QuicMemSliceSpan span1 = vector1.span(); + QuicMemSliceSpan span2 = vector2.span(); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 100u, 0u, + NO_FIN); + })); + // There is no buffered data before, all data should be consumed. + QuicConsumedData consumed = stream_->WriteMemSlices(span1, false); + EXPECT_EQ(2048u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(2 * QUIC_ARRAYSIZE(data) - 100, stream_->BufferedDataBytes()); + EXPECT_FALSE(stream_->fin_buffered()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0); + // No Data can be consumed as buffered data is beyond upper limit. + consumed = stream_->WriteMemSlices(span2, true); + EXPECT_EQ(0u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(2 * QUIC_ARRAYSIZE(data) - 100, stream_->BufferedDataBytes()); + EXPECT_FALSE(stream_->fin_buffered()); + + size_t data_to_write = 2 * QUIC_ARRAYSIZE(data) - 100 - + GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1; + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this, data_to_write]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), + data_to_write, 100u, NO_FIN); + })); + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); + stream_->OnCanWrite(); + EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1, + stream_->BufferedDataBytes()); + // Try to write slices2 again. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0); + consumed = stream_->WriteMemSlices(span2, true); + EXPECT_EQ(2048u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_EQ(2 * QUIC_ARRAYSIZE(data) + + GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1, + stream_->BufferedDataBytes()); + EXPECT_TRUE(stream_->fin_buffered()); + + // Flush all buffered data. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->OnCanWrite(); + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(0); + EXPECT_FALSE(stream_->HasBufferedData()); + EXPECT_TRUE(stream_->write_side_closed()); +} + +TEST_P(QuicStreamTest, WriteMemSlicesReachStreamLimit) { + Initialize(); + QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - 5u, stream_); + char data[5]; + std::vector<std::pair<char*, size_t>> buffers; + buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data))); + QuicTestMemSliceVector vector1(buffers); + QuicMemSliceSpan span1 = vector1.span(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 5u, 0u, + NO_FIN); + })); + // There is no buffered data before, all data should be consumed. + QuicConsumedData consumed = stream_->WriteMemSlices(span1, false); + EXPECT_EQ(5u, consumed.bytes_consumed); + + std::vector<std::pair<char*, size_t>> buffers2; + buffers2.push_back(std::make_pair(data, 1u)); + QuicTestMemSliceVector vector2(buffers); + QuicMemSliceSpan span2 = vector2.span(); + EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _)); + EXPECT_QUIC_BUG(stream_->WriteMemSlices(span2, false), + "Write too many data via stream"); +} + +TEST_P(QuicStreamTest, StreamDataGetAckedMultipleTimes) { + Initialize(); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + // Send [0, 27) and fin. + stream_->WriteOrBufferData(kData1, false, nullptr); + stream_->WriteOrBufferData(kData1, false, nullptr); + stream_->WriteOrBufferData(kData1, true, nullptr); + EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + // Ack [0, 9), [5, 22) and [18, 26) + // Verify [0, 9) 9 bytes are acked. + QuicByteCount newly_acked_length = 0; + EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(9u, newly_acked_length); + EXPECT_EQ(2u, QuicStreamPeer::SendBuffer(stream_).size()); + // Verify [9, 22) 13 bytes are acked. + EXPECT_TRUE(stream_->OnStreamFrameAcked(5, 17, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(13u, newly_acked_length); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + // Verify [22, 26) 4 bytes are acked. + EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 8, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(4u, newly_acked_length); + EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + // Ack [0, 27). Verify [26, 27) 1 byte is acked. + EXPECT_TRUE(stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(1u, newly_acked_length); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_TRUE(stream_->IsWaitingForAcks()); + + // Ack Fin. + EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(0u, newly_acked_length); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_FALSE(stream_->IsWaitingForAcks()); + + // Ack [10, 27) and fin. No new data is acked. + EXPECT_FALSE(stream_->OnStreamFrameAcked( + 10, 17, true, QuicTime::Delta::Zero(), &newly_acked_length)); + EXPECT_EQ(0u, newly_acked_length); + EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); + EXPECT_FALSE(stream_->IsWaitingForAcks()); +} + +TEST_P(QuicStreamTest, OnStreamFrameLost) { + Initialize(); + + // Send [0, 9). + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_FALSE(stream_->HasBufferedData()); + EXPECT_TRUE(stream_->IsStreamFrameOutstanding(0, 9, false)); + + // Try to send [9, 27), but connection is blocked. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, false))); + stream_->WriteOrBufferData(kData2, false, nullptr); + stream_->WriteOrBufferData(kData2, false, nullptr); + EXPECT_TRUE(stream_->HasBufferedData()); + EXPECT_FALSE(stream_->HasPendingRetransmission()); + + // Lost [0, 9). When stream gets a chance to write, only lost data is + // transmitted. + stream_->OnStreamFrameLost(0, 9, false); + EXPECT_TRUE(stream_->HasPendingRetransmission()); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->OnCanWrite(); + EXPECT_FALSE(stream_->HasPendingRetransmission()); + EXPECT_TRUE(stream_->HasBufferedData()); + + // This OnCanWrite causes [9, 27) to be sent. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->OnCanWrite(); + EXPECT_FALSE(stream_->HasBufferedData()); + + // Send a fin only frame. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData("", true, nullptr); + + // Lost [9, 27) and fin. + stream_->OnStreamFrameLost(9, 18, false); + stream_->OnStreamFrameLost(27, 0, true); + EXPECT_TRUE(stream_->HasPendingRetransmission()); + + // Ack [9, 18). + QuicByteCount newly_acked_length = 0; + EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(), + &newly_acked_length)); + EXPECT_EQ(9u, newly_acked_length); + EXPECT_FALSE(stream_->IsStreamFrameOutstanding(9, 3, false)); + EXPECT_TRUE(stream_->HasPendingRetransmission()); + // This OnCanWrite causes [18, 27) and fin to be retransmitted. Verify fin can + // be bundled with data. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 9u, 18u, + FIN); + })); + stream_->OnCanWrite(); + EXPECT_FALSE(stream_->HasPendingRetransmission()); + // Lost [9, 18) again, but it is not considered as lost because kData2 + // has been acked. + stream_->OnStreamFrameLost(9, 9, false); + EXPECT_FALSE(stream_->HasPendingRetransmission()); + EXPECT_TRUE(stream_->IsStreamFrameOutstanding(27, 0, true)); +} + +TEST_P(QuicStreamTest, CannotBundleLostFin) { + Initialize(); + + // Send [0, 18) and fin. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(kData1, false, nullptr); + stream_->WriteOrBufferData(kData2, true, nullptr); + + // Lost [0, 9) and fin. + stream_->OnStreamFrameLost(0, 9, false); + stream_->OnStreamFrameLost(18, 0, true); + + // Retransmit lost data. Verify [0, 9) and fin are retransmitted in two + // frames. + InSequence s; + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 9u, 0u, + NO_FIN); + })); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, true))); + stream_->OnCanWrite(); +} + +TEST_P(QuicStreamTest, MarkConnectionLevelWriteBlockedOnWindowUpdateFrame) { + // Set a small initial control window size. + set_initial_flow_control_window_bytes(100); + Initialize(); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(this, &QuicStreamTest::ClearControlFrame)); + QuicString data(1024, '.'); + stream_->WriteOrBufferData(data, false, nullptr); + EXPECT_FALSE(HasWriteBlockedStreams()); + + QuicWindowUpdateFrame window_update(kInvalidControlFrameId, stream_->id(), + 1234); + + stream_->OnWindowUpdateFrame(window_update); + // Verify stream is marked connection level write blocked. + EXPECT_TRUE(HasWriteBlockedStreams()); + EXPECT_TRUE(stream_->HasBufferedData()); +} + +// Regression test for b/73282665. +TEST_P(QuicStreamTest, + MarkConnectionLevelWriteBlockedOnWindowUpdateFrameWithNoBufferedData) { + // Set a small initial flow control window size. + const uint32_t kSmallWindow = 100; + set_initial_flow_control_window_bytes(kSmallWindow); + Initialize(); + + QuicString data(kSmallWindow, '.'); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillOnce(Invoke(this, &QuicStreamTest::ClearControlFrame)); + stream_->WriteOrBufferData(data, false, nullptr); + EXPECT_FALSE(HasWriteBlockedStreams()); + + QuicWindowUpdateFrame window_update(kInvalidControlFrameId, stream_->id(), + 120); + stream_->OnWindowUpdateFrame(window_update); + EXPECT_FALSE(stream_->HasBufferedData()); + // Verify stream is marked as blocked although there is no buffered data. + EXPECT_TRUE(HasWriteBlockedStreams()); +} + +TEST_P(QuicStreamTest, RetransmitStreamData) { + Initialize(); + InSequence s; + + // Send [0, 18) with fin. + EXPECT_CALL(*session_, WritevData(_, stream_->id(), _, _, _)) + .Times(2) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeData)); + stream_->WriteOrBufferData(kData1, false, nullptr); + stream_->WriteOrBufferData(kData1, true, nullptr); + // Ack [10, 13). + QuicByteCount newly_acked_length = 0; + stream_->OnStreamFrameAcked(10, 3, false, QuicTime::Delta::Zero(), + &newly_acked_length); + EXPECT_EQ(3u, newly_acked_length); + // Retransmit [0, 18) with fin, and only [0, 8) is consumed. + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 10, 0, NO_FIN)) + .WillOnce(InvokeWithoutArgs([this]() { + return MockQuicSession::ConsumeData(stream_, stream_->id(), 8, 0u, + NO_FIN); + })); + EXPECT_FALSE(stream_->RetransmitStreamData(0, 18, true)); + + // Retransmit [0, 18) with fin, and all is consumed. + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 10, 0, NO_FIN)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 5, 13, FIN)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_TRUE(stream_->RetransmitStreamData(0, 18, true)); + + // Retransmit [0, 8) with fin, and all is consumed. + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 8, 0, NO_FIN)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 0, 18, FIN)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_TRUE(stream_->RetransmitStreamData(0, 8, true)); +} + +TEST_P(QuicStreamTest, ResetStreamOnTtlExpiresRetransmitLostData) { + Initialize(); + + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 200, 0, FIN)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + QuicString body(200, 'a'); + stream_->WriteOrBufferData(body, true, nullptr); + + // Set TTL to be 1 s. + QuicTime::Delta ttl = QuicTime::Delta::FromSeconds(1); + ASSERT_TRUE(stream_->MaybeSetTtl(ttl)); + // Verify data gets retransmitted because TTL does not expire. + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 100, 0, NO_FIN)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + EXPECT_TRUE(stream_->RetransmitStreamData(0, 100, false)); + stream_->OnStreamFrameLost(100, 100, true); + EXPECT_TRUE(stream_->HasPendingRetransmission()); + + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + // Verify stream gets reset because TTL expires. + EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _)).Times(1); + stream_->OnCanWrite(); +} + +TEST_P(QuicStreamTest, ResetStreamOnTtlExpiresEarlyRetransmitData) { + Initialize(); + + EXPECT_CALL(*session_, WritevData(_, stream_->id(), 200, 0, FIN)) + .WillOnce(Invoke(MockQuicSession::ConsumeData)); + QuicString body(200, 'a'); + stream_->WriteOrBufferData(body, true, nullptr); + + // Set TTL to be 1 s. + QuicTime::Delta ttl = QuicTime::Delta::FromSeconds(1); + ASSERT_TRUE(stream_->MaybeSetTtl(ttl)); + + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + // Verify stream gets reset because TTL expires. + EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _)).Times(1); + stream_->RetransmitStreamData(0, 100, false); +} + +// Test that QuicStream::StopSending A) is a no-op if the connection is not in +// version 99, B) that it properly invokes QuicSession::StopSending, and C) that +// the correct data is passed along, including getting the stream ID. +TEST_P(QuicParameterizedStreamTest, CheckStopSending) { + Initialize(); + const int kStopSendingCode = 123; + // These must start as false. + EXPECT_FALSE(QuicStreamPeer::write_side_closed(stream_)); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + // Expect to actually see a stop sending if and only if we are in version 99. + if (connection_->transport_version() == QUIC_VERSION_99) { + EXPECT_CALL(*session_, SendStopSending(kStopSendingCode, stream_->id())) + .Times(1); + } else { + EXPECT_CALL(*session_, SendStopSending(_, _)).Times(0); + } + stream_->SendStopSending(kStopSendingCode); + // Sending a STOP_SENDING does not actually close the local stream. + // Our implementation waits for the responding RESET_STREAM to effect the + // closes. Therefore, read- and write-side closes should both be false. + EXPECT_FALSE(QuicStreamPeer::write_side_closed(stream_)); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); +} + +// Test that OnStreamReset does one-way (read) closes if version 99, two way +// (read and write) if not version 99. +TEST_P(QuicStreamTest, OnStreamResetReadOrReadWrite) { + Initialize(); + EXPECT_FALSE(QuicStreamPeer::write_side_closed(stream_)); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + stream_->OnStreamReset(rst_frame); + if (connection_->transport_version() == QUIC_VERSION_99) { + // Version 99/IETF QUIC should close just the read side. + EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_)); + EXPECT_FALSE(QuicStreamPeer::write_side_closed(stream_)); + } else { + // Google QUIC should close both sides of the stream. + EXPECT_TRUE(QuicStreamPeer::write_side_closed(stream_)); + EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_)); + } +} + +// Test that receiving a STOP_SENDING just closes the write side of the stream. +// If not V99, the test is a noop (no STOP_SENDING in Google QUIC). +TEST_P(QuicStreamTest, OnStopSendingReadOrReadWrite) { + Initialize(); + if (connection_->transport_version() != QUIC_VERSION_99) { + return; + } + + EXPECT_FALSE(QuicStreamPeer::write_side_closed(stream_)); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + + // Simulate receipt of a STOP_SENDING. + stream_->OnStopSending(123); + + // Should close just the read side. + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + EXPECT_TRUE(QuicStreamPeer::write_side_closed(stream_)); +} + +// SendOnlyRstStream must only send a RESET_STREAM (no bundled STOP_SENDING). +TEST_P(QuicStreamTest, SendOnlyRstStream) { + Initialize(); + if (connection_->transport_version() != QUIC_VERSION_99) { + return; + } + + EXPECT_CALL(*connection_, + OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD)); + EXPECT_CALL(*connection_, SendControlFrame(_)) + .Times(1) + .WillOnce(Invoke(this, &QuicStreamTest::ClearResetStreamFrame)); + + QuicSessionPeer::SendRstStreamInner(session_.get(), stream_->id(), + QUIC_BAD_APPLICATION_PAYLOAD, + stream_->stream_bytes_written(), + /*close_write_side_only=*/true); + + // ResetStreamOnly should just close the write side. + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + EXPECT_TRUE(QuicStreamPeer::write_side_closed(stream_)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_sustained_bandwidth_recorder.cc b/quic/core/quic_sustained_bandwidth_recorder.cc new file mode 100644 index 0000000..dd94a34 --- /dev/null +++ b/quic/core/quic_sustained_bandwidth_recorder.cc
@@ -0,0 +1,62 @@ +// Copyright 2014 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/quic_sustained_bandwidth_recorder.h" + +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +QuicSustainedBandwidthRecorder::QuicSustainedBandwidthRecorder() + : has_estimate_(false), + is_recording_(false), + bandwidth_estimate_recorded_during_slow_start_(false), + bandwidth_estimate_(QuicBandwidth::Zero()), + max_bandwidth_estimate_(QuicBandwidth::Zero()), + max_bandwidth_timestamp_(0), + start_time_(QuicTime::Zero()) {} + +void QuicSustainedBandwidthRecorder::RecordEstimate(bool in_recovery, + bool in_slow_start, + QuicBandwidth bandwidth, + QuicTime estimate_time, + QuicWallTime wall_time, + QuicTime::Delta srtt) { + if (in_recovery) { + is_recording_ = false; + QUIC_DVLOG(1) << "Stopped recording at: " + << estimate_time.ToDebuggingValue(); + return; + } + + if (!is_recording_) { + // This is the first estimate of a new recording period. + start_time_ = estimate_time; + is_recording_ = true; + QUIC_DVLOG(1) << "Started recording at: " << start_time_.ToDebuggingValue(); + return; + } + + // If we have been recording for at least 3 * srtt, then record the latest + // bandwidth estimate as a valid sustained bandwidth estimate. + if (estimate_time - start_time_ >= 3 * srtt) { + has_estimate_ = true; + bandwidth_estimate_recorded_during_slow_start_ = in_slow_start; + bandwidth_estimate_ = bandwidth; + QUIC_DVLOG(1) << "New sustained bandwidth estimate (KBytes/s): " + << bandwidth_estimate_.ToKBytesPerSecond(); + } + + // Check for an increase in max bandwidth. + if (bandwidth > max_bandwidth_estimate_) { + max_bandwidth_estimate_ = bandwidth; + max_bandwidth_timestamp_ = wall_time.ToUNIXSeconds(); + QUIC_DVLOG(1) << "New max bandwidth estimate (KBytes/s): " + << max_bandwidth_estimate_.ToKBytesPerSecond(); + } +} + +} // namespace quic
diff --git a/quic/core/quic_sustained_bandwidth_recorder.h b/quic/core/quic_sustained_bandwidth_recorder.h new file mode 100644 index 0000000..b65b50c --- /dev/null +++ b/quic/core/quic_sustained_bandwidth_recorder.h
@@ -0,0 +1,96 @@ +// Copyright 2014 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_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_ +#define QUICHE_QUIC_CORE_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_ + +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" + +namespace quic { + +namespace test { +class QuicSustainedBandwidthRecorderPeer; +} // namespace test + +// This class keeps track of a sustained bandwidth estimate to ultimately send +// to the client in a server config update message. A sustained bandwidth +// estimate is only marked as valid if the QuicSustainedBandwidthRecorder has +// been given uninterrupted reliable estimates over a certain period of time. +class QUIC_EXPORT_PRIVATE QuicSustainedBandwidthRecorder { + public: + QuicSustainedBandwidthRecorder(); + QuicSustainedBandwidthRecorder(const QuicSustainedBandwidthRecorder&) = + delete; + QuicSustainedBandwidthRecorder& operator=( + const QuicSustainedBandwidthRecorder&) = delete; + + // As long as |in_recovery| is consistently false, multiple calls to this + // method over a 3 * srtt period results in storage of a valid sustained + // bandwidth estimate. + // |time_now| is used as a max bandwidth timestamp if needed. + void RecordEstimate(bool in_recovery, + bool in_slow_start, + QuicBandwidth bandwidth, + QuicTime estimate_time, + QuicWallTime wall_time, + QuicTime::Delta srtt); + + bool HasEstimate() const { return has_estimate_; } + + QuicBandwidth BandwidthEstimate() const { + DCHECK(has_estimate_); + return bandwidth_estimate_; + } + + QuicBandwidth MaxBandwidthEstimate() const { + DCHECK(has_estimate_); + return max_bandwidth_estimate_; + } + + int64_t MaxBandwidthTimestamp() const { + DCHECK(has_estimate_); + return max_bandwidth_timestamp_; + } + + bool EstimateRecordedDuringSlowStart() const { + DCHECK(has_estimate_); + return bandwidth_estimate_recorded_during_slow_start_; + } + + private: + friend class test::QuicSustainedBandwidthRecorderPeer; + + // True if we have been able to calculate sustained bandwidth, over at least + // one recording period (3 * rtt). + bool has_estimate_; + + // True if the last call to RecordEstimate had a reliable estimate. + bool is_recording_; + + // True if the current sustained bandwidth estimate was generated while in + // slow start. + bool bandwidth_estimate_recorded_during_slow_start_; + + // The latest sustained bandwidth estimate. + QuicBandwidth bandwidth_estimate_; + + // The maximum sustained bandwidth seen over the lifetime of the connection. + QuicBandwidth max_bandwidth_estimate_; + + // Timestamp indicating when the max_bandwidth_estimate_ was seen. + int64_t max_bandwidth_timestamp_; + + // Timestamp marking the beginning of the latest recording period. + QuicTime start_time_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_
diff --git a/quic/core/quic_sustained_bandwidth_recorder_test.cc b/quic/core/quic_sustained_bandwidth_recorder_test.cc new file mode 100644 index 0000000..28bbad8 --- /dev/null +++ b/quic/core/quic_sustained_bandwidth_recorder_test.cc
@@ -0,0 +1,133 @@ +// Copyright 2014 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/quic_sustained_bandwidth_recorder.h" + +#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QuicSustainedBandwidthRecorderTest : public QuicTest {}; + +TEST_F(QuicSustainedBandwidthRecorderTest, BandwidthEstimates) { + QuicSustainedBandwidthRecorder recorder; + EXPECT_FALSE(recorder.HasEstimate()); + + QuicTime estimate_time = QuicTime::Zero(); + QuicWallTime wall_time = QuicWallTime::Zero(); + QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150); + const int kBandwidthBitsPerSecond = 12345678; + QuicBandwidth bandwidth = + QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond); + + bool in_recovery = false; + bool in_slow_start = false; + + // This triggers recording, but should not yield a valid estimate yet. + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + EXPECT_FALSE(recorder.HasEstimate()); + + // Send a second reading, again this should not result in a valid estimate, + // as not enough time has passed. + estimate_time = estimate_time + srtt; + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + EXPECT_FALSE(recorder.HasEstimate()); + + // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate. + estimate_time = estimate_time + srtt; + estimate_time = estimate_time + srtt; + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + EXPECT_TRUE(recorder.HasEstimate()); + EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth); + EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate()); + + // Resetting, and sending a different estimate will only change output after + // a further 3 * kSRTT has passed. + QuicBandwidth second_bandwidth = + QuicBandwidth::FromBitsPerSecond(2 * kBandwidthBitsPerSecond); + // Reset the recorder by passing in a measurement while in recovery. + in_recovery = true; + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + in_recovery = false; + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth); + + estimate_time = estimate_time + 3 * srtt; + const int64_t kSeconds = 556677; + QuicWallTime second_bandwidth_wall_time = + QuicWallTime::FromUNIXSeconds(kSeconds); + recorder.RecordEstimate(in_recovery, in_slow_start, second_bandwidth, + estimate_time, second_bandwidth_wall_time, srtt); + EXPECT_EQ(recorder.BandwidthEstimate(), second_bandwidth); + EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate()); + EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds); + + // Reset again, this time recording a lower bandwidth than before. + QuicBandwidth third_bandwidth = + QuicBandwidth::FromBitsPerSecond(0.5 * kBandwidthBitsPerSecond); + // Reset the recorder by passing in an unreliable measurement. + recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth, + estimate_time, wall_time, srtt); + recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth, + estimate_time, wall_time, srtt); + EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth); + + estimate_time = estimate_time + 3 * srtt; + recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth, + estimate_time, wall_time, srtt); + EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth); + + // Max bandwidth should not have changed. + EXPECT_LT(third_bandwidth, second_bandwidth); + EXPECT_EQ(recorder.MaxBandwidthEstimate(), second_bandwidth); + EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds); +} + +TEST_F(QuicSustainedBandwidthRecorderTest, SlowStart) { + // Verify that slow start status is correctly recorded. + QuicSustainedBandwidthRecorder recorder; + EXPECT_FALSE(recorder.HasEstimate()); + + QuicTime estimate_time = QuicTime::Zero(); + QuicWallTime wall_time = QuicWallTime::Zero(); + QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150); + const int kBandwidthBitsPerSecond = 12345678; + QuicBandwidth bandwidth = + QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond); + + bool in_recovery = false; + bool in_slow_start = true; + + // This triggers recording, but should not yield a valid estimate yet. + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + + // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate. + estimate_time = estimate_time + 3 * srtt; + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + EXPECT_TRUE(recorder.HasEstimate()); + EXPECT_TRUE(recorder.EstimateRecordedDuringSlowStart()); + + // Now send another estimate, this time not in slow start. + estimate_time = estimate_time + 3 * srtt; + in_slow_start = false; + recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time, + wall_time, srtt); + EXPECT_TRUE(recorder.HasEstimate()); + EXPECT_FALSE(recorder.EstimateRecordedDuringSlowStart()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_tag.cc b/quic/core/quic_tag.cc new file mode 100644 index 0000000..7c9c329 --- /dev/null +++ b/quic/core/quic_tag.cc
@@ -0,0 +1,72 @@ +// 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/quic_tag.h" + +#include <algorithm> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +namespace quic { + +bool FindMutualQuicTag(const QuicTagVector& our_tags, + const QuicTagVector& their_tags, + QuicTag* out_result, + size_t* out_index) { + const size_t num_our_tags = our_tags.size(); + const size_t num_their_tags = their_tags.size(); + for (size_t i = 0; i < num_our_tags; i++) { + for (size_t j = 0; j < num_their_tags; j++) { + if (our_tags[i] == their_tags[j]) { + *out_result = our_tags[i]; + if (out_index != nullptr) { + *out_index = j; + } + return true; + } + } + } + + return false; +} + +QuicString QuicTagToString(QuicTag tag) { + char chars[sizeof tag]; + bool ascii = true; + const QuicTag orig_tag = tag; + + for (size_t i = 0; i < QUIC_ARRAYSIZE(chars); i++) { + chars[i] = static_cast<char>(tag); + if ((chars[i] == 0 || chars[i] == '\xff') && + i == QUIC_ARRAYSIZE(chars) - 1) { + chars[i] = ' '; + } + if (!isprint(static_cast<unsigned char>(chars[i]))) { + ascii = false; + break; + } + tag >>= 8; + } + + if (ascii) { + return QuicString(chars, sizeof(chars)); + } + + return QuicTextUtils::Uint64ToString(orig_tag); +} + +uint32_t MakeQuicTag(char a, char b, char c, char d) { + return static_cast<uint32_t>(a) | static_cast<uint32_t>(b) << 8 | + static_cast<uint32_t>(c) << 16 | static_cast<uint32_t>(d) << 24; +} + +bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) { + return std::find(tag_vector.begin(), tag_vector.end(), tag) != + tag_vector.end(); +} + +} // namespace quic
diff --git a/quic/core/quic_tag.h b/quic/core/quic_tag.h new file mode 100644 index 0000000..1808d9d --- /dev/null +++ b/quic/core/quic_tag.h
@@ -0,0 +1,53 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_QUIC_TAG_H_ +#define QUICHE_QUIC_CORE_QUIC_TAG_H_ + +#include <map> +#include <vector> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// A QuicTag is a 32-bit used as identifiers in the QUIC handshake. The use of +// a uint32_t seeks to provide a balance between the tyranny of magic number +// registries and the verbosity of strings. As far as the wire protocol is +// concerned, these are opaque, 32-bit values. +// +// Tags will often be referred to by their ASCII equivalent, e.g. EXMP. This is +// just a mnemonic for the value 0x504d5845 (little-endian version of the ASCII +// string E X M P). +typedef uint32_t QuicTag; +typedef std::map<QuicTag, QuicString> QuicTagValueMap; +typedef std::vector<QuicTag> QuicTagVector; + +// MakeQuicTag returns a value given the four bytes. For example: +// MakeQuicTag('C', 'H', 'L', 'O'); +QUIC_EXPORT_PRIVATE QuicTag MakeQuicTag(char a, char b, char c, char d); + +// Returns true if |tag_vector| contains |tag|. +QUIC_EXPORT_PRIVATE bool ContainsQuicTag(const QuicTagVector& tag_vector, + QuicTag tag); + +// Sets |out_result| to the first tag in |our_tags| that is also in |their_tags| +// and returns true. If there is no intersection it returns false. +// +// If |out_index| is non-nullptr and a match is found then the index of that +// match in |their_tags| is written to |out_index|. +QUIC_EXPORT_PRIVATE bool FindMutualQuicTag(const QuicTagVector& our_tags, + const QuicTagVector& their_tags, + QuicTag* out_result, + size_t* out_index); + +// A utility function that converts a tag to a string. It will try to maintain +// the human friendly name if possible (i.e. kABCD -> "ABCD"), or will just +// treat it as a number if not. +QUIC_EXPORT_PRIVATE QuicString QuicTagToString(QuicTag tag); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_TAG_H_
diff --git a/quic/core/quic_tag_test.cc b/quic/core/quic_tag_test.cc new file mode 100644 index 0000000..88e636a --- /dev/null +++ b/quic/core/quic_tag_test.cc
@@ -0,0 +1,38 @@ +// 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/quic_tag.h" + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QuicTagTest : public QuicTest {}; + +TEST_F(QuicTagTest, TagToString) { + EXPECT_EQ("SCFG", QuicTagToString(kSCFG)); + EXPECT_EQ("SNO ", QuicTagToString(kServerNonceTag)); + EXPECT_EQ("CRT ", QuicTagToString(kCertificateTag)); + EXPECT_EQ("CHLO", QuicTagToString(MakeQuicTag('C', 'H', 'L', 'O'))); + // A tag that contains a non-printing character will be printed as a decimal + // number. + EXPECT_EQ("525092931", QuicTagToString(MakeQuicTag('C', 'H', 'L', '\x1f'))); +} + +TEST_F(QuicTagTest, MakeQuicTag) { + QuicTag tag = MakeQuicTag('A', 'B', 'C', 'D'); + char bytes[4]; + memcpy(bytes, &tag, 4); + EXPECT_EQ('A', bytes[0]); + EXPECT_EQ('B', bytes[1]); + EXPECT_EQ('C', bytes[2]); + EXPECT_EQ('D', bytes[3]); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_time.cc b/quic/core/quic_time.cc new file mode 100644 index 0000000..92a3eec --- /dev/null +++ b/quic/core/quic_time.cc
@@ -0,0 +1,85 @@ +// Copyright (c) 2012 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/quic_time.h" + +#include <cinttypes> +#include <cstdlib> +#include <limits> + +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +QuicString QuicTime::Delta::ToDebugValue() const { + const int64_t one_ms = 1000; + const int64_t one_s = 1000 * one_ms; + + int64_t absolute_value = std::abs(time_offset_); + + // For debugging purposes, always display the value with the highest precision + // available. + if (absolute_value > one_s && absolute_value % one_s == 0) { + return QuicStringPrintf("%" PRId64 "s", time_offset_ / one_s); + } + if (absolute_value > one_ms && absolute_value % one_ms == 0) { + return QuicStringPrintf("%" PRId64 "ms", time_offset_ / one_ms); + } + return QuicStringPrintf("%" PRId64 "us", time_offset_); +} + +uint64_t QuicWallTime::ToUNIXSeconds() const { + return microseconds_ / 1000000; +} + +uint64_t QuicWallTime::ToUNIXMicroseconds() const { + return microseconds_; +} + +bool QuicWallTime::IsAfter(QuicWallTime other) const { + return microseconds_ > other.microseconds_; +} + +bool QuicWallTime::IsBefore(QuicWallTime other) const { + return microseconds_ < other.microseconds_; +} + +bool QuicWallTime::IsZero() const { + return microseconds_ == 0; +} + +QuicTime::Delta QuicWallTime::AbsoluteDifference(QuicWallTime other) const { + uint64_t d; + + if (microseconds_ > other.microseconds_) { + d = microseconds_ - other.microseconds_; + } else { + d = other.microseconds_ - microseconds_; + } + + if (d > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) { + d = std::numeric_limits<int64_t>::max(); + } + return QuicTime::Delta::FromMicroseconds(d); +} + +QuicWallTime QuicWallTime::Add(QuicTime::Delta delta) const { + uint64_t microseconds = microseconds_ + delta.ToMicroseconds(); + if (microseconds < microseconds_) { + microseconds = std::numeric_limits<uint64_t>::max(); + } + return QuicWallTime(microseconds); +} + +// TODO(ianswett) Test this. +QuicWallTime QuicWallTime::Subtract(QuicTime::Delta delta) const { + uint64_t microseconds = microseconds_ - delta.ToMicroseconds(); + if (microseconds > microseconds_) { + microseconds = 0; + } + return QuicWallTime(microseconds); +} + +} // namespace quic
diff --git a/quic/core/quic_time.h b/quic/core/quic_time.h new file mode 100644 index 0000000..6bd3f68 --- /dev/null +++ b/quic/core/quic_time.h
@@ -0,0 +1,277 @@ +// Copyright (c) 2012 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. + +// QuicTime represents one point in time, stored in microsecond resolution. +// QuicTime is monotonically increasing, even across system clock adjustments. +// The epoch (time 0) of QuicTime is unspecified. +// +// This implementation wraps a int64_t of usec since the epoch. While +// the epoch is the Unix epoch, do not depend on this fact because other +// implementations, like Chrome's, do NOT have the same epoch. + +#ifndef QUICHE_QUIC_CORE_QUIC_TIME_H_ +#define QUICHE_QUIC_CORE_QUIC_TIME_H_ + +#include <cmath> +#include <cstdint> +#include <limits> +#include <ostream> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +// TODO(vasilvv): replace with ABSL_MUST_USE_RESULT once we're using absl. +#if defined(__clang__) +#define QUIC_TIME_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define QUIC_TIME_WARN_UNUSED_RESULT +#endif /* defined(__clang__) */ + +namespace quic { + +class QuicClock; + +// A QuicTime is a purely relative time. QuicTime values from different clocks +// cannot be compared to each other. If you need an absolute time, see +// QuicWallTime, below. +class QUIC_EXPORT_PRIVATE QuicTime { + public: + // A QuicTime::Delta represents the signed difference between two points in + // time, stored in microsecond resolution. + class QUIC_EXPORT_PRIVATE Delta { + public: + // Create a object with an offset of 0. + static constexpr Delta Zero() { return Delta(0); } + + // Create a object with infinite offset time. + static constexpr Delta Infinite() { return Delta(kQuicInfiniteTimeUs); } + + // Converts a number of seconds to a time offset. + static constexpr Delta FromSeconds(int64_t secs) { + return Delta(secs * 1000 * 1000); + } + + // Converts a number of milliseconds to a time offset. + static constexpr Delta FromMilliseconds(int64_t ms) { + return Delta(ms * 1000); + } + + // Converts a number of microseconds to a time offset. + static constexpr Delta FromMicroseconds(int64_t us) { return Delta(us); } + + // Converts the time offset to a rounded number of seconds. + inline int64_t ToSeconds() const { return time_offset_ / 1000 / 1000; } + + // Converts the time offset to a rounded number of milliseconds. + inline int64_t ToMilliseconds() const { return time_offset_ / 1000; } + + // Converts the time offset to a rounded number of microseconds. + inline int64_t ToMicroseconds() const { return time_offset_; } + + inline bool IsZero() const { return time_offset_ == 0; } + + inline bool IsInfinite() const { + return time_offset_ == kQuicInfiniteTimeUs; + } + + QuicString ToDebugValue() const; + + private: + friend inline bool operator==(QuicTime::Delta lhs, QuicTime::Delta rhs); + friend inline bool operator<(QuicTime::Delta lhs, QuicTime::Delta rhs); + friend inline QuicTime::Delta operator<<(QuicTime::Delta lhs, size_t rhs); + friend inline QuicTime::Delta operator>>(QuicTime::Delta lhs, size_t rhs); + + friend inline QuicTime::Delta operator+(QuicTime::Delta lhs, + QuicTime::Delta rhs); + friend inline QuicTime::Delta operator-(QuicTime::Delta lhs, + QuicTime::Delta rhs); + friend inline QuicTime::Delta operator*(QuicTime::Delta lhs, int rhs); + friend inline QuicTime::Delta operator*(QuicTime::Delta lhs, double rhs); + + friend inline QuicTime operator+(QuicTime lhs, QuicTime::Delta rhs); + friend inline QuicTime operator-(QuicTime lhs, QuicTime::Delta rhs); + friend inline QuicTime::Delta operator-(QuicTime lhs, QuicTime rhs); + + static const int64_t kQuicInfiniteTimeUs = + std::numeric_limits<int64_t>::max(); + + explicit constexpr Delta(int64_t time_offset) : time_offset_(time_offset) {} + + int64_t time_offset_; + friend class QuicTime; + }; + + // Creates a new QuicTime with an internal value of 0. IsInitialized() + // will return false for these times. + static constexpr QuicTime Zero() { return QuicTime(0); } + + // Creates a new QuicTime with an infinite time. + static constexpr QuicTime Infinite() { + return QuicTime(Delta::kQuicInfiniteTimeUs); + } + + QuicTime(const QuicTime& other) = default; + + QuicTime& operator=(const QuicTime& other) { + time_ = other.time_; + return *this; + } + + // Produce the internal value to be used when logging. This value + // represents the number of microseconds since some epoch. It may + // be the UNIX epoch on some platforms. On others, it may + // be a CPU ticks based value. + inline int64_t ToDebuggingValue() const { return time_; } + + inline bool IsInitialized() const { return 0 != time_; } + + private: + friend class QuicClock; + + friend inline bool operator==(QuicTime lhs, QuicTime rhs); + friend inline bool operator<(QuicTime lhs, QuicTime rhs); + friend inline QuicTime operator+(QuicTime lhs, QuicTime::Delta rhs); + friend inline QuicTime operator-(QuicTime lhs, QuicTime::Delta rhs); + friend inline QuicTime::Delta operator-(QuicTime lhs, QuicTime rhs); + + explicit constexpr QuicTime(int64_t time) : time_(time) {} + + int64_t time_; +}; + +// A QuicWallTime represents an absolute time that is globally consistent. In +// practice, clock-skew means that comparing values from different machines +// requires some flexibility. +class QUIC_EXPORT_PRIVATE QuicWallTime { + public: + // FromUNIXSeconds constructs a QuicWallTime from a count of the seconds + // since the UNIX epoch. + static constexpr QuicWallTime FromUNIXSeconds(uint64_t seconds) { + return QuicWallTime(seconds * 1000000); + } + + static constexpr QuicWallTime FromUNIXMicroseconds(uint64_t microseconds) { + return QuicWallTime(microseconds); + } + + // Zero returns a QuicWallTime set to zero. IsZero will return true for this + // value. + static constexpr QuicWallTime Zero() { return QuicWallTime(0); } + + // Returns the number of seconds since the UNIX epoch. + uint64_t ToUNIXSeconds() const; + // Returns the number of microseconds since the UNIX epoch. + uint64_t ToUNIXMicroseconds() const; + + bool IsAfter(QuicWallTime other) const; + bool IsBefore(QuicWallTime other) const; + + // IsZero returns true if this object is the result of calling |Zero|. + bool IsZero() const; + + // AbsoluteDifference returns the absolute value of the time difference + // between |this| and |other|. + QuicTime::Delta AbsoluteDifference(QuicWallTime other) const; + + // Add returns a new QuicWallTime that represents the time of |this| plus + // |delta|. + QUIC_TIME_WARN_UNUSED_RESULT QuicWallTime Add(QuicTime::Delta delta) const; + + // Subtract returns a new QuicWallTime that represents the time of |this| + // minus |delta|. + QUIC_TIME_WARN_UNUSED_RESULT QuicWallTime + Subtract(QuicTime::Delta delta) const; + + private: + explicit constexpr QuicWallTime(uint64_t microseconds) + : microseconds_(microseconds) {} + + uint64_t microseconds_; +}; + +// Non-member relational operators for QuicTime::Delta. +inline bool operator==(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return lhs.time_offset_ == rhs.time_offset_; +} +inline bool operator!=(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return !(lhs == rhs); +} +inline bool operator<(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return lhs.time_offset_ < rhs.time_offset_; +} +inline bool operator>(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return rhs < lhs; +} +inline bool operator<=(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return !(rhs < lhs); +} +inline bool operator>=(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return !(lhs < rhs); +} +inline QuicTime::Delta operator>>(QuicTime::Delta lhs, size_t rhs) { + return QuicTime::Delta(lhs.time_offset_ >> rhs); +} + +// Non-member relational operators for QuicTime. +inline bool operator==(QuicTime lhs, QuicTime rhs) { + return lhs.time_ == rhs.time_; +} +inline bool operator!=(QuicTime lhs, QuicTime rhs) { + return !(lhs == rhs); +} +inline bool operator<(QuicTime lhs, QuicTime rhs) { + return lhs.time_ < rhs.time_; +} +inline bool operator>(QuicTime lhs, QuicTime rhs) { + return rhs < lhs; +} +inline bool operator<=(QuicTime lhs, QuicTime rhs) { + return !(rhs < lhs); +} +inline bool operator>=(QuicTime lhs, QuicTime rhs) { + return !(lhs < rhs); +} + +// Non-member arithmetic operators for QuicTime::Delta. +inline QuicTime::Delta operator+(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return QuicTime::Delta(lhs.time_offset_ + rhs.time_offset_); +} +inline QuicTime::Delta operator-(QuicTime::Delta lhs, QuicTime::Delta rhs) { + return QuicTime::Delta(lhs.time_offset_ - rhs.time_offset_); +} +inline QuicTime::Delta operator*(QuicTime::Delta lhs, int rhs) { + return QuicTime::Delta(lhs.time_offset_ * rhs); +} +inline QuicTime::Delta operator*(QuicTime::Delta lhs, double rhs) { + return QuicTime::Delta( + static_cast<int64_t>(std::llround(lhs.time_offset_ * rhs))); +} +inline QuicTime::Delta operator*(int lhs, QuicTime::Delta rhs) { + return rhs * lhs; +} +inline QuicTime::Delta operator*(double lhs, QuicTime::Delta rhs) { + return rhs * lhs; +} + +// Non-member arithmetic operators for QuicTime and QuicTime::Delta. +inline QuicTime operator+(QuicTime lhs, QuicTime::Delta rhs) { + return QuicTime(lhs.time_ + rhs.time_offset_); +} +inline QuicTime operator-(QuicTime lhs, QuicTime::Delta rhs) { + return QuicTime(lhs.time_ - rhs.time_offset_); +} +inline QuicTime::Delta operator-(QuicTime lhs, QuicTime rhs) { + return QuicTime::Delta(lhs.time_ - rhs.time_); +} + +// Override stream output operator for gtest. +inline std::ostream& operator<<(std::ostream& output, + const QuicTime::Delta delta) { + output << delta.ToDebugValue(); + return output; +} +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_TIME_H_
diff --git a/quic/core/quic_time_test.cc b/quic/core/quic_time_test.cc new file mode 100644 index 0000000..9720532 --- /dev/null +++ b/quic/core/quic_time_test.cc
@@ -0,0 +1,182 @@ +// Copyright (c) 2012 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/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" + +namespace quic { +namespace test { + +class QuicTimeDeltaTest : public QuicTest {}; + +TEST_F(QuicTimeDeltaTest, Zero) { + EXPECT_TRUE(QuicTime::Delta::Zero().IsZero()); + EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite()); + EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsZero()); +} + +TEST_F(QuicTimeDeltaTest, Infinite) { + EXPECT_TRUE(QuicTime::Delta::Infinite().IsInfinite()); + EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite()); + EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsInfinite()); +} + +TEST_F(QuicTimeDeltaTest, FromTo) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), + QuicTime::Delta::FromMicroseconds(1000)); + EXPECT_EQ(QuicTime::Delta::FromSeconds(1), + QuicTime::Delta::FromMilliseconds(1000)); + EXPECT_EQ(QuicTime::Delta::FromSeconds(1), + QuicTime::Delta::FromMicroseconds(1000000)); + + EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds()); + EXPECT_EQ(2, QuicTime::Delta::FromMilliseconds(2000).ToSeconds()); + EXPECT_EQ(1000, QuicTime::Delta::FromMilliseconds(1).ToMicroseconds()); + EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2000).ToMicroseconds(), + QuicTime::Delta::FromSeconds(2).ToMicroseconds()); +} + +TEST_F(QuicTimeDeltaTest, Add) { + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000), + QuicTime::Delta::Zero() + QuicTime::Delta::FromMilliseconds(2)); +} + +TEST_F(QuicTimeDeltaTest, Subtract) { + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1000), + QuicTime::Delta::FromMilliseconds(2) - + QuicTime::Delta::FromMilliseconds(1)); +} + +TEST_F(QuicTimeDeltaTest, Multiply) { + int i = 2; + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000), + QuicTime::Delta::FromMilliseconds(2) * i); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000), + i * QuicTime::Delta::FromMilliseconds(2)); + double d = 2; + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000), + QuicTime::Delta::FromMilliseconds(2) * d); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000), + d * QuicTime::Delta::FromMilliseconds(2)); + + // Ensure we are rounding correctly within a single-bit level of precision. + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(5), + QuicTime::Delta::FromMicroseconds(9) * 0.5); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2), + QuicTime::Delta::FromMicroseconds(12) * 0.2); +} + +TEST_F(QuicTimeDeltaTest, Max) { + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000), + std::max(QuicTime::Delta::FromMicroseconds(1000), + QuicTime::Delta::FromMicroseconds(2000))); +} + +TEST_F(QuicTimeDeltaTest, NotEqual) { + EXPECT_TRUE(QuicTime::Delta::FromSeconds(0) != + QuicTime::Delta::FromSeconds(1)); + EXPECT_FALSE(QuicTime::Delta::FromSeconds(0) != + QuicTime::Delta::FromSeconds(0)); +} + +TEST_F(QuicTimeDeltaTest, DebugValue) { + const QuicTime::Delta one_us = QuicTime::Delta::FromMicroseconds(1); + const QuicTime::Delta one_ms = QuicTime::Delta::FromMilliseconds(1); + const QuicTime::Delta one_s = QuicTime::Delta::FromSeconds(1); + + EXPECT_EQ("3s", (3 * one_s).ToDebugValue()); + EXPECT_EQ("3ms", (3 * one_ms).ToDebugValue()); + EXPECT_EQ("3us", (3 * one_us).ToDebugValue()); + + EXPECT_EQ("3001us", (3 * one_ms + one_us).ToDebugValue()); + EXPECT_EQ("3001ms", (3 * one_s + one_ms).ToDebugValue()); + EXPECT_EQ("3000001us", (3 * one_s + one_us).ToDebugValue()); +} + +class QuicTimeTest : public QuicTest { + protected: + MockClock clock_; +}; + +TEST_F(QuicTimeTest, Initialized) { + EXPECT_FALSE(QuicTime::Zero().IsInitialized()); + EXPECT_TRUE((QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(1)) + .IsInitialized()); +} + +TEST_F(QuicTimeTest, CopyConstruct) { + QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1234); + EXPECT_NE(time_1, QuicTime(QuicTime::Zero())); + EXPECT_EQ(time_1, QuicTime(time_1)); +} + +TEST_F(QuicTimeTest, CopyAssignment) { + QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1234); + QuicTime time_2 = QuicTime::Zero(); + EXPECT_NE(time_1, time_2); + time_2 = time_1; + EXPECT_EQ(time_1, time_2); +} + +TEST_F(QuicTimeTest, Add) { + QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1); + QuicTime time_2 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2); + + QuicTime::Delta diff = time_2 - time_1; + + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), diff); + EXPECT_EQ(1000, diff.ToMicroseconds()); + EXPECT_EQ(1, diff.ToMilliseconds()); +} + +TEST_F(QuicTimeTest, Subtract) { + QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1); + QuicTime time_2 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2); + + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), time_2 - time_1); +} + +TEST_F(QuicTimeTest, SubtractDelta) { + QuicTime time = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2); + EXPECT_EQ(QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1), + time - QuicTime::Delta::FromMilliseconds(1)); +} + +TEST_F(QuicTimeTest, Max) { + QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1); + QuicTime time_2 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2); + + EXPECT_EQ(time_2, std::max(time_1, time_2)); +} + +TEST_F(QuicTimeTest, MockClock) { + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicTime now = clock_.ApproximateNow(); + QuicTime time = QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(1000); + + EXPECT_EQ(now, time); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + now = clock_.ApproximateNow(); + + EXPECT_NE(now, time); + + time = time + QuicTime::Delta::FromMilliseconds(1); + EXPECT_EQ(now, time); +} + +TEST_F(QuicTimeTest, LE) { + const QuicTime zero = QuicTime::Zero(); + const QuicTime one = zero + QuicTime::Delta::FromSeconds(1); + EXPECT_TRUE(zero <= zero); + EXPECT_TRUE(zero <= one); + EXPECT_TRUE(one <= one); + EXPECT_FALSE(one <= zero); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_time_wait_list_manager.cc b/quic/core/quic_time_wait_list_manager.cc new file mode 100644 index 0000000..b65f30b --- /dev/null +++ b/quic/core/quic_time_wait_list_manager.cc
@@ -0,0 +1,387 @@ +// Copyright (c) 2012 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/quic_time_wait_list_manager.h" + +#include <errno.h> + +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.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_connection_id.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_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +// A very simple alarm that just informs the QuicTimeWaitListManager to clean +// up old connection_ids. This alarm should be cancelled and deleted before +// the QuicTimeWaitListManager is deleted. +class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate { + public: + explicit ConnectionIdCleanUpAlarm( + QuicTimeWaitListManager* time_wait_list_manager) + : time_wait_list_manager_(time_wait_list_manager) {} + ConnectionIdCleanUpAlarm(const ConnectionIdCleanUpAlarm&) = delete; + ConnectionIdCleanUpAlarm& operator=(const ConnectionIdCleanUpAlarm&) = delete; + + void OnAlarm() override { + time_wait_list_manager_->CleanUpOldConnectionIds(); + } + + private: + // Not owned. + QuicTimeWaitListManager* time_wait_list_manager_; +}; + +QuicTimeWaitListManager::QuicTimeWaitListManager( + QuicPacketWriter* writer, + Visitor* visitor, + const QuicClock* clock, + QuicAlarmFactory* alarm_factory) + : time_wait_period_( + QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds)), + connection_id_clean_up_alarm_( + alarm_factory->CreateAlarm(new ConnectionIdCleanUpAlarm(this))), + clock_(clock), + writer_(writer), + visitor_(visitor) { + SetConnectionIdCleanUpAlarm(); +} + +QuicTimeWaitListManager::~QuicTimeWaitListManager() { + connection_id_clean_up_alarm_->Cancel(); +} + +void QuicTimeWaitListManager::AddConnectionIdToTimeWait( + QuicConnectionId connection_id, + bool ietf_quic, + TimeWaitAction action, + EncryptionLevel encryption_level, + std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets) { + DCHECK(action != SEND_TERMINATION_PACKETS || termination_packets != nullptr); + DCHECK(action != DO_NOTHING || ietf_quic); + int num_packets = 0; + auto it = connection_id_map_.find(connection_id); + const bool new_connection_id = it == connection_id_map_.end(); + if (!new_connection_id) { // Replace record if it is reinserted. + num_packets = it->second.num_packets; + connection_id_map_.erase(it); + } + TrimTimeWaitListIfNeeded(); + DCHECK_LT(num_connections(), + static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections)); + ConnectionIdData data(num_packets, ietf_quic, clock_->ApproximateNow(), + action); + if (termination_packets != nullptr) { + data.encryption_level = encryption_level; + data.termination_packets.swap(*termination_packets); + } + connection_id_map_.emplace(std::make_pair(connection_id, std::move(data))); + if (new_connection_id) { + visitor_->OnConnectionAddedToTimeWaitList(connection_id); + } +} + +bool QuicTimeWaitListManager::IsConnectionIdInTimeWait( + QuicConnectionId connection_id) const { + return QuicContainsKey(connection_id_map_, connection_id); +} + +void QuicTimeWaitListManager::OnBlockedWriterCanWrite() { + writer_->SetWritable(); + while (!pending_packets_queue_.empty()) { + QueuedPacket* queued_packet = pending_packets_queue_.front().get(); + if (!WriteToWire(queued_packet)) { + return; + } + pending_packets_queue_.pop_front(); + } +} + +void QuicTimeWaitListManager::ProcessPacket( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicConnectionId connection_id, + PacketHeaderFormat header_format, + std::unique_ptr<QuicPerPacketContext> packet_context) { + DCHECK(IsConnectionIdInTimeWait(connection_id)); + // TODO(satyamshekhar): Think about handling packets from different peer + // addresses. + auto it = connection_id_map_.find(connection_id); + DCHECK(it != connection_id_map_.end()); + // Increment the received packet count. + ConnectionIdData* connection_data = &it->second; + ++(connection_data->num_packets); + + if (!ShouldSendResponse(connection_data->num_packets)) { + QUIC_DLOG(INFO) << "Processing " << connection_id << " in time wait state: " + << "throttled"; + return; + } + + QUIC_DLOG(INFO) << "Processing " << connection_id << " in time wait state: " + << "header format=" << header_format + << " ietf=" << connection_data->ietf_quic + << ", action=" << connection_data->action + << ", number termination packets=" + << connection_data->termination_packets.size() + << ", encryption level=" << connection_data->encryption_level; + switch (connection_data->action) { + case SEND_TERMINATION_PACKETS: + if (connection_data->termination_packets.empty()) { + QUIC_BUG << "There are no termination packets."; + return; + } + switch (header_format) { + case IETF_QUIC_LONG_HEADER_PACKET: + if (!connection_data->ietf_quic) { + QUIC_CODE_COUNT(quic_received_long_header_packet_for_gquic); + } + if (connection_data->encryption_level == ENCRYPTION_FORWARD_SECURE) { + QUIC_CODE_COUNT( + quic_forward_secure_termination_packets_for_long_header); + } + break; + case IETF_QUIC_SHORT_HEADER_PACKET: + if (!connection_data->ietf_quic) { + QUIC_CODE_COUNT(quic_received_short_header_packet_for_gquic); + } + if (connection_data->encryption_level == ENCRYPTION_NONE) { + QUIC_CODE_COUNT( + quic_encryption_none_termination_packets_for_short_header); + if (GetQuicReloadableFlag(quic_always_reset_short_header_packets)) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_always_reset_short_header_packets); + // Send stateless reset in response to short header packets, + // because ENCRYPTION_NONE termination packets will not be + // processed by clients. + SendPublicReset(self_address, peer_address, connection_id, + connection_data->ietf_quic, + std::move(packet_context)); + return; + } + } else if (connection_data->encryption_level == ENCRYPTION_ZERO_RTT) { + QUIC_CODE_COUNT(quic_zero_rtt_termination_packets_for_short_header); + } + break; + case GOOGLE_QUIC_PACKET: + if (connection_data->ietf_quic) { + QUIC_CODE_COUNT(quic_received_gquic_packet_for_ietf_quic); + } + break; + } + + for (const auto& packet : connection_data->termination_packets) { + SendOrQueuePacket(QuicMakeUnique<QueuedPacket>( + self_address, peer_address, packet->Clone()), + packet_context.get()); + } + return; + case SEND_STATELESS_RESET: + if (header_format == IETF_QUIC_LONG_HEADER_PACKET) { + QUIC_CODE_COUNT(quic_stateless_reset_long_header_packet); + } + SendPublicReset(self_address, peer_address, connection_id, + connection_data->ietf_quic, std::move(packet_context)); + return; + case DO_NOTHING: + QUIC_CODE_COUNT(quic_time_wait_list_do_nothing); + DCHECK(connection_data->ietf_quic); + } +} + +void QuicTimeWaitListManager::SendVersionNegotiationPacket( + QuicConnectionId connection_id, + bool ietf_quic, + const ParsedQuicVersionVector& supported_versions, + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + std::unique_ptr<QuicPerPacketContext> packet_context) { + SendOrQueuePacket(QuicMakeUnique<QueuedPacket>( + self_address, peer_address, + QuicFramer::BuildVersionNegotiationPacket( + connection_id, ietf_quic, supported_versions)), + packet_context.get()); +} + +// Returns true if the number of packets received for this connection_id is a +// power of 2 to throttle the number of public reset packets we send to a peer. +bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) { + return (received_packet_count & (received_packet_count - 1)) == 0; +} + +void QuicTimeWaitListManager::SendPublicReset( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicConnectionId connection_id, + bool ietf_quic, + std::unique_ptr<QuicPerPacketContext> packet_context) { + if (ietf_quic) { + SendOrQueuePacket(QuicMakeUnique<QueuedPacket>( + self_address, peer_address, + BuildIetfStatelessResetPacket(connection_id)), + packet_context.get()); + return; + } + QuicPublicResetPacket packet; + packet.connection_id = connection_id; + // TODO(satyamshekhar): generate a valid nonce for this connection_id. + packet.nonce_proof = 1010101; + // TODO(wub): This is wrong for proxied sessions. Fix it. + packet.client_address = peer_address; + GetEndpointId(&packet.endpoint_id); + // Takes ownership of the packet. + SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(self_address, peer_address, + BuildPublicReset(packet)), + packet_context.get()); +} + +std::unique_ptr<QuicEncryptedPacket> QuicTimeWaitListManager::BuildPublicReset( + const QuicPublicResetPacket& packet) { + return QuicFramer::BuildPublicResetPacket(packet); +} + +std::unique_ptr<QuicEncryptedPacket> +QuicTimeWaitListManager::BuildIetfStatelessResetPacket( + QuicConnectionId connection_id) { + return QuicFramer::BuildIetfStatelessResetPacket( + connection_id, GetStatelessResetToken(connection_id)); +} + +// Either sends the packet and deletes it or makes pending queue the +// owner of the packet. +bool QuicTimeWaitListManager::SendOrQueuePacket( + std::unique_ptr<QueuedPacket> packet, + const QuicPerPacketContext* /*packet_context*/) { + if (WriteToWire(packet.get())) { + // Allow the packet to be deleted upon leaving this function. + return true; + } + pending_packets_queue_.push_back(std::move(packet)); + return false; +} + +bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { + if (writer_->IsWriteBlocked()) { + visitor_->OnWriteBlocked(this); + return false; + } + WriteResult result = writer_->WritePacket( + queued_packet->packet()->data(), queued_packet->packet()->length(), + queued_packet->self_address().host(), queued_packet->peer_address(), + nullptr); + + // If using a batch writer and the packet is buffered, flush it. + if (writer_->IsBatchMode() && result.status == WRITE_STATUS_OK && + result.bytes_written == 0) { + result = writer_->Flush(); + } + + if (IsWriteBlockedStatus(result.status)) { + // If blocked and unbuffered, return false to retry sending. + DCHECK(writer_->IsWriteBlocked()); + visitor_->OnWriteBlocked(this); + return result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED; + } else if (IsWriteError(result.status)) { + QUIC_LOG_FIRST_N(WARNING, 1) + << "Received unknown error while sending termination packet to " + << queued_packet->peer_address().ToString() << ": " + << strerror(result.error_code); + } + return true; +} + +void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() { + QuicTime::Delta next_alarm_interval = QuicTime::Delta::Zero(); + if (!connection_id_map_.empty()) { + QuicTime oldest_connection_id = + connection_id_map_.begin()->second.time_added; + QuicTime now = clock_->ApproximateNow(); + if (now - oldest_connection_id < time_wait_period_) { + next_alarm_interval = oldest_connection_id + time_wait_period_ - now; + } else { + QUIC_LOG(ERROR) + << "ConnectionId lingered for longer than time_wait_period_"; + } + } else { + // No connection_ids added so none will expire before time_wait_period_. + next_alarm_interval = time_wait_period_; + } + + connection_id_clean_up_alarm_->Update( + clock_->ApproximateNow() + next_alarm_interval, QuicTime::Delta::Zero()); +} + +bool QuicTimeWaitListManager::MaybeExpireOldestConnection( + QuicTime expiration_time) { + if (connection_id_map_.empty()) { + return false; + } + auto it = connection_id_map_.begin(); + QuicTime oldest_connection_id_time = it->second.time_added; + if (oldest_connection_id_time > expiration_time) { + // Too recent, don't retire. + return false; + } + // This connection_id has lived its age, retire it now. + QUIC_DLOG(INFO) << "Connection " << it->first + << " expired from time wait list"; + connection_id_map_.erase(it); + return true; +} + +void QuicTimeWaitListManager::CleanUpOldConnectionIds() { + QuicTime now = clock_->ApproximateNow(); + QuicTime expiration = now - time_wait_period_; + + while (MaybeExpireOldestConnection(expiration)) { + } + + SetConnectionIdCleanUpAlarm(); +} + +void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() { + if (FLAGS_quic_time_wait_list_max_connections < 0) { + return; + } + while (num_connections() >= + static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections)) { + MaybeExpireOldestConnection(QuicTime::Infinite()); + } +} + +QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData( + int num_packets, + bool ietf_quic, + QuicTime time_added, + TimeWaitAction action) + : num_packets(num_packets), + ietf_quic(ietf_quic), + time_added(time_added), + encryption_level(ENCRYPTION_NONE), + action(action) {} + +QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData( + ConnectionIdData&& other) = default; + +QuicTimeWaitListManager::ConnectionIdData::~ConnectionIdData() = default; + +QuicUint128 QuicTimeWaitListManager::GetStatelessResetToken( + QuicConnectionId connection_id) const { + return QuicUtils::GenerateStatelessResetToken(connection_id); +} + +} // namespace quic
diff --git a/quic/core/quic_time_wait_list_manager.h b/quic/core/quic_time_wait_list_manager.h new file mode 100644 index 0000000..74630ad --- /dev/null +++ b/quic/core/quic_time_wait_list_manager.h
@@ -0,0 +1,276 @@ +// Copyright (c) 2012 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. + +// Handles packets for connection_ids in time wait state by discarding the +// packet and sending the peers termination packets with exponential backoff. + +#ifndef QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_ +#define QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_ + +#include <cstddef> +#include <memory> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_framer.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +namespace test { +class QuicDispatcherPeer; +class QuicTimeWaitListManagerPeer; +} // namespace test + +// Maintains a list of all connection_ids that have been recently closed. A +// connection_id lives in this state for time_wait_period_. All packets received +// for connection_ids in this state are handed over to the +// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a +// public reset packet, a copy of the previously sent connection close packet, +// or nothing to the peer which sent a packet with the connection_id in time +// wait state. After the connection_id expires its time wait period, a new +// connection/session will be created if a packet is received for this +// connection_id. +class QuicTimeWaitListManager : public QuicBlockedWriterInterface { + public: + // Specifies what the time wait list manager should do when processing packets + // of a time wait connection. + enum TimeWaitAction : uint8_t { + // Send specified termination packets, error if termination packet is + // unavailable. + SEND_TERMINATION_PACKETS, + // Send stateless reset (public reset for GQUIC). + SEND_STATELESS_RESET, + + DO_NOTHING, + }; + + class Visitor : public QuicSession::Visitor { + public: + // Called after the given connection is added to the time-wait list. + virtual void OnConnectionAddedToTimeWaitList( + QuicConnectionId connection_id) = 0; + }; + + // writer - the entity that writes to the socket. (Owned by the caller) + // visitor - the entity that manages blocked writers. (Owned by the caller) + // clock - provide a clock (Owned by the caller) + // alarm_factory - used to run clean up alarms. (Owned by the caller) + QuicTimeWaitListManager(QuicPacketWriter* writer, + Visitor* visitor, + const QuicClock* clock, + QuicAlarmFactory* alarm_factory); + QuicTimeWaitListManager(const QuicTimeWaitListManager&) = delete; + QuicTimeWaitListManager& operator=(const QuicTimeWaitListManager&) = delete; + ~QuicTimeWaitListManager() override; + + // Adds the given connection_id to time wait state for time_wait_period_. + // If |termination_packets| are provided, copies of these packets will be sent + // when a packet with this connection ID is processed. Any termination packets + // will be move from |termination_packets| and will become owned by the + // manager. |action| specifies what the time wait list manager should do when + // processing packets of the connection. + virtual void AddConnectionIdToTimeWait( + QuicConnectionId connection_id, + bool ietf_quic, + TimeWaitAction action, + EncryptionLevel encryption_level, + std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets); + + // Returns true if the connection_id is in time wait state, false otherwise. + // Packets received for this connection_id should not lead to creation of new + // QuicSessions. + bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const; + + // Called when a packet is received for a connection_id that is in time wait + // state. Sends a public reset packet to the peer which sent this + // connection_id. Sending of the public reset packet is throttled by using + // exponential back off. DCHECKs for the connection_id to be in time wait + // state. virtual to override in tests. + virtual void ProcessPacket( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicConnectionId connection_id, + PacketHeaderFormat header_format, + std::unique_ptr<QuicPerPacketContext> packet_context); + + // Called by the dispatcher when the underlying socket becomes writable again, + // since we might need to send pending public reset packets which we didn't + // send because the underlying socket was write blocked. + void OnBlockedWriterCanWrite() override; + + bool IsWriterBlocked() const override { + return writer_ != nullptr && writer_->IsWriteBlocked(); + } + + // Used to delete connection_id entries that have outlived their time wait + // period. + void CleanUpOldConnectionIds(); + + // If necessary, trims the oldest connections from the time-wait list until + // the size is under the configured maximum. + void TrimTimeWaitListIfNeeded(); + + // The number of connections on the time-wait list. + size_t num_connections() const { return connection_id_map_.size(); } + + // Sends a version negotiation packet for |connection_id| announcing support + // for |supported_versions| to |peer_address| from |self_address|. + virtual void SendVersionNegotiationPacket( + QuicConnectionId connection_id, + bool ietf_quic, + const ParsedQuicVersionVector& supported_versions, + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + std::unique_ptr<QuicPerPacketContext> packet_context); + + // Return a non-owning pointer to the packet writer. + QuicPacketWriter* writer() { return writer_; } + + protected: + virtual std::unique_ptr<QuicEncryptedPacket> BuildPublicReset( + const QuicPublicResetPacket& packet); + + // Creates a public reset packet and sends it or queues it to be sent later. + virtual void SendPublicReset( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicConnectionId connection_id, + bool ietf_quic, + std::unique_ptr<QuicPerPacketContext> packet_context); + + virtual void GetEndpointId(QuicString* endpoint_id) {} + + // Returns a stateless reset token which will be included in the public reset + // packet. + virtual QuicUint128 GetStatelessResetToken( + QuicConnectionId connection_id) const; + + // Internal structure to store pending termination packets. + class QueuedPacket { + public: + QueuedPacket(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + std::unique_ptr<QuicEncryptedPacket> packet) + : self_address_(self_address), + peer_address_(peer_address), + packet_(std::move(packet)) {} + QueuedPacket(const QueuedPacket&) = delete; + QueuedPacket& operator=(const QueuedPacket&) = delete; + + const QuicSocketAddress& self_address() const { return self_address_; } + const QuicSocketAddress& peer_address() const { return peer_address_; } + QuicEncryptedPacket* packet() { return packet_.get(); } + + private: + // Server address on which a packet was received for a connection_id in + // time wait state. + const QuicSocketAddress self_address_; + // Address of the peer to send this packet to. + const QuicSocketAddress peer_address_; + // The pending termination packet that is to be sent to the peer. + std::unique_ptr<QuicEncryptedPacket> packet_; + }; + + // Called right after |packet| is serialized. Either sends the packet and + // deletes it or makes pending_packets_queue_ the owner of the packet. + // Subclasses overriding this method should call this class's base + // implementation at the end of the override. + // Return true if |packet| is sent, false if it is queued. + virtual bool SendOrQueuePacket(std::unique_ptr<QueuedPacket> packet, + const QuicPerPacketContext* packet_context); + + const QuicDeque<std::unique_ptr<QueuedPacket>>& pending_packets_queue() + const { + return pending_packets_queue_; + } + + private: + friend class test::QuicDispatcherPeer; + friend class test::QuicTimeWaitListManagerPeer; + + // Decides if a packet should be sent for this connection_id based on the + // number of received packets. + bool ShouldSendResponse(int received_packet_count); + + // Sends the packet out. Returns true if the packet was successfully consumed. + // If the writer got blocked and did not buffer the packet, we'll need to keep + // the packet and retry sending. In case of all other errors we drop the + // packet. + bool WriteToWire(QueuedPacket* packet); + + // Register the alarm server to wake up at appropriate time. + void SetConnectionIdCleanUpAlarm(); + + // Removes the oldest connection from the time-wait list if it was added prior + // to "expiration_time". To unconditionally remove the oldest connection, use + // a QuicTime::Delta:Infinity(). This function modifies the + // connection_id_map_. If you plan to call this function in a loop, any + // iterators that you hold before the call to this function may be invalid + // afterward. Returns true if the oldest connection was expired. Returns + // false if the map is empty or the oldest connection has not expired. + bool MaybeExpireOldestConnection(QuicTime expiration_time); + + std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket( + QuicConnectionId connection_id); + + // A map from a recently closed connection_id to the number of packets + // received after the termination of the connection bound to the + // connection_id. + struct ConnectionIdData { + ConnectionIdData(int num_packets, + bool ietf_quic, + QuicTime time_added, + TimeWaitAction action); + + ConnectionIdData(const ConnectionIdData& other) = delete; + ConnectionIdData(ConnectionIdData&& other); + + ~ConnectionIdData(); + + int num_packets; + bool ietf_quic; + QuicTime time_added; + // Encryption level of termination_packets. + EncryptionLevel encryption_level; + // These packets may contain CONNECTION_CLOSE frames, or SREJ messages. + std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; + TimeWaitAction action; + }; + + // QuicLinkedHashMap allows lookup by ConnectionId and traversal in add order. + typedef QuicLinkedHashMap<QuicConnectionId, + ConnectionIdData, + QuicConnectionIdHash> + ConnectionIdMap; + ConnectionIdMap connection_id_map_; + + // Pending termination packets that need to be sent out to the peer when we + // are given a chance to write by the dispatcher. + QuicDeque<std::unique_ptr<QueuedPacket>> pending_packets_queue_; + + // Time period for which connection_ids should remain in time wait state. + const QuicTime::Delta time_wait_period_; + + // Alarm to clean up connection_ids that have out lived their duration in + // time wait state. + std::unique_ptr<QuicAlarm> connection_id_clean_up_alarm_; + + // Clock to efficiently measure approximate time. + const QuicClock* clock_; + + // Interface that writes given buffer to the socket. + QuicPacketWriter* writer_; + + // Interface that manages blocked writers. + Visitor* visitor_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_
diff --git a/quic/core/quic_time_wait_list_manager_test.cc b/quic/core/quic_time_wait_list_manager_test.cc new file mode 100644 index 0000000..753a832 --- /dev/null +++ b/quic/core/quic_time_wait_list_manager_test.cc
@@ -0,0 +1,583 @@ +// Copyright 2013 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/quic_time_wait_list_manager.h" + +#include <cerrno> +#include <memory> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.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_data_reader.h" +#include "net/third_party/quiche/src/quic/core/quic_framer.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h" + +using testing::_; +using testing::Args; +using testing::Assign; +using testing::DoAll; +using testing::Matcher; +using testing::NiceMock; +using testing::Return; +using testing::ReturnPointee; +using testing::StrictMock; +using testing::Truly; + +namespace quic { +namespace test { +namespace { + +class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor { + public: + FramerVisitorCapturingPublicReset(QuicConnectionId connection_id) + : connection_id_(connection_id) {} + ~FramerVisitorCapturingPublicReset() override = default; + + void OnPublicResetPacket(const QuicPublicResetPacket& public_reset) override { + public_reset_packet_ = public_reset; + } + + const QuicPublicResetPacket public_reset_packet() { + return public_reset_packet_; + } + + bool IsValidStatelessResetToken(QuicUint128 token) const override { + return token == QuicUtils::GenerateStatelessResetToken(connection_id_); + } + + void OnAuthenticatedIetfStatelessResetPacket( + const QuicIetfStatelessResetPacket& packet) override { + stateless_reset_packet_ = packet; + } + + const QuicIetfStatelessResetPacket stateless_reset_packet() { + return stateless_reset_packet_; + } + + private: + QuicPublicResetPacket public_reset_packet_; + QuicIetfStatelessResetPacket stateless_reset_packet_; + QuicConnectionId connection_id_; +}; + +class MockAlarmFactory; +class MockAlarm : public QuicAlarm { + public: + explicit MockAlarm(QuicArenaScopedPtr<Delegate> delegate, + int alarm_index, + MockAlarmFactory* factory) + : QuicAlarm(std::move(delegate)), + alarm_index_(alarm_index), + factory_(factory) {} + virtual ~MockAlarm() {} + + void SetImpl() override; + void CancelImpl() override; + + private: + int alarm_index_; + MockAlarmFactory* factory_; +}; + +class MockAlarmFactory : public QuicAlarmFactory { + public: + ~MockAlarmFactory() override {} + + // Creates a new platform-specific alarm which will be configured to notify + // |delegate| when the alarm fires. Returns an alarm allocated on the heap. + // Caller takes ownership of the new alarm, which will not yet be "set" to + // fire. + QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override { + return new MockAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate), + alarm_index_++, this); + } + QuicArenaScopedPtr<QuicAlarm> CreateAlarm( + QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, + QuicConnectionArena* arena) override { + if (arena != nullptr) { + return arena->New<MockAlarm>(std::move(delegate), alarm_index_++, this); + } + return QuicArenaScopedPtr<MockAlarm>( + new MockAlarm(std::move(delegate), alarm_index_++, this)); + } + MOCK_METHOD2(OnAlarmSet, void(int, QuicTime)); + MOCK_METHOD1(OnAlarmCancelled, void(int)); + + private: + int alarm_index_ = 0; +}; + +void MockAlarm::SetImpl() { + factory_->OnAlarmSet(alarm_index_, deadline()); +} + +void MockAlarm::CancelImpl() { + factory_->OnAlarmCancelled(alarm_index_); +} + +class QuicTimeWaitListManagerTest : public QuicTest { + protected: + QuicTimeWaitListManagerTest() + : time_wait_list_manager_(&writer_, &visitor_, &clock_, &alarm_factory_), + connection_id_(TestConnectionId(45)), + peer_address_(TestPeerIPAddress(), kTestPort), + writer_is_blocked_(false) {} + + ~QuicTimeWaitListManagerTest() override = default; + + void SetUp() override { + EXPECT_CALL(writer_, IsWriteBlocked()) + .WillRepeatedly(ReturnPointee(&writer_is_blocked_)); + } + + void AddConnectionId(QuicConnectionId connection_id, + QuicTimeWaitListManager::TimeWaitAction action) { + AddConnectionId(connection_id, QuicVersionMax(), action, nullptr); + } + + void AddStatelessConnectionId(QuicConnectionId connection_id) { + std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; + termination_packets.push_back(std::unique_ptr<QuicEncryptedPacket>( + new QuicEncryptedPacket(nullptr, 0, false))); + time_wait_list_manager_.AddConnectionIdToTimeWait( + connection_id, false, QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + ENCRYPTION_NONE, &termination_packets); + } + + void AddConnectionId( + QuicConnectionId connection_id, + ParsedQuicVersion version, + QuicTimeWaitListManager::TimeWaitAction action, + std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets) { + time_wait_list_manager_.AddConnectionIdToTimeWait( + connection_id, version.transport_version > QUIC_VERSION_43, action, + ENCRYPTION_NONE, packets); + } + + bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) { + return time_wait_list_manager_.IsConnectionIdInTimeWait(connection_id); + } + + void ProcessPacket(QuicConnectionId connection_id) { + time_wait_list_manager_.ProcessPacket( + self_address_, peer_address_, connection_id, GOOGLE_QUIC_PACKET, + QuicMakeUnique<QuicPerPacketContext>()); + } + + QuicEncryptedPacket* ConstructEncryptedPacket( + QuicConnectionId destination_connection_id, + QuicConnectionId source_connection_id, + uint64_t packet_number) { + return quic::test::ConstructEncryptedPacket(destination_connection_id, + source_connection_id, false, + false, packet_number, "data"); + } + + MockClock clock_; + MockAlarmFactory alarm_factory_; + NiceMock<MockPacketWriter> writer_; + StrictMock<MockQuicSessionVisitor> visitor_; + QuicTimeWaitListManager time_wait_list_manager_; + QuicConnectionId connection_id_; + QuicSocketAddress self_address_; + QuicSocketAddress peer_address_; + bool writer_is_blocked_; +}; + +bool ValidPublicResetPacketPredicate( + QuicConnectionId expected_connection_id, + const testing::tuple<const char*, int>& packet_buffer) { + FramerVisitorCapturingPublicReset visitor(expected_connection_id); + QuicFramer framer(AllSupportedVersions(), QuicTime::Zero(), + Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength); + framer.set_visitor(&visitor); + QuicEncryptedPacket encrypted(testing::get<0>(packet_buffer), + testing::get<1>(packet_buffer)); + framer.ProcessPacket(encrypted); + QuicPublicResetPacket packet = visitor.public_reset_packet(); + bool public_reset_is_valid = + expected_connection_id == packet.connection_id && + TestPeerIPAddress() == packet.client_address.host() && + kTestPort == packet.client_address.port(); + + QuicIetfStatelessResetPacket stateless_reset = + visitor.stateless_reset_packet(); + + QuicUint128 expected_stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(expected_connection_id); + + bool stateless_reset_is_valid = + stateless_reset.stateless_reset_token == expected_stateless_reset_token; + + return public_reset_is_valid || stateless_reset_is_valid; +} + +Matcher<const testing::tuple<const char*, int>> PublicResetPacketEq( + QuicConnectionId connection_id) { + return Truly( + [connection_id](const testing::tuple<const char*, int> packet_buffer) { + return ValidPublicResetPacketPredicate(connection_id, packet_buffer); + }); +} + +TEST_F(QuicTimeWaitListManagerTest, CheckConnectionIdInTimeWait) { + EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_)); + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + AddConnectionId(connection_id_, QuicTimeWaitListManager::DO_NOTHING); + EXPECT_EQ(1u, time_wait_list_manager_.num_connections()); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); +} + +TEST_F(QuicTimeWaitListManagerTest, CheckStatelessConnectionIdInTimeWait) { + EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_)); + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + AddStatelessConnectionId(connection_id_); + EXPECT_EQ(1u, time_wait_list_manager_.num_connections()); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); +} + +TEST_F(QuicTimeWaitListManagerTest, SendVersionNegotiationPacket) { + std::unique_ptr<QuicEncryptedPacket> packet( + QuicFramer::BuildVersionNegotiationPacket(connection_id_, false, + AllSupportedVersions())); + EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(), + peer_address_, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + + time_wait_list_manager_.SendVersionNegotiationPacket( + connection_id_, false, AllSupportedVersions(), self_address_, + peer_address_, QuicMakeUnique<QuicPerPacketContext>()); + EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); +} + +TEST_F(QuicTimeWaitListManagerTest, SendConnectionClose) { + const size_t kConnectionCloseLength = 100; + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; + termination_packets.push_back( + std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket( + new char[kConnectionCloseLength], kConnectionCloseLength, true))); + AddConnectionId(connection_id_, QuicVersionMax(), + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + &termination_packets); + EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength, + self_address_.host(), peer_address_, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + + ProcessPacket(connection_id_); +} + +TEST_F(QuicTimeWaitListManagerTest, SendTwoConnectionCloses) { + const size_t kConnectionCloseLength = 100; + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; + termination_packets.push_back( + std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket( + new char[kConnectionCloseLength], kConnectionCloseLength, true))); + termination_packets.push_back( + std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket( + new char[kConnectionCloseLength], kConnectionCloseLength, true))); + AddConnectionId(connection_id_, QuicVersionMax(), + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + &termination_packets); + EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength, + self_address_.host(), peer_address_, _)) + .Times(2) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + + ProcessPacket(connection_id_); +} + +TEST_F(QuicTimeWaitListManagerTest, SendPublicReset) { + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + AddConnectionId(connection_id_, + QuicTimeWaitListManager::SEND_STATELESS_RESET); + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id_))) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + + ProcessPacket(connection_id_); +} + +TEST_F(QuicTimeWaitListManagerTest, SendPublicResetWithExponentialBackOff) { + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + AddConnectionId(connection_id_, + QuicTimeWaitListManager::SEND_STATELESS_RESET); + EXPECT_EQ(1u, time_wait_list_manager_.num_connections()); + for (int packet_number = 1; packet_number < 101; ++packet_number) { + if ((packet_number & (packet_number - 1)) == 0) { + EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + } + ProcessPacket(connection_id_); + // Send public reset with exponential back off. + if ((packet_number & (packet_number - 1)) == 0) { + EXPECT_TRUE(QuicTimeWaitListManagerPeer::ShouldSendResponse( + &time_wait_list_manager_, packet_number)); + } else { + EXPECT_FALSE(QuicTimeWaitListManagerPeer::ShouldSendResponse( + &time_wait_list_manager_, packet_number)); + } + } +} + +TEST_F(QuicTimeWaitListManagerTest, NoPublicResetForStatelessConnections) { + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + AddStatelessConnectionId(connection_id_); + + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + + ProcessPacket(connection_id_); +} + +TEST_F(QuicTimeWaitListManagerTest, CleanUpOldConnectionIds) { + const size_t kConnectionIdCount = 100; + const size_t kOldConnectionIdCount = 31; + + // Add connection_ids such that their expiry time is time_wait_period_. + for (uint64_t conn_id = 1; conn_id <= kOldConnectionIdCount; ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id)); + AddConnectionId(connection_id, QuicTimeWaitListManager::DO_NOTHING); + } + EXPECT_EQ(kOldConnectionIdCount, time_wait_list_manager_.num_connections()); + + // Add remaining connection_ids such that their add time is + // 2 * time_wait_period_. + const QuicTime::Delta time_wait_period = + QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_); + clock_.AdvanceTime(time_wait_period); + for (uint64_t conn_id = kOldConnectionIdCount + 1; + conn_id <= kConnectionIdCount; ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id)); + AddConnectionId(connection_id, QuicTimeWaitListManager::DO_NOTHING); + } + EXPECT_EQ(kConnectionIdCount, time_wait_list_manager_.num_connections()); + + QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39); + // Now set the current time as time_wait_period + offset usecs. + clock_.AdvanceTime(offset); + // After all the old connection_ids are cleaned up, check the next alarm + // interval. + QuicTime next_alarm_time = clock_.Now() + time_wait_period - offset; + EXPECT_CALL(alarm_factory_, OnAlarmSet(_, next_alarm_time)); + + time_wait_list_manager_.CleanUpOldConnectionIds(); + for (uint64_t conn_id = 1; conn_id <= kConnectionIdCount; ++conn_id) { + QuicConnectionId connection_id = TestConnectionId(conn_id); + EXPECT_EQ(conn_id > kOldConnectionIdCount, + IsConnectionIdInTimeWait(connection_id)) + << "kOldConnectionIdCount: " << kOldConnectionIdCount + << " connection_id: " << connection_id; + } + EXPECT_EQ(kConnectionIdCount - kOldConnectionIdCount, + time_wait_list_manager_.num_connections()); +} + +TEST_F(QuicTimeWaitListManagerTest, SendQueuedPackets) { + QuicConnectionId connection_id = TestConnectionId(1); + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id)); + AddConnectionId(connection_id, QuicTimeWaitListManager::SEND_STATELESS_RESET); + std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + connection_id, EmptyQuicConnectionId(), /*packet_number=*/234)); + // Let first write through. + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id))) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length()))); + ProcessPacket(connection_id); + + // write block for the next packet. + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id))) + .WillOnce(DoAll(Assign(&writer_is_blocked_, true), + Return(WriteResult(WRITE_STATUS_BLOCKED, EAGAIN)))); + EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_)); + ProcessPacket(connection_id); + // 3rd packet. No public reset should be sent; + ProcessPacket(connection_id); + + // write packet should not be called since we are write blocked but the + // should be queued. + QuicConnectionId other_connection_id = TestConnectionId(2); + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(other_connection_id)); + AddConnectionId(other_connection_id, + QuicTimeWaitListManager::SEND_STATELESS_RESET); + std::unique_ptr<QuicEncryptedPacket> other_packet(ConstructEncryptedPacket( + other_connection_id, EmptyQuicConnectionId(), /*packet_number=*/23423)); + EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(0); + EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_)); + ProcessPacket(other_connection_id); + EXPECT_EQ(2u, time_wait_list_manager_.num_connections()); + + // Now expect all the write blocked public reset packets to be sent again. + writer_is_blocked_ = false; + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id))) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length()))); + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .With(Args<0, 1>(PublicResetPacketEq(other_connection_id))) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length()))); + time_wait_list_manager_.OnBlockedWriterCanWrite(); +} + +TEST_F(QuicTimeWaitListManagerTest, AddConnectionIdTwice) { + // Add connection_ids such that their expiry time is time_wait_period_. + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + AddConnectionId(connection_id_, QuicTimeWaitListManager::DO_NOTHING); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); + const size_t kConnectionCloseLength = 100; + std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; + termination_packets.push_back( + std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket( + new char[kConnectionCloseLength], kConnectionCloseLength, true))); + AddConnectionId(connection_id_, QuicVersionMax(), + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + &termination_packets); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_)); + EXPECT_EQ(1u, time_wait_list_manager_.num_connections()); + + EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength, + self_address_.host(), peer_address_, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + + ProcessPacket(connection_id_); + + const QuicTime::Delta time_wait_period = + QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_); + + QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39); + clock_.AdvanceTime(offset + time_wait_period); + // Now set the current time as time_wait_period + offset usecs. + QuicTime next_alarm_time = clock_.Now() + time_wait_period; + EXPECT_CALL(alarm_factory_, OnAlarmSet(_, next_alarm_time)); + + time_wait_list_manager_.CleanUpOldConnectionIds(); + EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_)); + EXPECT_EQ(0u, time_wait_list_manager_.num_connections()); +} + +TEST_F(QuicTimeWaitListManagerTest, ConnectionIdsOrderedByTime) { + // Simple randomization: the values of connection_ids are randomly swapped. + // If the container is broken, the test will be 50% flaky. + const uint64_t conn_id1 = QuicRandom::GetInstance()->RandUint64() % 2; + const QuicConnectionId connection_id1 = TestConnectionId(conn_id1); + const QuicConnectionId connection_id2 = TestConnectionId(1 - conn_id1); + + // 1 will hash lower than 2, but we add it later. They should come out in the + // add order, not hash order. + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id1)); + AddConnectionId(connection_id1, QuicTimeWaitListManager::DO_NOTHING); + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(10)); + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id2)); + AddConnectionId(connection_id2, QuicTimeWaitListManager::DO_NOTHING); + EXPECT_EQ(2u, time_wait_list_manager_.num_connections()); + + const QuicTime::Delta time_wait_period = + QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_); + clock_.AdvanceTime(time_wait_period - QuicTime::Delta::FromMicroseconds(9)); + + EXPECT_CALL(alarm_factory_, OnAlarmSet(_, _)); + + time_wait_list_manager_.CleanUpOldConnectionIds(); + EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id1)); + EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id2)); + EXPECT_EQ(1u, time_wait_list_manager_.num_connections()); +} + +TEST_F(QuicTimeWaitListManagerTest, MaxConnectionsTest) { + // Basically, shut off time-based eviction. + FLAGS_quic_time_wait_list_seconds = 10000000000; + FLAGS_quic_time_wait_list_max_connections = 5; + + uint64_t current_conn_id = 0; + // Add exactly the maximum number of connections + for (int64_t i = 0; i < FLAGS_quic_time_wait_list_max_connections; ++i) { + ++current_conn_id; + QuicConnectionId current_connection_id = TestConnectionId(current_conn_id); + EXPECT_FALSE(IsConnectionIdInTimeWait(current_connection_id)); + EXPECT_CALL(visitor_, + OnConnectionAddedToTimeWaitList(current_connection_id)); + AddConnectionId(current_connection_id, QuicTimeWaitListManager::DO_NOTHING); + EXPECT_EQ(current_conn_id, time_wait_list_manager_.num_connections()); + EXPECT_TRUE(IsConnectionIdInTimeWait(current_connection_id)); + } + + // Now keep adding. Since we're already at the max, every new connection-id + // will evict the oldest one. + for (int64_t i = 0; i < FLAGS_quic_time_wait_list_max_connections; ++i) { + ++current_conn_id; + QuicConnectionId current_connection_id = TestConnectionId(current_conn_id); + const QuicConnectionId id_to_evict = TestConnectionId( + current_conn_id - FLAGS_quic_time_wait_list_max_connections); + EXPECT_TRUE(IsConnectionIdInTimeWait(id_to_evict)); + EXPECT_FALSE(IsConnectionIdInTimeWait(current_connection_id)); + EXPECT_CALL(visitor_, + OnConnectionAddedToTimeWaitList(current_connection_id)); + AddConnectionId(current_connection_id, QuicTimeWaitListManager::DO_NOTHING); + EXPECT_EQ(static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections), + time_wait_list_manager_.num_connections()); + EXPECT_FALSE(IsConnectionIdInTimeWait(id_to_evict)); + EXPECT_TRUE(IsConnectionIdInTimeWait(current_connection_id)); + } +} + +// Regression test for b/116200989. +TEST_F(QuicTimeWaitListManagerTest, + SendStatelessResetInResponseToShortHeaders) { + // This test mimics a scenario where an ENCRYPTION_NONE connection close is + // added as termination packet for an IETF connection ID. However, a short + // header packet is received later. + const size_t kConnectionCloseLength = 100; + EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); + std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets; + termination_packets.push_back( + std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket( + new char[kConnectionCloseLength], kConnectionCloseLength, true))); + // Add an ENCRYPTION_NONE termination packet. + time_wait_list_manager_.AddConnectionIdToTimeWait( + connection_id_, /*ietf_quic=*/true, + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_NONE, + &termination_packets); + + if (GetQuicReloadableFlag(quic_always_reset_short_header_packets)) { + // Termination packet is not encrypted, instead, send stateless reset. + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .With(Args<0, 1>(PublicResetPacketEq(connection_id_))) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); + } else { + // An unprocessable connection close is sent to peer. + EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength, + self_address_.host(), peer_address_, _)) + .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1))); + } + // Processes IETF short header packet. + time_wait_list_manager_.ProcessPacket( + self_address_, peer_address_, connection_id_, + IETF_QUIC_SHORT_HEADER_PACKET, QuicMakeUnique<QuicPerPacketContext>()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_trace_visitor.cc b/quic/core/quic_trace_visitor.cc new file mode 100644 index 0000000..ba2ed7f --- /dev/null +++ b/quic/core/quic_trace_visitor.cc
@@ -0,0 +1,329 @@ +// Copyright (c) 2018 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/quic_trace_visitor.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +quic_trace::EncryptionLevel EncryptionLevelToProto(EncryptionLevel level) { + switch (level) { + case ENCRYPTION_NONE: + return quic_trace::ENCRYPTION_INITIAL; + case ENCRYPTION_ZERO_RTT: + return quic_trace::ENCRYPTION_0RTT; + case ENCRYPTION_FORWARD_SECURE: + return quic_trace::ENCRYPTION_1RTT; + case NUM_ENCRYPTION_LEVELS: + QUIC_BUG << "Invalid encryption level specified"; + return quic_trace::ENCRYPTION_UNKNOWN; + } +} + +QuicTraceVisitor::QuicTraceVisitor(const QuicConnection* connection) + : connection_(connection), + start_time_(connection_->clock()->ApproximateNow()) { + QuicString binary_connection_id(connection->connection_id().data(), + connection->connection_id().length()); + // We assume that the connection ID in gQUIC is equivalent to the + // server-chosen client-selected ID. + switch (connection->perspective()) { + case Perspective::IS_CLIENT: + trace_.set_destination_connection_id(binary_connection_id); + break; + case Perspective::IS_SERVER: + trace_.set_source_connection_id(binary_connection_id); + break; + } +} + +void QuicTraceVisitor::OnPacketSent(const SerializedPacket& serialized_packet, + QuicPacketNumber /*original_packet_number*/, + TransmissionType /*transmission_type*/, + QuicTime sent_time) { + quic_trace::Event* event = trace_.add_events(); + event->set_event_type(quic_trace::PACKET_SENT); + event->set_time_us(ConvertTimestampToRecordedFormat(sent_time)); + event->set_packet_number(serialized_packet.packet_number.ToUint64()); + event->set_packet_size(serialized_packet.encrypted_length); + event->set_encryption_level( + EncryptionLevelToProto(serialized_packet.encryption_level)); + + for (const QuicFrame& frame : serialized_packet.retransmittable_frames) { + switch (frame.type) { + case STREAM_FRAME: + case RST_STREAM_FRAME: + case CONNECTION_CLOSE_FRAME: + case WINDOW_UPDATE_FRAME: + case BLOCKED_FRAME: + case PING_FRAME: + PopulateFrameInfo(frame, event->add_frames()); + break; + + case PADDING_FRAME: + case MTU_DISCOVERY_FRAME: + case STOP_WAITING_FRAME: + case ACK_FRAME: + QUIC_BUG + << "Frames of type are not retransmittable and are not supposed " + "to be in retransmittable_frames"; + break; + + // New IETF frames, not used in current gQUIC version. + case APPLICATION_CLOSE_FRAME: + case NEW_CONNECTION_ID_FRAME: + case RETIRE_CONNECTION_ID_FRAME: + case MAX_STREAM_ID_FRAME: + case STREAM_ID_BLOCKED_FRAME: + case PATH_RESPONSE_FRAME: + case PATH_CHALLENGE_FRAME: + case STOP_SENDING_FRAME: + case MESSAGE_FRAME: + case CRYPTO_FRAME: + case NEW_TOKEN_FRAME: + break; + + // Ignore gQUIC-specific frames. + case GOAWAY_FRAME: + break; + + case NUM_FRAME_TYPES: + QUIC_BUG << "Unknown frame type encountered"; + break; + } + } + + // Output PCC DebugState on packet sent for analysis. + if (connection_->sent_packet_manager() + .GetSendAlgorithm() + ->GetCongestionControlType() == kPCC) { + PopulateTransportState(event->mutable_transport_state()); + } +} + +void QuicTraceVisitor::PopulateFrameInfo(const QuicFrame& frame, + quic_trace::Frame* frame_record) { + switch (frame.type) { + case STREAM_FRAME: { + frame_record->set_frame_type(quic_trace::STREAM); + + quic_trace::StreamFrameInfo* info = + frame_record->mutable_stream_frame_info(); + info->set_stream_id(frame.stream_frame.stream_id); + info->set_fin(frame.stream_frame.fin); + info->set_offset(frame.stream_frame.offset); + info->set_length(frame.stream_frame.data_length); + break; + } + + case ACK_FRAME: { + frame_record->set_frame_type(quic_trace::ACK); + + quic_trace::AckInfo* info = frame_record->mutable_ack_info(); + info->set_ack_delay_us(frame.ack_frame->ack_delay_time.ToMicroseconds()); + for (const auto& interval : frame.ack_frame->packets) { + quic_trace::AckBlock* block = info->add_acked_packets(); + // We record intervals as [a, b], whereas the in-memory representation + // we currently use is [a, b). + block->set_first_packet(interval.min().ToUint64()); + block->set_last_packet(interval.max().ToUint64() - 1); + } + break; + } + + case RST_STREAM_FRAME: { + frame_record->set_frame_type(quic_trace::RESET_STREAM); + + quic_trace::ResetStreamInfo* info = + frame_record->mutable_reset_stream_info(); + info->set_stream_id(frame.rst_stream_frame->stream_id); + info->set_final_offset(frame.rst_stream_frame->byte_offset); + info->set_application_error_code(frame.rst_stream_frame->error_code); + break; + } + + case CONNECTION_CLOSE_FRAME: { + frame_record->set_frame_type(quic_trace::CONNECTION_CLOSE); + + quic_trace::CloseInfo* info = frame_record->mutable_close_info(); + info->set_error_code(frame.connection_close_frame->error_code); + info->set_reason_phrase(frame.connection_close_frame->error_details); + break; + } + + case GOAWAY_FRAME: + // Do not bother logging this since the frame in question is + // gQUIC-specific. + break; + + case WINDOW_UPDATE_FRAME: { + bool is_connection = frame.window_update_frame->stream_id == 0; + frame_record->set_frame_type(is_connection ? quic_trace::MAX_DATA + : quic_trace::MAX_STREAM_DATA); + + quic_trace::FlowControlInfo* info = + frame_record->mutable_flow_control_info(); + info->set_max_data(frame.window_update_frame->byte_offset); + if (!is_connection) { + info->set_stream_id(frame.window_update_frame->stream_id); + } + break; + } + + case BLOCKED_FRAME: { + bool is_connection = frame.blocked_frame->stream_id == 0; + frame_record->set_frame_type(is_connection ? quic_trace::BLOCKED + : quic_trace::STREAM_BLOCKED); + + quic_trace::FlowControlInfo* info = + frame_record->mutable_flow_control_info(); + if (!is_connection) { + info->set_stream_id(frame.window_update_frame->stream_id); + } + break; + } + + case PING_FRAME: + case MTU_DISCOVERY_FRAME: + frame_record->set_frame_type(quic_trace::PING); + break; + + case PADDING_FRAME: + frame_record->set_frame_type(quic_trace::PADDING); + break; + + case STOP_WAITING_FRAME: + // We're going to pretend those do not exist. + break; + + // New IETF frames, not used in current gQUIC version. + case APPLICATION_CLOSE_FRAME: + case NEW_CONNECTION_ID_FRAME: + case RETIRE_CONNECTION_ID_FRAME: + case MAX_STREAM_ID_FRAME: + case STREAM_ID_BLOCKED_FRAME: + case PATH_RESPONSE_FRAME: + case PATH_CHALLENGE_FRAME: + case STOP_SENDING_FRAME: + case MESSAGE_FRAME: + case CRYPTO_FRAME: + case NEW_TOKEN_FRAME: + break; + + case NUM_FRAME_TYPES: + QUIC_BUG << "Unknown frame type encountered"; + break; + } +} + +void QuicTraceVisitor::OnIncomingAck( + const QuicAckFrame& ack_frame, + QuicTime ack_receive_time, + QuicPacketNumber /*largest_observed*/, + bool /*rtt_updated*/, + QuicPacketNumber /*least_unacked_sent_packet*/) { + quic_trace::Event* event = trace_.add_events(); + event->set_time_us(ConvertTimestampToRecordedFormat(ack_receive_time)); + event->set_packet_number( + connection_->received_packet_manager().GetLargestObserved().ToUint64()); + event->set_event_type(quic_trace::PACKET_RECEIVED); + + // TODO(vasilvv): consider removing this copy. + QuicAckFrame copy_of_ack = ack_frame; + PopulateFrameInfo(QuicFrame(©_of_ack), event->add_frames()); + PopulateTransportState(event->mutable_transport_state()); +} + +void QuicTraceVisitor::OnPacketLoss(QuicPacketNumber lost_packet_number, + TransmissionType transmission_type, + QuicTime detection_time) { + quic_trace::Event* event = trace_.add_events(); + event->set_time_us(ConvertTimestampToRecordedFormat(detection_time)); + event->set_event_type(quic_trace::PACKET_LOST); + event->set_packet_number(lost_packet_number.ToUint64()); + PopulateTransportState(event->mutable_transport_state()); +} + +void QuicTraceVisitor::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + const QuicTime& receive_time) { + quic_trace::Event* event = trace_.add_events(); + event->set_time_us(ConvertTimestampToRecordedFormat(receive_time)); + event->set_event_type(quic_trace::PACKET_RECEIVED); + event->set_packet_number( + connection_->received_packet_manager().GetLargestObserved().ToUint64()); + + // TODO(vasilvv): consider removing this copy. + QuicWindowUpdateFrame copy_of_update = frame; + PopulateFrameInfo(QuicFrame(©_of_update), event->add_frames()); +} + +void QuicTraceVisitor::OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) { + uint32_t tag = QuicEndian::HostToNet32(CreateQuicVersionLabel(version)); + QuicString binary_tag(reinterpret_cast<const char*>(&tag), sizeof(tag)); + trace_.set_protocol_version(binary_tag); +} + +void QuicTraceVisitor::OnApplicationLimited() { + quic_trace::Event* event = trace_.add_events(); + event->set_time_us( + ConvertTimestampToRecordedFormat(connection_->clock()->ApproximateNow())); + event->set_event_type(quic_trace::APPLICATION_LIMITED); +} + +void QuicTraceVisitor::OnAdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + quic_trace::Event* event = trace_.add_events(); + event->set_time_us( + ConvertTimestampToRecordedFormat(connection_->clock()->ApproximateNow())); + event->set_event_type(quic_trace::EXTERNAL_PARAMETERS); + + quic_trace::ExternalNetworkParameters* parameters = + event->mutable_external_network_parameters(); + if (!bandwidth.IsZero()) { + parameters->set_bandwidth_bps(bandwidth.ToBitsPerSecond()); + } + if (!rtt.IsZero()) { + parameters->set_rtt_us(rtt.ToMicroseconds()); + } +} + +uint64_t QuicTraceVisitor::ConvertTimestampToRecordedFormat( + QuicTime timestamp) { + if (timestamp < start_time_) { + QUIC_BUG << "Timestamp went back in time while recording a trace"; + return 0; + } + + return (timestamp - start_time_).ToMicroseconds(); +} + +void QuicTraceVisitor::PopulateTransportState( + quic_trace::TransportState* state) { + const RttStats* rtt_stats = connection_->sent_packet_manager().GetRttStats(); + state->set_min_rtt_us(rtt_stats->min_rtt().ToMicroseconds()); + state->set_smoothed_rtt_us(rtt_stats->smoothed_rtt().ToMicroseconds()); + state->set_last_rtt_us(rtt_stats->latest_rtt().ToMicroseconds()); + + state->set_cwnd_bytes( + connection_->sent_packet_manager().GetCongestionWindowInBytes()); + QuicByteCount in_flight = + connection_->sent_packet_manager().GetBytesInFlight(); + state->set_in_flight_bytes(in_flight); + state->set_pacing_rate_bps(connection_->sent_packet_manager() + .GetSendAlgorithm() + ->PacingRate(in_flight) + .ToBitsPerSecond()); + + if (connection_->sent_packet_manager() + .GetSendAlgorithm() + ->GetCongestionControlType() == kPCC) { + state->set_congestion_control_state( + connection_->sent_packet_manager().GetSendAlgorithm()->GetDebugState()); + } +} + +} // namespace quic
diff --git a/quic/core/quic_trace_visitor.h b/quic/core/quic_trace_visitor.h new file mode 100644 index 0000000..a70b150 --- /dev/null +++ b/quic/core/quic_trace_visitor.h
@@ -0,0 +1,70 @@ +// Copyright (c) 2018 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_QUIC_TRACE_VISITOR_H_ +#define QUICHE_QUIC_CORE_QUIC_TRACE_VISITOR_H_ + +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "third_party/quic_trace/lib/quic_trace.pb.h" + +namespace quic { + +// Records a QUIC trace protocol buffer for a QuicConnection. It's the +// responsibility of the user of this visitor to process or store the resulting +// trace, which can be accessed via trace(). +class QuicTraceVisitor : public QuicConnectionDebugVisitor { + public: + explicit QuicTraceVisitor(const QuicConnection* connection); + + void OnPacketSent(const SerializedPacket& serialized_packet, + QuicPacketNumber original_packet_number, + TransmissionType transmission_type, + QuicTime sent_time) override; + + void OnIncomingAck(const QuicAckFrame& ack_frame, + QuicTime ack_receive_time, + QuicPacketNumber largest_observed, + bool rtt_updated, + QuicPacketNumber least_unacked_sent_packet) override; + + void OnPacketLoss(QuicPacketNumber lost_packet_number, + TransmissionType transmission_type, + QuicTime detection_time) override; + + void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + const QuicTime& receive_time) override; + + void OnSuccessfulVersionNegotiation( + const ParsedQuicVersion& version) override; + + void OnApplicationLimited() override; + + void OnAdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) override; + + // Returns a mutable pointer to the trace. The trace is owned by the + // visitor, but can be moved using Swap() method after the connection is + // finished. + quic_trace::Trace* trace() { return &trace_; } + + private: + // Converts QuicTime into a microsecond delta w.r.t. the beginning of the + // connection. + uint64_t ConvertTimestampToRecordedFormat(QuicTime timestamp); + // Populates a quic_trace::Frame message from |frame|. + void PopulateFrameInfo(const QuicFrame& frame, + quic_trace::Frame* frame_record); + // Populates a quic_trace::TransportState message from the associated + // connection. + void PopulateTransportState(quic_trace::TransportState* state); + + quic_trace::Trace trace_; + const QuicConnection* connection_; + const QuicTime start_time_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_TRACE_VISITOR_H_
diff --git a/quic/core/quic_trace_visitor_test.cc b/quic/core/quic_trace_visitor_test.cc new file mode 100644 index 0000000..008026d --- /dev/null +++ b/quic/core/quic_trace_visitor_test.cc
@@ -0,0 +1,168 @@ +// Copyright (c) 2018 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/quic_trace_visitor.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" + +namespace quic { +namespace { + +const QuicByteCount kTransferSize = 1000 * kMaxPacketSize; +const QuicByteCount kTestStreamNumber = 3; +const QuicTime::Delta kDelay = QuicTime::Delta::FromMilliseconds(20); + +// The trace for this test is generated using a simulator transfer. +class QuicTraceVisitorTest : public QuicTest { + public: + QuicTraceVisitorTest() { + QuicConnectionId connection_id = test::TestConnectionId(); + simulator::Simulator simulator; + simulator::QuicEndpoint client(&simulator, "Client", "Server", + Perspective::IS_CLIENT, connection_id); + simulator::QuicEndpoint server(&simulator, "Server", "Client", + Perspective::IS_SERVER, connection_id); + + const QuicBandwidth kBandwidth = QuicBandwidth::FromKBitsPerSecond(1000); + const QuicByteCount kBdp = kBandwidth * (2 * kDelay); + + // Create parameters such that some loss is observed. + simulator::Switch network_switch(&simulator, "Switch", 8, 0.5 * kBdp); + simulator::SymmetricLink client_link(&client, network_switch.port(1), + 2 * kBandwidth, kDelay); + simulator::SymmetricLink server_link(&server, network_switch.port(2), + kBandwidth, kDelay); + + QuicTraceVisitor visitor(client.connection()); + client.connection()->set_debug_visitor(&visitor); + + // Transfer about a megabyte worth of data from client to server. + const QuicTime::Delta kDeadline = + 3 * kBandwidth.TransferTime(kTransferSize); + client.AddBytesToTransfer(kTransferSize); + bool simulator_result = simulator.RunUntilOrTimeout( + [&]() { return server.bytes_received() >= kTransferSize; }, kDeadline); + CHECK(simulator_result); + + // Save the trace and ensure some loss was observed. + trace_.Swap(visitor.trace()); + CHECK_NE(0u, client.connection()->GetStats().packets_retransmitted); + packets_sent_ = client.connection()->GetStats().packets_sent; + } + + std::vector<quic_trace::Event> AllEventsWithType( + quic_trace::EventType event_type) { + std::vector<quic_trace::Event> result; + for (const auto& event : trace_.events()) { + if (event.event_type() == event_type) { + result.push_back(event); + } + } + return result; + } + + protected: + quic_trace::Trace trace_; + QuicPacketCount packets_sent_; +}; + +TEST_F(QuicTraceVisitorTest, ConnectionId) { + char expected_cid[] = {0, 0, 0, 0, 0, 0, 0, 42}; + EXPECT_EQ(QuicString(expected_cid, sizeof(expected_cid)), + trace_.destination_connection_id()); +} + +TEST_F(QuicTraceVisitorTest, Version) { + QuicString version = trace_.protocol_version(); + ASSERT_EQ(4u, version.size()); + EXPECT_EQ('Q', version[0]); +} + +// Check that basic metadata about sent packets is recorded. +TEST_F(QuicTraceVisitorTest, SentPacket) { + auto sent_packets = AllEventsWithType(quic_trace::PACKET_SENT); + EXPECT_EQ(packets_sent_, sent_packets.size()); + ASSERT_GT(sent_packets.size(), 0u); + + EXPECT_EQ(sent_packets[0].packet_size(), kDefaultMaxPacketSize); + EXPECT_EQ(sent_packets[0].packet_number(), 1u); +} + +// Ensure that every stream frame that was sent is recorded. +TEST_F(QuicTraceVisitorTest, SentStream) { + auto sent_packets = AllEventsWithType(quic_trace::PACKET_SENT); + + QuicIntervalSet<QuicStreamOffset> offsets; + for (const quic_trace::Event& packet : sent_packets) { + for (const quic_trace::Frame& frame : packet.frames()) { + if (frame.frame_type() != quic_trace::STREAM) { + continue; + } + + const quic_trace::StreamFrameInfo& info = frame.stream_frame_info(); + if (info.stream_id() != kTestStreamNumber) { + continue; + } + + ASSERT_GT(info.length(), 0u); + offsets.Add(info.offset(), info.offset() + info.length()); + } + } + + ASSERT_EQ(1u, offsets.Size()); + EXPECT_EQ(0u, offsets.begin()->min()); + EXPECT_EQ(kTransferSize, offsets.rbegin()->max()); +} + +// Ensure that all packets are either acknowledged or lost. +TEST_F(QuicTraceVisitorTest, AckPackets) { + QuicIntervalSet<QuicPacketNumber> packets; + for (const quic_trace::Event& packet : trace_.events()) { + if (packet.event_type() == quic_trace::PACKET_RECEIVED) { + for (const quic_trace::Frame& frame : packet.frames()) { + if (frame.frame_type() != quic_trace::ACK) { + continue; + } + + const quic_trace::AckInfo& info = frame.ack_info(); + for (const auto& block : info.acked_packets()) { + packets.Add(QuicPacketNumber(block.first_packet()), + QuicPacketNumber(block.last_packet()) + 1); + } + } + } + if (packet.event_type() == quic_trace::PACKET_LOST) { + packets.Add(QuicPacketNumber(packet.packet_number()), + QuicPacketNumber(packet.packet_number()) + 1); + } + } + + ASSERT_EQ(1u, packets.Size()); + EXPECT_EQ(QuicPacketNumber(1u), packets.begin()->min()); + // We leave some room (20 packets) for the packets which did not receive + // conclusive status at the end of simulation. + EXPECT_GT(packets.rbegin()->max(), QuicPacketNumber(packets_sent_ - 20)); +} + +TEST_F(QuicTraceVisitorTest, TransportState) { + auto acks = AllEventsWithType(quic_trace::PACKET_RECEIVED); + ASSERT_EQ(1, acks[0].frames_size()); + ASSERT_EQ(quic_trace::ACK, acks[0].frames(0).frame_type()); + + // Check that min-RTT at the end is a reasonable approximation. + EXPECT_LE((4 * kDelay).ToMicroseconds() * 1., + acks.rbegin()->transport_state().min_rtt_us()); + EXPECT_GE((4 * kDelay).ToMicroseconds() * 1.25, + acks.rbegin()->transport_state().min_rtt_us()); +} + +} // namespace +} // namespace quic
diff --git a/quic/core/quic_transmission_info.cc b/quic/core/quic_transmission_info.cc new file mode 100644 index 0000000..6e9d02c --- /dev/null +++ b/quic/core/quic_transmission_info.cc
@@ -0,0 +1,43 @@ +// 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/quic_transmission_info.h" + +namespace quic { + +QuicTransmissionInfo::QuicTransmissionInfo() + : encryption_level(ENCRYPTION_NONE), + packet_number_length(PACKET_1BYTE_PACKET_NUMBER), + bytes_sent(0), + sent_time(QuicTime::Zero()), + transmission_type(NOT_RETRANSMISSION), + in_flight(false), + state(OUTSTANDING), + has_crypto_handshake(false), + num_padding_bytes(0) {} + +QuicTransmissionInfo::QuicTransmissionInfo( + EncryptionLevel level, + QuicPacketNumberLength packet_number_length, + TransmissionType transmission_type, + QuicTime sent_time, + QuicPacketLength bytes_sent, + bool has_crypto_handshake, + int num_padding_bytes) + : encryption_level(level), + packet_number_length(packet_number_length), + bytes_sent(bytes_sent), + sent_time(sent_time), + transmission_type(transmission_type), + in_flight(false), + state(OUTSTANDING), + has_crypto_handshake(has_crypto_handshake), + num_padding_bytes(num_padding_bytes) {} + +QuicTransmissionInfo::QuicTransmissionInfo(const QuicTransmissionInfo& other) = + default; + +QuicTransmissionInfo::~QuicTransmissionInfo() {} + +} // namespace quic
diff --git a/quic/core/quic_transmission_info.h b/quic/core/quic_transmission_info.h new file mode 100644 index 0000000..74b18d2 --- /dev/null +++ b/quic/core/quic_transmission_info.h
@@ -0,0 +1,67 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_QUIC_TRANSMISSION_INFO_H_ +#define QUICHE_QUIC_CORE_QUIC_TRANSMISSION_INFO_H_ + +#include <list> + +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Stores details of a single sent packet. +struct QUIC_EXPORT_PRIVATE QuicTransmissionInfo { + // Used by STL when assigning into a map. + QuicTransmissionInfo(); + + // Constructs a Transmission with a new all_transmissions set + // containing |packet_number|. + QuicTransmissionInfo(EncryptionLevel level, + QuicPacketNumberLength packet_number_length, + TransmissionType transmission_type, + QuicTime sent_time, + QuicPacketLength bytes_sent, + bool has_crypto_handshake, + int num_padding_bytes); + + QuicTransmissionInfo(const QuicTransmissionInfo& other); + + ~QuicTransmissionInfo(); + + QuicFrames retransmittable_frames; + EncryptionLevel encryption_level; + QuicPacketNumberLength packet_number_length; + QuicPacketLength bytes_sent; + QuicTime sent_time; + // Reason why this packet was transmitted. + TransmissionType transmission_type; + // In flight packets have not been abandoned or lost. + bool in_flight; + // State of this packet. + SentPacketState state; + // True if the packet contains stream data from the crypto stream. + bool has_crypto_handshake; + // Non-zero if the packet needs padding if it's retransmitted. + int16_t num_padding_bytes; + // Stores the packet number of the next retransmission of this packet. + // Zero if the packet has not been retransmitted. + // TODO(fayang): rename this to first_sent_after_loss_ when deprecating + // QUIC_VERSION_41. + QuicPacketNumber retransmission; + // The largest_acked in the ack frame, if the packet contains an ack. + QuicPacketNumber largest_acked; +}; +// TODO(ianswett): Add static_assert when size of this struct is reduced below +// 64 bytes. +// NOTE(vlovich): Existing static_assert removed because padding differences on +// 64-bit iOS resulted in an 88-byte struct that is greater than the 84-byte +// limit on other platforms. Removing per ianswett's request. + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_TRANSMISSION_INFO_H_
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc new file mode 100644 index 0000000..16bf52d --- /dev/null +++ b/quic/core/quic_types.cc
@@ -0,0 +1,53 @@ +// Copyright 2014 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/quic_types.h" + +namespace quic { + +QuicConsumedData::QuicConsumedData(size_t bytes_consumed, bool fin_consumed) + : bytes_consumed(bytes_consumed), fin_consumed(fin_consumed) {} + +std::ostream& operator<<(std::ostream& os, const QuicConsumedData& s) { + os << "bytes_consumed: " << s.bytes_consumed + << " fin_consumed: " << s.fin_consumed; + return os; +} + +std::ostream& operator<<(std::ostream& os, const Perspective& s) { + if (s == Perspective::IS_SERVER) { + os << "IS_SERVER"; + } else { + os << "IS_CLIENT"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const AckedPacket& acked_packet) { + os << "{ packet_number: " << acked_packet.packet_number + << ", bytes_acked: " << acked_packet.bytes_acked << ", receive_timestamp: " + << acked_packet.receive_timestamp.ToDebuggingValue() << "} "; + return os; +} + +WriteResult::WriteResult() : status(WRITE_STATUS_ERROR), bytes_written(0) {} + +WriteResult::WriteResult(WriteStatus status, int bytes_written_or_error_code) + : status(status), bytes_written(bytes_written_or_error_code) {} + +std::ostream& operator<<(std::ostream& os, const WriteResult& s) { + os << "{ status: " << s.status; + if (s.status == WRITE_STATUS_OK) { + os << ", bytes_written: " << s.bytes_written; + } else { + os << ", error_code: " << s.error_code; + } + os << " }"; + return os; +} + +MessageResult::MessageResult(MessageStatus status, QuicMessageId message_id) + : status(status), message_id(message_id) {} + +} // namespace quic
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h new file mode 100644 index 0000000..acf3e02 --- /dev/null +++ b/quic/core/quic_types.h
@@ -0,0 +1,582 @@ +// Copyright 2014 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_QUIC_TYPES_H_ +#define QUICHE_QUIC_CORE_QUIC_TYPES_H_ + +#include <array> +#include <cstddef> +#include <map> +#include <ostream> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_number.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +typedef uint16_t QuicPacketLength; +typedef uint32_t QuicControlFrameId; +typedef uint32_t QuicHeaderId; +typedef uint32_t QuicMessageId; + +// TODO(fkastenholz): Should update this to 64 bits for V99. +typedef uint32_t QuicStreamId; + +typedef uint64_t QuicByteCount; +typedef uint64_t QuicPacketCount; +typedef uint64_t QuicPublicResetNonceProof; +typedef uint64_t QuicStreamOffset; +typedef std::array<char, 32> DiversificationNonce; +typedef std::vector<std::pair<QuicPacketNumber, QuicTime>> PacketTimeVector; + +typedef uint64_t QuicIetfStreamDataLength; +typedef uint64_t QuicIetfStreamId; +typedef uint64_t QuicIetfStreamOffset; + +const size_t kQuicPathFrameBufferSize = 8; +typedef std::array<uint8_t, kQuicPathFrameBufferSize> QuicPathFrameBuffer; + +// Application error code used in the QUIC Stop Sending frame. +typedef uint16_t QuicApplicationErrorCode; + +// The connection id sequence number specifies the order that connection +// ids must be used in. This is also the sequence number carried in +// the IETF QUIC NEW_CONNECTION_ID and RETIRE_CONNECTION_ID frames. +typedef uint64_t QuicConnectionIdSequenceNumber; + +// A struct for functions which consume data payloads and fins. +struct QUIC_EXPORT_PRIVATE QuicConsumedData { + QuicConsumedData(size_t bytes_consumed, bool fin_consumed); + + // By default, gtest prints the raw bytes of an object. The bool data + // member causes this object to have padding bytes, which causes the + // default gtest object printer to read uninitialize memory. So we need + // to teach gtest how to print this object. + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicConsumedData& s); + + // How many bytes were consumed. + size_t bytes_consumed; + + // True if an incoming fin was consumed. + bool fin_consumed; +}; + +// QuicAsyncStatus enumerates the possible results of an asynchronous +// operation. +enum QuicAsyncStatus { + QUIC_SUCCESS = 0, + QUIC_FAILURE = 1, + // QUIC_PENDING results from an operation that will occur asynchronously. When + // the operation is complete, a callback's |Run| method will be called. + QUIC_PENDING = 2, +}; + +// TODO(wtc): see if WriteStatus can be replaced by QuicAsyncStatus. +enum WriteStatus { + WRITE_STATUS_OK, + // Write is blocked, caller needs to retry. + WRITE_STATUS_BLOCKED, + // Write is blocked but the packet data is buffered, caller should not retry. + WRITE_STATUS_BLOCKED_DATA_BUFFERED, + // To make the IsWriteError(WriteStatus) function work properly: + // - Non-errors MUST be added before WRITE_STATUS_ERROR. + // - Errors MUST be added after WRITE_STATUS_ERROR. + WRITE_STATUS_ERROR, + WRITE_STATUS_MSG_TOO_BIG, + WRITE_STATUS_NUM_VALUES, +}; + +inline bool IsWriteBlockedStatus(WriteStatus status) { + return status == WRITE_STATUS_BLOCKED || + status == WRITE_STATUS_BLOCKED_DATA_BUFFERED; +} + +inline bool IsWriteError(WriteStatus status) { + return status >= WRITE_STATUS_ERROR; +} + +// A struct used to return the result of write calls including either the number +// of bytes written or the error code, depending upon the status. +struct QUIC_EXPORT_PRIVATE WriteResult { + WriteResult(WriteStatus status, int bytes_written_or_error_code); + WriteResult(); + + bool operator==(const WriteResult& other) const { + if (status != other.status) { + return false; + } + switch (status) { + case WRITE_STATUS_OK: + return bytes_written == other.bytes_written; + case WRITE_STATUS_BLOCKED: + case WRITE_STATUS_BLOCKED_DATA_BUFFERED: + return true; + default: + return error_code == other.error_code; + } + } + + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os, + const WriteResult& s); + + WriteStatus status; + union { + int bytes_written; // only valid when status is WRITE_STATUS_OK + int error_code; // only valid when status is WRITE_STATUS_ERROR + }; +}; + +enum TransmissionType : int8_t { + NOT_RETRANSMISSION, + FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION, + HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts. + ALL_UNACKED_RETRANSMISSION, // Retransmits all unacked packets. + ALL_INITIAL_RETRANSMISSION, // Retransmits all initially encrypted packets. + LOSS_RETRANSMISSION, // Retransmits due to loss detection. + RTO_RETRANSMISSION, // Retransmits due to retransmit time out. + TLP_RETRANSMISSION, // Tail loss probes. + PROBING_RETRANSMISSION, // Retransmission in order to probe bandwidth. + LAST_TRANSMISSION_TYPE = PROBING_RETRANSMISSION, +}; + +enum HasRetransmittableData : uint8_t { + NO_RETRANSMITTABLE_DATA, + HAS_RETRANSMITTABLE_DATA, +}; + +enum IsHandshake : uint8_t { NOT_HANDSHAKE, IS_HANDSHAKE }; + +enum class Perspective : uint8_t { IS_SERVER, IS_CLIENT }; +QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const Perspective& s); + +// Describes whether a ConnectionClose was originated by the peer. +enum class ConnectionCloseSource { FROM_PEER, FROM_SELF }; + +// Should a connection be closed silently or not. +enum class ConnectionCloseBehavior { + SILENT_CLOSE, + SEND_CONNECTION_CLOSE_PACKET, + SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK +}; + +enum QuicFrameType : uint8_t { + // Regular frame types. The values set here cannot change without the + // introduction of a new QUIC version. + PADDING_FRAME = 0, + RST_STREAM_FRAME = 1, + CONNECTION_CLOSE_FRAME = 2, + GOAWAY_FRAME = 3, + WINDOW_UPDATE_FRAME = 4, + BLOCKED_FRAME = 5, + STOP_WAITING_FRAME = 6, + PING_FRAME = 7, + CRYPTO_FRAME = 8, + + // STREAM and ACK frames are special frames. They are encoded differently on + // the wire and their values do not need to be stable. + STREAM_FRAME, + ACK_FRAME, + // The path MTU discovery frame is encoded as a PING frame on the wire. + MTU_DISCOVERY_FRAME, + + // These are for IETF-specific frames for which there is no mapping + // from Google QUIC frames. These are valid/allowed if and only if IETF- + // QUIC has been negotiated. Values are not important, they are not + // the values that are in the packets (see QuicIetfFrameType, below). + APPLICATION_CLOSE_FRAME, + NEW_CONNECTION_ID_FRAME, + MAX_STREAM_ID_FRAME, + STREAM_ID_BLOCKED_FRAME, + PATH_RESPONSE_FRAME, + PATH_CHALLENGE_FRAME, + STOP_SENDING_FRAME, + MESSAGE_FRAME, + NEW_TOKEN_FRAME, + RETIRE_CONNECTION_ID_FRAME, + + NUM_FRAME_TYPES +}; + +// Ietf frame types. These are defined in the IETF QUIC Specification. +// Explicit values are given in the enum so that we can be sure that +// the symbol will map to the correct stream type. +// All types are defined here, even if we have not yet implmented the +// quic/core/stream/.... stuff needed. +// Note: The protocol specifies that frame types are varint-62 encoded, +// further stating that the shortest encoding must be used. The current set of +// frame types all have values less than 0x40 (64) so can be encoded in a single +// byte, with the two most significant bits being 0. Thus, the following +// enumerations are valid as both the numeric values of frame types AND their +// encodings. +enum QuicIetfFrameType : uint8_t { + IETF_PADDING = 0x00, + IETF_PING = 0x01, + IETF_ACK = 0x02, + IETF_ACK_ECN = 0x03, + IETF_RST_STREAM = 0x04, + IETF_STOP_SENDING = 0x05, + IETF_CRYPTO = 0x06, + IETF_NEW_TOKEN = 0x07, + // the low-3 bits of the stream frame type value are actually flags + // declaring what parts of the frame are/are-not present, as well as + // some other control information. The code would then do something + // along the lines of "if ((frame_type & 0xf8) == 0x08)" to determine + // whether the frame is a stream frame or not, and then examine each + // bit specifically when/as needed. + IETF_STREAM = 0x08, + // 0x09 through 0x0f are various flag settings of the IETF_STREAM frame. + IETF_MAX_DATA = 0x10, + IETF_MAX_STREAM_DATA = 0x11, + IETF_MAX_STREAMS_BIDIRECTIONAL = 0x12, + IETF_MAX_STREAMS_UNIDIRECTIONAL = 0x13, + IETF_BLOCKED = 0x14, // TODO(fkastenholz): Should, eventually, be renamed to + // IETF_DATA_BLOCKED + IETF_STREAM_BLOCKED = 0x15, // TODO(fkastenholz): Should, eventually, be + // renamed to IETF_STREAM_DATA_BLOCKED + IETF_STREAMS_BLOCKED_BIDIRECTIONAL = 0x16, + IETF_STREAMS_BLOCKED_UNIDIRECTIONAL = 0x17, + IETF_NEW_CONNECTION_ID = 0x18, + IETF_RETIRE_CONNECTION_ID = 0x19, + IETF_PATH_CHALLENGE = 0x1a, + IETF_PATH_RESPONSE = 0x1b, + IETF_CONNECTION_CLOSE = 0x1c, + // 0x1d reserved, a flag setting for IETF_CONNECTION_CLOSE + // TODO(fkastenholz): IETF_APPLICATION_CLOSE disappears in the next version of + // QUIC. It is retained temporarily + IETF_APPLICATION_CLOSE = 0x1d, + + // MESSAGE frame type is not yet determined, use 0x2x temporarily to give + // stream frame some wiggle room. + IETF_EXTENSION_MESSAGE_NO_LENGTH = 0x20, + IETF_EXTENSION_MESSAGE = 0x21, +}; +// Masks for the bits that indicate the frame is a Stream frame vs the +// bits used as flags. +#define IETF_STREAM_FRAME_TYPE_MASK 0xfffffffffffffff8 +#define IETF_STREAM_FRAME_FLAG_MASK 0x07 +#define IS_IETF_STREAM_FRAME(_stype_) \ + (((_stype_)&IETF_STREAM_FRAME_TYPE_MASK) == IETF_STREAM) + +// These are the values encoded in the low-order 3 bits of the +// IETF_STREAMx frame type. +#define IETF_STREAM_FRAME_FIN_BIT 0x01 +#define IETF_STREAM_FRAME_LEN_BIT 0x02 +#define IETF_STREAM_FRAME_OFF_BIT 0x04 + +enum QuicVariableLengthIntegerLength : uint8_t { + // Length zero means the variable length integer is not present. + VARIABLE_LENGTH_INTEGER_LENGTH_0 = 0, + VARIABLE_LENGTH_INTEGER_LENGTH_1 = 1, + VARIABLE_LENGTH_INTEGER_LENGTH_2 = 2, + VARIABLE_LENGTH_INTEGER_LENGTH_4 = 4, + VARIABLE_LENGTH_INTEGER_LENGTH_8 = 8, +}; + +// By default we write the IETF long header length using the 2-byte encoding +// of variable length integers, even when the length is below 64, which allows +// us to fill in the length before knowing what the length actually is. +const QuicVariableLengthIntegerLength kQuicDefaultLongHeaderLengthLength = + VARIABLE_LENGTH_INTEGER_LENGTH_2; + +enum QuicPacketNumberLength : uint8_t { + PACKET_1BYTE_PACKET_NUMBER = 1, + PACKET_2BYTE_PACKET_NUMBER = 2, + PACKET_3BYTE_PACKET_NUMBER = 3, // Used in version > QUIC_VERSION_44. + PACKET_4BYTE_PACKET_NUMBER = 4, + // TODO(rch): Remove this when we remove QUIC_VERSION_39. + PACKET_6BYTE_PACKET_NUMBER = 6, + PACKET_8BYTE_PACKET_NUMBER = 8 +}; + +// Used to indicate a QuicSequenceNumberLength using two flag bits. +enum QuicPacketNumberLengthFlags { + PACKET_FLAGS_1BYTE_PACKET = 0, // 00 + PACKET_FLAGS_2BYTE_PACKET = 1, // 01 + PACKET_FLAGS_4BYTE_PACKET = 1 << 1, // 10 + PACKET_FLAGS_8BYTE_PACKET = 1 << 1 | 1, // 11 +}; + +// The public flags are specified in one byte. +enum QuicPacketPublicFlags { + PACKET_PUBLIC_FLAGS_NONE = 0, + + // Bit 0: Does the packet header contains version info? + PACKET_PUBLIC_FLAGS_VERSION = 1 << 0, + + // Bit 1: Is this packet a public reset packet? + PACKET_PUBLIC_FLAGS_RST = 1 << 1, + + // Bit 2: indicates the header includes a nonce. + PACKET_PUBLIC_FLAGS_NONCE = 1 << 2, + + // Bit 3: indicates whether a ConnectionID is included. + PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID = 0, + PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 1 << 3, + + // QUIC_VERSION_32 and earlier use two bits for an 8 byte + // connection id. + PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD = 1 << 3 | 1 << 2, + + // Bits 4 and 5 describe the packet number length as follows: + // --00----: 1 byte + // --01----: 2 bytes + // --10----: 4 bytes + // --11----: 6 bytes + PACKET_PUBLIC_FLAGS_1BYTE_PACKET = PACKET_FLAGS_1BYTE_PACKET << 4, + PACKET_PUBLIC_FLAGS_2BYTE_PACKET = PACKET_FLAGS_2BYTE_PACKET << 4, + PACKET_PUBLIC_FLAGS_4BYTE_PACKET = PACKET_FLAGS_4BYTE_PACKET << 4, + PACKET_PUBLIC_FLAGS_6BYTE_PACKET = PACKET_FLAGS_8BYTE_PACKET << 4, + + // Reserved, unimplemented flags: + + // Bit 7: indicates the presence of a second flags byte. + PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7, + + // All bits set (bits 6 and 7 are not currently used): 00111111 + PACKET_PUBLIC_FLAGS_MAX = (1 << 6) - 1, +}; + +// The private flags are specified in one byte. +enum QuicPacketPrivateFlags { + PACKET_PRIVATE_FLAGS_NONE = 0, + + // Bit 0: Does this packet contain an entropy bit? + PACKET_PRIVATE_FLAGS_ENTROPY = 1 << 0, + + // (bits 1-7 are not used): 00000001 + PACKET_PRIVATE_FLAGS_MAX = (1 << 1) - 1 +}; + +// Defines for all types of congestion control algorithms that can be used in +// QUIC. Note that this is separate from the congestion feedback type - +// some congestion control algorithms may use the same feedback type +// (Reno and Cubic are the classic example for that). +enum CongestionControlType { kCubicBytes, kRenoBytes, kBBR, kPCC, kGoogCC }; + +enum LossDetectionType : uint8_t { + kNack, // Used to mimic TCP's loss detection. + kTime, // Time based loss detection. + kAdaptiveTime, // Adaptive time based loss detection. + kLazyFack, // Nack based but with FACK disabled for the first ack. +}; + +// EncryptionLevel enumerates the stages of encryption that a QUIC connection +// progresses through. When retransmitting a packet, the encryption level needs +// to be specified so that it is retransmitted at a level which the peer can +// understand. +enum EncryptionLevel : int8_t { + ENCRYPTION_NONE = 0, + ENCRYPTION_ZERO_RTT = 1, + ENCRYPTION_FORWARD_SECURE = 2, + + NUM_ENCRYPTION_LEVELS, +}; + +enum AddressChangeType : uint8_t { + // IP address and port remain unchanged. + NO_CHANGE, + // Port changed, but IP address remains unchanged. + PORT_CHANGE, + // IPv4 address changed, but within the /24 subnet (port may have changed.) + IPV4_SUBNET_CHANGE, + // IPv4 address changed, excluding /24 subnet change (port may have changed.) + IPV4_TO_IPV4_CHANGE, + // IP address change from an IPv4 to an IPv6 address (port may have changed.) + IPV4_TO_IPV6_CHANGE, + // IP address change from an IPv6 to an IPv4 address (port may have changed.) + IPV6_TO_IPV4_CHANGE, + // IP address change from an IPv6 to an IPv6 address (port may have changed.) + IPV6_TO_IPV6_CHANGE, +}; + +enum StreamSendingState { + // Sender has more data to send on this stream. + NO_FIN, + // Sender is done sending on this stream. + FIN, + // Sender is done sending on this stream and random padding needs to be + // appended after all stream frames. + FIN_AND_PADDING, +}; + +enum SentPacketState : uint8_t { + // The packet has been sent and waiting to be acked. + OUTSTANDING, + FIRST_PACKET_STATE = OUTSTANDING, + // The packet was never sent. + NEVER_SENT, + // The packet has been acked. + ACKED, + // This packet is not expected to be acked. + UNACKABLE, + + // States below are corresponding to retransmission types in TransmissionType. + + // This packet has been retransmitted when retransmission timer fires in + // HANDSHAKE mode. + HANDSHAKE_RETRANSMITTED, + // This packet is considered as lost, this is used for LOST_RETRANSMISSION. + LOST, + // This packet has been retransmitted when TLP fires. + TLP_RETRANSMITTED, + // This packet has been retransmitted when RTO fires. + RTO_RETRANSMITTED, + // This packet has been retransmitted for probing purpose. + PROBE_RETRANSMITTED, + LAST_PACKET_STATE = PROBE_RETRANSMITTED, +}; + +enum PacketHeaderFormat : uint8_t { + IETF_QUIC_LONG_HEADER_PACKET, + IETF_QUIC_SHORT_HEADER_PACKET, + GOOGLE_QUIC_PACKET, +}; + +// Information about a newly acknowledged packet. +struct AckedPacket { + AckedPacket(QuicPacketNumber packet_number, + QuicPacketLength bytes_acked, + QuicTime receive_timestamp) + : packet_number(packet_number), + bytes_acked(bytes_acked), + receive_timestamp(receive_timestamp) {} + + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const AckedPacket& acked_packet); + + QuicPacketNumber packet_number; + // Number of bytes sent in the packet that was acknowledged. + QuicPacketLength bytes_acked; + // The time |packet_number| was received by the peer, according to the + // optional timestamp the peer included in the ACK frame which acknowledged + // |packet_number|. Zero if no timestamp was available for this packet. + QuicTime receive_timestamp; +}; + +// A vector of acked packets. +typedef std::vector<AckedPacket> AckedPacketVector; + +// Information about a newly lost packet. +struct LostPacket { + LostPacket(QuicPacketNumber packet_number, QuicPacketLength bytes_lost) + : packet_number(packet_number), bytes_lost(bytes_lost) {} + + QuicPacketNumber packet_number; + // Number of bytes sent in the packet that was lost. + QuicPacketLength bytes_lost; +}; + +// A vector of lost packets. +typedef std::vector<LostPacket> LostPacketVector; + +enum QuicIetfTransportErrorCodes : uint16_t { + NO_IETF_QUIC_ERROR = 0x0, + INTERNAL_ERROR = 0x1, + SERVER_BUSY_ERROR = 0x2, + FLOW_CONTROL_ERROR = 0x3, + STREAM_ID_ERROR = 0x4, + STREAM_STATE_ERROR = 0x5, + FINAL_OFFSET_ERROR = 0x6, + FRAME_ENCODING_ERROR = 0x7, + TRANSPORT_PARAMETER_ERROR = 0x8, + VERSION_NEGOTIATION_ERROR = 0x9, + PROTOCOL_VIOLATION = 0xA, + INVALID_MIGRATION = 0xC, + FRAME_ERROR_base = 0x100, // add frame type to this base +}; + +// Please note, this value cannot used directly for packet serialization. +enum QuicLongHeaderType : uint8_t { + VERSION_NEGOTIATION, + INITIAL, + ZERO_RTT_PROTECTED, + HANDSHAKE, + RETRY, + + INVALID_PACKET_TYPE, +}; + +enum QuicPacketHeaderTypeFlags : uint8_t { + // Bit 2: Reserved for experimentation for short header. + FLAGS_EXPERIMENTATION_BIT = 1 << 2, + // Bit 3: Google QUIC Demultiplexing bit, the short header always sets this + // bit to 0, allowing to distinguish Google QUIC packets from short header + // packets. + FLAGS_DEMULTIPLEXING_BIT = 1 << 3, + // Bits 4 and 5: Reserved bits for short header. + FLAGS_SHORT_HEADER_RESERVED_1 = 1 << 4, + FLAGS_SHORT_HEADER_RESERVED_2 = 1 << 5, + // Bit 6: the 'QUIC' bit. + FLAGS_FIXED_BIT = 1 << 6, + // Bit 7: Indicates the header is long or short header. + FLAGS_LONG_HEADER = 1 << 7, +}; + +enum MessageStatus { + MESSAGE_STATUS_SUCCESS, + MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, // Failed to send message because + // encryption is not established + // yet. + MESSAGE_STATUS_UNSUPPORTED, // Failed to send message because MESSAGE frame + // is not supported by the connection. + MESSAGE_STATUS_BLOCKED, // Failed to send message because connection is + // congestion control blocked or underlying socket is + // write blocked. + MESSAGE_STATUS_TOO_LARGE, // Failed to send message because the message is + // too large to fit into a single packet. + MESSAGE_STATUS_INTERNAL_ERROR, // Failed to send message because connection + // reaches an invalid state. +}; + +// Used to return the result of SendMessage calls +struct QUIC_EXPORT_PRIVATE MessageResult { + MessageResult(MessageStatus status, QuicMessageId message_id); + + bool operator==(const MessageResult& other) const { + return status == other.status && message_id == other.message_id; + } + + MessageStatus status; + // Only valid when status is MESSAGE_STATUS_SUCCESS. + QuicMessageId message_id; +}; + +enum WriteStreamDataResult { + WRITE_SUCCESS, + STREAM_MISSING, // Trying to write data of a nonexistent stream (e.g. + // closed). + WRITE_FAILED, // Trying to write nonexistent data of a stream +}; + +enum StreamType { + // Bidirectional streams allow for data to be sent in both directions. + BIDIRECTIONAL, + + // Unidirectional streams carry data in one direction only. + WRITE_UNIDIRECTIONAL, + READ_UNIDIRECTIONAL, +}; + +// A packet number space is the context in which a packet can be processed and +// acknowledged. +enum PacketNumberSpace : uint8_t { + INITIAL_DATA = 0, // Only used in IETF QUIC. + HANDSHAKE_DATA = 1, + APPLICATION_DATA = 2, + + NUM_PACKET_NUMBER_SPACES, +}; + +enum AckMode { TCP_ACKING, ACK_DECIMATION, ACK_DECIMATION_WITH_REORDERING }; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_TYPES_H_
diff --git a/quic/core/quic_unacked_packet_map.cc b/quic/core/quic_unacked_packet_map.cc new file mode 100644 index 0000000..859bf39 --- /dev/null +++ b/quic/core/quic_unacked_packet_map.cc
@@ -0,0 +1,552 @@ +// Copyright 2014 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/quic_unacked_packet_map.h" + +#include <limits> +#include <type_traits> + +#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" + +namespace quic { + +namespace { +bool WillStreamFrameLengthSumWrapAround(QuicPacketLength lhs, + QuicPacketLength rhs) { + static_assert( + std::is_unsigned<QuicPacketLength>::value, + "This function assumes QuicPacketLength is an unsigned integer type."); + return std::numeric_limits<QuicPacketLength>::max() - lhs < rhs; +} +} // namespace + +QuicUnackedPacketMap::QuicUnackedPacketMap(Perspective perspective) + : perspective_(perspective), + least_unacked_(FirstSendingPacketNumber()), + bytes_in_flight_(0), + pending_crypto_packet_count_(0), + last_crypto_packet_sent_time_(QuicTime::Zero()), + session_notifier_(nullptr), + session_decides_what_to_write_(false), + use_uber_loss_algorithm_( + GetQuicReloadableFlag(quic_use_uber_loss_algorithm)) { + if (use_uber_loss_algorithm_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_use_uber_loss_algorithm); + } +} + +QuicUnackedPacketMap::~QuicUnackedPacketMap() { + for (QuicTransmissionInfo& transmission_info : unacked_packets_) { + DeleteFrames(&(transmission_info.retransmittable_frames)); + } +} + +void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* packet, + QuicPacketNumber old_packet_number, + TransmissionType transmission_type, + QuicTime sent_time, + bool set_in_flight) { + QuicPacketNumber packet_number = packet->packet_number; + QuicPacketLength bytes_sent = packet->encrypted_length; + QUIC_BUG_IF(largest_sent_packet_.IsInitialized() && + largest_sent_packet_ >= packet_number) + << "largest_sent_packet_: " << largest_sent_packet_ + << ", packet_number: " << packet_number; + DCHECK_GE(packet_number, least_unacked_ + unacked_packets_.size()); + while (least_unacked_ + unacked_packets_.size() < packet_number) { + unacked_packets_.push_back(QuicTransmissionInfo()); + unacked_packets_.back().state = NEVER_SENT; + } + + const bool has_crypto_handshake = + packet->has_crypto_handshake == IS_HANDSHAKE; + QuicTransmissionInfo info( + packet->encryption_level, packet->packet_number_length, transmission_type, + sent_time, bytes_sent, has_crypto_handshake, packet->num_padding_bytes); + info.largest_acked = packet->largest_acked; + if (packet->largest_acked.IsInitialized()) { + largest_sent_largest_acked_ = + largest_sent_largest_acked_.IsInitialized() + ? std::max(largest_sent_largest_acked_, packet->largest_acked) + : packet->largest_acked; + } + if (old_packet_number.IsInitialized()) { + TransferRetransmissionInfo(old_packet_number, packet_number, + transmission_type, &info); + } + + largest_sent_packet_ = packet_number; + if (set_in_flight) { + bytes_in_flight_ += bytes_sent; + info.in_flight = true; + if (use_uber_loss_algorithm_) { + largest_sent_retransmittable_packets_[GetPacketNumberSpace( + info.encryption_level)] = packet_number; + } else { + largest_sent_retransmittable_packet_ = packet_number; + } + } + unacked_packets_.push_back(info); + // Swap the retransmittable frames to avoid allocations. + // TODO(ianswett): Could use emplace_back when Chromium can. + if (!old_packet_number.IsInitialized()) { + if (has_crypto_handshake) { + ++pending_crypto_packet_count_; + last_crypto_packet_sent_time_ = sent_time; + } + + packet->retransmittable_frames.swap( + unacked_packets_.back().retransmittable_frames); + } +} + +void QuicUnackedPacketMap::RemoveObsoletePackets() { + while (!unacked_packets_.empty()) { + if (!IsPacketUseless(least_unacked_, unacked_packets_.front())) { + break; + } + if (session_decides_what_to_write_) { + DeleteFrames(&unacked_packets_.front().retransmittable_frames); + } + unacked_packets_.pop_front(); + ++least_unacked_; + } +} + +void QuicUnackedPacketMap::TransferRetransmissionInfo( + QuicPacketNumber old_packet_number, + QuicPacketNumber new_packet_number, + TransmissionType transmission_type, + QuicTransmissionInfo* info) { + if (old_packet_number < least_unacked_) { + // This can happen when a retransmission packet is queued because of write + // blocked socket, and the original packet gets acked before the + // retransmission gets sent. + return; + } + if (old_packet_number > largest_sent_packet_) { + QUIC_BUG << "Old QuicTransmissionInfo never existed for :" + << old_packet_number << " largest_sent:" << largest_sent_packet_; + return; + } + DCHECK_GE(new_packet_number, least_unacked_ + unacked_packets_.size()); + DCHECK_NE(NOT_RETRANSMISSION, transmission_type); + + QuicTransmissionInfo* transmission_info = + &unacked_packets_.at(old_packet_number - least_unacked_); + QuicFrames* frames = &transmission_info->retransmittable_frames; + if (session_notifier_ != nullptr) { + for (const QuicFrame& frame : *frames) { + if (frame.type == STREAM_FRAME) { + session_notifier_->OnStreamFrameRetransmitted(frame.stream_frame); + } + } + } + + // Swap the frames and preserve num_padding_bytes and has_crypto_handshake. + frames->swap(info->retransmittable_frames); + info->has_crypto_handshake = transmission_info->has_crypto_handshake; + transmission_info->has_crypto_handshake = false; + info->num_padding_bytes = transmission_info->num_padding_bytes; + + // Don't link old transmissions to new ones when version or + // encryption changes. + if (transmission_type == ALL_INITIAL_RETRANSMISSION || + transmission_type == ALL_UNACKED_RETRANSMISSION) { + transmission_info->state = UNACKABLE; + } else { + transmission_info->retransmission = new_packet_number; + } + // Proactively remove obsolete packets so the least unacked can be raised. + RemoveObsoletePackets(); +} + +bool QuicUnackedPacketMap::HasRetransmittableFrames( + QuicPacketNumber packet_number) const { + DCHECK_GE(packet_number, least_unacked_); + DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size()); + return HasRetransmittableFrames( + unacked_packets_[packet_number - least_unacked_]); +} + +bool QuicUnackedPacketMap::HasRetransmittableFrames( + const QuicTransmissionInfo& info) const { + if (!session_decides_what_to_write_) { + return !info.retransmittable_frames.empty(); + } + + if (!QuicUtils::IsAckable(info.state)) { + return false; + } + + for (const auto& frame : info.retransmittable_frames) { + if (session_notifier_->IsFrameOutstanding(frame)) { + return true; + } + } + return false; +} + +void QuicUnackedPacketMap::RemoveRetransmittability( + QuicTransmissionInfo* info) { + if (session_decides_what_to_write_) { + DeleteFrames(&info->retransmittable_frames); + info->retransmission.Clear(); + return; + } + while (info->retransmission.IsInitialized()) { + const QuicPacketNumber retransmission = info->retransmission; + info->retransmission.Clear(); + info = &unacked_packets_[retransmission - least_unacked_]; + } + + if (info->has_crypto_handshake) { + DCHECK(HasRetransmittableFrames(*info)); + DCHECK_LT(0u, pending_crypto_packet_count_); + --pending_crypto_packet_count_; + info->has_crypto_handshake = false; + } + DeleteFrames(&info->retransmittable_frames); +} + +void QuicUnackedPacketMap::RemoveRetransmittability( + QuicPacketNumber packet_number) { + DCHECK_GE(packet_number, least_unacked_); + DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size()); + QuicTransmissionInfo* info = + &unacked_packets_[packet_number - least_unacked_]; + RemoveRetransmittability(info); +} + +void QuicUnackedPacketMap::IncreaseLargestAcked( + QuicPacketNumber largest_acked) { + DCHECK(!largest_acked_.IsInitialized() || largest_acked_ <= largest_acked); + largest_acked_ = largest_acked; +} + +void QuicUnackedPacketMap::MaybeUpdateLargestAckedOfPacketNumberSpace( + EncryptionLevel encryption_level, + QuicPacketNumber packet_number) { + DCHECK(use_uber_loss_algorithm_); + const PacketNumberSpace packet_number_space = + GetPacketNumberSpace(encryption_level); + if (!largest_acked_packets_[packet_number_space].IsInitialized()) { + largest_acked_packets_[packet_number_space] = packet_number; + } else { + largest_acked_packets_[packet_number_space] = + std::max(largest_acked_packets_[packet_number_space], packet_number); + } +} + +bool QuicUnackedPacketMap::IsPacketUsefulForMeasuringRtt( + QuicPacketNumber packet_number, + const QuicTransmissionInfo& info) const { + // Packet can be used for RTT measurement if it may yet be acked as the + // largest observed packet by the receiver. + return QuicUtils::IsAckable(info.state) && + (!largest_acked_.IsInitialized() || packet_number > largest_acked_); +} + +bool QuicUnackedPacketMap::IsPacketUsefulForCongestionControl( + const QuicTransmissionInfo& info) const { + // Packet contributes to congestion control if it is considered inflight. + return info.in_flight; +} + +bool QuicUnackedPacketMap::IsPacketUsefulForRetransmittableData( + const QuicTransmissionInfo& info) const { + if (!session_decides_what_to_write_) { + // Packet may have retransmittable frames, or the data may have been + // retransmitted with a new packet number. + // Allow for an extra 1 RTT before stopping to track old packets. + return (info.retransmission.IsInitialized() && + (!largest_acked_.IsInitialized() || + info.retransmission > largest_acked_)) || + HasRetransmittableFrames(info); + } + + // Wait for 1 RTT before giving up on the lost packet. + return info.retransmission.IsInitialized() && + (!largest_acked_.IsInitialized() || + info.retransmission > largest_acked_); +} + +bool QuicUnackedPacketMap::IsPacketUseless( + QuicPacketNumber packet_number, + const QuicTransmissionInfo& info) const { + return !IsPacketUsefulForMeasuringRtt(packet_number, info) && + !IsPacketUsefulForCongestionControl(info) && + !IsPacketUsefulForRetransmittableData(info); +} + +bool QuicUnackedPacketMap::IsUnacked(QuicPacketNumber packet_number) const { + if (packet_number < least_unacked_ || + packet_number >= least_unacked_ + unacked_packets_.size()) { + return false; + } + return !IsPacketUseless(packet_number, + unacked_packets_[packet_number - least_unacked_]); +} + +void QuicUnackedPacketMap::RemoveFromInFlight(QuicTransmissionInfo* info) { + if (info->in_flight) { + QUIC_BUG_IF(bytes_in_flight_ < info->bytes_sent); + bytes_in_flight_ -= info->bytes_sent; + info->in_flight = false; + } +} + +void QuicUnackedPacketMap::RemoveFromInFlight(QuicPacketNumber packet_number) { + DCHECK_GE(packet_number, least_unacked_); + DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size()); + QuicTransmissionInfo* info = + &unacked_packets_[packet_number - least_unacked_]; + RemoveFromInFlight(info); +} + +void QuicUnackedPacketMap::CancelRetransmissionsForStream( + QuicStreamId stream_id) { + DCHECK(!session_decides_what_to_write_); + QuicPacketNumber packet_number = least_unacked_; + for (auto it = unacked_packets_.begin(); it != unacked_packets_.end(); + ++it, ++packet_number) { + QuicFrames* frames = &it->retransmittable_frames; + if (frames->empty()) { + continue; + } + RemoveFramesForStream(frames, stream_id); + if (frames->empty()) { + RemoveRetransmittability(packet_number); + } + } +} + +bool QuicUnackedPacketMap::HasInFlightPackets() const { + return bytes_in_flight_ > 0; +} + +const QuicTransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo( + QuicPacketNumber packet_number) const { + return unacked_packets_[packet_number - least_unacked_]; +} + +QuicTransmissionInfo* QuicUnackedPacketMap::GetMutableTransmissionInfo( + QuicPacketNumber packet_number) { + return &unacked_packets_[packet_number - least_unacked_]; +} + +QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const { + auto it = unacked_packets_.rbegin(); + while (it != unacked_packets_.rend()) { + if (it->in_flight) { + QUIC_BUG_IF(it->sent_time == QuicTime::Zero()) + << "Sent time can never be zero for a packet in flight."; + return it->sent_time; + } + ++it; + } + QUIC_BUG << "GetLastPacketSentTime requires in flight packets."; + return QuicTime::Zero(); +} + +QuicTime QuicUnackedPacketMap::GetLastCryptoPacketSentTime() const { + return last_crypto_packet_sent_time_; +} + +size_t QuicUnackedPacketMap::GetNumUnackedPacketsDebugOnly() const { + size_t unacked_packet_count = 0; + QuicPacketNumber packet_number = least_unacked_; + for (auto it = unacked_packets_.begin(); it != unacked_packets_.end(); + ++it, ++packet_number) { + if (!IsPacketUseless(packet_number, *it)) { + ++unacked_packet_count; + } + } + return unacked_packet_count; +} + +bool QuicUnackedPacketMap::HasMultipleInFlightPackets() const { + if (bytes_in_flight_ > kDefaultTCPMSS) { + return true; + } + size_t num_in_flight = 0; + for (auto it = unacked_packets_.rbegin(); it != unacked_packets_.rend(); + ++it) { + if (it->in_flight) { + ++num_in_flight; + } + if (num_in_flight > 1) { + return true; + } + } + return false; +} + +bool QuicUnackedPacketMap::HasPendingCryptoPackets() const { + if (!session_decides_what_to_write_) { + return pending_crypto_packet_count_ > 0; + } + return session_notifier_->HasUnackedCryptoData(); +} + +bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const { + DCHECK(!GetQuicReloadableFlag(quic_optimize_inflight_check)); + for (auto it = unacked_packets_.rbegin(); it != unacked_packets_.rend(); + ++it) { + if (it->in_flight && HasRetransmittableFrames(*it)) { + return true; + } + } + return false; +} + +QuicPacketNumber QuicUnackedPacketMap::GetLeastUnacked() const { + return least_unacked_; +} + +void QuicUnackedPacketMap::SetSessionNotifier( + SessionNotifierInterface* session_notifier) { + session_notifier_ = session_notifier; +} + +bool QuicUnackedPacketMap::NotifyFramesAcked(const QuicTransmissionInfo& info, + QuicTime::Delta ack_delay) { + if (session_notifier_ == nullptr) { + return false; + } + bool new_data_acked = false; + for (const QuicFrame& frame : info.retransmittable_frames) { + if (session_notifier_->OnFrameAcked(frame, ack_delay)) { + new_data_acked = true; + } + } + return new_data_acked; +} + +void QuicUnackedPacketMap::NotifyFramesLost(const QuicTransmissionInfo& info, + TransmissionType type) { + DCHECK(session_decides_what_to_write_); + for (const QuicFrame& frame : info.retransmittable_frames) { + session_notifier_->OnFrameLost(frame); + } +} + +void QuicUnackedPacketMap::RetransmitFrames(const QuicTransmissionInfo& info, + TransmissionType type) { + DCHECK(session_decides_what_to_write_); + session_notifier_->RetransmitFrames(info.retransmittable_frames, type); +} + +void QuicUnackedPacketMap::MaybeAggregateAckedStreamFrame( + const QuicTransmissionInfo& info, + QuicTime::Delta ack_delay) { + if (session_notifier_ == nullptr) { + return; + } + for (const auto& frame : info.retransmittable_frames) { + // Determine whether acked stream frame can be aggregated. + const bool can_aggregate = + frame.type == STREAM_FRAME && + frame.stream_frame.stream_id == aggregated_stream_frame_.stream_id && + frame.stream_frame.offset == aggregated_stream_frame_.offset + + aggregated_stream_frame_.data_length && + // We would like to increment aggregated_stream_frame_.data_length by + // frame.stream_frame.data_length, so we need to make sure their sum is + // representable by QuicPacketLength, which is the type of the former. + !WillStreamFrameLengthSumWrapAround( + aggregated_stream_frame_.data_length, + frame.stream_frame.data_length); + + if (can_aggregate) { + // Aggregate stream frame. + aggregated_stream_frame_.data_length += frame.stream_frame.data_length; + aggregated_stream_frame_.fin = frame.stream_frame.fin; + if (aggregated_stream_frame_.fin) { + // Notify session notifier aggregated stream frame gets acked if fin is + // acked. + NotifyAggregatedStreamFrameAcked(ack_delay); + } + continue; + } + + NotifyAggregatedStreamFrameAcked(ack_delay); + if (frame.type != STREAM_FRAME || frame.stream_frame.fin) { + session_notifier_->OnFrameAcked(frame, ack_delay); + continue; + } + + // Delay notifying session notifier stream frame gets acked in case it can + // be aggregated with following acked ones. + aggregated_stream_frame_.stream_id = frame.stream_frame.stream_id; + aggregated_stream_frame_.offset = frame.stream_frame.offset; + aggregated_stream_frame_.data_length = frame.stream_frame.data_length; + aggregated_stream_frame_.fin = frame.stream_frame.fin; + } +} + +void QuicUnackedPacketMap::NotifyAggregatedStreamFrameAcked( + QuicTime::Delta ack_delay) { + if (aggregated_stream_frame_.stream_id == static_cast<QuicStreamId>(-1) || + session_notifier_ == nullptr) { + // Aggregated stream frame is empty. + return; + } + session_notifier_->OnFrameAcked(QuicFrame(aggregated_stream_frame_), + ack_delay); + // Clear aggregated stream frame. + aggregated_stream_frame_.stream_id = -1; +} + +PacketNumberSpace QuicUnackedPacketMap::GetPacketNumberSpace( + QuicPacketNumber packet_number) const { + DCHECK(use_uber_loss_algorithm_); + return GetPacketNumberSpace( + GetTransmissionInfo(packet_number).encryption_level); +} + +PacketNumberSpace QuicUnackedPacketMap::GetPacketNumberSpace( + EncryptionLevel encryption_level) const { + DCHECK(use_uber_loss_algorithm_); + if (perspective_ == Perspective::IS_CLIENT) { + return encryption_level == ENCRYPTION_NONE ? HANDSHAKE_DATA + : APPLICATION_DATA; + } + return encryption_level == ENCRYPTION_FORWARD_SECURE ? APPLICATION_DATA + : HANDSHAKE_DATA; +} + +QuicPacketNumber QuicUnackedPacketMap::GetLargestAckedOfPacketNumberSpace( + PacketNumberSpace packet_number_space) const { + DCHECK(use_uber_loss_algorithm_); + if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) { + QUIC_BUG << "Invalid packet number space: " << packet_number_space; + return QuicPacketNumber(); + } + return largest_acked_packets_[packet_number_space]; +} + +QuicPacketNumber +QuicUnackedPacketMap::GetLargestSentRetransmittableOfPacketNumberSpace( + PacketNumberSpace packet_number_space) const { + DCHECK(use_uber_loss_algorithm_); + if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) { + QUIC_BUG << "Invalid packet number space: " << packet_number_space; + return QuicPacketNumber(); + } + return largest_sent_retransmittable_packets_[packet_number_space]; +} + +void QuicUnackedPacketMap::SetSessionDecideWhatToWrite( + bool session_decides_what_to_write) { + if (largest_sent_packet_.IsInitialized()) { + QUIC_BUG << "Cannot change session_decide_what_to_write with packets sent."; + return; + } + session_decides_what_to_write_ = session_decides_what_to_write; +} + +} // namespace quic
diff --git a/quic/core/quic_unacked_packet_map.h b/quic/core/quic_unacked_packet_map.h new file mode 100644 index 0000000..b6f7af5 --- /dev/null +++ b/quic/core/quic_unacked_packet_map.h
@@ -0,0 +1,304 @@ +// Copyright 2014 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_QUIC_UNACKED_PACKET_MAP_H_ +#define QUICHE_QUIC_CORE_QUIC_UNACKED_PACKET_MAP_H_ + +#include <cstddef> +#include <deque> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h" +#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +namespace test { +class QuicUnackedPacketMapPeer; +} // namespace test + +// Class which tracks unacked packets for three purposes: +// 1) Track retransmittable data, including multiple transmissions of frames. +// 2) Track packets and bytes in flight for congestion control. +// 3) Track sent time of packets to provide RTT measurements from acks. +class QUIC_EXPORT_PRIVATE QuicUnackedPacketMap { + public: + QuicUnackedPacketMap(Perspective perspective); + QuicUnackedPacketMap(const QuicUnackedPacketMap&) = delete; + QuicUnackedPacketMap& operator=(const QuicUnackedPacketMap&) = delete; + ~QuicUnackedPacketMap(); + + // Adds |serialized_packet| to the map and marks it as sent at |sent_time|. + // Marks the packet as in flight if |set_in_flight| is true. + // Packets marked as in flight are expected to be marked as missing when they + // don't arrive, indicating the need for retransmission. + // |old_packet_number| is the packet number of the previous transmission, + // or 0 if there was none. + // Any AckNotifierWrappers in |serialized_packet| are swapped from the + // serialized packet into the QuicTransmissionInfo. + void AddSentPacket(SerializedPacket* serialized_packet, + QuicPacketNumber old_packet_number, + TransmissionType transmission_type, + QuicTime sent_time, + bool set_in_flight); + + // Returns true if the packet |packet_number| is unacked. + bool IsUnacked(QuicPacketNumber packet_number) const; + + // Notifies session_notifier that frames have been acked. Returns true if any + // new data gets acked, returns false otherwise. + bool NotifyFramesAcked(const QuicTransmissionInfo& info, + QuicTime::Delta ack_delay); + + // Notifies session_notifier that frames in |info| are considered as lost. + void NotifyFramesLost(const QuicTransmissionInfo& info, + TransmissionType type); + + // Notifies session_notifier to retransmit frames in |info| with + // |transmission_type|. + void RetransmitFrames(const QuicTransmissionInfo& info, + TransmissionType type); + + // Marks |info| as no longer in flight. + void RemoveFromInFlight(QuicTransmissionInfo* info); + + // Marks |packet_number| as no longer in flight. + void RemoveFromInFlight(QuicPacketNumber packet_number); + + // No longer retransmit data for |stream_id|. + void CancelRetransmissionsForStream(QuicStreamId stream_id); + + // Returns true if |packet_number| has retransmittable frames. This will + // return false if all frames of this packet are either non-retransmittable or + // have been acked. + bool HasRetransmittableFrames(QuicPacketNumber packet_number) const; + + // Returns true if |info| has retransmittable frames. This will return false + // if all frames of this packet are either non-retransmittable or have been + // acked. + bool HasRetransmittableFrames(const QuicTransmissionInfo& info) const; + + // Returns true if there are any unacked packets which have retransmittable + // frames. + bool HasUnackedRetransmittableFrames() const; + + // Returns true if there are no packets present in the unacked packet map. + bool empty() const { return unacked_packets_.empty(); } + + // Returns the largest packet number that has been sent. + QuicPacketNumber largest_sent_packet() const { return largest_sent_packet_; } + + // Returns the largest retransmittable packet number that has been sent. + QuicPacketNumber largest_sent_retransmittable_packet() const { + DCHECK(!use_uber_loss_algorithm_); + return largest_sent_retransmittable_packet_; + } + + QuicPacketNumber largest_sent_largest_acked() const { + return largest_sent_largest_acked_; + } + + // Returns the largest packet number that has been acked. + QuicPacketNumber largest_acked() const { return largest_acked_; } + + // Returns the sum of bytes from all packets in flight. + QuicByteCount bytes_in_flight() const { return bytes_in_flight_; } + + // Returns the smallest packet number of a serialized packet which has not + // been acked by the peer. If there are no unacked packets, returns 0. + QuicPacketNumber GetLeastUnacked() const; + + // This can not be a QuicDeque since pointers into this are + // assumed to be stable. + typedef std::deque<QuicTransmissionInfo> UnackedPacketMap; + + typedef UnackedPacketMap::const_iterator const_iterator; + typedef UnackedPacketMap::iterator iterator; + + const_iterator begin() const { return unacked_packets_.begin(); } + const_iterator end() const { return unacked_packets_.end(); } + iterator begin() { return unacked_packets_.begin(); } + iterator end() { return unacked_packets_.end(); } + + // Returns true if there are unacked packets that are in flight. + bool HasInFlightPackets() const; + + // Returns the QuicTransmissionInfo associated with |packet_number|, which + // must be unacked. + const QuicTransmissionInfo& GetTransmissionInfo( + QuicPacketNumber packet_number) const; + + // Returns mutable QuicTransmissionInfo associated with |packet_number|, which + // must be unacked. + QuicTransmissionInfo* GetMutableTransmissionInfo( + QuicPacketNumber packet_number); + + // Returns the time that the last unacked packet was sent. + QuicTime GetLastPacketSentTime() const; + + // Returns the time that the last unacked crypto packet was sent. + QuicTime GetLastCryptoPacketSentTime() const; + + // Returns the number of unacked packets. + size_t GetNumUnackedPacketsDebugOnly() const; + + // Returns true if there are multiple packets in flight. + bool HasMultipleInFlightPackets() const; + + // Returns true if there are any pending crypto packets. + // TODO(fayang): Remove this method and call session_notifier_'s + // HasUnackedCryptoData() when session_decides_what_to_write_ is default true. + bool HasPendingCryptoPackets() const; + + // Removes any retransmittable frames from this transmission or an associated + // transmission. It removes now useless transmissions, and disconnects any + // other packets from other transmissions. + void RemoveRetransmittability(QuicTransmissionInfo* info); + + // Looks up the QuicTransmissionInfo by |packet_number| and calls + // RemoveRetransmittability. + void RemoveRetransmittability(QuicPacketNumber packet_number); + + // Increases the largest acked. Any packets less or equal to + // |largest_acked| are discarded if they are only for the RTT purposes. + void IncreaseLargestAcked(QuicPacketNumber largest_acked); + + // Called when |packet_number| gets acked. Maybe increase the largest acked of + // corresponding packet number space of |encryption_level|. + void MaybeUpdateLargestAckedOfPacketNumberSpace( + EncryptionLevel encryption_level, + QuicPacketNumber packet_number); + + // Remove any packets no longer needed for retransmission, congestion, or + // RTT measurement purposes. + void RemoveObsoletePackets(); + + // Try to aggregate acked contiguous stream frames. For noncontiguous stream + // frames or control frames, notify the session notifier they get acked + // immediately. + void MaybeAggregateAckedStreamFrame(const QuicTransmissionInfo& info, + QuicTime::Delta ack_delay); + + // Notify the session notifier of any stream data aggregated in + // aggregated_stream_frame_. No effect if the stream frame has an invalid + // stream id. + void NotifyAggregatedStreamFrameAcked(QuicTime::Delta ack_delay); + + // Returns packet number space that |packet_number| belongs to. Please use + // GetPacketNumberSpace(EncryptionLevel) whenever encryption level is + // available. + PacketNumberSpace GetPacketNumberSpace(QuicPacketNumber packet_number) const; + + // Returns packet number space of |encryption_level|. + PacketNumberSpace GetPacketNumberSpace( + EncryptionLevel encryption_level) const; + + // Returns largest acked packet number of |packet_number_space|. + QuicPacketNumber GetLargestAckedOfPacketNumberSpace( + PacketNumberSpace packet_number_space) const; + + // Returns largest sent retransmittable packet number of + // |packet_number_space|. + QuicPacketNumber GetLargestSentRetransmittableOfPacketNumberSpace( + PacketNumberSpace packet_number_space) const; + + // Called to start/stop letting session decide what to write. + void SetSessionDecideWhatToWrite(bool session_decides_what_to_write); + + void SetSessionNotifier(SessionNotifierInterface* session_notifier); + + bool session_decides_what_to_write() const { + return session_decides_what_to_write_; + } + + bool use_uber_loss_algorithm() const { return use_uber_loss_algorithm_; } + + Perspective perspective() const { return perspective_; } + + private: + friend class test::QuicUnackedPacketMapPeer; + + // Called when a packet is retransmitted with a new packet number. + // |old_packet_number| will remain unacked, but will have no + // retransmittable data associated with it. Retransmittable frames will be + // transferred to |info| and all_transmissions will be populated. + void TransferRetransmissionInfo(QuicPacketNumber old_packet_number, + QuicPacketNumber new_packet_number, + TransmissionType transmission_type, + QuicTransmissionInfo* info); + + // Returns true if packet may be useful for an RTT measurement. + bool IsPacketUsefulForMeasuringRtt(QuicPacketNumber packet_number, + const QuicTransmissionInfo& info) const; + + // Returns true if packet may be useful for congestion control purposes. + bool IsPacketUsefulForCongestionControl( + const QuicTransmissionInfo& info) const; + + // Returns true if packet may be associated with retransmittable data + // directly or through retransmissions. + bool IsPacketUsefulForRetransmittableData( + const QuicTransmissionInfo& info) const; + + // Returns true if the packet no longer has a purpose in the map. + bool IsPacketUseless(QuicPacketNumber packet_number, + const QuicTransmissionInfo& info) const; + + const Perspective perspective_; + + QuicPacketNumber largest_sent_packet_; + // The largest sent packet we expect to receive an ack for. + // TODO(fayang): Remove largest_sent_retransmittable_packet_ when deprecating + // quic_use_uber_loss_algorithm. + QuicPacketNumber largest_sent_retransmittable_packet_; + // The largest sent packet we expect to receive an ack for per packet number + // space. Only used if use_uber_loss_algorithm_ is true. + QuicPacketNumber + largest_sent_retransmittable_packets_[NUM_PACKET_NUMBER_SPACES]; + // The largest sent largest_acked in an ACK frame. + QuicPacketNumber largest_sent_largest_acked_; + // The largest received largest_acked from an ACK frame. + QuicPacketNumber largest_acked_; + // The largest received largest_acked from ACK frame per packet number space. + // Only used if use_uber_loss_algorithm_ is true. + QuicPacketNumber largest_acked_packets_[NUM_PACKET_NUMBER_SPACES]; + + // Newly serialized retransmittable packets are added to this map, which + // contains owning pointers to any contained frames. If a packet is + // retransmitted, this map will contain entries for both the old and the new + // packet. The old packet's retransmittable frames entry will be nullptr, + // while the new packet's entry will contain the frames to retransmit. + // If the old packet is acked before the new packet, then the old entry will + // be removed from the map and the new entry's retransmittable frames will be + // set to nullptr. + UnackedPacketMap unacked_packets_; + // The packet at the 0th index of unacked_packets_. + QuicPacketNumber least_unacked_; + + QuicByteCount bytes_in_flight_; + // Number of retransmittable crypto handshake packets. + size_t pending_crypto_packet_count_; + + // Time that the last unacked crypto packet was sent. + QuicTime last_crypto_packet_sent_time_; + + // Aggregates acked stream data across multiple acked sent packets to save CPU + // by reducing the number of calls to the session notifier. + QuicStreamFrame aggregated_stream_frame_; + + // Receives notifications of frames being retransmitted or acknowledged. + SessionNotifierInterface* session_notifier_; + + // If true, let session decides what to write. + bool session_decides_what_to_write_; + + // Latched value of quic_use_uber_loss_algorithm. + const bool use_uber_loss_algorithm_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_UNACKED_PACKET_MAP_H_
diff --git a/quic/core/quic_unacked_packet_map_test.cc b/quic/core/quic_unacked_packet_map_test.cc new file mode 100644 index 0000000..cf725fa --- /dev/null +++ b/quic/core/quic_unacked_packet_map_test.cc
@@ -0,0 +1,679 @@ +// Copyright 2014 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/quic_unacked_packet_map.h" +#include <limits> + +#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h" + +using testing::_; +using testing::Return; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +// Default packet length. +const uint32_t kDefaultLength = 1000; + +struct TestParams { + TestParams(Perspective perspective, bool session_decides_what_to_write) + : perspective(perspective), + session_decides_what_to_write(session_decides_what_to_write) {} + + friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { + os << "{ Perspective: " << p.perspective + << " session_decides_what_to_write: " << p.session_decides_what_to_write + << " }"; + return os; + } + + Perspective perspective; + bool session_decides_what_to_write; +}; + +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + for (Perspective perspective : + {Perspective::IS_CLIENT, Perspective::IS_SERVER}) { + for (bool session_decides_what_to_write : {true, false}) { + params.push_back(TestParams(perspective, session_decides_what_to_write)); + } + } + return params; +} + +class QuicUnackedPacketMapTest : public QuicTestWithParam<TestParams> { + protected: + QuicUnackedPacketMapTest() + : unacked_packets_(GetParam().perspective), + now_(QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1000)) { + unacked_packets_.SetSessionNotifier(¬ifier_); + unacked_packets_.SetSessionDecideWhatToWrite( + GetParam().session_decides_what_to_write); + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(notifier_, OnStreamFrameRetransmitted(_)) + .Times(testing::AnyNumber()); + } + + ~QuicUnackedPacketMapTest() override {} + + SerializedPacket CreateRetransmittablePacket(uint64_t packet_number) { + return CreateRetransmittablePacketForStream( + packet_number, QuicUtils::GetHeadersStreamId( + CurrentSupportedVersions()[0].transport_version)); + } + + SerializedPacket CreateRetransmittablePacketForStream( + uint64_t packet_number, + QuicStreamId stream_id) { + SerializedPacket packet(QuicPacketNumber(packet_number), + PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength, + false, false); + QuicStreamFrame frame; + frame.stream_id = stream_id; + packet.retransmittable_frames.push_back(QuicFrame(frame)); + return packet; + } + + SerializedPacket CreateNonRetransmittablePacket(uint64_t packet_number) { + return SerializedPacket(QuicPacketNumber(packet_number), + PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength, + false, false); + } + + void VerifyInFlightPackets(uint64_t* packets, size_t num_packets) { + unacked_packets_.RemoveObsoletePackets(); + if (num_packets == 0) { + EXPECT_FALSE(unacked_packets_.HasInFlightPackets()); + EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets()); + return; + } + if (num_packets == 1) { + EXPECT_TRUE(unacked_packets_.HasInFlightPackets()); + EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets()); + ASSERT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[0]))); + EXPECT_TRUE( + unacked_packets_.GetTransmissionInfo(QuicPacketNumber(packets[0])) + .in_flight); + } + for (size_t i = 0; i < num_packets; ++i) { + ASSERT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[i]))); + EXPECT_TRUE( + unacked_packets_.GetTransmissionInfo(QuicPacketNumber(packets[i])) + .in_flight); + } + size_t in_flight_count = 0; + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it) { + if (it->in_flight) { + ++in_flight_count; + } + } + EXPECT_EQ(num_packets, in_flight_count); + } + + void VerifyUnackedPackets(uint64_t* packets, size_t num_packets) { + unacked_packets_.RemoveObsoletePackets(); + if (num_packets == 0) { + EXPECT_TRUE(unacked_packets_.empty()); + if (!GetQuicReloadableFlag(quic_optimize_inflight_check)) { + EXPECT_FALSE(unacked_packets_.HasUnackedRetransmittableFrames()); + } + return; + } + EXPECT_FALSE(unacked_packets_.empty()); + for (size_t i = 0; i < num_packets; ++i) { + EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[i]))) + << packets[i]; + } + EXPECT_EQ(num_packets, unacked_packets_.GetNumUnackedPacketsDebugOnly()); + } + + void VerifyRetransmittablePackets(uint64_t* packets, size_t num_packets) { + unacked_packets_.RemoveObsoletePackets(); + size_t num_retransmittable_packets = 0; + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it) { + if (unacked_packets_.HasRetransmittableFrames(*it)) { + ++num_retransmittable_packets; + } + } + EXPECT_EQ(num_packets, num_retransmittable_packets); + for (size_t i = 0; i < num_packets; ++i) { + EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames( + QuicPacketNumber(packets[i]))) + << " packets[" << i << "]:" << packets[i]; + } + } + + void UpdatePacketState(uint64_t packet_number, SentPacketState state) { + unacked_packets_ + .GetMutableTransmissionInfo(QuicPacketNumber(packet_number)) + ->state = state; + } + + void RetransmitAndSendPacket(uint64_t old_packet_number, + uint64_t new_packet_number, + TransmissionType transmission_type) { + DCHECK(unacked_packets_.HasRetransmittableFrames( + QuicPacketNumber(old_packet_number))); + if (!unacked_packets_.session_decides_what_to_write()) { + SerializedPacket packet( + CreateNonRetransmittablePacket(new_packet_number)); + unacked_packets_.AddSentPacket(&packet, + QuicPacketNumber(old_packet_number), + transmission_type, now_, true); + return; + } + QuicTransmissionInfo* info = unacked_packets_.GetMutableTransmissionInfo( + QuicPacketNumber(old_packet_number)); + QuicStreamId stream_id = QuicUtils::GetHeadersStreamId( + CurrentSupportedVersions()[0].transport_version); + for (const auto& frame : info->retransmittable_frames) { + if (frame.type == STREAM_FRAME) { + stream_id = frame.stream_frame.stream_id; + break; + } + } + UpdatePacketState( + old_packet_number, + QuicUtils::RetransmissionTypeToPacketState(transmission_type)); + info->retransmission = QuicPacketNumber(new_packet_number); + SerializedPacket packet( + CreateRetransmittablePacketForStream(new_packet_number, stream_id)); + unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(), + transmission_type, now_, true); + } + QuicUnackedPacketMap unacked_packets_; + QuicTime now_; + StrictMock<MockSessionNotifier> notifier_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QuicUnackedPacketMapTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QuicUnackedPacketMapTest, RttOnly) { + // Acks are only tracked for RTT measurement purposes. + SerializedPacket packet(CreateNonRetransmittablePacket(1)); + unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, false); + + uint64_t unacked[] = {1}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(nullptr, 0); + VerifyRetransmittablePackets(nullptr, 0); + + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(1)); + VerifyUnackedPackets(nullptr, 0); + VerifyInFlightPackets(nullptr, 0); + VerifyRetransmittablePackets(nullptr, 0); +} + +TEST_P(QuicUnackedPacketMapTest, RetransmittableInflightAndRtt) { + // Simulate a retransmittable packet being sent and acked. + SerializedPacket packet(CreateRetransmittablePacket(1)); + unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + uint64_t unacked[] = {1}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(unacked, QUIC_ARRAYSIZE(unacked)); + + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1)); + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); + + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(1)); + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); + + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1)); + VerifyUnackedPackets(nullptr, 0); + VerifyInFlightPackets(nullptr, 0); + VerifyRetransmittablePackets(nullptr, 0); +} + +TEST_P(QuicUnackedPacketMapTest, StopRetransmission) { + const QuicStreamId stream_id = 2; + SerializedPacket packet(CreateRetransmittablePacketForStream(1, stream_id)); + unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + uint64_t unacked[] = {1}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + uint64_t retransmittable[] = {1}; + VerifyRetransmittablePackets(retransmittable, + QUIC_ARRAYSIZE(retransmittable)); + + if (unacked_packets_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + } else { + unacked_packets_.CancelRetransmissionsForStream(stream_id); + } + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); +} + +TEST_P(QuicUnackedPacketMapTest, StopRetransmissionOnOtherStream) { + const QuicStreamId stream_id = 2; + SerializedPacket packet(CreateRetransmittablePacketForStream(1, stream_id)); + unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + uint64_t unacked[] = {1}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + uint64_t retransmittable[] = {1}; + VerifyRetransmittablePackets(retransmittable, + QUIC_ARRAYSIZE(retransmittable)); + + // Stop retransmissions on another stream and verify the packet is unchanged. + if (!unacked_packets_.session_decides_what_to_write()) { + unacked_packets_.CancelRetransmissionsForStream(stream_id + 2); + } + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(retransmittable, + QUIC_ARRAYSIZE(retransmittable)); +} + +TEST_P(QuicUnackedPacketMapTest, StopRetransmissionAfterRetransmission) { + const QuicStreamId stream_id = 2; + SerializedPacket packet1(CreateRetransmittablePacketForStream(1, stream_id)); + unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + RetransmitAndSendPacket(1, 2, LOSS_RETRANSMISSION); + + uint64_t unacked[] = {1, 2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + std::vector<uint64_t> retransmittable; + if (unacked_packets_.session_decides_what_to_write()) { + retransmittable = {1, 2}; + } else { + retransmittable = {2}; + } + VerifyRetransmittablePackets(&retransmittable[0], retransmittable.size()); + + if (unacked_packets_.session_decides_what_to_write()) { + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + } else { + unacked_packets_.CancelRetransmissionsForStream(stream_id); + } + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); +} + +TEST_P(QuicUnackedPacketMapTest, RetransmittedPacket) { + // Simulate a retransmittable packet being sent, retransmitted, and the first + // transmission being acked. + SerializedPacket packet1(CreateRetransmittablePacket(1)); + unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + RetransmitAndSendPacket(1, 2, LOSS_RETRANSMISSION); + + uint64_t unacked[] = {1, 2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + std::vector<uint64_t> retransmittable; + if (unacked_packets_.session_decides_what_to_write()) { + retransmittable = {1, 2}; + } else { + retransmittable = {2}; + } + VerifyRetransmittablePackets(&retransmittable[0], retransmittable.size()); + + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1)); + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); + + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2)); + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyRetransmittablePackets(nullptr, 0); + + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + uint64_t unacked2[] = {1}; + VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2)); + VerifyInFlightPackets(unacked2, QUIC_ARRAYSIZE(unacked2)); + VerifyRetransmittablePackets(nullptr, 0); + + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1)); + VerifyUnackedPackets(nullptr, 0); + VerifyInFlightPackets(nullptr, 0); + VerifyRetransmittablePackets(nullptr, 0); +} + +TEST_P(QuicUnackedPacketMapTest, RetransmitThreeTimes) { + // Simulate a retransmittable packet being sent and retransmitted twice. + SerializedPacket packet1(CreateRetransmittablePacket(1)); + unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + SerializedPacket packet2(CreateRetransmittablePacket(2)); + unacked_packets_.AddSentPacket(&packet2, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + uint64_t unacked[] = {1, 2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + uint64_t retransmittable[] = {1, 2}; + VerifyRetransmittablePackets(retransmittable, + QUIC_ARRAYSIZE(retransmittable)); + + // Early retransmit 1 as 3 and send new data as 4. + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(2)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1)); + RetransmitAndSendPacket(1, 3, LOSS_RETRANSMISSION); + SerializedPacket packet4(CreateRetransmittablePacket(4)); + unacked_packets_.AddSentPacket(&packet4, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + uint64_t unacked2[] = {1, 3, 4}; + VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2)); + uint64_t pending2[] = {3, 4}; + VerifyInFlightPackets(pending2, QUIC_ARRAYSIZE(pending2)); + std::vector<uint64_t> retransmittable2; + if (unacked_packets_.session_decides_what_to_write()) { + retransmittable2 = {1, 3, 4}; + } else { + retransmittable2 = {3, 4}; + } + VerifyRetransmittablePackets(&retransmittable2[0], retransmittable2.size()); + + // Early retransmit 3 (formerly 1) as 5, and remove 1 from unacked. + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(4)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(4)); + RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION); + SerializedPacket packet6(CreateRetransmittablePacket(6)); + unacked_packets_.AddSentPacket(&packet6, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + std::vector<uint64_t> unacked3; + std::vector<uint64_t> retransmittable3; + if (unacked_packets_.session_decides_what_to_write()) { + unacked3 = {3, 5, 6}; + retransmittable3 = {3, 5, 6}; + } else { + unacked3 = {3, 5, 6}; + retransmittable3 = {5, 6}; + } + VerifyUnackedPackets(&unacked3[0], unacked3.size()); + VerifyRetransmittablePackets(&retransmittable3[0], retransmittable3.size()); + uint64_t pending3[] = {3, 5, 6}; + VerifyInFlightPackets(pending3, QUIC_ARRAYSIZE(pending3)); + + // Early retransmit 5 as 7 and ensure in flight packet 3 is not removed. + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(6)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(6)); + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(6)); + RetransmitAndSendPacket(5, 7, LOSS_RETRANSMISSION); + + std::vector<uint64_t> unacked4; + std::vector<uint64_t> retransmittable4; + if (unacked_packets_.session_decides_what_to_write()) { + unacked4 = {3, 5, 7}; + retransmittable4 = {3, 5, 7}; + } else { + unacked4 = {3, 5, 7}; + retransmittable4 = {7}; + } + VerifyUnackedPackets(&unacked4[0], unacked4.size()); + VerifyRetransmittablePackets(&retransmittable4[0], retransmittable4.size()); + uint64_t pending4[] = {3, 5, 7}; + VerifyInFlightPackets(pending4, QUIC_ARRAYSIZE(pending4)); + + // Remove the older two transmissions from in flight. + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5)); + uint64_t pending5[] = {7}; + VerifyInFlightPackets(pending5, QUIC_ARRAYSIZE(pending5)); +} + +TEST_P(QuicUnackedPacketMapTest, RetransmitFourTimes) { + // Simulate a retransmittable packet being sent and retransmitted twice. + SerializedPacket packet1(CreateRetransmittablePacket(1)); + unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + SerializedPacket packet2(CreateRetransmittablePacket(2)); + unacked_packets_.AddSentPacket(&packet2, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + uint64_t unacked[] = {1, 2}; + VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked)); + VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked)); + uint64_t retransmittable[] = {1, 2}; + VerifyRetransmittablePackets(retransmittable, + QUIC_ARRAYSIZE(retransmittable)); + + // Early retransmit 1 as 3. + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(2)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1)); + RetransmitAndSendPacket(1, 3, LOSS_RETRANSMISSION); + + uint64_t unacked2[] = {1, 3}; + VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2)); + uint64_t pending2[] = {3}; + VerifyInFlightPackets(pending2, QUIC_ARRAYSIZE(pending2)); + std::vector<uint64_t> retransmittable2; + if (unacked_packets_.session_decides_what_to_write()) { + retransmittable2 = {1, 3}; + } else { + retransmittable2 = {3}; + } + VerifyRetransmittablePackets(&retransmittable2[0], retransmittable2.size()); + + // TLP 3 (formerly 1) as 4, and don't remove 1 from unacked. + RetransmitAndSendPacket(3, 4, TLP_RETRANSMISSION); + SerializedPacket packet5(CreateRetransmittablePacket(5)); + unacked_packets_.AddSentPacket(&packet5, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + + uint64_t unacked3[] = {1, 3, 4, 5}; + VerifyUnackedPackets(unacked3, QUIC_ARRAYSIZE(unacked3)); + uint64_t pending3[] = {3, 4, 5}; + VerifyInFlightPackets(pending3, QUIC_ARRAYSIZE(pending3)); + std::vector<uint64_t> retransmittable3; + if (unacked_packets_.session_decides_what_to_write()) { + retransmittable3 = {1, 3, 4, 5}; + } else { + retransmittable3 = {4, 5}; + } + VerifyRetransmittablePackets(&retransmittable3[0], retransmittable3.size()); + + // Early retransmit 4 as 6 and ensure in flight packet 3 is removed. + unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(5)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5)); + unacked_packets_.RemoveRetransmittability(QuicPacketNumber(5)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3)); + unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); + RetransmitAndSendPacket(4, 6, LOSS_RETRANSMISSION); + + std::vector<uint64_t> unacked4; + if (unacked_packets_.session_decides_what_to_write()) { + unacked4 = {4, 6}; + } else { + unacked4 = {4, 6}; + } + VerifyUnackedPackets(&unacked4[0], unacked4.size()); + uint64_t pending4[] = {6}; + VerifyInFlightPackets(pending4, QUIC_ARRAYSIZE(pending4)); + std::vector<uint64_t> retransmittable4; + if (unacked_packets_.session_decides_what_to_write()) { + retransmittable4 = {4, 6}; + } else { + retransmittable4 = {6}; + } + VerifyRetransmittablePackets(&retransmittable4[0], retransmittable4.size()); +} + +TEST_P(QuicUnackedPacketMapTest, SendWithGap) { + // Simulate a retransmittable packet being sent, retransmitted, and the first + // transmission being acked. + SerializedPacket packet1(CreateRetransmittablePacket(1)); + unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + SerializedPacket packet3(CreateRetransmittablePacket(3)); + unacked_packets_.AddSentPacket(&packet3, QuicPacketNumber(), + NOT_RETRANSMISSION, now_, true); + RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION); + + EXPECT_EQ(QuicPacketNumber(1u), unacked_packets_.GetLeastUnacked()); + EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(1))); + EXPECT_FALSE(unacked_packets_.IsUnacked(QuicPacketNumber(2))); + EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(3))); + EXPECT_FALSE(unacked_packets_.IsUnacked(QuicPacketNumber(4))); + EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(5))); + EXPECT_EQ(QuicPacketNumber(5u), unacked_packets_.largest_sent_packet()); +} + +TEST_P(QuicUnackedPacketMapTest, AggregateContiguousAckedStreamFrames) { + testing::InSequence s; + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0); + unacked_packets_.NotifyAggregatedStreamFrameAcked(QuicTime::Delta::Zero()); + + QuicTransmissionInfo info1; + QuicStreamFrame stream_frame1(3, false, 0, 100); + info1.retransmittable_frames.push_back(QuicFrame(stream_frame1)); + + QuicTransmissionInfo info2; + QuicStreamFrame stream_frame2(3, false, 100, 100); + info2.retransmittable_frames.push_back(QuicFrame(stream_frame2)); + + QuicTransmissionInfo info3; + QuicStreamFrame stream_frame3(3, false, 200, 100); + info3.retransmittable_frames.push_back(QuicFrame(stream_frame3)); + + QuicTransmissionInfo info4; + QuicStreamFrame stream_frame4(3, true, 300, 0); + info4.retransmittable_frames.push_back(QuicFrame(stream_frame4)); + + // Verify stream frames are aggregated. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0); + unacked_packets_.MaybeAggregateAckedStreamFrame(info1, + QuicTime::Delta::Zero()); + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0); + unacked_packets_.MaybeAggregateAckedStreamFrame(info2, + QuicTime::Delta::Zero()); + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0); + unacked_packets_.MaybeAggregateAckedStreamFrame(info3, + QuicTime::Delta::Zero()); + + // Verify aggregated stream frame gets acked since fin is acked. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1); + unacked_packets_.MaybeAggregateAckedStreamFrame(info4, + QuicTime::Delta::Zero()); +} + +// Regression test for b/112930090. +TEST_P(QuicUnackedPacketMapTest, CannotAggregateIfDataLengthOverflow) { + QuicByteCount kMaxAggregatedDataLength = + std::numeric_limits<decltype(QuicStreamFrame().data_length)>::max(); + QuicStreamId stream_id = 2; + + // acked_stream_length=512 covers the case where a frame will cause the + // aggregated frame length to be exactly 64K. + // acked_stream_length=1300 covers the case where a frame will cause the + // aggregated frame length to exceed 64K. + for (const QuicPacketLength acked_stream_length : {512, 1300}) { + ++stream_id; + QuicStreamOffset offset = 0; + // Expected length of the aggregated stream frame. + QuicByteCount aggregated_data_length = 0; + + while (offset < 1e6) { + QuicTransmissionInfo info; + QuicStreamFrame stream_frame(stream_id, false, offset, + acked_stream_length); + info.retransmittable_frames.push_back(QuicFrame(stream_frame)); + + const QuicStreamFrame& aggregated_stream_frame = + QuicUnackedPacketMapPeer::GetAggregatedStreamFrame(unacked_packets_); + if (aggregated_stream_frame.data_length + acked_stream_length <= + kMaxAggregatedDataLength) { + // Verify the acked stream frame can be aggregated. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0); + unacked_packets_.MaybeAggregateAckedStreamFrame( + info, QuicTime::Delta::Zero()); + aggregated_data_length += acked_stream_length; + testing::Mock::VerifyAndClearExpectations(¬ifier_); + } else { + // Verify the acked stream frame cannot be aggregated because + // data_length is overflow. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1); + unacked_packets_.MaybeAggregateAckedStreamFrame( + info, QuicTime::Delta::Zero()); + aggregated_data_length = acked_stream_length; + testing::Mock::VerifyAndClearExpectations(¬ifier_); + } + + EXPECT_EQ(aggregated_data_length, aggregated_stream_frame.data_length); + offset += acked_stream_length; + } + + // Ack the last frame of the stream. + QuicTransmissionInfo info; + QuicStreamFrame stream_frame(stream_id, true, offset, acked_stream_length); + info.retransmittable_frames.push_back(QuicFrame(stream_frame)); + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1); + unacked_packets_.MaybeAggregateAckedStreamFrame(info, + QuicTime::Delta::Zero()); + testing::Mock::VerifyAndClearExpectations(¬ifier_); + } +} + +TEST_P(QuicUnackedPacketMapTest, CannotAggregateAckedControlFrames) { + testing::InSequence s; + QuicWindowUpdateFrame window_update(1, 5, 100); + QuicStreamFrame stream_frame1(3, false, 0, 100); + QuicStreamFrame stream_frame2(3, false, 100, 100); + QuicBlockedFrame blocked(2, 5); + QuicGoAwayFrame go_away(3, QUIC_PEER_GOING_AWAY, 5, "Going away."); + + QuicTransmissionInfo info1; + info1.retransmittable_frames.push_back(QuicFrame(&window_update)); + info1.retransmittable_frames.push_back(QuicFrame(stream_frame1)); + info1.retransmittable_frames.push_back(QuicFrame(stream_frame2)); + + QuicTransmissionInfo info2; + info2.retransmittable_frames.push_back(QuicFrame(&blocked)); + info2.retransmittable_frames.push_back(QuicFrame(&go_away)); + + // Verify 2 contiguous stream frames are aggregated. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1); + unacked_packets_.MaybeAggregateAckedStreamFrame(info1, + QuicTime::Delta::Zero()); + // Verify aggregated stream frame gets acked. + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(3); + unacked_packets_.MaybeAggregateAckedStreamFrame(info2, + QuicTime::Delta::Zero()); + + EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0); + unacked_packets_.NotifyAggregatedStreamFrameAcked(QuicTime::Delta::Zero()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc new file mode 100644 index 0000000..d83db1c --- /dev/null +++ b/quic/core/quic_utils.cc
@@ -0,0 +1,512 @@ +// Copyright (c) 2012 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/quic_utils.h" + +#include <algorithm> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_prefetch.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { +namespace { + +// We know that >= GCC 4.8 and Clang have a __uint128_t intrinsic. Other +// compilers don't necessarily, notably MSVC. +#if defined(__x86_64__) && \ + ((defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ + defined(__clang__)) +#define QUIC_UTIL_HAS_UINT128 1 +#endif + +#ifdef QUIC_UTIL_HAS_UINT128 +QuicUint128 IncrementalHashFast(QuicUint128 uhash, QuicStringPiece data) { + // This code ends up faster than the naive implementation for 2 reasons: + // 1. QuicUint128 is sufficiently complicated that the compiler + // cannot transform the multiplication by kPrime into a shift-multiply-add; + // it has go through all of the instructions for a 128-bit multiply. + // 2. Because there are so fewer instructions (around 13), the hot loop fits + // nicely in the instruction queue of many Intel CPUs. + // kPrime = 309485009821345068724781371 + static const QuicUint128 kPrime = + (static_cast<QuicUint128>(16777216) << 64) + 315; + auto hi = QuicUint128High64(uhash); + auto lo = QuicUint128Low64(uhash); + QuicUint128 xhash = (static_cast<QuicUint128>(hi) << 64) + lo; + const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data()); + for (size_t i = 0; i < data.length(); ++i) { + xhash = (xhash ^ static_cast<uint32_t>(octets[i])) * kPrime; + } + return MakeQuicUint128(QuicUint128High64(xhash), QuicUint128Low64(xhash)); +} +#endif + +#ifndef QUIC_UTIL_HAS_UINT128 +// Slow implementation of IncrementalHash. In practice, only used by Chromium. +QuicUint128 IncrementalHashSlow(QuicUint128 hash, QuicStringPiece data) { + // kPrime = 309485009821345068724781371 + static const QuicUint128 kPrime = MakeQuicUint128(16777216, 315); + const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data()); + for (size_t i = 0; i < data.length(); ++i) { + hash = hash ^ MakeQuicUint128(0, octets[i]); + hash = hash * kPrime; + } + return hash; +} +#endif + +QuicUint128 IncrementalHash(QuicUint128 hash, QuicStringPiece data) { +#ifdef QUIC_UTIL_HAS_UINT128 + return IncrementalHashFast(hash, data); +#else + return IncrementalHashSlow(hash, data); +#endif +} + +} // namespace + +// static +uint64_t QuicUtils::FNV1a_64_Hash(QuicStringPiece data) { + static const uint64_t kOffset = UINT64_C(14695981039346656037); + static const uint64_t kPrime = UINT64_C(1099511628211); + + const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data()); + + uint64_t hash = kOffset; + + for (size_t i = 0; i < data.length(); ++i) { + hash = hash ^ octets[i]; + hash = hash * kPrime; + } + + return hash; +} + +// static +QuicUint128 QuicUtils::FNV1a_128_Hash(QuicStringPiece data) { + return FNV1a_128_Hash_Three(data, QuicStringPiece(), QuicStringPiece()); +} + +// static +QuicUint128 QuicUtils::FNV1a_128_Hash_Two(QuicStringPiece data1, + QuicStringPiece data2) { + return FNV1a_128_Hash_Three(data1, data2, QuicStringPiece()); +} + +// static +QuicUint128 QuicUtils::FNV1a_128_Hash_Three(QuicStringPiece data1, + QuicStringPiece data2, + QuicStringPiece data3) { + // The two constants are defined as part of the hash algorithm. + // see http://www.isthe.com/chongo/tech/comp/fnv/ + // kOffset = 144066263297769815596495629667062367629 + const QuicUint128 kOffset = MakeQuicUint128(UINT64_C(7809847782465536322), + UINT64_C(7113472399480571277)); + + QuicUint128 hash = IncrementalHash(kOffset, data1); + if (data2.empty()) { + return hash; + } + + hash = IncrementalHash(hash, data2); + if (data3.empty()) { + return hash; + } + return IncrementalHash(hash, data3); +} + +// static +void QuicUtils::SerializeUint128Short(QuicUint128 v, uint8_t* out) { + const uint64_t lo = QuicUint128Low64(v); + const uint64_t hi = QuicUint128High64(v); + // This assumes that the system is little-endian. + memcpy(out, &lo, sizeof(lo)); + memcpy(out + sizeof(lo), &hi, sizeof(hi) / 2); +} + +#define RETURN_STRING_LITERAL(x) \ + case x: \ + return #x; + +// static +const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) { + switch (level) { + RETURN_STRING_LITERAL(ENCRYPTION_NONE); + RETURN_STRING_LITERAL(ENCRYPTION_ZERO_RTT); + RETURN_STRING_LITERAL(ENCRYPTION_FORWARD_SECURE); + RETURN_STRING_LITERAL(NUM_ENCRYPTION_LEVELS); + } + return "INVALID_ENCRYPTION_LEVEL"; +} + +// static +const char* QuicUtils::TransmissionTypeToString(TransmissionType type) { + switch (type) { + RETURN_STRING_LITERAL(NOT_RETRANSMISSION); + RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMISSION); + RETURN_STRING_LITERAL(LOSS_RETRANSMISSION); + RETURN_STRING_LITERAL(ALL_UNACKED_RETRANSMISSION); + RETURN_STRING_LITERAL(ALL_INITIAL_RETRANSMISSION); + RETURN_STRING_LITERAL(RTO_RETRANSMISSION); + RETURN_STRING_LITERAL(TLP_RETRANSMISSION); + RETURN_STRING_LITERAL(PROBING_RETRANSMISSION); + } + return "INVALID_TRANSMISSION_TYPE"; +} + +QuicString QuicUtils::AddressChangeTypeToString(AddressChangeType type) { + switch (type) { + RETURN_STRING_LITERAL(NO_CHANGE); + RETURN_STRING_LITERAL(PORT_CHANGE); + RETURN_STRING_LITERAL(IPV4_SUBNET_CHANGE); + RETURN_STRING_LITERAL(IPV4_TO_IPV6_CHANGE); + RETURN_STRING_LITERAL(IPV6_TO_IPV4_CHANGE); + RETURN_STRING_LITERAL(IPV6_TO_IPV6_CHANGE); + RETURN_STRING_LITERAL(IPV4_TO_IPV4_CHANGE); + } + return "INVALID_ADDRESS_CHANGE_TYPE"; +} + +const char* QuicUtils::SentPacketStateToString(SentPacketState state) { + switch (state) { + RETURN_STRING_LITERAL(OUTSTANDING); + RETURN_STRING_LITERAL(NEVER_SENT); + RETURN_STRING_LITERAL(ACKED); + RETURN_STRING_LITERAL(UNACKABLE); + RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMITTED); + RETURN_STRING_LITERAL(LOST); + RETURN_STRING_LITERAL(TLP_RETRANSMITTED); + RETURN_STRING_LITERAL(RTO_RETRANSMITTED); + RETURN_STRING_LITERAL(PROBE_RETRANSMITTED); + } + return "INVALID_SENT_PACKET_STATE"; +} + +// static +const char* QuicUtils::QuicLongHeaderTypetoString(QuicLongHeaderType type) { + switch (type) { + RETURN_STRING_LITERAL(VERSION_NEGOTIATION); + RETURN_STRING_LITERAL(INITIAL); + RETURN_STRING_LITERAL(RETRY); + RETURN_STRING_LITERAL(HANDSHAKE); + RETURN_STRING_LITERAL(ZERO_RTT_PROTECTED); + default: + return "INVALID_PACKET_TYPE"; + } +} + +// static +AddressChangeType QuicUtils::DetermineAddressChangeType( + const QuicSocketAddress& old_address, + const QuicSocketAddress& new_address) { + if (!old_address.IsInitialized() || !new_address.IsInitialized() || + old_address == new_address) { + return NO_CHANGE; + } + + if (old_address.host() == new_address.host()) { + return PORT_CHANGE; + } + + bool old_ip_is_ipv4 = old_address.host().IsIPv4() ? true : false; + bool migrating_ip_is_ipv4 = new_address.host().IsIPv4() ? true : false; + if (old_ip_is_ipv4 && !migrating_ip_is_ipv4) { + return IPV4_TO_IPV6_CHANGE; + } + + if (!old_ip_is_ipv4) { + return migrating_ip_is_ipv4 ? IPV6_TO_IPV4_CHANGE : IPV6_TO_IPV6_CHANGE; + } + + const int kSubnetMaskLength = 24; + if (old_address.host().InSameSubnet(new_address.host(), kSubnetMaskLength)) { + // Subnet part does not change (here, we use /24), which is considered to be + // caused by NATs. + return IPV4_SUBNET_CHANGE; + } + + return IPV4_TO_IPV4_CHANGE; +} + +// static +void QuicUtils::CopyToBuffer(const struct iovec* iov, + int iov_count, + size_t iov_offset, + size_t buffer_length, + char* buffer) { + int iovnum = 0; + while (iovnum < iov_count && iov_offset >= iov[iovnum].iov_len) { + iov_offset -= iov[iovnum].iov_len; + ++iovnum; + } + DCHECK_LE(iovnum, iov_count); + DCHECK_LE(iov_offset, iov[iovnum].iov_len); + if (iovnum >= iov_count || buffer_length == 0) { + return; + } + + // Unroll the first iteration that handles iov_offset. + const size_t iov_available = iov[iovnum].iov_len - iov_offset; + size_t copy_len = std::min(buffer_length, iov_available); + + // Try to prefetch the next iov if there is at least one more after the + // current. Otherwise, it looks like an irregular access that the hardware + // prefetcher won't speculatively prefetch. Only prefetch one iov because + // generally, the iov_offset is not 0, input iov consists of 2K buffers and + // the output buffer is ~1.4K. + if (copy_len == iov_available && iovnum + 1 < iov_count) { + char* next_base = static_cast<char*>(iov[iovnum + 1].iov_base); + // Prefetch 2 cachelines worth of data to get the prefetcher started; leave + // it to the hardware prefetcher after that. + QuicPrefetchT0(next_base); + if (iov[iovnum + 1].iov_len >= 64) { + QuicPrefetchT0(next_base + QUIC_CACHELINE_SIZE); + } + } + + const char* src = static_cast<char*>(iov[iovnum].iov_base) + iov_offset; + while (true) { + memcpy(buffer, src, copy_len); + buffer_length -= copy_len; + buffer += copy_len; + if (buffer_length == 0 || ++iovnum >= iov_count) { + break; + } + src = static_cast<char*>(iov[iovnum].iov_base); + copy_len = std::min(buffer_length, iov[iovnum].iov_len); + } + QUIC_BUG_IF(buffer_length > 0) << "Failed to copy entire length to buffer."; +} + +// static +struct iovec QuicUtils::MakeIovec(QuicStringPiece data) { + struct iovec iov = {const_cast<char*>(data.data()), + static_cast<size_t>(data.size())}; + return iov; +} + +// static +bool QuicUtils::IsAckable(SentPacketState state) { + return state != NEVER_SENT && state != ACKED && state != UNACKABLE; +} + +// static +bool QuicUtils::IsRetransmittableFrame(QuicFrameType type) { + switch (type) { + case ACK_FRAME: + case PADDING_FRAME: + case STOP_WAITING_FRAME: + case MTU_DISCOVERY_FRAME: + return false; + default: + return true; + } +} + +// static +bool QuicUtils::IsHandshakeFrame(const QuicFrame& frame, + QuicTransportVersion transport_version) { + if (transport_version < QUIC_VERSION_47) { + return frame.type == STREAM_FRAME && + frame.stream_frame.stream_id == GetCryptoStreamId(transport_version); + } else { + return frame.type == CRYPTO_FRAME; + } +} + +// static +SentPacketState QuicUtils::RetransmissionTypeToPacketState( + TransmissionType retransmission_type) { + switch (retransmission_type) { + case ALL_UNACKED_RETRANSMISSION: + case ALL_INITIAL_RETRANSMISSION: + return UNACKABLE; + case HANDSHAKE_RETRANSMISSION: + return HANDSHAKE_RETRANSMITTED; + case LOSS_RETRANSMISSION: + return LOST; + case TLP_RETRANSMISSION: + return TLP_RETRANSMITTED; + case RTO_RETRANSMISSION: + return RTO_RETRANSMITTED; + case PROBING_RETRANSMISSION: + return PROBE_RETRANSMITTED; + default: + QUIC_BUG << QuicUtils::TransmissionTypeToString(retransmission_type) + << " is not a retransmission_type"; + return UNACKABLE; + } +} + +// static +bool QuicUtils::IsIetfPacketHeader(uint8_t first_byte) { + return (first_byte & FLAGS_LONG_HEADER) || (first_byte & FLAGS_FIXED_BIT) || + !(first_byte & FLAGS_DEMULTIPLEXING_BIT); +} + +// static +bool QuicUtils::IsIetfPacketShortHeader(uint8_t first_byte) { + return IsIetfPacketHeader(first_byte) && !(first_byte & FLAGS_LONG_HEADER); +} + +// static +QuicStreamId QuicUtils::GetInvalidStreamId(QuicTransportVersion version) { + return version == QUIC_VERSION_99 ? std::numeric_limits<QuicStreamId>::max() + : 0; +} + +// static +QuicStreamId QuicUtils::GetCryptoStreamId(QuicTransportVersion version) { + // TODO(nharper): Change this to return GetInvalidStreamId for version 47 or + // greater. Currently, too many things break with that change. + return version == QUIC_VERSION_99 ? 0 : 1; +} + +// static +QuicStreamId QuicUtils::GetHeadersStreamId(QuicTransportVersion version) { + return version == QUIC_VERSION_99 ? 4 : 3; +} + +// static +bool QuicUtils::IsClientInitiatedStreamId(QuicTransportVersion version, + QuicStreamId id) { + if (id == GetInvalidStreamId(version)) { + return false; + } + return version == QUIC_VERSION_99 ? id % 2 == 0 : id % 2 != 0; +} + +// static +bool QuicUtils::IsServerInitiatedStreamId(QuicTransportVersion version, + QuicStreamId id) { + if (id == GetInvalidStreamId(version)) { + return false; + } + return version == QUIC_VERSION_99 ? id % 2 != 0 : id % 2 == 0; +} + +// static +bool QuicUtils::IsBidirectionalStreamId(QuicStreamId id) { + return id % 4 < 2; +} + +// static +StreamType QuicUtils::GetStreamType(QuicStreamId id, + Perspective perspective, + bool peer_initiated) { + if (IsBidirectionalStreamId(id)) { + return BIDIRECTIONAL; + } + + if (peer_initiated) { + if (perspective == Perspective::IS_SERVER) { + DCHECK_EQ(2u, id % 4); + } else { + DCHECK_EQ(Perspective::IS_CLIENT, perspective); + DCHECK_EQ(3u, id % 4); + } + return READ_UNIDIRECTIONAL; + } + + if (perspective == Perspective::IS_SERVER) { + DCHECK_EQ(3u, id % 4); + } else { + DCHECK_EQ(Perspective::IS_CLIENT, perspective); + DCHECK_EQ(2u, id % 4); + } + return WRITE_UNIDIRECTIONAL; +} + +// static +QuicStreamId QuicUtils::StreamIdDelta(QuicTransportVersion version) { + return version == QUIC_VERSION_99 ? 4 : 2; +} + +// static +QuicStreamId QuicUtils::GetFirstBidirectionalStreamId( + QuicTransportVersion version, + Perspective perspective) { + if (perspective == Perspective::IS_CLIENT) { + return version == QUIC_VERSION_99 ? 4 : 3; + } + return version == QUIC_VERSION_99 ? 1 : 2; +} + +// static +QuicStreamId QuicUtils::GetFirstUnidirectionalStreamId( + QuicTransportVersion version, + Perspective perspective) { + if (perspective == Perspective::IS_CLIENT) { + return version == QUIC_VERSION_99 ? 2 : 3; + } + return version == QUIC_VERSION_99 ? 3 : 2; +} + +// static +QuicConnectionId QuicUtils::CreateRandomConnectionId() { + return CreateRandomConnectionId(QuicRandom::GetInstance()); +} + +// static +QuicConnectionId QuicUtils::CreateRandomConnectionId(QuicRandom* random) { + char connection_id_bytes[kQuicDefaultConnectionIdLength]; + random->RandBytes(connection_id_bytes, QUIC_ARRAYSIZE(connection_id_bytes)); + return QuicConnectionId(static_cast<char*>(connection_id_bytes), + QUIC_ARRAYSIZE(connection_id_bytes)); +} + +// static +bool QuicUtils::VariableLengthConnectionIdAllowedForVersion( + QuicTransportVersion version) { + // TODO(dschinazi): Allow in appropriate version when supported. + return false; +} + +// static +QuicConnectionId QuicUtils::CreateZeroConnectionId( + QuicTransportVersion version) { + if (!VariableLengthConnectionIdAllowedForVersion(version)) { + char connection_id_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + return QuicConnectionId(static_cast<char*>(connection_id_bytes), + QUIC_ARRAYSIZE(connection_id_bytes)); + } + return EmptyQuicConnectionId(); +} + +// static +bool QuicUtils::IsConnectionIdValidForVersion(QuicConnectionId connection_id, + QuicTransportVersion version) { + if (VariableLengthConnectionIdAllowedForVersion(version)) { + return true; + } + return connection_id.length() == kQuicDefaultConnectionIdLength; +} + +QuicUint128 QuicUtils::GenerateStatelessResetToken( + QuicConnectionId connection_id) { + uint64_t data_bytes[3] = {0, 0, 0}; + static_assert(sizeof(data_bytes) >= kQuicMaxConnectionIdLength, + "kQuicMaxConnectionIdLength changed"); + memcpy(data_bytes, connection_id.data(), connection_id.length()); + // This is designed so that the common case of 64bit connection IDs + // produces a stateless reset token that is equal to the connection ID + // interpreted as a 64bit unsigned integer, to facilitate debugging. + return MakeQuicUint128( + QuicEndian::NetToHost64(sizeof(uint64_t) ^ connection_id.length() ^ + data_bytes[1] ^ data_bytes[2]), + QuicEndian::NetToHost64(data_bytes[0])); +} + +#undef RETURN_STRING_LITERAL // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h new file mode 100644 index 0000000..28ea15b --- /dev/null +++ b/quic/core/quic_utils.h
@@ -0,0 +1,178 @@ +// Copyright (c) 2012 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_QUIC_UTILS_H_ +#define QUICHE_QUIC_CORE_QUIC_UTILS_H_ + +#include <cstddef> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicUtils { + public: + QuicUtils() = delete; + + // Returns the 64 bit FNV1a hash of the data. See + // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param + static uint64_t FNV1a_64_Hash(QuicStringPiece data); + + // Returns the 128 bit FNV1a hash of the data. See + // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param + static QuicUint128 FNV1a_128_Hash(QuicStringPiece data); + + // Returns the 128 bit FNV1a hash of the two sequences of data. See + // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param + static QuicUint128 FNV1a_128_Hash_Two(QuicStringPiece data1, + QuicStringPiece data2); + + // Returns the 128 bit FNV1a hash of the three sequences of data. See + // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param + static QuicUint128 FNV1a_128_Hash_Three(QuicStringPiece data1, + QuicStringPiece data2, + QuicStringPiece data3); + + // SerializeUint128 writes the first 96 bits of |v| in little-endian form + // to |out|. + static void SerializeUint128Short(QuicUint128 v, uint8_t* out); + + // Returns the level of encryption as a char* + static const char* EncryptionLevelToString(EncryptionLevel level); + + // Returns TransmissionType as a char* + static const char* TransmissionTypeToString(TransmissionType type); + + // Returns AddressChangeType as a string. + static QuicString AddressChangeTypeToString(AddressChangeType type); + + // Returns SentPacketState as a char*. + static const char* SentPacketStateToString(SentPacketState state); + + // Returns QuicLongHeaderType as a char*. + static const char* QuicLongHeaderTypetoString(QuicLongHeaderType type); + + // Determines and returns change type of address change from |old_address| to + // |new_address|. + static AddressChangeType DetermineAddressChangeType( + const QuicSocketAddress& old_address, + const QuicSocketAddress& new_address); + + // Copies |buffer_length| bytes from iov starting at offset |iov_offset| into + // buffer. |iov| must be at least iov_offset+length total length and buffer + // must be at least |length| long. + static void CopyToBuffer(const struct iovec* iov, + int iov_count, + size_t iov_offset, + size_t buffer_length, + char* buffer); + + // Creates an iovec pointing to the same data as |data|. + static struct iovec MakeIovec(QuicStringPiece data); + + // Returns true if a packet is ackable. A packet is unackable if it can never + // be acked. Occurs when a packet is never sent, after it is acknowledged + // once, or if it's a crypto packet we never expect to receive an ack for. + static bool IsAckable(SentPacketState state); + + // Returns true if frame with |type| is retransmittable. A retransmittable + // frame should be retransmitted if it is detected as lost. + static bool IsRetransmittableFrame(QuicFrameType type); + + // Returns true if |frame| is a handshake frame in version |version|. + static bool IsHandshakeFrame(const QuicFrame& frame, + QuicTransportVersion transport_version); + + // Returns packet state corresponding to |retransmission_type|. + static SentPacketState RetransmissionTypeToPacketState( + TransmissionType retransmission_type); + + // Returns true if header with |first_byte| is considered as an IETF QUIC + // packet header. + static bool IsIetfPacketHeader(uint8_t first_byte); + + // Returns true if header with |first_byte| is considered as an IETF QUIC + // short packet header. + static bool IsIetfPacketShortHeader(uint8_t first_byte); + + // Returns ID to denote an invalid stream of |version|. + static QuicStreamId GetInvalidStreamId(QuicTransportVersion version); + + // Returns crypto stream ID of |version|. + static QuicStreamId GetCryptoStreamId(QuicTransportVersion version); + + // Returns headers stream ID of |version|. + static QuicStreamId GetHeadersStreamId(QuicTransportVersion version); + + // Returns true if |id| is considered as client initiated stream ID. + static bool IsClientInitiatedStreamId(QuicTransportVersion version, + QuicStreamId id); + + // Returns true if |id| is considered as server initiated stream ID. + static bool IsServerInitiatedStreamId(QuicTransportVersion version, + QuicStreamId id); + + // Returns true if |id| is considered as bidirectional stream ID. Only used in + // v99. + static bool IsBidirectionalStreamId(QuicStreamId id); + + // Returns stream type. Either |perspective| or |peer_initiated| would be + // enough together with |id|. This method enforces that the three parameters + // are consistent. Only used in v99. + static StreamType GetStreamType(QuicStreamId id, + Perspective perspective, + bool peer_initiated); + + // Returns the delta between consecutive stream IDs of the same type. + static QuicStreamId StreamIdDelta(QuicTransportVersion version); + + // Returns the first initiated bidirectional stream ID of |perspective|. + static QuicStreamId GetFirstBidirectionalStreamId( + QuicTransportVersion version, + Perspective perspective); + + // Returns the first initiated unidirectional stream ID of |perspective|. + static QuicStreamId GetFirstUnidirectionalStreamId( + QuicTransportVersion version, + Perspective perspective); + + // Generates a random 64bit connection ID. + static QuicConnectionId CreateRandomConnectionId(); + + // Generates a random 64bit connection ID using the provided QuicRandom. + static QuicConnectionId CreateRandomConnectionId(QuicRandom* random); + + // Returns true if the QUIC version allows variable length connection IDs. + static bool VariableLengthConnectionIdAllowedForVersion( + QuicTransportVersion version); + + // Returns true if the connection ID is valid for this QUIC version. + static bool IsConnectionIdValidForVersion(QuicConnectionId connection_id, + QuicTransportVersion version); + + // Returns a connection ID suitable for QUIC use-cases that do not need the + // connection ID for multiplexing. If the version allows variable lengths, + // a connection of length zero is returned, otherwise 64bits set to zero. + static QuicConnectionId CreateZeroConnectionId(QuicTransportVersion version); + + // Generates a 128bit stateless reset token based on a connection ID. + static QuicUint128 GenerateStatelessResetToken( + QuicConnectionId connection_id); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_UTILS_H_
diff --git a/quic/core/quic_utils_test.cc b/quic/core/quic_utils_test.cc new file mode 100644 index 0000000..de4c76b --- /dev/null +++ b/quic/core/quic_utils_test.cc
@@ -0,0 +1,208 @@ +// Copyright (c) 2013 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/quic_utils.h" + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +class QuicUtilsTest : public QuicTest {}; + +TEST_F(QuicUtilsTest, DetermineAddressChangeType) { + const QuicString kIPv4String1 = "1.2.3.4"; + const QuicString kIPv4String2 = "1.2.3.5"; + const QuicString kIPv4String3 = "1.1.3.5"; + const QuicString kIPv6String1 = "2001:700:300:1800::f"; + const QuicString kIPv6String2 = "2001:700:300:1800:1:1:1:f"; + QuicSocketAddress old_address; + QuicSocketAddress new_address; + QuicIpAddress address; + + EXPECT_EQ(NO_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + ASSERT_TRUE(address.FromString(kIPv4String1)); + old_address = QuicSocketAddress(address, 1234); + EXPECT_EQ(NO_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + new_address = QuicSocketAddress(address, 1234); + EXPECT_EQ(NO_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + + new_address = QuicSocketAddress(address, 5678); + EXPECT_EQ(PORT_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + ASSERT_TRUE(address.FromString(kIPv6String1)); + old_address = QuicSocketAddress(address, 1234); + new_address = QuicSocketAddress(address, 5678); + EXPECT_EQ(PORT_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + + ASSERT_TRUE(address.FromString(kIPv4String1)); + old_address = QuicSocketAddress(address, 1234); + ASSERT_TRUE(address.FromString(kIPv6String1)); + new_address = QuicSocketAddress(address, 1234); + EXPECT_EQ(IPV4_TO_IPV6_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + + old_address = QuicSocketAddress(address, 1234); + ASSERT_TRUE(address.FromString(kIPv4String1)); + new_address = QuicSocketAddress(address, 1234); + EXPECT_EQ(IPV6_TO_IPV4_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + + ASSERT_TRUE(address.FromString(kIPv6String2)); + new_address = QuicSocketAddress(address, 1234); + EXPECT_EQ(IPV6_TO_IPV6_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + + ASSERT_TRUE(address.FromString(kIPv4String1)); + old_address = QuicSocketAddress(address, 1234); + ASSERT_TRUE(address.FromString(kIPv4String2)); + new_address = QuicSocketAddress(address, 1234); + EXPECT_EQ(IPV4_SUBNET_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); + ASSERT_TRUE(address.FromString(kIPv4String3)); + new_address = QuicSocketAddress(address, 1234); + EXPECT_EQ(IPV4_TO_IPV4_CHANGE, + QuicUtils::DetermineAddressChangeType(old_address, new_address)); +} + +QuicUint128 IncrementalHashReference(const void* data, size_t len) { + // The two constants are defined as part of the hash algorithm. + // see http://www.isthe.com/chongo/tech/comp/fnv/ + // hash = 144066263297769815596495629667062367629 + QuicUint128 hash = MakeQuicUint128(UINT64_C(7809847782465536322), + UINT64_C(7113472399480571277)); + // kPrime = 309485009821345068724781371 + const QuicUint128 kPrime = MakeQuicUint128(16777216, 315); + const uint8_t* octets = reinterpret_cast<const uint8_t*>(data); + for (size_t i = 0; i < len; ++i) { + hash = hash ^ MakeQuicUint128(0, octets[i]); + hash = hash * kPrime; + } + return hash; +} + +TEST_F(QuicUtilsTest, ReferenceTest) { + std::vector<uint8_t> data(32); + for (size_t i = 0; i < data.size(); ++i) { + data[i] = i % 255; + } + EXPECT_EQ(IncrementalHashReference(data.data(), data.size()), + QuicUtils::FNV1a_128_Hash(QuicStringPiece( + reinterpret_cast<const char*>(data.data()), data.size()))); +} + +TEST_F(QuicUtilsTest, IsUnackable) { + for (size_t i = FIRST_PACKET_STATE; i <= LAST_PACKET_STATE; ++i) { + if (i == NEVER_SENT || i == ACKED || i == UNACKABLE) { + EXPECT_FALSE(QuicUtils::IsAckable(static_cast<SentPacketState>(i))); + } else { + EXPECT_TRUE(QuicUtils::IsAckable(static_cast<SentPacketState>(i))); + } + } +} + +TEST_F(QuicUtilsTest, RetransmissionTypeToPacketState) { + for (size_t i = FIRST_TRANSMISSION_TYPE; i <= LAST_TRANSMISSION_TYPE; ++i) { + if (i == NOT_RETRANSMISSION) { + continue; + } + SentPacketState state = QuicUtils::RetransmissionTypeToPacketState( + static_cast<TransmissionType>(i)); + if (i == HANDSHAKE_RETRANSMISSION) { + EXPECT_EQ(HANDSHAKE_RETRANSMITTED, state); + } else if (i == LOSS_RETRANSMISSION) { + EXPECT_EQ(LOST, state); + } else if (i == ALL_UNACKED_RETRANSMISSION || + i == ALL_INITIAL_RETRANSMISSION) { + EXPECT_EQ(UNACKABLE, state); + } else if (i == TLP_RETRANSMISSION) { + EXPECT_EQ(TLP_RETRANSMITTED, state); + } else if (i == RTO_RETRANSMISSION) { + EXPECT_EQ(RTO_RETRANSMITTED, state); + } else if (i == PROBING_RETRANSMISSION) { + EXPECT_EQ(PROBE_RETRANSMITTED, state); + } else { + DCHECK(false) + << "No corresponding packet state according to transmission type: " + << i; + } + } +} + +TEST_F(QuicUtilsTest, IsIetfPacketHeader) { + // IETF QUIC short header + uint8_t first_byte = 0; + EXPECT_TRUE(QuicUtils::IsIetfPacketHeader(first_byte)); + EXPECT_TRUE(QuicUtils::IsIetfPacketShortHeader(first_byte)); + + // IETF QUIC long header + first_byte |= (FLAGS_LONG_HEADER | FLAGS_DEMULTIPLEXING_BIT); + EXPECT_TRUE(QuicUtils::IsIetfPacketHeader(first_byte)); + EXPECT_FALSE(QuicUtils::IsIetfPacketShortHeader(first_byte)); + + // IETF QUIC long header, version negotiation. + first_byte = 0; + first_byte |= FLAGS_LONG_HEADER; + EXPECT_TRUE(QuicUtils::IsIetfPacketHeader(first_byte)); + EXPECT_FALSE(QuicUtils::IsIetfPacketShortHeader(first_byte)); + + // GQUIC + first_byte = 0; + first_byte |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID; + EXPECT_FALSE(QuicUtils::IsIetfPacketHeader(first_byte)); + EXPECT_FALSE(QuicUtils::IsIetfPacketShortHeader(first_byte)); +} + +TEST_F(QuicUtilsTest, RandomConnectionId) { + MockRandom random(33); + QuicConnectionId connection_id = QuicUtils::CreateRandomConnectionId(&random); + EXPECT_EQ(connection_id.length(), sizeof(uint64_t)); + char connection_id_bytes[sizeof(uint64_t)]; + random.RandBytes(connection_id_bytes, QUIC_ARRAYSIZE(connection_id_bytes)); + EXPECT_EQ(connection_id, + QuicConnectionId(static_cast<char*>(connection_id_bytes), + QUIC_ARRAYSIZE(connection_id_bytes))); + EXPECT_NE(connection_id, EmptyQuicConnectionId()); + EXPECT_NE(connection_id, TestConnectionId()); + EXPECT_NE(connection_id, TestConnectionId(1)); +} + +TEST_F(QuicUtilsTest, VariableLengthConnectionId) { + EXPECT_FALSE( + QuicUtils::VariableLengthConnectionIdAllowedForVersion(QUIC_VERSION_39)); + EXPECT_TRUE(QuicUtils::IsConnectionIdValidForVersion( + QuicUtils::CreateZeroConnectionId(QUIC_VERSION_39), QUIC_VERSION_39)); + EXPECT_TRUE(QuicUtils::IsConnectionIdValidForVersion( + QuicUtils::CreateZeroConnectionId(QUIC_VERSION_99), QUIC_VERSION_99)); + EXPECT_NE(QuicUtils::CreateZeroConnectionId(QUIC_VERSION_39), + EmptyQuicConnectionId()); + EXPECT_FALSE(QuicUtils::IsConnectionIdValidForVersion(EmptyQuicConnectionId(), + QUIC_VERSION_39)); +} + +TEST_F(QuicUtilsTest, StatelessResetToken) { + QuicConnectionId connection_id1a = test::TestConnectionId(1); + QuicConnectionId connection_id1b = test::TestConnectionId(1); + QuicConnectionId connection_id2 = test::TestConnectionId(2); + QuicUint128 token1a = QuicUtils::GenerateStatelessResetToken(connection_id1a); + QuicUint128 token1b = QuicUtils::GenerateStatelessResetToken(connection_id1b); + QuicUint128 token2 = QuicUtils::GenerateStatelessResetToken(connection_id2); + EXPECT_EQ(token1a, token1b); + EXPECT_NE(token1a, token2); + EXPECT_EQ(token1a, MakeQuicUint128(0, 1)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_version_manager.cc b/quic/core/quic_version_manager.cc new file mode 100644 index 0000000..62b2e44 --- /dev/null +++ b/quic/core/quic_version_manager.cc
@@ -0,0 +1,71 @@ +// 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/quic_version_manager.h" + +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +#include <algorithm> + +namespace quic { + +QuicVersionManager::QuicVersionManager( + ParsedQuicVersionVector supported_versions) + : enable_version_99_(GetQuicReloadableFlag(quic_enable_version_99)), + enable_version_47_(GetQuicReloadableFlag(quic_enable_version_47)), + enable_version_46_(GetQuicReloadableFlag(quic_enable_version_46)), + enable_version_44_(GetQuicReloadableFlag(quic_enable_version_44)), + enable_version_43_(GetQuicReloadableFlag(quic_enable_version_43)), + disable_version_39_(GetQuicReloadableFlag(quic_disable_version_39)), + allowed_supported_versions_(std::move(supported_versions)) { + RefilterSupportedVersions(); +} + +QuicVersionManager::~QuicVersionManager() {} + +const QuicTransportVersionVector& +QuicVersionManager::GetSupportedTransportVersions() { + MaybeRefilterSupportedVersions(); + return filtered_transport_versions_; +} + +const ParsedQuicVersionVector& QuicVersionManager::GetSupportedVersions() { + MaybeRefilterSupportedVersions(); + return filtered_supported_versions_; +} + +void QuicVersionManager::MaybeRefilterSupportedVersions() { + if (enable_version_99_ != GetQuicReloadableFlag(quic_enable_version_99) || + enable_version_47_ != GetQuicReloadableFlag(quic_enable_version_47) || + enable_version_46_ != GetQuicReloadableFlag(quic_enable_version_46) || + enable_version_44_ != GetQuicReloadableFlag(quic_enable_version_44) || + enable_version_43_ != GetQuicReloadableFlag(quic_enable_version_43) || + disable_version_39_ != GetQuicReloadableFlag(quic_disable_version_39)) { + enable_version_99_ = GetQuicReloadableFlag(quic_enable_version_99); + enable_version_47_ = GetQuicReloadableFlag(quic_enable_version_47); + enable_version_46_ = GetQuicReloadableFlag(quic_enable_version_46); + enable_version_44_ = GetQuicReloadableFlag(quic_enable_version_44); + enable_version_43_ = GetQuicReloadableFlag(quic_enable_version_43); + disable_version_39_ = GetQuicReloadableFlag(quic_disable_version_39); + RefilterSupportedVersions(); + } +} + +void QuicVersionManager::RefilterSupportedVersions() { + filtered_supported_versions_ = + FilterSupportedVersions(allowed_supported_versions_); + filtered_transport_versions_.clear(); + for (ParsedQuicVersion version : filtered_supported_versions_) { + auto transport_version = version.transport_version; + if (std::find(filtered_transport_versions_.begin(), + filtered_transport_versions_.end(), + transport_version) == filtered_transport_versions_.end()) { + filtered_transport_versions_.push_back(transport_version); + } + } +} + +} // namespace quic
diff --git a/quic/core/quic_version_manager.h b/quic/core/quic_version_manager.h new file mode 100644 index 0000000..4fedae3 --- /dev/null +++ b/quic/core/quic_version_manager.h
@@ -0,0 +1,66 @@ +// 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. + +#ifndef QUICHE_QUIC_CORE_QUIC_VERSION_MANAGER_H_ +#define QUICHE_QUIC_CORE_QUIC_VERSION_MANAGER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// Used to generate filtered supported versions based on flags. +class QUIC_EXPORT_PRIVATE QuicVersionManager { + public: + // |supported_versions| should be sorted in the order of preference (typically + // highest supported version to the lowest supported version). + explicit QuicVersionManager(ParsedQuicVersionVector supported_versions); + virtual ~QuicVersionManager(); + + // Returns currently supported QUIC versions. + // TODO(nharper): Remove this method once it is unused. + const QuicTransportVersionVector& GetSupportedTransportVersions(); + + // Returns currently supported QUIC versions. This vector has the same order + // as the versions passed to the constructor. + const ParsedQuicVersionVector& GetSupportedVersions(); + + protected: + // Maybe refilter filtered_supported_versions_ based on flags. + void MaybeRefilterSupportedVersions(); + + // Refilters filtered_supported_versions_. + virtual void RefilterSupportedVersions(); + + const QuicTransportVersionVector& filtered_supported_versions() const { + return filtered_transport_versions_; + } + + private: + // quic_enable_version_99 flag + bool enable_version_99_; + // quic_enable_version_47 flag + bool enable_version_47_; + // quic_enable_version_46 flag + bool enable_version_46_; + // quic_enable_version_44 flag + bool enable_version_44_; + // quic_enable_version_43 flag + bool enable_version_43_; + // quic_disable_version_39 flag + bool disable_version_39_; + // The list of versions that may be supported. + ParsedQuicVersionVector allowed_supported_versions_; + // This vector contains QUIC versions which are currently supported based on + // flags. + ParsedQuicVersionVector filtered_supported_versions_; + // This vector contains the transport versions from + // |filtered_supported_versions_|. No guarantees are made that the same + // transport version isn't repeated. + QuicTransportVersionVector filtered_transport_versions_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_VERSION_MANAGER_H_
diff --git a/quic/core/quic_version_manager_test.cc b/quic/core/quic_version_manager_test.cc new file mode 100644 index 0000000..7c7a08d --- /dev/null +++ b/quic/core/quic_version_manager_test.cc
@@ -0,0 +1,71 @@ +// 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/quic_version_manager.h" + +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QuicVersionManagerTest : public QuicTest {}; + +TEST_F(QuicVersionManagerTest, QuicVersionManager) { + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, + "Supported versions out of sync"); + SetQuicReloadableFlag(quic_enable_version_99, false); + SetQuicReloadableFlag(quic_enable_version_47, false); + SetQuicReloadableFlag(quic_enable_version_46, false); + SetQuicReloadableFlag(quic_enable_version_44, false); + SetQuicReloadableFlag(quic_enable_version_43, false); + SetQuicReloadableFlag(quic_disable_version_39, true); + QuicVersionManager manager(AllSupportedVersions()); + + EXPECT_EQ(FilterSupportedTransportVersions(AllSupportedTransportVersions()), + manager.GetSupportedTransportVersions()); + + EXPECT_TRUE(manager.GetSupportedTransportVersions().empty()); + + SetQuicReloadableFlag(quic_disable_version_39, false); + EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_39}), + manager.GetSupportedTransportVersions()); + + SetQuicReloadableFlag(quic_enable_version_43, true); + EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_43, QUIC_VERSION_39}), + manager.GetSupportedTransportVersions()); + + SetQuicReloadableFlag(quic_enable_version_44, true); + EXPECT_EQ(QuicTransportVersionVector( + {QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}), + manager.GetSupportedTransportVersions()); + + SetQuicReloadableFlag(quic_enable_version_46, true); + EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_46, QUIC_VERSION_44, + QUIC_VERSION_43, QUIC_VERSION_39}), + manager.GetSupportedTransportVersions()); + + SetQuicReloadableFlag(quic_enable_version_47, true); + EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_47, QUIC_VERSION_46, + QUIC_VERSION_44, QUIC_VERSION_43, + QUIC_VERSION_39}), + manager.GetSupportedTransportVersions()); + + SetQuicReloadableFlag(quic_enable_version_99, true); + EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_99, QUIC_VERSION_47, + QUIC_VERSION_46, QUIC_VERSION_44, + QUIC_VERSION_43, QUIC_VERSION_39}), + manager.GetSupportedTransportVersions()); + + // Ensure that all versions are now supported. + EXPECT_EQ(FilterSupportedTransportVersions(AllSupportedTransportVersions()), + manager.GetSupportedTransportVersions()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc new file mode 100644 index 0000000..efc823d --- /dev/null +++ b/quic/core/quic_versions.cc
@@ -0,0 +1,335 @@ +// Copyright (c) 2012 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/quic_versions.h" + +#include "net/third_party/quiche/src/quic/core/quic_tag.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { +namespace { + +// Constructs a version label from the 4 bytes such that the on-the-wire +// order will be: d, c, b, a. +QuicVersionLabel MakeVersionLabel(char a, char b, char c, char d) { + return MakeQuicTag(d, c, b, a); +} + +} // namespace + +ParsedQuicVersion::ParsedQuicVersion(HandshakeProtocol handshake_protocol, + QuicTransportVersion transport_version) + : handshake_protocol(handshake_protocol), + transport_version(transport_version) { + if (handshake_protocol == PROTOCOL_TLS1_3 && + !FLAGS_quic_supports_tls_handshake) { + QUIC_BUG << "TLS use attempted when not enabled"; + } +} + +std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version) { + os << ParsedQuicVersionToString(version); + return os; +} + +QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { + char proto = 0; + switch (parsed_version.handshake_protocol) { + case PROTOCOL_QUIC_CRYPTO: + proto = 'Q'; + break; + case PROTOCOL_TLS1_3: + proto = 'T'; + break; + default: + QUIC_LOG(ERROR) << "Invalid HandshakeProtocol: " + << parsed_version.handshake_protocol; + return 0; + } + switch (parsed_version.transport_version) { + case QUIC_VERSION_39: + return MakeVersionLabel(proto, '0', '3', '9'); + case QUIC_VERSION_43: + return MakeVersionLabel(proto, '0', '4', '3'); + case QUIC_VERSION_44: + return MakeVersionLabel(proto, '0', '4', '4'); + case QUIC_VERSION_46: + return MakeVersionLabel(proto, '0', '4', '6'); + case QUIC_VERSION_47: + return MakeVersionLabel(proto, '0', '4', '7'); + case QUIC_VERSION_99: + return MakeVersionLabel(proto, '0', '9', '9'); + default: + // This shold be an ERROR because we should never attempt to convert an + // invalid QuicTransportVersion to be written to the wire. + QUIC_LOG(ERROR) << "Unsupported QuicTransportVersion: " + << parsed_version.transport_version; + return 0; + } +} + +QuicVersionLabelVector CreateQuicVersionLabelVector( + const ParsedQuicVersionVector& versions) { + QuicVersionLabelVector out; + out.reserve(versions.size()); + for (const auto& version : versions) { + out.push_back(CreateQuicVersionLabel(version)); + } + return out; +} + +ParsedQuicVersion ParseQuicVersionLabel(QuicVersionLabel version_label) { + std::vector<HandshakeProtocol> protocols = {PROTOCOL_QUIC_CRYPTO}; + if (FLAGS_quic_supports_tls_handshake) { + protocols.push_back(PROTOCOL_TLS1_3); + } + for (QuicTransportVersion version : kSupportedTransportVersions) { + for (HandshakeProtocol handshake : protocols) { + if (version_label == + CreateQuicVersionLabel(ParsedQuicVersion(handshake, version))) { + return ParsedQuicVersion(handshake, version); + } + } + } + // Reading from the client so this should not be considered an ERROR. + QUIC_DLOG(INFO) << "Unsupported QuicVersionLabel version: " + << QuicVersionLabelToString(version_label); + return UnsupportedQuicVersion(); +} + +QuicTransportVersionVector AllSupportedTransportVersions() { + QuicTransportVersionVector supported_versions; + for (QuicTransportVersion version : kSupportedTransportVersions) { + supported_versions.push_back(version); + } + return supported_versions; +} + +ParsedQuicVersionVector AllSupportedVersions() { + ParsedQuicVersionVector supported_versions; + for (HandshakeProtocol protocol : kSupportedHandshakeProtocols) { + if (protocol == PROTOCOL_TLS1_3 && !FLAGS_quic_supports_tls_handshake) { + continue; + } + for (QuicTransportVersion version : kSupportedTransportVersions) { + if (protocol == PROTOCOL_TLS1_3 && version < QUIC_VERSION_47) { + // The TLS handshake is only deployable if CRYPTO frames are also used, + // which are added in v47. + continue; + } + supported_versions.push_back(ParsedQuicVersion(protocol, version)); + } + } + return supported_versions; +} + +// TODO(nharper): Remove this function when it is no longer in use. +QuicTransportVersionVector CurrentSupportedTransportVersions() { + return FilterSupportedTransportVersions(AllSupportedTransportVersions()); +} + +ParsedQuicVersionVector CurrentSupportedVersions() { + return FilterSupportedVersions(AllSupportedVersions()); +} + +// TODO(nharper): Remove this function when it is no longer in use. +QuicTransportVersionVector FilterSupportedTransportVersions( + QuicTransportVersionVector versions) { + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + ParsedQuicVersionVector filtered_parsed_versions = + FilterSupportedVersions(parsed_versions); + QuicTransportVersionVector filtered_versions; + for (ParsedQuicVersion version : filtered_parsed_versions) { + filtered_versions.push_back(version.transport_version); + } + return filtered_versions; +} + +ParsedQuicVersionVector FilterSupportedVersions( + ParsedQuicVersionVector versions) { + ParsedQuicVersionVector filtered_versions; + filtered_versions.reserve(versions.size()); + for (ParsedQuicVersion version : versions) { + if (version.transport_version == QUIC_VERSION_99) { + if (GetQuicReloadableFlag(quic_enable_version_99) && + GetQuicReloadableFlag(quic_enable_version_47) && + GetQuicReloadableFlag(quic_enable_version_46) && + GetQuicReloadableFlag(quic_enable_version_44) && + GetQuicReloadableFlag(quic_enable_version_43)) { + filtered_versions.push_back(version); + } + } else if (version.transport_version == QUIC_VERSION_47) { + if (GetQuicReloadableFlag(quic_enable_version_47) && + GetQuicReloadableFlag(quic_enable_version_46) && + GetQuicReloadableFlag(quic_enable_version_44) && + GetQuicReloadableFlag(quic_enable_version_43)) { + filtered_versions.push_back(version); + } + } else if (version.transport_version == QUIC_VERSION_46) { + if (GetQuicReloadableFlag(quic_enable_version_46) && + GetQuicReloadableFlag(quic_enable_version_44) && + GetQuicReloadableFlag(quic_enable_version_43)) { + filtered_versions.push_back(version); + } + } else if (version.transport_version == QUIC_VERSION_44) { + if (GetQuicReloadableFlag(quic_enable_version_44) && + GetQuicReloadableFlag(quic_enable_version_43)) { + filtered_versions.push_back(version); + } + } else if (version.transport_version == QUIC_VERSION_43) { + if (GetQuicReloadableFlag(quic_enable_version_43)) { + filtered_versions.push_back(version); + } + } else if (version.transport_version == QUIC_VERSION_39) { + if (!GetQuicReloadableFlag(quic_disable_version_39)) { + filtered_versions.push_back(version); + } + } else { + filtered_versions.push_back(version); + } + } + return filtered_versions; +} + +QuicTransportVersionVector VersionOfIndex( + const QuicTransportVersionVector& versions, + int index) { + QuicTransportVersionVector version; + int version_count = versions.size(); + if (index >= 0 && index < version_count) { + version.push_back(versions[index]); + } else { + version.push_back(QUIC_VERSION_UNSUPPORTED); + } + return version; +} + +ParsedQuicVersionVector ParsedVersionOfIndex( + const ParsedQuicVersionVector& versions, + int index) { + ParsedQuicVersionVector version; + int version_count = versions.size(); + if (index >= 0 && index < version_count) { + version.push_back(versions[index]); + } else { + version.push_back(UnsupportedQuicVersion()); + } + return version; +} + +QuicTransportVersionVector ParsedVersionsToTransportVersions( + const ParsedQuicVersionVector& versions) { + QuicTransportVersionVector transport_versions; + transport_versions.resize(versions.size()); + for (size_t i = 0; i < versions.size(); ++i) { + transport_versions[i] = versions[i].transport_version; + } + return transport_versions; +} + +QuicVersionLabel QuicVersionToQuicVersionLabel( + QuicTransportVersion transport_version) { + return CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, transport_version)); +} + +QuicString QuicVersionLabelToString(QuicVersionLabel version_label) { + return QuicTagToString(QuicEndian::HostToNet32(version_label)); +} + +QuicString QuicVersionLabelVectorToString( + const QuicVersionLabelVector& version_labels, + const QuicString& separator, + size_t skip_after_nth_version) { + QuicString result; + for (size_t i = 0; i < version_labels.size(); ++i) { + if (i != 0) { + result.append(separator); + } + + if (i > skip_after_nth_version) { + result.append("..."); + break; + } + result.append(QuicVersionLabelToString(version_labels[i])); + } + return result; +} + +QuicTransportVersion QuicVersionLabelToQuicVersion( + QuicVersionLabel version_label) { + return ParseQuicVersionLabel(version_label).transport_version; +} + +HandshakeProtocol QuicVersionLabelToHandshakeProtocol( + QuicVersionLabel version_label) { + return ParseQuicVersionLabel(version_label).handshake_protocol; +} + +#define RETURN_STRING_LITERAL(x) \ + case x: \ + return #x + +QuicString QuicVersionToString(QuicTransportVersion transport_version) { + switch (transport_version) { + RETURN_STRING_LITERAL(QUIC_VERSION_39); + RETURN_STRING_LITERAL(QUIC_VERSION_43); + RETURN_STRING_LITERAL(QUIC_VERSION_44); + RETURN_STRING_LITERAL(QUIC_VERSION_46); + RETURN_STRING_LITERAL(QUIC_VERSION_47); + RETURN_STRING_LITERAL(QUIC_VERSION_99); + default: + return "QUIC_VERSION_UNSUPPORTED"; + } +} + +QuicString ParsedQuicVersionToString(ParsedQuicVersion version) { + return QuicVersionLabelToString(CreateQuicVersionLabel(version)); +} + +QuicString QuicTransportVersionVectorToString( + const QuicTransportVersionVector& versions) { + QuicString result = ""; + for (size_t i = 0; i < versions.size(); ++i) { + if (i != 0) { + result.append(","); + } + result.append(QuicVersionToString(versions[i])); + } + return result; +} + +QuicString ParsedQuicVersionVectorToString( + const ParsedQuicVersionVector& versions, + const QuicString& separator, + size_t skip_after_nth_version) { + QuicString result; + for (size_t i = 0; i < versions.size(); ++i) { + if (i != 0) { + result.append(separator); + } + if (i > skip_after_nth_version) { + result.append("..."); + break; + } + result.append(ParsedQuicVersionToString(versions[i])); + } + return result; +} + +ParsedQuicVersion UnsupportedQuicVersion() { + return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED); +} + +#undef RETURN_STRING_LITERAL // undef for jumbo builds +} // namespace quic
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h new file mode 100644 index 0000000..c0fa34a --- /dev/null +++ b/quic/core/quic_versions.h
@@ -0,0 +1,346 @@ +// Copyright (c) 2012 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. + +// Definitions and utility functions related to handling of QUIC versions. +// +// QUIC version is a four-byte tag that can be represented in memory as a +// QuicVersionLabel type (which is an alias to uint32_t). In actuality, all +// versions supported by this implementation have the following format: +// [QT]0\d\d +// e.g. Q046. Q or T distinguishes the type of handshake used (Q for QUIC +// Crypto handshake, T for TLS-based handshake), and the two digits at the end +// is the actual numeric value of transport version used by the code. + +#ifndef QUICHE_QUIC_CORE_QUIC_VERSIONS_H_ +#define QUICHE_QUIC_CORE_QUIC_VERSIONS_H_ + +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_tag.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// The available versions of QUIC. The numeric value of the enum is guaranteed +// to match the number in the name. The versions not currently supported are +// documented in comments. +// +// See go/new-quic-version for more details on how to roll out new versions. +enum QuicTransportVersion { + // Special case to indicate unknown/unsupported QUIC version. + QUIC_VERSION_UNSUPPORTED = 0, + + // Version 1 was the first version of QUIC that supported versioning. + // Version 2 decoupled versioning of non-cryptographic parameters from the + // SCFG. + // Version 3 moved public flags into the beginning of the packet. + // Version 4 added support for variable-length connection IDs. + // Version 5 made specifying FEC groups optional. + // Version 6 introduced variable-length packet numbers. + // Version 7 introduced a lower-overhead encoding for stream frames. + // Version 8 made salt length equal to digest length for the RSA-PSS + // signatures. + // Version 9 added stream priority. + // Version 10 redid the frame type numbering. + // Version 11 reduced the length of null encryption authentication tag + // from 16 to 12 bytes. + // Version 12 made the sequence numbers in the ACK frames variable-sized. + // Version 13 added the dedicated header stream. + // Version 14 added byte_offset to RST_STREAM frame. + // Version 15 added a list of packets recovered using FEC to the ACK frame. + // Version 16 added STOP_WAITING frame. + // Version 17 added per-stream flow control. + // Version 18 added PING frame. + // Version 19 added connection-level flow control + // Version 20 allowed to set stream- and connection-level flow control windows + // to different values. + // Version 21 made header and crypto streams flow-controlled. + // Version 22 added support for SCUP (server config update) messages. + // Version 23 added timestamps into the ACK frame. + // Version 24 added SPDY/4 header compression. + // Version 25 added support for SPDY/4 header keys and removed error_details + // from RST_STREAM frame. + // Version 26 added XLCT (expected leaf certificate) tag into CHLO. + // Version 27 added a nonce into SHLO. + // Version 28 allowed receiver to refuse creating a requested stream. + // Version 29 added support for QUIC_STREAM_NO_ERROR. + // Version 30 added server-side support for certificate transparency. + // Version 31 incorporated the hash of CHLO into the crypto proof supplied by + // the server. + // Version 32 removed FEC-related fields from wire format. + // Version 33 added diversification nonces. + // Version 34 removed entropy bits from packets and ACK frames, removed + // private flag from packet header and changed the ACK format to + // specify ranges of packets acknowledged rather than missing + // ranges. + // Version 35 allows endpoints to independently set stream limit. + // Version 36 added support for forced head-of-line blocking experiments. + // Version 37 added perspective into null encryption. + // Version 38 switched to IETF padding frame format and support for NSTP (no + // stop waiting frame) connection option. + + QUIC_VERSION_39 = 39, // Integers and floating numbers are written in big + // endian. Dot not ack acks. Send a connection level + // WINDOW_UPDATE every 20 sent packets which do not + // contain retransmittable frames. + + // Version 40 was an attempt to convert QUIC to IETF frame format; it was + // never shipped due to a bug. + // Version 41 was a bugfix for version 40. The working group changed the wire + // format before it shipped, which caused it to be never shipped + // and all the changes from it to be reverted. No changes from v40 + // or v41 are present in subsequent versions. + // Version 42 allowed receiving overlapping stream data. + + QUIC_VERSION_43 = 43, // PRIORITY frames are sent by client and accepted by + // server. + QUIC_VERSION_44 = 44, // Use IETF header format. + + // Version 45 added MESSAGE frame. + + QUIC_VERSION_46 = 46, // Use IETF draft-17 header format with demultiplexing + // bit. + QUIC_VERSION_47 = 47, // Use CRYPTO frames for QuicCryptoStreams. + QUIC_VERSION_99 = 99, // Dumping ground for IETF QUIC changes which are not + // yet ready for production. +}; + +// The crypto handshake protocols that can be used with QUIC. +enum HandshakeProtocol { + PROTOCOL_UNSUPPORTED, + PROTOCOL_QUIC_CRYPTO, + PROTOCOL_TLS1_3, +}; + +// A parsed QUIC version label which determines that handshake protocol +// and the transport version. +struct QUIC_EXPORT_PRIVATE ParsedQuicVersion { + HandshakeProtocol handshake_protocol; + QuicTransportVersion transport_version; + + ParsedQuicVersion(HandshakeProtocol handshake_protocol, + QuicTransportVersion transport_version); + + ParsedQuicVersion(const ParsedQuicVersion& other) + : handshake_protocol(other.handshake_protocol), + transport_version(other.transport_version) {} + + ParsedQuicVersion& operator=(const ParsedQuicVersion& other) { + if (this != &other) { + handshake_protocol = other.handshake_protocol; + transport_version = other.transport_version; + } + return *this; + } + + bool operator==(const ParsedQuicVersion& other) const { + return handshake_protocol == other.handshake_protocol && + transport_version == other.transport_version; + } + + bool operator!=(const ParsedQuicVersion& other) const { + return handshake_protocol != other.handshake_protocol || + transport_version != other.transport_version; + } +}; + +QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion(); + +QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const ParsedQuicVersion& version); + +using ParsedQuicVersionVector = std::vector<ParsedQuicVersion>; + +// Representation of the on-the-wire QUIC version number. Will be written/read +// to the wire in network-byte-order. +using QuicVersionLabel = uint32_t; +using QuicVersionLabelVector = std::vector<QuicVersionLabel>; + +// This vector contains QUIC versions which we currently support. +// This should be ordered such that the highest supported version is the first +// element, with subsequent elements in descending order (versions can be +// skipped as necessary). +// +// See go/new-quic-version for more details on how to roll out new versions. +static const QuicTransportVersion kSupportedTransportVersions[] = { + QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46, + QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39, +}; + +// This vector contains all crypto handshake protocols that are supported. +static const HandshakeProtocol kSupportedHandshakeProtocols[] = { + PROTOCOL_QUIC_CRYPTO, PROTOCOL_TLS1_3}; + +typedef std::vector<QuicTransportVersion> QuicTransportVersionVector; + +// Returns a vector of QUIC versions in kSupportedTransportVersions. +QUIC_EXPORT_PRIVATE QuicTransportVersionVector AllSupportedTransportVersions(); + +// Returns a vector of QUIC versions that is the cartesian product of +// kSupportedTransportVersions and kSupportedHandshakeProtocols. +QUIC_EXPORT_PRIVATE ParsedQuicVersionVector AllSupportedVersions(); + +// Returns a vector of QUIC versions from kSupportedTransportVersions which +// exclude any versions which are disabled by flags. +QUIC_EXPORT_PRIVATE QuicTransportVersionVector +CurrentSupportedTransportVersions(); + +// Returns a vector of QUIC versions that is the cartesian product of +// kSupportedTransportVersions and kSupportedHandshakeProtocols, with any +// versions disabled by flags excluded. +QUIC_EXPORT_PRIVATE ParsedQuicVersionVector CurrentSupportedVersions(); + +// Returns a vector of QUIC versions from |versions| which exclude any versions +// which are disabled by flags. +QUIC_EXPORT_PRIVATE QuicTransportVersionVector +FilterSupportedTransportVersions(QuicTransportVersionVector versions); + +// Returns a vector of QUIC versions from |versions| which exclude any versions +// which are disabled by flags. +QUIC_EXPORT_PRIVATE ParsedQuicVersionVector +FilterSupportedVersions(ParsedQuicVersionVector versions); + +// Returns QUIC version of |index| in result of |versions|. Returns +// QUIC_VERSION_UNSUPPORTED if |index| is out of bounds. +QUIC_EXPORT_PRIVATE QuicTransportVersionVector +VersionOfIndex(const QuicTransportVersionVector& versions, int index); + +// Returns QUIC version of |index| in result of |versions|. Returns +// UnsupportedQuicVersion() if |index| is out of bounds. +QUIC_EXPORT_PRIVATE ParsedQuicVersionVector +ParsedVersionOfIndex(const ParsedQuicVersionVector& versions, int index); + +// Returns a vector of QuicTransportVersions corresponding to just the transport +// versions in |versions|. If the input vector contains multiple parsed versions +// with different handshake protocols (but the same transport version), that +// transport version will appear in the resulting vector multiple times. +QUIC_EXPORT_PRIVATE QuicTransportVersionVector +ParsedVersionsToTransportVersions(const ParsedQuicVersionVector& versions); + +// QuicVersionLabel is written to and read from the wire, but we prefer to use +// the more readable ParsedQuicVersion at other levels. +// Helper function which translates from a QuicVersionLabel to a +// ParsedQuicVersion. +QUIC_EXPORT_PRIVATE ParsedQuicVersion +ParseQuicVersionLabel(QuicVersionLabel version_label); + +// Constructs a QuicVersionLabel from the provided ParsedQuicVersion. +QUIC_EXPORT_PRIVATE QuicVersionLabel +CreateQuicVersionLabel(ParsedQuicVersion parsed_version); + +// Constructs a QuicVersionLabelVector from the provided +// ParsedQuicVersionVector. +QUIC_EXPORT_PRIVATE QuicVersionLabelVector +CreateQuicVersionLabelVector(const ParsedQuicVersionVector& versions); + +// QuicVersionLabel is written to and read from the wire, but we prefer to use +// the more readable QuicTransportVersion at other levels. +// Helper function which translates from a QuicTransportVersion to a +// QuicVersionLabel. Returns 0 if |version| is unsupported. +QUIC_EXPORT_PRIVATE QuicVersionLabel +QuicVersionToQuicVersionLabel(QuicTransportVersion transport_version); + +// Helper function which translates from a QuicVersionLabel to a string. +QUIC_EXPORT_PRIVATE QuicString +QuicVersionLabelToString(QuicVersionLabel version_label); + +// Returns |separator|-separated list of string representations of +// QuicVersionLabel values in the supplied |version_labels| vector. The values +// after the (0-based) |skip_after_nth_version|'th are skipped. +QUIC_EXPORT_PRIVATE QuicString +QuicVersionLabelVectorToString(const QuicVersionLabelVector& version_labels, + const QuicString& separator, + size_t skip_after_nth_version); + +// Returns comma separated list of string representations of QuicVersionLabel +// values in the supplied |version_labels| vector. +QUIC_EXPORT_PRIVATE inline QuicString QuicVersionLabelVectorToString( + const QuicVersionLabelVector& version_labels) { + return QuicVersionLabelVectorToString(version_labels, ",", + std::numeric_limits<size_t>::max()); +} + +// Returns appropriate QuicTransportVersion from a QuicVersionLabel. +// Returns QUIC_VERSION_UNSUPPORTED if |version_label| cannot be understood. +QUIC_EXPORT_PRIVATE QuicTransportVersion +QuicVersionLabelToQuicVersion(QuicVersionLabel version_label); + +// Returns the HandshakeProtocol used with the given |version_label|, returning +// PROTOCOL_UNSUPPORTED if it is unknown. +QUIC_EXPORT_PRIVATE HandshakeProtocol +QuicVersionLabelToHandshakeProtocol(QuicVersionLabel version_label); + +// Helper function which translates from a QuicTransportVersion to a string. +// Returns strings corresponding to enum names (e.g. QUIC_VERSION_6). +QUIC_EXPORT_PRIVATE QuicString +QuicVersionToString(QuicTransportVersion transport_version); + +// Helper function which translates from a ParsedQuicVersion to a string. +// Returns strings corresponding to the on-the-wire tag. +QUIC_EXPORT_PRIVATE QuicString +ParsedQuicVersionToString(ParsedQuicVersion version); + +// Returns comma separated list of string representations of +// QuicTransportVersion enum values in the supplied |versions| vector. +QUIC_EXPORT_PRIVATE QuicString +QuicTransportVersionVectorToString(const QuicTransportVersionVector& versions); + +// Returns comma separated list of string representations of ParsedQuicVersion +// values in the supplied |versions| vector. +QUIC_EXPORT_PRIVATE QuicString +ParsedQuicVersionVectorToString(const ParsedQuicVersionVector& versions); + +// Returns |separator|-separated list of string representations of +// ParsedQuicVersion values in the supplied |versions| vector. The values after +// the (0-based) |skip_after_nth_version|'th are skipped. +QUIC_EXPORT_PRIVATE QuicString +ParsedQuicVersionVectorToString(const ParsedQuicVersionVector& versions, + const QuicString& separator, + size_t skip_after_nth_version); + +// Returns comma separated list of string representations of ParsedQuicVersion +// values in the supplied |versions| vector. +QUIC_EXPORT_PRIVATE inline QuicString ParsedQuicVersionVectorToString( + const ParsedQuicVersionVector& versions) { + return ParsedQuicVersionVectorToString(versions, ",", + std::numeric_limits<size_t>::max()); +} + +// Returns true if QuicSpdyStream encodes body using HTTP/3 specification and +// sends data frame header along with body. +QUIC_EXPORT_PRIVATE inline bool VersionHasDataFrameHeader( + QuicTransportVersion transport_version) { + return transport_version == QUIC_VERSION_99; +} + +// Returns true if QuicSpdySession instantiates a QPACK encoder and decoder. +// TODO(123528590): Implement the following features and gate them on this +// function as well, optionally renaming this function as appropriate. +// Send HEADERS on the request/response stream instead of the headers stream. +// Send PUSH_PROMISE on the request/response stream instead of headers stream. +// Send PRIORITY on the request/response stream instead of the headers stream. +// Do not instantiate the headers stream object. +QUIC_EXPORT_PRIVATE inline bool VersionUsesQpack( + QuicTransportVersion transport_version) { + const bool uses_qpack = (transport_version == QUIC_VERSION_99); + if (uses_qpack) { + DCHECK(VersionHasDataFrameHeader(transport_version)); + } + return uses_qpack; +} + +// Returns whether the transport_version supports the variable length integer +// length field as defined by IETF QUIC draft-13 and later. +QUIC_EXPORT_PRIVATE inline bool QuicVersionHasLongHeaderLengths( + QuicTransportVersion transport_version) { + // TODO(dschinazi) if we enable long header lengths before v99, we need to + // add support for fixing up lengths in QuicFramer::BuildDataPacket. + return transport_version == QUIC_VERSION_99; +} + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_VERSIONS_H_
diff --git a/quic/core/quic_versions_test.cc b/quic/core/quic_versions_test.cc new file mode 100644 index 0000000..db7f53a --- /dev/null +++ b/quic/core/quic_versions_test.cc
@@ -0,0 +1,521 @@ +// Copyright (c) 2012 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/quic_versions.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_mock_log.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +using testing::_; + +class QuicVersionsTest : public QuicTest { + protected: + QuicVersionLabel MakeVersionLabel(char a, char b, char c, char d) { + return MakeQuicTag(d, c, b, a); + } +}; + +TEST_F(QuicVersionsTest, QuicVersionToQuicVersionLabel) { + // If you add a new version to the QuicTransportVersion enum you will need to + // add a new case to QuicVersionToQuicVersionLabel, otherwise this test will + // fail. + + // Any logs would indicate an unsupported version which we don't expect. + CREATE_QUIC_MOCK_LOG(log); + EXPECT_QUIC_LOG_CALL(log).Times(0); + log.StartCapturingLogs(); + + // Explicitly test a specific version. + EXPECT_EQ(MakeQuicTag('9', '3', '0', 'Q'), + QuicVersionToQuicVersionLabel(QUIC_VERSION_39)); + + // Loop over all supported versions and make sure that we never hit the + // default case (i.e. all supported versions should be successfully converted + // to valid QuicVersionLabels). + for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) { + QuicTransportVersion version = kSupportedTransportVersions[i]; + EXPECT_LT(0u, QuicVersionToQuicVersionLabel(version)); + } +} + +TEST_F(QuicVersionsTest, QuicVersionToQuicVersionLabelUnsupported) { + // TODO(rjshade): Change to DFATAL once we actually support multiple versions, + // and QuicConnectionTest::SendVersionNegotiationPacket can be changed to use + // mis-matched versions rather than relying on QUIC_VERSION_UNSUPPORTED. + CREATE_QUIC_MOCK_LOG(log); + log.StartCapturingLogs(); + + if (QUIC_LOG_ERROR_IS_ON()) { + EXPECT_QUIC_LOG_CALL_CONTAINS(log, ERROR, + "Unsupported QuicTransportVersion: 0"); + } + + EXPECT_EQ(0u, QuicVersionToQuicVersionLabel(QUIC_VERSION_UNSUPPORTED)); +} + +TEST_F(QuicVersionsTest, QuicVersionLabelToQuicTransportVersion) { + // If you add a new version to the QuicTransportVersion enum you will need to + // add a new case to QuicVersionLabelToQuicTransportVersion, otherwise this + // test will fail. + + // Any logs would indicate an unsupported version which we don't expect. + CREATE_QUIC_MOCK_LOG(log); + EXPECT_QUIC_LOG_CALL(log).Times(0); + log.StartCapturingLogs(); + + // Explicitly test specific versions. + EXPECT_EQ(QUIC_VERSION_39, + QuicVersionLabelToQuicVersion(MakeQuicTag('9', '3', '0', 'Q'))); + + for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) { + QuicTransportVersion version = kSupportedTransportVersions[i]; + + // Get the label from the version (we can loop over QuicVersions easily). + QuicVersionLabel version_label = QuicVersionToQuicVersionLabel(version); + EXPECT_LT(0u, version_label); + + // Now try converting back. + QuicTransportVersion label_to_transport_version = + QuicVersionLabelToQuicVersion(version_label); + EXPECT_EQ(version, label_to_transport_version); + EXPECT_NE(QUIC_VERSION_UNSUPPORTED, label_to_transport_version); + } +} + +TEST_F(QuicVersionsTest, QuicVersionLabelToQuicVersionUnsupported) { + CREATE_QUIC_MOCK_LOG(log); + if (QUIC_DLOG_INFO_IS_ON()) { + EXPECT_QUIC_LOG_CALL_CONTAINS(log, INFO, + "Unsupported QuicVersionLabel version: EKAF") + .Times(1); + } + log.StartCapturingLogs(); + + EXPECT_EQ(QUIC_VERSION_UNSUPPORTED, + QuicVersionLabelToQuicVersion(MakeQuicTag('F', 'A', 'K', 'E'))); +} + +TEST_F(QuicVersionsTest, QuicVersionLabelToHandshakeProtocol) { + CREATE_QUIC_MOCK_LOG(log); + EXPECT_QUIC_LOG_CALL(log).Times(0); + log.StartCapturingLogs(); + + for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) { + QuicVersionLabel version_label = + QuicVersionToQuicVersionLabel(kSupportedTransportVersions[i]); + EXPECT_EQ(PROTOCOL_QUIC_CRYPTO, + QuicVersionLabelToHandshakeProtocol(version_label)); + } + + // Test a TLS version: + FLAGS_quic_supports_tls_handshake = true; + QuicTag tls_tag = MakeQuicTag('3', '4', '0', 'T'); + EXPECT_EQ(PROTOCOL_TLS1_3, QuicVersionLabelToHandshakeProtocol(tls_tag)); + + FLAGS_quic_supports_tls_handshake = false; + if (QUIC_DLOG_INFO_IS_ON()) { + EXPECT_QUIC_LOG_CALL_CONTAINS(log, INFO, + "Unsupported QuicVersionLabel version: T043") + .Times(1); + } + EXPECT_EQ(PROTOCOL_UNSUPPORTED, QuicVersionLabelToHandshakeProtocol(tls_tag)); +} + +TEST_F(QuicVersionsTest, ParseQuicVersionLabel) { + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39), + ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '3', '9'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), + ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '3'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44), + ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '4'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), + ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '6'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47), + ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '7'))); + + // Test a TLS version: + FLAGS_quic_supports_tls_handshake = true; + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_39), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6'))); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '7'))); + + FLAGS_quic_supports_tls_handshake = false; + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '5'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '5'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '7'))); +} + +TEST_F(QuicVersionsTest, CreateQuicVersionLabel) { + EXPECT_EQ(MakeVersionLabel('Q', '0', '3', '9'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39))); + EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '3'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43))); + EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '4'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44))); + EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '6'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46))); + EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '7'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47))); + + // Test a TLS version: + FLAGS_quic_supports_tls_handshake = true; + EXPECT_EQ(MakeVersionLabel('T', '0', '3', '9'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_39))); + EXPECT_EQ(MakeVersionLabel('T', '0', '4', '3'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43))); + EXPECT_EQ(MakeVersionLabel('T', '0', '4', '4'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44))); + EXPECT_EQ(MakeVersionLabel('T', '0', '4', '6'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46))); + EXPECT_EQ(MakeVersionLabel('T', '0', '4', '7'), + CreateQuicVersionLabel( + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47))); + + FLAGS_quic_supports_tls_handshake = false; + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '5'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6'))); + EXPECT_EQ(UnsupportedQuicVersion(), + ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '7'))); +} + +TEST_F(QuicVersionsTest, QuicVersionLabelToString) { + QuicVersionLabelVector version_labels = { + MakeVersionLabel('Q', '0', '3', '5'), + MakeVersionLabel('Q', '0', '3', '7'), + MakeVersionLabel('T', '0', '3', '8'), + }; + + EXPECT_EQ("Q035", QuicVersionLabelToString(version_labels[0])); + EXPECT_EQ("T038", QuicVersionLabelToString(version_labels[2])); + + EXPECT_EQ("Q035,Q037,T038", QuicVersionLabelVectorToString(version_labels)); + EXPECT_EQ("Q035:Q037:T038", + QuicVersionLabelVectorToString(version_labels, ":", 2)); + EXPECT_EQ("Q035|Q037|...", + QuicVersionLabelVectorToString(version_labels, "|", 1)); +} + +TEST_F(QuicVersionsTest, QuicVersionToString) { + EXPECT_EQ("QUIC_VERSION_39", QuicVersionToString(QUIC_VERSION_39)); + EXPECT_EQ("QUIC_VERSION_UNSUPPORTED", + QuicVersionToString(QUIC_VERSION_UNSUPPORTED)); + + QuicTransportVersion single_version[] = {QUIC_VERSION_39}; + QuicTransportVersionVector versions_vector; + for (size_t i = 0; i < QUIC_ARRAYSIZE(single_version); ++i) { + versions_vector.push_back(single_version[i]); + } + EXPECT_EQ("QUIC_VERSION_39", + QuicTransportVersionVectorToString(versions_vector)); + + QuicTransportVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, + QUIC_VERSION_39}; + versions_vector.clear(); + for (size_t i = 0; i < QUIC_ARRAYSIZE(multiple_versions); ++i) { + versions_vector.push_back(multiple_versions[i]); + } + EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_39", + QuicTransportVersionVectorToString(versions_vector)); + + // Make sure that all supported versions are present in QuicVersionToString. + for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) { + QuicTransportVersion version = kSupportedTransportVersions[i]; + EXPECT_NE("QUIC_VERSION_UNSUPPORTED", QuicVersionToString(version)); + } +} + +TEST_F(QuicVersionsTest, ParsedQuicVersionToString) { + ParsedQuicVersion unsupported = UnsupportedQuicVersion(); + ParsedQuicVersion version39(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39); + EXPECT_EQ("Q039", ParsedQuicVersionToString(version39)); + EXPECT_EQ("0", ParsedQuicVersionToString(unsupported)); + + ParsedQuicVersionVector versions_vector = {version39}; + EXPECT_EQ("Q039", ParsedQuicVersionVectorToString(versions_vector)); + + versions_vector = {unsupported, version39}; + EXPECT_EQ("0,Q039", ParsedQuicVersionVectorToString(versions_vector)); + EXPECT_EQ("0:Q039", ParsedQuicVersionVectorToString(versions_vector, ":", + versions_vector.size())); + EXPECT_EQ("0|...", ParsedQuicVersionVectorToString(versions_vector, "|", 0)); + + // Make sure that all supported versions are present in + // ParsedQuicVersionToString. + FLAGS_quic_supports_tls_handshake = true; + for (QuicTransportVersion transport_version : kSupportedTransportVersions) { + for (HandshakeProtocol protocol : kSupportedHandshakeProtocols) { + EXPECT_NE("0", ParsedQuicVersionToString( + ParsedQuicVersion(protocol, transport_version))); + } + } +} +TEST_F(QuicVersionsTest, AllSupportedTransportVersions) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + ASSERT_EQ(QUIC_ARRAYSIZE(kSupportedTransportVersions), all_versions.size()); + for (size_t i = 0; i < all_versions.size(); ++i) { + EXPECT_EQ(kSupportedTransportVersions[i], all_versions[i]); + } +} + +TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsAllVersions) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, true); + SetQuicReloadableFlag(quic_enable_version_44, true); + SetQuicReloadableFlag(quic_enable_version_46, true); + SetQuicReloadableFlag(quic_enable_version_47, true); + SetQuicReloadableFlag(quic_enable_version_99, true); + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : all_versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + QuicTransportVersionVector expected_versions = { + QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46, + QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}; + ParsedQuicVersionVector expected_parsed_versions; + for (QuicTransportVersion version : expected_versions) { + expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + + ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); + ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); +} + +TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo99) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, true); + SetQuicReloadableFlag(quic_enable_version_44, true); + SetQuicReloadableFlag(quic_enable_version_46, true); + SetQuicReloadableFlag(quic_enable_version_47, true); + SetQuicReloadableFlag(quic_enable_version_99, false); + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : all_versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + QuicTransportVersionVector expected_versions = { + QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43, + QUIC_VERSION_39}; + ParsedQuicVersionVector expected_parsed_versions; + for (QuicTransportVersion version : expected_versions) { + expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + + ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); + ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); +} + +TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo47) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, true); + SetQuicReloadableFlag(quic_enable_version_44, true); + SetQuicReloadableFlag(quic_enable_version_46, true); + SetQuicReloadableFlag(quic_enable_version_47, false); + SetQuicReloadableFlag(quic_enable_version_99, false); + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : all_versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + QuicTransportVersionVector expected_versions = { + QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}; + ParsedQuicVersionVector expected_parsed_versions; + for (QuicTransportVersion version : expected_versions) { + expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + + ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); + ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); +} + +TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo46) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, true); + SetQuicReloadableFlag(quic_enable_version_44, true); + SetQuicReloadableFlag(quic_enable_version_46, false); + SetQuicReloadableFlag(quic_enable_version_47, false); + SetQuicReloadableFlag(quic_enable_version_99, false); + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : all_versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + QuicTransportVersionVector expected_versions = { + QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}; + ParsedQuicVersionVector expected_parsed_versions; + for (QuicTransportVersion version : expected_versions) { + expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + + ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); + ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); +} + +TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo44) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, true); + SetQuicReloadableFlag(quic_enable_version_44, false); + SetQuicReloadableFlag(quic_enable_version_46, false); + SetQuicReloadableFlag(quic_enable_version_47, false); + SetQuicReloadableFlag(quic_enable_version_99, false); + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : all_versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + QuicTransportVersionVector expected_versions = {QUIC_VERSION_43, + QUIC_VERSION_39}; + ParsedQuicVersionVector expected_parsed_versions; + for (QuicTransportVersion version : expected_versions) { + expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + + ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); + ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); +} + +TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo43) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + SetQuicReloadableFlag(quic_disable_version_39, false); + SetQuicReloadableFlag(quic_enable_version_43, false); + SetQuicReloadableFlag(quic_enable_version_44, false); + SetQuicReloadableFlag(quic_enable_version_46, false); + SetQuicReloadableFlag(quic_enable_version_47, false); + SetQuicReloadableFlag(quic_enable_version_99, false); + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : all_versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + QuicTransportVersionVector expected_versions = {QUIC_VERSION_39}; + ParsedQuicVersionVector expected_parsed_versions; + for (QuicTransportVersion version : expected_versions) { + expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + + ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); + ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); +} + +TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo39) { + QuicTransportVersionVector all_versions = AllSupportedTransportVersions(); + SetQuicReloadableFlag(quic_disable_version_39, true); + SetQuicReloadableFlag(quic_enable_version_43, true); + SetQuicReloadableFlag(quic_enable_version_44, false); + SetQuicReloadableFlag(quic_enable_version_46, false); + SetQuicReloadableFlag(quic_enable_version_47, false); + SetQuicReloadableFlag(quic_enable_version_99, false); + ParsedQuicVersionVector parsed_versions; + for (QuicTransportVersion version : all_versions) { + parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + QuicTransportVersionVector expected_versions = {QUIC_VERSION_43}; + ParsedQuicVersionVector expected_parsed_versions; + for (QuicTransportVersion version : expected_versions) { + expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version)); + } + + ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions)); + ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions)); +} + +TEST_F(QuicVersionsTest, LookUpVersionByIndex) { + QuicTransportVersionVector all_versions = {QUIC_VERSION_39}; + int version_count = all_versions.size(); + for (int i = -5; i <= version_count + 1; ++i) { + if (i >= 0 && i < version_count) { + EXPECT_EQ(all_versions[i], VersionOfIndex(all_versions, i)[0]); + } else { + EXPECT_EQ(QUIC_VERSION_UNSUPPORTED, VersionOfIndex(all_versions, i)[0]); + } + } +} + +TEST_F(QuicVersionsTest, LookUpParsedVersionByIndex) { + ParsedQuicVersionVector all_versions = AllSupportedVersions(); + int version_count = all_versions.size(); + for (int i = -5; i <= version_count + 1; ++i) { + if (i >= 0 && i < version_count) { + EXPECT_EQ(all_versions[i], ParsedVersionOfIndex(all_versions, i)[0]); + } else { + EXPECT_EQ(UnsupportedQuicVersion(), + ParsedVersionOfIndex(all_versions, i)[0]); + } + } +} + +TEST_F(QuicVersionsTest, ParsedVersionsToTransportVersions) { + ParsedQuicVersionVector all_versions = AllSupportedVersions(); + QuicTransportVersionVector transport_versions = + ParsedVersionsToTransportVersions(all_versions); + ASSERT_EQ(all_versions.size(), transport_versions.size()); + for (size_t i = 0; i < all_versions.size(); ++i) { + EXPECT_EQ(transport_versions[i], all_versions[i].transport_version); + } +} + +// This test may appear to be so simplistic as to be unnecessary, +// yet a typo was made in doing the #defines and it was caught +// only in some test far removed from here... Better safe than sorry. +TEST_F(QuicVersionsTest, CheckVersionNumbersForTypos) { + static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, + "Supported versions out of sync"); + EXPECT_EQ(QUIC_VERSION_39, 39); + EXPECT_EQ(QUIC_VERSION_43, 43); + EXPECT_EQ(QUIC_VERSION_44, 44); + EXPECT_EQ(QUIC_VERSION_46, 46); + EXPECT_EQ(QUIC_VERSION_47, 47); + EXPECT_EQ(QUIC_VERSION_99, 99); +} +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_write_blocked_list.cc b/quic/core/quic_write_blocked_list.cc new file mode 100644 index 0000000..e392955 --- /dev/null +++ b/quic/core/quic_write_blocked_list.cc
@@ -0,0 +1,19 @@ +// Copyright 2014 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/quic_write_blocked_list.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" + +namespace quic { + +QuicWriteBlockedList::QuicWriteBlockedList() : last_priority_popped_(0) { + memset(batch_write_stream_id_, 0, sizeof(batch_write_stream_id_)); + memset(bytes_left_for_batch_write_, 0, sizeof(bytes_left_for_batch_write_)); +} + +QuicWriteBlockedList::~QuicWriteBlockedList() {} + +} // namespace quic
diff --git a/quic/core/quic_write_blocked_list.h b/quic/core/quic_write_blocked_list.h new file mode 100644 index 0000000..11a5f50 --- /dev/null +++ b/quic/core/quic_write_blocked_list.h
@@ -0,0 +1,265 @@ +// Copyright 2014 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_QUIC_WRITE_BLOCKED_LIST_H_ +#define QUICHE_QUIC_CORE_QUIC_WRITE_BLOCKED_LIST_H_ + +#include <cstddef> +#include <cstdint> + +#include "base/macros.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" +#include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h" + +namespace quic { + +// Keeps tracks of the QUIC streams that have data to write, sorted by +// priority. QUIC stream priority order is: +// Crypto stream > Headers stream > Data streams by requested priority. +class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { + private: + typedef spdy::PriorityWriteScheduler<QuicStreamId> QuicPriorityWriteScheduler; + + public: + explicit QuicWriteBlockedList(); + QuicWriteBlockedList(const QuicWriteBlockedList&) = delete; + QuicWriteBlockedList& operator=(const QuicWriteBlockedList&) = delete; + ~QuicWriteBlockedList(); + + bool HasWriteBlockedDataStreams() const { + return priority_write_scheduler_.HasReadyStreams(); + } + + bool HasWriteBlockedSpecialStream() const { + return static_stream_collection_.num_blocked() > 0; + } + + size_t NumBlockedSpecialStreams() const { + return static_stream_collection_.num_blocked(); + } + + size_t NumBlockedStreams() const { + return NumBlockedSpecialStreams() + + priority_write_scheduler_.NumReadyStreams(); + } + + bool ShouldYield(QuicStreamId id) const { + for (const auto& stream : static_stream_collection_) { + if (stream.id == id) { + // Static streams should never yield to data streams, or to lower + // priority static stream. + return false; + } + if (stream.is_blocked) { + return true; // All data streams yield to static streams. + } + } + + return priority_write_scheduler_.ShouldYield(id); + } + + // Pops the highest priorty stream, special casing crypto and headers streams. + // Latches the most recently popped data stream for batch writing purposes. + QuicStreamId PopFront() { + QuicStreamId static_stream_id; + if (static_stream_collection_.UnblockFirstBlocked(&static_stream_id)) { + return static_stream_id; + } + + const auto id_and_precedence = + priority_write_scheduler_.PopNextReadyStreamAndPrecedence(); + const QuicStreamId id = std::get<0>(id_and_precedence); + const spdy::SpdyPriority priority = + std::get<1>(id_and_precedence).spdy3_priority(); + + if (!priority_write_scheduler_.HasReadyStreams()) { + // If no streams are blocked, don't bother latching. This stream will be + // the first popped for its priority anyway. + batch_write_stream_id_[priority] = 0; + last_priority_popped_ = priority; + } else if (batch_write_stream_id_[priority] != id) { + // If newly latching this batch write stream, let it write 16k. + batch_write_stream_id_[priority] = id; + bytes_left_for_batch_write_[priority] = 16000; + last_priority_popped_ = priority; + } + + return id; + } + + void RegisterStream(QuicStreamId stream_id, + bool is_static_stream, + spdy::SpdyPriority priority) { + DCHECK(!priority_write_scheduler_.StreamRegistered(stream_id)); + if (is_static_stream) { + static_stream_collection_.Register(stream_id); + return; + } + + priority_write_scheduler_.RegisterStream( + stream_id, spdy::SpdyStreamPrecedence(priority)); + } + + void UnregisterStream(QuicStreamId stream_id, bool is_static) { + if (is_static) { + static_stream_collection_.Unregister(stream_id); + return; + } + priority_write_scheduler_.UnregisterStream(stream_id); + } + + void UpdateStreamPriority(QuicStreamId stream_id, + spdy::SpdyPriority new_priority) { + DCHECK(!static_stream_collection_.IsRegistered(stream_id)); + priority_write_scheduler_.UpdateStreamPrecedence( + stream_id, spdy::SpdyStreamPrecedence(new_priority)); + } + + void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) { + if (batch_write_stream_id_[last_priority_popped_] == stream_id) { + // If this was the last data stream popped by PopFront, update the + // bytes remaining in its batch write. + bytes_left_for_batch_write_[last_priority_popped_] -= + static_cast<int32_t>(bytes); + } + } + + // Pushes a stream to the back of the list for its priority level *unless* it + // is latched for doing batched writes in which case it goes to the front of + // the list for its priority level. + // Headers and crypto streams are special cased to always resume first. + void AddStream(QuicStreamId stream_id) { + if (static_stream_collection_.SetBlocked(stream_id)) { + return; + } + + bool push_front = + stream_id == batch_write_stream_id_[last_priority_popped_] && + bytes_left_for_batch_write_[last_priority_popped_] > 0; + priority_write_scheduler_.MarkStreamReady(stream_id, push_front); + } + + // Returns true if stream with |stream_id| is write blocked. + bool IsStreamBlocked(QuicStreamId stream_id) const { + for (const auto& stream : static_stream_collection_) { + if (stream.id == stream_id) { + return stream.is_blocked; + } + } + + return priority_write_scheduler_.IsStreamReady(stream_id); + } + + private: + QuicPriorityWriteScheduler priority_write_scheduler_; + + // If performing batch writes, this will be the stream ID of the stream doing + // batch writes for this priority level. We will allow this stream to write + // until it has written kBatchWriteSize bytes, it has no more data to write, + // or a higher priority stream preempts. + QuicStreamId batch_write_stream_id_[spdy::kV3LowestPriority + 1]; + // Set to kBatchWriteSize when we set a new batch_write_stream_id_ for a given + // priority. This is decremented with each write the stream does until it is + // done with its batch write. + int32_t bytes_left_for_batch_write_[spdy::kV3LowestPriority + 1]; + // Tracks the last priority popped for UpdateBytesForStream. + spdy::SpdyPriority last_priority_popped_; + + // A StaticStreamCollection is a vector of <QuicStreamId, bool> pairs plus a + // eagerly-computed number of blocked static streams. + class StaticStreamCollection { + public: + struct StreamIdBlockedPair { + QuicStreamId id; + bool is_blocked; + }; + + // Optimized for the typical case of 2 static streams per session. + typedef QuicInlinedVector<StreamIdBlockedPair, 2> StreamsVector; + + StreamsVector::const_iterator begin() const { return streams_.cbegin(); } + + StreamsVector::const_iterator end() const { return streams_.cend(); } + + size_t num_blocked() const { return num_blocked_; } + + // Add |id| to the collection in unblocked state. + void Register(QuicStreamId id) { + DCHECK(!IsRegistered(id)); + DCHECK(streams_.empty() || id > streams_.back().id) + << "stream_id: " << id + << " last static stream: " << streams_.back().id; + streams_.push_back({id, false}); + } + + // True if |id| is in the collection, regardless of its state. + bool IsRegistered(QuicStreamId id) const { + for (const auto& stream : streams_) { + if (stream.id == id) { + return true; + } + } + return false; + } + + // Remove |id| from the collection, if it is in the blocked state, reduce + // |num_blocked_| by 1. + void Unregister(QuicStreamId id) { + for (auto it = streams_.begin(); it != streams_.end(); ++it) { + if (it->id == id) { + if (it->is_blocked) { + --num_blocked_; + } + streams_.erase(it); + return; + } + } + DCHECK(false) << "Erasing a non-exist stream with id " << id; + } + + // Set |id| to be blocked. If |id| is not already blocked, increase + // |num_blocked_| by 1. + // Return true if |id| is in the collection. + bool SetBlocked(QuicStreamId id) { + for (auto& stream : streams_) { + if (stream.id == id) { + if (!stream.is_blocked) { + stream.is_blocked = true; + ++num_blocked_; + } + return true; + } + } + return false; + } + + // Unblock the first blocked stream in the collection. + // If no stream is blocked, return false. Otherwise return true, set *id to + // the unblocked stream id and reduce |num_blocked_| by 1. + bool UnblockFirstBlocked(QuicStreamId* id) { + for (auto& stream : streams_) { + if (stream.is_blocked) { + --num_blocked_; + stream.is_blocked = false; + *id = stream.id; + return true; + } + } + return false; + } + + private: + size_t num_blocked_ = 0; + StreamsVector streams_; + }; + + StaticStreamCollection static_stream_collection_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_WRITE_BLOCKED_LIST_H_
diff --git a/quic/core/quic_write_blocked_list_test.cc b/quic/core/quic_write_blocked_list_test.cc new file mode 100644 index 0000000..a9319e3 --- /dev/null +++ b/quic/core/quic_write_blocked_list_test.cc
@@ -0,0 +1,233 @@ +// Copyright 2014 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/quic_write_blocked_list.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using spdy::kV3HighestPriority; +using spdy::kV3LowestPriority; + +namespace quic { +namespace test { +namespace { + +class QuicWriteBlockedListTest : public QuicTest {}; + +TEST_F(QuicWriteBlockedListTest, PriorityOrder) { + QuicWriteBlockedList write_blocked_list; + + // Mark streams blocked in roughly reverse priority order, and + // verify that streams are sorted. + write_blocked_list.RegisterStream(40, false, kV3LowestPriority); + write_blocked_list.RegisterStream(23, false, kV3HighestPriority); + write_blocked_list.RegisterStream(17, false, kV3HighestPriority); + write_blocked_list.RegisterStream(1, true, kV3HighestPriority); + write_blocked_list.RegisterStream(3, true, kV3HighestPriority); + + write_blocked_list.AddStream(40); + EXPECT_TRUE(write_blocked_list.IsStreamBlocked(40)); + write_blocked_list.AddStream(23); + EXPECT_TRUE(write_blocked_list.IsStreamBlocked(23)); + write_blocked_list.AddStream(17); + EXPECT_TRUE(write_blocked_list.IsStreamBlocked(17)); + write_blocked_list.AddStream(3); + EXPECT_TRUE(write_blocked_list.IsStreamBlocked(3)); + write_blocked_list.AddStream(1); + EXPECT_TRUE(write_blocked_list.IsStreamBlocked(1)); + + EXPECT_EQ(5u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream()); + EXPECT_EQ(2u, write_blocked_list.NumBlockedSpecialStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams()); + // The Crypto stream is highest priority. + EXPECT_EQ(1u, write_blocked_list.PopFront()); + EXPECT_EQ(1u, write_blocked_list.NumBlockedSpecialStreams()); + EXPECT_FALSE(write_blocked_list.IsStreamBlocked(1)); + // Followed by the Headers stream. + EXPECT_EQ(3u, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedSpecialStreams()); + EXPECT_FALSE(write_blocked_list.IsStreamBlocked(3)); + // Streams with same priority are popped in the order they were inserted. + EXPECT_EQ(23u, write_blocked_list.PopFront()); + EXPECT_FALSE(write_blocked_list.IsStreamBlocked(23)); + EXPECT_EQ(17u, write_blocked_list.PopFront()); + EXPECT_FALSE(write_blocked_list.IsStreamBlocked(17)); + // Low priority stream appears last. + EXPECT_EQ(40u, write_blocked_list.PopFront()); + EXPECT_FALSE(write_blocked_list.IsStreamBlocked(40)); + + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams()); +} + +TEST_F(QuicWriteBlockedListTest, CryptoStream) { + QuicWriteBlockedList write_blocked_list; + write_blocked_list.RegisterStream(1, true, kV3HighestPriority); + write_blocked_list.AddStream(1); + + EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream()); + EXPECT_EQ(1u, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream()); +} + +TEST_F(QuicWriteBlockedListTest, HeadersStream) { + QuicWriteBlockedList write_blocked_list; + write_blocked_list.RegisterStream(3, true, kV3HighestPriority); + write_blocked_list.AddStream(3); + + EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream()); + EXPECT_EQ(3u, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream()); +} + +TEST_F(QuicWriteBlockedListTest, VerifyHeadersStream) { + QuicWriteBlockedList write_blocked_list; + write_blocked_list.RegisterStream(5, false, kV3HighestPriority); + write_blocked_list.RegisterStream(3, true, kV3HighestPriority); + write_blocked_list.AddStream(5); + write_blocked_list.AddStream(3); + + EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams()); + // In newer QUIC versions, there is a headers stream which is + // higher priority than data streams. + EXPECT_EQ(3u, write_blocked_list.PopFront()); + EXPECT_EQ(5u, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams()); +} + +TEST_F(QuicWriteBlockedListTest, NoDuplicateEntries) { + // Test that QuicWriteBlockedList doesn't allow duplicate entries. + QuicWriteBlockedList write_blocked_list; + + // Try to add a stream to the write blocked list multiple times at the same + // priority. + const QuicStreamId kBlockedId = 3 + 2; + write_blocked_list.RegisterStream(kBlockedId, false, kV3HighestPriority); + write_blocked_list.AddStream(kBlockedId); + write_blocked_list.AddStream(kBlockedId); + write_blocked_list.AddStream(kBlockedId); + + // This should only result in one blocked stream being added. + EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams()); + + // There should only be one stream to pop off the front. + EXPECT_EQ(kBlockedId, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams()); +} + +TEST_F(QuicWriteBlockedListTest, BatchingWrites) { + QuicWriteBlockedList write_blocked_list; + + const QuicStreamId id1 = 3 + 2; + const QuicStreamId id2 = id1 + 2; + const QuicStreamId id3 = id2 + 2; + write_blocked_list.RegisterStream(id1, false, kV3LowestPriority); + write_blocked_list.RegisterStream(id2, false, kV3LowestPriority); + write_blocked_list.RegisterStream(id3, false, kV3HighestPriority); + + write_blocked_list.AddStream(id1); + write_blocked_list.AddStream(id2); + EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams()); + + // The first stream we push back should stay at the front until 16k is + // written. + EXPECT_EQ(id1, write_blocked_list.PopFront()); + write_blocked_list.UpdateBytesForStream(id1, 15999); + write_blocked_list.AddStream(id1); + EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams()); + EXPECT_EQ(id1, write_blocked_list.PopFront()); + + // Once 16k is written the first stream will yield to the next. + write_blocked_list.UpdateBytesForStream(id1, 1); + write_blocked_list.AddStream(id1); + EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams()); + EXPECT_EQ(id2, write_blocked_list.PopFront()); + + // Set the new stream to have written all but one byte. + write_blocked_list.UpdateBytesForStream(id2, 15999); + write_blocked_list.AddStream(id2); + EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams()); + + // Ensure higher priority streams are popped first. + write_blocked_list.AddStream(id3); + EXPECT_EQ(id3, write_blocked_list.PopFront()); + + // Higher priority streams will always be popped first, even if using their + // byte quota + write_blocked_list.UpdateBytesForStream(id3, 20000); + write_blocked_list.AddStream(id3); + EXPECT_EQ(id3, write_blocked_list.PopFront()); + + // Once the higher priority stream is out of the way, id2 will resume its 16k + // write, with only 1 byte remaining of its guaranteed write allocation. + EXPECT_EQ(id2, write_blocked_list.PopFront()); + write_blocked_list.UpdateBytesForStream(id2, 1); + write_blocked_list.AddStream(id2); + EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams()); + EXPECT_EQ(id1, write_blocked_list.PopFront()); +} + +TEST_F(QuicWriteBlockedListTest, Ceding) { + QuicWriteBlockedList write_blocked_list; + + write_blocked_list.RegisterStream(15, false, kV3HighestPriority); + write_blocked_list.RegisterStream(16, false, kV3HighestPriority); + write_blocked_list.RegisterStream(5, false, 5); + write_blocked_list.RegisterStream(4, false, 5); + write_blocked_list.RegisterStream(7, false, 7); + write_blocked_list.RegisterStream(1, true, kV3HighestPriority); + write_blocked_list.RegisterStream(3, true, kV3HighestPriority); + + // When nothing is on the list, nothing yields. + EXPECT_FALSE(write_blocked_list.ShouldYield(5)); + + write_blocked_list.AddStream(5); + // 5 should not yield to itself. + EXPECT_FALSE(write_blocked_list.ShouldYield(5)); + // 4 and 7 are equal or lower priority and should yield to 5. + EXPECT_TRUE(write_blocked_list.ShouldYield(4)); + EXPECT_TRUE(write_blocked_list.ShouldYield(7)); + // 15, headers and crypto should preempt 5. + EXPECT_FALSE(write_blocked_list.ShouldYield(15)); + EXPECT_FALSE(write_blocked_list.ShouldYield(3)); + EXPECT_FALSE(write_blocked_list.ShouldYield(1)); + + // Block a high priority stream. + write_blocked_list.AddStream(15); + // 16 should yield (same priority) but headers and crypto will still not. + EXPECT_TRUE(write_blocked_list.ShouldYield(16)); + EXPECT_FALSE(write_blocked_list.ShouldYield(3)); + EXPECT_FALSE(write_blocked_list.ShouldYield(1)); + + // Block the headers stream. All streams but crypto and headers should yield. + write_blocked_list.AddStream(3); + EXPECT_TRUE(write_blocked_list.ShouldYield(16)); + EXPECT_TRUE(write_blocked_list.ShouldYield(15)); + EXPECT_FALSE(write_blocked_list.ShouldYield(3)); + EXPECT_FALSE(write_blocked_list.ShouldYield(1)); + + // Block the crypto stream. All streams but crypto should yield. + write_blocked_list.AddStream(1); + EXPECT_TRUE(write_blocked_list.ShouldYield(16)); + EXPECT_TRUE(write_blocked_list.ShouldYield(15)); + EXPECT_TRUE(write_blocked_list.ShouldYield(3)); + EXPECT_FALSE(write_blocked_list.ShouldYield(1)); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/session_notifier_interface.h b/quic/core/session_notifier_interface.h new file mode 100644 index 0000000..83fc0f1 --- /dev/null +++ b/quic/core/session_notifier_interface.h
@@ -0,0 +1,43 @@ +// Copyright (c) 2017 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_SESSION_NOTIFIER_INTERFACE_H_ +#define QUICHE_QUIC_CORE_SESSION_NOTIFIER_INTERFACE_H_ + +#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" + +namespace quic { + +// Pure virtual class to be notified when a packet containing a frame is acked +// or lost. +class QUIC_EXPORT_PRIVATE SessionNotifierInterface { + public: + virtual ~SessionNotifierInterface() {} + + // Called when |frame| is acked. Returns true if any new data gets acked, + // returns false otherwise. + virtual bool OnFrameAcked(const QuicFrame& frame, + QuicTime::Delta ack_delay_time) = 0; + + // Called when |frame| is retransmitted. + virtual void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) = 0; + + // Called when |frame| is considered as lost. + virtual void OnFrameLost(const QuicFrame& frame) = 0; + + // Called to retransmit |frames| with transmission |type|. + virtual void RetransmitFrames(const QuicFrames& frames, + TransmissionType type) = 0; + + // Returns true if |frame| is outstanding and waiting to be acked. + virtual bool IsFrameOutstanding(const QuicFrame& frame) const = 0; + + // Returns true if crypto stream is waiting for acks. + virtual bool HasUnackedCryptoData() const = 0; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_SESSION_NOTIFIER_INTERFACE_H_
diff --git a/quic/core/stateless_rejector.cc b/quic/core/stateless_rejector.cc new file mode 100644 index 0000000..3cf30df --- /dev/null +++ b/quic/core/stateless_rejector.cc
@@ -0,0 +1,161 @@ +// Copyright 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/stateless_rejector.h" + +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +class StatelessRejector::ValidateCallback + : public ValidateClientHelloResultCallback { + public: + explicit ValidateCallback( + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> cb) + : rejector_(std::move(rejector)), cb_(std::move(cb)) {} + + ~ValidateCallback() override = default; + + void Run(QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> /* proof_source_details */) + override { + StatelessRejector* rejector_ptr = rejector_.get(); + rejector_ptr->ProcessClientHello(std::move(result), std::move(rejector_), + std::move(cb_)); + } + + private: + std::unique_ptr<StatelessRejector> rejector_; + std::unique_ptr<StatelessRejector::ProcessDoneCallback> cb_; +}; + +StatelessRejector::StatelessRejector( + ParsedQuicVersion version, + const ParsedQuicVersionVector& versions, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + const QuicClock* clock, + QuicRandom* random, + QuicByteCount chlo_packet_size, + const QuicSocketAddress& client_address, + const QuicSocketAddress& server_address) + : state_(UNKNOWN), + error_(QUIC_INTERNAL_ERROR), + version_(version), + versions_(versions), + connection_id_(EmptyQuicConnectionId()), + chlo_packet_size_(chlo_packet_size), + client_address_(client_address), + server_address_(server_address), + clock_(clock), + random_(random), + crypto_config_(crypto_config), + compressed_certs_cache_(compressed_certs_cache), + signed_config_(new QuicSignedServerConfig), + params_(new QuicCryptoNegotiatedParameters) {} + +StatelessRejector::~StatelessRejector() = default; + +void StatelessRejector::OnChlo(QuicTransportVersion version, + QuicConnectionId connection_id, + QuicConnectionId server_designated_connection_id, + const CryptoHandshakeMessage& message) { + DCHECK_EQ(kCHLO, message.tag()); + DCHECK_NE(connection_id, server_designated_connection_id); + DCHECK_EQ(state_, UNKNOWN); + + if (!GetQuicReloadableFlag(enable_quic_stateless_reject_support) || + !GetQuicReloadableFlag(quic_use_cheap_stateless_rejects) || + !QuicCryptoServerStream::DoesPeerSupportStatelessRejects(message)) { + state_ = UNSUPPORTED; + return; + } + + connection_id_ = connection_id; + server_designated_connection_id_ = server_designated_connection_id; + chlo_ = message; // Note: copies the message +} + +void StatelessRejector::Process(std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<ProcessDoneCallback> done_cb) { + QUIC_BUG_IF(rejector->state() != UNKNOWN) << "StatelessRejector::Process " + "called for a rejector which " + "has already made a decision"; + StatelessRejector* rejector_ptr = rejector.get(); + rejector_ptr->crypto_config_->ValidateClientHello( + rejector_ptr->chlo_, rejector_ptr->client_address_.host(), + rejector_ptr->server_address_, rejector_ptr->version_.transport_version, + rejector_ptr->clock_, rejector_ptr->signed_config_, + std::unique_ptr<ValidateCallback>( + new ValidateCallback(std::move(rejector), std::move(done_cb)))); +} + +class StatelessRejector::ProcessClientHelloCallback + : public ProcessClientHelloResultCallback { + public: + ProcessClientHelloCallback( + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) + : rejector_(std::move(rejector)), done_cb_(std::move(done_cb)) {} + + void Run(QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> /* proof_source_details */) + override { + StatelessRejector* rejector_ptr = rejector_.get(); + rejector_ptr->ProcessClientHelloDone( + error, error_details, std::move(message), std::move(rejector_), + std::move(done_cb_)); + } + + private: + std::unique_ptr<StatelessRejector> rejector_; + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb_; +}; + +void StatelessRejector::ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) { + std::unique_ptr<ProcessClientHelloCallback> cb( + new ProcessClientHelloCallback(std::move(rejector), std::move(done_cb))); + crypto_config_->ProcessClientHello( + result, + /*reject_only=*/true, connection_id_, server_address_, client_address_, + version_, versions_, + /*use_stateless_rejects=*/true, server_designated_connection_id_, clock_, + random_, compressed_certs_cache_, params_, signed_config_, + QuicCryptoStream::CryptoMessageFramingOverhead(version_.transport_version, + connection_id_), + chlo_packet_size_, std::move(cb)); +} + +void StatelessRejector::ProcessClientHelloDone( + QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) { + reply_ = std::move(message); + + if (error != QUIC_NO_ERROR) { + error_ = error; + error_details_ = error_details; + state_ = FAILED; + } else if (reply_->tag() == kSREJ) { + state_ = REJECTED; + } else { + state_ = ACCEPTED; + } + done_cb->Run(std::move(rejector)); +} + +} // namespace quic
diff --git a/quic/core/stateless_rejector.h b/quic/core/stateless_rejector.h new file mode 100644 index 0000000..d9ff94d --- /dev/null +++ b/quic/core/stateless_rejector.h
@@ -0,0 +1,122 @@ +// Copyright 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. + +#ifndef QUICHE_QUIC_CORE_STATELESS_REJECTOR_H_ +#define QUICHE_QUIC_CORE_STATELESS_REJECTOR_H_ + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// The StatelessRejector receives CHLO messages and generates an SREJ +// message in response, if the CHLO can be statelessly rejected. +class StatelessRejector { + public: + enum State { + UNKNOWN, // State has not yet been determined + UNSUPPORTED, // Stateless rejects are not supported + FAILED, // There was an error processing the CHLO. + ACCEPTED, // The CHLO was accepted + REJECTED, // The CHLO was rejected. + }; + + StatelessRejector(ParsedQuicVersion version, + const ParsedQuicVersionVector& versions, + const QuicCryptoServerConfig* crypto_config, + QuicCompressedCertsCache* compressed_certs_cache, + const QuicClock* clock, + QuicRandom* random, + QuicByteCount chlo_packet_size, + const QuicSocketAddress& client_address, + const QuicSocketAddress& server_address); + StatelessRejector(const StatelessRejector&) = delete; + StatelessRejector& operator=(const StatelessRejector&) = delete; + + ~StatelessRejector(); + + // Called when |chlo| is received for |connection_id|. + void OnChlo(QuicTransportVersion version, + QuicConnectionId connection_id, + QuicConnectionId server_designated_connection_id, + const CryptoHandshakeMessage& chlo); + + class ProcessDoneCallback { + public: + virtual ~ProcessDoneCallback() = default; + virtual void Run(std::unique_ptr<StatelessRejector> rejector) = 0; + }; + + // Perform processing to determine whether the CHLO received in OnChlo should + // be statelessly rejected, and invoke the callback once a decision has been + // made. + static void Process(std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<ProcessDoneCallback> done_cb); + + // Return the version of the CHLO. + ParsedQuicVersion version() const { return version_; } + + // Returns the state of the rejector after OnChlo() has been called. + State state() const { return state_; } + + // Returns the error code when state() returns FAILED. + QuicErrorCode error() const { return error_; } + + // Returns the error details when state() returns FAILED. + QuicString error_details() const { return error_details_; } + + // Returns the connection ID. + QuicConnectionId connection_id() const { return connection_id_; } + + // Returns the SREJ message when state() returns REJECTED. + const CryptoHandshakeMessage& reply() const { return *reply_; } + + private: + // Helper class which is passed in to + // QuicCryptoServerConfig::ValidateClientHello. + class ValidateCallback; + friend class ValidateCallback; + + class ProcessClientHelloCallback; + friend class ProcessClientHelloCallback; + + void ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb); + + void ProcessClientHelloDone( + QuicErrorCode error, + const QuicString& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<StatelessRejector> rejector, + std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb); + + State state_; + QuicErrorCode error_; + QuicString error_details_; + ParsedQuicVersion version_; + ParsedQuicVersionVector versions_; + QuicConnectionId connection_id_; + QuicConnectionId server_designated_connection_id_; + QuicByteCount chlo_packet_size_; + QuicSocketAddress client_address_; + QuicSocketAddress server_address_; + const QuicClock* clock_; + QuicRandom* random_; + const QuicCryptoServerConfig* crypto_config_; + QuicCompressedCertsCache* compressed_certs_cache_; + CryptoHandshakeMessage chlo_; + std::unique_ptr<CryptoHandshakeMessage> reply_; + CryptoFramer crypto_framer_; + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_STATELESS_REJECTOR_H_
diff --git a/quic/core/stateless_rejector_test.cc b/quic/core/stateless_rejector_test.cc new file mode 100644 index 0000000..19eef64 --- /dev/null +++ b/quic/core/stateless_rejector_test.cc
@@ -0,0 +1,290 @@ +// Copyright 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/stateless_rejector.h" + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +QuicConnectionId TestServerDesignatedConnectionId() { + return TestConnectionId(24); +} + +// All four combinations of the two flags involved. +enum FlagsMode { ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED }; + +const char* FlagsModeToString(FlagsMode mode) { + switch (mode) { + case ENABLED: + return "ENABLED"; + case STATELESS_DISABLED: + return "STATELESS_DISABLED"; + case CHEAP_DISABLED: + return "CHEAP_DISABLED"; + case BOTH_DISABLED: + return "BOTH_DISABLED"; + default: + QUIC_DLOG(FATAL) << "Unexpected FlagsMode"; + return nullptr; + } +} + +// Test various combinations of QUIC version and flag state. +struct TestParams { + ParsedQuicVersion version = UnsupportedQuicVersion(); + FlagsMode flags; +}; + +QuicString TestParamToString(const testing::TestParamInfo<TestParams>& params) { + return QuicStrCat("v", ParsedQuicVersionToString(params.param.version), "_", + FlagsModeToString(params.param.flags)); +} + +std::vector<TestParams> GetTestParams() { + std::vector<TestParams> params; + for (FlagsMode flags : + {ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED}) { + for (ParsedQuicVersion version : AllSupportedVersions()) { + TestParams param; + param.version = version; + param.flags = flags; + params.push_back(param); + } + } + return params; +} + +class StatelessRejectorTest : public QuicTestWithParam<TestParams> { + public: + StatelessRejectorTest() + : proof_source_(crypto_test_utils::ProofSourceForTesting()), + config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance(), + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default(), + TlsServerHandshaker::CreateSslCtx()), + config_peer_(&config_), + compressed_certs_cache_( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), + rejector_(QuicMakeUnique<StatelessRejector>( + GetParam().version, + AllSupportedVersions(), + &config_, + &compressed_certs_cache_, + &clock_, + QuicRandom::GetInstance(), + kDefaultMaxPacketSize, + QuicSocketAddress(QuicIpAddress::Loopback4(), 12345), + QuicSocketAddress(QuicIpAddress::Loopback4(), 443))) { + SetQuicReloadableFlag( + enable_quic_stateless_reject_support, + GetParam().flags == ENABLED || GetParam().flags == CHEAP_DISABLED); + SetQuicReloadableFlag( + quic_use_cheap_stateless_rejects, + GetParam().flags == ENABLED || GetParam().flags == STATELESS_DISABLED); + + // Add a new primary config. + std::unique_ptr<CryptoHandshakeMessage> msg(config_.AddDefaultConfig( + QuicRandom::GetInstance(), &clock_, config_options_)); + + // Save the server config. + scid_hex_ = + "#" + QuicTextUtils::HexEncode(config_peer_.GetPrimaryConfig()->id); + + // Encode the QUIC version. + ver_hex_ = ParsedQuicVersionToString(GetParam().version); + + // Generate a public value. + char public_value[32]; + memset(public_value, 42, sizeof(public_value)); + pubs_hex_ = + "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value)); + + // Generate a client nonce. + QuicString nonce; + CryptoUtils::GenerateNonce( + clock_.WallNow(), QuicRandom::GetInstance(), + QuicStringPiece( + reinterpret_cast<char*>(config_peer_.GetPrimaryConfig()->orbit), + kOrbitSize), + &nonce); + nonc_hex_ = "#" + QuicTextUtils::HexEncode(nonce); + + // Generate a source address token. + SourceAddressTokens previous_tokens; + QuicIpAddress ip = QuicIpAddress::Loopback4(); + MockRandom rand; + QuicString stk = config_peer_.NewSourceAddressToken( + config_peer_.GetPrimaryConfig()->id, previous_tokens, ip, &rand, + clock_.WallNow(), nullptr); + stk_hex_ = "#" + QuicTextUtils::HexEncode(stk); + } + + protected: + class ProcessDoneCallback : public StatelessRejector::ProcessDoneCallback { + public: + explicit ProcessDoneCallback(StatelessRejectorTest* test) : test_(test) {} + void Run(std::unique_ptr<StatelessRejector> rejector) override { + test_->rejector_ = std::move(rejector); + } + + private: + StatelessRejectorTest* test_; + }; + + std::unique_ptr<ProofSource> proof_source_; + MockClock clock_; + QuicCryptoServerConfig config_; + QuicCryptoServerConfigPeer config_peer_; + QuicCompressedCertsCache compressed_certs_cache_; + QuicCryptoServerConfig::ConfigOptions config_options_; + std::unique_ptr<StatelessRejector> rejector_; + + // Values used in CHLO messages + QuicString scid_hex_; + QuicString nonc_hex_; + QuicString pubs_hex_; + QuicString ver_hex_; + QuicString stk_hex_; +}; + +INSTANTIATE_TEST_SUITE_P(Flags, StatelessRejectorTest, + ::testing::ValuesIn(GetTestParams()), + TestParamToString); + +TEST_P(StatelessRejectorTest, InvalidChlo) { + // clang-format off + const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"COPT", "SREJ"}}); + // clang-format on + rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(), + TestServerDesignatedConnectionId(), client_hello); + + if (GetParam().flags != ENABLED) { + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); + return; + } + + // The StatelessRejector is undecided - proceed with async processing + ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state()); + StatelessRejector::Process(std::move(rejector_), + QuicMakeUnique<ProcessDoneCallback>(this)); + + EXPECT_EQ(StatelessRejector::FAILED, rejector_->state()); + EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, rejector_->error()); +} + +TEST_P(StatelessRejectorTest, ValidChloWithoutSrejSupport) { + // clang-format off + const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"PUBS", pubs_hex_}, + {"NONC", nonc_hex_}, + {"VER\0", ver_hex_}}, + kClientHelloMinimumSize); + // clang-format on + + rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(), + TestServerDesignatedConnectionId(), client_hello); + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); +} + +TEST_P(StatelessRejectorTest, RejectChlo) { + // clang-format off + const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"COPT", "SREJ"}, + {"SCID", scid_hex_}, + {"PUBS", pubs_hex_}, + {"NONC", nonc_hex_}, + {"#004b5453", stk_hex_}, + {"VER\0", ver_hex_}}, + kClientHelloMinimumSize); + // clang-format on + + rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(), + TestServerDesignatedConnectionId(), client_hello); + if (GetParam().flags != ENABLED) { + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); + return; + } + + // The StatelessRejector is undecided - proceed with async processing + ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state()); + StatelessRejector::Process(std::move(rejector_), + QuicMakeUnique<ProcessDoneCallback>(this)); + + ASSERT_EQ(StatelessRejector::REJECTED, rejector_->state()); + const CryptoHandshakeMessage& reply = rejector_->reply(); + EXPECT_EQ(kSREJ, reply.tag()); + QuicTagVector reject_reasons; + EXPECT_EQ(QUIC_NO_ERROR, reply.GetTaglist(kRREJ, &reject_reasons)); + EXPECT_EQ(1u, reject_reasons.size()); + EXPECT_EQ(INVALID_EXPECTED_LEAF_CERTIFICATE, + static_cast<HandshakeFailureReason>(reject_reasons[0])); +} + +TEST_P(StatelessRejectorTest, AcceptChlo) { + const uint64_t xlct = crypto_test_utils::LeafCertHashForTesting(); + const QuicString xlct_hex = + "#" + QuicTextUtils::HexEncode(reinterpret_cast<const char*>(&xlct), + sizeof(xlct)); + // clang-format off + const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, + {"AEAD", "AESG"}, + {"KEXS", "C255"}, + {"COPT", "SREJ"}, + {"SCID", scid_hex_}, + {"PUBS", pubs_hex_}, + {"NONC", nonc_hex_}, + {"#004b5453", stk_hex_}, + {"VER\0", ver_hex_}, + {"XLCT", xlct_hex}}, + kClientHelloMinimumSize); + // clang-format on + + rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(), + TestServerDesignatedConnectionId(), client_hello); + if (GetParam().flags != ENABLED) { + EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state()); + return; + } + + // The StatelessRejector is undecided - proceed with async processing + ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state()); + StatelessRejector::Process(std::move(rejector_), + QuicMakeUnique<ProcessDoneCallback>(this)); + + EXPECT_EQ(StatelessRejector::ACCEPTED, rejector_->state()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc new file mode 100644 index 0000000..f232dc3 --- /dev/null +++ b/quic/core/tls_client_handshaker.cc
@@ -0,0 +1,308 @@ +// Copyright (c) 2017 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/tls_client_handshaker.h" + +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +TlsClientHandshaker::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl( + TlsClientHandshaker* parent) + : parent_(parent) {} + +TlsClientHandshaker::ProofVerifierCallbackImpl::~ProofVerifierCallbackImpl() {} + +void TlsClientHandshaker::ProofVerifierCallbackImpl::Run( + bool ok, + const QuicString& error_details, + std::unique_ptr<ProofVerifyDetails>* details) { + if (parent_ == nullptr) { + return; + } + + parent_->verify_details_ = std::move(*details); + parent_->verify_result_ = ok ? ssl_verify_ok : ssl_verify_invalid; + parent_->state_ = STATE_HANDSHAKE_RUNNING; + parent_->proof_verify_callback_ = nullptr; + parent_->AdvanceHandshake(); +} + +void TlsClientHandshaker::ProofVerifierCallbackImpl::Cancel() { + parent_ = nullptr; +} + +TlsClientHandshaker::TlsClientHandshaker( + QuicCryptoStream* stream, + QuicSession* session, + const QuicServerId& server_id, + ProofVerifier* proof_verifier, + SSL_CTX* ssl_ctx, + std::unique_ptr<ProofVerifyContext> verify_context, + const QuicString& user_agent_id) + : TlsHandshaker(stream, session, ssl_ctx), + server_id_(server_id), + proof_verifier_(proof_verifier), + verify_context_(std::move(verify_context)), + user_agent_id_(user_agent_id), + crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {} + +TlsClientHandshaker::~TlsClientHandshaker() { + if (proof_verify_callback_) { + proof_verify_callback_->Cancel(); + } +} + +// static +bssl::UniquePtr<SSL_CTX> TlsClientHandshaker::CreateSslCtx() { + return TlsHandshaker::CreateSslCtx(); +} + +bool TlsClientHandshaker::CryptoConnect() { + CrypterPair crypters; + CryptoUtils::CreateTlsInitialCrypters( + Perspective::IS_CLIENT, session()->connection()->transport_version(), + session()->connection_id(), &crypters); + session()->connection()->SetEncrypter(ENCRYPTION_NONE, + std::move(crypters.encrypter)); + session()->connection()->SetDecrypter(ENCRYPTION_NONE, + std::move(crypters.decrypter)); + state_ = STATE_HANDSHAKE_RUNNING; + // Configure certificate verification. + // TODO(nharper): This only verifies certs on initial connection, not on + // resumption. Chromium has this callback be a no-op and verifies the + // certificate after the connection is complete. We need to re-verify on + // resumption in case of expiration or revocation/distrust. + SSL_set_custom_verify(ssl(), SSL_VERIFY_PEER, &VerifyCallback); + + // Configure the SSL to be a client. + SSL_set_connect_state(ssl()); + if (SSL_set_tlsext_host_name(ssl(), server_id_.host().c_str()) != 1) { + return false; + } + + // Set the Transport Parameters to send in the ClientHello + if (!SetTransportParameters()) { + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Failed to set Transport Parameters"); + return false; + } + + // Start the handshake. + AdvanceHandshake(); + return session()->connection()->connected(); +} + +bool TlsClientHandshaker::SetTransportParameters() { + TransportParameters params; + params.perspective = Perspective::IS_CLIENT; + params.version = + CreateQuicVersionLabel(session()->supported_versions().front()); + + if (!session()->config()->FillTransportParameters(¶ms)) { + return false; + } + params.google_quic_params->SetStringPiece(kUAID, user_agent_id_); + + std::vector<uint8_t> param_bytes; + return SerializeTransportParameters(params, ¶m_bytes) && + SSL_set_quic_transport_params(ssl(), param_bytes.data(), + param_bytes.size()) == 1; +} + +bool TlsClientHandshaker::ProcessTransportParameters( + QuicString* error_details) { + TransportParameters params; + const uint8_t* param_bytes; + size_t param_bytes_len; + SSL_get_peer_quic_transport_params(ssl(), ¶m_bytes, ¶m_bytes_len); + if (param_bytes_len == 0 || + !ParseTransportParameters(param_bytes, param_bytes_len, + Perspective::IS_SERVER, ¶ms)) { + *error_details = "Unable to parse Transport Parameters"; + return false; + } + + if (params.version != + CreateQuicVersionLabel(session()->connection()->version())) { + *error_details = "Version mismatch detected"; + return false; + } + if (CryptoUtils::ValidateServerHelloVersions( + params.supported_versions, + session()->connection()->server_supported_versions(), + error_details) != QUIC_NO_ERROR || + session()->config()->ProcessTransportParameters( + params, SERVER, error_details) != QUIC_NO_ERROR) { + return false; + } + + session()->OnConfigNegotiated(); + return true; +} + +int TlsClientHandshaker::num_sent_client_hellos() const { + // TODO(nharper): Return a sensible value here. + return 0; +} + +int TlsClientHandshaker::num_scup_messages_received() const { + // SCUP messages aren't sent or received when using the TLS handshake. + return 0; +} + +bool TlsClientHandshaker::WasChannelIDSent() const { + // Channel ID is not used with TLS in QUIC. + return false; +} + +bool TlsClientHandshaker::WasChannelIDSourceCallbackRun() const { + // Channel ID is not used with TLS in QUIC. + return false; +} + +QuicString TlsClientHandshaker::chlo_hash() const { + return ""; +} + +bool TlsClientHandshaker::encryption_established() const { + return encryption_established_; +} + +bool TlsClientHandshaker::handshake_confirmed() const { + return handshake_confirmed_; +} + +const QuicCryptoNegotiatedParameters& +TlsClientHandshaker::crypto_negotiated_params() const { + return *crypto_negotiated_params_; +} + +CryptoMessageParser* TlsClientHandshaker::crypto_message_parser() { + return TlsHandshaker::crypto_message_parser(); +} + +void TlsClientHandshaker::AdvanceHandshake() { + if (state_ == STATE_CONNECTION_CLOSED) { + QUIC_LOG(INFO) + << "TlsClientHandshaker received message after connection closed"; + return; + } + if (state_ == STATE_IDLE) { + CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed"); + return; + } + if (state_ == STATE_HANDSHAKE_COMPLETE) { + // TODO(nharper): Handle post-handshake messages. + return; + } + + QUIC_LOG(INFO) << "TlsClientHandshaker: continuing handshake"; + int rv = SSL_do_handshake(ssl()); + if (rv == 1) { + FinishHandshake(); + return; + } + int ssl_error = SSL_get_error(ssl(), rv); + bool should_close = true; + switch (state_) { + case STATE_HANDSHAKE_RUNNING: + should_close = ssl_error != SSL_ERROR_WANT_READ; + break; + case STATE_CERT_VERIFY_PENDING: + should_close = ssl_error != SSL_ERROR_WANT_CERTIFICATE_VERIFY; + break; + default: + should_close = true; + } + if (should_close && state_ != STATE_CONNECTION_CLOSED) { + // TODO(nharper): Surface error details from the error queue when ssl_error + // is SSL_ERROR_SSL. + QUIC_LOG(WARNING) << "SSL_do_handshake failed; closing connection"; + CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed"); + } +} + +void TlsClientHandshaker::CloseConnection(QuicErrorCode error, + const QuicString& reason_phrase) { + state_ = STATE_CONNECTION_CLOSED; + stream()->CloseConnectionWithDetails(error, reason_phrase); +} + +void TlsClientHandshaker::FinishHandshake() { + QUIC_LOG(INFO) << "Client: handshake finished"; + state_ = STATE_HANDSHAKE_COMPLETE; + + QuicString error_details; + if (!ProcessTransportParameters(&error_details)) { + CloseConnection(QUIC_HANDSHAKE_FAILED, error_details); + return; + } + + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + session()->NeuterUnencryptedData(); + encryption_established_ = true; + handshake_confirmed_ = true; +} + +// static +TlsClientHandshaker* TlsClientHandshaker::HandshakerFromSsl(SSL* ssl) { + return static_cast<TlsClientHandshaker*>( + TlsHandshaker::HandshakerFromSsl(ssl)); +} + +// static +enum ssl_verify_result_t TlsClientHandshaker::VerifyCallback( + SSL* ssl, + uint8_t* out_alert) { + return HandshakerFromSsl(ssl)->VerifyCert(out_alert); +} + +enum ssl_verify_result_t TlsClientHandshaker::VerifyCert(uint8_t* out_alert) { + if (verify_result_ != ssl_verify_retry || + state_ == STATE_CERT_VERIFY_PENDING) { + enum ssl_verify_result_t result = verify_result_; + verify_result_ = ssl_verify_retry; + return result; + } + const STACK_OF(CRYPTO_BUFFER)* cert_chain = SSL_get0_peer_certificates(ssl()); + if (cert_chain == nullptr) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_verify_invalid; + } + // TODO(nharper): Pass the CRYPTO_BUFFERs into the QUIC stack to avoid copies. + std::vector<QuicString> certs; + for (CRYPTO_BUFFER* cert : cert_chain) { + certs.push_back( + QuicString(reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert)), + CRYPTO_BUFFER_len(cert))); + } + + ProofVerifierCallbackImpl* proof_verify_callback = + new ProofVerifierCallbackImpl(this); + + QuicAsyncStatus verify_result = proof_verifier_->VerifyCertChain( + server_id_.host(), certs, verify_context_.get(), + &cert_verify_error_details_, &verify_details_, + std::unique_ptr<ProofVerifierCallback>(proof_verify_callback)); + switch (verify_result) { + case QUIC_SUCCESS: + return ssl_verify_ok; + case QUIC_PENDING: + proof_verify_callback_ = proof_verify_callback; + state_ = STATE_CERT_VERIFY_PENDING; + return ssl_verify_retry; + case QUIC_FAILURE: + default: + QUIC_LOG(INFO) << "Cert chain verification failed: " + << cert_verify_error_details_; + return ssl_verify_invalid; + } +} + +} // namespace quic
diff --git a/quic/core/tls_client_handshaker.h b/quic/core/tls_client_handshaker.h new file mode 100644 index 0000000..3e216f0 --- /dev/null +++ b/quic/core/tls_client_handshaker.h
@@ -0,0 +1,127 @@ +// Copyright (c) 2017 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_CLIENT_HANDSHAKER_H_ +#define QUICHE_QUIC_CORE_TLS_CLIENT_HANDSHAKER_H_ + +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/tls_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// An implementation of QuicCryptoClientStream::HandshakerDelegate which uses +// TLS 1.3 for the crypto handshake protocol. +class QUIC_EXPORT_PRIVATE TlsClientHandshaker + : public QuicCryptoClientStream::HandshakerDelegate, + public TlsHandshaker { + public: + TlsClientHandshaker(QuicCryptoStream* stream, + QuicSession* session, + const QuicServerId& server_id, + ProofVerifier* proof_verifier, + SSL_CTX* ssl_ctx, + std::unique_ptr<ProofVerifyContext> verify_context, + const QuicString& user_agent_id); + TlsClientHandshaker(const TlsClientHandshaker&) = delete; + TlsClientHandshaker& operator=(const TlsClientHandshaker&) = delete; + + ~TlsClientHandshaker() override; + + // Creates and configures an SSL_CTX to be used with a TlsClientHandshaker. + // The caller is responsible for ownership of the newly created struct. + static bssl::UniquePtr<SSL_CTX> CreateSslCtx(); + + // From QuicCryptoClientStream::HandshakerDelegate + bool CryptoConnect() override; + int num_sent_client_hellos() const override; + int num_scup_messages_received() const override; + bool WasChannelIDSent() const override; + bool WasChannelIDSourceCallbackRun() const override; + QuicString chlo_hash() const override; + + // From QuicCryptoClientStream::HandshakerDelegate and TlsHandshaker + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + + private: + // ProofVerifierCallbackImpl handles the result of an asynchronous certificate + // verification operation. + class ProofVerifierCallbackImpl : public ProofVerifierCallback { + public: + explicit ProofVerifierCallbackImpl(TlsClientHandshaker* parent); + ~ProofVerifierCallbackImpl() override; + + // ProofVerifierCallback interface. + void Run(bool ok, + const QuicString& error_details, + std::unique_ptr<ProofVerifyDetails>* details) override; + + // If called, Cancel causes the pending callback to be a no-op. + void Cancel(); + + private: + TlsClientHandshaker* parent_; + }; + + enum State { + STATE_IDLE, + STATE_HANDSHAKE_RUNNING, + STATE_CERT_VERIFY_PENDING, + STATE_HANDSHAKE_COMPLETE, + STATE_CONNECTION_CLOSED, + } state_ = STATE_IDLE; + + bool SetTransportParameters(); + bool ProcessTransportParameters(QuicString* error_details); + void FinishHandshake(); + + void AdvanceHandshake() override; + void CloseConnection(QuicErrorCode error, + const QuicString& reason_phrase) override; + + // Certificate verification functions: + + enum ssl_verify_result_t VerifyCert(uint8_t* out_alert); + // Static method to supply to SSL_set_custom_verify. + static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert); + + // Takes an SSL* |ssl| and returns a pointer to the TlsClientHandshaker that + // it belongs to. This is a specialization of + // TlsHandshaker::HandshakerFromSsl. + static TlsClientHandshaker* HandshakerFromSsl(SSL* ssl); + + QuicServerId server_id_; + + // Objects used for verifying the server's certificate chain. + // |proof_verifier_| is owned by the caller of TlsClientHandshaker's + // constructor. + ProofVerifier* proof_verifier_; + std::unique_ptr<ProofVerifyContext> verify_context_; + + QuicString user_agent_id_; + + // ProofVerifierCallback used for async certificate verification. This object + // is owned by |proof_verifier_|. + ProofVerifierCallbackImpl* proof_verify_callback_ = nullptr; + std::unique_ptr<ProofVerifyDetails> verify_details_; + enum ssl_verify_result_t verify_result_ = ssl_verify_retry; + QuicString cert_verify_error_details_; + + bool encryption_established_ = false; + bool handshake_confirmed_ = false; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> + crypto_negotiated_params_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_TLS_CLIENT_HANDSHAKER_H_
diff --git a/quic/core/tls_handshaker.cc b/quic/core/tls_handshaker.cc new file mode 100644 index 0000000..d6207bb --- /dev/null +++ b/quic/core/tls_handshaker.cc
@@ -0,0 +1,241 @@ +// Copyright (c) 2017 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/tls_handshaker.h" + +#include "third_party/boringssl/src/include/openssl/crypto.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" + +namespace quic { + +namespace { + +class SslIndexSingleton { + public: + static SslIndexSingleton* GetInstance() { + static SslIndexSingleton* instance = new SslIndexSingleton(); + return instance; + } + + int HandshakerIndex() const { return ssl_ex_data_index_handshaker_; } + + private: + SslIndexSingleton() { + CRYPTO_library_init(); + ssl_ex_data_index_handshaker_ = + SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); + CHECK_LE(0, ssl_ex_data_index_handshaker_); + } + + SslIndexSingleton(const SslIndexSingleton&) = delete; + SslIndexSingleton& operator=(const SslIndexSingleton&) = delete; + + int ssl_ex_data_index_handshaker_; +}; + +} // namespace + +TlsHandshaker::TlsHandshaker(QuicCryptoStream* stream, + QuicSession* session, + SSL_CTX* ssl_ctx) + : stream_(stream), session_(session) { + ssl_.reset(SSL_new(ssl_ctx)); + SSL_set_ex_data(ssl(), SslIndexSingleton::GetInstance()->HandshakerIndex(), + this); +} + +TlsHandshaker::~TlsHandshaker() {} + +bool TlsHandshaker::ProcessInput(QuicStringPiece input, EncryptionLevel level) { + if (parser_error_ != QUIC_NO_ERROR) { + return false; + } + // TODO(nharper): Call SSL_quic_read_level(ssl()) and check whether the + // encryption level BoringSSL expects matches the encryption level that we + // just received input at. If they mismatch, should ProcessInput return true + // or false? If data is for a future encryption level, it should be queued for + // later? + if (SSL_provide_quic_data(ssl(), BoringEncryptionLevel(level), + reinterpret_cast<const uint8_t*>(input.data()), + input.size()) != 1) { + // SSL_provide_quic_data can fail for 3 reasons: + // - API misuse (calling it before SSL_set_custom_quic_method, which we + // call in the TlsHandshaker c'tor) + // - Memory exhaustion when appending data to its buffer + // - Data provided at the wrong encryption level + // + // Of these, the only sensible error to handle is data provided at the wrong + // encryption level. + // + // Note: the error provided below has a good-sounding enum value, although + // it doesn't match the description as it's a QUIC Crypto specific error. + parser_error_ = QUIC_INVALID_CRYPTO_MESSAGE_TYPE; + parser_error_detail_ = "TLS stack failed to receive data"; + return false; + } + AdvanceHandshake(); + return true; +} + +// static +bssl::UniquePtr<SSL_CTX> TlsHandshaker::CreateSslCtx() { + CRYPTO_library_init(); + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_with_buffers_method())); + SSL_CTX_set_min_proto_version(ssl_ctx.get(), TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION); + SSL_CTX_set_quic_method(ssl_ctx.get(), &kSslQuicMethod); + return ssl_ctx; +} + +// static +TlsHandshaker* TlsHandshaker::HandshakerFromSsl(const SSL* ssl) { + return reinterpret_cast<TlsHandshaker*>(SSL_get_ex_data( + ssl, SslIndexSingleton::GetInstance()->HandshakerIndex())); +} + +// static +EncryptionLevel TlsHandshaker::QuicEncryptionLevel( + enum ssl_encryption_level_t level) { + switch (level) { + case ssl_encryption_initial: + return ENCRYPTION_NONE; + case ssl_encryption_early_data: + case ssl_encryption_handshake: + return ENCRYPTION_ZERO_RTT; + case ssl_encryption_application: + return ENCRYPTION_FORWARD_SECURE; + } +} + +// static +enum ssl_encryption_level_t TlsHandshaker::BoringEncryptionLevel( + EncryptionLevel level) { + switch (level) { + case ENCRYPTION_NONE: + return ssl_encryption_initial; + case ENCRYPTION_ZERO_RTT: + return ssl_encryption_handshake; + case ENCRYPTION_FORWARD_SECURE: + return ssl_encryption_application; + default: + QUIC_BUG << "Invalid encryption level " << level; + return ssl_encryption_initial; + } +} + +const EVP_MD* TlsHandshaker::Prf() { + return EVP_get_digestbynid( + SSL_CIPHER_get_prf_nid(SSL_get_pending_cipher(ssl()))); +} + +std::unique_ptr<QuicEncrypter> TlsHandshaker::CreateEncrypter( + const std::vector<uint8_t>& pp_secret) { + std::unique_ptr<QuicEncrypter> encrypter = + QuicEncrypter::CreateFromCipherSuite( + SSL_CIPHER_get_id(SSL_get_pending_cipher(ssl()))); + CryptoUtils::SetKeyAndIV(Prf(), pp_secret, encrypter.get()); + return encrypter; +} + +std::unique_ptr<QuicDecrypter> TlsHandshaker::CreateDecrypter( + const std::vector<uint8_t>& pp_secret) { + std::unique_ptr<QuicDecrypter> decrypter = + QuicDecrypter::CreateFromCipherSuite( + SSL_CIPHER_get_id(SSL_get_pending_cipher(ssl()))); + CryptoUtils::SetKeyAndIV(Prf(), pp_secret, decrypter.get()); + return decrypter; +} + +const SSL_QUIC_METHOD TlsHandshaker::kSslQuicMethod{ + TlsHandshaker::SetEncryptionSecretCallback, + TlsHandshaker::WriteMessageCallback, TlsHandshaker::FlushFlightCallback, + TlsHandshaker::SendAlertCallback}; + +// static +int TlsHandshaker::SetEncryptionSecretCallback( + SSL* ssl, + enum ssl_encryption_level_t level, + const uint8_t* read_key, + const uint8_t* write_key, + size_t secret_len) { + // TODO(nharper): replace these vectors and memcpys with spans (which + // unfortunately doesn't yet exist in quic/platform/api). + std::vector<uint8_t> read_secret(secret_len), write_secret(secret_len); + memcpy(read_secret.data(), read_key, secret_len); + memcpy(write_secret.data(), write_key, secret_len); + HandshakerFromSsl(ssl)->SetEncryptionSecret(QuicEncryptionLevel(level), + read_secret, write_secret); + return 1; +} + +// static +int TlsHandshaker::WriteMessageCallback(SSL* ssl, + enum ssl_encryption_level_t level, + const uint8_t* data, + size_t len) { + HandshakerFromSsl(ssl)->WriteMessage( + QuicEncryptionLevel(level), + QuicStringPiece(reinterpret_cast<const char*>(data), len)); + return 1; +} + +// static +int TlsHandshaker::FlushFlightCallback(SSL* ssl) { + HandshakerFromSsl(ssl)->FlushFlight(); + return 1; +} + +// static +int TlsHandshaker::SendAlertCallback(SSL* ssl, + enum ssl_encryption_level_t level, + uint8_t desc) { + HandshakerFromSsl(ssl)->SendAlert(QuicEncryptionLevel(level), desc); + return 1; +} + +void TlsHandshaker::SetEncryptionSecret( + EncryptionLevel level, + const std::vector<uint8_t>& read_secret, + const std::vector<uint8_t>& write_secret) { + std::unique_ptr<QuicEncrypter> encrypter = CreateEncrypter(write_secret); + session()->connection()->SetEncrypter(level, std::move(encrypter)); + if (level != ENCRYPTION_FORWARD_SECURE) { + std::unique_ptr<QuicDecrypter> decrypter = CreateDecrypter(read_secret); + session()->connection()->SetDecrypter(level, std::move(decrypter)); + } else { + // When forward-secure read keys are available, they get set as the + // alternative decrypter instead of the primary decrypter. One reason for + // this is that after the forward secure keys become available, the server + // still has crypto handshake messages to read at the handshake encryption + // level, meaning that both the ENCRYPTION_ZERO_RTT and + // ENCRYPTION_FORWARD_SECURE decrypters need to be available. (Tests also + // assume that an alternative decrypter gets set, so at some point we need + // to call SetAlternativeDecrypter.) + std::unique_ptr<QuicDecrypter> decrypter = CreateDecrypter(read_secret); + session()->connection()->SetAlternativeDecrypter( + level, std::move(decrypter), /*latch_once_used*/ true); + } +} + +void TlsHandshaker::WriteMessage(EncryptionLevel level, QuicStringPiece data) { + stream_->WriteCryptoData(level, data); +} + +void TlsHandshaker::FlushFlight() {} + +void TlsHandshaker::SendAlert(EncryptionLevel level, uint8_t desc) { + // TODO(nharper): Alerts should be sent on the wire as a 16-bit QUIC error + // code computed to be 0x100 | desc (draft-ietf-quic-tls-14, section 4.8). + // This puts it in the range reserved for CRYPTO_ERROR + // (draft-ietf-quic-transport-14, section 11.3). However, according to + // quic_error_codes.h, this QUIC implementation only sends 1-byte error codes + // right now. + CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failure"); +} + +} // namespace quic
diff --git a/quic/core/tls_handshaker.h b/quic/core/tls_handshaker.h new file mode 100644 index 0000000..ceba9a9 --- /dev/null +++ b/quic/core/tls_handshaker.h
@@ -0,0 +1,145 @@ +// Copyright (c) 2017 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_HANDSHAKER_H_ +#define QUICHE_QUIC_CORE_TLS_HANDSHAKER_H_ + +#include "third_party/boringssl/src/include/openssl/base.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.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_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +class QuicCryptoStream; + +// Base class for TlsClientHandshaker and TlsServerHandshaker. TlsHandshaker +// provides functionality common to both the client and server, such as moving +// messages between the TLS stack and the QUIC crypto stream, and handling +// derivation of secrets. +class QUIC_EXPORT_PRIVATE TlsHandshaker : public CryptoMessageParser { + public: + // TlsHandshaker does not take ownership of any of its arguments; they must + // outlive the TlsHandshaker. + TlsHandshaker(QuicCryptoStream* stream, + QuicSession* session, + SSL_CTX* ssl_ctx); + TlsHandshaker(const TlsHandshaker&) = delete; + TlsHandshaker& operator=(const TlsHandshaker&) = delete; + + ~TlsHandshaker() override; + + // From CryptoMessageParser + bool ProcessInput(QuicStringPiece input, EncryptionLevel level) override; + size_t InputBytesRemaining() const override { return 0; } + QuicErrorCode error() const override { return parser_error_; } + const QuicString& error_detail() const override { + return parser_error_detail_; + } + + // From QuicCryptoStream + virtual bool encryption_established() const = 0; + virtual bool handshake_confirmed() const = 0; + virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const = 0; + virtual CryptoMessageParser* crypto_message_parser() { return this; } + + protected: + virtual void AdvanceHandshake() = 0; + + virtual void CloseConnection(QuicErrorCode error, + const QuicString& reason_phrase) = 0; + + // Creates an SSL_CTX and configures it with the options that are appropriate + // for both client and server. The caller is responsible for ownership of the + // newly created struct. + static bssl::UniquePtr<SSL_CTX> CreateSslCtx(); + + // From a given SSL* |ssl|, returns a pointer to the TlsHandshaker that it + // belongs to. This is a helper method for implementing callbacks set on an + // SSL, as it allows the callback function to find the TlsHandshaker instance + // and call an instance method. + static TlsHandshaker* HandshakerFromSsl(const SSL* ssl); + + static EncryptionLevel QuicEncryptionLevel(enum ssl_encryption_level_t level); + static enum ssl_encryption_level_t BoringEncryptionLevel( + EncryptionLevel level); + + // Returns the PRF used by the cipher suite negotiated in the TLS handshake. + const EVP_MD* Prf(); + + std::unique_ptr<QuicEncrypter> CreateEncrypter( + const std::vector<uint8_t>& pp_secret); + std::unique_ptr<QuicDecrypter> CreateDecrypter( + const std::vector<uint8_t>& pp_secret); + + SSL* ssl() { return ssl_.get(); } + QuicCryptoStream* stream() { return stream_; } + QuicSession* session() { return session_; } + + private: + // TlsHandshaker implements SSL_QUIC_METHOD, which provides the interface + // between BoringSSL's TLS stack and a QUIC implementation. + static const SSL_QUIC_METHOD kSslQuicMethod; + + // Pointers to the following 4 functions form |kSslQuicMethod|. Each one + // is a wrapper around the corresponding instance method below. The |ssl| + // argument is used to look up correct TlsHandshaker instance on which to call + // the method. According to the BoringSSL documentation, these functions + // return 0 on error and 1 otherwise; here they never error and thus always + // return 1. + static int SetEncryptionSecretCallback(SSL* ssl, + enum ssl_encryption_level_t level, + const uint8_t* read_key, + const uint8_t* write_key, + size_t secret_len); + 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 alert); + + // SetEncryptionSecret provides the encryption secret to use at a particular + // encryption level. The secrets provided here are the ones from the TLS 1.3 + // key schedule (RFC 8446 section 7.1), in particular the handshake traffic + // secrets and application traffic secrets. For a given secret |secret|, + // |level| indicates which EncryptionLevel it is to be used at, and |is_write| + // indicates whether it is used for encryption or decryption. + void SetEncryptionSecret(EncryptionLevel level, + const std::vector<uint8_t>& read_secret, + const std::vector<uint8_t>& write_secret); + + // WriteMessage is called when there is |data| from the TLS stack ready for + // the QUIC stack to write in a crypto frame. The data must be transmitted at + // encryption level |level|. + void WriteMessage(EncryptionLevel level, QuicStringPiece data); + + // FlushFlight is called to signal that the current flight of + // messages have all been written (via calls to WriteMessage) and can be + // flushed to the underlying transport. + void FlushFlight(); + + // SendAlert causes this TlsHandshaker to close the QUIC connection with an + // error code corresponding to the TLS alert description |desc|. + void SendAlert(EncryptionLevel level, uint8_t desc); + + QuicCryptoStream* stream_; + QuicSession* session_; + + QuicErrorCode parser_error_ = QUIC_NO_ERROR; + QuicString parser_error_detail_; + + bssl::UniquePtr<SSL> ssl_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_TLS_HANDSHAKER_H_
diff --git a/quic/core/tls_handshaker_test.cc b/quic/core/tls_handshaker_test.cc new file mode 100644 index 0000000..2d678dd --- /dev/null +++ b/quic/core/tls_handshaker_test.cc
@@ -0,0 +1,417 @@ +// Copyright (c) 2017 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/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" +#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { +namespace { + +using ::testing::_; + +class FakeProofVerifier : public ProofVerifier { + public: + FakeProofVerifier() + : verifier_(crypto_test_utils::ProofVerifierForTesting()) {} + + QuicAsyncStatus VerifyProof( + const QuicString& hostname, + const uint16_t port, + const QuicString& server_config, + QuicTransportVersion quic_version, + QuicStringPiece chlo_hash, + const std::vector<QuicString>& certs, + const QuicString& cert_sct, + const QuicString& signature, + const ProofVerifyContext* context, + QuicString* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) override { + return verifier_->VerifyProof( + hostname, port, server_config, quic_version, chlo_hash, certs, cert_sct, + signature, context, error_details, details, std::move(callback)); + } + + QuicAsyncStatus VerifyCertChain( + const QuicString& hostname, + const std::vector<QuicString>& certs, + const ProofVerifyContext* context, + QuicString* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) override { + if (!active_) { + return verifier_->VerifyCertChain(hostname, certs, context, error_details, + details, std::move(callback)); + } + pending_ops_.push_back(QuicMakeUnique<VerifyChainPendingOp>( + hostname, certs, context, error_details, details, std::move(callback), + verifier_.get())); + return QUIC_PENDING; + } + + std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override { + return nullptr; + } + + void Activate() { active_ = true; } + + size_t NumPendingCallbacks() const { return pending_ops_.size(); } + + void InvokePendingCallback(size_t n) { + CHECK(NumPendingCallbacks() > n); + pending_ops_[n]->Run(); + auto it = pending_ops_.begin() + n; + pending_ops_.erase(it); + } + + private: + // Implementation of ProofVerifierCallback that fails if the callback is ever + // run. + class FailingProofVerifierCallback : public ProofVerifierCallback { + public: + void Run(bool ok, + const QuicString& error_details, + std::unique_ptr<ProofVerifyDetails>* details) override { + FAIL(); + } + }; + + class VerifyChainPendingOp { + public: + VerifyChainPendingOp(const QuicString& hostname, + const std::vector<QuicString>& certs, + const ProofVerifyContext* context, + QuicString* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback, + ProofVerifier* delegate) + : hostname_(hostname), + certs_(certs), + context_(context), + error_details_(error_details), + details_(details), + callback_(std::move(callback)), + delegate_(delegate) {} + + void Run() { + // FakeProofVerifier depends on crypto_test_utils::ProofVerifierForTesting + // running synchronously. It passes a FailingProofVerifierCallback and + // runs the original callback after asserting that the verification ran + // synchronously. + QuicAsyncStatus status = delegate_->VerifyCertChain( + hostname_, certs_, context_, error_details_, details_, + QuicMakeUnique<FailingProofVerifierCallback>()); + ASSERT_NE(status, QUIC_PENDING); + callback_->Run(status == QUIC_SUCCESS, *error_details_, details_); + } + + private: + QuicString hostname_; + std::vector<QuicString> certs_; + const ProofVerifyContext* context_; + QuicString* error_details_; + std::unique_ptr<ProofVerifyDetails>* details_; + std::unique_ptr<ProofVerifierCallback> callback_; + ProofVerifier* delegate_; + }; + + std::unique_ptr<ProofVerifier> verifier_; + bool active_ = false; + std::vector<std::unique_ptr<VerifyChainPendingOp>> pending_ops_; +}; + +class TestQuicCryptoStream : public QuicCryptoStream { + public: + explicit TestQuicCryptoStream(QuicSession* session) + : QuicCryptoStream(session) {} + + ~TestQuicCryptoStream() override = default; + + virtual TlsHandshaker* handshaker() const = 0; + + bool encryption_established() const override { + return handshaker()->encryption_established(); + } + + bool handshake_confirmed() const override { + return handshaker()->handshake_confirmed(); + } + + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override { + return handshaker()->crypto_negotiated_params(); + } + + CryptoMessageParser* crypto_message_parser() override { + return handshaker()->crypto_message_parser(); + } + + void WriteCryptoData(EncryptionLevel level, QuicStringPiece data) override { + pending_writes_.push_back(std::make_pair(QuicString(data), level)); + } + + const std::vector<std::pair<QuicString, EncryptionLevel>>& pending_writes() { + return pending_writes_; + } + + // Sends the pending frames to |stream| and clears the array of pending + // writes. + void SendCryptoMessagesToPeer(QuicCryptoStream* stream) { + QUIC_LOG(INFO) << "Sending " << pending_writes_.size() << " frames"; + // This is a minimal re-implementation of QuicCryptoStream::OnDataAvailable. + // It doesn't work to call QuicStream::OnStreamFrame because + // QuicCryptoStream::OnDataAvailable currently (as an implementation detail) + // relies on the QuicConnection to know the EncryptionLevel to pass into + // CryptoMessageParser::ProcessInput. Since the crypto messages in this test + // never reach the framer or connection and never get encrypted/decrypted, + // QuicCryptoStream::OnDataAvailable isn't able to call ProcessInput with + // the correct EncryptionLevel. Instead, that can be short-circuited by + // directly calling ProcessInput here. + for (size_t i = 0; i < pending_writes_.size(); ++i) { + if (!stream->crypto_message_parser()->ProcessInput( + pending_writes_[i].first, pending_writes_[i].second)) { + CloseConnectionWithDetails( + stream->crypto_message_parser()->error(), + stream->crypto_message_parser()->error_detail()); + break; + } + } + pending_writes_.clear(); + } + + private: + std::vector<std::pair<QuicString, EncryptionLevel>> pending_writes_; +}; + +class TestQuicCryptoClientStream : public TestQuicCryptoStream { + public: + explicit TestQuicCryptoClientStream(QuicSession* session) + : TestQuicCryptoStream(session), + proof_verifier_(new FakeProofVerifier), + ssl_ctx_(TlsClientHandshaker::CreateSslCtx()), + handshaker_(new TlsClientHandshaker( + this, + session, + QuicServerId("test.example.com", 443, false), + proof_verifier_.get(), + ssl_ctx_.get(), + crypto_test_utils::ProofVerifyContextForTesting(), + "quic-tester")) {} + + ~TestQuicCryptoClientStream() override = default; + + TlsHandshaker* handshaker() const override { return handshaker_.get(); } + + bool CryptoConnect() { return handshaker_->CryptoConnect(); } + + FakeProofVerifier* GetFakeProofVerifier() const { + return proof_verifier_.get(); + } + + private: + std::unique_ptr<FakeProofVerifier> proof_verifier_; + bssl::UniquePtr<SSL_CTX> ssl_ctx_; + std::unique_ptr<TlsClientHandshaker> handshaker_; +}; + +class TestQuicCryptoServerStream : public TestQuicCryptoStream { + public: + TestQuicCryptoServerStream(QuicSession* session, + FakeProofSource* proof_source) + : TestQuicCryptoStream(session), + proof_source_(proof_source), + ssl_ctx_(TlsServerHandshaker::CreateSslCtx()), + handshaker_(new TlsServerHandshaker(this, + session, + ssl_ctx_.get(), + proof_source_)) {} + + ~TestQuicCryptoServerStream() override = default; + + void CancelOutstandingCallbacks() { + handshaker_->CancelOutstandingCallbacks(); + } + + TlsHandshaker* handshaker() const override { return handshaker_.get(); } + + FakeProofSource* GetFakeProofSource() const { return proof_source_; } + + private: + FakeProofSource* proof_source_; + bssl::UniquePtr<SSL_CTX> ssl_ctx_; + std::unique_ptr<TlsServerHandshaker> handshaker_; +}; + +void ExchangeHandshakeMessages(TestQuicCryptoStream* client, + TestQuicCryptoStream* server) { + while (!client->pending_writes().empty() || + !server->pending_writes().empty()) { + client->SendCryptoMessagesToPeer(server); + server->SendCryptoMessagesToPeer(client); + } +} + +class TlsHandshakerTest : public QuicTest { + public: + TlsHandshakerTest() + : client_conn_(new MockQuicConnection(&conn_helper_, + &alarm_factory_, + Perspective::IS_CLIENT)), + server_conn_(new MockQuicConnection(&conn_helper_, + &alarm_factory_, + Perspective::IS_SERVER)), + client_session_(client_conn_, /*create_mock_crypto_stream=*/false), + server_session_(server_conn_, /*create_mock_crypto_stream=*/false) { + client_stream_ = new TestQuicCryptoClientStream(&client_session_); + client_session_.SetCryptoStream(client_stream_); + server_stream_ = + new TestQuicCryptoServerStream(&server_session_, &proof_source_); + server_session_.SetCryptoStream(server_stream_); + client_session_.Initialize(); + server_session_.Initialize(); + EXPECT_FALSE(client_stream_->encryption_established()); + EXPECT_FALSE(client_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream_->encryption_established()); + EXPECT_FALSE(server_stream_->handshake_confirmed()); + } + + MockQuicConnectionHelper conn_helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* client_conn_; + MockQuicConnection* server_conn_; + MockQuicSession client_session_; + MockQuicSession server_session_; + + FakeProofSource proof_source_; + TestQuicCryptoClientStream* client_stream_; + TestQuicCryptoServerStream* server_stream_; +}; + +TEST_F(TlsHandshakerTest, CryptoHandshake) { + EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0); + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + EXPECT_TRUE(client_stream_->handshake_confirmed()); + EXPECT_TRUE(client_stream_->encryption_established()); + EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream_->encryption_established()); +} + +TEST_F(TlsHandshakerTest, HandshakeWithAsyncProofSource) { + EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0); + // Enable FakeProofSource to capture call to ComputeTlsSignature and run it + // asynchronously. + FakeProofSource* proof_source = server_stream_->GetFakeProofSource(); + proof_source->Activate(); + + // Start handshake. + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + ASSERT_EQ(proof_source->NumPendingCallbacks(), 1); + proof_source->InvokePendingCallback(0); + + ExchangeHandshakeMessages(client_stream_, server_stream_); + + EXPECT_TRUE(client_stream_->handshake_confirmed()); + EXPECT_TRUE(client_stream_->encryption_established()); + EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream_->encryption_established()); +} + +TEST_F(TlsHandshakerTest, CancelPendingProofSource) { + EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0); + // Enable FakeProofSource to capture call to ComputeTlsSignature and run it + // asynchronously. + FakeProofSource* proof_source = server_stream_->GetFakeProofSource(); + proof_source->Activate(); + + // Start handshake. + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + ASSERT_EQ(proof_source->NumPendingCallbacks(), 1); + server_stream_ = nullptr; + + proof_source->InvokePendingCallback(0); +} + +TEST_F(TlsHandshakerTest, HandshakeWithAsyncProofVerifier) { + EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0); + EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0); + // Enable FakeProofVerifier to capture call to VerifyCertChain and run it + // asynchronously. + FakeProofVerifier* proof_verifier = client_stream_->GetFakeProofVerifier(); + proof_verifier->Activate(); + + // Start handshake. + client_stream_->CryptoConnect(); + ExchangeHandshakeMessages(client_stream_, server_stream_); + + ASSERT_EQ(proof_verifier->NumPendingCallbacks(), 1u); + proof_verifier->InvokePendingCallback(0); + + ExchangeHandshakeMessages(client_stream_, server_stream_); + + EXPECT_TRUE(client_stream_->handshake_confirmed()); + EXPECT_TRUE(client_stream_->encryption_established()); + EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream_->encryption_established()); +} + +TEST_F(TlsHandshakerTest, ClientConnectionClosedOnTlsError) { + // Have client send ClientHello. + client_stream_->CryptoConnect(); + EXPECT_CALL(*client_conn_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); + + // Send a zero-length ServerHello from server to client. + char bogus_handshake_message[] = { + // Handshake struct (RFC 8446 appendix B.3) + 2, // HandshakeType server_hello + 0, 0, 0, // uint24 length + }; + server_stream_->WriteCryptoData( + ENCRYPTION_NONE, + QuicStringPiece(bogus_handshake_message, + QUIC_ARRAYSIZE(bogus_handshake_message))); + server_stream_->SendCryptoMessagesToPeer(client_stream_); + + EXPECT_FALSE(client_stream_->handshake_confirmed()); +} + +TEST_F(TlsHandshakerTest, ServerConnectionClosedOnTlsError) { + EXPECT_CALL(*server_conn_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); + + // Send a zero-length ClientHello from client to server. + char bogus_handshake_message[] = { + // Handshake struct (RFC 8446 appendix B.3) + 1, // HandshakeType client_hello + 0, 0, 0, // uint24 length + }; + client_stream_->WriteCryptoData( + ENCRYPTION_NONE, + QuicStringPiece(bogus_handshake_message, + QUIC_ARRAYSIZE(bogus_handshake_message))); + client_stream_->SendCryptoMessagesToPeer(server_stream_); + + EXPECT_FALSE(server_stream_->handshake_confirmed()); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc new file mode 100644 index 0000000..02cc441 --- /dev/null +++ b/quic/core/tls_server_handshaker.cc
@@ -0,0 +1,366 @@ +// Copyright (c) 2017 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/tls_server_handshaker.h" + +#include <memory> + +#include "third_party/boringssl/src/include/openssl/pool.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +TlsServerHandshaker::SignatureCallback::SignatureCallback( + TlsServerHandshaker* handshaker) + : handshaker_(handshaker) {} + +void TlsServerHandshaker::SignatureCallback::Run(bool ok, + QuicString signature) { + if (handshaker_ == nullptr) { + return; + } + if (ok) { + handshaker_->cert_verify_sig_ = std::move(signature); + } + State last_state = handshaker_->state_; + handshaker_->state_ = STATE_SIGNATURE_COMPLETE; + handshaker_->signature_callback_ = nullptr; + if (last_state == STATE_SIGNATURE_PENDING) { + handshaker_->AdvanceHandshake(); + } +} + +void TlsServerHandshaker::SignatureCallback::Cancel() { + handshaker_ = nullptr; +} + +const SSL_PRIVATE_KEY_METHOD TlsServerHandshaker::kPrivateKeyMethod{ + &TlsServerHandshaker::PrivateKeySign, + nullptr, // decrypt + &TlsServerHandshaker::PrivateKeyComplete, +}; + +// static +bssl::UniquePtr<SSL_CTX> TlsServerHandshaker::CreateSslCtx() { + bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsHandshaker::CreateSslCtx(); + SSL_CTX_set_tlsext_servername_callback( + ssl_ctx.get(), TlsServerHandshaker::SelectCertificateCallback); + return ssl_ctx; +} + +TlsServerHandshaker::TlsServerHandshaker(QuicCryptoStream* stream, + QuicSession* session, + SSL_CTX* ssl_ctx, + ProofSource* proof_source) + : TlsHandshaker(stream, session, ssl_ctx), + proof_source_(proof_source), + crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) { + CrypterPair crypters; + CryptoUtils::CreateTlsInitialCrypters( + Perspective::IS_SERVER, session->connection()->transport_version(), + session->connection_id(), &crypters); + session->connection()->SetEncrypter(ENCRYPTION_NONE, + std::move(crypters.encrypter)); + session->connection()->SetDecrypter(ENCRYPTION_NONE, + std::move(crypters.decrypter)); + + // Configure the SSL to be a server. + SSL_set_accept_state(ssl()); + + if (!SetTransportParameters()) { + CloseConnection(QUIC_HANDSHAKE_FAILED, + "Failed to set Transport Parameters"); + } +} + +TlsServerHandshaker::~TlsServerHandshaker() { + CancelOutstandingCallbacks(); +} + +void TlsServerHandshaker::CancelOutstandingCallbacks() { + if (signature_callback_) { + signature_callback_->Cancel(); + signature_callback_ = nullptr; + } +} + +bool TlsServerHandshaker::GetBase64SHA256ClientChannelID( + QuicString* output) const { + // Channel ID is not supported when TLS is used in QUIC. + return false; +} + +void TlsServerHandshaker::SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) { + // SCUP messages aren't supported when using the TLS handshake. +} + +uint8_t TlsServerHandshaker::NumHandshakeMessages() const { + // TODO(nharper): Return a sensible value here. + return 0; +} + +uint8_t TlsServerHandshaker::NumHandshakeMessagesWithServerNonces() const { + // TODO(nharper): Return a sensible value here. + return 0; +} + +int TlsServerHandshaker::NumServerConfigUpdateMessagesSent() const { + // SCUP messages aren't supported when using the TLS handshake. + return 0; +} + +const CachedNetworkParameters* +TlsServerHandshaker::PreviousCachedNetworkParams() const { + return nullptr; +} + +bool TlsServerHandshaker::ZeroRttAttempted() const { + // TODO(nharper): Support 0-RTT with TLS 1.3 in QUIC. + return false; +} + +void TlsServerHandshaker::SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) {} + +bool TlsServerHandshaker::ShouldSendExpectCTHeader() const { + return false; +} + +bool TlsServerHandshaker::encryption_established() const { + return encryption_established_; +} + +bool TlsServerHandshaker::handshake_confirmed() const { + return handshake_confirmed_; +} + +const QuicCryptoNegotiatedParameters& +TlsServerHandshaker::crypto_negotiated_params() const { + return *crypto_negotiated_params_; +} + +CryptoMessageParser* TlsServerHandshaker::crypto_message_parser() { + return TlsHandshaker::crypto_message_parser(); +} + +void TlsServerHandshaker::AdvanceHandshake() { + if (state_ == STATE_CONNECTION_CLOSED) { + QUIC_LOG(INFO) << "TlsServerHandshaker received handshake message after " + "connection was closed"; + return; + } + if (state_ == STATE_HANDSHAKE_COMPLETE) { + // TODO(nharper): Handle post-handshake messages. + return; + } + + int rv = SSL_do_handshake(ssl()); + if (rv == 1) { + FinishHandshake(); + return; + } + + int ssl_error = SSL_get_error(ssl(), rv); + bool should_close = true; + switch (state_) { + case STATE_LISTENING: + case STATE_SIGNATURE_COMPLETE: + should_close = ssl_error != SSL_ERROR_WANT_READ; + break; + case STATE_SIGNATURE_PENDING: + should_close = ssl_error != SSL_ERROR_WANT_PRIVATE_KEY_OPERATION; + break; + default: + should_close = true; + } + if (should_close && state_ != STATE_CONNECTION_CLOSED) { + QUIC_LOG(WARNING) << "SSL_do_handshake failed; SSL_get_error returns " + << ssl_error << ", state_ = " << state_; + ERR_print_errors_fp(stderr); + CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed"); + } +} + +void TlsServerHandshaker::CloseConnection(QuicErrorCode error, + const QuicString& reason_phrase) { + state_ = STATE_CONNECTION_CLOSED; + stream()->CloseConnectionWithDetails(error, reason_phrase); +} + +bool TlsServerHandshaker::ProcessTransportParameters( + QuicString* error_details) { + TransportParameters client_params; + const uint8_t* client_params_bytes; + size_t params_bytes_len; + SSL_get_peer_quic_transport_params(ssl(), &client_params_bytes, + ¶ms_bytes_len); + if (params_bytes_len == 0 || + !ParseTransportParameters(client_params_bytes, params_bytes_len, + Perspective::IS_CLIENT, &client_params)) { + *error_details = "Unable to parse Transport Parameters"; + return false; + } + if (CryptoUtils::ValidateClientHelloVersion( + client_params.version, session()->connection()->version(), + session()->supported_versions(), error_details) != QUIC_NO_ERROR || + session()->config()->ProcessTransportParameters( + client_params, CLIENT, error_details) != QUIC_NO_ERROR) { + return false; + } + + session()->OnConfigNegotiated(); + return true; +} + +bool TlsServerHandshaker::SetTransportParameters() { + TransportParameters server_params; + server_params.perspective = Perspective::IS_SERVER; + server_params.supported_versions = + CreateQuicVersionLabelVector(session()->supported_versions()); + server_params.version = + CreateQuicVersionLabel(session()->connection()->version()); + + if (!session()->config()->FillTransportParameters(&server_params)) { + return false; + } + + // TODO(nharper): Provide an actual value for the stateless reset token. + server_params.stateless_reset_token.resize(16); + std::vector<uint8_t> server_params_bytes; + if (!SerializeTransportParameters(server_params, &server_params_bytes) || + SSL_set_quic_transport_params(ssl(), server_params_bytes.data(), + server_params_bytes.size()) != 1) { + return false; + } + return true; +} + +void TlsServerHandshaker::FinishHandshake() { + QUIC_LOG(INFO) << "Server: handshake finished"; + state_ = STATE_HANDSHAKE_COMPLETE; + + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + session()->NeuterUnencryptedData(); + encryption_established_ = true; + handshake_confirmed_ = true; +} + +// static +TlsServerHandshaker* TlsServerHandshaker::HandshakerFromSsl(SSL* ssl) { + return static_cast<TlsServerHandshaker*>( + TlsHandshaker::HandshakerFromSsl(ssl)); +} + +// static +ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign(SSL* ssl, + uint8_t* out, + size_t* out_len, + size_t max_out, + uint16_t sig_alg, + const uint8_t* in, + size_t in_len) { + return HandshakerFromSsl(ssl)->PrivateKeySign( + out, out_len, max_out, sig_alg, + QuicStringPiece(reinterpret_cast<const char*>(in), in_len)); +} + +ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign( + uint8_t* out, + size_t* out_len, + size_t max_out, + uint16_t sig_alg, + QuicStringPiece in) { + signature_callback_ = new SignatureCallback(this); + proof_source_->ComputeTlsSignature( + session()->connection()->self_address(), hostname_, sig_alg, in, + std::unique_ptr<SignatureCallback>(signature_callback_)); + if (state_ == STATE_SIGNATURE_COMPLETE) { + return PrivateKeyComplete(out, out_len, max_out); + } + state_ = STATE_SIGNATURE_PENDING; + return ssl_private_key_retry; +} + +// static +ssl_private_key_result_t TlsServerHandshaker::PrivateKeyComplete( + SSL* ssl, + uint8_t* out, + size_t* out_len, + size_t max_out) { + return HandshakerFromSsl(ssl)->PrivateKeyComplete(out, out_len, max_out); +} + +ssl_private_key_result_t TlsServerHandshaker::PrivateKeyComplete( + uint8_t* out, + size_t* out_len, + size_t max_out) { + if (state_ == STATE_SIGNATURE_PENDING) { + return ssl_private_key_retry; + } + if (cert_verify_sig_.size() > max_out || cert_verify_sig_.empty()) { + return ssl_private_key_failure; + } + *out_len = cert_verify_sig_.size(); + memcpy(out, cert_verify_sig_.data(), *out_len); + cert_verify_sig_.clear(); + cert_verify_sig_.shrink_to_fit(); + return ssl_private_key_success; +} + +// static +int TlsServerHandshaker::SelectCertificateCallback(SSL* ssl, + int* out_alert, + void* arg) { + return HandshakerFromSsl(ssl)->SelectCertificate(out_alert); +} + +int TlsServerHandshaker::SelectCertificate(int* out_alert) { + const char* hostname = SSL_get_servername(ssl(), TLSEXT_NAMETYPE_host_name); + if (hostname) { + hostname_ = hostname; + } else { + QUIC_LOG(INFO) << "No hostname indicated in SNI"; + } + + QuicReferenceCountedPointer<ProofSource::Chain> chain = + proof_source_->GetCertChain(session()->connection()->self_address(), + hostname_); + if (chain->certs.empty()) { + QUIC_LOG(ERROR) << "No certs provided for host '" << hostname_ << "'"; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + std::vector<CRYPTO_BUFFER*> certs; + certs.resize(chain->certs.size()); + for (size_t i = 0; i < certs.size(); i++) { + certs[i] = CRYPTO_BUFFER_new( + reinterpret_cast<const uint8_t*>(chain->certs[i].data()), + chain->certs[i].length(), nullptr); + } + + SSL_set_chain_and_key(ssl(), certs.data(), certs.size(), nullptr, + &kPrivateKeyMethod); + + for (size_t i = 0; i < certs.size(); i++) { + CRYPTO_BUFFER_free(certs[i]); + } + + QuicString error_details; + if (!ProcessTransportParameters(&error_details)) { + CloseConnection(QUIC_HANDSHAKE_FAILED, error_details); + *out_alert = SSL_AD_INTERNAL_ERROR; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + QUIC_LOG(INFO) << "Set " << chain->certs.size() << " certs for server"; + return SSL_TLSEXT_ERR_OK; +} + +} // namespace quic
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h new file mode 100644 index 0000000..c3efc82 --- /dev/null +++ b/quic/core/tls_server_handshaker.h
@@ -0,0 +1,168 @@ +// Copyright (c) 2017 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_SERVER_HANDSHAKER_H_ +#define QUICHE_QUIC_CORE_TLS_SERVER_HANDSHAKER_H_ + +#include "third_party/boringssl/src/include/openssl/pool.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" +#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" +#include "net/third_party/quiche/src/quic/core/tls_handshaker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string.h" + +namespace quic { + +// An implementation of QuicCryptoServerStream::HandshakerDelegate which uses +// TLS 1.3 for the crypto handshake protocol. +class QUIC_EXPORT_PRIVATE TlsServerHandshaker + : public QuicCryptoServerStream::HandshakerDelegate, + public TlsHandshaker { + public: + TlsServerHandshaker(QuicCryptoStream* stream, + QuicSession* session, + SSL_CTX* ssl_ctx, + ProofSource* proof_source); + TlsServerHandshaker(const TlsServerHandshaker&) = delete; + TlsServerHandshaker& operator=(const TlsServerHandshaker&) = delete; + + ~TlsServerHandshaker() override; + + // Creates and configures an SSL_CTX to be used with a TlsServerHandshaker. + // The caller is responsible for ownership of the newly created struct. + static bssl::UniquePtr<SSL_CTX> CreateSslCtx(); + + // From QuicCryptoServerStream::HandshakerDelegate + void CancelOutstandingCallbacks() override; + bool GetBase64SHA256ClientChannelID(QuicString* output) const override; + void SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) override; + uint8_t NumHandshakeMessages() const override; + uint8_t NumHandshakeMessagesWithServerNonces() const override; + int NumServerConfigUpdateMessagesSent() const override; + const CachedNetworkParameters* PreviousCachedNetworkParams() const override; + bool ZeroRttAttempted() const override; + void SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) override; + bool ShouldSendExpectCTHeader() const override; + + // From QuicCryptoServerStream::HandshakerDelegate and TlsHandshaker + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + + // Calls SelectCertificate after looking up the TlsServerHandshaker from + // |ssl|. + static int SelectCertificateCallback(SSL* ssl, int* out_alert, void* arg); + + private: + class SignatureCallback : public ProofSource::SignatureCallback { + public: + explicit SignatureCallback(TlsServerHandshaker* handshaker); + void Run(bool ok, QuicString signature) override; + + // If called, Cancel causes the pending callback to be a no-op. + void Cancel(); + + private: + TlsServerHandshaker* handshaker_; + }; + + enum State { + STATE_LISTENING, + STATE_SIGNATURE_PENDING, + STATE_SIGNATURE_COMPLETE, + STATE_HANDSHAKE_COMPLETE, + STATE_CONNECTION_CLOSED, + }; + + // |kPrivateKeyMethod| is a vtable pointing to PrivateKeySign and + // PrivateKeyComplete used by the TLS stack to compute the signature for the + // CertificateVerify message (using the server's private key). + static const SSL_PRIVATE_KEY_METHOD kPrivateKeyMethod; + + // Called when a new message is received on the crypto stream and is available + // for the TLS stack to read. + void AdvanceHandshake() override; + void CloseConnection(QuicErrorCode error, + const QuicString& reason_phrase) override; + + // Called when the TLS handshake is complete. + void FinishHandshake(); + + void CloseConnection(const QuicString& reason_phrase); + + bool SetTransportParameters(); + bool ProcessTransportParameters(QuicString* error_details); + + // Calls the instance method PrivateKeySign after looking up the + // TlsServerHandshaker from |ssl|. + static ssl_private_key_result_t PrivateKeySign(SSL* ssl, + uint8_t* out, + size_t* out_len, + size_t max_out, + uint16_t sig_alg, + const uint8_t* in, + size_t in_len); + + // Signs |in| using the signature algorithm specified by |sig_alg| (an + // SSL_SIGN_* value). If the signing operation cannot be completed + // synchronously, ssl_private_key_retry is returned. If there is an error + // signing, or if the signature is longer than |max_out|, then + // ssl_private_key_failure is returned. Otherwise, ssl_private_key_success is + // returned with the signature put in |*out| and the length in |*out_len|. + ssl_private_key_result_t PrivateKeySign(uint8_t* out, + size_t* out_len, + size_t max_out, + uint16_t sig_alg, + QuicStringPiece in); + + // Calls the instance method PrivateKeyComplete after looking up the + // TlsServerHandshaker from |ssl|. + static ssl_private_key_result_t PrivateKeyComplete(SSL* ssl, + uint8_t* out, + size_t* out_len, + size_t max_out); + + // When PrivateKeySign returns ssl_private_key_retry, PrivateKeyComplete will + // be called after the async sign operation has completed. PrivateKeyComplete + // puts the resulting signature in |*out| and length in |*out_len|. If the + // length is greater than |max_out| or if there was an error in signing, then + // ssl_private_key_failure is returned. Otherwise, ssl_private_key_success is + // returned. + ssl_private_key_result_t PrivateKeyComplete(uint8_t* out, + size_t* out_len, + size_t max_out); + + // Configures the certificate to use on |ssl_| based on the SNI sent by the + // client. Returns an SSL_TLSEXT_ERR_* value (see + // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_tlsext_servername_callback). + // + // If SelectCertificate returns SSL_TLSEXT_ERR_ALERT_FATAL, then it puts in + // |*out_alert| the TLS alert value that the server will send. + int SelectCertificate(int* out_alert); + + static TlsServerHandshaker* HandshakerFromSsl(SSL* ssl); + + State state_ = STATE_LISTENING; + + ProofSource* proof_source_; + SignatureCallback* signature_callback_ = nullptr; + + QuicString hostname_; + QuicString cert_verify_sig_; + + bool encryption_established_ = false; + bool handshake_confirmed_ = false; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> + crypto_negotiated_params_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_TLS_SERVER_HANDSHAKER_H_
diff --git a/quic/core/uber_quic_stream_id_manager.cc b/quic/core/uber_quic_stream_id_manager.cc new file mode 100644 index 0000000..51ea994 --- /dev/null +++ b/quic/core/uber_quic_stream_id_manager.cc
@@ -0,0 +1,210 @@ +// Copyright (c) 2018 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/uber_quic_stream_id_manager.h" + +#include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" + +namespace quic { +namespace { + +Perspective Reverse(Perspective perspective) { + return perspective == Perspective::IS_SERVER ? Perspective::IS_CLIENT + : Perspective::IS_SERVER; +} + +} // namespace + +UberQuicStreamIdManager::UberQuicStreamIdManager( + QuicSession* session, + size_t max_open_outgoing_streams, + size_t max_open_incoming_streams) + : bidirectional_stream_id_manager_( + session, + QuicUtils::GetFirstBidirectionalStreamId( + session->connection()->transport_version(), + session->perspective()), + session->perspective() == Perspective::IS_SERVER + ? QuicUtils::GetCryptoStreamId( + session->connection()->transport_version()) + : QuicUtils::GetInvalidStreamId( + session->connection()->transport_version()), + QuicUtils::GetFirstBidirectionalStreamId( + session->connection()->transport_version(), + Reverse(session->perspective())), + max_open_outgoing_streams, + max_open_incoming_streams), + unidirectional_stream_id_manager_( + session, + QuicUtils::GetFirstUnidirectionalStreamId( + session->connection()->transport_version(), + session->perspective()), + QuicUtils::GetInvalidStreamId( + session->connection()->transport_version()), + QuicUtils::GetFirstUnidirectionalStreamId( + session->connection()->transport_version(), + Reverse(session->perspective())), + max_open_outgoing_streams, + max_open_incoming_streams) {} + +void UberQuicStreamIdManager::RegisterStaticStream(QuicStreamId id) { + if (QuicUtils::IsBidirectionalStreamId(id)) { + bidirectional_stream_id_manager_.RegisterStaticStream(id); + return; + } + unidirectional_stream_id_manager_.RegisterStaticStream(id); +} + +void UberQuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_streams) { + bidirectional_stream_id_manager_.SetMaxOpenOutgoingStreams(max_streams); + unidirectional_stream_id_manager_.SetMaxOpenOutgoingStreams(max_streams); +} + +void UberQuicStreamIdManager::SetMaxOpenIncomingStreams(size_t max_streams) { + bidirectional_stream_id_manager_.SetMaxOpenIncomingStreams(max_streams); + unidirectional_stream_id_manager_.SetMaxOpenIncomingStreams(max_streams); +} + +bool UberQuicStreamIdManager::CanOpenNextOutgoingBidirectionalStream() { + return bidirectional_stream_id_manager_.CanOpenNextOutgoingStream(); +} + +bool UberQuicStreamIdManager::CanOpenNextOutgoingUnidirectionalStream() { + return unidirectional_stream_id_manager_.CanOpenNextOutgoingStream(); +} + +QuicStreamId UberQuicStreamIdManager::GetNextOutgoingBidirectionalStreamId() { + return bidirectional_stream_id_manager_.GetNextOutgoingStreamId(); +} + +QuicStreamId UberQuicStreamIdManager::GetNextOutgoingUnidirectionalStreamId() { + return unidirectional_stream_id_manager_.GetNextOutgoingStreamId(); +} + +bool UberQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( + QuicStreamId id) { + if (QuicUtils::IsBidirectionalStreamId(id)) { + return bidirectional_stream_id_manager_.MaybeIncreaseLargestPeerStreamId( + id); + } + return unidirectional_stream_id_manager_.MaybeIncreaseLargestPeerStreamId(id); +} + +void UberQuicStreamIdManager::OnStreamClosed(QuicStreamId id) { + if (QuicUtils::IsBidirectionalStreamId(id)) { + bidirectional_stream_id_manager_.OnStreamClosed(id); + return; + } + unidirectional_stream_id_manager_.OnStreamClosed(id); +} + +bool UberQuicStreamIdManager::OnMaxStreamIdFrame( + const QuicMaxStreamIdFrame& frame) { + if (QuicUtils::IsBidirectionalStreamId(frame.max_stream_id)) { + return bidirectional_stream_id_manager_.OnMaxStreamIdFrame(frame); + } + return unidirectional_stream_id_manager_.OnMaxStreamIdFrame(frame); +} + +bool UberQuicStreamIdManager::OnStreamIdBlockedFrame( + const QuicStreamIdBlockedFrame& frame) { + if (QuicUtils::IsBidirectionalStreamId(frame.stream_id)) { + return bidirectional_stream_id_manager_.OnStreamIdBlockedFrame(frame); + } + return unidirectional_stream_id_manager_.OnStreamIdBlockedFrame(frame); +} + +bool UberQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const { + if (QuicUtils::IsBidirectionalStreamId(id)) { + return bidirectional_stream_id_manager_.IsIncomingStream(id); + } + return unidirectional_stream_id_manager_.IsIncomingStream(id); +} + +bool UberQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { + if (QuicUtils::IsBidirectionalStreamId(id)) { + return bidirectional_stream_id_manager_.IsAvailableStream(id); + } + return unidirectional_stream_id_manager_.IsAvailableStream(id); +} + +size_t UberQuicStreamIdManager::GetMaxAllowdIncomingBidirectionalStreams() + const { + return bidirectional_stream_id_manager_.max_allowed_incoming_streams(); +} + +size_t UberQuicStreamIdManager::GetMaxAllowdIncomingUnidirectionalStreams() + const { + return unidirectional_stream_id_manager_.max_allowed_incoming_streams(); +} + +void UberQuicStreamIdManager::SetLargestPeerCreatedStreamId( + QuicStreamId largest_peer_created_stream_id) { + if (QuicUtils::IsBidirectionalStreamId(largest_peer_created_stream_id)) { + bidirectional_stream_id_manager_.set_largest_peer_created_stream_id( + largest_peer_created_stream_id); + return; + } + unidirectional_stream_id_manager_.set_largest_peer_created_stream_id( + largest_peer_created_stream_id); +} + +QuicStreamId UberQuicStreamIdManager::next_outgoing_bidirectional_stream_id() + const { + return bidirectional_stream_id_manager_.next_outgoing_stream_id(); +} + +QuicStreamId UberQuicStreamIdManager::next_outgoing_unidirectional_stream_id() + const { + return unidirectional_stream_id_manager_.next_outgoing_stream_id(); +} + +QuicStreamId +UberQuicStreamIdManager::max_allowed_outgoing_bidirectional_stream_id() const { + return bidirectional_stream_id_manager_.max_allowed_outgoing_stream_id(); +} + +QuicStreamId +UberQuicStreamIdManager::max_allowed_outgoing_unidirectional_stream_id() const { + return unidirectional_stream_id_manager_.max_allowed_outgoing_stream_id(); +} + +size_t UberQuicStreamIdManager::max_allowed_outgoing_bidirectional_streams() + const { + return bidirectional_stream_id_manager_.max_allowed_outgoing_streams(); +} + +size_t UberQuicStreamIdManager::max_allowed_outgoing_unidirectional_streams() + const { + return unidirectional_stream_id_manager_.max_allowed_outgoing_streams(); +} + +QuicStreamId +UberQuicStreamIdManager::actual_max_allowed_incoming_bidirectional_stream_id() + const { + return bidirectional_stream_id_manager_ + .actual_max_allowed_incoming_stream_id(); +} + +QuicStreamId +UberQuicStreamIdManager::actual_max_allowed_incoming_unidirectional_stream_id() + const { + return unidirectional_stream_id_manager_ + .actual_max_allowed_incoming_stream_id(); +} + +QuicStreamId UberQuicStreamIdManager:: + advertised_max_allowed_incoming_bidirectional_stream_id() const { + return bidirectional_stream_id_manager_ + .advertised_max_allowed_incoming_stream_id(); +} + +QuicStreamId UberQuicStreamIdManager:: + advertised_max_allowed_incoming_unidirectional_stream_id() const { + return unidirectional_stream_id_manager_ + .advertised_max_allowed_incoming_stream_id(); +} + +} // namespace quic
diff --git a/quic/core/uber_quic_stream_id_manager.h b/quic/core/uber_quic_stream_id_manager.h new file mode 100644 index 0000000..bf5a588 --- /dev/null +++ b/quic/core/uber_quic_stream_id_manager.h
@@ -0,0 +1,100 @@ +// Copyright (c) 2018 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_UBER_QUIC_STREAM_ID_MANAGER_H_ +#define QUICHE_QUIC_CORE_UBER_QUIC_STREAM_ID_MANAGER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h" + +namespace quic { + +namespace test { +class QuicSessionPeer; +} // namespace test + +class QuicSession; + +// This class comprises two QuicStreamIdManagers, which manage bidirectional and +// unidirectional stream IDs, respectively. +class QUIC_EXPORT_PRIVATE UberQuicStreamIdManager { + public: + UberQuicStreamIdManager(QuicSession* session, + size_t max_open_outgoing_streams, + size_t max_open_incoming_streams); + + // Called when a stream with |stream_id| is registered as a static stream. + void RegisterStaticStream(QuicStreamId id); + + // Initialize the maximum allowed outgoing stream id, number of streams, and + // MAX_STREAM_ID advertisement window. + void SetMaxOpenOutgoingStreams(size_t max_streams); + + // Initialize the maximum allowed incoming stream id and number of streams. + void SetMaxOpenIncomingStreams(size_t max_streams); + + // Returns true if next outgoing bidirectional stream ID can be allocated. + bool CanOpenNextOutgoingBidirectionalStream(); + + // Returns true if next outgoing unidirectional stream ID can be allocated. + bool CanOpenNextOutgoingUnidirectionalStream(); + + // Returns the next outgoing bidirectional stream id. + QuicStreamId GetNextOutgoingBidirectionalStreamId(); + + // Returns the next outgoing unidirectional stream id. + QuicStreamId GetNextOutgoingUnidirectionalStreamId(); + + // Returns true if allow to open the incoming |id|. + bool MaybeIncreaseLargestPeerStreamId(QuicStreamId id); + + // Called when |id| is released. + void OnStreamClosed(QuicStreamId id); + + // Called when a MAX_STREAM_ID frame is received. + bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame); + + // Called when a STREAM_ID_BLOCKED frame is received. + bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame); + + // Return true if |id| is peer initiated. + bool IsIncomingStream(QuicStreamId id) const; + + // Returns true if |id| is still available. + bool IsAvailableStream(QuicStreamId id) const; + + size_t GetMaxAllowdIncomingBidirectionalStreams() const; + + size_t GetMaxAllowdIncomingUnidirectionalStreams() const; + + void SetLargestPeerCreatedStreamId( + QuicStreamId largest_peer_created_stream_id); + + QuicStreamId next_outgoing_bidirectional_stream_id() const; + QuicStreamId next_outgoing_unidirectional_stream_id() const; + + QuicStreamId max_allowed_outgoing_bidirectional_stream_id() const; + QuicStreamId max_allowed_outgoing_unidirectional_stream_id() const; + + size_t max_allowed_outgoing_bidirectional_streams() const; + size_t max_allowed_outgoing_unidirectional_streams() const; + + QuicStreamId actual_max_allowed_incoming_bidirectional_stream_id() const; + QuicStreamId actual_max_allowed_incoming_unidirectional_stream_id() const; + + QuicStreamId advertised_max_allowed_incoming_bidirectional_stream_id() const; + QuicStreamId advertised_max_allowed_incoming_unidirectional_stream_id() const; + + private: + friend class test::QuicSessionPeer; + + // Manages stream IDs of bidirectional streams. + QuicStreamIdManager bidirectional_stream_id_manager_; + + // Manages stream IDs of unidirectional streams. + QuicStreamIdManager unidirectional_stream_id_manager_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_UBER_QUIC_STREAM_ID_MANAGER_H_
diff --git a/quic/core/uber_quic_stream_id_manager_test.cc b/quic/core/uber_quic_stream_id_manager_test.cc new file mode 100644 index 0000000..353e35d --- /dev/null +++ b/quic/core/uber_quic_stream_id_manager_test.cc
@@ -0,0 +1,322 @@ +// Copyright (c) 2018 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/uber_quic_stream_id_manager.h" + +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +using testing::_; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +class UberQuicStreamIdManagerTest : public QuicTestWithParam<Perspective> { + public: + bool SaveControlFrame(const QuicFrame& frame) { + frame_ = frame; + return true; + } + + protected: + UberQuicStreamIdManagerTest() + : connection_(new MockQuicConnection( + &helper_, + &alarm_factory_, + GetParam(), + ParsedQuicVersionVector( + {{PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99}}))) { + session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_); + manager_ = QuicSessionPeer::v99_streamid_manager(session_.get()); + } + + QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { + return QuicUtils::GetFirstBidirectionalStreamId( + connection_->transport_version(), Perspective::IS_CLIENT) + + kV99StreamIdIncrement * n; + } + + QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) { + return QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_CLIENT) + + kV99StreamIdIncrement * n; + } + + QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { + return QuicUtils::GetFirstBidirectionalStreamId( + connection_->transport_version(), Perspective::IS_SERVER) + + kV99StreamIdIncrement * n; + } + + QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) { + return QuicUtils::GetFirstUnidirectionalStreamId( + connection_->transport_version(), Perspective::IS_SERVER) + + kV99StreamIdIncrement * n; + } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + MockQuicConnection* connection_; + std::unique_ptr<StrictMock<MockQuicSession>> session_; + UberQuicStreamIdManager* manager_; + QuicFrame frame_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, UberQuicStreamIdManagerTest, + ::testing::ValuesIn({Perspective::IS_CLIENT, + Perspective::IS_SERVER})); + +TEST_P(UberQuicStreamIdManagerTest, Initialization) { + if (GetParam() == Perspective::IS_SERVER) { + EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0), + manager_->next_outgoing_bidirectional_stream_id()); + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0), + manager_->next_outgoing_unidirectional_stream_id()); + } else { + EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0), + manager_->next_outgoing_bidirectional_stream_id()); + EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(0), + manager_->next_outgoing_unidirectional_stream_id()); + } +} + +TEST_P(UberQuicStreamIdManagerTest, RegisterStaticStream) { + QuicStreamId first_incoming_bidirectional_stream_id = + GetParam() == Perspective::IS_SERVER + ? GetNthClientInitiatedBidirectionalId(0) + : GetNthServerInitiatedBidirectionalId(0); + QuicStreamId first_incoming_unidirectional_stream_id = + GetParam() == Perspective::IS_SERVER + ? GetNthClientInitiatedUnidirectionalId(0) + : GetNthServerInitiatedUnidirectionalId(0); + + QuicStreamId actual_max_allowed_incoming_bidirectional_stream_id = + manager_->actual_max_allowed_incoming_bidirectional_stream_id(); + QuicStreamId actual_max_allowed_incoming_unidirectional_stream_id = + manager_->actual_max_allowed_incoming_unidirectional_stream_id(); + manager_->RegisterStaticStream(first_incoming_bidirectional_stream_id); + // Verify actual_max_allowed_incoming_bidirectional_stream_id increases. + EXPECT_EQ(actual_max_allowed_incoming_bidirectional_stream_id + + kV99StreamIdIncrement, + manager_->actual_max_allowed_incoming_bidirectional_stream_id()); + // Verify actual_max_allowed_incoming_unidirectional_stream_id does not + // change. + EXPECT_EQ(actual_max_allowed_incoming_unidirectional_stream_id, + manager_->actual_max_allowed_incoming_unidirectional_stream_id()); + + manager_->RegisterStaticStream(first_incoming_unidirectional_stream_id); + EXPECT_EQ(actual_max_allowed_incoming_bidirectional_stream_id + + kV99StreamIdIncrement, + manager_->actual_max_allowed_incoming_bidirectional_stream_id()); + EXPECT_EQ(actual_max_allowed_incoming_unidirectional_stream_id + + kV99StreamIdIncrement, + manager_->actual_max_allowed_incoming_unidirectional_stream_id()); +} + +TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreams) { + const size_t kNumMaxOutgoingStream = 123; + manager_->SetMaxOpenOutgoingStreams(kNumMaxOutgoingStream); + EXPECT_EQ(kNumMaxOutgoingStream, + manager_->max_allowed_outgoing_bidirectional_streams()); + EXPECT_EQ(kNumMaxOutgoingStream, + manager_->max_allowed_outgoing_unidirectional_streams()); +} + +TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenIncomingStreams) { + const size_t kNumMaxIncomingStreams = 456; + manager_->SetMaxOpenIncomingStreams(kNumMaxIncomingStreams); + EXPECT_EQ(kNumMaxIncomingStreams, + manager_->GetMaxAllowdIncomingBidirectionalStreams()); + EXPECT_EQ(kNumMaxIncomingStreams, + manager_->GetMaxAllowdIncomingUnidirectionalStreams()); + EXPECT_EQ( + manager_->actual_max_allowed_incoming_bidirectional_stream_id(), + manager_->advertised_max_allowed_incoming_bidirectional_stream_id()); + EXPECT_EQ( + manager_->actual_max_allowed_incoming_unidirectional_stream_id(), + manager_->advertised_max_allowed_incoming_unidirectional_stream_id()); + + QuicStreamId first_incoming_bidirectional_stream_id = + GetParam() == Perspective::IS_SERVER + ? GetNthClientInitiatedBidirectionalId(0) + : GetNthServerInitiatedBidirectionalId(0); + QuicStreamId first_incoming_unidirectional_stream_id = + GetParam() == Perspective::IS_SERVER + ? GetNthClientInitiatedUnidirectionalId(0) + : GetNthServerInitiatedUnidirectionalId(0); + EXPECT_EQ(first_incoming_bidirectional_stream_id + + (kNumMaxIncomingStreams - 1) * kV99StreamIdIncrement, + manager_->actual_max_allowed_incoming_bidirectional_stream_id()); + EXPECT_EQ(first_incoming_unidirectional_stream_id + + (kNumMaxIncomingStreams - 1) * kV99StreamIdIncrement, + manager_->actual_max_allowed_incoming_unidirectional_stream_id()); +} + +TEST_P(UberQuicStreamIdManagerTest, GetNextOutgoingStreamId) { + if (GetParam() == Perspective::IS_SERVER) { + EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0), + manager_->GetNextOutgoingBidirectionalStreamId()); + EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1), + manager_->GetNextOutgoingBidirectionalStreamId()); + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0), + manager_->GetNextOutgoingUnidirectionalStreamId()); + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(1), + manager_->GetNextOutgoingUnidirectionalStreamId()); + } else { + EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0), + manager_->GetNextOutgoingBidirectionalStreamId()); + EXPECT_EQ(GetNthClientInitiatedBidirectionalId(1), + manager_->GetNextOutgoingBidirectionalStreamId()); + EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(0), + manager_->GetNextOutgoingUnidirectionalStreamId()); + EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(1), + manager_->GetNextOutgoingUnidirectionalStreamId()); + } +} + +TEST_P(UberQuicStreamIdManagerTest, AvailableStreams) { + if (GetParam() == Perspective::IS_SERVER) { + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId( + GetNthClientInitiatedBidirectionalId(3))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthClientInitiatedBidirectionalId(1))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthClientInitiatedBidirectionalId(2))); + + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId( + GetNthClientInitiatedUnidirectionalId(3))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthClientInitiatedUnidirectionalId(1))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthClientInitiatedUnidirectionalId(2))); + } else { + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId( + GetNthServerInitiatedBidirectionalId(3))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthServerInitiatedBidirectionalId(1))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthServerInitiatedBidirectionalId(2))); + + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId( + GetNthServerInitiatedUnidirectionalId(3))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthServerInitiatedUnidirectionalId(1))); + EXPECT_TRUE( + manager_->IsAvailableStream(GetNthServerInitiatedUnidirectionalId(2))); + } +} + +TEST_P(UberQuicStreamIdManagerTest, MaybeIncreaseLargestPeerStreamId) { + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId( + manager_->actual_max_allowed_incoming_bidirectional_stream_id())); + EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId( + manager_->actual_max_allowed_incoming_unidirectional_stream_id())); + + QuicString error_details = GetParam() == Perspective::IS_SERVER + ? "Stream id 404 above 400" + : "Stream id 401 above 397"; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _)); + EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId( + manager_->actual_max_allowed_incoming_bidirectional_stream_id() + + kV99StreamIdIncrement)); + error_details = GetParam() == Perspective::IS_SERVER + ? "Stream id 402 above 398" + : "Stream id 403 above 399"; + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _)); + EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId( + manager_->actual_max_allowed_incoming_unidirectional_stream_id() + + kV99StreamIdIncrement)); +} + +TEST_P(UberQuicStreamIdManagerTest, OnMaxStreamIdFrame) { + QuicStreamId max_allowed_outgoing_bidirectional_stream_id = + manager_->max_allowed_outgoing_bidirectional_stream_id(); + QuicStreamId max_allowed_outgoing_unidirectional_stream_id = + manager_->max_allowed_outgoing_unidirectional_stream_id(); + + QuicMaxStreamIdFrame frame(kInvalidControlFrameId, + max_allowed_outgoing_bidirectional_stream_id); + EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame)); + EXPECT_EQ(max_allowed_outgoing_bidirectional_stream_id, + manager_->max_allowed_outgoing_bidirectional_stream_id()); + frame.max_stream_id = max_allowed_outgoing_unidirectional_stream_id; + EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame)); + EXPECT_EQ(max_allowed_outgoing_unidirectional_stream_id, + manager_->max_allowed_outgoing_unidirectional_stream_id()); + + frame.max_stream_id = + max_allowed_outgoing_bidirectional_stream_id + kV99StreamIdIncrement; + EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame)); + EXPECT_EQ( + max_allowed_outgoing_bidirectional_stream_id + kV99StreamIdIncrement, + manager_->max_allowed_outgoing_bidirectional_stream_id()); + EXPECT_EQ(max_allowed_outgoing_unidirectional_stream_id, + manager_->max_allowed_outgoing_unidirectional_stream_id()); + + frame.max_stream_id = + max_allowed_outgoing_unidirectional_stream_id + kV99StreamIdIncrement; + EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame)); + EXPECT_EQ( + max_allowed_outgoing_bidirectional_stream_id + kV99StreamIdIncrement, + manager_->max_allowed_outgoing_bidirectional_stream_id()); + EXPECT_EQ( + max_allowed_outgoing_unidirectional_stream_id + kV99StreamIdIncrement, + manager_->max_allowed_outgoing_unidirectional_stream_id()); +} + +TEST_P(UberQuicStreamIdManagerTest, OnStreamIdBlockedFrame) { + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly( + Invoke(this, &UberQuicStreamIdManagerTest::SaveControlFrame)); + + QuicStreamId stream_id = + manager_->advertised_max_allowed_incoming_bidirectional_stream_id() - + kV99StreamIdIncrement; + QuicStreamIdBlockedFrame frame(kInvalidControlFrameId, stream_id); + session_->OnStreamIdBlockedFrame(frame); + EXPECT_EQ(MAX_STREAM_ID_FRAME, frame_.type); + EXPECT_EQ(manager_->actual_max_allowed_incoming_bidirectional_stream_id(), + frame_.max_stream_id_frame.max_stream_id); + + frame.stream_id = + manager_->advertised_max_allowed_incoming_unidirectional_stream_id() - + kV99StreamIdIncrement; + session_->OnStreamIdBlockedFrame(frame); + EXPECT_EQ(MAX_STREAM_ID_FRAME, frame_.type); + EXPECT_EQ(manager_->actual_max_allowed_incoming_unidirectional_stream_id(), + frame_.max_stream_id_frame.max_stream_id); +} + +TEST_P(UberQuicStreamIdManagerTest, IsIncomingStream) { + if (GetParam() == Perspective::IS_SERVER) { + EXPECT_TRUE( + manager_->IsIncomingStream(GetNthClientInitiatedBidirectionalId(0))); + EXPECT_TRUE( + manager_->IsIncomingStream(GetNthClientInitiatedUnidirectionalId(0))); + EXPECT_FALSE( + manager_->IsIncomingStream(GetNthServerInitiatedBidirectionalId(0))); + EXPECT_FALSE( + manager_->IsIncomingStream(GetNthServerInitiatedUnidirectionalId(0))); + } else { + EXPECT_FALSE( + manager_->IsIncomingStream(GetNthClientInitiatedBidirectionalId(0))); + EXPECT_FALSE( + manager_->IsIncomingStream(GetNthClientInitiatedUnidirectionalId(0))); + EXPECT_TRUE( + manager_->IsIncomingStream(GetNthServerInitiatedBidirectionalId(0))); + EXPECT_TRUE( + manager_->IsIncomingStream(GetNthServerInitiatedUnidirectionalId(0))); + } +} + +} // namespace +} // namespace test +} // namespace quic