// 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/quic/quic_config.h" #include <algorithm> #include "base/logging.h" #include "net/quic/crypto/crypto_handshake_message.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_sent_packet_manager.h" #include "net/quic/quic_utils.h" using std::min; using std::string; namespace net { // 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 default_value, uint32* out, string* error_details) { DCHECK(error_details != NULL); QuicErrorCode error = msg.GetUint32(tag, out); switch (error) { case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: if (presence == PRESENCE_REQUIRED) { *error_details = "Missing " + QuicUtils::TagToString(tag); break; } error = QUIC_NO_ERROR; *out = default_value; break; case QUIC_NO_ERROR: break; default: *error_details = "Bad " + QuicUtils::TagToString(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) { } QuicNegotiableUint32::~QuicNegotiableUint32() {} void QuicNegotiableUint32::set(uint32 max, uint32 default_value) { DCHECK_LE(default_value, max); max_value_ = max; default_value_ = default_value; } uint32 QuicNegotiableUint32::GetUint32() const { if (negotiated_) { return negotiated_value_; } return default_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, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); uint32 value; QuicErrorCode error = ReadUint32(peer_hello, tag_, presence_, default_value_, &value, error_details); if (error != QUIC_NO_ERROR) { return error; } if (hello_type == SERVER && value > max_value_) { *error_details = "Invalid value received for " + QuicUtils::TagToString(tag_); return QUIC_INVALID_NEGOTIATED_VALUE; } negotiated_ = true; negotiated_value_ = min(value, max_value_); return QUIC_NO_ERROR; } QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, QuicConfigPresence presence) : QuicNegotiableValue(tag, presence), negotiated_tag_(0), default_value_(0) { } QuicNegotiableTag::~QuicNegotiableTag() {} void QuicNegotiableTag::set(const QuicTagVector& possible, QuicTag default_value) { DCHECK(ContainsQuicTag(possible, default_value)); possible_values_ = possible; default_value_ = default_value; } QuicTag QuicNegotiableTag::GetTag() const { if (negotiated_) { return negotiated_tag_; } return default_value_; } void QuicNegotiableTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const { if (negotiated_) { // Because of the way we serialize and parse handshake messages we can // serialize this as value and still parse it as a vector. out->SetValue(tag_, negotiated_tag_); } else { out->SetVector(tag_, possible_values_); } } QuicErrorCode QuicNegotiableTag::ReadVector( const CryptoHandshakeMessage& msg, const QuicTag** out, size_t* out_length, string* error_details) const { DCHECK(error_details != NULL); QuicErrorCode error = msg.GetTaglist(tag_, out, out_length); switch (error) { case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: if (presence_ == PRESENCE_REQUIRED) { *error_details = "Missing " + QuicUtils::TagToString(tag_); break; } error = QUIC_NO_ERROR; *out_length = 1; *out = &default_value_; case QUIC_NO_ERROR: break; default: *error_details = "Bad " + QuicUtils::TagToString(tag_); break; } return error; } QuicErrorCode QuicNegotiableTag::ProcessPeerHello( const CryptoHandshakeMessage& peer_hello, HelloType hello_type, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); const QuicTag* received_tags; size_t received_tags_length; QuicErrorCode error = ReadVector(peer_hello, &received_tags, &received_tags_length, error_details); if (error != QUIC_NO_ERROR) { return error; } if (hello_type == SERVER) { if (received_tags_length != 1 || !ContainsQuicTag(possible_values_, *received_tags)) { *error_details = "Invalid " + QuicUtils::TagToString(tag_); return QUIC_INVALID_NEGOTIATED_VALUE; } negotiated_tag_ = *received_tags; } else { QuicTag negotiated_tag; if (!QuicUtils::FindMutualTag(possible_values_, received_tags, received_tags_length, QuicUtils::LOCAL_PRIORITY, &negotiated_tag, NULL)) { *error_details = "Unsupported " + QuicUtils::TagToString(tag_); return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP; } negotiated_tag_ = negotiated_tag; } negotiated_ = true; 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 QuicFixedUint32::GetSendValue() const { LOG_IF(DFATAL, !has_send_value_) << "No send value to get for tag:" << QuicUtils::TagToString(tag_); return send_value_; } void QuicFixedUint32::SetSendValue(uint32 value) { has_send_value_ = true; send_value_ = value; } bool QuicFixedUint32::HasReceivedValue() const { return has_receive_value_; } uint32 QuicFixedUint32::GetReceivedValue() const { LOG_IF(DFATAL, !has_receive_value_) << "No receive value to get for tag:" << QuicUtils::TagToString(tag_); return receive_value_; } void QuicFixedUint32::SetReceivedValue(uint32 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, string* error_details) { DCHECK(error_details != NULL); 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 " + QuicUtils::TagToString(tag_); break; case QUIC_NO_ERROR: has_receive_value_ = true; break; default: *error_details = "Bad " + QuicUtils::TagToString(tag_); break; } return error; } QuicFixedTag::QuicFixedTag(QuicTag name, QuicConfigPresence presence) : QuicConfigValue(name, presence), has_send_value_(false), has_receive_value_(false) { } QuicFixedTag::~QuicFixedTag() {} bool QuicFixedTag::HasSendValue() const { return has_send_value_; } uint32 QuicFixedTag::GetSendValue() const { LOG_IF(DFATAL, !has_send_value_) << "No send value to get for tag:" << QuicUtils::TagToString(tag_); return send_value_; } void QuicFixedTag::SetSendValue(uint32 value) { has_send_value_ = true; send_value_ = value; } bool QuicFixedTag::HasReceivedValue() const { return has_receive_value_; } uint32 QuicFixedTag::GetReceivedValue() const { LOG_IF(DFATAL, !has_receive_value_) << "No receive value to get for tag:" << QuicUtils::TagToString(tag_); return receive_value_; } void QuicFixedTag::SetReceivedValue(uint32 value) { has_receive_value_ = true; receive_value_ = value; } void QuicFixedTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const { if (has_send_value_) { out->SetValue(tag_, send_value_); } } QuicErrorCode QuicFixedTag::ProcessPeerHello( const CryptoHandshakeMessage& peer_hello, HelloType hello_type, string* error_details) { DCHECK(error_details != NULL); 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 " + QuicUtils::TagToString(tag_); break; case QUIC_NO_ERROR: has_receive_value_ = true; break; default: *error_details = "Bad " + QuicUtils::TagToString(tag_); break; } return error; } QuicFixedTagVector::QuicFixedTagVector(QuicTag name, QuicConfigPresence presence) : QuicConfigValue(name, presence), has_send_values_(false), has_receive_values_(false) { } QuicFixedTagVector::~QuicFixedTagVector() {} bool QuicFixedTagVector::HasSendValues() const { return has_send_values_; } QuicTagVector QuicFixedTagVector::GetSendValues() const { LOG_IF(DFATAL, !has_send_values_) << "No send values to get for tag:" << QuicUtils::TagToString(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 { LOG_IF(DFATAL, !has_receive_values_) << "No receive value to get for tag:" << QuicUtils::TagToString(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, string* error_details) { DCHECK(error_details != NULL); const QuicTag* received_tags; size_t received_tags_length; QuicErrorCode error = peer_hello.GetTaglist(tag_, &received_tags, &received_tags_length); switch (error) { case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: if (presence_ == PRESENCE_OPTIONAL) { return QUIC_NO_ERROR; } *error_details = "Missing " + QuicUtils::TagToString(tag_); break; case QUIC_NO_ERROR: has_receive_values_ = true; for (size_t i = 0; i < received_tags_length; ++i) { receive_values_.push_back(received_tags[i]); } break; default: *error_details = "Bad " + QuicUtils::TagToString(tag_); break; } return error; } QuicConfig::QuicConfig() : congestion_feedback_(kCGST, PRESENCE_REQUIRED), congestion_options_(kCOPT, PRESENCE_OPTIONAL), loss_detection_(kLOSS, PRESENCE_OPTIONAL), idle_connection_state_lifetime_seconds_(kICSL, PRESENCE_REQUIRED), keepalive_timeout_seconds_(kKATO, PRESENCE_OPTIONAL), max_streams_per_connection_(kMSPC, PRESENCE_REQUIRED), max_time_before_crypto_handshake_(QuicTime::Delta::Zero()), initial_congestion_window_(kSWND, PRESENCE_OPTIONAL), initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL), // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring // QUIC_VERSION_17. initial_flow_control_window_bytes_(kIFCW, PRESENCE_OPTIONAL), // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring // QUIC_VERSION_19. initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL), // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring // QUIC_VERSION_19. initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL) { } QuicConfig::~QuicConfig() {} void QuicConfig::set_congestion_feedback( const QuicTagVector& congestion_feedback, QuicTag default_congestion_feedback) { congestion_feedback_.set(congestion_feedback, default_congestion_feedback); } QuicTag QuicConfig::congestion_feedback() const { return congestion_feedback_.GetTag(); } void QuicConfig::SetCongestionOptionsToSend( const QuicTagVector& congestion_options) { congestion_options_.SetSendValues(congestion_options); } bool QuicConfig::HasReceivedCongestionOptions() const { return congestion_options_.HasReceivedValues(); } QuicTagVector QuicConfig::ReceivedCongestionOptions() const { return congestion_options_.GetReceivedValues(); } void QuicConfig::SetLossDetectionToSend(QuicTag loss_detection) { loss_detection_.SetSendValue(loss_detection); } bool QuicConfig::HasReceivedLossDetection() const { return loss_detection_.HasReceivedValue(); } QuicTag QuicConfig::ReceivedLossDetection() const { return loss_detection_.GetReceivedValue(); } void QuicConfig::set_idle_connection_state_lifetime( QuicTime::Delta max_idle_connection_state_lifetime, QuicTime::Delta default_idle_conection_state_lifetime) { idle_connection_state_lifetime_seconds_.set( max_idle_connection_state_lifetime.ToSeconds(), default_idle_conection_state_lifetime.ToSeconds()); } QuicTime::Delta QuicConfig::idle_connection_state_lifetime() const { return QuicTime::Delta::FromSeconds( idle_connection_state_lifetime_seconds_.GetUint32()); } QuicTime::Delta QuicConfig::keepalive_timeout() const { return QuicTime::Delta::FromSeconds( keepalive_timeout_seconds_.GetUint32()); } void QuicConfig::set_max_streams_per_connection(size_t max_streams, size_t default_streams) { max_streams_per_connection_.set(max_streams, default_streams); } uint32 QuicConfig::max_streams_per_connection() const { return max_streams_per_connection_.GetUint32(); } void QuicConfig::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 QuicConfig::max_time_before_crypto_handshake() const { return max_time_before_crypto_handshake_; } void QuicConfig::SetInitialCongestionWindowToSend(size_t initial_window) { initial_congestion_window_.SetSendValue(initial_window); } bool QuicConfig::HasReceivedInitialCongestionWindow() const { return initial_congestion_window_.HasReceivedValue(); } uint32 QuicConfig::ReceivedInitialCongestionWindow() const { return initial_congestion_window_.GetReceivedValue(); } void QuicConfig::SetInitialRoundTripTimeUsToSend(size_t rtt) { initial_round_trip_time_us_.SetSendValue(rtt); } bool QuicConfig::HasReceivedInitialRoundTripTimeUs() const { return initial_round_trip_time_us_.HasReceivedValue(); } uint32 QuicConfig::ReceivedInitialRoundTripTimeUs() const { return initial_round_trip_time_us_.GetReceivedValue(); } void QuicConfig::SetInitialFlowControlWindowToSend(uint32 window_bytes) { if (window_bytes < kDefaultFlowControlSendWindow) { LOG(DFATAL) << "Initial flow control receive window (" << window_bytes << ") cannot be set lower than default (" << kDefaultFlowControlSendWindow << ")."; window_bytes = kDefaultFlowControlSendWindow; } initial_flow_control_window_bytes_.SetSendValue(window_bytes); } uint32 QuicConfig::GetInitialFlowControlWindowToSend() const { return initial_flow_control_window_bytes_.GetSendValue(); } bool QuicConfig::HasReceivedInitialFlowControlWindowBytes() const { return initial_flow_control_window_bytes_.HasReceivedValue(); } uint32 QuicConfig::ReceivedInitialFlowControlWindowBytes() const { return initial_flow_control_window_bytes_.GetReceivedValue(); } void QuicConfig::SetInitialStreamFlowControlWindowToSend(uint32 window_bytes) { if (window_bytes < kDefaultFlowControlSendWindow) { LOG(DFATAL) << "Initial stream flow control receive window (" << window_bytes << ") cannot be set lower than default (" << kDefaultFlowControlSendWindow << ")."; window_bytes = kDefaultFlowControlSendWindow; } initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes); } uint32 QuicConfig::GetInitialStreamFlowControlWindowToSend() const { return initial_stream_flow_control_window_bytes_.GetSendValue(); } bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const { return initial_stream_flow_control_window_bytes_.HasReceivedValue(); } uint32 QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const { return initial_stream_flow_control_window_bytes_.GetReceivedValue(); } void QuicConfig::SetInitialSessionFlowControlWindowToSend(uint32 window_bytes) { if (window_bytes < kDefaultFlowControlSendWindow) { LOG(DFATAL) << "Initial session flow control receive window (" << window_bytes << ") cannot be set lower than default (" << kDefaultFlowControlSendWindow << ")."; window_bytes = kDefaultFlowControlSendWindow; } initial_session_flow_control_window_bytes_.SetSendValue(window_bytes); } uint32 QuicConfig::GetInitialSessionFlowControlWindowToSend() const { return initial_session_flow_control_window_bytes_.GetSendValue(); } bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const { return initial_session_flow_control_window_bytes_.HasReceivedValue(); } uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const { return initial_session_flow_control_window_bytes_.GetReceivedValue(); } bool QuicConfig::negotiated() { // TODO(ianswett): Add the negotiated parameters once and iterate over all // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and // ProcessServerHello. return congestion_feedback_.negotiated() && idle_connection_state_lifetime_seconds_.negotiated() && keepalive_timeout_seconds_.negotiated() && max_streams_per_connection_.negotiated(); } void QuicConfig::SetDefaults() { QuicTagVector congestion_feedback; if (FLAGS_enable_quic_pacing) { congestion_feedback.push_back(kPACE); } congestion_feedback.push_back(kQBIC); congestion_feedback_.set(congestion_feedback, kQBIC); idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs, kDefaultInitialTimeoutSecs); // kKATO is optional. Return 0 if not negotiated. keepalive_timeout_seconds_.set(0, 0); max_streams_per_connection_.set(kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds( kDefaultMaxTimeForCryptoHandshakeSecs); SetInitialFlowControlWindowToSend(kDefaultFlowControlSendWindow); SetInitialStreamFlowControlWindowToSend(kDefaultFlowControlSendWindow); SetInitialSessionFlowControlWindowToSend(kDefaultFlowControlSendWindow); } void QuicConfig::EnablePacing(bool enable_pacing) { QuicTagVector congestion_feedback; if (enable_pacing) { congestion_feedback.push_back(kPACE); } congestion_feedback.push_back(kQBIC); congestion_feedback_.set(congestion_feedback, kQBIC); } void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { congestion_feedback_.ToHandshakeMessage(out); idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out); keepalive_timeout_seconds_.ToHandshakeMessage(out); max_streams_per_connection_.ToHandshakeMessage(out); initial_congestion_window_.ToHandshakeMessage(out); initial_round_trip_time_us_.ToHandshakeMessage(out); loss_detection_.ToHandshakeMessage(out); initial_flow_control_window_bytes_.ToHandshakeMessage(out); initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out); initial_session_flow_control_window_bytes_.ToHandshakeMessage(out); congestion_options_.ToHandshakeMessage(out); } QuicErrorCode QuicConfig::ProcessPeerHello( const CryptoHandshakeMessage& peer_hello, HelloType hello_type, string* error_details) { DCHECK(error_details != NULL); QuicErrorCode error = QUIC_NO_ERROR; if (error == QUIC_NO_ERROR) { error = congestion_feedback_.ProcessPeerHello( peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { error = idle_connection_state_lifetime_seconds_.ProcessPeerHello( peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { error = keepalive_timeout_seconds_.ProcessPeerHello( peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { error = max_streams_per_connection_.ProcessPeerHello( peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { error = initial_congestion_window_.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_flow_control_window_bytes_.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 = loss_detection_.ProcessPeerHello( peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { error = congestion_options_.ProcessPeerHello( peer_hello, hello_type, error_details); } return error; } } // namespace net