summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 01:46:18 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 01:46:18 +0000
commitaa765b335635a85f490747ebb05a7afda7b63eb2 (patch)
treec786bf1c25b22f988aad34ad0c7a8cfaaea49bb3 /net
parent5f75c63e1d251f05f00ef357f8ec5382d2181a6e (diff)
downloadchromium_src-aa765b335635a85f490747ebb05a7afda7b63eb2.zip
chromium_src-aa765b335635a85f490747ebb05a7afda7b63eb2.tar.gz
chromium_src-aa765b335635a85f490747ebb05a7afda7b63eb2.tar.bz2
Trying again to add QuicFramer and friends.
Includes a second attempt to fix the constants. 162468 - Reverted 162462 162462 - Relanding w/ fix 162263 - Reverted 162259 162259 - Add QuicFramer and friends. Review URL: https://chromiumcodereview.appspot.com/11187044 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162606 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/net.gyp26
-rw-r--r--net/quic/crypto/crypto_framer.cc156
-rw-r--r--net/quic/crypto/crypto_framer.h106
-rw-r--r--net/quic/crypto/crypto_framer_test.cc316
-rw-r--r--net/quic/crypto/crypto_protocol.cc12
-rw-r--r--net/quic/crypto/crypto_protocol.h45
-rw-r--r--net/quic/crypto/null_decrypter.cc35
-rw-r--r--net/quic/crypto/null_decrypter.h28
-rw-r--r--net/quic/crypto/null_decrypter_test.cc77
-rw-r--r--net/quic/crypto/null_encrypter.cc37
-rw-r--r--net/quic/crypto/null_encrypter.h30
-rw-r--r--net/quic/crypto/null_encrypter_test.cc56
-rw-r--r--net/quic/crypto/quic_decrypter.cc21
-rw-r--r--net/quic/crypto/quic_decrypter.h28
-rw-r--r--net/quic/crypto/quic_encrypter.cc21
-rw-r--r--net/quic/crypto/quic_encrypter.h38
-rw-r--r--net/quic/quic_data_reader.cc173
-rw-r--r--net/quic/quic_data_reader.h125
-rw-r--r--net/quic/quic_data_writer.cc69
-rw-r--r--net/quic/quic_data_writer.h94
-rw-r--r--net/quic/quic_framer.cc784
-rw-r--r--net/quic/quic_framer.h214
-rw-r--r--net/quic/quic_framer_test.cc2157
-rw-r--r--net/quic/quic_protocol.cc39
-rw-r--r--net/quic/quic_protocol.h422
-rw-r--r--net/quic/quic_utils.cc80
-rw-r--r--net/quic/quic_utils.h40
-rw-r--r--net/quic/test_tools/quic_test_utils.cc140
-rw-r--r--net/quic/test_tools/quic_test_utils.h69
-rw-r--r--net/quic/uint128.h39
30 files changed, 5477 insertions, 0 deletions
diff --git a/net/net.gyp b/net/net.gyp
index 0a9270c..10c9c0a 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -628,6 +628,26 @@
'proxy/sync_host_resolver.h',
'proxy/sync_host_resolver_bridge.cc',
'proxy/sync_host_resolver_bridge.h',
+ 'quic/crypto/crypto_framer.cc',
+ 'quic/crypto/crypto_framer.h',
+ 'quic/crypto/crypto_protocol.cc',
+ 'quic/crypto/crypto_protocol.h',
+ 'quic/crypto/null_decrypter.cc',
+ 'quic/crypto/null_encrypter.cc',
+ 'quic/crypto/quic_decrypter.h',
+ 'quic/crypto/quic_decrypter.cc',
+ 'quic/crypto/quic_encrypter.h',
+ 'quic/crypto/quic_encrypter.cc',
+ 'quic/quic_data_reader.cc',
+ 'quic/quic_data_reader.h',
+ 'quic/quic_data_writer.cc',
+ 'quic/quic_data_writer.h',
+ 'quic/quic_framer.cc',
+ 'quic/quic_framer.h',
+ 'quic/quic_protocol.cc',
+ 'quic/quic_protocol.h',
+ 'quic/quic_utils.cc',
+ 'quic/quic_utils.h',
'socket/buffered_write_stream_socket.cc',
'socket/buffered_write_stream_socket.h',
'socket/client_socket_factory.cc',
@@ -1350,6 +1370,12 @@
'proxy/proxy_server_unittest.cc',
'proxy/proxy_service_unittest.cc',
'proxy/sync_host_resolver_bridge_unittest.cc',
+ 'quic/crypto/crypto_framer_test.cc',
+ 'quic/crypto/null_decrypter_test.cc',
+ 'quic/crypto/null_encrypter_test.cc',
+ 'quic/test_tools/quic_test_utils.cc',
+ 'quic/test_tools/quic_test_utils.h',
+ 'quic/quic_framer_test.cc',
'socket/buffered_write_stream_socket_unittest.cc',
'socket/client_socket_pool_base_unittest.cc',
'socket/deterministic_socket_data_unittest.cc',
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_