diff options
92 files changed, 1761 insertions, 585 deletions
diff --git a/net/base/ip_endpoint.cc b/net/base/ip_endpoint.cc index 923596e..e86c457 100644 --- a/net/base/ip_endpoint.cc +++ b/net/base/ip_endpoint.cc @@ -35,15 +35,7 @@ IPEndPoint::IPEndPoint(const IPEndPoint& endpoint) { } AddressFamily IPEndPoint::GetFamily() const { - switch (address_.size()) { - case kIPv4AddressSize: - return ADDRESS_FAMILY_IPV4; - case kIPv6AddressSize: - return ADDRESS_FAMILY_IPV6; - default: - NOTREACHED() << "Bad IP address"; - return ADDRESS_FAMILY_UNSPECIFIED; - } + return GetAddressFamily(address_); } int IPEndPoint::GetSockAddrFamily() const { diff --git a/net/base/net_util.cc b/net/base/net_util.cc index a279293..3d4924a 100644 --- a/net/base/net_util.cc +++ b/net/base/net_util.cc @@ -2016,6 +2016,17 @@ bool HaveOnlyLoopbackAddresses() { #endif // defined(various platforms) } +AddressFamily GetAddressFamily(const IPAddressNumber& address) { + switch (address.size()) { + case kIPv4AddressSize: + return ADDRESS_FAMILY_IPV4; + case kIPv6AddressSize: + return ADDRESS_FAMILY_IPV6; + default: + return ADDRESS_FAMILY_UNSPECIFIED; + } +} + bool ParseIPLiteralToNumber(const std::string& ip_literal, IPAddressNumber* ip_number) { // |ip_literal| could be either a IPv4 or an IPv6 literal. If it contains diff --git a/net/base/net_util.h b/net/base/net_util.h index 5cfd1ea..e2519ba 100644 --- a/net/base/net_util.h +++ b/net/base/net_util.h @@ -22,6 +22,7 @@ #include "base/basictypes.h" #include "base/string16.h" +#include "net/base/address_family.h" #include "net/base/escape.h" #include "net/base/net_export.h" #include "net/base/net_log.h" @@ -458,6 +459,10 @@ NET_EXPORT IPv6SupportResult TestIPv6Support(); // Also returns false if it cannot determine this. bool HaveOnlyLoopbackAddresses(); +// Returns AddressFamily of the address. +NET_EXPORT_PRIVATE AddressFamily GetAddressFamily( + const IPAddressNumber& address); + // Parses an IP address literal (either IPv4 or IPv6) to its numeric value. // Returns true on success and fills |ip_number| with the numeric value. NET_EXPORT_PRIVATE bool ParseIPLiteralToNumber(const std::string& ip_literal, diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc index 4ddafe6..a40ad79 100644 --- a/net/base/net_util_unittest.cc +++ b/net/base/net_util_unittest.cc @@ -3171,6 +3171,14 @@ TEST(NetUtilTest, GetHostOrSpecFromURL) { GetHostOrSpecFromURL(GURL("file:///tmp/test.html"))); } +TEST(NetUtilTest, GetAddressFamily) { + IPAddressNumber number; + EXPECT_TRUE(ParseIPLiteralToNumber("192.168.0.1", &number)); + EXPECT_EQ(ADDRESS_FAMILY_IPV4, GetAddressFamily(number)); + EXPECT_TRUE(ParseIPLiteralToNumber("1:abcd::3:4:ff", &number)); + EXPECT_EQ(ADDRESS_FAMILY_IPV6, GetAddressFamily(number)); +} + // Test that invalid IP literals fail to parse. TEST(NetUtilTest, ParseIPLiteralToNumber_FailParse) { IPAddressNumber number; diff --git a/net/net.gyp b/net/net.gyp index 41775bd..6bdb387 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -1659,6 +1659,8 @@ 'quic/test_tools/mock_crypto_client_stream_factory.h', 'quic/test_tools/mock_random.cc', 'quic/test_tools/mock_random.h', + 'quic/test_tools/quic_client_session_peer.cc', + 'quic/test_tools/quic_client_session_peer.h', 'quic/test_tools/quic_connection_peer.cc', 'quic/test_tools/quic_connection_peer.h', 'quic/test_tools/quic_framer_peer.cc', @@ -1678,6 +1680,7 @@ 'quic/quic_bandwidth_test.cc', 'quic/quic_client_session_test.cc', 'quic/quic_clock_test.cc', + 'quic/quic_config_test.cc', 'quic/quic_connection_helper_test.cc', 'quic/quic_connection_test.cc', 'quic/quic_crypto_client_stream_test.cc', @@ -2685,6 +2688,10 @@ 'tools/quic/test_tools/http_message_test_utils.h', 'tools/quic/test_tools/mock_epoll_server.cc', 'tools/quic/test_tools/mock_epoll_server.h', + 'tools/quic/test_tools/quic_client_peer.cc', + 'tools/quic/test_tools/quic_client_peer.h', + 'tools/quic/test_tools/quic_epoll_connection_helper_peer.cc', + 'tools/quic/test_tools/quic_epoll_connection_helper_peer.h', 'tools/quic/test_tools/quic_test_client.cc', 'tools/quic/test_tools/quic_test_client.h', 'tools/quic/test_tools/quic_test_utils.cc', diff --git a/net/quic/congestion_control/hybrid_slow_start.cc b/net/quic/congestion_control/hybrid_slow_start.cc index 62b28f8..8968dc9 100644 --- a/net/quic/congestion_control/hybrid_slow_start.cc +++ b/net/quic/congestion_control/hybrid_slow_start.cc @@ -41,7 +41,6 @@ void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) { } bool HybridSlowStart::EndOfRound(QuicPacketSequenceNumber ack) { - // TODO(pwestin): do we need to handle wraparound? return end_sequence_number_ <= ack; } diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 78bd7d9..e6ed9d3 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -228,6 +228,7 @@ void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { } hybrid_slow_start_.Update(rtt, delay_min_); if (hybrid_slow_start_.Exit()) { + DLOG(INFO) << "Set slowstart threshold:" << congestion_window_; slowstart_threshold_ = congestion_window_; } } diff --git a/net/quic/crypto/cert_compressor.cc b/net/quic/crypto/cert_compressor.cc index 2f36c19..58d259a 100644 --- a/net/quic/crypto/cert_compressor.cc +++ b/net/quic/crypto/cert_compressor.cc @@ -20,7 +20,7 @@ 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 unsigned char kCommonCertSubstrings[] = { +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, @@ -176,7 +176,7 @@ struct CertEntry { vector<CertEntry> MatchCerts(const vector<string>& certs, StringPiece client_common_set_hashes, StringPiece client_cached_cert_hashes, - const CommonCertSets* common_set) { + const CommonCertSets* common_sets) { vector<CertEntry> entries; entries.reserve(certs.size()); @@ -214,8 +214,8 @@ vector<CertEntry> MatchCerts(const vector<string>& certs, } } - if (common_set && common_set->MatchCert(*i, client_common_set_hashes, - &entry.set_hash, &entry.index)) { + 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; @@ -328,11 +328,11 @@ vector<uint64> HashCerts(const vector<string>& certs) { // 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_set| and written to |out_certs|. +// resolved using |cached_certs| and |common_sets| and written to |out_certs|. // |in_out| is updated to contain the trailing data. bool ParseEntries(StringPiece* in_out, const vector<string>& cached_certs, - const CommonCertSets* common_set, + const CommonCertSets* common_sets, vector<CertEntry>* out_entries, vector<string>* out_certs) { StringPiece in = *in_out; @@ -383,7 +383,7 @@ bool ParseEntries(StringPiece* in_out, break; } case CertEntry::COMMON: { - if (!common_set) { + if (!common_sets) { return false; } if (in.size() < sizeof(uint64)) { @@ -398,7 +398,7 @@ bool ParseEntries(StringPiece* in_out, memcpy(&entry.index, in.data(), sizeof(uint32)); in.remove_prefix(sizeof(uint32)); - StringPiece cert = common_set->GetCert(entry.set_hash, entry.index); + StringPiece cert = common_sets->GetCert(entry.set_hash, entry.index); if (cert.empty()) { return false; } @@ -449,9 +449,9 @@ class ScopedZLib { string CertCompressor::CompressChain(const vector<string>& certs, StringPiece client_common_set_hashes, StringPiece client_cached_cert_hashes, - const CommonCertSets* common_set) { + const CommonCertSets* common_sets) { const vector<CertEntry> entries = MatchCerts( - certs, client_common_set_hashes, client_cached_cert_hashes, common_set); + certs, client_common_set_hashes, client_cached_cert_hashes, common_sets); DCHECK_EQ(entries.size(), certs.size()); size_t uncompressed_size = 0; @@ -549,10 +549,10 @@ string CertCompressor::CompressChain(const vector<string>& certs, // static bool CertCompressor::DecompressChain(StringPiece in, const vector<string>& cached_certs, - const CommonCertSets* common_set, + const CommonCertSets* common_sets, vector<string>* out_certs) { vector<CertEntry> entries; - if (!ParseEntries(&in, cached_certs, common_set, &entries, out_certs)) { + if (!ParseEntries(&in, cached_certs, common_sets, &entries, out_certs)) { return false; } DCHECK_EQ(entries.size(), out_certs->size()); diff --git a/net/quic/crypto/cert_compressor.h b/net/quic/crypto/cert_compressor.h index 587684d..612bc529 100644 --- a/net/quic/crypto/cert_compressor.h +++ b/net/quic/crypto/cert_compressor.h @@ -22,31 +22,31 @@ namespace net { // 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. +// pre-shared certificates. 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 NET_EXPORT_PRIVATE CertCompressor { public: // CompressChain compresses the certificates in |certs| and returns a - // compressed representation. |common_set| contains the common certificate + // 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| contains 64-bit, FNV-1a - // hashes of certificates that the peer already possesses. + // 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 std::string CompressChain(const std::vector<std::string>& certs, base::StringPiece client_common_set_hashes, base::StringPiece client_cached_cert_hashes, - const CommonCertSets* common_set); + 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_set| contains the common certificate sets known locally. + // |common_sets| contains the common certificate sets known locally. static bool DecompressChain(base::StringPiece in, const std::vector<std::string>& cached_certs, - const CommonCertSets* common_set, + const CommonCertSets* common_sets, std::vector<std::string>* out_certs); }; diff --git a/net/quic/crypto/cert_compressor_test.cc b/net/quic/crypto/cert_compressor_test.cc index ef12ccc..5bfef8f 100644 --- a/net/quic/crypto/cert_compressor_test.cc +++ b/net/quic/crypto/cert_compressor_test.cc @@ -47,12 +47,12 @@ TEST(CertCompressor, Common) { vector<string> chain; chain.push_back("testcert"); static const uint64 set_hash = 42; - scoped_ptr<CommonCertSets> common_set( + scoped_ptr<CommonCertSets> common_sets( CryptoTestUtils::MockCommonCertSets(chain[0], set_hash, 1)); const string compressed = CertCompressor::CompressChain( chain, StringPiece(reinterpret_cast<const char*>(&set_hash), sizeof(set_hash)), - StringPiece(), common_set.get()); + StringPiece(), common_sets.get()); const string common("03" /* common */ "2A00000000000000" /* set hash 42 */ "01000000" /* index 1 */ @@ -62,7 +62,7 @@ TEST(CertCompressor, Common) { vector<string> chain2, cached_certs; ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, - common_set.get(), &chain2)); + common_sets.get(), &chain2)); EXPECT_EQ(chain.size(), chain2.size()); EXPECT_EQ(chain[0], chain2[0]); } @@ -124,7 +124,7 @@ TEST(CertCompressor, BadInputs) { without_a_common_cert_set.size()), cached_certs, NULL, &chain)); - scoped_ptr<CommonCertSets> common_set( + scoped_ptr<CommonCertSets> common_sets( CryptoTestUtils::MockCommonCertSets("foo", 42, 1)); /* incorrect hash and index */ diff --git a/net/quic/crypto/common_cert_set.cc b/net/quic/crypto/common_cert_set.cc index 78aa927..ee1ac67 100644 --- a/net/quic/crypto/common_cert_set.cc +++ b/net/quic/crypto/common_cert_set.cc @@ -53,17 +53,17 @@ StringPiece CommonCertSetsQUIC::GetCommonHashes() const { } StringPiece CommonCertSetsQUIC::GetCert(uint64 hash, uint32 index) const { + StringPiece cert; for (size_t i = 0; i < arraysize(kSets); i++) { if (kSets[i].hash == hash) { - if (index >= kSets[i].num_certs) { - return StringPiece(); + if (index < kSets[i].num_certs) { + cert.set(kSets[i].certs[index], kSets[i].lens[index]); } - return StringPiece(reinterpret_cast<const char*>(kSets[i].certs[index]), - kSets[i].lens[index]); + break; } } - return StringPiece(); + return cert; } // Compare returns a value less than, equal to or greater than zero if |a| is @@ -110,11 +110,7 @@ bool CommonCertSetsQUIC::MatchCert(StringPiece cert, // Binary search for a matching certificate. size_t min = 0; size_t max = kSets[j].num_certs - 1; - for (;;) { - if (max < min) { - break; - } - + 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) { diff --git a/net/quic/crypto/common_cert_set.h b/net/quic/crypto/common_cert_set.h index 6972926..4f5d330 100644 --- a/net/quic/crypto/common_cert_set.h +++ b/net/quic/crypto/common_cert_set.h @@ -20,24 +20,26 @@ class NET_EXPORT_PRIVATE CommonCertSets { virtual ~CommonCertSets(); // GetCommonHashes returns a StringPiece containing the hashes of common sets - // supported by this object. + // supported by this object. The 64-bit hashes are concatenated in the + // StringPiece. virtual base::StringPiece GetCommonHashes() const = 0; - // GetCert returns a specific certificate in the common set identified by - // |hash|. If no such certificate is known, an empty StringPiece is returned. + // GetCert returns a specific certificate (at index |index|) in the common + // set identified by |hash|. If no such certificate is known, an empty + // StringPiece is returned. virtual base::StringPiece GetCert(uint64 hash, uint32 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 in - // |out_hash|, the index in the set in |out_index| and returns true. Otherwise - // it returns false. + // 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(base::StringPiece cert, base::StringPiece common_set_hashes, uint64* out_hash, uint32* out_index) const = 0; }; -// CommonCertSetsQUIC implements the CommonCertSet interface using the default +// CommonCertSetsQUIC implements the CommonCertSets interface using the default // certificate sets. class NET_EXPORT_PRIVATE CommonCertSetsQUIC : public CommonCertSets { public: diff --git a/net/quic/crypto/common_cert_set_test.cc b/net/quic/crypto/common_cert_set_test.cc index 51b41d8..57615c9 100644 --- a/net/quic/crypto/common_cert_set_test.cc +++ b/net/quic/crypto/common_cert_set_test.cc @@ -11,7 +11,7 @@ using base::StringPiece; namespace net { namespace test { -static unsigned char kGIACertificate[] = { +static const unsigned char kGIACertificate[] = { 0x30, 0x82, 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x0b, 0x67, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31, @@ -76,30 +76,30 @@ TEST(CommonCertSets, FindGIA) { StringPiece gia(reinterpret_cast<const char*>(kGIACertificate), sizeof(kGIACertificate)); - CommonCertSetsQUIC set; + CommonCertSetsQUIC sets; const uint64 in_hash = GG_UINT64_C(0xde8086f914a3af54); uint64 hash; uint32 index; - ASSERT_TRUE(set.MatchCert( + ASSERT_TRUE(sets.MatchCert( gia, StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)), &hash, &index)); EXPECT_EQ(in_hash, hash); - StringPiece gia_copy = set.GetCert(hash, index); + StringPiece gia_copy = sets.GetCert(hash, index); EXPECT_FALSE(gia_copy.empty()); ASSERT_EQ(gia.size(), gia_copy.size()); EXPECT_TRUE(0 == memcmp(gia.data(), gia_copy.data(), gia.size())); } TEST(CommonCertSets, NonMatch) { - CommonCertSetsQUIC set; + CommonCertSetsQUIC sets; StringPiece not_a_cert("hello"); const uint64 in_hash = GG_UINT64_C(0xde8086f914a3af54); uint64 hash; uint32 index; - EXPECT_FALSE(set.MatchCert( + EXPECT_FALSE(sets.MatchCert( not_a_cert, StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)), &hash, &index)); diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 16d32d6..3585eb5 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -107,7 +107,6 @@ QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag, const QuicTag** out_tags, size_t* out_len) const { QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); - *out_len = 0; QuicErrorCode ret = QUIC_NO_ERROR; if (it == tag_value_map_.end()) { @@ -221,40 +220,12 @@ QuicErrorCode CryptoHandshakeMessage::GetPOD( return ret; } -// TagToString is a utility function for pretty-printing handshake messages -// 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. -static string TagToString(QuicTag tag) { - char chars[4]; - bool ascii = true; - const QuicTag orig_tag = tag; - - for (size_t i = 0; i < sizeof(chars); i++) { - chars[i] = tag; - if (chars[i] == 0 && i == 3) { - chars[i] = ' '; - } - if (!isprint(static_cast<unsigned char>(chars[i]))) { - ascii = false; - break; - } - tag >>= 8; - } - - if (ascii) { - return string(chars, sizeof(chars)); - } - - return base::UintToString(orig_tag); -} - string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { - string ret = string(2 * indent, ' ') + TagToString(tag_) + "<\n"; + string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n"; ++indent; for (QuicTagValueMap::const_iterator it = tag_value_map_.begin(); it != tag_value_map_.end(); ++it) { - ret += string(2 * indent, ' ') + TagToString(it->first) + ": "; + ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": "; bool done = false; switch (it->first) { @@ -280,7 +251,7 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { if (j > 0) { ret += ","; } - ret += TagToString(tag); + ret += QuicUtils::TagToString(tag); } done = true; } @@ -331,7 +302,7 @@ const char QuicCryptoConfig::kForwardSecureLabel[] = QuicCryptoConfig::QuicCryptoConfig() : version(0), - common_cert_set_(new CommonCertSetsQUIC) { + common_cert_sets(new CommonCertSetsQUIC) { } QuicCryptoConfig::~QuicCryptoConfig() {} @@ -476,13 +447,19 @@ void QuicCryptoClientConfig::FillInchoateClientHello( out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); } - out->SetTaglist(kPDMD, kX509, 0); + if (proof_verifier_.get()) { + out->SetTaglist(kPDMD, kX509, 0); + } - if (common_cert_set_.get()) { - out->SetStringPiece(kCCS, common_cert_set_->GetCommonHashes()); + if (common_cert_sets.get()) { + out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); } const vector<string>& 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()) { vector<uint64> hashes; @@ -492,10 +469,6 @@ void QuicCryptoClientConfig::FillInchoateClientHello( hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); } out->SetVector(kCCRT, hashes); - // 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. } } @@ -557,12 +530,12 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( } size_t key_exchange_index; - if (!CryptoUtils::FindMutualTag(aead, their_aeads, num_their_aeads, - CryptoUtils::PEER_PRIORITY, &out_params->aead, - NULL) || - !CryptoUtils::FindMutualTag( + if (!QuicUtils::FindMutualTag(aead, their_aeads, num_their_aeads, + QuicUtils::PEER_PRIORITY, &out_params->aead, + NULL) || + !QuicUtils::FindMutualTag( kexs, their_key_exchanges, num_their_key_exchanges, - CryptoUtils::PEER_PRIORITY, &out_params->key_exchange, + QuicUtils::PEER_PRIORITY, &out_params->key_exchange, &key_exchange_index)) { *error_details = "Unsupported AEAD or KEXS"; return QUIC_CRYPTO_NO_SUPPORT; @@ -667,7 +640,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( rej.GetStringPiece(kCertificateTag, &cert_bytes)) { vector<string> certs; if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs, - common_cert_set_.get(), &certs)) { + common_cert_sets.get(), &certs)) { *error_details = "Certificate data invalid"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index 6b6c320..0774905 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -14,7 +14,6 @@ #include "net/base/net_export.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_time.h" namespace net { @@ -188,7 +187,7 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { // Authenticated encryption with associated data (AEAD) algorithms. QuicTagVector aead; - scoped_ptr<CommonCertSets> common_cert_set_; + scoped_ptr<CommonCertSets> common_cert_sets; private: DISALLOW_COPY_AND_ASSIGN(QuicCryptoConfig); @@ -311,9 +310,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { const ProofVerifier* proof_verifier() const; // SetProofVerifier takes ownership of a |ProofVerifier| that clients are - // free to use in order to verify certificate chains from servers. Setting a - // |ProofVerifier| does not alter the behaviour of the - // QuicCryptoClientConfig, it's just a place to store it. + // free to use in order to verify certificate chains from servers. If a + // ProofVerifier is set then the client will request a certificate chain from + // the server. void SetProofVerifier(ProofVerifier* verifier); private: diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc index f3d86a5..e2e45f3 100644 --- a/net/quic/crypto/crypto_handshake_test.cc +++ b/net/quic/crypto/crypto_handshake_test.cc @@ -42,10 +42,9 @@ class QuicCryptoServerConfigPeer { TEST(QuicCryptoServerConfigTest, ServerConfig) { QuicCryptoServerConfig server("source address token secret"); MockClock clock; - CryptoHandshakeMessage extra_tags; scoped_ptr<CryptoHandshakeMessage>( - server.AddDefaultConfig(QuicRandom::GetInstance(), &clock, extra_tags, + server.AddDefaultConfig(QuicRandom::GetInstance(), &clock, QuicCryptoServerConfig::kDefaultExpiry)); } diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 32ec7b1..c32884a 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -60,6 +60,7 @@ const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state // lifetime const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout +const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection. const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name // indication const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc index e4ee400..d7eca6a 100644 --- a/net/quic/crypto/crypto_server_config.cc +++ b/net/quic/crypto/crypto_server_config.cc @@ -52,7 +52,11 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( // Salsa20+Poly1305). : strike_register_lock_(), source_address_token_encrypter_(new Aes128GcmEncrypter), - source_address_token_decrypter_(new Aes128GcmDecrypter) { + source_address_token_decrypter_(new Aes128GcmDecrypter), + strike_register_max_entries_(1 << 10), + strike_register_window_secs_(600), + source_address_token_future_secs_(3600), + source_address_token_lifetime_secs_(86400) { crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, "QUIC source address token key", source_address_token_encrypter_->GetKeySize(), @@ -69,7 +73,6 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() { QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( QuicRandom* rand, const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags, uint64 expiry_time) { CryptoHandshakeMessage msg; @@ -101,8 +104,6 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( msg.SetTaglist(kAEAD, kAESG, 0); msg.SetValue(kVERS, static_cast<uint16>(0)); msg.SetStringPiece(kPUBS, encoded_public_values); - msg.Insert(extra_tags.tag_value_map().begin(), - extra_tags.tag_value_map().end()); if (expiry_time == 0) { const QuicWallTime now = clock->WallNow(); @@ -277,10 +278,9 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( QuicRandom* rand, const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags, uint64 expiry_time) { - scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig( - rand, clock, extra_tags, expiry_time)); + scoped_ptr<QuicServerConfigProtobuf> config( + DefaultConfig(rand, clock, expiry_time)); return AddConfig(config.get()); } @@ -334,10 +334,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( if (strike_register_.get() == NULL) { strike_register_.reset(new StrikeRegister( - // TODO(agl): these magic numbers should come from config. - 1024 /* max entries */, + strike_register_max_entries_, static_cast<uint32>(now.ToUNIXSeconds()), - 600 /* window secs */, config->orbit)); + strike_register_window_secs_, + config->orbit)); } unique_by_strike_register = strike_register_->Insert( reinterpret_cast<const uint8*>(client_nonce.data()), @@ -401,7 +401,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( const string compressed = CertCompressor::CompressChain( *certs, their_common_set_hashes, their_cached_cert_hashes, - config->common_cert_set_.get()); + config->common_cert_sets.get()); // kMaxUnverifiedSize is the number of bytes that the certificate chain // and signature can consume before we will demand a valid @@ -433,12 +433,12 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( } size_t key_exchange_index; - if (!CryptoUtils::FindMutualTag(config->aead, their_aeads, num_their_aeads, - CryptoUtils::LOCAL_PRIORITY, ¶ms->aead, - NULL) || - !CryptoUtils::FindMutualTag( + if (!QuicUtils::FindMutualTag(config->aead, their_aeads, num_their_aeads, + QuicUtils::LOCAL_PRIORITY, ¶ms->aead, + NULL) || + !QuicUtils::FindMutualTag( config->kexs, their_key_exchanges, num_their_key_exchanges, - CryptoUtils::LOCAL_PRIORITY, ¶ms->key_exchange, + QuicUtils::LOCAL_PRIORITY, ¶ms->key_exchange, &key_exchange_index)) { *error_details = "Unsupported AEAD or KEXS"; return QUIC_CRYPTO_NO_SUPPORT; @@ -523,6 +523,28 @@ void QuicCryptoServerConfig::SetEphemeralKeySource( ephemeral_key_source_.reset(ephemeral_key_source); } +void QuicCryptoServerConfig::set_strike_register_max_entries( + uint32 max_entries) { + DCHECK(!strike_register_.get()); + strike_register_max_entries_ = max_entries; +} + +void QuicCryptoServerConfig::set_strike_register_window_secs( + uint32 window_secs) { + DCHECK(!strike_register_.get()); + strike_register_window_secs_ = window_secs; +} + +void QuicCryptoServerConfig::set_source_address_token_future_secs( + uint32 future_secs) { + source_address_token_future_secs_ = future_secs; +} + +void QuicCryptoServerConfig::set_source_address_token_lifetime_secs( + uint32 lifetime_secs) { + source_address_token_lifetime_secs_ = lifetime_secs; +} + string QuicCryptoServerConfig::NewSourceAddressToken( const IPEndPoint& ip, QuicRandom* rand, @@ -600,15 +622,13 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken( QuicWallTime::FromUNIXSeconds(source_address_token.timestamp())); const QuicTime::Delta delta(now.AbsoluteDifference(timestamp)); - // TODO(agl): consider whether and how these magic values should be moved to - // a config. - if (now.IsBefore(timestamp) && delta.ToSeconds() > 3600) { - // We only allow timestamps to be from an hour in the future. + if (now.IsBefore(timestamp) && + delta.ToSeconds() > source_address_token_future_secs_) { return false; } - if (now.IsAfter(timestamp) && delta.ToSeconds() > 86400) { - // We allow one day into the past. + if (now.IsAfter(timestamp) && + delta.ToSeconds() > source_address_token_lifetime_secs_) { return false; } diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/crypto_server_config.h index 588d83d4..5c7d4da 100644 --- a/net/quic/crypto/crypto_server_config.h +++ b/net/quic/crypto/crypto_server_config.h @@ -54,14 +54,12 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { static const char TESTING[]; // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for - // using in tests. |extra_tags| contains additional key/value pairs that will - // be inserted into the config. If |expiry_time| is non-zero then it's used - // as the expiry for the server config in UNIX epoch seconds. Otherwise the - // default expiry time is six months from now. + // using in tests. If |expiry_time| is non-zero then it's used as the expiry + // for the server config in UNIX epoch seconds. Otherwise the default expiry + // time is six months from now. static QuicServerConfigProtobuf* DefaultConfig( QuicRandom* rand, const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags, uint64 expiry_time); // AddConfig adds a QuicServerConfigProtobuf to the availible configurations. @@ -69,12 +67,12 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // takes ownership of the CryptoHandshakeMessage. CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf); - // AddDefaultConfig creates a config and then calls AddConfig to add it. See - // the comment for |DefaultConfig| for details of the arguments. + // 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 CryptoHandshakeMessage& extra_tags, uint64 expiry_time); // ProcessClientHello processes |client_hello| and decides whether to accept @@ -97,7 +95,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, QuicGuid guid, const IPEndPoint& client_ip, - const QuicClock* now, + const QuicClock* clock, QuicRandom* rand, QuicCryptoNegotiatedParameters* params, CryptoHandshakeMessage* out, @@ -113,6 +111,27 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // per-connection. void SetEphemeralKeySource(EphemeralKeySource* ephemeral_key_source); + // set_strike_register_max_entries sets the maximum number of entries that + // the internal strike register will hold. If the strike register fills up + // then the oldest entries (by the client's clock) will be dropped. + void set_strike_register_max_entries(uint32 max_entries); + + // set_strike_register_window_secs sets the number of seconds around the + // current time that the strike register will attempt to be authoritative + // for. Setting a larger value allows for greater client clock-skew, but + // means that the quiescent startup period must be longer. + void set_strike_register_window_secs(uint32 window_secs); + + // 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 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 lifetime_secs); + private: friend class test::QuicCryptoServerConfigPeer; @@ -177,6 +196,13 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // ephemeral_key_source_ contains an object that caches ephemeral keys for a // short period of time. scoped_ptr<EphemeralKeySource> ephemeral_key_source_; + + // These fields store configuration values. See the comments for their + // respective setter functions. + uint32 strike_register_max_entries_; + uint32 strike_register_window_secs_; + uint32 source_address_token_future_secs_; + uint32 source_address_token_lifetime_secs_; }; } // namespace net diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc index bf1f4d2..a95ac42 100644 --- a/net/quic/crypto/crypto_utils.cc +++ b/net/quic/crypto/crypto_utils.cc @@ -17,53 +17,6 @@ using std::string; namespace net { -// static -bool CryptoUtils::FindMutualTag(const QuicTagVector& our_tags_vector, - const QuicTag* their_tags, - size_t num_their_tags, - Priority priority, - QuicTag* out_result, - size_t* out_index) { - if (our_tags_vector.empty()) { - return false; - } - const size_t num_our_tags = our_tags_vector.size(); - const QuicTag* our_tags = &our_tags_vector[0]; - - size_t num_priority_tags, num_inferior_tags; - const QuicTag* priority_tags; - const QuicTag* inferior_tags; - if (priority == LOCAL_PRIORITY) { - num_priority_tags = num_our_tags; - priority_tags = our_tags; - num_inferior_tags = num_their_tags; - inferior_tags = their_tags; - } else { - num_priority_tags = num_their_tags; - priority_tags = their_tags; - num_inferior_tags = num_our_tags; - inferior_tags = our_tags; - } - - for (size_t i = 0; i < num_priority_tags; i++) { - for (size_t j = 0; j < num_inferior_tags; j++) { - if (priority_tags[i] == inferior_tags[j]) { - *out_result = priority_tags[i]; - if (out_index) { - if (priority == LOCAL_PRIORITY) { - *out_index = j; - } else { - *out_index = i; - } - } - return true; - } - } - } - - return false; -} - void CryptoUtils::GenerateNonce(QuicWallTime now, QuicRandom* random_generator, StringPiece orbit, diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h index 6c607a5..e516718 100644 --- a/net/quic/crypto/crypto_utils.h +++ b/net/quic/crypto/crypto_utils.h @@ -23,31 +23,11 @@ struct QuicCryptoNegotiatedParameters; class NET_EXPORT_PRIVATE CryptoUtils { public: - enum Priority { - LOCAL_PRIORITY, - PEER_PRIORITY, - }; - enum Perspective { SERVER, CLIENT, }; - // FindMutualTag sets |out_result| to the first tag in the priority list that - // is also in the other list and returns true. If there is no intersection it - // returns false. - // - // Which list has priority is determined by |priority|. - // - // If |out_index| is non-NULL and a match is found then the index of that - // match in |their_tags| is written to |out_index|. - static bool FindMutualTag(const QuicTagVector& our_tags, - const QuicTag* their_tags, - size_t num_their_tags, - Priority priority, - QuicTag* out_result, - size_t* out_index); - // Generates the connection nonce. The nonce is formed as: // <4 bytes> current time // <8 bytes> |orbit| (or random if |orbit| is empty) diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc index 30ca803..796fef6 100644 --- a/net/quic/quic_client_session.cc +++ b/net/quic/quic_client_session.cc @@ -24,9 +24,10 @@ QuicClientSession::QuicClientSession( QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, const string& server_hostname, + const QuicConfig& config, QuicCryptoClientConfig* crypto_config, NetLog* net_log) - : QuicSession(connection, false), + : QuicSession(connection, config, false), weak_factory_(this), stream_factory_(stream_factory), socket_(socket), @@ -35,13 +36,12 @@ QuicClientSession::QuicClientSession( num_total_streams_(0), net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)), logger_(net_log_) { - config_.SetDefaults(); + QuicSession::config()->SetDefaults(); crypto_stream_.reset( crypto_client_stream_factory ? crypto_client_stream_factory->CreateQuicCryptoClientStream( - server_hostname, config_, this, crypto_config) : - new QuicCryptoClientStream( - server_hostname, config_, this, crypto_config)); + server_hostname, this, crypto_config) : + new QuicCryptoClientStream(server_hostname, this, crypto_config)); connection->set_debug_visitor(&logger_); // TODO(rch): pass in full host port proxy pair diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h index fbbbecf..2a63ca1 100644 --- a/net/quic/quic_client_session.h +++ b/net/quic/quic_client_session.h @@ -26,6 +26,10 @@ class QuicConnectionHelper; class QuicCryptoClientStreamFactory; class QuicStreamFactory; +namespace test { +class QuicClientSessionPeer; +} // namespace test + class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { public: // Constructs a new session which will own |connection| and |helper|, but @@ -36,6 +40,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, const std::string& server_hostname, + const QuicConfig& config, QuicCryptoClientConfig* crypto_config, NetLog* net_log); @@ -70,13 +75,11 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { QuicStreamId id) OVERRIDE; private: + friend class test::QuicClientSessionPeer; // A completion callback invoked when a read completes. void OnReadComplete(int result); base::WeakPtrFactory<QuicClientSession> weak_factory_; - // config_ contains non-crypto configuration options negotiated in the crypto - // handshake. - QuicConfig config_; scoped_ptr<QuicCryptoClientStream> crypto_stream_; QuicStreamFactory* stream_factory_; scoped_ptr<DatagramClientSocket> socket_; diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index 3883a05..cab44a7 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -13,6 +13,7 @@ #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/test_tools/crypto_test_utils.h" +#include "net/quic/test_tools/quic_client_session_peer.h" #include "net/quic/test_tools/quic_test_utils.h" using testing::_; @@ -29,8 +30,9 @@ class QuicClientSessionTest : public ::testing::Test { : guid_(1), connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)), session_(connection_, NULL, NULL, NULL, kServerHostname, - &crypto_config_, &net_log_) { + QuicConfig(), &crypto_config_, &net_log_) { crypto_config_.SetDefaults(); + QuicClientSessionPeer::SetMaxOpenStreams(&session_, 1, 1); } void CompleteCryptoHandshake() { @@ -49,7 +51,6 @@ class QuicClientSessionTest : public ::testing::Test { MockRandom random_; QuicConnectionVisitorInterface* visitor_; TestCompletionCallback callback_; - QuicConfig* config_; QuicCryptoClientConfig crypto_config_; }; diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc index 867cb6d..eacde84 100644 --- a/net/quic/quic_config.cc +++ b/net/quic/quic_config.cc @@ -4,126 +4,340 @@ #include "net/quic/quic_config.h" +#include <algorithm> + +#include "base/logging.h" + using std::string; namespace net { -QuicNegotiatedParameters::QuicNegotiatedParameters() - : idle_connection_state_lifetime(QuicTime::Delta::Zero()), - keepalive_timeout(QuicTime::Delta::Zero()) { +QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence) + : tag_(tag), + presence_(presence), + negotiated_(false) { } -QuicConfig::QuicConfig() - : idle_connection_state_lifetime_(QuicTime::Delta::Zero()), - keepalive_timeout_(QuicTime::Delta::Zero()) { +QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence) + : QuicNegotiableValue(tag, presence) { } -QuicConfig::~QuicConfig() { +void QuicNegotiableUint32::set(uint32 max, uint32 default_value) { + DCHECK_LE(default_value, max); + max_value_ = max; + default_value_ = default_value; } -void QuicConfig::SetDefaults() { - idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(300); - keepalive_timeout_ = QuicTime::Delta::Zero(); - congestion_control_.clear(); - congestion_control_.push_back(kQBIC); +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_); + } } -bool QuicConfig::SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg) { - const QuicTag* cgst; - size_t num_cgst; - QuicErrorCode error; +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_; - error = scfg.GetTaglist(kCGST, &cgst, &num_cgst); + 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 false; + return error; } - congestion_control_.assign(cgst, cgst + num_cgst); + negotiated_ = true; + negotiated_value_ = std::min(value, max_value_); - uint32 idle; - error = scfg.GetUint32(kICSL, &idle); + 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 false; + return error; } - idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(idle); - - keepalive_timeout_ = QuicTime::Delta::Zero(); - uint32 keepalive; - error = scfg.GetUint32(kKATO, &keepalive); - // KATO is optional. - if (error == QUIC_NO_ERROR) { - keepalive_timeout_ = QuicTime::Delta::FromSeconds(keepalive); + if (value > max_value_) { + *error_details = "Invalid value received for " + + QuicUtils::TagToString(tag_); + return QUIC_INVALID_NEGOTIATED_VALUE; } - return true; + negotiated_ = true; + negotiated_value_ = value; + return QUIC_NO_ERROR; } -void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { - out->SetValue( - kICSL, static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds())); - out->SetValue(kKATO, static_cast<uint32>(keepalive_timeout_.ToSeconds())); - out->SetVector(kCGST, congestion_control_); +QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, Presence presence) + : QuicNegotiableValue(tag, presence) { +} + +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_; } -QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( +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, - CryptoUtils::Priority priority, - QuicNegotiatedParameters* out_params, + 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_; - const QuicTag* their_congestion_controls; - size_t num_their_congestion_controls; - QuicErrorCode error; + case QUIC_NO_ERROR: + break; + default: + *error_details = "Bad " + QuicUtils::TagToString(tag_); + break; + } + return error; +} - error = msg.GetTaglist(kCGST, &their_congestion_controls, - &num_their_congestion_controls); +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) { - *error_details = "Missing CGST"; return error; } - if (!CryptoUtils::FindMutualTag(congestion_control_, - their_congestion_controls, - num_their_congestion_controls, - priority, - &out_params->congestion_control, - NULL)) { - *error_details = "Unsuported CGST"; + 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; } - uint32 idle; - error = msg.GetUint32(kICSL, &idle); + 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) { - *error_details = "Missing ICSL"; return error; } - out_params->idle_connection_state_lifetime = QuicTime::Delta::FromSeconds( - std::min(static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds()), - idle)); - - uint32 keepalive; - error = msg.GetUint32(kKATO, &keepalive); - switch (error) { - case QUIC_NO_ERROR: - out_params->keepalive_timeout = QuicTime::Delta::FromSeconds( - std::min(static_cast<uint32>(keepalive_timeout_.ToSeconds()), - keepalive)); - break; - case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: - // KATO is optional. - out_params->keepalive_timeout = QuicTime::Delta::Zero(); - break; - default: - *error_details = "Bad KATO"; - 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) { + idle_connection_state_lifetime_seconds_.set(0, 0); + keepalive_timeout_seconds_.set(0, 0); +} + +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(); +} + +bool QuicConfig::negotiated() { + return congestion_control_.negotiated() && + idle_connection_state_lifetime_seconds_.negotiated() && + keepalive_timeout_seconds_.negotiated() && + max_streams_per_connection_.negotiated(); +} + +void QuicConfig::SetDefaults() { + congestion_control_.set(QuicTagVector(1, kQBIC), kQBIC); + idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs, + kDefaultTimeoutSecs); + // kKATO is optional. Return 0 if not negotiated. + keepalive_timeout_seconds_.set(0, 0); + max_streams_per_connection_.set(kDefaultMaxStreamsPerConnection, + kDefaultMaxStreamsPerConnection); +} + +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); +} + +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); + } + 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); + } + return error; +} + } // namespace net diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h index 013a4f2..0f11493 100644 --- a/net/quic/quic_config.h +++ b/net/quic/quic_config.h @@ -7,21 +7,126 @@ #include <string> +#include "base/basictypes.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" +#include "net/quic/quic_utils.h" namespace net { -// QuicNegotiatedParameters contains non-crypto parameters that are agreed upon -// during the crypto handshake. -class NET_EXPORT_PRIVATE QuicNegotiatedParameters { +class NET_EXPORT_PRIVATE QuicNegotiableValue { public: - QuicNegotiatedParameters(); + enum Presence { + // 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, + }; - QuicTag congestion_control; - QuicTime::Delta idle_connection_state_lifetime; - QuicTime::Delta keepalive_timeout; + QuicNegotiableValue(QuicTag tag, Presence presence); + + bool negotiated() const { + return negotiated_; + } + + protected: + const QuicTag tag_; + const Presence presence_; + bool negotiated_; +}; + +class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { + public: + QuicNegotiableUint32(QuicTag name, Presence presence); + + // 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 max, uint32 default_value); + + // Returns the value negotiated if |negotiated_| is true, otherwise returns + // default_value_ (used to set default values before negotiation finishes). + uint32 GetUint32() 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; + + // Sets |negotiated_value_| to the minimum of |max_value_| and the + // corresponding value from |client_hello|. If the corresponding value is + // missing and PRESENCE_OPTIONAL then |negotiated_value_| is set to + // |default_value_|. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + std::string* error_details); + + // Sets the |negotiated_value_| to the corresponding value from + // |server_hello|. Returns error if the value received in |server_hello| is + // greater than |max_value_|. If the corresponding value is missing and + // PRESENCE_OPTIONAL then |negotiated_value_| is set to |0|, + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + std::string* error_details); + + private: + // 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 |max_value_|. + QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg, + uint32* out, + std::string* error_details) const; + + uint32 max_value_; + uint32 default_value_; + uint32 negotiated_value_; +}; + +class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue { + public: + QuicNegotiableTag(QuicTag name, Presence presence); + ~QuicNegotiableTag(); + + // Sets the possible values that |negotiated_tag_| can take after negotiation + // and the default value that |negotiated_tag_| takes if OPTIONAL and *HLO + // msg doesn't contain tag |name_|. + void set(const QuicTagVector& possible_values, QuicTag default_value); + + // Returns the negotiated tag if |negotiated_| is true, otherwise returns + // |default_value_| (used to set default values before negotiation finishes). + QuicTag GetTag() const; + + // Serialises |name_| and vector (either possible or negotiated) to |out|. If + // |negotiated_| is true then |negotiated_tag_| is serialised, otherwise + // |possible_values_| is serialised. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + + // Selects the tag common to both tags in |client_hello| for |name_| and + // |possible_values_| with preference to tag in |possible_values_|. The + // selected tag is set as |negotiated_tag_|. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + std::string* error_details); + + // Sets the value for |name_| tag in |server_hello| as |negotiated_value_|. + // Returns error if the value received in |server_hello| isn't present in + // |possible_values_|. + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + std::string* error_details); + + private: + // Reads the vector corresponding to |name_| from |msg| into |out|. If the + // |name_| is absent in |msg| and |presence_| is set to OPTIONAL |out| is set + // to |possible_values_|. + QuicErrorCode ReadVector(const CryptoHandshakeMessage& msg, + const QuicTag** out, + size_t* out_length, + std::string* error_details) const; + + QuicTag negotiated_tag_; + QuicTagVector possible_values_; + QuicTag default_value_; }; // QuicConfig contains non-crypto configuration options that are negotiated in @@ -31,39 +136,52 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicConfig(); ~QuicConfig(); + void set_congestion_control(const QuicTagVector& congestion_control, + QuicTag default_congestion_control); + + QuicTag congestion_control() const; + void set_idle_connection_state_lifetime( - QuicTime::Delta idle_connection_state_lifetime) { - idle_connection_state_lifetime_ = idle_connection_state_lifetime; - } + QuicTime::Delta max_idle_connection_state_lifetime, + QuicTime::Delta default_idle_conection_state_lifetime); + + QuicTime::Delta idle_connection_state_lifetime() const; + + QuicTime::Delta keepalive_timeout() const; + + void set_max_streams_per_connection(size_t max_streams, + size_t default_streams); + + uint32 max_streams_per_connection() const; + + bool negotiated(); // SetDefaults sets the members to sensible, default values. void SetDefaults(); - // SetFromMessage extracts the non-crypto configuration from |msg| and sets - // the members of this object to match. This is expected to be called in the - // case of a server which is loading a server config. The server config - // contains the non-crypto parameters and so the server will need to keep its - // QuicConfig in sync with the server config that it'll be sending to - // clients. - bool SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg); - // ToHandshakeMessage serializes the settings in this object as a series of // tags /value pairs and adds them to |out|. void ToHandshakeMessage(CryptoHandshakeMessage* out) const; - QuicErrorCode ProcessFinalPeerHandshake( - const CryptoHandshakeMessage& peer_handshake, - CryptoUtils::Priority priority, - QuicNegotiatedParameters* out_params, - std::string* error_details) const; + // Calls ProcessClientHello on each negotiable parameter. On failure returns + // the corresponding QuicErrorCode and sets detailed error in |error_details|. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + std::string* error_details); + + // Calls ProcessServerHello on each negotiable parameter. On failure returns + // the corresponding QuicErrorCode and sets detailed error in |error_details|. + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + std::string* error_details); private: // Congestion control feedback type. - QuicTagVector congestion_control_; + QuicNegotiableTag congestion_control_; // Idle connection state lifetime - QuicTime::Delta idle_connection_state_lifetime_; + QuicNegotiableUint32 idle_connection_state_lifetime_seconds_; // Keepalive timeout, or 0 to turn off keepalive probes - QuicTime::Delta keepalive_timeout_; + QuicNegotiableUint32 keepalive_timeout_seconds_; + // Maximum number of streams that the connection can support. + QuicNegotiableUint32 max_streams_per_connection_; }; } // namespace net diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc new file mode 100644 index 0000000..eeaa97d --- /dev/null +++ b/net/quic/quic_config_test.cc @@ -0,0 +1,167 @@ +// 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 "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { +namespace { + +class QuicConfigTest : public ::testing::Test { + protected: + QuicConfigTest() { + config_.SetDefaults(); + } + + QuicConfig config_; +}; + +TEST_F(QuicConfigTest, ToHandshakeMessage) { + config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5), + QuicTime::Delta::FromSeconds(2)); + config_.set_max_streams_per_connection(4, 2); + CryptoHandshakeMessage msg; + config_.ToHandshakeMessage(&msg); + + uint32 value; + QuicErrorCode error = msg.GetUint32(kICSL, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(5u, value); + + error = msg.GetUint32(kMSPC, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(4u, value); + + const QuicTag* out; + size_t out_len; + error = msg.GetTaglist(kCGST, &out, &out_len); + EXPECT_EQ(1u, out_len); + EXPECT_EQ(kQBIC, *out); +} + +TEST_F(QuicConfigTest, ProcessClientHello) { + QuicConfig client_config; + QuicTagVector cgst; + cgst.push_back(kINAR); + cgst.push_back(kQBIC); + client_config.set_congestion_control(cgst, kQBIC); + client_config.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs), + QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs)); + client_config.set_max_streams_per_connection( + 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); + + CryptoHandshakeMessage msg; + client_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + EXPECT_EQ(kQBIC, config_.congestion_control()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs), + config_.idle_connection_state_lifetime()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + config_.max_streams_per_connection()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); +} + +TEST_F(QuicConfigTest, ProcessServerHello) { + QuicConfig server_config; + QuicTagVector cgst; + cgst.push_back(kQBIC); + server_config.set_congestion_control(cgst, kQBIC); + server_config.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2), + QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2)); + server_config.set_max_streams_per_connection( + kDefaultMaxStreamsPerConnection / 2, + kDefaultMaxStreamsPerConnection / 2); + + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + EXPECT_EQ(kQBIC, config_.congestion_control()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2), + config_.idle_connection_state_lifetime()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2, + config_.max_streams_per_connection()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); +} + +TEST_F(QuicConfigTest, MissingValueInCHLO) { + CryptoHandshakeMessage msg; + msg.SetValue(kICSL, 1); + msg.SetVector(kCGST, QuicTagVector(1, kQBIC)); + // Missing kMSPC. KATO is optional. + string error_details; + const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error); +} + +TEST_F(QuicConfigTest, MissingValueInSHLO) { + CryptoHandshakeMessage msg; + msg.SetValue(kICSL, 1); + msg.SetValue(kMSPC, 3); + // Missing CGST. KATO is optional. + string error_details; + const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error); +} + +TEST_F(QuicConfigTest, OutOfBoundSHLO) { + QuicConfig server_config; + server_config.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs), + QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs)); + + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error); +} + +TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) { + QuicConfig server_config; + QuicTagVector cgst; + cgst.push_back(kQBIC); + cgst.push_back(kINAR); + server_config.set_congestion_control(cgst, kQBIC); + + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error); +} + +TEST_F(QuicConfigTest, NoOverLapInCGST) { + QuicConfig server_config; + server_config.SetDefaults(); + QuicTagVector cgst; + cgst.push_back(kINAR); + server_config.set_congestion_control(cgst, kINAR); + + CryptoHandshakeMessage msg; + string error_details; + server_config.ToHandshakeMessage(&msg); + const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); + LOG(INFO) << QuicUtils::ErrorToString(error); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 481fca9..51319ff 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -30,17 +30,6 @@ namespace { // This will likely have to be tuned. const QuicPacketSequenceNumber kMaxPacketGap = 5000; -// The maximum number of nacks which can be transmitted in a single ack packet -// without exceeding kMaxPacketSize. -// TODO(satyamshekhar): Get rid of magic numbers and move this to protocol.h -// 16 - Min ack frame size. -// 16 - Crypto hash for integrity. Not a static value. Use -// QuicEncrypter::GetMaxPlaintextSize. -size_t GetMaxUnackedPackets(bool include_version) { - return (kMaxPacketSize - GetPacketHeaderSize(include_version) - 16 - 16) / - kSequenceNumberSize; -} - // We want to make sure if we get a large nack packet, we don't queue up too // many packets at once. 10 is arbitrary. const int kMaxRetransmissionsPerAck = 10; @@ -58,7 +47,11 @@ const int kMaxPacketsToSerializeAtOnce = 6; // Limit the number of packets we send per retransmission-alarm so we // eventually cede. 10 is arbitrary. -const int kMaxPacketsPerRetransmissionAlarm = 10; +const size_t kMaxPacketsPerRetransmissionAlarm = 10; + +// Limit the number of FEC groups to two. If we get enough out of order packets +// that this becomes limiting, we can revisit. +const size_t kMaxFecGroups = 2; bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { QuicPacketSequenceNumber delta = (a > b) ? a - b : b - a; @@ -91,20 +84,20 @@ QuicConnection::QuicConnection(QuicGuid guid, debug_visitor_(NULL), packet_creator_(guid_, &framer_, random_generator_, is_server), packet_generator_(this, &packet_creator_), - timeout_(QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)), + timeout_(QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)), time_of_last_received_packet_(clock_->ApproximateNow()), time_of_last_sent_packet_(clock_->ApproximateNow()), time_largest_observed_(QuicTime::Zero()), congestion_manager_(clock_, kTCP), version_negotiation_state_(START_NEGOTIATION), quic_version_(kQuicVersion1), + max_packets_per_retransmission_alarm_(kMaxPacketsPerRetransmissionAlarm), is_server_(is_server), connected_(true), received_truncated_ack_(false), - send_ack_in_response_to_packet_(false) { + send_ack_in_response_to_packet_(false), + address_migrating_(false) { helper_->SetConnection(this); - // TODO(satyamshekhar): Have a smaller timeout till version is negotiated and - // connection is established (CHLO fully processed). helper_->SetTimeoutAlarm(timeout_); framer_.set_visitor(this); framer_.set_entropy_calculator(&entropy_manager_); @@ -160,10 +153,6 @@ void QuicConnection::OnPacket() { time_of_last_received_packet_ = clock_->Now(); DVLOG(1) << "time of last received packet: " << time_of_last_received_packet_.ToDebuggingValue(); - - // TODO(alyssar, rch) handle migration! - self_address_ = last_self_address_; - peer_address_ = last_peer_address_; } void QuicConnection::OnPublicResetPacket( @@ -175,6 +164,12 @@ void QuicConnection::OnPublicResetPacket( } bool QuicConnection::OnProtocolVersionMismatch(QuicTag received_version) { + if (address_migrating_) { + SendConnectionCloseWithDetails( + QUIC_ERROR_MIGRATING_ADDRESS, + "Address migration is not yet a supported feature"); + } + // TODO(satyamshekhar): Implement no server state in this mode. if (!is_server_) { LOG(DFATAL) << "Framer called OnProtocolVersionMismatch for server. " @@ -226,6 +221,11 @@ bool QuicConnection::OnProtocolVersionMismatch(QuicTag received_version) { // Handles version negotiation for client connection. void QuicConnection::OnVersionNegotiationPacket( const QuicVersionNegotiationPacket& packet) { + if (address_migrating_) { + SendConnectionCloseWithDetails( + QUIC_ERROR_MIGRATING_ADDRESS, + "Address migration is not yet a supported feature"); + } if (is_server_) { LOG(DFATAL) << "Framer parsed VersionNegotiationPacket for server." << "Closing connection."; @@ -268,6 +268,13 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { debug_visitor_->OnPacketHeader(header); } + if (address_migrating_) { + SendConnectionCloseWithDetails( + QUIC_ERROR_MIGRATING_ADDRESS, + "Address migration is not yet a supported feature"); + return false; + } + // Will be decrement below if we fall through to return true; ++stats_.packets_dropped; @@ -281,7 +288,8 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { last_header_.packet_sequence_number)) { DLOG(INFO) << ENDPOINT << "Packet " << header.packet_sequence_number << " out of bounds. Discarding"; - // TODO(alyssar) close the connection entirely. + SendConnectionCloseWithDetails(QUIC_INVALID_PACKET_HEADER, + "Packet sequence number out of bounds"); return false; } @@ -326,7 +334,9 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { void QuicConnection::OnFecProtectedPayload(StringPiece payload) { DCHECK_NE(0u, last_header_.fec_group); QuicFecGroup* group = GetFecGroup(); - group->Update(last_header_, payload); + if (group != NULL) { + group->Update(last_header_, payload); + } } bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { @@ -356,16 +366,9 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { return false; } - // TODO(satyamshekhar): Not true if missing_packets.size() was actually - // kMaxUnackedPackets. This can result in a dead connection if all the - // missing packets get lost during retransmission. Now the new packets(or the - // older packets) will not be retransmitted due to RTO - // since received_truncated_ack_ is true and their sequence_number is > - // peer_largest_observed_packet. Fix either by resetting it in - // MaybeRetransmitPacketForRTO or keeping an explicit flag for ack truncation. received_truncated_ack_ = incoming_ack.received_info.missing_packets.size() >= - GetMaxUnackedPackets(last_header_.public_header.version_flag); + QuicFramer::GetMaxUnackedPackets(last_header_.public_header.version_flag); UpdatePacketInformationReceivedByPeer(incoming_ack); UpdatePacketInformationSentByPeer(incoming_ack); @@ -422,7 +425,8 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { // We can't have too many unacked packets, or our ack frames go over // kMaxPacketSize. DCHECK_LE(incoming_ack.received_info.missing_packets.size(), - GetMaxUnackedPackets(last_header_.public_header.version_flag)); + QuicFramer::GetMaxUnackedPackets( + last_header_.public_header.version_flag)); if (incoming_ack.sent_info.least_unacked < peer_least_packet_awaiting_ack_) { DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " @@ -581,8 +585,10 @@ void QuicConnection::UpdatePacketInformationSentByPeer( void QuicConnection::OnFecData(const QuicFecData& fec) { DCHECK_NE(0u, last_header_.fec_group); QuicFecGroup* group = GetFecGroup(); - group->UpdateFec(last_header_.packet_sequence_number, - last_header_.fec_entropy_flag, fec); + if (group != NULL) { + group->UpdateFec(last_header_.packet_sequence_number, + last_header_.fec_entropy_flag, fec); + } } bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { @@ -618,8 +624,12 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { } void QuicConnection::OnPacketComplete() { - // TODO(satyamshekhar): Don't do anything if this packet closed the - // connection. + // Don't do anything if this packet closed the connection. + if (!connected_) { + last_stream_frames_.clear(); + return; + } + if (!last_packet_revived_) { DLOG(INFO) << ENDPOINT << "Got packet " << last_header_.packet_sequence_number @@ -717,8 +727,19 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, } last_packet_revived_ = false; last_size_ = packet.length(); - last_self_address_ = self_address; - last_peer_address_ = peer_address; + + address_migrating_ = false; + + if (peer_address_.address().empty()) { + peer_address_ = peer_address; + } + if (self_address_.address().empty()) { + self_address_ = self_address; + } + + if (!(peer_address == peer_address_) && (self_address == self_address_)) { + address_migrating_ = true; + } stats_.bytes_received += packet.length(); ++stats_.packets_received; @@ -826,12 +847,18 @@ bool QuicConnection::MaybeRetransmitPacketForRTO( return true; } + RetransmissionMap::iterator retransmission_it = + retransmission_map_.find(sequence_number); // If the packet hasn't been acked and we're getting truncated acks, ignore // any RTO for packets larger than the peer's largest observed packet; it may // have been received by the peer and just wasn't acked due to the ack frame // running out of space. if (received_truncated_ack_ && - sequence_number > peer_largest_observed_packet_) { + sequence_number > peer_largest_observed_packet_ && + // We allow retransmission of already retransmitted packets so that we + // retransmit packets that were retransmissions of the packet with + // sequence number < the largest observed field of the truncated ack. + retransmission_it->second.number_retransmissions == 0) { return false; } else { ++stats_.rto_count; @@ -901,8 +928,7 @@ bool QuicConnection::CanWrite(Retransmission retransmission, QuicTime::Delta delay = congestion_manager_.TimeUntilSend( now, retransmission, retransmittable); if (delay.IsInfinite()) { - // TODO(pwestin): should be false but trigger other bugs see b/8350327. - return true; + return false; } // If the scheduler requires a delay, then we can not send this packet now. @@ -983,20 +1009,23 @@ bool QuicConnection::WritePacket(EncryptionLevel level, int error; QuicTime now = clock_->Now(); - int rv = helper_->WritePacketToWire(*encrypted, &error); - if (rv == -1 && helper_->IsWriteBlocked(error)) { - // TODO(satyashekhar): It might be more efficient (fewer system calls), if - // all connections share this variable i.e this becomes a part of - // PacketWriterInterface. - write_blocked_ = true; - // If the socket buffers the the data, then the packet should not - // be queued and sent again, which would result in an unnecessary duplicate - // packet being sent. - return helper_->IsWriteBlockedDataBuffered(); + if (helper_->WritePacketToWire(*encrypted, &error) == -1) { + if (helper_->IsWriteBlocked(error)) { + // TODO(satyashekhar): It might be more efficient (fewer system calls), if + // all connections share this variable i.e this becomes a part of + // PacketWriterInterface. + write_blocked_ = true; + // If the socket buffers the the data, then the packet should not + // be queued and sent again, which would result in an unnecessary + // duplicate packet being sent. + return helper_->IsWriteBlockedDataBuffered(); + } + // We can't send an error as the socket is presumably borked. + CloseConnection(QUIC_PACKET_WRITE_ERROR, false); + return false; } time_of_last_sent_packet_ = now; DVLOG(1) << "time of last sent packet: " << now.ToDebuggingValue(); - // TODO(wtc): Is it correct to continue if the write failed. // Set the retransmit alarm only when we have sent the packet to the client // and not when it goes to the pending queue, otherwise we will end up adding @@ -1113,7 +1142,7 @@ QuicTime QuicConnection::OnRetransmissionTimeout() { // want to set it to the RTO of B when we return from this function. handling_retransmission_timeout_ = true; - for (int i = 0; i < kMaxPacketsPerRetransmissionAlarm && + for (size_t i = 0; i < max_packets_per_retransmission_alarm_ && !retransmission_timeouts_.empty(); ++i) { RetransmissionInfo retransmission_info = retransmission_timeouts_.top(); DCHECK(retransmission_info.scheduled_time.IsInitialized()); @@ -1206,7 +1235,16 @@ QuicFecGroup* QuicConnection::GetFecGroup() { return NULL; } if (group_map_.count(fec_group_num) == 0) { - // TODO(rch): limit the number of active FEC groups. + if (group_map_.size() >= kMaxFecGroups) { // Too many groups + if (fec_group_num < group_map_.begin()->first) { + // The group being requested is a group we've seen before and deleted. + // Don't recreate it. + return NULL; + } + // Clear the lowest group number. + delete group_map_.begin()->second; + group_map_.erase(group_map_.begin()); + } group_map_[fec_group_num] = new QuicFecGroup(); } return group_map_[fec_group_num]; diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index ce3a263..dab64ee 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -503,7 +503,8 @@ class NET_EXPORT_PRIVATE QuicConnection void MaybeSendAckInResponseToPacket(); - // Get the FEC group associate with the last processed packet. + // Get the FEC group associate with the last processed packet or NULL, if the + // group has already been deleted. QuicFecGroup* GetFecGroup(); // Closes any FEC groups protecting packets before |sequence_number|. @@ -519,10 +520,6 @@ class NET_EXPORT_PRIVATE QuicConnection // client. IPEndPoint self_address_; IPEndPoint peer_address_; - // Address on the last(currently being processed) packet received. Not - // verified/authenticated. - IPEndPoint last_self_address_; - IPEndPoint last_peer_address_; bool last_packet_revived_; // True if the last packet was revived from FEC. size_t last_size_; // Size of the last received packet. @@ -606,6 +603,8 @@ class NET_EXPORT_PRIVATE QuicConnection // The version of the protocol this connection is using. QuicTag quic_version_; + size_t max_packets_per_retransmission_alarm_; + // Tracks if the connection was created by the server. bool is_server_; @@ -619,6 +618,10 @@ class NET_EXPORT_PRIVATE QuicConnection bool send_ack_in_response_to_packet_; + // Set to true if the udp packet headers have a new self or peer address. + // This is checked later on validating a data or version negotiation packet. + bool address_migrating_; + DISALLOW_COPY_AND_ASSIGN(QuicConnection); }; diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index 0c81722..fa7484a 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -23,7 +23,7 @@ namespace net { namespace test { const char kData[] = "foo"; -const bool kHasData = true; +const bool kFromPeer = true; class TestConnection : public QuicConnection { public: @@ -328,7 +328,7 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) { // Verify that a single task was posted. ASSERT_EQ(1u, runner_->GetPostedTasks().size()); - EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs), + EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultInitialTimeoutSecs), runner_->GetPostedTasks().front().delay); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); @@ -336,8 +336,8 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) { EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); runner_->RunNextTask(); - EXPECT_EQ(QuicTime::Zero().Add( - QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)), + EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds( + kDefaultInitialTimeoutSecs)), clock_.ApproximateNow()); EXPECT_FALSE(connection_->connected()); EXPECT_TRUE(AtEof()); @@ -374,7 +374,8 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { EXPECT_TRUE(connection_->connected()); QuicTime start = clock_.ApproximateNow(); - // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. + // When we send a packet, the timeout will change to 5000 + + // kDefaultInitialTimeoutSecs. clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000)); EXPECT_EQ(5000u, clock_.ApproximateNow().Subtract(start).ToMicroseconds()); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); @@ -386,17 +387,18 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { // network event at t=5000. The alarm will reregister. runner_->RunNextTask(); - EXPECT_EQ(QuicTime::Zero().Add( - QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)), + EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds( + kDefaultInitialTimeoutSecs)), clock_.ApproximateNow()); EXPECT_TRUE(connection_->connected()); // This time, we should time out. - EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer)); EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION)); runner_->RunNextTask(); - EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.ApproximateNow().Subtract( - QuicTime::Zero()).ToMicroseconds()); + EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000, + clock_.ApproximateNow().Subtract( + QuicTime::Zero()).ToMicroseconds()); EXPECT_FALSE(connection_->connected()); EXPECT_TRUE(AtEof()); } diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 826681a..b761aaf 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -756,10 +756,8 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { TEST_F(QuicConnectionTest, RejectPacketTooFarOut) { // Call ProcessDataPacket rather than ProcessPacket, as we should not get a // packet call to the visitor. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_PACKET_HEADER, false)); ProcessDataPacket(6000, 0, !kEntropyFlag); - - SendAckPacketToPeer(); // Packet 2 - EXPECT_EQ(0u, outgoing_ack()->received_info.largest_observed); } TEST_F(QuicConnectionTest, TruncatedAck) { @@ -1119,6 +1117,45 @@ TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) { ProcessAckPacket(&frame); } +TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) { + for (int i = 0; i < 200; ++i) { + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); + connection_.SendStreamData(1, "foo", i * 3, !kFin); + } + + // Make a truncated ack frame. + QuicAckFrame frame(0, QuicTime::Zero(), 1); + frame.received_info.largest_observed = 192; + InsertMissingPacketsBetween(&frame.received_info, 1, 192); + frame.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 192) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 191); + + + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(visitor_, OnAck(_)).Times(1); + ProcessAckPacket(&frame); + EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); + + QuicConnectionPeer::SetMaxPacketsPerRetransmissionAlarm(&connection_, 200); + const QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + clock_.AdvanceTime(kDefaultRetransmissionTime); + // Only packets that are less than largest observed should be retransmitted. + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(191); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(191); + connection_.OnRetransmissionTimeout(); + + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds( + 2 * kDefaultRetransmissionTime.ToMicroseconds())); + // Retransmit already retransmitted packets event though the sequence number + // greater than the largest observed. + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(191); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(191); + connection_.OnRetransmissionTimeout(); +} + TEST_F(QuicConnectionTest, LimitPacketsPerNack) { EXPECT_CALL(*send_algorithm_, OnIncomingAck(12, _, _)).Times(1); EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); @@ -1487,11 +1524,12 @@ TEST_F(QuicConnectionTest, InitialTimeout) { EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); QuicTime default_timeout = clock_.ApproximateNow().Add( - QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)); EXPECT_EQ(default_timeout, helper_->timeout_alarm()); // Simulate the timeout alarm firing - clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); + clock_.AdvanceTime( + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)); EXPECT_TRUE(connection_.CheckForTimeout()); EXPECT_FALSE(connection_.connected()); } @@ -1500,9 +1538,10 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) { EXPECT_TRUE(connection_.connected()); QuicTime default_timeout = clock_.ApproximateNow().Add( - QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)); - // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. + // When we send a packet, the timeout will change to 5000 + + // kDefaultInitialTimeoutSecs. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); // Send an ack so we don't set the retransimission alarm. @@ -1512,7 +1551,7 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) { // The original alarm will fire. We should not time out because we had a // network event at t=5000. The alarm will reregister. clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds( - kDefaultTimeoutUs - 5000)); + kDefaultInitialTimeoutSecs * 1000000 - 5000)); EXPECT_EQ(default_timeout, clock_.ApproximateNow()); EXPECT_FALSE(connection_.CheckForTimeout()); EXPECT_TRUE(connection_.connected()); @@ -1993,6 +2032,60 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) { EXPECT_EQ(1u, stats.packets_dropped); } +TEST_F(QuicConnectionTest, TestFecGroupLimits) { + // Create and return a group for 1 + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) != NULL); + + // Create and return a group for 2 + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != NULL); + + // Create and return a group for 4. This should remove 1 but not 2. + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) == NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != NULL); + + // Create and return a group for 3. This will kill off 2. + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) == NULL); + + // Verify that adding 5 kills off 3, despite 4 being created before 3. + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 5) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL); +} + +TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { + // Construct a packet with stream frame and connection close frame. + header_.public_header.guid = guid_; + header_.packet_sequence_number = 1; + header_.public_header.reset_flag = false; + header_.public_header.version_flag = false; + header_.entropy_flag = false; + header_.fec_flag = false; + header_.fec_entropy_flag = false; + header_.fec_group = 0; + + QuicConnectionCloseFrame qccf; + qccf.error_code = QUIC_PEER_GOING_AWAY; + qccf.ack_frame = QuicAckFrame(0, QuicTime::Zero(), 1); + QuicFrame close_frame(&qccf); + QuicFrame stream_frame(&frame1_); + + QuicFrames frames; + frames.push_back(stream_frame); + frames.push_back(close_frame); + scoped_ptr<QuicPacket> packet( + framer_.ConstructFrameDataPacket(header_, frames).packet); + EXPECT_TRUE(NULL != packet.get()); + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + ENCRYPTION_NONE, 1, *packet)); + + EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true)); + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(0); + + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index e6322a8..80d298e 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -15,13 +15,11 @@ namespace net { QuicCryptoClientStream::QuicCryptoClientStream( const string& server_hostname, - const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config) : QuicCryptoStream(session), next_state_(STATE_IDLE), num_client_hellos_(0), - config_(config), crypto_config_(crypto_config), server_hostname_(server_hostname) { } @@ -82,8 +80,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( SendHandshakeMessage(out); return; } - const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); - config_.ToHandshakeMessage(&out); + session()->config()->ToHandshakeMessage(&out); error = crypto_config_->FillClientHello( server_hostname_, session()->connection()->guid(), @@ -97,13 +94,6 @@ void QuicCryptoClientStream::DoHandshakeLoop( CloseConnectionWithDetails(error, error_details); return; } - error = config_.ProcessFinalPeerHandshake( - *scfg, CryptoUtils::PEER_PRIORITY, &negotiated_params_, - &error_details); - if (error != QUIC_NO_ERROR) { - CloseConnectionWithDetails(error, error_details); - return; - } next_state_ = STATE_RECV_SHLO; DLOG(INFO) << "Client Sending: " << out.DebugString(); SendHandshakeMessage(out); @@ -214,6 +204,12 @@ void QuicCryptoClientStream::DoHandshakeLoop( error, "Server hello invalid: " + error_details); return; } + error = session()->config()->ProcessServerHello(*in, &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails( + error, "Server hello invalid: " + error_details); + return; + } CrypterPair* crypters = &crypto_negotiated_params_.forward_secure_crypters; // TODO(agl): we don't currently latch this decrypter because the idea diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 041d11c..e85a764 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -13,7 +13,6 @@ namespace net { -class QuicConfig; class QuicSession; namespace test { @@ -23,7 +22,6 @@ class CryptoTestUtils; class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { public: QuicCryptoClientStream(const string& server_hostname, - const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config); virtual ~QuicCryptoClientStream(); @@ -61,7 +59,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { // connection has sent. int num_client_hellos_; - const QuicConfig& config_; QuicCryptoClientConfig* const crypto_config_; // Client's connection nonce (4-byte timestamp + 28 random bytes) diff --git a/net/quic/quic_crypto_client_stream_factory.h b/net/quic/quic_crypto_client_stream_factory.h index 4d0eb1e..4fa5f57 100644 --- a/net/quic/quic_crypto_client_stream_factory.h +++ b/net/quic/quic_crypto_client_stream_factory.h @@ -22,7 +22,6 @@ class NET_EXPORT QuicCryptoClientStreamFactory { virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( const string& server_hostname, - const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config) = 0; }; diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index c913964..90b9122 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -8,6 +8,7 @@ #include "net/quic/crypto/aes_128_gcm_encrypter.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_protocol.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/simple_quic_framer.h" @@ -50,10 +51,10 @@ class QuicCryptoClientStreamTest : public ::testing::Test { QuicCryptoClientStreamTest() : addr_(), connection_(new PacketSavingConnection(1, addr_, true)), - session_(connection_, true), - stream_(kServerHostname, config_, &session_, &crypto_config_) { + session_(connection_, QuicConfig(), true), + stream_(kServerHostname, &session_, &crypto_config_) { session_.SetCryptoStream(&stream_); - config_.SetDefaults(); + session_.config()->SetDefaults(); crypto_config_.SetDefaults(); } @@ -73,7 +74,6 @@ class QuicCryptoClientStreamTest : public ::testing::Test { QuicCryptoClientStream stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; - QuicConfig config_; QuicCryptoClientConfig crypto_config_; }; @@ -137,10 +137,13 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { CompleteCryptoHandshake(); - const QuicNegotiatedParameters& params(stream_.negotiated_params()); - EXPECT_EQ(kQBIC, params.congestion_control); - EXPECT_EQ(300, params.idle_connection_state_lifetime.ToSeconds()); - EXPECT_EQ(0, params.keepalive_timeout.ToSeconds()); + const QuicConfig* config = session_.config(); + EXPECT_EQ(kQBIC, config->congestion_control()); + EXPECT_EQ(kDefaultTimeoutSecs, + config->idle_connection_state_lifetime().ToSeconds()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + config->max_streams_per_connection()); + EXPECT_EQ(0, config->keepalive_timeout().ToSeconds()); const QuicCryptoNegotiatedParameters& crypto_params( stream_.crypto_negotiated_params()); diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 57c1076..c87c6d5 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -14,11 +14,9 @@ namespace net { QuicCryptoServerStream::QuicCryptoServerStream( - const QuicConfig& config, const QuicCryptoServerConfig& crypto_config, QuicSession* session) : QuicCryptoStream(session), - config_(config), crypto_config_(crypto_config) { } @@ -58,14 +56,15 @@ void QuicCryptoServerStream::OnHandshakeMessage( } // If we are returning a SHLO then we accepted the handshake. - error = config_.ProcessFinalPeerHandshake( - message, CryptoUtils::LOCAL_PRIORITY, &negotiated_params_, - &error_details); + QuicConfig* config = session()->config(); + error = config->ProcessClientHello(message, &error_details); if (error != QUIC_NO_ERROR) { CloseConnectionWithDetails(error, error_details); return; } + config->ToHandshakeMessage(&reply); + // 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. diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h index 47c1478..1c7ac06 100644 --- a/net/quic/quic_crypto_server_stream.h +++ b/net/quic/quic_crypto_server_stream.h @@ -15,7 +15,6 @@ namespace net { class CryptoHandshakeMessage; class QuicCryptoServerConfig; -class QuicNegotiatedParameters; class QuicSession; namespace test { @@ -24,8 +23,7 @@ class CryptoTestUtils; class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { public: - QuicCryptoServerStream(const QuicConfig& config, - const QuicCryptoServerConfig& crypto_config, + QuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config, QuicSession* session); explicit QuicCryptoServerStream(QuicSession* session); virtual ~QuicCryptoServerStream(); @@ -37,9 +35,6 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { private: friend class test::CryptoTestUtils; - // config_ contains non-crypto parameters that are negotiated in the crypto - // handshake. - const QuicConfig& config_; // crypto_config_ contains crypto parameters for the handshake. const QuicCryptoServerConfig& crypto_config_; }; diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index d12597b..73469db 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -61,9 +61,10 @@ class QuicCryptoServerStreamTest : public ::testing::Test { addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? ip_ : IPAddressNumber(), 1), connection_(new PacketSavingConnection(guid_, addr_, true)), - session_(connection_, true), + session_(connection_, QuicConfig(), true), crypto_config_(QuicCryptoServerConfig::TESTING), - stream_(config_, crypto_config_, &session_) { + stream_(crypto_config_, &session_) { + session_.config()->SetDefaults(); session_.SetCryptoStream(&stream_); // We advance the clock initially because the default time is zero and the // strike register worries that we've just overflowed a uint32 time. @@ -72,8 +73,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test { // crypto_config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); CryptoTestUtils::SetupCryptoServerConfigForTest( - connection_->clock(), connection_->random_generator(), &config_, - &crypto_config_); + connection_->clock(), connection_->random_generator(), + session_.config(), &crypto_config_); } void ConstructHandshakeMessage() { @@ -82,7 +83,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test { } int CompleteCryptoHandshake() { - return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_); + return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_, + client_options_); } protected: @@ -96,6 +98,7 @@ class QuicCryptoServerStreamTest : public ::testing::Test { QuicCryptoServerStream stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; + CryptoTestUtils::FakeClientOptions client_options_; }; TEST_F(QuicCryptoServerStreamTest, NotInitiallyConected) { @@ -144,18 +147,15 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000)); server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000)); - scoped_ptr<TestSession> client_session(new TestSession(client_conn, true)); - scoped_ptr<TestSession> server_session(new TestSession(server_conn, true)); - QuicConfig client_config; + scoped_ptr<TestSession> client_session( + new TestSession(client_conn, client_config, false)); + client_session->config()->SetDefaults(); QuicCryptoClientConfig client_crypto_config; - - client_config.SetDefaults(); client_crypto_config.SetDefaults(); scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream( - "test.example.com", client_config, client_session.get(), - &client_crypto_config)); + "test.example.com", client_session.get(), &client_crypto_config)); client_session->SetCryptoStream(client.get()); // Do a first handshake in order to prime the client config with the server's @@ -163,9 +163,11 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { CHECK(client->CryptoConnect()); CHECK_EQ(1u, client_conn->packets_.size()); + scoped_ptr<TestSession> server_session( + new TestSession(server_conn, config_, true)); + server_session->config()->SetDefaults(); scoped_ptr<QuicCryptoServerStream> server( - new QuicCryptoServerStream(config_, crypto_config_, - server_session.get())); + new QuicCryptoServerStream(crypto_config_, server_session.get())); server_session->SetCryptoStream(server.get()); CryptoTestUtils::CommunicateHandshakeMessages( @@ -185,14 +187,15 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { // This causes the client's nonce to be different and thus stops the // strike-register from rejecting the repeated nonce. client_conn->random_generator()->Reseed(NULL, 0); - client_session.reset(new TestSession(client_conn, true)); - server_session.reset(new TestSession(server_conn, true)); + client_session.reset(new TestSession(client_conn, client_config, false)); + client_session->config()->SetDefaults(); + server_session.reset(new TestSession(server_conn, config_, true)); + server_session->config()->SetDefaults(); client.reset(new QuicCryptoClientStream( - "test.example.com", client_config, client_session.get(), - &client_crypto_config)); + "test.example.com", client_session.get(), &client_crypto_config)); client_session->SetCryptoStream(client.get()); - server.reset(new QuicCryptoServerStream(config_, crypto_config_, + server.reset(new QuicCryptoServerStream(crypto_config_, server_session.get())); server_session->SetCryptoStream(server.get()); @@ -230,6 +233,22 @@ TEST_F(QuicCryptoServerStreamTest, BadMessageType) { stream_.ProcessData(message_data_->data(), message_data_->length()); } +TEST_F(QuicCryptoServerStreamTest, WithoutCertificates) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + crypto_config_.SetProofSource(NULL); + client_options_.dont_verify_certs = true; + + // Only 2 client hellos need to be sent in the no-certs case: one to get the + // source-address token and the second to finish. + EXPECT_EQ(2, CompleteCryptoHandshake()); + EXPECT_TRUE(stream_.encryption_established()); + EXPECT_TRUE(stream_.handshake_confirmed()); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc index 70bf4d3..2f06e3b 100644 --- a/net/quic/quic_crypto_stream.cc +++ b/net/quic/quic_crypto_stream.cc @@ -57,11 +57,6 @@ void QuicCryptoStream::SendHandshakeMessage( WriteData(string(data.data(), data.length()), false); } -const QuicNegotiatedParameters& -QuicCryptoStream::negotiated_params() const { - return negotiated_params_; -} - const QuicCryptoNegotiatedParameters& QuicCryptoStream::crypto_negotiated_params() const { return crypto_negotiated_params_; diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h index 1352f2e..bdae59e 100644 --- a/net/quic/quic_crypto_stream.h +++ b/net/quic/quic_crypto_stream.h @@ -46,7 +46,6 @@ class NET_EXPORT_PRIVATE QuicCryptoStream bool encryption_established() { return encryption_established_; } bool handshake_confirmed() { return handshake_confirmed_; } - const QuicNegotiatedParameters& negotiated_params() const; const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const; protected: @@ -57,7 +56,6 @@ class NET_EXPORT_PRIVATE QuicCryptoStream bool encryption_established_; bool handshake_confirmed_; - QuicNegotiatedParameters negotiated_params_; QuicCryptoNegotiatedParameters crypto_negotiated_params_; private: diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 67a376e..0e56382 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -102,6 +102,14 @@ size_t QuicFramer::GetMinGoAwayFrameSize() { kQuicStreamIdSize; } +// static +// TODO(satyamshekhar): 16 - Crypto hash for integrity. Not a static value. Use +// QuicEncrypter::GetMaxPlaintextSize. +size_t QuicFramer::GetMaxUnackedPackets(bool include_version) { + return (kMaxPacketSize - GetPacketHeaderSize(include_version) - + GetMinAckFrameSize() - 16) / kSequenceNumberSize; +} + bool QuicFramer::IsSupportedVersion(QuicTag version) { return version == kQuicVersion1; } @@ -313,6 +321,8 @@ QuicEncryptedPacket* QuicFramer::ConstructVersionNegotiationPacket( } bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { + // TODO(satyamshekhar): Don't RaiseError (and close the connection) for + // invalid (unauthenticated) packets. DCHECK(!reader_.get()); reader_.reset(new QuicDataReader(packet.data(), packet.length())); @@ -409,15 +419,14 @@ bool QuicFramer::ProcessPublicResetPacket( const QuicPacketPublicHeader& public_header) { QuicPublicResetPacket packet(public_header); if (!reader_->ReadUInt64(&packet.nonce_proof)) { - // TODO(satyamshekhar): Raise error. set_detailed_error("Unable to read nonce proof."); - return false; + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); } // TODO(satyamshekhar): validate nonce to protect against DoS. if (!reader_->ReadUInt48(&packet.rejected_sequence_number)) { set_detailed_error("Unable to read rejected sequence number."); - return false; + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); } visitor_->OnPublicResetPacket(packet); return true; @@ -431,8 +440,9 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, header->entropy_hash = GetPacketEntropyHash(*header); - // TODO(satyamshekhar): Don't process if the visitor refuses the header. - visitor_->OnPacketHeader(*header); + if (!visitor_->OnPacketHeader(*header)) { + return true; + } if (payload.length() > kMaxPacketSize) { set_detailed_error("Revived packet too large."); @@ -704,6 +714,13 @@ bool QuicFramer::ProcessFrameData() { if (!ProcessConnectionCloseFrame(&frame)) { return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA); } + + if (!visitor_->OnAckFrame(frame.ack_frame)) { + DLOG(INFO) << "Visitor asked to stopped further processing."; + // Returning true since there was no parsing error. + return true; + } + if (!visitor_->OnConnectionCloseFrame(frame)) { DLOG(INFO) << "Visitor asked to stopped further processing."; // Returning true since there was no parsing error. @@ -988,12 +1005,6 @@ bool QuicFramer::ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame) { return false; } - if (!visitor_->OnAckFrame(frame->ack_frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; - // Returning true since there was no parsing error. - return true; - } - return true; } diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 8286844..b13a257 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -231,6 +231,9 @@ class NET_EXPORT_PRIVATE QuicFramer { static size_t GetMinConnectionCloseFrameSize(); // Size in bytes of all GoAway frame fields without the reason phrase. static size_t GetMinGoAwayFrameSize(); + // The maximum number of nacks which can be transmitted in a single ack packet + // without exceeding kMaxPacketSize. + static size_t GetMaxUnackedPackets(bool include_version); // Size in bytes required for a serialized version negotiation packet size_t GetVersionNegotiationPacketSize(size_t number_versions); diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 6986443..701e2bb 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -572,7 +572,7 @@ TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { // public flags (version) 0x01, // version tag - 'Q', '0', '0', '1', + 'Q', '0', '0', '2', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -817,7 +817,7 @@ TEST_F(QuicFramerTest, StreamFrameWithVersion) { // public flags (version) 0x01, // version tag - 'Q', '0', '0', '1', + 'Q', '0', '0', '2', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1608,14 +1608,21 @@ TEST_F(QuicFramerTest, PublicResetPacket) { DLOG(INFO) << "iteration: " << i; if (i < kPublicFlagsOffset) { expected_error = "Unable to read GUID."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PACKET_HEADER); } else if (i < kPublicResetPacketNonceProofOffset) { expected_error = "Unable to read public flags."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PACKET_HEADER); } else if (i < kPublicResetPacketRejectedSequenceNumberOffset) { expected_error = "Unable to read nonce proof."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PUBLIC_RST_PACKET); } else { expected_error = "Unable to read rejected sequence number."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PUBLIC_RST_PACKET); } - CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); } } @@ -1626,7 +1633,7 @@ TEST_F(QuicFramerTest, VersionNegotiationPacket) { // public flags (version) 0x01, // version tag - 'Q', '0', '0', '1', + 'Q', '0', '0', '2', 'Q', '2', '.', '0', }; @@ -1825,7 +1832,7 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { // public flags (version) 0x01, // version tag - 'Q', '0', '0', '1', + 'Q', '0', '0', '2', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1873,7 +1880,7 @@ TEST_F(QuicFramerTest, ConstructVersionNegotiationPacket) { // public flags (version) 0x01, // version tag - 'Q', '0', '0', '1', + 'Q', '0', '0', '2', 'Q', '2', '.', '0', }; @@ -2801,5 +2808,65 @@ TEST_F(QuicFramerTest, StopPacketProcessing) { EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); } +TEST_F(QuicFramerTest, ConnectionCloseWithInvalidAck) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // public flags + 0x00, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + // first fec protected packet offset + 0xFF, + + // frame type (connection close frame) + 0x05, + // error code + 0x11, 0x00, 0x00, 0x00, + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + + // Ack frame. + // entropy hash of sent packets till least awaiting - 1. + 0xE0, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // entropy hash of all received packets. + 0x43, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Infinite delta time. + 0xFF, 0xFF, 0xFF, 0xFF, + // num missing packets + 0x01, + // missing packet + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()); + EXPECT_CALL(visitor, OnPacketHeader(_)); + EXPECT_CALL(visitor, OnAckFrame(_)).WillOnce(Return(false)); + EXPECT_CALL(visitor, OnConnectionCloseFrame(_)).Times(0); + EXPECT_CALL(visitor, OnPacketComplete()); + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); +} + } // namespace test } // namespace net diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index d7fafe0..ff82ab4 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -179,8 +179,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { crypto_config_.SetDefaults(); session_.reset(new QuicClientSession(connection_, socket, NULL, &crypto_client_stream_factory_, - "www.google.com", &crypto_config_, - NULL)); + "www.google.com", QuicConfig(), + &crypto_config_, NULL)); session_->GetCryptoStream()->CryptoConnect(); EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed()); QuicReliableClientStream* stream = @@ -265,7 +265,6 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { testing::StrictMock<MockConnectionVisitor> visitor_; scoped_ptr<QuicHttpStream> stream_; scoped_ptr<QuicClientSession> session_; - QuicConfig* config_; QuicCryptoClientConfig crypto_config_; TestCompletionCallback callback_; HttpRequestInfo request_; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 31fab86..f4c5e95 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -93,7 +93,10 @@ const QuicStreamId kCryptoStreamId = 1; // Value which indicates this packet is not FEC protected. const uint8 kNoFecOffset = 0xFF; -const int64 kDefaultTimeoutUs = 600000000; // 10 minutes. +// This is the default network timeout a for connection till the crypto +// handshake succeeds and the negotiated timeout from the handshake is received. +const int64 kDefaultInitialTimeoutSecs = 30; // 30 secs. +const int64 kDefaultTimeoutSecs = 60 * 10; // 10 minutes. enum Retransmission { NOT_RETRANSMISSION, @@ -173,6 +176,8 @@ enum QuicErrorCode { QUIC_INVALID_ACK_DATA, // Version negotiation packet is malformed. QUIC_INVALID_VERSION_NEGOTIATION_PACKET, + // Public RST packet is malformed. + QUIC_INVALID_PUBLIC_RST_PACKET, // There was an error decrypting. QUIC_DECRYPTION_FAILURE, // There was an error encrypting. @@ -195,9 +200,17 @@ enum QuicErrorCode { QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED, // The Header ID for a stream was too far from the previous. QUIC_INVALID_HEADER_ID, - + // Negotiable parameter received during handshake had invalid value. + QUIC_INVALID_NEGOTIATED_VALUE, + // There was an error decompressing data. + QUIC_DECOMPRESSION_FAILURE, // We hit our prenegotiated (or default) timeout QUIC_CONNECTION_TIMED_OUT, + // There was an error encountered migrating addresses + QUIC_ERROR_MIGRATING_ADDRESS, + // There was an error while writing the packet. + QUIC_PACKET_WRITE_ERROR, + // Crypto errors. @@ -258,7 +271,7 @@ const QuicTag kUnsupportedVersion = -1; // Each time the wire format changes, this need needs to be incremented. // At some point, we will actually freeze the wire format and make an official // version number, but this works for now. -const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '1'); +const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '2'); #undef TAG // MakeQuicTag returns a value given the four bytes. For example: diff --git a/net/quic/quic_reliable_client_stream_test.cc b/net/quic/quic_reliable_client_stream_test.cc index 2517bac..12402fd 100644 --- a/net/quic/quic_reliable_client_stream_test.cc +++ b/net/quic/quic_reliable_client_stream_test.cc @@ -42,7 +42,6 @@ class QuicReliableClientStreamTest : public ::testing::Test { testing::StrictMock<MockDelegate> delegate_; MockSession session_; QuicReliableClientStream stream_; - QuicConfig config_; QuicCryptoClientConfig crypto_config_; }; diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 66879300..5d5df18 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -64,9 +64,12 @@ class VisitorShim : public QuicConnectionVisitorInterface { QuicSession* session_; }; -QuicSession::QuicSession(QuicConnection* connection, bool is_server) +QuicSession::QuicSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server) : connection_(connection), visitor_shim_(new VisitorShim(this)), + config_(config), max_open_streams_(kDefaultMaxStreamsPerConnection), next_stream_id_(is_server ? 2 : 3), is_server_(is_server), @@ -214,11 +217,17 @@ bool QuicSession::IsCryptoHandshakeConfirmed() { void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { if (event == QuicSession::HANDSHAKE_CONFIRMED) { - connection_->SetConnectionTimeout( - GetCryptoStream()->negotiated_params().idle_connection_state_lifetime); + LOG_IF(DFATAL, !config_.negotiated()) + << "Handshake confirmed without parameter negotiation."; + connection_->SetConnectionTimeout(config_.idle_connection_state_lifetime()); + max_open_streams_ = config_.max_streams_per_connection(); } } +QuicConfig* QuicSession::config() { + return &config_; +} + void QuicSession::ActivateStream(ReliableQuicStream* stream) { DLOG(INFO) << "num_streams: " << stream_map_.size() << ". activating " << stream->id(); diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index eff0546..00003d4 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -50,7 +50,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { HANDSHAKE_CONFIRMED, }; - QuicSession(QuicConnection* connection, bool is_server); + QuicSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server); virtual ~QuicSession(); @@ -102,6 +104,8 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Servers will simply call it once with HANDSHAKE_CONFIRMED. virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event); + virtual 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. @@ -176,6 +180,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { return max_open_streams_; } + void set_max_open_streams(size_t max_open_streams) { + max_open_streams_ = max_open_streams; + } + private: friend class test::QuicSessionPeer; friend class VisitorShim; @@ -195,8 +203,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { QuicSpdyDecompressor decompressor_; QuicSpdyCompressor compressor_; + QuicConfig config_; + // Returns the maximum number of streams this connection can open. - const size_t max_open_streams_; + size_t max_open_streams_; // Map from StreamId to pointers to streams that are owned by the caller. ReliableStreamMap stream_map_; diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index 02b9853..338caed 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -3,12 +3,14 @@ // found in the LICENSE file. #include "net/quic/quic_session.h" -#include "net/quic/quic_connection.h" #include <set> #include "base/hash_tables.h" #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -32,6 +34,12 @@ class TestCryptoStream : public QuicCryptoStream { const CryptoHandshakeMessage& message) OVERRIDE { encryption_established_ = true; handshake_confirmed_ = true; + CryptoHandshakeMessage msg; + string error_details; + session()->config()->ToHandshakeMessage(&msg); + const QuicErrorCode error = session()->config()->ProcessClientHello( + msg, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); } }; @@ -52,8 +60,9 @@ class TestStream : public ReliableQuicStream { class TestSession : public QuicSession { public: TestSession(QuicConnection* connection, bool is_server) - : QuicSession(connection, is_server), + : QuicSession(connection, QuicConfig(), is_server), crypto_stream_(this) { + config()->SetDefaults(); } virtual QuicCryptoStream* GetCryptoStream() OVERRIDE { @@ -84,6 +93,7 @@ class TestSession : public QuicSession { } TestCryptoStream crypto_stream_; + QuicConfig config_; }; class QuicSessionTest : public ::testing::Test { @@ -166,6 +176,16 @@ TEST_F(QuicSessionTest, StreamIdTooLarge) { session_.GetIncomingReliableStream(105); } +TEST_F(QuicSessionTest, DecompressionError) { + ReliableQuicStream* stream = session_.GetIncomingReliableStream(3); + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE)); + const char data[] = + "\1\0\0\0" // headers id + "\0\0\0\4" // length + "abcd"; // invalid compressed data + stream->ProcessRawData(data, arraysize(data)); +} + TEST_F(QuicSessionTest, OnCanWrite) { TestStream* stream2 = session_.CreateOutgoingReliableStream(); TestStream* stream4 = session_.CreateOutgoingReliableStream(); @@ -213,6 +233,15 @@ TEST_F(QuicSessionTest, SendGoAway) { EXPECT_FALSE(session_.GetIncomingReliableStream(3u)); } +TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) { + EXPECT_EQ(kDefaultInitialTimeoutSecs, + QuicConnectionPeer::GetTimeout(connection_).ToSeconds()); + CryptoHandshakeMessage msg; + session_.crypto_stream_.OnHandshakeMessage(msg); + EXPECT_EQ(kDefaultTimeoutSecs, + QuicConnectionPeer::GetTimeout(connection_).ToSeconds()); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_spdy_compressor.cc b/net/quic/quic_spdy_compressor.cc index 4497c59..4f67e26 100644 --- a/net/quic/quic_spdy_compressor.cc +++ b/net/quic/quic_spdy_compressor.cc @@ -12,7 +12,7 @@ using std::string; namespace net { QuicSpdyCompressor::QuicSpdyCompressor() - : spdy_framer_(3), + : spdy_framer_(kSpdyVersion3), header_sequence_id_(1) { spdy_framer_.set_enable_compression(true); } diff --git a/net/quic/quic_spdy_decompressor.cc b/net/quic/quic_spdy_decompressor.cc index f96e846..0d58937 100644 --- a/net/quic/quic_spdy_decompressor.cc +++ b/net/quic/quic_spdy_decompressor.cc @@ -75,7 +75,7 @@ bool SpdyFramerVisitor::OnControlFrameHeaderData(SpdyStreamId stream_id, } QuicSpdyDecompressor::QuicSpdyDecompressor() - : spdy_framer_(3), + : spdy_framer_(kSpdyVersion3), spdy_visitor_(new SpdyFramerVisitor(NULL)), current_header_id_(1), has_current_compressed_size_(false), @@ -116,9 +116,11 @@ size_t QuicSpdyDecompressor::DecompressData(StringPiece data, min(current_compressed_size_ - compressed_bytes_consumed_, static_cast<uint32>(data.length())); if (bytes_to_consume > 0) { - bool success = spdy_framer_.IncrementallyDecompressControlFrameHeaderData( - current_header_id_, data.data(), bytes_to_consume); - DCHECK(success); + if (!spdy_framer_.IncrementallyDecompressControlFrameHeaderData( + current_header_id_, data.data(), bytes_to_consume)) { + visitor->OnDecompressionError(); + return bytes_consumed; + } compressed_bytes_consumed_ += bytes_to_consume; bytes_consumed += bytes_to_consume; } diff --git a/net/quic/quic_spdy_decompressor.h b/net/quic/quic_spdy_decompressor.h index 3fd50783..a56c479 100644 --- a/net/quic/quic_spdy_decompressor.h +++ b/net/quic/quic_spdy_decompressor.h @@ -27,6 +27,7 @@ class NET_EXPORT_PRIVATE QuicSpdyDecompressor { public: virtual ~Visitor() {} virtual bool OnDecompressedData(base::StringPiece data) = 0; + virtual void OnDecompressionError() = 0; }; QuicSpdyDecompressor(); diff --git a/net/quic/quic_spdy_decompressor_test.cc b/net/quic/quic_spdy_decompressor_test.cc index b2fbb37..1e63396 100644 --- a/net/quic/quic_spdy_decompressor_test.cc +++ b/net/quic/quic_spdy_decompressor_test.cc @@ -52,6 +52,22 @@ TEST_F(QuicSpdyDecompressorTest, DecompressAndIgnoreTrailingData) { EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data()); } +TEST_F(QuicSpdyDecompressorTest, DecompressError) { + SpdyHeaderBlock headers; + headers[":host"] = "www.google.com"; + headers[":path"] = "/index.hml"; + headers[":scheme"] = "https"; + + EXPECT_EQ(1u, decompressor_.current_header_id()); + string compressed_headers = compressor_.CompressHeaders(headers).substr(4); + compressed_headers[compressed_headers.length() - 1] ^= 0x01; + EXPECT_NE(compressed_headers.length(), + decompressor_.DecompressData(compressed_headers, &visitor_)); + + EXPECT_TRUE(visitor_.error()); + EXPECT_EQ("", visitor_.data()); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index 5ac6bf58..007fc88 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc @@ -372,7 +372,6 @@ QuicClientSession* QuicStreamFactory::CreateSession( DatagramSocket::DEFAULT_BIND, base::Bind(&base::RandInt), net_log.net_log(), net_log.source()); socket->Connect(addr); - socket->GetLocalAddress(&addr); QuicConnectionHelper* helper = new QuicConnectionHelper( MessageLoop::current()->message_loop_proxy(), @@ -387,7 +386,7 @@ QuicClientSession* QuicStreamFactory::CreateSession( QuicClientSession* session = new QuicClientSession(connection, socket, this, quic_crypto_client_stream_factory_, - host_port_proxy_pair.first.host(), + host_port_proxy_pair.first.host(), QuicConfig(), crypto_config, net_log.net_log()); all_sessions_.insert(session); // owning pointer return session; diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 228b796..a42993c 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -4,8 +4,13 @@ #include "net/quic/quic_utils.h" +#include <ctype.h> + #include "base/logging.h" #include "base/port.h" +#include "base/strings/string_number_conversions.h" + +using std::string; namespace net { @@ -49,6 +54,53 @@ uint128 QuicUtils::FNV1a_128_Hash(const char* data, int len) { } // static +bool QuicUtils::FindMutualTag(const QuicTagVector& our_tags_vector, + const QuicTag* their_tags, + size_t num_their_tags, + Priority priority, + QuicTag* out_result, + size_t* out_index) { + if (our_tags_vector.empty()) { + return false; + } + const size_t num_our_tags = our_tags_vector.size(); + const QuicTag* our_tags = &our_tags_vector[0]; + + size_t num_priority_tags, num_inferior_tags; + const QuicTag* priority_tags; + const QuicTag* inferior_tags; + if (priority == LOCAL_PRIORITY) { + num_priority_tags = num_our_tags; + priority_tags = our_tags; + num_inferior_tags = num_their_tags; + inferior_tags = their_tags; + } else { + num_priority_tags = num_their_tags; + priority_tags = their_tags; + num_inferior_tags = num_our_tags; + inferior_tags = our_tags; + } + + for (size_t i = 0; i < num_priority_tags; i++) { + for (size_t j = 0; j < num_inferior_tags; j++) { + if (priority_tags[i] == inferior_tags[j]) { + *out_result = priority_tags[i]; + if (out_index) { + if (priority == LOCAL_PRIORITY) { + *out_index = j; + } else { + *out_index = i; + } + } + return true; + } + } + } + + return false; +} + +// static void QuicUtils::SerializeUint128(uint128 v, uint8* out) { const uint64 lo = Uint128Low64(v); const uint64 hi = Uint128High64(v); @@ -100,6 +152,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_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); @@ -124,7 +177,11 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); RETURN_STRING_LITERAL(QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID); + RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE); + RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE); RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT); + RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_ADDRESS); + RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR); RETURN_STRING_LITERAL(QUIC_PROOF_INVALID); RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG); RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT); @@ -139,4 +196,29 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { return "INVALID_ERROR_CODE"; } +// static +string QuicUtils::TagToString(QuicTag tag) { + char chars[4]; + bool ascii = true; + const QuicTag orig_tag = tag; + + for (size_t i = 0; i < sizeof(chars); i++) { + chars[i] = tag; + if (chars[i] == 0 && i == 3) { + chars[i] = ' '; + } + if (!isprint(static_cast<unsigned char>(chars[i]))) { + ascii = false; + break; + } + tag >>= 8; + } + + if (ascii) { + return string(chars, sizeof(chars)); + } + + return base::UintToString(orig_tag); +} + } // namespace net diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h index 14345eb..e866a07 100644 --- a/net/quic/quic_utils.h +++ b/net/quic/quic_utils.h @@ -15,6 +15,11 @@ namespace net { class NET_EXPORT_PRIVATE QuicUtils { public: + enum Priority { + LOCAL_PRIORITY, + PEER_PRIORITY, + }; + // returns the 64 bit FNV1a hash of the data. See // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param static uint64 FNV1a_64_Hash(const char* data, int len); @@ -23,6 +28,21 @@ class NET_EXPORT_PRIVATE QuicUtils { // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param static uint128 FNV1a_128_Hash(const char* data, int len); + // FindMutualTag sets |out_result| to the first tag in the priority list that + // is also in the other list and returns true. If there is no intersection it + // returns false. + // + // Which list has priority is determined by |priority|. + // + // If |out_index| is non-NULL and a match is found then the index of that + // match in |their_tags| is written to |out_index|. + static bool FindMutualTag(const QuicTagVector& our_tags, + const QuicTag* their_tags, + size_t num_their_tags, + Priority priority, + QuicTag* out_result, + size_t* out_index); + // SerializeUint128 writes |v| in little-endian form to |out|. static void SerializeUint128(uint128 v, uint8* out); @@ -34,6 +54,12 @@ class NET_EXPORT_PRIVATE QuicUtils { // Returns the name of the QuicErrorCode as a char* static const char* ErrorToString(QuicErrorCode error); + + // TagToString is a utility function for pretty-printing handshake messages + // 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. + static std::string TagToString(QuicTag tag); }; } // namespace net diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc index cadbe9a..c293eed 100644 --- a/net/quic/reliable_quic_stream.cc +++ b/net/quic/reliable_quic_stream.cc @@ -365,6 +365,11 @@ bool ReliableQuicStream::OnDecompressedData(StringPiece data) { return true; } +void ReliableQuicStream::OnDecompressionError() { + session_->connection()->SendConnectionClose(QUIC_DECOMPRESSION_FAILURE); +} + + void ReliableQuicStream::CloseWriteSide() { if (write_side_closed_) { return; diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h index 726cc9c..a22f90d 100644 --- a/net/quic/reliable_quic_stream.h +++ b/net/quic/reliable_quic_stream.h @@ -76,6 +76,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public virtual uint32 ProcessData(const char* data, uint32 data_len) = 0; virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; + virtual void OnDecompressionError() OVERRIDE; // Called to close the stream from this end. virtual void Close(QuicRstStreamErrorCode error); diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 058157e..4e285ad 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -102,6 +102,10 @@ void MovePackets(PacketSavingConnection* source_conn, } // anonymous namespace +CryptoTestUtils::FakeClientOptions::FakeClientOptions() + : dont_verify_certs(false) { +} + // static void CryptoTestUtils::CommunicateHandshakeMessages( PacketSavingConnection* a_conn, @@ -135,16 +139,15 @@ int CryptoTestUtils::HandshakeWithFakeServer( IPEndPoint addr = IPEndPoint(ip, 1); PacketSavingConnection* server_conn = new PacketSavingConnection(guid, addr, true); - TestSession server_session(server_conn, true); + TestSession server_session(server_conn, QuicConfig(), true); - QuicConfig config; QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING); SetupCryptoServerConfigForTest( server_session.connection()->clock(), server_session.connection()->random_generator(), - &config, &crypto_config); + server_session.config(), &crypto_config); - QuicCryptoServerStream server(config, crypto_config, &server_session); + QuicCryptoServerStream server(crypto_config, &server_session); server_session.SetCryptoStream(&server); // The client's handshake must have been started already. @@ -160,22 +163,24 @@ int CryptoTestUtils::HandshakeWithFakeServer( // static int CryptoTestUtils::HandshakeWithFakeClient( PacketSavingConnection* server_conn, - QuicCryptoServerStream* server) { + QuicCryptoServerStream* server, + const FakeClientOptions& options) { QuicGuid guid(1); IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); IPEndPoint addr = IPEndPoint(ip, 1); PacketSavingConnection* client_conn = new PacketSavingConnection(guid, addr, false); - TestSession client_session(client_conn, true); - QuicConfig config; + TestSession client_session(client_conn, QuicConfig(), false); QuicCryptoClientConfig crypto_config; - config.SetDefaults(); + client_session.config()->SetDefaults(); crypto_config.SetDefaults(); // TODO(rtenneti): Enable testing of ProofVerifier. - // crypto_config.SetProofVerifier(ProofVerifierForTesting()); - QuicCryptoClientStream client("test.example.com", config, &client_session, + // if (!options.dont_verify_certs) { + // crypto_config.SetProofVerifier(ProofVerifierForTesting()); + // } + QuicCryptoClientStream client("test.example.com", &client_session, &crypto_config); client_session.SetCryptoStream(&client); @@ -196,15 +201,9 @@ void CryptoTestUtils::SetupCryptoServerConfigForTest( QuicConfig* config, QuicCryptoServerConfig* crypto_config) { config->SetDefaults(); - CryptoHandshakeMessage extra_tags; - config->ToHandshakeMessage(&extra_tags); - scoped_ptr<CryptoHandshakeMessage> scfg( crypto_config->AddDefaultConfig( - rand, clock, extra_tags, QuicCryptoServerConfig::kDefaultExpiry)); - if (!config->SetFromHandshakeMessage(*scfg)) { - CHECK(false) << "Crypto config could not be parsed by QuicConfig."; - } + rand, clock, QuicCryptoServerConfig::kDefaultExpiry)); } // static @@ -274,8 +273,8 @@ class MockCommonCertSets : public CommonCertSets { }; CommonCertSets* CryptoTestUtils::MockCommonCertSets(StringPiece cert, - uint64 hash, - uint32 index) { + uint64 hash, + uint32 index) { return new class MockCommonCertSets(cert, hash, index); } diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h index 62748a3..3a4fdd4 100644 --- a/net/quic/test_tools/crypto_test_utils.h +++ b/net/quic/test_tools/crypto_test_utils.h @@ -32,13 +32,24 @@ class PacketSavingConnection; class CryptoTestUtils { public: + // FakeClientOptions bundles together a number of options for configuring + // HandshakeWithFakeClient. + struct FakeClientOptions { + FakeClientOptions(); + + // If dont_verify_certs is true then no ProofVerifier is set on the client. + // Thus no certificates will be requested or checked. + bool dont_verify_certs; + }; + // returns: the number of client hellos that the client sent. static int HandshakeWithFakeServer(PacketSavingConnection* client_conn, QuicCryptoClientStream* client); // returns: the number of client hellos that the client sent. static int HandshakeWithFakeClient(PacketSavingConnection* server_conn, - QuicCryptoServerStream* server); + QuicCryptoServerStream* server, + const FakeClientOptions& options); // SetupCryptoServerConfigForTest configures |config| and |crypto_config| // with sensible defaults for testing. diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc index afaa570..dda5965 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.cc +++ b/net/quic/test_tools/mock_crypto_client_stream.cc @@ -8,11 +8,10 @@ namespace net { MockCryptoClientStream::MockCryptoClientStream( const string& server_hostname, - const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config, HandshakeMode handshake_mode) - : QuicCryptoClientStream(server_hostname, config, session, crypto_config), + : QuicCryptoClientStream(server_hostname, session, crypto_config), handshake_mode_(handshake_mode) { } diff --git a/net/quic/test_tools/mock_crypto_client_stream.h b/net/quic/test_tools/mock_crypto_client_stream.h index 4546d23..3351dec 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.h +++ b/net/quic/test_tools/mock_crypto_client_stream.h @@ -34,7 +34,6 @@ class MockCryptoClientStream : public QuicCryptoClientStream { MockCryptoClientStream( const string& server_hostname, - const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config, HandshakeMode handshake_mode); diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/net/quic/test_tools/mock_crypto_client_stream_factory.cc index 20926ce..7578790 100644 --- a/net/quic/test_tools/mock_crypto_client_stream_factory.cc +++ b/net/quic/test_tools/mock_crypto_client_stream_factory.cc @@ -18,11 +18,10 @@ MockCryptoClientStreamFactory::MockCryptoClientStreamFactory() QuicCryptoClientStream* MockCryptoClientStreamFactory::CreateQuicCryptoClientStream( const string& server_hostname, - const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config) { - return new MockCryptoClientStream(server_hostname, config, session, - crypto_config, handshake_mode_); + return new MockCryptoClientStream(server_hostname, session, crypto_config, + handshake_mode_); } } // namespace net diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.h b/net/quic/test_tools/mock_crypto_client_stream_factory.h index 01d0c75..e3f2a4a 100644 --- a/net/quic/test_tools/mock_crypto_client_stream_factory.h +++ b/net/quic/test_tools/mock_crypto_client_stream_factory.h @@ -22,7 +22,6 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( const string& server_hostname, - const QuicConfig& config, QuicSession* session, QuicCryptoClientConfig* crypto_config) OVERRIDE; diff --git a/net/quic/test_tools/quic_client_session_peer.cc b/net/quic/test_tools/quic_client_session_peer.cc new file mode 100644 index 0000000..e88da49 --- /dev/null +++ b/net/quic/test_tools/quic_client_session_peer.cc @@ -0,0 +1,21 @@ +// 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/test_tools/quic_client_session_peer.h" + +#include "net/quic/quic_client_session.h" + +namespace net { +namespace test { + +// static +void QuicClientSessionPeer::SetMaxOpenStreams(QuicClientSession* session, + size_t max_streams, + size_t default_streams) { + session->config()->set_max_streams_per_connection(max_streams, + default_streams); +} + +} // namespace test +} // namespace net diff --git a/net/quic/test_tools/quic_client_session_peer.h b/net/quic/test_tools/quic_client_session_peer.h new file mode 100644 index 0000000..7217d9d --- /dev/null +++ b/net/quic/test_tools/quic_client_session_peer.h @@ -0,0 +1,29 @@ +// 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 NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicClientSession; + +namespace test { + +class QuicClientSessionPeer { + public: + static void SetMaxOpenStreams(QuicClientSession* session, + size_t max_streams, + size_t default_streams); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicClientSessionPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_ diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc index 6816e54..4f2b500 100644 --- a/net/quic/test_tools/quic_connection_peer.cc +++ b/net/quic/test_tools/quic_connection_peer.cc @@ -63,6 +63,11 @@ size_t QuicConnectionPeer::GetNumRetransmissionTimeouts( } // static +QuicTime::Delta QuicConnectionPeer::GetTimeout(QuicConnection* connection) { + return connection->timeout_; +} + +// static bool QuicConnectionPeer::IsSavedForRetransmission( QuicConnection* connection, QuicPacketSequenceNumber sequence_number) { @@ -121,5 +126,24 @@ void QuicConnectionPeer::SwapCrypters(QuicConnection* connection, framer->SwapCryptersForTest(&connection->framer_); } +// static +void QuicConnectionPeer:: SetMaxPacketsPerRetransmissionAlarm( + QuicConnection* connection, + int max_packets) { + connection->max_packets_per_retransmission_alarm_ = max_packets; +} + +// static +QuicConnectionHelperInterface* QuicConnectionPeer::GetHelper( + QuicConnection* connection) { + return connection->helper_.get(); +} + +QuicFecGroup* QuicConnectionPeer::GetFecGroup(QuicConnection* connection, + int fec_group) { + connection->last_header_.fec_group = fec_group; + return connection->GetFecGroup(); +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/quic_connection_peer.h b/net/quic/test_tools/quic_connection_peer.h index 89c6482..e2b4ff9 100644 --- a/net/quic/test_tools/quic_connection_peer.h +++ b/net/quic/test_tools/quic_connection_peer.h @@ -12,8 +12,11 @@ namespace net { struct QuicAckFrame; +struct QuicPacketHeader; class QuicConnection; +class QuicConnectionHelperInterface; class QuicConnectionVisitorInterface; +class QuicFecGroup; class QuicFramer; class QuicPacketCreator; class ReceiveAlgorithmInterface; @@ -43,6 +46,8 @@ class QuicConnectionPeer { static size_t GetNumRetransmissionTimeouts(QuicConnection* connection); + static QuicTime::Delta GetTimeout(QuicConnection* connection); + static bool IsSavedForRetransmission( QuicConnection* connection, QuicPacketSequenceNumber sequence_number); @@ -70,6 +75,14 @@ class QuicConnectionPeer { static void SwapCrypters(QuicConnection* connection, QuicFramer* framer); + static void SetMaxPacketsPerRetransmissionAlarm(QuicConnection* connection, + int max_packets); + + static QuicConnectionHelperInterface* GetHelper(QuicConnection* connection); + + // Set last_header_->fec_group = fec_group and return connection->GetFecGroup + static QuicFecGroup* GetFecGroup(QuicConnection* connection, int fec_group); + private: DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer); }; diff --git a/net/quic/test_tools/quic_session_peer.cc b/net/quic/test_tools/quic_session_peer.cc index 16b5c41..0a81ca9 100644 --- a/net/quic/test_tools/quic_session_peer.cc +++ b/net/quic/test_tools/quic_session_peer.cc @@ -10,9 +10,15 @@ namespace net { namespace test { // static -void QuicSessionPeer::SetNextStreamId(QuicStreamId id, QuicSession* session) { +void QuicSessionPeer::SetNextStreamId(QuicSession* session, QuicStreamId id) { session->next_stream_id_ = id; } +// static +void QuicSessionPeer::SetMaxOpenStreams(QuicSession* session, + uint32 max_streams) { + session->max_open_streams_ = max_streams; +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/quic_session_peer.h b/net/quic/test_tools/quic_session_peer.h index 16b0c21..9aff659 100644 --- a/net/quic/test_tools/quic_session_peer.h +++ b/net/quic/test_tools/quic_session_peer.h @@ -15,7 +15,8 @@ namespace test { class QuicSessionPeer { public: - static void SetNextStreamId(QuicStreamId id, QuicSession* session); + static void SetNextStreamId(QuicSession* session, QuicStreamId id); + static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams); private: DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer); diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index c987277..8821387 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -235,7 +235,7 @@ bool PacketSavingConnection::SendOrQueuePacket( } MockSession::MockSession(QuicConnection* connection, bool is_server) - : QuicSession(connection, is_server) { + : QuicSession(connection, QuicConfig(), is_server) { ON_CALL(*this, WriteData(_, _, _, _)) .WillByDefault(testing::Return(QuicConsumedData(0, false))); } @@ -243,8 +243,10 @@ MockSession::MockSession(QuicConnection* connection, bool is_server) MockSession::~MockSession() { } -TestSession::TestSession(QuicConnection* connection, bool is_server) - : QuicSession(connection, is_server), +TestSession::TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server) + : QuicSession(connection, config, is_server), crypto_stream_(NULL) { } @@ -404,5 +406,9 @@ bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) { return true; } +void TestDecompressorVisitor::OnDecompressionError() { + error_ = true; +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 5ea2a78..8fbf054 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -295,7 +295,9 @@ class MockSession : public QuicSession { class TestSession : public QuicSession { public: - TestSession(QuicConnection* connection, bool is_server); + TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server); virtual ~TestSession(); MOCK_METHOD1(CreateIncomingReliableStream, @@ -351,11 +353,14 @@ class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { public: virtual ~TestDecompressorVisitor() {} virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; + virtual void OnDecompressionError() OVERRIDE; string data() { return data_; } + bool error() { return error_; } private: string data_; + bool error_; }; } // namespace test diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index 06cb5f9..aa92856 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -1498,6 +1498,7 @@ const BoundNetLog& MockUDPClientSocket::NetLog() const { int MockUDPClientSocket::Connect(const IPEndPoint& address) { connected_ = true; + peer_addr_ = address; return OK; } diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index c03f9d8..01e9181 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -17,16 +17,22 @@ #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_session_peer.h" #include "net/quic/test_tools/reliable_quic_stream_peer.h" +#include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/quic_server.h" +#include "net/tools/quic/quic_socket_utils.h" #include "net/tools/quic/test_tools/http_message_test_utils.h" +#include "net/tools/quic/test_tools/quic_client_peer.h" +#include "net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h" #include "net/tools/quic/test_tools/quic_test_client.h" #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; using base::WaitableEvent; +using net::test::QuicConnectionPeer; using net::test::QuicSessionPeer; using net::test::ReliableQuicStreamPeer; using std::string; @@ -209,7 +215,7 @@ TEST_F(EndToEndTest, SimpleRequestResponse) { ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, SimpleRequestResponsev6) { @@ -220,12 +226,12 @@ TEST_F(EndToEndTest, SimpleRequestResponsev6) { } IPAddressNumber ip; - CHECK(net::ParseIPLiteralToNumber("::", &ip)); + CHECK(net::ParseIPLiteralToNumber("::1", &ip)); server_address_ = IPEndPoint(ip, server_address_.port()); ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, SeparateFinPacket) { @@ -247,7 +253,7 @@ TEST_F(EndToEndTest, SeparateFinPacket) { client_->WaitForResponse(); EXPECT_EQ(kFooResponseBody, client_->response_body()); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); request.AddBody("foo", true); @@ -255,7 +261,7 @@ TEST_F(EndToEndTest, SeparateFinPacket) { client_->SendData(std::string(), true); client_->WaitForResponse(); EXPECT_EQ(kFooResponseBody, client_->response_body()); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, MultipleRequestResponse) { @@ -268,9 +274,9 @@ TEST_F(EndToEndTest, MultipleRequestResponse) { ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, MultipleClients) { @@ -294,12 +300,12 @@ TEST_F(EndToEndTest, MultipleClients) { client_->SendData("bar", true); client_->WaitForResponse(); EXPECT_EQ(kFooResponseBody, client_->response_body()); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); client2->SendData("eep", true); client2->WaitForResponse(); EXPECT_EQ(kFooResponseBody, client2->response_body()); - EXPECT_EQ(200ul, client2->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client2->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, RequestOverMultiplePackets) { @@ -329,7 +335,7 @@ TEST_F(EndToEndTest, RequestOverMultiplePackets) { // Make sure our request is too large to fit in one packet. EXPECT_GT(strlen(kLargeRequest), min_payload_size); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest(kLargeRequest)); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, MultipleFramesRandomOrder) { @@ -360,7 +366,7 @@ TEST_F(EndToEndTest, MultipleFramesRandomOrder) { // Make sure our request is too large to fit in one packet. EXPECT_GT(strlen(kLargeRequest), min_payload_size); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest(kLargeRequest)); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, PostMissingBytes) { @@ -382,7 +388,7 @@ TEST_F(EndToEndTest, PostMissingBytes) { // triggering an error response. client_->SendCustomSynchronousRequest(request); EXPECT_EQ("bad", client_->response_body()); - EXPECT_EQ(500ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(500u, client_->response_headers()->parsed_response_code()); } TEST_F(EndToEndTest, LargePost) { @@ -460,7 +466,7 @@ TEST_F(EndToEndTest, InvalidStream) { request.AddBody(body, true); // Force the client to write with a stream ID belonging to a nonexistant // server-side stream. - QuicSessionPeer::SetNextStreamId(2, client_->client()->session()); + QuicSessionPeer::SetNextStreamId(client_->client()->session(), 2); client_->SendCustomSynchronousRequest(request); // EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); @@ -505,6 +511,7 @@ TEST_F(EndToEndTest, MultipleTermination) { /*TEST_F(EndToEndTest, Timeout) { config_.set_idle_connection_state_lifetime( + 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. @@ -524,10 +531,58 @@ TEST_F(EndToEndTest, ResetConnection) { ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); client_->ResetConnection(); EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); - EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code()); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); +} + +class WrongAddressWriter : public QuicPacketWriter { + public: + explicit WrongAddressWriter(int fd) : fd_(fd) { + IPAddressNumber ip; + CHECK(net::ParseIPLiteralToNumber("127.0.0.2", &ip)); + self_address_ = IPEndPoint(ip, 0); + } + + virtual int WritePacket(const char* buffer, size_t buf_len, + const IPAddressNumber& real_self_address, + const IPEndPoint& peer_address, + QuicBlockedWriterInterface* blocked_writer, + int* error) OVERRIDE { + return QuicSocketUtils::WritePacket(fd_, buffer, buf_len, + self_address_.address(), peer_address, + error); + } + + IPEndPoint self_address_; + int fd_; +}; + +TEST_F(EndToEndTest, ConnectionMigration) { + // TODO(rtenneti): Delete this when NSS is supported. + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + ASSERT_TRUE(Initialize()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); + + WrongAddressWriter writer(QuicClientPeer::GetFd(client_->client())); + QuicEpollConnectionHelper* helper = + reinterpret_cast<QuicEpollConnectionHelper*>( + QuicConnectionPeer::GetHelper( + client_->client()->session()->connection())); + QuicEpollConnectionHelperPeer::SetWriter(helper, &writer); + + client_->SendSynchronousRequest("/bar"); + QuicEpollConnectionHelperPeer::SetWriter(helper, NULL); + + EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error()); + EXPECT_EQ(QUIC_ERROR_MIGRATING_ADDRESS, client_->connection_error()); } } // namespace diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index 0206c3f..9ca70a3 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc @@ -63,6 +63,8 @@ QuicClient::~QuicClient() { } bool QuicClient::Initialize() { + DCHECK(!initialized_); + epoll_server_.set_timeout_in_us(50 * 1000); crypto_config_.SetDefaults(); int address_family = server_address_.GetSockAddrFamily(); @@ -167,6 +169,7 @@ void QuicClient::Disconnect() { epoll_server_.UnregisterFD(fd_); close(fd_); fd_ = -1; + initialized_ = false; } void QuicClient::SendRequestsAndWaitForResponse(int argc, char *argv[]) { diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h index 9cfaa3e..0ce9104 100644 --- a/net/tools/quic/quic_client.h +++ b/net/tools/quic/quic_client.h @@ -24,6 +24,10 @@ namespace net { namespace tools { +namespace test { +class QuicClientPeer; +} // namespace test + class QuicClient : public EpollCallbackInterface { public: QuicClient(IPEndPoint server_address, const std::string& server_hostname); @@ -108,6 +112,8 @@ class QuicClient : public EpollCallbackInterface { int fd() { return fd_; } private: + friend class net::tools::test::QuicClientPeer; + // Read a UDP packet and hand it to the framer. bool ReadAndProcessPacket(); diff --git a/net/tools/quic/quic_client_session.cc b/net/tools/quic/quic_client_session.cc index e3394d3..c41df67 100644 --- a/net/tools/quic/quic_client_session.cc +++ b/net/tools/quic/quic_client_session.cc @@ -19,8 +19,8 @@ QuicClientSession::QuicClientSession( const QuicConfig& config, QuicConnection* connection, QuicCryptoClientConfig* crypto_config) - : QuicSession(connection, false), - crypto_stream_(server_hostname, config, this, crypto_config) { + : QuicSession(connection, config, false), + crypto_stream_(server_hostname, this, crypto_config) { } QuicClientSession::~QuicClientSession() { diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc index 39543db..0b9603c 100644 --- a/net/tools/quic/quic_client_session_test.cc +++ b/net/tools/quic/quic_client_session_test.cc @@ -27,22 +27,23 @@ class QuicClientSessionTest : public ::testing::Test { protected: QuicClientSessionTest() : guid_(1), - connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)), - session_(kServerHostname, config_, connection_, &crypto_config_) { - config_.SetDefaults(); + connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)) { crypto_config_.SetDefaults(); + session_.reset(new QuicClientSession(kServerHostname, QuicConfig(), + connection_, &crypto_config_)); + session_->config()->SetDefaults(); + session_->config()->set_max_streams_per_connection(1, 1); } void CompleteCryptoHandshake() { - ASSERT_TRUE(session_.CryptoConnect()); + ASSERT_TRUE(session_->CryptoConnect()); CryptoTestUtils::HandshakeWithFakeServer( - connection_, session_.GetCryptoStream()); + connection_, session_->GetCryptoStream()); } QuicGuid guid_; PacketSavingConnection* connection_; - QuicClientSession session_; - QuicConfig config_; + scoped_ptr<QuicClientSession> session_; QuicCryptoClientConfig crypto_config_; }; @@ -56,28 +57,28 @@ TEST_F(QuicClientSessionTest, DISABLED_MaxNumConnections) { CompleteCryptoHandshake(); QuicReliableClientStream* stream = - session_.CreateOutgoingReliableStream(); + session_->CreateOutgoingReliableStream(); ASSERT_TRUE(stream); - EXPECT_FALSE(session_.CreateOutgoingReliableStream()); + EXPECT_FALSE(session_->CreateOutgoingReliableStream()); // Close a stream and ensure I can now open a new one. - session_.CloseStream(stream->id()); - stream = session_.CreateOutgoingReliableStream(); + session_->CloseStream(stream->id()); + stream = session_->CreateOutgoingReliableStream(); EXPECT_TRUE(stream); } TEST_F(QuicClientSessionTest, GoAwayReceived) { // Initialize crypto before the client session will create a stream. - ASSERT_TRUE(session_.CryptoConnect()); + ASSERT_TRUE(session_->CryptoConnect()); // Simulate the server crypto handshake. CryptoHandshakeMessage server_message; server_message.set_tag(kSHLO); - session_.GetCryptoStream()->OnHandshakeMessage(server_message); + session_->GetCryptoStream()->OnHandshakeMessage(server_message); // After receiving a GoAway, I should no longer be able to create outgoing // streams. - session_.OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away.")); - EXPECT_EQ(NULL, session_.CreateOutgoingReliableStream()); + session_->OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away.")); + EXPECT_EQ(NULL, session_->CreateOutgoingReliableStream()); } } // namespace diff --git a/net/tools/quic/quic_epoll_connection_helper.h b/net/tools/quic/quic_epoll_connection_helper.h index edf5615..9ddef2f 100644 --- a/net/tools/quic/quic_epoll_connection_helper.h +++ b/net/tools/quic/quic_epoll_connection_helper.h @@ -30,6 +30,10 @@ class RetransmissionAlarm; class SendAlarm; class TimeoutAlarm; +namespace test { +class QuicEpollConnectionHelperPeer; +} // namespace test + class QuicEpollConnectionHelper : public QuicConnectionHelperInterface { public: QuicEpollConnectionHelper(int fd, EpollServer* eps); @@ -56,6 +60,7 @@ class QuicEpollConnectionHelper : public QuicConnectionHelperInterface { private: friend class QuicConnectionPeer; + friend class net::tools::test::QuicEpollConnectionHelperPeer; QuicPacketWriter* writer_; // Not owned EpollServer* epoll_server_; // Not owned. diff --git a/net/tools/quic/quic_epoll_connection_helper_test.cc b/net/tools/quic/quic_epoll_connection_helper_test.cc index 91fafa0..5f5dc1b 100644 --- a/net/tools/quic/quic_epoll_connection_helper_test.cc +++ b/net/tools/quic/quic_epoll_connection_helper_test.cc @@ -29,7 +29,7 @@ namespace test { namespace { const char data1[] = "foo"; -const bool kHasData = true; +const bool kFromPeer = true; class TestConnectionHelper : public QuicEpollConnectionHelper { public: @@ -141,17 +141,18 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) { EXPECT_TRUE(connection_.connected()); EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); - EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kHasData)); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer)); epoll_server_.WaitForEventsAndExecuteCallbacks(); EXPECT_FALSE(connection_.connected()); - EXPECT_EQ(kDefaultTimeoutUs, epoll_server_.NowInUsec()); + EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000, epoll_server_.NowInUsec()); } TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { EXPECT_TRUE(connection_.connected()); EXPECT_EQ(0, epoll_server_.NowInUsec()); - // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. + // When we send a packet, the timeout will change to 5000 + + // kDefaultInitialTimeoutSecs. epoll_server_.AdvanceBy(5000); EXPECT_EQ(5000, epoll_server_.NowInUsec()); @@ -162,13 +163,14 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { // The original alarm will fire. We should not time out because we had a // network event at t=5000. The alarm will reregister. epoll_server_.WaitForEventsAndExecuteCallbacks(); - EXPECT_EQ(kDefaultTimeoutUs, epoll_server_.NowInUsec()); + EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000, epoll_server_.NowInUsec()); // This time, we should time out. - EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer)); EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION)); epoll_server_.WaitForEventsAndExecuteCallbacks(); - EXPECT_EQ(kDefaultTimeoutUs + 5000, epoll_server_.NowInUsec()); + EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000, + epoll_server_.NowInUsec()); EXPECT_FALSE(connection_.connected()); } diff --git a/net/tools/quic/quic_reliable_client_stream.h b/net/tools/quic/quic_reliable_client_stream.h index c46420b..10b60c2 100644 --- a/net/tools/quic/quic_reliable_client_stream.h +++ b/net/tools/quic/quic_reliable_client_stream.h @@ -49,8 +49,6 @@ class QuicReliableClientStream : public ReliableQuicStream { // Returns whatever headers have been received for this stream. const BalsaHeaders& headers() { return headers_; } - bool closed() { return closed_; } - protected: std::string* mutable_data() { return &data_; } BalsaHeaders* mutable_headers() { return &headers_; } @@ -58,7 +56,6 @@ class QuicReliableClientStream : public ReliableQuicStream { private: BalsaHeaders headers_; std::string data_; - bool closed_; DISALLOW_COPY_AND_ASSIGN(QuicReliableClientStream); }; diff --git a/net/tools/quic/quic_reliable_client_stream_test.cc b/net/tools/quic/quic_reliable_client_stream_test.cc index 8020c33..1f1eb54 100644 --- a/net/tools/quic/quic_reliable_client_stream_test.cc +++ b/net/tools/quic/quic_reliable_client_stream_test.cc @@ -25,11 +25,11 @@ namespace { class QuicClientStreamTest : public ::testing::Test { public: QuicClientStreamTest() - : session_("localhost", config_, + : session_("localhost", QuicConfig(), new MockConnection(1, IPEndPoint(), 0, &eps_, false), &crypto_config_), body_("hello world") { - config_.SetDefaults(); + session_.config()->SetDefaults(); crypto_config_.SetDefaults(); headers_.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "Ok"); @@ -45,7 +45,6 @@ class QuicClientStreamTest : public ::testing::Test { BalsaHeaders headers_; string headers_string_; string body_; - QuicConfig config_; QuicCryptoClientConfig crypto_config_; }; diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc index 7319c67..a92c4ba 100644 --- a/net/tools/quic/quic_server.cc +++ b/net/tools/quic/quic_server.cc @@ -46,19 +46,12 @@ QuicServer::QuicServer() // Use hardcoded crypto parameters for now. config_.SetDefaults(); - CryptoHandshakeMessage extra_tags; - config_.ToHandshakeMessage(&extra_tags); QuicEpollClock clock(&epoll_server_); scoped_ptr<CryptoHandshakeMessage> scfg( crypto_config_.AddDefaultConfig( - QuicRandom::GetInstance(), &clock, extra_tags, + QuicRandom::GetInstance(), &clock, QuicCryptoServerConfig::kDefaultExpiry)); - // If we were using the same config in many servers then we would have to - // parse a QuicConfig from config_tags here. - if (!config_.SetFromHandshakeMessage(*scfg)) { - CHECK(false) << "Crypto config could not be parsed by QuicConfig."; - } } QuicServer::~QuicServer() { diff --git a/net/tools/quic/quic_server_session.cc b/net/tools/quic/quic_server_session.cc index f692332..330c307 100644 --- a/net/tools/quic/quic_server_session.cc +++ b/net/tools/quic/quic_server_session.cc @@ -16,9 +16,10 @@ QuicServerSession::QuicServerSession( const QuicCryptoServerConfig& crypto_config, QuicConnection* connection, QuicSessionOwner* owner) - : QuicSession(connection, true), - crypto_stream_(config, crypto_config, this), + : QuicSession(connection, config, true), + crypto_stream_(crypto_config, this), owner_(owner) { + set_max_open_streams(config.max_streams_per_connection()); } QuicServerSession::~QuicServerSession() { diff --git a/net/tools/quic/quic_socket_utils.cc b/net/tools/quic/quic_socket_utils.cc index 1798b04..329217a 100644 --- a/net/tools/quic/quic_socket_utils.cc +++ b/net/tools/quic/quic_socket_utils.cc @@ -149,7 +149,7 @@ int QuicSocketUtils::WritePacket(int fd, const char* buffer, size_t buf_len, if (self_address.empty()) { hdr.msg_control = 0; hdr.msg_controllen = 0; - } else if (self_address.size() == sizeof(sockaddr_in)) { + } else if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) { cmsghdr *cmsg = reinterpret_cast<cmsghdr*>(cbuf); hdr.msg_control = cmsg; hdr.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo)); diff --git a/net/tools/quic/test_tools/quic_client_peer.cc b/net/tools/quic/test_tools/quic_client_peer.cc new file mode 100644 index 0000000..8583594 --- /dev/null +++ b/net/tools/quic/test_tools/quic_client_peer.cc @@ -0,0 +1,27 @@ +// 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/tools/quic/test_tools/quic_client_peer.h" + +#include "net/tools/quic/quic_client.h" + +namespace net { +namespace tools { +namespace test { + +// static +void QuicClientPeer::Reinitialize(QuicClient* client) { + client->initialized_ = false; + client->epoll_server_.UnregisterFD(client->fd_); + client->Initialize(); +} + +// static +int QuicClientPeer::GetFd(QuicClient* client) { + return client->fd_; +} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/net/tools/quic/test_tools/quic_client_peer.h b/net/tools/quic/test_tools/quic_client_peer.h new file mode 100644 index 0000000..8eaa17e --- /dev/null +++ b/net/tools/quic/test_tools/quic_client_peer.h @@ -0,0 +1,25 @@ +// 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 NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ + +namespace net { +namespace tools { + +class QuicClient; + +namespace test { + +class QuicClientPeer { + public: + static void Reinitialize(QuicClient* client); + static int GetFd(QuicClient* client); +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_ diff --git a/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc new file mode 100644 index 0000000..e358273 --- /dev/null +++ b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc @@ -0,0 +1,21 @@ +// 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/tools/quic/test_tools/quic_epoll_connection_helper_peer.h" + +#include "net/tools/quic/quic_epoll_connection_helper.h" + +namespace net { +namespace tools { +namespace test { + +// static +void QuicEpollConnectionHelperPeer::SetWriter(QuicEpollConnectionHelper* helper, + QuicPacketWriter* writer) { + helper->writer_ = writer; +} + +} // namespace test +} // namespace tools +} // namespace net diff --git a/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h new file mode 100644 index 0000000..72085df --- /dev/null +++ b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h @@ -0,0 +1,31 @@ +// 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 NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_ +#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_ + +#include "base/basictypes.h" + +namespace net { +namespace tools { + +class QuicPacketWriter; +class QuicEpollConnectionHelper; + +namespace test { + +class QuicEpollConnectionHelperPeer { + public: + static void SetWriter(QuicEpollConnectionHelper* helper, + QuicPacketWriter* writer); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicEpollConnectionHelperPeer); +}; + +} // namespace test +} // namespace tools +} // namespace net + +#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_ diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc index c6c921e8..1a7539f 100644 --- a/net/tools/quic/test_tools/quic_test_client.cc +++ b/net/tools/quic/test_tools/quic_test_client.cc @@ -182,6 +182,9 @@ size_t QuicTestClient::bytes_written() const { } void QuicTestClient::OnClose(ReliableQuicStream* stream) { + if (stream_ != stream) { + return; + } response_ = stream_->data(); headers_.CopyFrom(stream_->headers()); stream_error_ = stream_->stream_error(); diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc index a5c22ea6..05c3a57 100644 --- a/net/tools/quic/test_tools/quic_test_utils.cc +++ b/net/tools/quic/test_tools/quic_test_utils.cc @@ -54,8 +54,14 @@ bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) { return true; } -TestSession::TestSession(QuicConnection* connection, bool is_server) - : QuicSession(connection, is_server), +void TestDecompressorVisitor::OnDecompressionError() { + error_ = true; +} + +TestSession::TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server) + : QuicSession(connection, config, is_server), crypto_stream_(NULL) { } diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h index fd69a1d..68f4de4 100644 --- a/net/tools/quic/test_tools/quic_test_utils.h +++ b/net/tools/quic/test_tools/quic_test_utils.h @@ -75,16 +75,21 @@ class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { public: virtual ~TestDecompressorVisitor() {} virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; + virtual void OnDecompressionError() OVERRIDE; std::string data() { return data_; } + bool error() { return error_; } private: std::string data_; + bool error_; }; class TestSession : public QuicSession { public: - TestSession(QuicConnection* connection, bool is_server); + TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server); virtual ~TestSession(); MOCK_METHOD1(CreateIncomingReliableStream, |