diff options
Diffstat (limited to 'net/quic')
29 files changed, 5451 insertions, 0 deletions
diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc new file mode 100644 index 0000000..806b336 --- /dev/null +++ b/net/quic/crypto/crypto_framer.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/crypto_framer.h" + +#include "net/quic/quic_data_reader.h" +#include "net/quic/quic_data_writer.h" +#include "net/quic/quic_protocol.h" + +using base::StringPiece; + +namespace net { + +CryptoFramer::CryptoFramer() + : visitor_(NULL) { + Clear(); +} + +CryptoFramer::~CryptoFramer() {} + +bool CryptoFramer::ProcessInput(StringPiece input) { + DCHECK_EQ(QUIC_NO_ERROR, error_); + if (error_ != QUIC_NO_ERROR) { + return false; + } + // Add this data to the buffer. + buffer_.append(input.data(), input.length()); + QuicDataReader reader(buffer_.data(), buffer_.length()); + + switch (state_) { + case STATE_READING_TAG: + if (reader.BytesRemaining() < sizeof(uint32)) { + break; + } + reader.ReadUInt32(&message_tag_); + state_ = STATE_READING_NUM_ENTRIES; + case STATE_READING_NUM_ENTRIES: + if (reader.BytesRemaining() < sizeof(uint16)) { + break; + } + reader.ReadUInt16(&num_entries_); + if (num_entries_ > kMaxEntries) { + error_ = QUIC_CRYPTO_TOO_MANY_ENTRIES; + return false; + } + state_ = STATE_READING_KEY_TAGS; + case STATE_READING_KEY_TAGS: + if (reader.BytesRemaining() < num_entries_ * sizeof(uint32)) { + break; + } + for (int i = 0; i < num_entries_; ++i) { + CryptoTag tag; + reader.ReadUInt32(&tag); + if (i > 0 && tag <= tags_.back()) { + error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER; + return false; + } + tags_.push_back(tag); + } + state_ = STATE_READING_LENGTHS; + case STATE_READING_LENGTHS: + if (reader.BytesRemaining() < num_entries_ * sizeof(uint16)) { + break; + } + values_len_ = 0; + for (int i = 0; i < num_entries_; ++i) { + uint16 len; + reader.ReadUInt16(&len); + tag_length_map_[tags_[i]] = len; + values_len_ += len; + if (len == 0 && i != num_entries_ - 1) { + error_ = QUIC_CRYPTO_INVALID_VALUE_LENGTH; + return false; + } + } + state_ = STATE_READING_VALUES; + case STATE_READING_VALUES: + if (reader.BytesRemaining() < values_len_) { + break; + } + for (int i = 0; i < num_entries_; ++i) { + StringPiece value; + reader.ReadStringPiece(&value, tag_length_map_[tags_[i]]); + tag_value_map_[tags_[i]] = value; + } + CryptoHandshakeMessage message; + message.tag = message_tag_; + message.tag_value_map.swap(tag_value_map_); + visitor_->OnHandshakeMessage(message); + Clear(); + state_ = STATE_READING_TAG; + break; + } + // Save any left over data + buffer_ = reader.PeekRemainingPayload().as_string(); + return true; +} + +bool CryptoFramer::ConstructHandshakeMessage( + const CryptoHandshakeMessage& message, + QuicData** packet) { + if (message.tag_value_map.size() > kMaxEntries) { + return false; + } + size_t len = sizeof(uint32); // message tag + len += sizeof(uint16); // number of map entries + CryptoTagValueMap::const_iterator it = message.tag_value_map.begin(); + while (it != message.tag_value_map.end()) { + len += sizeof(uint32); // tag + len += sizeof(uint16); // value len + len += it->second.length(); // value + if (it->second.length() == 0) { + return false; + } + ++it; + } + if (message.tag_value_map.size() % 2 == 1) { + len += sizeof(uint16); // padding + } + + QuicDataWriter writer(len); + CHECK(writer.WriteUInt32(message.tag)); + CHECK(writer.WriteUInt16(message.tag_value_map.size())); + // Tags + for (it = message.tag_value_map.begin(); + it != message.tag_value_map.end(); ++it) { + CHECK(writer.WriteUInt32(it->first)); + } + // Lengths + for (it = message.tag_value_map.begin(); + it != message.tag_value_map.end(); ++it) { + CHECK(writer.WriteUInt16(it->second.length())); + } + // Possible padding + if (message.tag_value_map.size() % 2 == 1) { + CHECK(writer.WriteUInt16(0xABAB)); + } + // Values + for (it = message.tag_value_map.begin(); + it != message.tag_value_map.end(); ++it) { + CHECK(writer.WriteBytes(it->second.data(), it->second.length())); + } + *packet = new QuicData(writer.take(), len, true); + return true; +} + +void CryptoFramer::Clear() { + tag_value_map_.clear(); + tag_length_map_.clear(); + tags_.clear(); + error_ = QUIC_NO_ERROR; + state_ = STATE_READING_TAG; +} + +} // namespace net diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h new file mode 100644 index 0000000..e52b264 --- /dev/null +++ b/net/quic/crypto/crypto_framer.h @@ -0,0 +1,106 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ +#define NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ + +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class CryptoFramer; +class QuicDataReader; +class QuicData; + +class NET_EXPORT_PRIVATE CryptoFramerVisitorInterface { + public: + virtual ~CryptoFramerVisitorInterface() {} + + // Called if an error is detected. + virtual void OnError(CryptoFramer* framer) = 0; + + // Called when a complete handshake message has been parsed. + virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) = 0; +}; + +// A class for framing the crypto message that are exchanged in a QUIC session. +class NET_EXPORT_PRIVATE CryptoFramer { + public: + CryptoFramer(); + + virtual ~CryptoFramer(); + + // Set callbacks to be called from the framer. A visitor must be set, or + // else the framer will likely crash. It is acceptable for the visitor + // to do nothing. If this is called multiple times, only the last visitor + // will be used. |visitor| will be owned by the framer. + void set_visitor(CryptoFramerVisitorInterface* visitor) { + visitor_ = visitor; + } + + QuicErrorCode error() const { + return error_; + } + + // Processes input data, which must be delievered in order. Returns + // false if there was an error, and true otherwise. + bool ProcessInput(base::StringPiece input); + + // Serializes |message| into |packet|. Returns false if there was an + // error, and true otherwise. + bool ConstructHandshakeMessage(const CryptoHandshakeMessage& message, + QuicData** packet); + + private: + // Clears per-message state. Does not clear the visitor. + void Clear(); + + void set_error(QuicErrorCode error) { + error_ = error; + } + + enum CryptoFramerState { + STATE_READING_TAG, + STATE_READING_NUM_ENTRIES, + STATE_READING_KEY_TAGS, + STATE_READING_LENGTHS, + STATE_READING_VALUES + }; + + // Visitor to send invoke when messages are parsed. + CryptoFramerVisitorInterface* visitor_; + // Last error. + QuicErrorCode error_; + // Remaining unparsed data. + std::string buffer_; + // Current state of the parsing. + CryptoFramerState state_; + // Tag of the message currently being parsed. + CryptoTag message_tag_; + // Number of entires in the message currently being parsed. + uint16 num_entries_; + // Vector of tags in the message currently being parsed. + std::vector<CryptoTag> tags_; + // Length of the data associated with each tag in the message currently + // being parsed. + std::map<CryptoTag, size_t> tag_length_map_; + // Length of the data associated with each tag in the message currently + // being parsed. + CryptoTagValueMap tag_value_map_; + // Cumulative length of all values in the message currently being parsed. + size_t values_len_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ diff --git a/net/quic/crypto/crypto_framer_test.cc b/net/quic/crypto/crypto_framer_test.cc new file mode 100644 index 0000000..8a32f26 --- /dev/null +++ b/net/quic/crypto/crypto_framer_test.cc @@ -0,0 +1,316 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <map> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; +using std::map; +using std::string; +using std::vector; + +namespace net { + +namespace { + +char* AsChars(unsigned char* data) { + return reinterpret_cast<char*>(data); +} + +} // namespace + +namespace test { + +class TestCryptoVisitor : public ::net::CryptoFramerVisitorInterface { + public: + TestCryptoVisitor() + : error_count_(0) { + } + + ~TestCryptoVisitor() {} + + virtual void OnError(CryptoFramer* f) { + LOG(INFO) << "CryptoFramer Error: " << f->error(); + error_count_++; + } + + virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) { + message_tags_.push_back(message.tag); + message_maps_.push_back(map<CryptoTag, string>()); + CryptoTagValueMap::const_iterator it = message.tag_value_map.begin(); + while (it != message.tag_value_map.end()) { + message_maps_.back()[it->first] = it->second.as_string(); + ++it; + } + } + + CryptoFramer framer_; + + // Counters from the visitor callbacks. + int error_count_; + + vector<CryptoTag> message_tags_; + vector<map<CryptoTag, string> > message_maps_; +}; + +} // namespace test + +TEST(CryptoFramerTest, ConstructHandshakeMessage) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + message.tag_value_map[0x12345678] = "abcdef"; + message.tag_value_map[0x12345679] = "ghijk"; + message.tag_value_map[0x1234567A] = "lmnopqr"; + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x03, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // tag 3 + 0x7A, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // len 3 + 0x07, 0x00, + // padding + 0xAB, 0xAB, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + // value 3 + 'l', 'm', 'n', 'o', + 'p', 'q', 'r', + }; + + CryptoFramer framer; + QuicData* data; + EXPECT_TRUE(framer.ConstructHandshakeMessage(message, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + message.tag_value_map[0x12345678] = "abcdef"; + message.tag_value_map[0x12345679] = "ghijk"; + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + }; + + CryptoFramer framer; + QuicData* data; + EXPECT_TRUE(framer.ConstructHandshakeMessage(message, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + for (uint32 key = 1; key <= kMaxEntries + 1; key++) { + message.tag_value_map[key] = "abcdef"; + } + + CryptoFramer framer; + QuicData* data = NULL; + EXPECT_FALSE(framer.ConstructHandshakeMessage(message, &data)); + delete data; +} + + +TEST(CryptoFramerTest, ConstructHandshakeMessageInvalidLength) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + message.tag_value_map[0x12345678] = ""; + + CryptoFramer framer; + QuicData* data = NULL; + EXPECT_FALSE(framer.ConstructHandshakeMessage(message, &data)); + delete data; +} + +TEST(CryptoFramerTest, EmptyPacket) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); +} + +TEST(CryptoFramerTest, ProcessInput) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + }; + + EXPECT_TRUE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + ASSERT_EQ(1u, visitor.message_tags_.size()); + EXPECT_EQ(0xFFAA7733, visitor.message_tags_[0]); + EXPECT_EQ(2u, visitor.message_maps_[0].size()); + EXPECT_EQ("abcdef",visitor.message_maps_[0][0x12345678]); + EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]); +} + +TEST(CryptoFramerTest, ProcessInputIncrementally) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + }; + + for (size_t i = 0; i < arraysize(input); i++) { + EXPECT_TRUE(framer.ProcessInput(StringPiece(AsChars(input)+ i, 1))); + } + ASSERT_EQ(1u, visitor.message_tags_.size()); + EXPECT_EQ(0xFFAA7733, visitor.message_tags_[0]); + EXPECT_EQ(2u, visitor.message_maps_[0].size()); + EXPECT_EQ("abcdef",visitor.message_maps_[0][0x12345678]); + EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]); +} + +TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x79, 0x56, 0x34, 0x12, + // tag 2 + 0x78, 0x56, 0x34, 0x12, + }; + + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error()); +} + +TEST(CryptoFramerTest, ProcessInputTooManyEntries) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0xA0, 0x00, + }; + + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error()); +} + +TEST(CryptoFramerTest, ProcessInputInvalidLength) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x00, 0x00, + // len 2 + 0x05, 0x00, + }; + + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_INVALID_VALUE_LENGTH, framer.error()); +} + +} // namespace net diff --git a/net/quic/crypto/crypto_protocol.cc b/net/quic/crypto/crypto_protocol.cc new file mode 100644 index 0000000..ba87906 --- /dev/null +++ b/net/quic/crypto/crypto_protocol.cc @@ -0,0 +1,12 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/crypto_protocol.h" + +namespace net { + +CryptoHandshakeMessage::CryptoHandshakeMessage() {} +CryptoHandshakeMessage::~CryptoHandshakeMessage() {} + +} // namespace net diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h new file mode 100644 index 0000000..a9c7083 --- /dev/null +++ b/net/quic/crypto/crypto_protocol.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ +#define NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" + +namespace net { + +typedef uint32 CryptoTag; +typedef std::map<CryptoTag, base::StringPiece> CryptoTagValueMap; +struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { + CryptoHandshakeMessage(); + ~CryptoHandshakeMessage(); + CryptoTag tag; + CryptoTagValueMap tag_value_map; +}; + +// Crypto tags are written to the wire with a big-endian +// representation of the name of the tag. For example +// the client hello tag (CHLO) will be written as the +// following 4 bytes: 'C' 'H' 'L' 'O'. Since it is +// stored in memory as a little endian uint32, we need +// to reverse the order of the bytes. +#define MAKE_TAG(a,b,c,d) (d << 24) + (c << 16) + (b << 8) + a + +const CryptoTag kCHLO = MAKE_TAG('C', 'H', 'L', 'O'); // Client hello +const CryptoTag kSHLO = MAKE_TAG('S', 'H', 'L', 'O'); // Server hello + +// AEAD algorithms +const CryptoTag kNULL = MAKE_TAG('N', 'U', 'L', 'L'); // null algorithm +const CryptoTag kAESH = MAKE_TAG('A', 'E', 'S', 'H'); // AES128 + SHA256 + +const size_t kMaxEntries = 16; // Max number of entries in a message. + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ diff --git a/net/quic/crypto/null_decrypter.cc b/net/quic/crypto/null_decrypter.cc new file mode 100644 index 0000000..072c684 --- /dev/null +++ b/net/quic/crypto/null_decrypter.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/null_decrypter.h" +#include "net/quic/quic_utils.h" +#include "net/quic/quic_data_reader.h" + +using base::StringPiece; +using std::string; + +namespace net { + +QuicData* NullDecrypter::Decrypt(StringPiece associated_data, + StringPiece ciphertext) { + QuicDataReader reader(ciphertext.data(), ciphertext.length()); + + uint128 hash; + if (!reader.ReadUInt128(&hash)) { + return NULL; + } + + StringPiece plaintext = reader.ReadRemainingPayload(); + + // TODO(rch): avoid buffer copy here + string buffer = associated_data.as_string(); + plaintext.AppendToString(&buffer); + + if (hash != QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length())) { + return NULL; + } + return new QuicData(plaintext.data(), plaintext.length()); +} + +} // namespace net diff --git a/net/quic/crypto/null_decrypter.h b/net/quic/crypto/null_decrypter.h new file mode 100644 index 0000000..5495092 --- /dev/null +++ b/net/quic/crypto/null_decrypter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_NULL_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_NULL_DECRYPTER_H_ + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/quic_decrypter.h" + +namespace net { + +// A NullDecrypter is a QuicDecrypter used before a crypto negotiation +// has occurred. It does not actually decrypt the payload, but does +// verify a hash (fnv128) over both the payload and associated data. +class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { + public: + virtual ~NullDecrypter() {} + + // QuicDecrypter implementation + virtual QuicData* Decrypt(base::StringPiece associated_data, + base::StringPiece ciphertext) OVERRIDE; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_NULL_DECRYPTER_H_ diff --git a/net/quic/crypto/null_decrypter_test.cc b/net/quic/crypto/null_decrypter_test.cc new file mode 100644 index 0000000..6c914d7 --- /dev/null +++ b/net/quic/crypto/null_decrypter_test.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/null_decrypter.h" +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; + +namespace net { + +namespace test { + +TEST(NullDecrypterTest, Decrypt) { + unsigned char expected[] = { + // fnv hash + 0x07, 0x2d, 0x42, 0xf0, + 0xbe, 0x69, 0x12, 0x3d, + 0x20, 0x80, 0x5f, 0x9a, + 0x84, 0x9d, 0xd6, 0x0a, + /* TODO(rch): replace this when uint128 multiplication is implemented. + 0x47, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, 0x37, + */ + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted( + decrypter.Decrypt("hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_TRUE(decrypted.get()); + EXPECT_EQ("goodbye!", decrypted->AsStringPiece()); +} + +TEST(NullDecrypterTest, BadHash) { + unsigned char expected[] = { + // fnv hash + 0x46, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, 0x37, + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted( + decrypter.Decrypt("hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_FALSE(decrypted.get()); +} + +TEST(NullDecrypterTest, ShortInput) { + unsigned char expected[] = { + // fnv hash (truncated) + 0x46, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted( + decrypter.Decrypt("hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_FALSE(decrypted.get()); +} + +} // namespace test + +} // namespace net diff --git a/net/quic/crypto/null_encrypter.cc b/net/quic/crypto/null_encrypter.cc new file mode 100644 index 0000000..fda844b --- /dev/null +++ b/net/quic/crypto/null_encrypter.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/null_encrypter.h" +#include "net/quic/quic_data_writer.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using std::string; + +namespace net { + +const size_t kHashSize = 16; // size of uint128 serialized + +QuicData* NullEncrypter::Encrypt(StringPiece associated_data, + StringPiece plaintext) { + // TODO(rch): avoid buffer copy here + string buffer = associated_data.as_string(); + plaintext.AppendToString(&buffer); + uint128 hash = QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length()); + QuicDataWriter writer(plaintext.length() + kHashSize); + writer.WriteUInt128(hash); + writer.WriteBytes(plaintext.data(), plaintext.length()); + size_t len = writer.length(); + return new QuicData(writer.take(), len, true); +} + +size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) { + return ciphertext_size - kHashSize; +} + +size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) { + return plaintext_size + kHashSize; +} + +} // namespace net diff --git a/net/quic/crypto/null_encrypter.h b/net/quic/crypto/null_encrypter.h new file mode 100644 index 0000000..e73423d --- /dev/null +++ b/net/quic/crypto/null_encrypter.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_ + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/quic_encrypter.h" + +namespace net { + +// A NullEncrypter is a QuicEncrypter used before a crypto negotiation +// has occurred. It does not actually encrypt the payload, but does +// generate a MAC (fnv128) over both the payload and associated data. +class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { + public: + virtual ~NullEncrypter() {} + + // QuicEncrypter implementation + virtual QuicData* Encrypt(base::StringPiece associated_data, + base::StringPiece plaintext) OVERRIDE; + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) OVERRIDE; + virtual size_t GetCiphertextSize(size_t plaintext_size) OVERRIDE; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_ diff --git a/net/quic/crypto/null_encrypter_test.cc b/net/quic/crypto/null_encrypter_test.cc new file mode 100644 index 0000000..a71ee34 --- /dev/null +++ b/net/quic/crypto/null_encrypter_test.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/null_encrypter.h" +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; + +namespace net { + +namespace test { + +TEST(NullEncrypterTest, Encrypt) { + unsigned char expected[] = { + // fnv hash + 0x07, 0x2d, 0x42, 0xf0, + 0xbe, 0x69, 0x12, 0x3d, + 0x20, 0x80, 0x5f, 0x9a, + 0x84, 0x9d, 0xd6, 0x0a, + /* TODO(rch): use this when uint128 multiplication is implemented. + 0x47, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, 0x37, + */ + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullEncrypter encrypter; + scoped_ptr<QuicData> encrypted(encrypter.Encrypt("hello world!", + "goodbye!")); + ASSERT_TRUE(encrypted.get()); + test::CompareCharArraysWithHexError( + "encrypted data", encrypted->data(), encrypted->length(), + reinterpret_cast<const char*>(expected), arraysize(expected)); +} + +TEST(NullEncrypterTest, GetMaxPlaintextSize) { + NullEncrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26)); +} + +TEST(NullEncrypterTest, GetCiphertextSize) { + NullEncrypter encrypter; + EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(116u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(26u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test + +} // namespace net diff --git a/net/quic/crypto/quic_decrypter.cc b/net/quic/crypto/quic_decrypter.cc new file mode 100644 index 0000000..997a311 --- /dev/null +++ b/net/quic/crypto/quic_decrypter.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/null_decrypter.h" + +namespace net { + +// static +QuicDecrypter* QuicDecrypter::Create(CryptoTag algorithm) { + switch (algorithm) { + case kNULL: + return new NullDecrypter(); + default: + LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return NULL; + } +} + +} // namespace net diff --git a/net/quic/crypto/quic_decrypter.h b/net/quic/crypto/quic_decrypter.h new file mode 100644 index 0000000..e5c5635 --- /dev/null +++ b/net/quic/crypto/quic_decrypter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_ + +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicDecrypter { + public: + virtual ~QuicDecrypter() {} + + static QuicDecrypter* Create(CryptoTag algorithm); + + // Returns a newly created QuicData object containing the decrypted + // |ciphertext| or NULL if there is an error. + virtual QuicData* Decrypt(base::StringPiece associated_data, + base::StringPiece ciphertext) = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_ diff --git a/net/quic/crypto/quic_encrypter.cc b/net/quic/crypto/quic_encrypter.cc new file mode 100644 index 0000000..cc13013 --- /dev/null +++ b/net/quic/crypto/quic_encrypter.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/null_encrypter.h" + +namespace net { + +// static +QuicEncrypter* QuicEncrypter::Create(CryptoTag algorithm) { + switch (algorithm) { + case kNULL: + return new NullEncrypter(); + default: + LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return NULL; + } +} + +} // namespace net diff --git a/net/quic/crypto/quic_encrypter.h b/net/quic/crypto/quic_encrypter.h new file mode 100644 index 0000000..f077c1f --- /dev/null +++ b/net/quic/crypto/quic_encrypter.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ + +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicEncrypter { + public: + virtual ~QuicEncrypter() {} + + static QuicEncrypter* Create(CryptoTag algorithm); + + // Returns a newly created QuicData object containing the encrypted + // |plaintext| as well as a MAC over both |plaintext| and |associated_data|, + // or NULL if there is an error. + virtual QuicData* Encrypt(base::StringPiece associated_data, + base::StringPiece plaintext) = 0; + + // Returns the maximum length of plaintext that can be encrypted + // to ciphertext no larger than |ciphertext_size|. + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) = 0; + + // Returns the length of the ciphertext that would be generated by encrypting + // to plaintext of size |plaintext_size|. + virtual size_t GetCiphertextSize(size_t plaintext_size) = 0; + +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ diff --git a/net/quic/quic_data_reader.cc b/net/quic/quic_data_reader.cc new file mode 100644 index 0000000..263bafb --- /dev/null +++ b/net/quic/quic_data_reader.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_data_reader.h" + +using base::StringPiece; + +namespace net { + +QuicDataReader::QuicDataReader(const char* data, const size_t len) + : data_(data), + len_(len), + pos_(0) { +} + +bool QuicDataReader::ReadUInt16(uint16* result) { + // Make sure that we have the whole uint16. + // TODO(rch): use sizeof instead of magic numbers. + // Refactor to use a common Read(void* buffer, size_t len) + // method that will do the memcpy and the advancement of pos_. + if (!CanRead(2)) { + OnFailure(); + return false; + } + + // Read into result. + memcpy(result, data_ + pos_, 2); + + // Iterate. + pos_ += 2; + + return true; +} + +bool QuicDataReader::ReadUInt32(uint32* result) { + // Make sure that we have the whole uint32. + if (!CanRead(4)) { + OnFailure(); + return false; + } + + // Read into result. + memcpy(result, data_ + pos_, 4); + + // Iterate. + pos_ += 4; + + return true; +} + +bool QuicDataReader::ReadUInt48(uint64* result) { + uint32 lo; + if (!ReadUInt32(&lo)) { + return false; + } + + uint16 hi; + if (!ReadUInt16(&hi)) { + return false; + } + + *result = hi; + *result <<= 32; + *result += lo; + + return true; +} + +bool QuicDataReader::ReadUInt64(uint64* result) { + // Make sure that we have the whole uint64. + if (!CanRead(8)) { + OnFailure(); + return false; + } + + // Read into result. + memcpy(result, data_ + pos_, 8); + + // Iterate. + pos_ += 8; + + return true; +} + +bool QuicDataReader::ReadUInt128(uint128* result) { + uint64 high_hash; + uint64 low_hash; + + if (!ReadUInt64(&low_hash)) { + return false; + } + if (!ReadUInt64(&high_hash)) { + return false; + } + + *result = uint128(high_hash, low_hash); + return true; +} + +bool QuicDataReader::ReadStringPiece16(StringPiece* result) { + // Read resultant length. + uint16 result_len; + if (!ReadUInt16(&result_len)) { + // OnFailure() already called. + return false; + } + + return ReadStringPiece(result, result_len); +} + +bool QuicDataReader::ReadBytes(void* result, size_t size) { + // Make sure that we have enough data to read. + if (!CanRead(size)) { + OnFailure(); + return false; + } + + // Read into result. + memcpy(result, data_ + pos_, size); + + // Iterate. + pos_ += size; + + return true; +} + + +bool QuicDataReader::ReadStringPiece(StringPiece* result, size_t size) { + // Make sure that we have enough data to read. + if (!CanRead(size)) { + OnFailure(); + return false; + } + + // Set result. + result->set(data_ + pos_, size); + + // Iterate. + pos_ += size; + + return true; +} + +StringPiece QuicDataReader::PeekRemainingPayload() { + return StringPiece(data_ + pos_, len_ - pos_); +} + +StringPiece QuicDataReader::ReadRemainingPayload() { + StringPiece payload = PeekRemainingPayload(); + pos_ = len_; + return payload; +} + +bool QuicDataReader::IsDoneReading() const { + return len_ == pos_; +} + +size_t QuicDataReader::BytesRemaining() const { + return len_ - pos_; +} + +bool QuicDataReader::CanRead(size_t bytes) const { + return bytes <= (len_ - pos_); +} + +void QuicDataReader::OnFailure() { + // Set our iterator to the end of the buffer so that further reads fail + // immediately. + pos_ = len_; +} + +} // namespace net diff --git a/net/quic/quic_data_reader.h b/net/quic/quic_data_reader.h new file mode 100644 index 0000000..c03c0a9 --- /dev/null +++ b/net/quic/quic_data_reader.h @@ -0,0 +1,125 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_DATA_READER_H_ +#define NET_QUIC_QUIC_DATA_READER_H_ + +#include "base/basictypes.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/uint128.h" + +namespace net { + +// Used for reading QUIC data. Though there isn't really anything terribly +// QUIC-specific here, it's a helper class that's useful when doing QUIC +// framing. +// +// To use, simply construct a QuicDataReader using the underlying buffer that +// you'd like to read fields from, then call one of the Read*() methods to +// actually do some reading. +// +// This class keeps an internal iterator to keep track of what's already been +// read and each successive Read*() call automatically increments said iterator +// on success. On failure, internal state of the QuicDataReader should not be +// trusted and it is up to the caller to throw away the failed instance and +// handle the error as appropriate. None of the Read*() methods should ever be +// called after failure, as they will also fail immediately. +class NET_EXPORT_PRIVATE QuicDataReader { + public: + // Caller must provide an underlying buffer to work on. + QuicDataReader(const char* data, const size_t len); + + // Empty destructor. + ~QuicDataReader() {} + + // Reads a 16-bit unsigned integer into the given output parameter. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadUInt16(uint16* result); + + // Reads a 32-bit unsigned integer into the given output parameter. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadUInt32(uint32* result); + + // Reads a 48-bit unsigned integer into the given output parameter. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadUInt48(uint64* result); + + // Reads a 64-bit unsigned integer into the given output parameter. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadUInt64(uint64* result); + + // Reads a 128-bit unsigned integer into the given output parameter. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadUInt128(uint128* result); + // Reads a string prefixed with 16-bit length into the given output parameter. + // + // NOTE: Does not copy but rather references strings in the underlying buffer. + // This should be kept in mind when handling memory management! + // + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadStringPiece16(base::StringPiece* result); + + // Reads a given number of bytes into the given buffer. The buffer + // must be of adequate size. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadStringPiece(base::StringPiece* result, size_t len); + + // Returns the remaining payload as a StringPiece. + // + // NOTE: Does not copy but rather references strings in the underlying buffer. + // This should be kept in mind when handling memory management! + // + // Forwards the internal iterator. + base::StringPiece ReadRemainingPayload(); + + // Returns the remaining payload as a StringPiece. + // + // NOTE: Does not copy but rather references strings in the underlying buffer. + // This should be kept in mind when handling memory management! + // + // DOES NOT forward the internal iterator. + base::StringPiece PeekRemainingPayload(); + + // Reads a given number of bytes into the given buffer. The buffer + // must be of adequate size. + // Forwards the internal iterator on success. + // Returns true on success, false otherwise. + bool ReadBytes(void* result, size_t size); + + // Returns true if the entirety of the underlying buffer has been read via + // Read*() calls. + bool IsDoneReading() const; + + // Returns the number of bytes remaining to be read. + size_t BytesRemaining() const; + + private: + // Returns true if the underlying buffer has enough room to read the given + // amount of bytes. + bool CanRead(size_t bytes) const; + + // To be called when a read fails for any reason. + void OnFailure(); + + // The data buffer that we're reading from. + const char* data_; + + // The length of the data buffer that we're reading from. + const size_t len_; + + // The location of the next read from our data buffer. + size_t pos_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_DATA_READER_H_ diff --git a/net/quic/quic_data_writer.cc b/net/quic/quic_data_writer.cc new file mode 100644 index 0000000..f76b253 --- /dev/null +++ b/net/quic/quic_data_writer.cc @@ -0,0 +1,69 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_data_writer.h" + +#include <algorithm> +#include <limits> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "net/quic/quic_protocol.h" + +using std::numeric_limits; + +namespace net { + +QuicDataWriter::QuicDataWriter(size_t size) + : buffer_(new char[size]), + capacity_(size), + length_(0) { +} + +QuicDataWriter::~QuicDataWriter() { + delete[] buffer_; +} + +char* QuicDataWriter::BeginWrite(size_t length) { + if (capacity_ - length_ < length) { + return NULL; + } + +#ifdef ARCH_CPU_64_BITS + DCHECK_LE(length, numeric_limits<uint32>::max()); +#endif + + return buffer_ + length_; +} + +bool QuicDataWriter::AdvancePointer(uint32 len) { + if (!BeginWrite(len)) { + return false; + } + length_ += len; + return true; +} + +bool QuicDataWriter::WriteBytes(const void* data, uint32 data_len) { + char* dest = BeginWrite(data_len); + if (!dest) { + return false; + } + + memcpy(dest, data, data_len); + + length_ += data_len; + return true; +} + +void QuicDataWriter::WriteUint64ToBuffer(uint64 value, char* buffer) { + memcpy(buffer, &value, sizeof(value)); +} + +void QuicDataWriter::WriteUint128ToBuffer(uint128 value, char* buffer) { + WriteUint64ToBuffer(value.lo, buffer); + WriteUint64ToBuffer(value.hi, buffer + sizeof(value.lo)); +} + +} // namespace net diff --git a/net/quic/quic_data_writer.h b/net/quic/quic_data_writer.h new file mode 100644 index 0000000..956051f --- /dev/null +++ b/net/quic/quic_data_writer.h @@ -0,0 +1,94 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_DATA_WRITER_H_ +#define NET_QUIC_QUIC_DATA_WRITER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/uint128.h" + +namespace net { + +// This class provides facilities for packing QUIC data. +// +// The QuicDataWriter supports appending primitive values (int, string, etc) +// to a frame instance. The QuicDataWriter grows its internal memory buffer +// dynamically to hold the sequence of primitive values. The internal memory +// buffer is exposed as the "data" of the QuicDataWriter. +class NET_EXPORT_PRIVATE QuicDataWriter { + public: + explicit QuicDataWriter(size_t length); + ~QuicDataWriter(); + + // Returns the size of the QuicDataWriter's data. + size_t length() const { return length_; } + + // Takes the buffer from the QuicDataWriter. + // TODO(rch): move non-trivial methods into .cc file. + char* take() { + char* rv = buffer_; + buffer_ = NULL; + capacity_ = 0; + length_ = 0; + return rv; + } + + // Methods for adding to the payload. These values are appended to the end + // of the QuicDataWriter payload. Note - binary integers are written in + // host byte order (little endian) not network byte order (big endian). + bool WriteUInt8(uint8 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt16(uint16 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt32(uint32 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt48(uint64 value) { + uint32 hi = value >> 32; + uint32 lo = value & GG_ULONGLONG(0x00000000FFFFFFFF); + return WriteUInt32(lo) && WriteUInt16(hi); + } + bool WriteUInt64(uint64 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt128(uint128 value) { + return WriteUInt64(value.lo) && WriteUInt64(value.hi); + } + + bool AdvancePointer(uint32 len); + + bool WriteBytes(const void* data, uint32 data_len); + + static void WriteUint64ToBuffer(uint64 value, char* buffer); + static void WriteUint128ToBuffer(uint128 value, char* buffer); + + size_t capacity() const { + return capacity_; + } + + protected: + const char* end_of_payload() const { return buffer_ + length_; } + + private: + // Returns the location that the data should be written at, or NULL if there + // is not enough room. Call EndWrite with the returned offset and the given + // length to pad out for the next write. + char* BeginWrite(size_t length); + + char* buffer_; + size_t capacity_; // Allocation size of payload (or -1 if buffer is const). + size_t length_; // Current length of the buffer. +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_DATA_WRITER_H_ diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc new file mode 100644 index 0000000..e392306e2 --- /dev/null +++ b/net/quic/quic_framer.cc @@ -0,0 +1,784 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_framer.h" + +#include "base/hash_tables.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_data_reader.h" +#include "net/quic/quic_data_writer.h" +#include "net/quic/quic_utils.h" + +using base::hash_set; +using base::StringPiece; + +namespace net { + +QuicFramer::QuicFramer(QuicDecrypter* decrypter, QuicEncrypter* encrypter) + : visitor_(NULL), + fec_builder_(NULL), + error_(QUIC_NO_ERROR), + decrypter_(decrypter), + encrypter_(encrypter) { +} + +QuicFramer::~QuicFramer() {} + +bool QuicFramer::ConstructFragementDataPacket( + const QuicPacketHeader& header, + const QuicFragments& fragments, + QuicPacket** packet) { + // Compute the length of the packet. We use "magic numbers" here because + // sizeof(member_) is not necessairly the same as sizeof(member_wire_format). + size_t len = kPacketHeaderSize; + len += 1; // fragment count + for (size_t i = 0; i < fragments.size(); ++i) { + len += 1; // space for the 8 bit type + len += ComputeFragmentPayloadLength(fragments[i]); + } + + QuicDataWriter writer(len); + + if (!WritePacketHeader(header, &writer)) { + return false; + } + + // fragment count + DCHECK_GE(256u, fragments.size()); + if (!writer.WriteUInt8(fragments.size())) { + return false; + } + + for (size_t i = 0; i < fragments.size(); ++i) { + const QuicFragment& fragment = fragments[i]; + if (!writer.WriteUInt8(fragment.type)) { + return false; + } + + switch (fragment.type) { + case STREAM_FRAGMENT: + if (!AppendStreamFragmentPayload(*fragment.stream_fragment, + &writer)) { + return false; + } + break; + case PDU_FRAGMENT: + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + case ACK_FRAGMENT: + if (!AppendAckFragmentPayload(*fragment.ack_fragment, &writer)) { + return false; + } + break; + case RST_STREAM_FRAGMENT: + if (!AppendRstStreamFragmentPayload(*fragment.rst_stream_fragment, + &writer)) { + return false; + } + break; + case CONNECTION_CLOSE_FRAGMENT: + if (!AppendConnectionCloseFragmentPayload( + *fragment.connection_close_fragment, &writer)) { + return false; + } + break; + default: + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + } + + *packet = new QuicPacket(writer.take(), len, true); + if (fec_builder_) { + fec_builder_->OnBuiltFecProtectedPayload(header, + (*packet)->FecProtectedData()); + } + + return true; +} + +bool QuicFramer::ConstructFecPacket(const QuicPacketHeader& header, + const QuicFecData& fec, + QuicPacket** packet) { + // Compute the length of the packet. We use "magic numbers" here because + // sizeof(member_) is not necessairly the same as sizeof(member_wire_format). + size_t len = kPacketHeaderSize; + len += 6; // first protected packet sequence number + len += fec.redundancy.length(); + + QuicDataWriter writer(len); + + if (!WritePacketHeader(header, &writer)) { + return false; + } + + if (!writer.WriteUInt48(fec.first_protected_packet_sequence_number)) { + return false; + } + + if (!writer.WriteBytes(fec.redundancy.data(), fec.redundancy.length())) { + return false; + } + + *packet = new QuicPacket(writer.take(), len, true); + + return true; +} + +void QuicFramer::IncrementRetransmitCount(QuicPacket* packet) { + CHECK_GT(packet->length(), kPacketHeaderSize); + + ++packet->mutable_data()[kRetransmissionOffset]; +} + +uint8 QuicFramer::GetRetransmitCount(QuicPacket* packet) { + CHECK_GT(packet->length(), kPacketHeaderSize); + + return packet->mutable_data()[kRetransmissionOffset]; +} + +bool QuicFramer::ProcessPacket(const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet) { + DCHECK(!reader_.get()); + reader_.reset(new QuicDataReader(packet.data(), packet.length())); + visitor_->OnPacket(peer_address); + + // First parse the packet header. + QuicPacketHeader header; + if (!ProcessPacketHeader(&header, packet)) { + DLOG(WARNING) << "Unable to process header."; + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + + if (!visitor_->OnPacketHeader(header)) { + reader_.reset(NULL); + return true; + } + + if (packet.length() > kMaxPacketSize) { + DLOG(WARNING) << "Packet too large: " << packet.length(); + return RaiseError(QUIC_PACKET_TOO_LARGE); + } + + // Handle the payload. + if ((header.flags & PACKET_FLAGS_FEC) == 0) { + if (header.fec_group != 0) { + StringPiece payload = reader_->PeekRemainingPayload(); + visitor_->OnFecProtectedPayload(payload); + } + if (!ProcessFragmentData()) { + DLOG(WARNING) << "Unable to process fragment data."; + return false; + } + } else { + QuicFecData fec_data; + fec_data.fec_group = header.fec_group; + if (!reader_->ReadUInt48( + &fec_data.first_protected_packet_sequence_number)) { + set_detailed_error("Unable to read first protected packet."); + return false; + } + + fec_data.redundancy = reader_->ReadRemainingPayload(); + visitor_->OnFecData(fec_data); + } + + visitor_->OnPacketComplete(); + reader_.reset(NULL); + return true; +} + +bool QuicFramer::ProcessRevivedPacket(const IPEndPoint& peer_address, + const QuicPacketHeader& header, + StringPiece payload) { + DCHECK(!reader_.get()); + + visitor_->OnPacket(peer_address); + + visitor_->OnPacketHeader(header); + + if (payload.length() > kMaxPacketSize) { + set_detailed_error("Revived packet too large."); + return RaiseError(QUIC_PACKET_TOO_LARGE); + } + + reader_.reset(new QuicDataReader(payload.data(), payload.length())); + if (!ProcessFragmentData()) { + DLOG(WARNING) << "Unable to process fragment data."; + return false; + } + + visitor_->OnPacketComplete(); + reader_.reset(NULL); + return true; +} + +bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer) { + // ConnectionHeader + if (!writer->WriteUInt64(header.guid)) { + return false; + } + + if (!writer->WriteUInt48(header.packet_sequence_number)) { + return false; + } + + if (!writer->WriteBytes(&header.retransmission_count, 1)) { + return false; + } + + // CongestionMonitoredHeader + if (!writer->WriteUInt64(header.transmission_time)) { + return false; + } + + uint8 flags = static_cast<uint8>(header.flags); + if (!writer->WriteBytes(&flags, 1)) { + return false; + } + + if (!writer->WriteBytes(&header.fec_group, 1)) { + return false; + } + + return true; +} + +bool QuicFramer::ProcessPacketHeader(QuicPacketHeader* header, + const QuicEncryptedPacket& packet) { + // ConnectionHeader + if (!reader_->ReadUInt64(&header->guid)) { + set_detailed_error("Unable to read GUID."); + return false; + } + + if (!reader_->ReadUInt48(&header->packet_sequence_number)) { + set_detailed_error("Unable to read sequence number."); + return false; + } + + if (!reader_->ReadBytes(&header->retransmission_count, 1)) { + set_detailed_error("Unable to read retransmission count."); + return false; + } + + // CongestionMonitoredHeader + if (!reader_->ReadUInt64(&header->transmission_time)) { + set_detailed_error("Unable to read transmission time."); + return false; + } + + unsigned char flags; + if (!reader_->ReadBytes(&flags, 1)) { + set_detailed_error("Unable to read flags."); + return false; + } + + if (flags > PACKET_FLAGS_MAX) { + set_detailed_error("Illegal flags value."); + return false; + } + + header->flags = static_cast<QuicPacketFlags>(flags); + + if (!DecryptPayload(packet)) { + DLOG(WARNING) << "Unable to decrypt payload."; + return RaiseError(QUIC_DECRYPTION_FAILURE); + } + + if (!reader_->ReadBytes(&header->fec_group, 1)) { + set_detailed_error("Unable to read fec group."); + return false; + } + + return true; +} + +bool QuicFramer::ProcessFragmentData() { + uint8 fragment_count; + if (!reader_->ReadBytes(&fragment_count, 1)) { + set_detailed_error("Unable to read fragment count."); + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + + for (uint8 i = 0; i < fragment_count; ++i) { + uint8 fragment_type; + if (!reader_->ReadBytes(&fragment_type, 1)) { + set_detailed_error("Unable to read fragment type."); + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + switch (fragment_type) { + case STREAM_FRAGMENT: + if (!ProcessStreamFragment()) { + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + break; + case PDU_FRAGMENT: + if (!ProcessPDUFragment()) { + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + break; + case ACK_FRAGMENT: { + QuicAckFragment fragment; + if (!ProcessAckFragment(&fragment)) { + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + break; + } + case RST_STREAM_FRAGMENT: + if (!ProcessRstStreamFragment()) { + return RaiseError(QUIC_INVALID_RST_STREAM_DATA); + } + break; + case CONNECTION_CLOSE_FRAGMENT: + if (!ProcessConnectionCloseFragment()) { + return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA); + } + break; + default: + set_detailed_error("Illegal fragment type."); + DLOG(WARNING) << "Illegal fragment type: " << (int)fragment_type; + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + } + + return true; +} + +bool QuicFramer::ProcessStreamFragment() { + QuicStreamFragment fragment; + if (!reader_->ReadUInt32(&fragment.stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + uint8 fin; + if (!reader_->ReadBytes(&fin, 1)) { + set_detailed_error("Unable to read fin."); + return false; + } + if (fin > 1) { + set_detailed_error("Invalid fin value."); + return false; + } + fragment.fin = (fin == 1); + + if (!reader_->ReadUInt64(&fragment.offset)) { + set_detailed_error("Unable to read offset."); + return false; + } + + if (!reader_->ReadStringPiece16(&fragment.data)) { + set_detailed_error("Unable to read fragment data."); + return false; + } + + visitor_->OnStreamFragment(fragment); + return true; +} + +bool QuicFramer::ProcessPDUFragment() { + return false; +} + +bool QuicFramer::ProcessAckFragment(QuicAckFragment* fragment) { + if (!reader_->ReadUInt48(&fragment->received_info.largest_received)) { + set_detailed_error("Unable to read largest received."); + return false; + } + + if (!reader_->ReadUInt64(&fragment->received_info.time_received)) { + set_detailed_error("Unable to read time received."); + return false; + } + + uint8 num_unacked_packets; + if (!reader_->ReadBytes(&num_unacked_packets, 1)) { + set_detailed_error("Unable to read num unacked packets."); + return false; + } + + for (int i = 0; i < num_unacked_packets; ++i) { + QuicPacketSequenceNumber sequence_number; + if (!reader_->ReadUInt48(&sequence_number)) { + set_detailed_error("Unable to read sequence number in unacked packets."); + return false; + } + fragment->received_info.missing_packets.insert(sequence_number); + } + + if (!reader_->ReadUInt48(&fragment->sent_info.least_unacked)) { + set_detailed_error("Unable to read least unacked."); + return false; + } + + uint8 num_non_retransmiting_packets; + if (!reader_->ReadBytes(&num_non_retransmiting_packets, 1)) { + set_detailed_error("Unable to read num non-retransmitting."); + return false; + } + for (uint8 i = 0; i < num_non_retransmiting_packets; ++i) { + QuicPacketSequenceNumber sequence_number; + if (!reader_->ReadUInt48(&sequence_number)) { + set_detailed_error( + "Unable to read sequence number in non-retransmitting."); + return false; + } + fragment->sent_info.non_retransmiting.insert(sequence_number); + } + + uint8 congestion_info_type; + if (!reader_->ReadBytes(&congestion_info_type, 1)) { + set_detailed_error("Unable to read congestion info type."); + return false; + } + fragment->congestion_info.type = + static_cast<CongestionFeedbackType>(congestion_info_type); + + switch (fragment->congestion_info.type) { + case kNone: + break; + case kInterArrival: { + CongestionFeedbackMessageInterArrival* inter_arrival = + &fragment->congestion_info.inter_arrival; + if (!reader_->ReadUInt16( + &inter_arrival->accumulated_number_of_lost_packets)) { + set_detailed_error( + "Unable to read accumulated number of lost packets."); + return false; + } + if (!reader_->ReadBytes(&inter_arrival->offset_time, 2)) { + set_detailed_error("Unable to read offset time."); + return false; + } + if (!reader_->ReadUInt16(&inter_arrival->delta_time)) { + set_detailed_error("Unable to read delta time."); + return false; + } + break; + } + case kFixRate: { + CongestionFeedbackMessageFixRate* fix_rate = + &fragment->congestion_info.fix_rate; + if (!reader_->ReadUInt32(&fix_rate->bitrate_in_bytes_per_second)) { + set_detailed_error("Unable to read bitrate."); + return false; + } + break; + } + case kTCP: { + CongestionFeedbackMessageTCP* tcp = &fragment->congestion_info.tcp; + if (!reader_->ReadUInt16(&tcp->accumulated_number_of_lost_packets)) { + set_detailed_error( + "Unable to read accumulated number of lost packets."); + return false; + } + if (!reader_->ReadUInt16(&tcp->receive_window)) { + set_detailed_error("Unable to read receive window."); + return false; + } + break; + } + default: + set_detailed_error("Illegal congestion info type."); + DLOG(WARNING) << "Illegal congestion info type: " + << fragment->congestion_info.type; + return RaiseError(QUIC_INVALID_FRAGMENT_DATA); + } + + visitor_->OnAckFragment(*fragment); + return true; +} + +bool QuicFramer::ProcessRstStreamFragment() { + QuicRstStreamFragment fragment; + if (!reader_->ReadUInt32(&fragment.stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + if (!reader_->ReadUInt64(&fragment.offset)) { + set_detailed_error("Unable to read offset in rst fragment."); + return false; + } + + uint32 details; + if (!reader_->ReadUInt32(&details)) { + set_detailed_error("Unable to read rst stream details."); + return false; + } + fragment.details = static_cast<QuicErrorCode>(details); + + visitor_->OnRstStreamFragment(fragment); + return true; +} + +bool QuicFramer::ProcessConnectionCloseFragment() { + QuicConnectionCloseFragment fragment; + + uint32 details; + if (!reader_->ReadUInt32(&details)) { + set_detailed_error("Unable to read connection close details."); + return false; + } + fragment.details = static_cast<QuicErrorCode>(details); + + if (!ProcessAckFragment(&fragment.ack_fragment)) { + DLOG(WARNING) << "Unable to process ack fragment."; + return false; + } + + visitor_->OnConnectionCloseFragment(fragment); + return true; +} + +void QuicFramer::WriteTransmissionTime(QuicTransmissionTime time, + QuicPacket* packet) { + QuicDataWriter::WriteUint64ToBuffer( + time, packet->mutable_data() + kTransmissionTimeOffset); +} + +QuicEncryptedPacket* QuicFramer::EncryptPacket(const QuicPacket& packet) { + scoped_ptr<QuicData> out(encrypter_->Encrypt(packet.AssociatedData(), + packet.Plaintext())); + if (out.get() == NULL) { + RaiseError(QUIC_ENCRYPTION_FAILURE); + return NULL; + } + size_t len = kStartOfEncryptedData + out->length(); + char* buffer = new char[len]; + // TODO(rch): eliminate this buffer copy by passing in a buffer to Encrypt(). + memcpy(buffer, packet.data(), kStartOfEncryptedData); + memcpy(buffer + kStartOfEncryptedData, out->data(), out->length()); + return new QuicEncryptedPacket(buffer, len, true); +} + +size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) { + return encrypter_->GetMaxPlaintextSize(ciphertext_size); +} + +bool QuicFramer::DecryptPayload(const QuicEncryptedPacket& packet) { + StringPiece encrypted; + if (!reader_->ReadStringPiece(&encrypted, reader_->BytesRemaining())) { + return false; + } + DCHECK(decrypter_.get() != NULL); + decrypted_.reset(decrypter_->Decrypt(packet.AssociatedData(), encrypted)); + if (decrypted_.get() == NULL) { + return false; + } + + reader_.reset(new QuicDataReader(decrypted_->data(), decrypted_->length())); + return true; +} + +size_t QuicFramer::ComputeFragmentPayloadLength(const QuicFragment& fragment) { + size_t len = 0; + // We use "magic numbers" here because sizeof(member_) is not necessairly the + // same as sizeof(member_wire_format). + switch (fragment.type) { + case STREAM_FRAGMENT: + len += 4; // stream id + len += 1; // fin + len += 8; // offset + len += 2; // space for the 16 bit length + len += fragment.stream_fragment->data.size(); + break; + case PDU_FRAGMENT: + DLOG(INFO) << "PDU_FRAGMENT not yet supported"; + break; // Need to support this eventually :> + case ACK_FRAGMENT: { + const QuicAckFragment& ack = *fragment.ack_fragment; + len += 6; // largest received packet sequence number + len += 8; // time delta + len += 1; // num missing packets + len += 6 * ack.received_info.missing_packets.size(); + len += 6; // least packet sequence number awaiting an ack + len += 1; // num non retransmitting packets + len += 6 * ack.sent_info.non_retransmiting.size(); + len += 1; // congestion control type + switch (ack.congestion_info.type) { + case kNone: + break; + case kInterArrival: + len += 6; + break; + case kFixRate: + len += 4; + break; + case kTCP: + len += 4; + break; + default: + set_detailed_error("Illegal feedback type."); + DLOG(INFO) << "Illegal feedback type: " << ack.congestion_info.type; + break; + } + break; + } + case RST_STREAM_FRAGMENT: + len += 4; // stream id + len += 8; // offset + len += 4; // details + break; + case CONNECTION_CLOSE_FRAGMENT: + len += 4; // details + len += ComputeFragmentPayloadLength( + QuicFragment(&fragment.connection_close_fragment->ack_fragment)); + break; + default: + set_detailed_error("Illegal fragment type."); + DLOG(INFO) << "Illegal fragment type: " << fragment.type; + break; + } + return len; +} + +bool QuicFramer::AppendStreamFragmentPayload( + const QuicStreamFragment& fragment, + QuicDataWriter* writer) { + if (!writer->WriteUInt32(fragment.stream_id)) { + return false; + } + if (!writer->WriteUInt8(fragment.fin)) { + return false; + } + if (!writer->WriteUInt64(fragment.offset)) { + return false; + } + if (!writer->WriteUInt16(fragment.data.size())) { + return false; + } + if (!writer->WriteBytes(fragment.data.data(), + fragment.data.size())) { + return false; + } + return true; +} + +bool QuicFramer::AppendAckFragmentPayload( + const QuicAckFragment& fragment, + QuicDataWriter* writer) { + if (!writer->WriteUInt48(fragment.received_info.largest_received)) { + return false; + } + if (!writer->WriteUInt64(fragment.received_info.time_received)) { + return false; + } + + size_t num_unacked_packets = fragment.received_info.missing_packets.size(); + if (!writer->WriteBytes(&num_unacked_packets, 1)) { + return false; + } + + hash_set<QuicPacketSequenceNumber>::const_iterator it = + fragment.received_info.missing_packets.begin(); + for (; it != fragment.received_info.missing_packets.end(); ++it) { + if (!writer->WriteUInt48(*it)) { + return false; + } + } + + if (!writer->WriteUInt48(fragment.sent_info.least_unacked)) { + return false; + } + + size_t num_non_retransmiting_packets = + fragment.sent_info.non_retransmiting.size(); + if (!writer->WriteBytes(&num_non_retransmiting_packets, 1)) { + return false; + } + + it = fragment.sent_info.non_retransmiting.begin(); + while (it != fragment.sent_info.non_retransmiting.end()) { + if (!writer->WriteUInt48(*it)) { + return false; + } + ++it; + } + + if (!writer->WriteBytes(&fragment.congestion_info.type, 1)) { + return false; + } + + switch (fragment.congestion_info.type) { + case kNone: + break; + case kInterArrival: { + const CongestionFeedbackMessageInterArrival& inter_arrival = + fragment.congestion_info.inter_arrival; + if (!writer->WriteUInt16( + inter_arrival.accumulated_number_of_lost_packets)) { + return false; + } + if (!writer->WriteBytes(&inter_arrival.offset_time, 2)) { + return false; + } + if (!writer->WriteUInt16(inter_arrival.delta_time)) { + return false; + } + break; + } + case kFixRate: { + const CongestionFeedbackMessageFixRate& fix_rate = + fragment.congestion_info.fix_rate; + if (!writer->WriteUInt32(fix_rate.bitrate_in_bytes_per_second)) { + return false; + } + break; + } + case kTCP: { + const CongestionFeedbackMessageTCP& tcp = fragment.congestion_info.tcp; + if (!writer->WriteUInt16(tcp.accumulated_number_of_lost_packets)) { + return false; + } + if (!writer->WriteUInt16(tcp.receive_window)) { + return false; + } + break; + } + default: + return false; + } + + return true; +} + +bool QuicFramer::AppendRstStreamFragmentPayload( + const QuicRstStreamFragment& fragment, + QuicDataWriter* writer) { + if (!writer->WriteUInt32(fragment.stream_id)) { + return false; + } + if (!writer->WriteUInt64(fragment.offset)) { + return false; + } + + uint32 details = static_cast<uint32>(fragment.details); + if (!writer->WriteUInt32(details)) { + return false; + } + return true; +} + +bool QuicFramer::AppendConnectionCloseFragmentPayload( + const QuicConnectionCloseFragment& fragment, + QuicDataWriter* writer) { + uint32 details = static_cast<uint32>(fragment.details); + if (!writer->WriteUInt32(details)) { + return false; + } + AppendAckFragmentPayload(fragment.ack_fragment, writer); + return true; +} + +bool QuicFramer::RaiseError(QuicErrorCode error) { + DLOG(INFO) << detailed_error_; + set_error(error); + visitor_->OnError(this); + reader_.reset(NULL); + return false; +} + +} // namespace net diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h new file mode 100644 index 0000000..648ae7d5 --- /dev/null +++ b/net/quic/quic_framer.h @@ -0,0 +1,214 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_FRAMER_H_ +#define NET_QUIC_QUIC_FRAMER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_piece.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" + +namespace net { + +class QuicEncrypter; +class QuicDecrypter; +class QuicFramer; +class QuicDataReader; +class QuicDataWriter; + +// Class that receives callbacks from the framer when packets +// are processed. +class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { + public: + virtual ~QuicFramerVisitorInterface() {} + + // Called if an error is detected in the QUIC protocol. + virtual void OnError(QuicFramer* framer) = 0; + + // Called when a new packet has been recieved, before it + // has been validated or processed. + virtual void OnPacket(const IPEndPoint& peer_address) = 0; + + // Called when the header of a packet had been parsed. + // If OnPacketHeader returns false, framing for this packet will cease. + virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0; + + // Called when a data packet is parsed that is part of an FEC group. + // |payload| is the non-encrypted FEC protected payload of the packet. + virtual void OnFecProtectedPayload(base::StringPiece payload) = 0; + + // Called when a StreamFragment has been parsed. + virtual void OnStreamFragment(const QuicStreamFragment& fragment) = 0; + + // Called when a AckFragment has been parsed. + virtual void OnAckFragment(const QuicAckFragment& fragment) = 0; + + // Called when a RstStreamFragment has been parsed. + virtual void OnRstStreamFragment( + const QuicRstStreamFragment& fragment) = 0; + + // Called when a ConnectionCloseFragment has been parsed. + virtual void OnConnectionCloseFragment( + const QuicConnectionCloseFragment& fragment) = 0; + + // Called when FEC data has been parsed. + virtual void OnFecData(const QuicFecData& fec) = 0; + + // Called when a packet has been completely processed. + virtual void OnPacketComplete() = 0; +}; + +class NET_EXPORT_PRIVATE QuicFecBuilderInterface { + public: + virtual ~QuicFecBuilderInterface() {} + + // Called when a data packet is constructed that is part of an FEC group. + // |payload| is the non-encrypted FEC protected payload of the packet. + virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header, + base::StringPiece payload) = 0; +}; + +// Class for parsing and constructing QUIC packets. Has a +// QuicFramerVisitorInterface that is called when packets are parsed. +// Also has a QuicFecBuilder that is called when packets are constructed +// in order to generate FEC data for subsequently building FEC packets. +class NET_EXPORT_PRIVATE QuicFramer { + public: + // Constructs a new framer that will own |decrypter| and |encrypter|. + QuicFramer(QuicDecrypter* decrypter, QuicEncrypter* encrypter); + + virtual ~QuicFramer(); + + // Set callbacks to be called from the framer. A visitor must be set, or + // else the framer will likely crash. It is acceptable for the visitor + // to do nothing. If this is called multiple times, only the last visitor + // will be used. + void set_visitor(QuicFramerVisitorInterface* visitor) { + visitor_ = visitor; + } + + // Set a builder to be called from the framer when building FEC protected + // packets. If this is called multiple times, only the last builder + // will be used. The builder need not be set. + void set_fec_builder(QuicFecBuilderInterface* builder) { + fec_builder_ = builder; + } + + QuicErrorCode error() const { + return error_; + } + + // Pass a UDP packet into the framer for parsing. + // Return true if the packet was processed succesfully. |packet| must be a + // single, complete UDP packet (not a fragment of a packet). This packet + // might be null padded past the end of the payload, which will be correctly + // ignored. + bool ProcessPacket(const IPEndPoint& client_address, + const QuicEncryptedPacket& packet); + + // Pass a data packet that was revived from FEC data into the framer + // for parsing. + // Return true if the packet was processed succesfully. |payload| must be + // the complete DECRYPTED payload of the revived packet. + bool ProcessRevivedPacket(const IPEndPoint& client_address, + const QuicPacketHeader& header, + base::StringPiece payload); + + // Creates a new QuicPacket populated with the fields in |header| and + // |fragments|. Assigns |*packet| to the address of the new object. + // Returns true upon success. + bool ConstructFragementDataPacket(const QuicPacketHeader& header, + const QuicFragments& fragments, + QuicPacket** packet); + + // Creates a new QuicPacket populated with the fields in |header| and + // |fec|. Assigns |*packet| to the address of the new object. + // Returns true upon success. + bool ConstructFecPacket(const QuicPacketHeader& header, + const QuicFecData& fec, + QuicPacket** packet); + + // Increments the retransmission count by one, and updates the authentication + // hash accordingly. + void IncrementRetransmitCount(QuicPacket* packet); + + uint8 GetRetransmitCount(QuicPacket* packet); + + void WriteTransmissionTime(QuicTransmissionTime time, QuicPacket* packet); + + // Returns a new encrypted packet, owned by the caller. + QuicEncryptedPacket* EncryptPacket(const QuicPacket& packet); + + // Returns the maximum length of plaintext that can be encrypted + // to ciphertext no larger than |ciphertext_size|. + size_t GetMaxPlaintextSize(size_t ciphertext_size); + + const std::string& detailed_error() { return detailed_error_; } + + private: + bool WritePacketHeader(const QuicPacketHeader& header, + QuicDataWriter* builder); + + bool ProcessPacketHeader(QuicPacketHeader* header, + const QuicEncryptedPacket& packet); + + bool ProcessFragmentData(); + bool ProcessStreamFragment(); + bool ProcessPDUFragment(); + bool ProcessAckFragment(QuicAckFragment* fragment); + bool ProcessRstStreamFragment(); + bool ProcessConnectionCloseFragment(); + + bool DecryptPayload(const QuicEncryptedPacket& packet); + + // Computes the wire size in bytes of the payload of |fragment|. + size_t ComputeFragmentPayloadLength(const QuicFragment& fragment); + + bool AppendStreamFragmentPayload( + const QuicStreamFragment& fragment, + QuicDataWriter* builder); + bool AppendAckFragmentPayload( + const QuicAckFragment& fragment, + QuicDataWriter* builder); + bool AppendRstStreamFragmentPayload( + const QuicRstStreamFragment& fragment, + QuicDataWriter* builder); + bool AppendConnectionCloseFragmentPayload( + const QuicConnectionCloseFragment& fragment, + QuicDataWriter* builder); + bool RaiseError(QuicErrorCode error); + + void set_error(QuicErrorCode error) { + error_ = error; + } + + void set_detailed_error(const char* error) { + detailed_error_ = error; + } + + std::string detailed_error_; + scoped_ptr<QuicDataReader> reader_; + QuicFramerVisitorInterface* visitor_; + QuicFecBuilderInterface* fec_builder_; + QuicErrorCode error_; + // Buffer containing decrypted payload data during parsing. + scoped_ptr<QuicData> decrypted_; + // Decrypter used to decrypt packets during parsing. + scoped_ptr<QuicDecrypter> decrypter_; + // Encrypter used to encrypt packets via EncryptPacket(). + scoped_ptr<QuicEncrypter> encrypter_; + + DISALLOW_COPY_AND_ASSIGN(QuicFramer); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_FRAMER_H_ diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc new file mode 100644 index 0000000..884f708 --- /dev/null +++ b/net/quic/quic_framer_test.cc @@ -0,0 +1,2157 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include <vector> + +#include "base/hash_tables.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_test_utils.h" + +using base::hash_set; +using base::StringPiece; +using std::string; +using std::vector; + +namespace net { + +namespace test { + +class TestEncrypter : public QuicEncrypter { + public: + virtual ~TestEncrypter() {} + virtual QuicData* Encrypt(StringPiece associated_data, + StringPiece plaintext) { + associated_data_ = associated_data.as_string(); + plaintext_ = plaintext.as_string(); + return new QuicData(plaintext.data(), plaintext.length()); + } + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) { + return ciphertext_size; + } + virtual size_t GetCiphertextSize(size_t plaintext_size) { + return plaintext_size; + } + string associated_data_; + string plaintext_; +}; + +class TestDecrypter : public QuicDecrypter { + public: + virtual ~TestDecrypter() {} + virtual QuicData* Decrypt(StringPiece associated_data, + StringPiece ciphertext) { + associated_data_ = associated_data.as_string(); + ciphertext_ = ciphertext.as_string(); + return new QuicData(ciphertext.data(), ciphertext.length()); + } + string associated_data_; + string ciphertext_; +}; + +// The offset of congestion info in our tests, given the size of our usual ack +// fragment. This does NOT work for all packets. +const int kCongestionInfoOffset = kPacketHeaderSize + 54; + +class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { + public: + TestQuicVisitor() + : error_count_(0), + packet_count_(0), + fragment_count_(0), + fec_count_(0), + complete_packets_(0), + accept_packet_(true) { + } + + ~TestQuicVisitor() { + STLDeleteElements(&stream_fragments_); + STLDeleteElements(&ack_fragments_); + STLDeleteElements(&fec_data_); + } + + virtual void OnError(QuicFramer* f) { + DLOG(INFO) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error()) + << " (" << f->error() << ")"; + error_count_++; + } + + virtual void OnPacket(const IPEndPoint& client_address) { + address_ = client_address; + } + + virtual bool OnPacketHeader(const QuicPacketHeader& header) { + packet_count_++; + header_.reset(new QuicPacketHeader(header)); + return accept_packet_; + } + + virtual void OnStreamFragment(const QuicStreamFragment& fragment) { + fragment_count_++; + stream_fragments_.push_back(new QuicStreamFragment(fragment)); + } + + virtual void OnFecProtectedPayload(StringPiece payload) { + fec_protected_payload_ = payload.as_string(); + } + + virtual void OnAckFragment(const QuicAckFragment& fragment) { + fragment_count_++; + ack_fragments_.push_back(new QuicAckFragment(fragment)); + } + + virtual void OnFecData(const QuicFecData& fec) { + fec_count_++; + fec_data_.push_back(new QuicFecData(fec)); + } + + virtual void OnPacketComplete() { + complete_packets_++; + } + + virtual void OnRstStreamFragment(const QuicRstStreamFragment& fragment) { + rst_stream_fragment_ = fragment; + } + + virtual void OnConnectionCloseFragment( + const QuicConnectionCloseFragment& fragment) { + connection_close_fragment_ = fragment; + } + + // Counters from the visitor_ callbacks. + int error_count_; + int packet_count_; + int fragment_count_; + int fec_count_; + int complete_packets_; + bool accept_packet_; + + IPEndPoint address_; + scoped_ptr<QuicPacketHeader> header_; + vector<QuicStreamFragment*> stream_fragments_; + vector<QuicAckFragment*> ack_fragments_; + vector<QuicFecData*> fec_data_; + string fec_protected_payload_; + QuicRstStreamFragment rst_stream_fragment_; + QuicConnectionCloseFragment connection_close_fragment_; +}; + +class QuicFramerTest : public ::testing::Test { + public: + QuicFramerTest() + : encrypter_(new test::TestEncrypter()), + decrypter_(new test::TestDecrypter()), + framer_(decrypter_, encrypter_) { + framer_.set_visitor(&visitor_); + } + + bool CheckEncryption(StringPiece packet) { + StringPiece associated_data( + packet.substr(kStartOfHashData, + kStartOfEncryptedData - kStartOfHashData)); + if (associated_data != encrypter_->associated_data_) { + LOG(ERROR) << "Encrypted incorrect associated data. expected " + << associated_data << " actual: " + << encrypter_->associated_data_; + return false; + } + StringPiece plaintext(packet.substr(kStartOfEncryptedData)); + if (plaintext != encrypter_->plaintext_) { + LOG(ERROR) << "Encrypted incorrect plaintext data. expected " + << plaintext << " actual: " + << encrypter_->plaintext_; + return false; + } + return true; + } + + bool CheckDecryption(StringPiece packet) { + StringPiece associated_data( + packet.substr(kStartOfHashData, + kStartOfEncryptedData - kStartOfHashData)); + if (associated_data != decrypter_->associated_data_) { + LOG(ERROR) << "Decrypted incorrect associated data. expected " + << associated_data << " actual: " + << decrypter_->associated_data_; + return false; + } + StringPiece plaintext(packet.substr(kStartOfEncryptedData)); + if (plaintext != decrypter_->ciphertext_) { + LOG(ERROR) << "Decrypted incorrect chipertext data. expected " + << plaintext << " actual: " + << decrypter_->ciphertext_; + return false; + } + return true; + } + + char* AsChars(unsigned char* data) { + return reinterpret_cast<char*>(data); + } + + test::TestEncrypter* encrypter_; + test::TestDecrypter* decrypter_; + QuicFramer framer_; + test::TestQuicVisitor visitor_; + IPEndPoint address_; +}; + +TEST_F(QuicFramerTest, EmptyPacket) { + char packet[] = { 0x00 }; + EXPECT_FALSE(framer_.ProcessPacket(address_, + QuicEncryptedPacket(packet, 0, false))); + EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); +} + +TEST_F(QuicFramerTest, LargePacket) { + unsigned char packet[kMaxPacketSize + 1] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + // fragment count + 0x01, + }; + + memset(packet + kPacketHeaderSize, 0, kMaxPacketSize - kPacketHeaderSize + 1); + + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + ASSERT_TRUE(visitor_.header_.get()); + // Make sure we've parsed the packet header, so we can send an error. + EXPECT_EQ(0xFEDCBA9876543210, visitor_.header_->guid); + // Make sure the correct error is propogated. + EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error()); +} + +TEST_F(QuicFramerTest, PacketHeader) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + }; + + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_EQ(QUIC_INVALID_FRAGMENT_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(0xFEDCBA9876543210, visitor_.header_->guid); + EXPECT_EQ(0x1, visitor_.header_->retransmission_count); + EXPECT_EQ(static_cast<uint64>(0x123456789ABC), + visitor_.header_->packet_sequence_number); + EXPECT_EQ(static_cast<uint64>(0xF0E1D2C3B4A59687), + visitor_.header_->transmission_time); + EXPECT_EQ(0x00, visitor_.header_->flags); + EXPECT_EQ(0x00, visitor_.header_->fec_group); + + // Now test framing boundaries + for (int i = 0; i < 25; ++i) { + string expected_error; + if (i < 8) { + expected_error = "Unable to read GUID."; + } else if (i < 14) { + expected_error = "Unable to read sequence number."; + } else if (i < 15) { + expected_error = "Unable to read retransmission count."; + } else if (i < 23) { + expected_error = "Unable to read transmission time."; + } else if (i < 24) { + expected_error = "Unable to read flags."; + } else if (i < 25) { + expected_error = "Unable to read fec group."; + } + + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); + } +} + +TEST_F(QuicFramerTest, StreamFragment) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (stream fragment) + 0x00, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + ASSERT_EQ(address_, visitor_.address_); + + ASSERT_EQ(1u, visitor_.stream_fragments_.size()); + EXPECT_EQ(0u, visitor_.ack_fragments_.size()); + EXPECT_EQ(static_cast<uint64>(0x01020304), + visitor_.stream_fragments_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_fragments_[0]->fin); + EXPECT_EQ(0xBA98FEDC32107654, visitor_.stream_fragments_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_fragments_[0]->data); + + // Now test framing boundaries + for (size_t i = kPacketHeaderSize; i < kPacketHeaderSize + 29; ++i) { + string expected_error; + if (i < kPacketHeaderSize + 1) { + expected_error = "Unable to read fragment count."; + } else if (i < kPacketHeaderSize + 2) { + expected_error = "Unable to read fragment type."; + } else if (i < kPacketHeaderSize + 6) { + expected_error = "Unable to read stream_id."; + } else if (i < kPacketHeaderSize + 7) { + expected_error = "Unable to read fin."; + } else if (i < kPacketHeaderSize + 15) { + expected_error = "Unable to read offset."; + } else if (i < kPacketHeaderSize + 29) { + expected_error = "Unable to read fragment data."; + } + + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_FRAGMENT_DATA, framer_.error()); + } +} + +TEST_F(QuicFramerTest, RejectPacket) { + visitor_.accept_packet_ = false; + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (stream fragment) + 0x00, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + ASSERT_EQ(address_, visitor_.address_); + + ASSERT_EQ(0u, visitor_.stream_fragments_.size()); + EXPECT_EQ(0u, visitor_.ack_fragments_.size()); +} + +TEST_F(QuicFramerTest, RevivedStreamFragment) { + unsigned char payload[] = { + // fragment count + 0x01, + // fragment type (stream fragment) + 0x00, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + // Do not encrypt the payload because the revived payload is post-encryption. + EXPECT_TRUE(framer_.ProcessRevivedPacket(address_, + header, + StringPiece(AsChars(payload), + arraysize(payload)))); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_EQ(address_, visitor_.address_); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(0xFEDCBA9876543210, visitor_.header_->guid); + EXPECT_EQ(0x1, visitor_.header_->retransmission_count); + EXPECT_EQ(static_cast<uint64>(0x123456789ABC), + visitor_.header_->packet_sequence_number); + EXPECT_EQ(static_cast<uint64>(0xF0E1D2C3B4A59687), + visitor_.header_->transmission_time); + EXPECT_EQ(0x00, visitor_.header_->flags); + EXPECT_EQ(0x00, visitor_.header_->fec_group); + + + ASSERT_EQ(1u, visitor_.stream_fragments_.size()); + EXPECT_EQ(0u, visitor_.ack_fragments_.size()); + EXPECT_EQ(static_cast<uint64>(0x01020304), + visitor_.stream_fragments_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_fragments_[0]->fin); + EXPECT_EQ(0xBA98FEDC32107654, visitor_.stream_fragments_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_fragments_[0]->data); +} + +TEST_F(QuicFramerTest, StreamFragmentInFecGroup) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x12, 0x34, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x02, + + // fragment count + 0x01, + // fragment type (stream fragment) + 0x00, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(2, visitor_.header_->fec_group); + EXPECT_EQ(string(AsChars(packet) + kStartOfFecProtectedData, + arraysize(packet) - kStartOfFecProtectedData), + visitor_.fec_protected_payload_); + ASSERT_EQ(address_, visitor_.address_); + + ASSERT_EQ(1u, visitor_.stream_fragments_.size()); + EXPECT_EQ(0u, visitor_.ack_fragments_.size()); + EXPECT_EQ(static_cast<uint64>(0x01020304), + visitor_.stream_fragments_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_fragments_[0]->fin); + EXPECT_EQ(0xBA98FEDC32107654, visitor_.stream_fragments_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_fragments_[0]->data); +} + +TEST_F(QuicFramerTest, AckFragment) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // congestion feedback type (none) + 0x00, + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + EXPECT_EQ(0u, visitor_.stream_fragments_.size()); + ASSERT_EQ(1u, visitor_.ack_fragments_.size()); + const QuicAckFragment& fragment = *visitor_.ack_fragments_[0]; + EXPECT_EQ(static_cast<uint64>(0x0123456789ABC), + fragment.received_info.largest_received); + EXPECT_EQ(0xF0E1D2C3B4A59687, fragment.received_info.time_received); + + const hash_set<QuicPacketSequenceNumber>* sequence_nums = + &fragment.received_info.missing_packets; + ASSERT_EQ(2u, sequence_nums->size()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABB) != sequence_nums->end()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABA) != sequence_nums->end()); + EXPECT_EQ(static_cast<uint64>(0x0123456789AA0), + fragment.sent_info.least_unacked); + ASSERT_EQ(3u, fragment.sent_info.non_retransmiting.size()); + const hash_set<QuicPacketSequenceNumber>* non_retrans = + &fragment.sent_info.non_retransmiting; + EXPECT_TRUE(non_retrans->find(0x0123456789AB0) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAF) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAE) != non_retrans->end()); + ASSERT_EQ(kNone, fragment.congestion_info.type); + + // Now test framing boundaries + for (size_t i = kPacketHeaderSize; i < kPacketHeaderSize + 55; ++i) { + string expected_error; + if (i < kPacketHeaderSize + 1) { + expected_error = "Unable to read fragment count."; + } else if (i < kPacketHeaderSize + 2) { + expected_error = "Unable to read fragment type."; + } else if (i < kPacketHeaderSize + 8) { + expected_error = "Unable to read largest received."; + } else if (i < kPacketHeaderSize + 16) { + expected_error = "Unable to read time received."; + } else if (i < kPacketHeaderSize + 17) { + expected_error = "Unable to read num unacked packets."; + } else if (i < kPacketHeaderSize + 29) { + expected_error = "Unable to read sequence number in unacked packets."; + } else if (i < kPacketHeaderSize + 35) { + expected_error = "Unable to read least unacked."; + } else if (i < kPacketHeaderSize + 36) { + expected_error = "Unable to read num non-retransmitting."; + } else if (i < kPacketHeaderSize + 54) { + expected_error = "Unable to read sequence number in non-retransmitting."; + } else if (i < kPacketHeaderSize + 55) { + expected_error = "Unable to read congestion info type."; + } + + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_FRAGMENT_DATA, framer_.error()); + } +} + +TEST_F(QuicFramerTest, AckFragmentTCP) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // congestion feedback type (tcp) + 0x01, + // ack_fragment.congestion_info.tcp.accumulated_number_of_lost_packets + 0x01, 0x02, + // ack_fragment.congestion_info.tcp.receive_window + 0x03, 0x04, + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + EXPECT_EQ(0u, visitor_.stream_fragments_.size()); + ASSERT_EQ(1u, visitor_.ack_fragments_.size()); + const QuicAckFragment& fragment = *visitor_.ack_fragments_[0]; + EXPECT_EQ(static_cast<uint64>(0x0123456789ABC), + fragment.received_info.largest_received); + EXPECT_EQ(0xF0E1D2C3B4A59687, fragment.received_info.time_received); + + const hash_set<QuicPacketSequenceNumber>* sequence_nums = + &fragment.received_info.missing_packets; + ASSERT_EQ(2u, sequence_nums->size()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABB) != sequence_nums->end()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABA) != sequence_nums->end()); + EXPECT_EQ(static_cast<uint64>(0x0123456789AA0), + fragment.sent_info.least_unacked); + ASSERT_EQ(3u, fragment.sent_info.non_retransmiting.size()); + const hash_set<QuicPacketSequenceNumber>* non_retrans = + &fragment.sent_info.non_retransmiting; + EXPECT_TRUE(non_retrans->find(0x0123456789AB0) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAF) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAE) != non_retrans->end()); + ASSERT_EQ(kTCP, fragment.congestion_info.type); + EXPECT_EQ(0x0201, + fragment.congestion_info.tcp.accumulated_number_of_lost_packets); + EXPECT_EQ(0x0403, fragment.congestion_info.tcp.receive_window); + + // Now test framing boundaries + for (size_t i = kCongestionInfoOffset; i < kCongestionInfoOffset + 5; ++i) { + string expected_error; + if (i < kCongestionInfoOffset + 1) { + expected_error = "Unable to read congestion info type."; + } else if (i < kCongestionInfoOffset + 3) { + expected_error = "Unable to read accumulated number of lost packets."; + } else if (i < kCongestionInfoOffset + 5) { + expected_error = "Unable to read receive window."; + } + + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_FRAGMENT_DATA, framer_.error()); + } +} + +TEST_F(QuicFramerTest, AckFragmentInterArrival) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // congestion feedback type (inter arrival) + 0x02, + // accumulated_number_of_lost_packets + 0x02, 0x03, + // offset_time + 0x04, 0x05, + // delta_time + 0x06, 0x07, + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + EXPECT_EQ(0u, visitor_.stream_fragments_.size()); + ASSERT_EQ(1u, visitor_.ack_fragments_.size()); + const QuicAckFragment& fragment = *visitor_.ack_fragments_[0]; + EXPECT_EQ(static_cast<uint64>(0x0123456789ABC), + fragment.received_info.largest_received); + EXPECT_EQ(0xF0E1D2C3B4A59687, fragment.received_info.time_received); + + const hash_set<QuicPacketSequenceNumber>* sequence_nums = + &fragment.received_info.missing_packets; + ASSERT_EQ(2u, sequence_nums->size()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABB) != sequence_nums->end()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABA) != sequence_nums->end()); + EXPECT_EQ(static_cast<uint64>(0x0123456789AA0), + fragment.sent_info.least_unacked); + ASSERT_EQ(3u, fragment.sent_info.non_retransmiting.size()); + const hash_set<QuicPacketSequenceNumber>* non_retrans = + &fragment.sent_info.non_retransmiting; + EXPECT_TRUE(non_retrans->find(0x0123456789AB0) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAF) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAE) != non_retrans->end()); + ASSERT_EQ(kInterArrival, fragment.congestion_info.type); + EXPECT_EQ(0x0302, fragment.congestion_info.inter_arrival. + accumulated_number_of_lost_packets); + EXPECT_EQ(0x0504, + fragment.congestion_info.inter_arrival.offset_time); + EXPECT_EQ(0x0706, + fragment.congestion_info.inter_arrival.delta_time); + + // Now test framing boundaries + for (size_t i = kCongestionInfoOffset; i < kCongestionInfoOffset + 5; ++i) { + string expected_error; + if (i < kCongestionInfoOffset + 1) { + expected_error = "Unable to read congestion info type."; + } else if (i < kCongestionInfoOffset + 3) { + expected_error = "Unable to read accumulated number of lost packets."; + } else if (i < kCongestionInfoOffset + 5) { + expected_error = "Unable to read offset time."; + } else if (i < kCongestionInfoOffset + 7) { + expected_error = "Unable to read delta time."; + } + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_FRAGMENT_DATA, framer_.error()); + } +} + +TEST_F(QuicFramerTest, AckFragmentFixRate) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // congestion feedback type (fix rate) + 0x03, + // bitrate_in_bytes_per_second; + 0x01, 0x02, 0x03, 0x04, + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + EXPECT_EQ(0u, visitor_.stream_fragments_.size()); + ASSERT_EQ(1u, visitor_.ack_fragments_.size()); + const QuicAckFragment& fragment = *visitor_.ack_fragments_[0]; + EXPECT_EQ(static_cast<uint64>(0x0123456789ABC), + fragment.received_info.largest_received); + EXPECT_EQ(0xF0E1D2C3B4A59687, fragment.received_info.time_received); + + const hash_set<QuicPacketSequenceNumber>* sequence_nums = + &fragment.received_info.missing_packets; + ASSERT_EQ(2u, sequence_nums->size()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABB) != sequence_nums->end()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABA) != sequence_nums->end()); + EXPECT_EQ(static_cast<uint64>(0x0123456789AA0), + fragment.sent_info.least_unacked); + ASSERT_EQ(3u, fragment.sent_info.non_retransmiting.size()); + const hash_set<QuicPacketSequenceNumber>* non_retrans = + &fragment.sent_info.non_retransmiting; + EXPECT_TRUE(non_retrans->find(0x0123456789AB0) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAF) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAE) != non_retrans->end()); + ASSERT_EQ(kFixRate, fragment.congestion_info.type); + EXPECT_EQ(static_cast<uint32>(0x04030201), + fragment.congestion_info.fix_rate.bitrate_in_bytes_per_second); + + // Now test framing boundaries + for (size_t i = kCongestionInfoOffset; i < kCongestionInfoOffset + 5; ++i) { + string expected_error; + if (i < kCongestionInfoOffset + 1) { + expected_error = "Unable to read congestion info type."; + } else if (i < kCongestionInfoOffset + 5) { + expected_error = "Unable to read bitrate."; + } + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_FRAGMENT_DATA, framer_.error()); + } +} + + +TEST_F(QuicFramerTest, AckFragmentInvalidFeedback) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // congestion feedback type (invalid) + 0x04, + }; + + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_INVALID_FRAGMENT_DATA, framer_.error()); +} + +TEST_F(QuicFramerTest, RstStreamFragment) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (rst stream fragment) + 0x03, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // details + 0x08, 0x07, 0x06, 0x05, + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + ASSERT_EQ(address_, visitor_.address_); + + EXPECT_EQ(static_cast<uint64>(0x01020304), + visitor_.rst_stream_fragment_.stream_id); + EXPECT_EQ(0x05060708, visitor_.rst_stream_fragment_.details); + EXPECT_EQ(0xBA98FEDC32107654, visitor_.rst_stream_fragment_.offset); + + // Now test framing boundaries + for (size_t i = kPacketHeaderSize + 3; i < kPacketHeaderSize + 18; ++i) { + string expected_error; + if (i < kPacketHeaderSize + 6) { + expected_error = "Unable to read stream_id."; + } else if (i < kPacketHeaderSize + 14) { + expected_error = "Unable to read offset in rst fragment."; + } else if (i < kPacketHeaderSize + 18) { + expected_error = "Unable to read rst stream details."; + } + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_RST_STREAM_DATA, framer_.error()); + } +} + +TEST_F(QuicFramerTest, ConnectionCloseFragment) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + + // fragment count + 0x01, + // fragment type (connection close fragment) + 0x04, + // details + 0x08, 0x07, 0x06, 0x05, + + // Ack fragment. + + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // congestion feedback type (inter arrival) + 0x02, + // accumulated_number_of_lost_packets + 0x02, 0x03, + // offset_time + 0x04, 0x05, + // delta_time + 0x06, 0x07, + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + EXPECT_EQ(0u, visitor_.stream_fragments_.size()); + + EXPECT_EQ(0x05060708, visitor_.connection_close_fragment_.details); + + ASSERT_EQ(1u, visitor_.ack_fragments_.size()); + const QuicAckFragment& fragment = *visitor_.ack_fragments_[0]; + EXPECT_EQ(static_cast<uint64>(0x0123456789ABC), + fragment.received_info.largest_received); + EXPECT_EQ(0xF0E1D2C3B4A59687, fragment.received_info.time_received); + + const hash_set<QuicPacketSequenceNumber>* sequence_nums = + &fragment.received_info.missing_packets; + ASSERT_EQ(2u, sequence_nums->size()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABB) != sequence_nums->end()); + EXPECT_TRUE(sequence_nums->find(0x0123456789ABA) != sequence_nums->end()); + EXPECT_EQ(static_cast<uint64>(0x0123456789AA0), + fragment.sent_info.least_unacked); + ASSERT_EQ(3u, fragment.sent_info.non_retransmiting.size()); + const hash_set<QuicPacketSequenceNumber>* non_retrans = + &fragment.sent_info.non_retransmiting; + EXPECT_TRUE(non_retrans->find(0x0123456789AB0) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAF) != non_retrans->end()); + EXPECT_TRUE(non_retrans->find(0x0123456789AAE) != non_retrans->end()); + ASSERT_EQ(kInterArrival, fragment.congestion_info.type); + EXPECT_EQ(0x0302, fragment.congestion_info.inter_arrival. + accumulated_number_of_lost_packets); + EXPECT_EQ(0x0504, + fragment.congestion_info.inter_arrival.offset_time); + EXPECT_EQ(0x0706, + fragment.congestion_info.inter_arrival.delta_time); + + // Now test framing boundaries + for (size_t i = kPacketHeaderSize + 3; i < kPacketHeaderSize + 6; ++i) { + string expected_error; + if (i < kPacketHeaderSize + 6) { + expected_error = "Unable to read connection close details."; + } + EXPECT_FALSE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), i, false))); + EXPECT_EQ(expected_error, framer_.detailed_error()); + EXPECT_EQ(QUIC_INVALID_CONNECTION_CLOSE_DATA, framer_.error()); + } +} + +TEST_F(QuicFramerTest, FecPacket) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags (FEC) + 0x01, + // fec group + 0x01, + + // first protected packet + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + EXPECT_TRUE(framer_.ProcessPacket( + address_, QuicEncryptedPacket(AsChars(packet), + arraysize(packet), false))); + + EXPECT_TRUE(CheckDecryption(StringPiece(AsChars(packet), arraysize(packet)))); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + + EXPECT_EQ(0u, visitor_.stream_fragments_.size()); + EXPECT_EQ(0u, visitor_.ack_fragments_.size()); + ASSERT_EQ(1, visitor_.fec_count_); + const QuicFecData& fec_data = *visitor_.fec_data_[0]; + EXPECT_EQ(static_cast<uint64>(0x0123456789ABB), + fec_data.first_protected_packet_sequence_number); + EXPECT_EQ("abcdefghijklmnop", fec_data.redundancy); +} + +TEST_F(QuicFramerTest, ConstructStreamFragmentPacket) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicStreamFragment stream_fragment; + stream_fragment.stream_id = 0x01020304; + stream_fragment.fin = true; + stream_fragment.offset = 0xBA98FEDC32107654; + stream_fragment.data = "hello world!"; + + QuicFragment fragment; + fragment.type = STREAM_FRAGMENT; + fragment.stream_fragment = &stream_fragment; + + QuicFragments fragments; + fragments.push_back(fragment); + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (stream fragment) + 0x00, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicPacket* data; + ASSERT_TRUE(framer_.ConstructFragementDataPacket(header, fragments, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, ConstructAckFragmentPacket) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicAckFragment ack_fragment; + ack_fragment.received_info.largest_received = 0x0123456789ABC; + ack_fragment.received_info.time_received = 0xF0E1D2C3B4A59687; + ack_fragment.received_info.missing_packets.insert(0x0123456789ABB); + ack_fragment.received_info.missing_packets.insert(0x0123456789ABA); + ack_fragment.sent_info.least_unacked = 0x0123456789AA0; + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AB0); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAF); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAE); + ack_fragment.congestion_info.type = kNone; + + QuicFragment fragment; + fragment.type = ACK_FRAGMENT; + fragment.ack_fragment = &ack_fragment; + + QuicFragments fragments; + fragments.push_back(fragment); + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, +#if defined(OS_WIN) + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, +#if defined(OS_WIN) + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // congestion feedback type (none) + 0x00, + }; + + QuicPacket* data; + EXPECT_TRUE(framer_.ConstructFragementDataPacket(header, fragments, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, ConstructAckFragmentPacketTCP) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicAckFragment ack_fragment; + ack_fragment.received_info.largest_received = 0x0123456789ABC; + ack_fragment.received_info.time_received = 0xF0E1D2C3B4A59687; + ack_fragment.received_info.missing_packets.insert(0x0123456789ABB); + ack_fragment.received_info.missing_packets.insert(0x0123456789ABA); + ack_fragment.sent_info.least_unacked = 0x0123456789AA0; + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AB0); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAF); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAE); + ack_fragment.congestion_info.type = kTCP; + ack_fragment.congestion_info.tcp.accumulated_number_of_lost_packets = 0x0201; + ack_fragment.congestion_info.tcp.receive_window = 0x0403; + + QuicFragment fragment; + fragment.type = ACK_FRAGMENT; + fragment.ack_fragment = &ack_fragment; + + QuicFragments fragments; + fragments.push_back(fragment); + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, +#if defined(OS_WIN) + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, +#if defined(OS_WIN) + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // congestion feedback type (tcp) + 0x01, + // ack_fragment.congestion_info.tcp.accumulated_number_of_lost_packets + 0x01, 0x02, + // ack_fragment.congestion_info.tcp.receive_window + 0x03, 0x04, + }; + + QuicPacket* data; + EXPECT_TRUE(framer_.ConstructFragementDataPacket(header, fragments, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, ConstructAckFragmentPacketInterArrival) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicAckFragment ack_fragment; + ack_fragment.received_info.largest_received = 0x0123456789ABC; + ack_fragment.received_info.time_received = 0xF0E1D2C3B4A59687; + ack_fragment.received_info.missing_packets.insert(0x0123456789ABB); + ack_fragment.received_info.missing_packets.insert(0x0123456789ABA); + ack_fragment.sent_info.least_unacked = 0x0123456789AA0; + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AB0); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAF); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAE); + ack_fragment.congestion_info.type = kInterArrival; + ack_fragment.congestion_info.inter_arrival.accumulated_number_of_lost_packets + = 0x0302; + ack_fragment.congestion_info.inter_arrival.offset_time = 0x0504; + ack_fragment.congestion_info.inter_arrival.delta_time = 0x0706; + + QuicFragment fragment; + fragment.type = ACK_FRAGMENT; + fragment.ack_fragment = &ack_fragment; + + QuicFragments fragments; + fragments.push_back(fragment); + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, +#if defined(OS_WIN) + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, +#if defined(OS_WIN) + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // congestion feedback type (inter arrival) + 0x02, + // accumulated_number_of_lost_packets + 0x02, 0x03, + // offset_time + 0x04, 0x05, + // delta_time + 0x06, 0x07, + }; + + QuicPacket* data; + EXPECT_TRUE(framer_.ConstructFragementDataPacket(header, fragments, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, ConstructAckFragmentPacketFixRate) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicAckFragment ack_fragment; + ack_fragment.received_info.largest_received = 0x0123456789ABC; + ack_fragment.received_info.time_received = 0xF0E1D2C3B4A59687; + ack_fragment.received_info.missing_packets.insert(0x0123456789ABB); + ack_fragment.received_info.missing_packets.insert(0x0123456789ABA); + ack_fragment.sent_info.least_unacked = 0x0123456789AA0; + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AB0); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAF); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAE); + ack_fragment.congestion_info.type = kFixRate; + ack_fragment.congestion_info.fix_rate.bitrate_in_bytes_per_second + = 0x04030201; + + QuicFragment fragment; + fragment.type = ACK_FRAGMENT; + fragment.ack_fragment = &ack_fragment; + + QuicFragments fragments; + fragments.push_back(fragment); + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (ack fragment) + 0x02, + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, +#if defined(OS_WIN) + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, +#if defined(OS_WIN) + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // congestion feedback type (fix rate) + 0x03, + // bitrate_in_bytes_per_second; + 0x01, 0x02, 0x03, 0x04, + }; + + QuicPacket* data; + EXPECT_TRUE(framer_.ConstructFragementDataPacket(header, fragments, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, ConstructAckFragmentPacketInvalidFeedback) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicAckFragment ack_fragment; + ack_fragment.received_info.largest_received = 0x0123456789ABC; + ack_fragment.received_info.time_received = 0xF0E1D2C3B4A59687; + ack_fragment.received_info.missing_packets.insert(0x0123456789ABB); + ack_fragment.received_info.missing_packets.insert(0x0123456789ABA); + ack_fragment.sent_info.least_unacked = 0x0123456789AA0; + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AB0); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAF); + ack_fragment.sent_info.non_retransmiting.insert(0x0123456789AAE); + ack_fragment.congestion_info.type = + static_cast<CongestionFeedbackType>(kFixRate + 1); + + QuicFragment fragment; + fragment.type = ACK_FRAGMENT; + fragment.ack_fragment = &ack_fragment; + + QuicFragments fragments; + fragments.push_back(fragment); + + QuicPacket* data; + EXPECT_FALSE(framer_.ConstructFragementDataPacket(header, fragments, &data)); +} + +TEST_F(QuicFramerTest, ConstructRstFragmentPacket) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicRstStreamFragment rst_fragment; + rst_fragment.stream_id = 0x01020304; + rst_fragment.details = static_cast<QuicErrorCode>(0x05060708); + rst_fragment.offset = 0xBA98FEDC32107654; + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (rst stream fragment) + 0x03, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // details + 0x08, 0x07, 0x06, 0x05, + }; + + QuicFragment fragment(&rst_fragment); + + QuicFragments fragments; + fragments.push_back(fragment); + + QuicPacket* data; + EXPECT_TRUE(framer_.ConstructFragementDataPacket(header, fragments, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, ConstructCloseFragmentPacket) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicConnectionCloseFragment close_fragment; + close_fragment.details = static_cast<QuicErrorCode>(0x05060708); + + QuicAckFragment* ack_fragment = &close_fragment.ack_fragment; + ack_fragment->received_info.largest_received = 0x0123456789ABC; + ack_fragment->received_info.time_received = 0xF0E1D2C3B4A59687; + ack_fragment->received_info.missing_packets.insert(0x0123456789ABB); + ack_fragment->received_info.missing_packets.insert(0x0123456789ABA); + ack_fragment->sent_info.least_unacked = 0x0123456789AA0; + ack_fragment->sent_info.non_retransmiting.insert(0x0123456789AB0); + ack_fragment->sent_info.non_retransmiting.insert(0x0123456789AAF); + ack_fragment->sent_info.non_retransmiting.insert(0x0123456789AAE); + ack_fragment->congestion_info.type = kInterArrival; + ack_fragment->congestion_info.inter_arrival.accumulated_number_of_lost_packets + = 0x0302; + ack_fragment->congestion_info.inter_arrival.offset_time = 0x0504; + ack_fragment->congestion_info.inter_arrival.delta_time = 0x0706; + + QuicFragment fragment(&close_fragment); + + QuicFragments fragments; + fragments.push_back(fragment); + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x00, + // fec group + 0x00, + + // fragment count + 0x01, + // fragment type (connection close fragment) + 0x04, + // details + 0x08, 0x07, 0x06, 0x05, + + // Ack fragment. + + // largest received packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // time delta + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // num_unacked_packets + 0x02, +#if defined(OS_WIN) + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // unacked packet sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // unacked packet sequence number + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num non retransmitting packets + 0x03, +#if defined(OS_WIN) + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#else + // non retransmitting packet sequence number + 0xAE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xAF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // non retransmitting packet sequence number + 0xB0, 0x9A, 0x78, 0x56, + 0x34, 0x12, +#endif + // congestion feedback type (inter arrival) + 0x02, + // accumulated_number_of_lost_packets + 0x02, 0x03, + // offset_time + 0x04, 0x05, + // delta_time + 0x06, 0x07, + }; + + QuicPacket* data; + EXPECT_TRUE(framer_.ConstructFragementDataPacket(header, fragments, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, ConstructFecPacket) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 0x01; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_FEC; + header.fec_group = 1; + + QuicFecData fec_data; + fec_data.fec_group = 1; + fec_data.first_protected_packet_sequence_number = 0x123456789ABB; + fec_data.redundancy = "abcdefghijklmnop"; + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x01, + // fec group + 0x01, + // first protected packet + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + QuicPacket* data; + EXPECT_TRUE(framer_.ConstructFecPacket(header, fec_data, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST_F(QuicFramerTest, IncrementRetransmitCount) { + QuicPacketHeader header; + header.guid = 0xFEDCBA9876543210; + header.retransmission_count = 1; + header.packet_sequence_number = 0x123456789ABC; + header.transmission_time = 0xF0E1D2C3B4A59687; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicStreamFragment stream_fragment; + stream_fragment.stream_id = 0x01020304; + stream_fragment.fin = true; + stream_fragment.offset = 0xBA98FEDC32107654; + stream_fragment.data = "hello world!"; + + QuicFragment fragment; + fragment.type = STREAM_FRAGMENT; + fragment.stream_fragment = &stream_fragment; + + QuicFragments fragments; + fragments.push_back(fragment); + + QuicPacket *original; + ASSERT_TRUE(framer_.ConstructFragementDataPacket( + header, fragments, &original)); + EXPECT_EQ(header.retransmission_count, framer_.GetRetransmitCount(original)); + + header.retransmission_count = 2; + QuicPacket *retransmitted; + ASSERT_TRUE(framer_.ConstructFragementDataPacket( + header, fragments, &retransmitted)); + + framer_.IncrementRetransmitCount(original); + EXPECT_EQ(header.retransmission_count, framer_.GetRetransmitCount(original)); + + test::CompareCharArraysWithHexError( + "constructed packet", original->data(), original->length(), + retransmitted->data(), retransmitted->length()); + delete original; + delete retransmitted; +} + +TEST_F(QuicFramerTest, EncryptPacket) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet id + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // retransmission count + 0x01, + // transmission time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0xF0, + // flags + 0x01, + // fec group + 0x01, + // first protected packet + 0xBB, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // redundancy + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + }; + + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + QuicPacket(AsChars(packet), arraysize(packet), false))); + ASSERT_TRUE(encrypted.get() != NULL); + EXPECT_TRUE(CheckEncryption(StringPiece(AsChars(packet), arraysize(packet)))); +} + +} // namespace test + +} // namespace net diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc new file mode 100644 index 0000000..78c0241d --- /dev/null +++ b/net/quic/quic_protocol.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_protocol.h" + +using base::StringPiece; + +namespace net { + +QuicStreamFragment::QuicStreamFragment() {} + +QuicStreamFragment::QuicStreamFragment(QuicStreamId stream_id, + bool fin, + uint64 offset, + StringPiece data) + : stream_id(stream_id), + fin(fin), + offset(offset), + data(data) { +} + +ReceivedPacketInfo::ReceivedPacketInfo() {} + +ReceivedPacketInfo::~ReceivedPacketInfo() {} + +SentPacketInfo::SentPacketInfo() {} + +SentPacketInfo::~SentPacketInfo() {} + +QuicFecData::QuicFecData() {} + +QuicData::~QuicData() { + if (owns_buffer_) { + delete [] const_cast<char*>(buffer_); + } +} + +} // namespace net diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h new file mode 100644 index 0000000..d096fff --- /dev/null +++ b/net/quic/quic_protocol.h @@ -0,0 +1,422 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_PROTOCOL_H_ +#define NET_QUIC_QUIC_PROTOCOL_H_ + +#include <limits> +#include <utility> +#include <vector> + +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/logging.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/uint128.h" + +namespace net { + +class QuicPacket; + +typedef uint64 QuicGuid; +typedef uint32 QuicStreamId; +typedef uint64 QuicStreamOffset; +typedef uint64 QuicPacketSequenceNumber; +typedef uint64 QuicTransmissionTime; +typedef uint8 QuicFecGroupNumber; + +// TODO(rch): Consider Quic specific names for these constants. +const size_t kMaxPacketSize = 1200; // Maximum size in bytes of a QUIC packet. + +// Maximum number of open streams per connection. +const size_t kDefaultMaxStreamsPerConnection = 100; + +// Size in bytes of the packet header common across all packets. +const size_t kPacketHeaderSize = 25; +// Index of the first byte in a QUIC packet of FEC protected data. +const size_t kStartOfFecProtectedData = kPacketHeaderSize; +// Index of the first byte in a QUIC packet of encrypted data. +const size_t kStartOfEncryptedData = kPacketHeaderSize - 1; +// Index of the first byte in a QUIC packet which is hashed. +const size_t kStartOfHashData = 0; +// Index into the retransmission offset in the header. +// (After GUID and sequence number.) +const int kRetransmissionOffset = 14; +// Index into the transmission time offset in the header. +const int kTransmissionTimeOffset = 15; + +// Size in bytes of all stream fragment fields. +const size_t kMinStreamFragmentLength = 15; + +// Limit on the delta between stream IDs. +const QuicStreamId kMaxStreamIdDelta = 100; + +// Reserved ID for the crypto stream. +// TODO(rch): ensure that this is not usable by any other streams. +const QuicStreamId kCryptoStreamId = 1; + +typedef std::pair<QuicPacketSequenceNumber, QuicPacket*> PacketPair; + +const int64 kDefaultTimeout = 600000000; // 10 minutes + +enum QuicFragmentType { + STREAM_FRAGMENT = 0, + PDU_FRAGMENT, + ACK_FRAGMENT, + RST_STREAM_FRAGMENT, + CONNECTION_CLOSE_FRAGMENT, + NUM_FRAGMENT_TYPES +}; + +enum QuicPacketFlags { + PACKET_FLAGS_NONE = 0, + PACKET_FLAGS_FEC = 1, // Payload is FEC as opposed to fragments. + + PACKET_FLAGS_MAX = PACKET_FLAGS_FEC +}; + +enum QuicErrorCode { + // Stream errors. + QUIC_NO_ERROR = 0, + + // There were data fragments after the a fin or reset. + QUIC_STREAM_DATA_AFTER_TERMINATION, + // There was some server error which halted stream processing. + QUIC_SERVER_ERROR_PROCESSING_STREAM, + // We got two fin or reset offsets which did not match. + QUIC_MULTIPLE_TERMINATION_OFFSETS, + // We got bad payload and can not respond to it at the protocol level. + QUIC_BAD_APPLICATION_PAYLOAD, + + // Connection errors. + + // Control frame is malformed. + QUIC_INVALID_PACKET_HEADER, + // Fragment data is malformed. + QUIC_INVALID_FRAGMENT_DATA, + // FEC data is malformed. + QUIC_INVALID_FEC_DATA, + // Stream rst data is malformed + QUIC_INVALID_RST_STREAM_DATA, + // Connection close data is malformed. + QUIC_INVALID_CONNECTION_CLOSE_DATA, + // Ack data is malformed. + QUIC_INVALID_ACK_DATA, + // There was an error decrypting. + QUIC_DECRYPTION_FAILURE, + // There was an error encrypting. + QUIC_ENCRYPTION_FAILURE, + // The packet exceeded kMaxPacketSize. + QUIC_PACKET_TOO_LARGE, + // Data was sent for a stream which did not exist. + QUIC_PACKET_FOR_NONEXISTENT_STREAM, + // The client is going away (browser close, etc.) + QUIC_CLIENT_GOING_AWAY, + // The server is going away (restart etc.) + QUIC_SERVER_GOING_AWAY, + // A stream ID was invalid. + QUIC_INVALID_STREAM_ID, + // Too many streams already open. + QUIC_TOO_MANY_OPEN_STREAMS, + + // We hit our prenegotiated (or default) timeout + QUIC_CONNECTION_TIMED_OUT, + + // Crypto errors. + + // Handshake message contained out of order tags. + QUIC_CRYPTO_TAGS_OUT_OF_ORDER, + // Handshake message contained too many entires. + QUIC_CRYPTO_TOO_MANY_ENTRIES, + // Handshake message contained an invalid value length. + QUIC_CRYPTO_INVALID_VALUE_LENGTH, + // A crypto message was received after the handshake was complete. + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, + // A crypto message was receieved with an illegal message tag. + QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + +}; + +struct NET_EXPORT_PRIVATE QuicPacketHeader { + // Includes the ConnectionHeader and CongestionMonitoredHeader + // from the design docs, as well as some elements of DecryptedData. + QuicGuid guid; + QuicPacketSequenceNumber packet_sequence_number; + uint8 retransmission_count; + QuicTransmissionTime transmission_time; + QuicPacketFlags flags; + QuicFecGroupNumber fec_group; +}; + +struct NET_EXPORT_PRIVATE QuicStreamFragment { + QuicStreamFragment(); + QuicStreamFragment(QuicStreamId stream_id, + bool fin, + uint64 offset, + base::StringPiece data); + + QuicStreamId stream_id; + bool fin; + uint64 offset; + base::StringPiece data; +}; + +struct NET_EXPORT_PRIVATE ReceivedPacketInfo { + ReceivedPacketInfo(); + ~ReceivedPacketInfo(); + // The highest packet sequence number we've received from the peer. + QuicPacketSequenceNumber largest_received; + // The time at which we received the above packet. + QuicTransmissionTime time_received; + // The set of packets which we're expecting and have not received. + // This includes any packets between the lowest and largest_received + // which we have neither seen nor been informed are non-retransmitting. + base::hash_set<QuicPacketSequenceNumber> missing_packets; +}; + +struct NET_EXPORT_PRIVATE SentPacketInfo { + SentPacketInfo(); + ~SentPacketInfo(); + // The lowest packet we've sent which is unacked, and we expect an ack for. + QuicPacketSequenceNumber least_unacked; + // The set of packets between least_unacked and the last packet we have sent + // which we will not resend. + base::hash_set<QuicPacketSequenceNumber> non_retransmiting; +}; + +// Defines for all types of congestion feedback that will be negotiated in QUIC, +// kTCP MUST be supported by all QUIC implementations to guarentee 100% +// compatibility. +enum CongestionFeedbackType { + kNone = 0, // No feedback provided + kTCP, // Used to mimic TCP. + kInterArrival, // Use additional inter arrival information. + kFixRate, // Provided for testing. +}; + +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP { + uint16 accumulated_number_of_lost_packets; + uint16 receive_window; // Number of bytes >> 4. +}; + +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival { + uint16 accumulated_number_of_lost_packets; + int16 offset_time; + uint16 delta_time; // delta time is described below. +}; + +/* + * Description of delta time. + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |D|S| offset_time | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Where: + * D is the time domain. If set time domain is in milliseconds, else in + * microseconds. + * S is the sign bit. + * offset_time is the time offset where the relative packet size is equal to + * 0. + */ + +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageFixRate { + uint32 bitrate_in_bytes_per_second; +}; + +struct NET_EXPORT_PRIVATE CongestionInfo { + CongestionFeedbackType type; + union { + CongestionFeedbackMessageTCP tcp; + CongestionFeedbackMessageInterArrival inter_arrival; + CongestionFeedbackMessageFixRate fix_rate; + }; +}; + +struct NET_EXPORT_PRIVATE QuicAckFragment { + QuicAckFragment() {} + QuicAckFragment(QuicPacketSequenceNumber largest_received, + QuicTransmissionTime time_received, + QuicPacketSequenceNumber least_unacked) { + received_info.largest_received = largest_received; + received_info.time_received = time_received; + sent_info.least_unacked = least_unacked; + congestion_info.type = kNone; + } + + SentPacketInfo sent_info; + ReceivedPacketInfo received_info; + CongestionInfo congestion_info; + + friend std::ostream& operator<<(std::ostream& os, const QuicAckFragment& s) { + os << "largest_received: " << s.received_info.largest_received + << " time: " << s.received_info.time_received + << " missing: "; + for (base::hash_set<QuicPacketSequenceNumber>::const_iterator it = + s.received_info.missing_packets.begin(); + it != s.received_info.missing_packets.end(); ++it) { + os << *it << " "; + } + + os << " least_waiting: " << s.sent_info.least_unacked + << " no_retransmit: "; + for (base::hash_set<QuicPacketSequenceNumber>::const_iterator it = + s.sent_info.non_retransmiting.begin(); + it != s.sent_info.non_retransmiting.end(); ++it) { + os << *it << " "; + } + os << "\n"; + return os; + } +}; + +struct NET_EXPORT_PRIVATE QuicRstStreamFragment { + QuicRstStreamFragment() {} + QuicRstStreamFragment(QuicStreamId stream_id, uint64 offset, + QuicErrorCode details) + : stream_id(stream_id), offset(offset), details(details) { + DCHECK_LE(details, std::numeric_limits<uint8>::max()); + } + + QuicStreamId stream_id; + uint64 offset; + QuicErrorCode details; +}; + +struct NET_EXPORT_PRIVATE QuicConnectionCloseFragment { + QuicErrorCode details; + QuicAckFragment ack_fragment; +}; + +struct NET_EXPORT_PRIVATE QuicFragment { + QuicFragment() {} + explicit QuicFragment(QuicStreamFragment* stream_fragment) + : type(STREAM_FRAGMENT), stream_fragment(stream_fragment) { + } + explicit QuicFragment(QuicAckFragment* fragment) + : type(ACK_FRAGMENT), ack_fragment(fragment) { + } + explicit QuicFragment(QuicRstStreamFragment* fragment) + : type(RST_STREAM_FRAGMENT), + rst_stream_fragment(fragment) { + } + explicit QuicFragment(QuicConnectionCloseFragment* fragment) + : type(CONNECTION_CLOSE_FRAGMENT), + connection_close_fragment(fragment) { + } + + QuicFragmentType type; + union { + QuicStreamFragment* stream_fragment; + QuicAckFragment* ack_fragment; + QuicRstStreamFragment* rst_stream_fragment; + QuicConnectionCloseFragment* connection_close_fragment; + }; +}; + +typedef std::vector<QuicFragment> QuicFragments; + +struct NET_EXPORT_PRIVATE QuicFecData { + QuicFecData(); + QuicFecGroupNumber fec_group; + QuicPacketSequenceNumber first_protected_packet_sequence_number; + // The last protected packet's sequence number will be one + // less than the sequence number of the FEC packet. + base::StringPiece redundancy; + bool operator==(const QuicFecData& other) const { + if (fec_group != other.fec_group) { + return false; + } + if (first_protected_packet_sequence_number != + other.first_protected_packet_sequence_number) { + return false; + } + if (redundancy != other.redundancy) { + return false; + } + return true; + } +}; + +struct NET_EXPORT_PRIVATE QuicPacketData { + std::string data; +}; + +class NET_EXPORT_PRIVATE QuicData { + public: + QuicData(const char* buffer, size_t length) + : buffer_(buffer), + length_(length), + owns_buffer_(false) { } + + QuicData(char* buffer, size_t length, bool owns_buffer) + : buffer_(buffer), + length_(length), + owns_buffer_(owns_buffer) { } + + virtual ~QuicData(); + + base::StringPiece AsStringPiece() const { + return base::StringPiece(data(), length()); + } + + const char* data() const { return buffer_; } + size_t length() const { return length_; } + + private: + const char* buffer_; + size_t length_; + bool owns_buffer_; + + DISALLOW_COPY_AND_ASSIGN(QuicData); +}; + +class NET_EXPORT_PRIVATE QuicPacket : public QuicData { + public: + QuicPacket(char* buffer, size_t length, bool owns_buffer) + : QuicData(buffer, length, owns_buffer), + buffer_(buffer) { } + + base::StringPiece FecProtectedData() const { + return base::StringPiece(data() + kStartOfFecProtectedData, + length() - kStartOfFecProtectedData); + } + + base::StringPiece AssociatedData() const { + return base::StringPiece(data() + kStartOfHashData, kStartOfEncryptedData); + } + + base::StringPiece Plaintext() const { + return base::StringPiece(data() + kStartOfEncryptedData, + length() - kStartOfEncryptedData); + } + char* mutable_data() { return buffer_; } + + private: + char* buffer_; + + // TODO(rch): DISALLOW_COPY_AND_ASSIGN +}; + +class NET_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData { + public: + QuicEncryptedPacket(const char* buffer, size_t length) + : QuicData(buffer, length) { } + + QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer) + : QuicData(buffer, length, owns_buffer) { } + + base::StringPiece AssociatedData() const { + return base::StringPiece(data() + kStartOfHashData, kStartOfEncryptedData); + } + + // TODO(rch): DISALLOW_COPY_AND_ASSIGN +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_PROTOCOL_H_ diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc new file mode 100644 index 0000000..8865738 --- /dev/null +++ b/net/quic/quic_utils.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_utils.h" + +#include "base/logging.h" +#include "base/port.h" + +namespace net { + +// static +int QuicUtils::StreamFragmentPacketOverhead(int num_fragments) { + // TODO(jar): Use sizeof(some name). + return kPacketHeaderSize + + 1 + // fragment count + (1 + // 8 bit type + 2 + // 16 bit length + kMinStreamFragmentLength) * num_fragments; +} + +// TODO(jar): put into an anonymous namespage. +// The following two constants are defined as part of the hash algorithm. +// 309485009821345068724781371 +static uint128 kPrime(16777216, 315); +// 14406626329776981559649562966706236762 +static uint128 kOffset(GG_ULONGLONG(780984778246553632), + GG_ULONGLONG(4400696054689967450)); + +uint128 QuicUtils::FNV1a_128_Hash(const char* data, int len) { + const uint8* octets = reinterpret_cast<const uint8*>(data); + + uint128 hash = kOffset; + + for (int i = 0; i < len; ++i) { + hash = hash ^ uint128(0, octets[i]); + hash = hash * kPrime; + } + + return hash; +} + +#define RETURN_STRING_LITERAL(x) \ +case x: \ +return #x; + +const char* QuicUtils::ErrorToString(QuicErrorCode error) { + switch (error) { + RETURN_STRING_LITERAL(QUIC_NO_ERROR); + RETURN_STRING_LITERAL(QUIC_STREAM_DATA_AFTER_TERMINATION); + RETURN_STRING_LITERAL(QUIC_SERVER_ERROR_PROCESSING_STREAM); + RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS); + RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD); + RETURN_STRING_LITERAL(QUIC_INVALID_PACKET_HEADER); + RETURN_STRING_LITERAL(QUIC_INVALID_FRAGMENT_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA); + RETURN_STRING_LITERAL(QUIC_DECRYPTION_FAILURE); + RETURN_STRING_LITERAL(QUIC_ENCRYPTION_FAILURE); + RETURN_STRING_LITERAL(QUIC_PACKET_TOO_LARGE); + RETURN_STRING_LITERAL(QUIC_PACKET_FOR_NONEXISTENT_STREAM); + RETURN_STRING_LITERAL(QUIC_CLIENT_GOING_AWAY); + RETURN_STRING_LITERAL(QUIC_SERVER_GOING_AWAY); + RETURN_STRING_LITERAL(QUIC_CRYPTO_TAGS_OUT_OF_ORDER); + RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_ENTRIES); + RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH) + RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); + RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); + RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS); + RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT); + // Intentionally have no default case, so we'll break the build + // if we add errors and don't put them here. + } + return ""; +} + +} // namespace net diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h new file mode 100644 index 0000000..9a851ad --- /dev/null +++ b/net/quic/quic_utils.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Some helpers for quic + +#ifndef NET_QUIC_QUIC_UTILS_H_ +#define NET_QUIC_QUIC_UTILS_H_ + +#include <string> + +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/uint128.h" + +class SocketAddress; + +namespace gfe2 { + class BalsaHeaders; +} + +namespace net { + +class NET_EXPORT_PRIVATE QuicUtils { + public: + // The overhead the quic framing will add for a packet with num_fragments + // fragments. + static int StreamFragmentPacketOverhead(int num_fragments); + + // returns the 128 bit FNV1a hash of the data. See + // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param + static uint128 FNV1a_128_Hash(const char* data, int len); + + // Returns the name of the quic error code as a char* + static const char* ErrorToString(QuicErrorCode error); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_UTILS_H_ diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc new file mode 100644 index 0000000..a4c71bd --- /dev/null +++ b/net/quic/test_tools/quic_test_utils.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/test_tools/quic_test_utils.h" +#include "net/quic/crypto/crypto_framer.h" + +using std::max; +using std::min; +using std::string; + +namespace net { +namespace test { + +MockFramerVisitor::MockFramerVisitor() { + // By default, we want to accept packets. + ON_CALL(*this, OnPacketHeader(testing::_)) + .WillByDefault(testing::Return(true)); +} + +MockFramerVisitor::~MockFramerVisitor() {} + +bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) { + return true; +} + +namespace { + +string HexDumpWithMarks(const char* data, int length, + const bool* marks, int mark_length) { + static const char kHexChars[] = "0123456789abcdef"; + static const int kColumns = 4; + + const int kSizeLimit = 1024; + if (length > kSizeLimit || mark_length > kSizeLimit) { + LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes."; + length = min(length, kSizeLimit); + mark_length = min(mark_length, kSizeLimit); + } + + string hex; + for (const char* row = data; length > 0; + row += kColumns, length -= kColumns) { + for (const char *p = row; p < row + 4; ++p) { + if (p < row + length) { + const bool mark = + (marks && (p - data) < mark_length && marks[p - data]); + hex += mark ? '*' : ' '; + hex += kHexChars[(*p & 0xf0) >> 4]; + hex += kHexChars[*p & 0x0f]; + hex += mark ? '*' : ' '; + } else { + hex += " "; + } + } + hex = hex + " "; + + for (const char *p = row; p < row + 4 && p < row + length; ++p) + hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.'; + + hex = hex + '\n'; + } + return hex; +} + +} // namespace + +void CompareCharArraysWithHexError( + const string& description, + const char* actual, + const int actual_len, + const char* expected, + const int expected_len) { + const int min_len = min(actual_len, expected_len); + const int max_len = max(actual_len, expected_len); + scoped_array<bool> marks(new bool[max_len]); + bool identical = (actual_len == expected_len); + for (int i = 0; i < min_len; ++i) { + if (actual[i] != expected[i]) { + marks[i] = true; + identical = false; + } else { + marks[i] = false; + } + } + for (int i = min_len; i < max_len; ++i) { + marks[i] = true; + } + if (identical) return; + ADD_FAILURE() + << "Description:\n" + << description + << "\n\nExpected:\n" + << HexDumpWithMarks(expected, expected_len, marks.get(), max_len) + << "\nActual:\n" + << HexDumpWithMarks(actual, actual_len, marks.get(), max_len); +} + +void CompareQuicDataWithHexError( + const string& description, + QuicData* actual, + QuicData* expected) { + CompareCharArraysWithHexError( + description, + actual->data(), actual->length(), + expected->data(), expected->length()); +} + +QuicPacket* ConstructHandshakePacket(QuicGuid guid, CryptoTag tag) { + CryptoHandshakeMessage message; + message.tag = tag; + CryptoFramer crypto_framer; + QuicData* data; + crypto_framer.ConstructHandshakeMessage(message, &data); + QuicFramer quic_framer(QuicDecrypter::Create(kNULL), + QuicEncrypter::Create(kNULL)); + + QuicPacketHeader header; + header.guid = guid; + header.retransmission_count = 0; + header.packet_sequence_number = 1; + header.transmission_time = 0; + header.flags = PACKET_FLAGS_NONE; + header.fec_group = 0; + + QuicStreamFragment stream_fragment(kCryptoStreamId, false, 0, + data->AsStringPiece()); + + QuicFragment fragment(&stream_fragment); + QuicFragments fragments; + fragments.push_back(fragment); + QuicPacket* packet; + quic_framer.ConstructFragementDataPacket(header, fragments, &packet); + delete data; + return packet; +} + +} // namespace test + +} // namespace net diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h new file mode 100644 index 0000000..14e3fd2 --- /dev/null +++ b/net/quic/test_tools/quic_test_utils.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Common utilities for Quic tests + +#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ + +#include "net/quic/quic_framer.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace net { + +namespace test { + +void CompareCharArraysWithHexError(const std::string& description, + const char* actual, + const int actual_len, + const char* expected, + const int expected_len); + +void CompareQuicDataWithHexError(const std::string& description, + QuicData* actual, + QuicData* expected); + +// Constructs a basic crypto handshake message +QuicPacket* ConstructHandshakePacket(QuicGuid guid, CryptoTag tag); + +class MockFramerVisitor : public QuicFramerVisitorInterface { + public: + MockFramerVisitor(); + ~MockFramerVisitor(); + + MOCK_METHOD1(OnError, void(QuicFramer* framer)); + MOCK_METHOD1(OnPacket, void(const IPEndPoint& client_address)); + MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header)); + MOCK_METHOD1(OnFecProtectedPayload, void(base::StringPiece payload)); + MOCK_METHOD1(OnStreamFragment, void(const QuicStreamFragment& fragment)); + MOCK_METHOD1(OnAckFragment, void(const QuicAckFragment& fragment)); + MOCK_METHOD1(OnFecData, void(const QuicFecData& fec)); + MOCK_METHOD1(OnRstStreamFragment, + void(const QuicRstStreamFragment& fragment)); + MOCK_METHOD1(OnConnectionCloseFragment, + void(const QuicConnectionCloseFragment& fragment)); + MOCK_METHOD0(OnPacketComplete, void()); +}; + +class NoOpFramerVisitor : public QuicFramerVisitorInterface { + public: + virtual void OnError(QuicFramer* framer) OVERRIDE {} + virtual void OnPacket(const IPEndPoint& client_address) OVERRIDE {} + virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; + virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {} + virtual void OnStreamFragment(const QuicStreamFragment& fragment) OVERRIDE {} + virtual void OnAckFragment(const QuicAckFragment& fragment) OVERRIDE {} + virtual void OnFecData(const QuicFecData& fec) OVERRIDE {} + virtual void OnRstStreamFragment( + const QuicRstStreamFragment& fragment) OVERRIDE {} + virtual void OnConnectionCloseFragment( + const QuicConnectionCloseFragment& fragment) OVERRIDE {} + virtual void OnPacketComplete() OVERRIDE {} +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ diff --git a/net/quic/uint128.h b/net/quic/uint128.h new file mode 100644 index 0000000..c368242 --- /dev/null +++ b/net/quic/uint128.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_UINT128_H_ +#define NET_QUIC_UINT128_H_ + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace net { + +struct uint128 { + uint128() {} + uint128(uint64 hi, uint64 lo) : hi(hi), lo(lo) {} + uint64 hi; + uint64 lo; +}; + +inline uint128 operator ^(const uint128& lhs, const uint128& rhs) { + return uint128(lhs.hi ^ rhs.hi, lhs.lo ^ rhs.lo); +} + +inline uint128 operator *(const uint128& lhs, const uint128& rhs) { + // TODO(rch): correctly implement uint128 multiplication. + return lhs ^ rhs; +} + +inline bool operator ==(const uint128& lhs, const uint128& rhs) { + return lhs.hi == rhs.hi && lhs.lo == rhs.lo; +} + +inline bool operator !=(const uint128& lhs, const uint128& rhs) { + return !(lhs == rhs); +} + +} // namespace net + +#endif // NET_QUIC_UINT128_H_ |