// 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 #include "base/logging.h" #include "net/quic/quic_sent_packet_manager.h" using std::string; namespace net { QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence) : tag_(tag), presence_(presence), negotiated_(false) { } QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence) : QuicNegotiableValue(tag, presence), max_value_(0), default_value_(0) { } 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::ReadUint32( const CryptoHandshakeMessage& msg, uint32* out, string* error_details) const { DCHECK(error_details != NULL); QuicErrorCode error = msg.GetUint32(tag_, out); switch (error) { case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: if (presence_ == QuicNegotiableValue::PRESENCE_REQUIRED) { *error_details = "Missing " + QuicUtils::TagToString(tag_); break; } error = QUIC_NO_ERROR; *out = default_value_; case QUIC_NO_ERROR: break; default: *error_details = "Bad " + QuicUtils::TagToString(tag_); break; } return error; } QuicErrorCode QuicNegotiableUint32::ProcessClientHello( const CryptoHandshakeMessage& client_hello, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); uint32 value; QuicErrorCode error = ReadUint32(client_hello, &value, error_details); if (error != QUIC_NO_ERROR) { return error; } negotiated_ = true; negotiated_value_ = std::min(value, max_value_); return QUIC_NO_ERROR; } QuicErrorCode QuicNegotiableUint32::ProcessServerHello( const CryptoHandshakeMessage& server_hello, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); uint32 value; QuicErrorCode error = ReadUint32(server_hello, &value, error_details); if (error != QUIC_NO_ERROR) { return error; } if (value > max_value_) { *error_details = "Invalid value received for " + QuicUtils::TagToString(tag_); return QUIC_INVALID_NEGOTIATED_VALUE; } negotiated_ = true; negotiated_value_ = value; return QUIC_NO_ERROR; } QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, Presence presence) : QuicNegotiableValue(tag, presence), negotiated_tag_(0), default_value_(0) { } QuicNegotiableTag::~QuicNegotiableTag() {} void QuicNegotiableTag::set(const QuicTagVector& possible, QuicTag default_value) { DCHECK(std::find(possible.begin(), possible.end(), default_value) != possible.end()); 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::ProcessClientHello( const CryptoHandshakeMessage& client_hello, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); const QuicTag* received_tags; size_t received_tags_length; QuicErrorCode error = ReadVector(client_hello, &received_tags, &received_tags_length, error_details); if (error != QUIC_NO_ERROR) { return error; } QuicTag negotiated_tag; if (!QuicUtils::FindMutualTag(possible_values_, received_tags, received_tags_length, QuicUtils::LOCAL_PRIORITY, &negotiated_tag, NULL)) { *error_details = "Unsuported " + QuicUtils::TagToString(tag_); return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP; } negotiated_ = true; negotiated_tag_ = negotiated_tag; return QUIC_NO_ERROR; } QuicErrorCode QuicNegotiableTag::ProcessServerHello( const CryptoHandshakeMessage& server_hello, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); const QuicTag* received_tags; size_t received_tags_length; QuicErrorCode error = ReadVector(server_hello, &received_tags, &received_tags_length, error_details); if (error != QUIC_NO_ERROR) { return error; } if (received_tags_length != 1 || std::find(possible_values_.begin(), possible_values_.end(), *received_tags) == possible_values_.end()) { *error_details = "Invalid " + QuicUtils::TagToString(tag_); return QUIC_INVALID_NEGOTIATED_VALUE; } negotiated_ = true; negotiated_tag_ = *received_tags; return QUIC_NO_ERROR; } QuicConfig::QuicConfig() : congestion_control_(kCGST, QuicNegotiableValue::PRESENCE_REQUIRED), idle_connection_state_lifetime_seconds_( kICSL, QuicNegotiableValue::PRESENCE_REQUIRED), keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL), max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED), max_time_before_crypto_handshake_(QuicTime::Delta::Zero()), server_initial_congestion_window_( kSWND, QuicNegotiableValue::PRESENCE_OPTIONAL), initial_round_trip_time_us_(kIRTT, QuicNegotiableValue::PRESENCE_OPTIONAL) { // All optional non-zero parameters should be initialized here. server_initial_congestion_window_.set(kMaxInitialWindow, kDefaultInitialWindow); } QuicConfig::~QuicConfig() {} void QuicConfig::set_congestion_control( const QuicTagVector& congestion_control, QuicTag default_congestion_control) { congestion_control_.set(congestion_control, default_congestion_control); } QuicTag QuicConfig::congestion_control() const { return congestion_control_.GetTag(); } 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::set_server_initial_congestion_window(size_t max_initial_window, size_t default_initial_window) { server_initial_congestion_window_.set(max_initial_window, default_initial_window); } uint32 QuicConfig::server_initial_congestion_window() const { return server_initial_congestion_window_.GetUint32(); } void QuicConfig::set_initial_round_trip_time_us(size_t max_rtt, size_t default_rtt) { initial_round_trip_time_us_.set(max_rtt, default_rtt); } uint32 QuicConfig::initial_round_trip_time_us() const { return initial_round_trip_time_us_.GetUint32(); } bool QuicConfig::negotiated() { // TODO(ianswett): Add the negotiated parameters once and iterate over all // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and // ProcessServerHello. return congestion_control_.negotiated() && idle_connection_state_lifetime_seconds_.negotiated() && keepalive_timeout_seconds_.negotiated() && max_streams_per_connection_.negotiated() && server_initial_congestion_window_.negotiated() && initial_round_trip_time_us_.negotiated(); } void QuicConfig::SetDefaults() { QuicTagVector congestion_control; if (FLAGS_enable_quic_pacing) { congestion_control.push_back(kPACE); } congestion_control.push_back(kQBIC); congestion_control_.set(congestion_control, 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); server_initial_congestion_window_.set(kDefaultInitialWindow, kDefaultInitialWindow); } void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { congestion_control_.ToHandshakeMessage(out); idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out); keepalive_timeout_seconds_.ToHandshakeMessage(out); max_streams_per_connection_.ToHandshakeMessage(out); server_initial_congestion_window_.ToHandshakeMessage(out); // TODO(ianswett): Don't transmit parameters which are optional and not set. initial_round_trip_time_us_.ToHandshakeMessage(out); } QuicErrorCode QuicConfig::ProcessClientHello( const CryptoHandshakeMessage& client_hello, string* error_details) { DCHECK(error_details != NULL); QuicErrorCode error = QUIC_NO_ERROR; if (error == QUIC_NO_ERROR) { error = congestion_control_.ProcessClientHello(client_hello, error_details); } if (error == QUIC_NO_ERROR) { error = idle_connection_state_lifetime_seconds_.ProcessClientHello( client_hello, error_details); } if (error == QUIC_NO_ERROR) { error = keepalive_timeout_seconds_.ProcessClientHello( client_hello, error_details); } if (error == QUIC_NO_ERROR) { error = max_streams_per_connection_.ProcessClientHello( client_hello, error_details); } if (error == QUIC_NO_ERROR) { error = server_initial_congestion_window_.ProcessClientHello( client_hello, error_details); } if (error == QUIC_NO_ERROR) { error = initial_round_trip_time_us_.ProcessClientHello( client_hello, error_details); } return error; } QuicErrorCode QuicConfig::ProcessServerHello( const CryptoHandshakeMessage& server_hello, string* error_details) { DCHECK(error_details != NULL); QuicErrorCode error = QUIC_NO_ERROR; if (error == QUIC_NO_ERROR) { error = congestion_control_.ProcessServerHello(server_hello, error_details); } if (error == QUIC_NO_ERROR) { error = idle_connection_state_lifetime_seconds_.ProcessServerHello( server_hello, error_details); } if (error == QUIC_NO_ERROR) { error = keepalive_timeout_seconds_.ProcessServerHello( server_hello, error_details); } if (error == QUIC_NO_ERROR) { error = max_streams_per_connection_.ProcessServerHello( server_hello, error_details); } if (error == QUIC_NO_ERROR) { error = server_initial_congestion_window_.ProcessServerHello( server_hello, error_details); } if (error == QUIC_NO_ERROR) { error = initial_round_trip_time_us_.ProcessServerHello( server_hello, error_details); } return error; } } // namespace net