| // 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 <string> |
| |
| #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_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_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_text_utils.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 && |
| !GetQuicFlag(FLAGS_quic_supports_tls_handshake)) { |
| QUIC_BUG << "TLS use attempted when not enabled"; |
| } |
| } |
| |
| bool ParsedQuicVersion::KnowsWhichDecrypterToUse() const { |
| return transport_version >= QUIC_VERSION_47 || |
| handshake_protocol == PROTOCOL_TLS1_3; |
| } |
| |
| bool ParsedQuicVersion::AllowsLowFlowControlLimits() const { |
| return transport_version == QUIC_VERSION_99 && |
| handshake_protocol == PROTOCOL_TLS1_3; |
| } |
| |
| bool ParsedQuicVersion::HasHeaderProtection() const { |
| return transport_version == QUIC_VERSION_99; |
| } |
| |
| bool ParsedQuicVersion::SupportsRetry() const { |
| return transport_version > QUIC_VERSION_46; |
| } |
| |
| bool ParsedQuicVersion::SupportsClientConnectionIds() const { |
| if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) { |
| // Do not enable this feature in a production version until this flag has |
| // been deprecated. |
| return false; |
| } |
| return transport_version >= QUIC_VERSION_99 || |
| transport_version == QUIC_VERSION_UNSUPPORTED; |
| } |
| |
| 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: |
| if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 && |
| GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) { |
| return MakeVersionLabel(0xff, 0x00, 0x00, |
| GetQuicFlag(FLAGS_quic_ietf_draft_version)); |
| } |
| return MakeVersionLabel(proto, '0', '9', '9'); |
| case QUIC_VERSION_RESERVED_FOR_NEGOTIATION: |
| return MakeVersionLabel(0xda, 0x5a, 0x3a, 0x3a); |
| 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 (GetQuicFlag(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(); |
| } |
| |
| ParsedQuicVersion ParseQuicVersionString(std::string version_string) { |
| if (version_string.empty()) { |
| return UnsupportedQuicVersion(); |
| } |
| int quic_version_number = 0; |
| if (QuicTextUtils::StringToInt(version_string, &quic_version_number) && |
| quic_version_number > 0) { |
| return ParsedQuicVersion( |
| PROTOCOL_QUIC_CRYPTO, |
| static_cast<QuicTransportVersion>(quic_version_number)); |
| } |
| |
| std::vector<HandshakeProtocol> protocols = {PROTOCOL_QUIC_CRYPTO}; |
| if (GetQuicFlag(FLAGS_quic_supports_tls_handshake)) { |
| protocols.push_back(PROTOCOL_TLS1_3); |
| } |
| for (QuicTransportVersion version : kSupportedTransportVersions) { |
| for (HandshakeProtocol handshake : protocols) { |
| const ParsedQuicVersion parsed_version = |
| ParsedQuicVersion(handshake, version); |
| if (version_string == ParsedQuicVersionToString(parsed_version)) { |
| return parsed_version; |
| } |
| } |
| } |
| // Still recognize T099 even if flag quic_ietf_draft_version has been changed. |
| if (GetQuicFlag(FLAGS_quic_supports_tls_handshake) && |
| version_string == "T099") { |
| return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); |
| } |
| // Reading from the client so this should not be considered an ERROR. |
| QUIC_DLOG(INFO) << "Unsupported QUIC version string: \"" << version_string |
| << "\"."; |
| 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 && |
| !GetQuicFlag(FLAGS_quic_supports_tls_handshake)) { |
| continue; |
| } |
| for (QuicTransportVersion version : kSupportedTransportVersions) { |
| if (protocol == PROTOCOL_TLS1_3 && |
| !QuicVersionUsesCryptoFrames(version)) { |
| // 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)) { |
| 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)) { |
| filtered_versions.push_back(version); |
| } |
| } else if (version.transport_version == QUIC_VERSION_46) { |
| if (GetQuicReloadableFlag(quic_enable_version_46) && |
| GetQuicReloadableFlag(quic_enable_version_44)) { |
| filtered_versions.push_back(version); |
| } |
| } else if (version.transport_version == QUIC_VERSION_44) { |
| if (GetQuicReloadableFlag(quic_enable_version_44)) { |
| 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)); |
| } |
| |
| std::string QuicVersionLabelToString(QuicVersionLabel version_label) { |
| return QuicTagToString(QuicEndian::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; |
| } |
| |
| 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 |
| |
| std::string 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"; |
| } |
| } |
| |
| std::string ParsedQuicVersionToString(ParsedQuicVersion version) { |
| 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; |
| } |
| |
| ParsedQuicVersion UnsupportedQuicVersion() { |
| return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED); |
| } |
| |
| ParsedQuicVersion QuicVersionReservedForNegotiation() { |
| return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, |
| QUIC_VERSION_RESERVED_FOR_NEGOTIATION); |
| } |
| |
| std::string AlpnForVersion(ParsedQuicVersion parsed_version) { |
| if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 && |
| parsed_version.transport_version == QUIC_VERSION_99 && |
| GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) { |
| return "h3-" + QuicTextUtils::Uint64ToString( |
| GetQuicFlag(FLAGS_quic_ietf_draft_version)); |
| } |
| return "h3-google-" + ParsedQuicVersionToString(parsed_version); |
| } |
| |
| void QuicVersionInitializeSupportForIetfDraft(int32_t draft_version) { |
| if (draft_version < 0 || draft_version >= 256) { |
| QUIC_LOG(FATAL) << "Invalid IETF draft version " << draft_version; |
| return; |
| } |
| |
| SetQuicFlag(FLAGS_quic_ietf_draft_version, draft_version); |
| |
| if (draft_version == 0) { |
| return; |
| } |
| |
| // Enable necessary flags. |
| SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); |
| SetQuicReloadableFlag(quic_deprecate_ack_bundling_mode, true); |
| SetQuicReloadableFlag(quic_rpm_decides_when_to_send_acks, true); |
| SetQuicReloadableFlag(quic_use_uber_loss_algorithm, true); |
| SetQuicReloadableFlag(quic_use_uber_received_packet_manager, true); |
| SetQuicReloadableFlag(quic_validate_packet_number_post_decryption, true); |
| SetQuicReloadableFlag(quic_print_tag_hex, true); |
| SetQuicReloadableFlag(quic_send_version_negotiation_fixed_bit, true); |
| SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, true); |
| SetQuicReloadableFlag(quic_eliminate_static_stream_map_3, true); |
| SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, true); |
| SetQuicRestartFlag(quic_server_drop_version_negotiation, true); |
| SetQuicRestartFlag(quic_enable_accept_random_ipn, true); |
| SetQuicRestartFlag(quic_allow_variable_length_connection_id_for_negotiation, |
| true); |
| SetQuicRestartFlag(quic_do_not_override_connection_id, true); |
| } |
| |
| void QuicEnableVersion(ParsedQuicVersion parsed_version) { |
| if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3) { |
| SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); |
| } |
| static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u, |
| "Supported versions out of sync"); |
| if (parsed_version.transport_version >= QUIC_VERSION_99) { |
| SetQuicReloadableFlag(quic_enable_version_99, true); |
| } |
| if (parsed_version.transport_version >= QUIC_VERSION_47) { |
| SetQuicReloadableFlag(quic_enable_version_47, true); |
| } |
| if (parsed_version.transport_version >= QUIC_VERSION_46) { |
| SetQuicReloadableFlag(quic_enable_version_46, true); |
| } |
| if (parsed_version.transport_version >= QUIC_VERSION_44) { |
| SetQuicReloadableFlag(quic_enable_version_44, true); |
| } |
| } |
| |
| #undef RETURN_STRING_LITERAL // undef for jumbo builds |
| } // namespace quic |