| // 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 "quic/core/quic_versions.h" |
| |
| #include <string> |
| |
| #include "absl/base/macros.h" |
| #include "absl/strings/numbers.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_split.h" |
| #include "quic/core/crypto/quic_random.h" |
| #include "quic/core/quic_tag.h" |
| #include "quic/core/quic_types.h" |
| #include "quic/platform/api/quic_bug_tracker.h" |
| #include "quic/platform/api/quic_flag_utils.h" |
| #include "quic/platform/api/quic_flags.h" |
| #include "quic/platform/api/quic_logging.h" |
| #include "common/platform/api/quiche_text_utils.h" |
| #include "common/quiche_endian.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); |
| } |
| |
| QuicVersionLabel CreateRandomVersionLabelForNegotiation() { |
| QuicVersionLabel result; |
| if (!GetQuicFlag(FLAGS_quic_disable_version_negotiation_grease_randomness)) { |
| QuicRandom::GetInstance()->RandBytes(&result, sizeof(result)); |
| } else { |
| result = MakeVersionLabel(0xd1, 0x57, 0x38, 0x3f); |
| } |
| result &= 0xf0f0f0f0; |
| result |= 0x0a0a0a0a; |
| return result; |
| } |
| |
| void SetVersionFlag(const ParsedQuicVersion& version, bool should_enable) { |
| static_assert(SupportedVersions().size() == 6u, |
| "Supported versions out of sync"); |
| const bool enable = should_enable; |
| const bool disable = !should_enable; |
| if (version == ParsedQuicVersion::RFCv1()) { |
| SetQuicReloadableFlag(quic_enable_version_rfcv1, enable); |
| } else if (version == ParsedQuicVersion::Draft29()) { |
| SetQuicReloadableFlag(quic_disable_version_draft_29, disable); |
| } else if (version == ParsedQuicVersion::T051()) { |
| SetQuicReloadableFlag(quic_disable_version_t051, disable); |
| } else if (version == ParsedQuicVersion::Q050()) { |
| SetQuicReloadableFlag(quic_disable_version_q050, disable); |
| } else if (version == ParsedQuicVersion::Q046()) { |
| SetQuicReloadableFlag(quic_disable_version_q046, disable); |
| } else if (version == ParsedQuicVersion::Q043()) { |
| SetQuicReloadableFlag(quic_disable_version_q043, disable); |
| } else { |
| QUIC_BUG << "Cannot " << (enable ? "en" : "dis") << "able version " |
| << version; |
| } |
| } |
| |
| } // namespace |
| |
| bool ParsedQuicVersion::IsKnown() const { |
| QUICHE_DCHECK(ParsedQuicVersionIsValid(handshake_protocol, transport_version)) |
| << QuicVersionToString(transport_version) << " " |
| << HandshakeProtocolToString(handshake_protocol); |
| return transport_version != QUIC_VERSION_UNSUPPORTED; |
| } |
| |
| bool ParsedQuicVersion::KnowsWhichDecrypterToUse() const { |
| QUICHE_DCHECK(IsKnown()); |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool ParsedQuicVersion::UsesInitialObfuscators() const { |
| QUICHE_DCHECK(IsKnown()); |
| // Initial obfuscators were added in version 50. |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool ParsedQuicVersion::AllowsLowFlowControlLimits() const { |
| QUICHE_DCHECK(IsKnown()); |
| // Low flow-control limits are used for all IETF versions. |
| return UsesHttp3(); |
| } |
| |
| bool ParsedQuicVersion::HasHeaderProtection() const { |
| QUICHE_DCHECK(IsKnown()); |
| // Header protection was added in version 50. |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool ParsedQuicVersion::SupportsRetry() const { |
| QUICHE_DCHECK(IsKnown()); |
| // Retry was added in version 47. |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool ParsedQuicVersion::SendsVariableLengthPacketNumberInLongHeader() const { |
| QUICHE_DCHECK(IsKnown()); |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool ParsedQuicVersion::AllowsVariableLengthConnectionIds() const { |
| QUICHE_DCHECK(IsKnown()); |
| return VersionAllowsVariableLengthConnectionIds(transport_version); |
| } |
| |
| bool ParsedQuicVersion::SupportsClientConnectionIds() const { |
| QUICHE_DCHECK(IsKnown()); |
| // Client connection IDs were added in version 49. |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool ParsedQuicVersion::HasLengthPrefixedConnectionIds() const { |
| QUICHE_DCHECK(IsKnown()); |
| return VersionHasLengthPrefixedConnectionIds(transport_version); |
| } |
| |
| bool ParsedQuicVersion::SupportsAntiAmplificationLimit() const { |
| QUICHE_DCHECK(IsKnown()); |
| // The anti-amplification limit is used for all IETF versions. |
| return UsesHttp3(); |
| } |
| |
| bool ParsedQuicVersion::CanSendCoalescedPackets() const { |
| QUICHE_DCHECK(IsKnown()); |
| return HasLongHeaderLengths() && UsesTls(); |
| } |
| |
| bool ParsedQuicVersion::SupportsGoogleAltSvcFormat() const { |
| QUICHE_DCHECK(IsKnown()); |
| return VersionSupportsGoogleAltSvcFormat(transport_version); |
| } |
| |
| bool ParsedQuicVersion::HasIetfInvariantHeader() const { |
| QUICHE_DCHECK(IsKnown()); |
| return VersionHasIetfInvariantHeader(transport_version); |
| } |
| |
| bool ParsedQuicVersion::SupportsMessageFrames() const { |
| QUICHE_DCHECK(IsKnown()); |
| return VersionSupportsMessageFrames(transport_version); |
| } |
| |
| bool ParsedQuicVersion::UsesHttp3() const { |
| QUICHE_DCHECK(IsKnown()); |
| return VersionUsesHttp3(transport_version); |
| } |
| |
| bool ParsedQuicVersion::HasLongHeaderLengths() const { |
| QUICHE_DCHECK(IsKnown()); |
| return QuicVersionHasLongHeaderLengths(transport_version); |
| } |
| |
| bool ParsedQuicVersion::UsesCryptoFrames() const { |
| QUICHE_DCHECK(IsKnown()); |
| return QuicVersionUsesCryptoFrames(transport_version); |
| } |
| |
| bool ParsedQuicVersion::HasIetfQuicFrames() const { |
| QUICHE_DCHECK(IsKnown()); |
| return VersionHasIetfQuicFrames(transport_version); |
| } |
| |
| bool ParsedQuicVersion::UsesLegacyTlsExtension() const { |
| QUICHE_DCHECK(IsKnown()); |
| return UsesTls() && transport_version <= QUIC_VERSION_IETF_DRAFT_29; |
| } |
| |
| bool ParsedQuicVersion::UsesTls() const { |
| QUICHE_DCHECK(IsKnown()); |
| return handshake_protocol == PROTOCOL_TLS1_3; |
| } |
| |
| bool ParsedQuicVersion::UsesQuicCrypto() const { |
| QUICHE_DCHECK(IsKnown()); |
| return handshake_protocol == PROTOCOL_QUIC_CRYPTO; |
| } |
| |
| bool VersionHasLengthPrefixedConnectionIds( |
| QuicTransportVersion transport_version) { |
| QUICHE_DCHECK(transport_version != QUIC_VERSION_UNSUPPORTED); |
| // Length-prefixed connection IDs were added in version 49. |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version) { |
| os << ParsedQuicVersionToString(version); |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, |
| const ParsedQuicVersionVector& versions) { |
| os << ParsedQuicVersionVectorToString(versions); |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, |
| const QuicVersionLabelVector& version_labels) { |
| os << QuicVersionLabelVectorToString(version_labels); |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, |
| const QuicTransportVersionVector& transport_versions) { |
| os << QuicTransportVersionVectorToString(transport_versions); |
| return os; |
| } |
| |
| QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { |
| static_assert(SupportedVersions().size() == 6u, |
| "Supported versions out of sync"); |
| if (parsed_version == ParsedQuicVersion::RFCv1()) { |
| return MakeVersionLabel(0x00, 0x00, 0x00, 0x01); |
| } else if (parsed_version == ParsedQuicVersion::Draft29()) { |
| return MakeVersionLabel(0xff, 0x00, 0x00, 29); |
| } else if (parsed_version == ParsedQuicVersion::T051()) { |
| return MakeVersionLabel('T', '0', '5', '1'); |
| } else if (parsed_version == ParsedQuicVersion::Q050()) { |
| return MakeVersionLabel('Q', '0', '5', '0'); |
| } else if (parsed_version == ParsedQuicVersion::Q046()) { |
| return MakeVersionLabel('Q', '0', '4', '6'); |
| } else if (parsed_version == ParsedQuicVersion::Q043()) { |
| return MakeVersionLabel('Q', '0', '4', '3'); |
| } else if (parsed_version == ParsedQuicVersion::ReservedForNegotiation()) { |
| return CreateRandomVersionLabelForNegotiation(); |
| } |
| QUIC_BUG << "Unsupported version " |
| << QuicVersionToString(parsed_version.transport_version) << " " |
| << HandshakeProtocolToString(parsed_version.handshake_protocol); |
| 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; |
| } |
| |
| ParsedQuicVersionVector AllSupportedVersionsWithQuicCrypto() { |
| ParsedQuicVersionVector versions; |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { |
| if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { |
| versions.push_back(version); |
| } |
| } |
| QUIC_BUG_IF(versions.empty()) << "No version with QUIC crypto found."; |
| return versions; |
| } |
| |
| ParsedQuicVersionVector CurrentSupportedVersionsWithQuicCrypto() { |
| ParsedQuicVersionVector versions; |
| for (const ParsedQuicVersion& version : CurrentSupportedVersions()) { |
| if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { |
| versions.push_back(version); |
| } |
| } |
| QUIC_BUG_IF(versions.empty()) << "No version with QUIC crypto found."; |
| return versions; |
| } |
| |
| ParsedQuicVersionVector AllSupportedVersionsWithTls() { |
| ParsedQuicVersionVector versions; |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { |
| if (version.UsesTls()) { |
| versions.push_back(version); |
| } |
| } |
| QUIC_BUG_IF(versions.empty()) << "No version with TLS handshake found."; |
| return versions; |
| } |
| |
| ParsedQuicVersionVector CurrentSupportedVersionsWithTls() { |
| ParsedQuicVersionVector versions; |
| for (const ParsedQuicVersion& version : CurrentSupportedVersions()) { |
| if (version.UsesTls()) { |
| versions.push_back(version); |
| } |
| } |
| QUIC_BUG_IF(versions.empty()) << "No version with TLS handshake found."; |
| return versions; |
| } |
| |
| ParsedQuicVersion ParseQuicVersionLabel(QuicVersionLabel version_label) { |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { |
| if (version_label == CreateQuicVersionLabel(version)) { |
| return version; |
| } |
| } |
| // Reading from the client so this should not be considered an ERROR. |
| QUIC_DLOG(INFO) << "Unsupported QuicVersionLabel version: " |
| << QuicVersionLabelToString(version_label); |
| return UnsupportedQuicVersion(); |
| } |
| |
| ParsedQuicVersion ParseQuicVersionString(absl::string_view version_string) { |
| if (version_string.empty()) { |
| return UnsupportedQuicVersion(); |
| } |
| int quic_version_number = 0; |
| const ParsedQuicVersionVector supported_versions = AllSupportedVersions(); |
| if (absl::SimpleAtoi(version_string, &quic_version_number) && |
| quic_version_number > 0) { |
| QuicTransportVersion transport_version = |
| static_cast<QuicTransportVersion>(quic_version_number); |
| if (!ParsedQuicVersionIsValid(PROTOCOL_QUIC_CRYPTO, transport_version)) { |
| return UnsupportedQuicVersion(); |
| } |
| ParsedQuicVersion version(PROTOCOL_QUIC_CRYPTO, transport_version); |
| if (std::find(supported_versions.begin(), supported_versions.end(), |
| version) != supported_versions.end()) { |
| return version; |
| } |
| return UnsupportedQuicVersion(); |
| } |
| for (const ParsedQuicVersion& version : supported_versions) { |
| if (version_string == ParsedQuicVersionToString(version) || |
| version_string == AlpnForVersion(version) || |
| (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO && |
| version_string == QuicVersionToString(version.transport_version))) { |
| return version; |
| } |
| } |
| for (const ParsedQuicVersion& version : supported_versions) { |
| if (version.UsesHttp3() && |
| version_string == |
| QuicVersionLabelToString(CreateQuicVersionLabel(version))) { |
| return version; |
| } |
| } |
| // Reading from the client so this should not be considered an ERROR. |
| QUIC_DLOG(INFO) << "Unsupported QUIC version string: \"" << version_string |
| << "\"."; |
| return UnsupportedQuicVersion(); |
| } |
| |
| ParsedQuicVersionVector ParseQuicVersionVectorString( |
| absl::string_view versions_string) { |
| ParsedQuicVersionVector versions; |
| std::vector<absl::string_view> version_strings = |
| absl::StrSplit(versions_string, ','); |
| for (absl::string_view version_string : version_strings) { |
| quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace( |
| &version_string); |
| ParsedQuicVersion version = ParseQuicVersionString(version_string); |
| if (!version.IsKnown() || std::find(versions.begin(), versions.end(), |
| version) != versions.end()) { |
| continue; |
| } |
| versions.push_back(version); |
| } |
| return versions; |
| } |
| |
| QuicTransportVersionVector AllSupportedTransportVersions() { |
| QuicTransportVersionVector transport_versions; |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { |
| if (std::find(transport_versions.begin(), transport_versions.end(), |
| version.transport_version) == transport_versions.end()) { |
| transport_versions.push_back(version.transport_version); |
| } |
| } |
| return transport_versions; |
| } |
| |
| ParsedQuicVersionVector AllSupportedVersions() { |
| constexpr auto supported_versions = SupportedVersions(); |
| return ParsedQuicVersionVector(supported_versions.begin(), |
| supported_versions.end()); |
| } |
| |
| ParsedQuicVersionVector CurrentSupportedVersions() { |
| return FilterSupportedVersions(AllSupportedVersions()); |
| } |
| |
| ParsedQuicVersionVector FilterSupportedVersions( |
| ParsedQuicVersionVector versions) { |
| ParsedQuicVersionVector filtered_versions; |
| filtered_versions.reserve(versions.size()); |
| for (const ParsedQuicVersion& version : versions) { |
| if (version == ParsedQuicVersion::RFCv1()) { |
| if (GetQuicReloadableFlag(quic_enable_version_rfcv1)) { |
| filtered_versions.push_back(version); |
| } |
| } else if (version == ParsedQuicVersion::Draft29()) { |
| if (!GetQuicReloadableFlag(quic_disable_version_draft_29)) { |
| filtered_versions.push_back(version); |
| } |
| } else if (version == ParsedQuicVersion::T051()) { |
| if (!GetQuicReloadableFlag(quic_disable_version_t051)) { |
| filtered_versions.push_back(version); |
| } |
| } else if (version == ParsedQuicVersion::Q050()) { |
| if (!GetQuicReloadableFlag(quic_disable_version_q050)) { |
| filtered_versions.push_back(version); |
| } |
| } else if (version == ParsedQuicVersion::Q046()) { |
| if (!GetQuicReloadableFlag(quic_disable_version_q046)) { |
| filtered_versions.push_back(version); |
| } |
| } else if (version == ParsedQuicVersion::Q043()) { |
| if (!GetQuicReloadableFlag(quic_disable_version_q043)) { |
| filtered_versions.push_back(version); |
| } |
| } else { |
| QUIC_BUG << "QUIC version " << version << " has no flag protection"; |
| filtered_versions.push_back(version); |
| } |
| } |
| return filtered_versions; |
| } |
| |
| 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; |
| } |
| |
| std::string QuicVersionLabelToString(QuicVersionLabel version_label) { |
| return QuicTagToString(quiche::QuicheEndian::HostToNet32(version_label)); |
| } |
| |
| std::string QuicVersionLabelVectorToString( |
| const QuicVersionLabelVector& version_labels, |
| const std::string& separator, |
| size_t skip_after_nth_version) { |
| std::string 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; |
| } |
| |
| #define RETURN_STRING_LITERAL(x) \ |
| case x: \ |
| return #x |
| |
| std::string QuicVersionToString(QuicTransportVersion transport_version) { |
| switch (transport_version) { |
| RETURN_STRING_LITERAL(QUIC_VERSION_43); |
| RETURN_STRING_LITERAL(QUIC_VERSION_46); |
| RETURN_STRING_LITERAL(QUIC_VERSION_50); |
| RETURN_STRING_LITERAL(QUIC_VERSION_51); |
| RETURN_STRING_LITERAL(QUIC_VERSION_IETF_DRAFT_29); |
| RETURN_STRING_LITERAL(QUIC_VERSION_IETF_RFC_V1); |
| RETURN_STRING_LITERAL(QUIC_VERSION_UNSUPPORTED); |
| RETURN_STRING_LITERAL(QUIC_VERSION_RESERVED_FOR_NEGOTIATION); |
| } |
| return absl::StrCat("QUIC_VERSION_UNKNOWN(", |
| static_cast<int>(transport_version), ")"); |
| } |
| |
| std::string HandshakeProtocolToString(HandshakeProtocol handshake_protocol) { |
| switch (handshake_protocol) { |
| RETURN_STRING_LITERAL(PROTOCOL_UNSUPPORTED); |
| RETURN_STRING_LITERAL(PROTOCOL_QUIC_CRYPTO); |
| RETURN_STRING_LITERAL(PROTOCOL_TLS1_3); |
| } |
| return absl::StrCat("PROTOCOL_UNKNOWN(", static_cast<int>(handshake_protocol), |
| ")"); |
| } |
| |
| std::string ParsedQuicVersionToString(ParsedQuicVersion version) { |
| static_assert(SupportedVersions().size() == 6u, |
| "Supported versions out of sync"); |
| if (version == UnsupportedQuicVersion()) { |
| return "0"; |
| } else if (version == ParsedQuicVersion::RFCv1()) { |
| QUICHE_DCHECK(version.UsesHttp3()); |
| return "RFCv1"; |
| } else if (version == ParsedQuicVersion::Draft29()) { |
| QUICHE_DCHECK(version.UsesHttp3()); |
| return "draft29"; |
| } |
| |
| return QuicVersionLabelToString(CreateQuicVersionLabel(version)); |
| } |
| |
| std::string QuicTransportVersionVectorToString( |
| const QuicTransportVersionVector& versions) { |
| std::string result = ""; |
| for (size_t i = 0; i < versions.size(); ++i) { |
| if (i != 0) { |
| result.append(","); |
| } |
| result.append(QuicVersionToString(versions[i])); |
| } |
| return result; |
| } |
| |
| std::string ParsedQuicVersionVectorToString( |
| const ParsedQuicVersionVector& versions, |
| const std::string& separator, |
| size_t skip_after_nth_version) { |
| std::string 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; |
| } |
| |
| bool VersionSupportsGoogleAltSvcFormat(QuicTransportVersion transport_version) { |
| return transport_version <= QUIC_VERSION_46; |
| } |
| |
| bool VersionAllowsVariableLengthConnectionIds( |
| QuicTransportVersion transport_version) { |
| QUICHE_DCHECK_NE(transport_version, QUIC_VERSION_UNSUPPORTED); |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool QuicVersionLabelUses4BitConnectionIdLength( |
| QuicVersionLabel version_label) { |
| // As we deprecate old versions, we still need the ability to send valid |
| // version negotiation packets for those versions. This function keeps track |
| // of the versions that ever supported the 4bit connection ID length encoding |
| // that we know about. Google QUIC 43 and earlier used a different encoding, |
| // and Google QUIC 49 and later use the new length prefixed encoding. |
| // Similarly, only IETF drafts 11 to 21 used this encoding. |
| |
| // Check Q044, Q045, Q046, Q047 and Q048. |
| for (uint8_t c = '4'; c <= '8'; ++c) { |
| if (version_label == MakeVersionLabel('Q', '0', '4', c)) { |
| return true; |
| } |
| } |
| // Check T048. |
| if (version_label == MakeVersionLabel('T', '0', '4', '8')) { |
| return true; |
| } |
| // Check IETF draft versions in [11,21]. |
| for (uint8_t draft_number = 11; draft_number <= 21; ++draft_number) { |
| if (version_label == MakeVersionLabel(0xff, 0x00, 0x00, draft_number)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| ParsedQuicVersion UnsupportedQuicVersion() { |
| return ParsedQuicVersion::Unsupported(); |
| } |
| |
| ParsedQuicVersion QuicVersionReservedForNegotiation() { |
| return ParsedQuicVersion::ReservedForNegotiation(); |
| } |
| |
| ParsedQuicVersion LegacyVersionForEncapsulation() { |
| return ParsedQuicVersion::Q043(); |
| } |
| |
| std::string AlpnForVersion(ParsedQuicVersion parsed_version) { |
| if (parsed_version == ParsedQuicVersion::RFCv1()) { |
| return "h3"; |
| } else if (parsed_version == ParsedQuicVersion::Draft29()) { |
| return "h3-29"; |
| } |
| return "h3-" + ParsedQuicVersionToString(parsed_version); |
| } |
| |
| void QuicVersionInitializeSupportForIetfDraft() { |
| // Enable necessary flags. |
| } |
| |
| void QuicEnableVersion(const ParsedQuicVersion& version) { |
| SetVersionFlag(version, /*should_enable=*/true); |
| } |
| |
| void QuicDisableVersion(const ParsedQuicVersion& version) { |
| SetVersionFlag(version, /*should_enable=*/false); |
| } |
| |
| bool QuicVersionIsEnabled(const ParsedQuicVersion& version) { |
| ParsedQuicVersionVector current = CurrentSupportedVersions(); |
| return std::find(current.begin(), current.end(), version) != current.end(); |
| } |
| |
| #undef RETURN_STRING_LITERAL // undef for jumbo builds |
| } // namespace quic |