diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-27 18:43:06 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-27 18:43:06 +0000 |
commit | d27ea08b3c0d4b0d16e898755dd299381481bba6 (patch) | |
tree | dfc7ffcfe4178365c94ee84225858b84d6713ab8 /net | |
parent | 18713e5dd919fd5536b3cd8b8fbe1e358743c76c (diff) | |
download | chromium_src-d27ea08b3c0d4b0d16e898755dd299381481bba6.zip chromium_src-d27ea08b3c0d4b0d16e898755dd299381481bba6.tar.gz chromium_src-d27ea08b3c0d4b0d16e898755dd299381481bba6.tar.bz2 |
Add support to SpdyFramer for the CREDENTIAL frame.
BUG=106103
TEST=None
Review URL: http://codereview.chromium.org/8893003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115835 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/spdy/buffered_spdy_framer_unittest.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_frame_builder.cc | 15 | ||||
-rw-r--r-- | net/spdy/spdy_frame_builder.h | 5 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 114 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 42 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 225 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 50 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 6 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 6 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.cc | 6 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.h | 2 |
11 files changed, 469 insertions, 7 deletions
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc index 56c5679..746e750 100644 --- a/net/spdy/buffered_spdy_framer_unittest.cc +++ b/net/spdy/buffered_spdy_framer_unittest.cc @@ -56,6 +56,11 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface { LOG(FATAL) << "Unexpected OnStreamFrameData call."; } + bool OnCredentialFrameData(const char*, size_t) { + LOG(FATAL) << "Unexpected OnCredentialFrameData call."; + return false; + } + void OnDataFrameHeader(const SpdyDataFrame* frame) { LOG(FATAL) << "Unexpected OnDataFrameHeader call."; } diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc index c6625b2..92dc3cd 100644 --- a/net/spdy/spdy_frame_builder.cc +++ b/net/spdy/spdy_frame_builder.cc @@ -86,7 +86,7 @@ bool SpdyFrameBuilder::ReadString(void** iter, std::string* result) const { } bool SpdyFrameBuilder::ReadBytes(void** iter, const char** data, - uint16 length) const { + uint32 length) const { DCHECK(iter); DCHECK(data); @@ -111,6 +111,19 @@ bool SpdyFrameBuilder::ReadData(void** iter, const char** data, return ReadBytes(iter, data, *length); } +bool SpdyFrameBuilder::ReadReadLen32PrefixedData(void** iter, + const char** data, + uint32* length) const { + DCHECK(iter); + DCHECK(data); + DCHECK(length); + + if (!ReadUInt32(iter, length)) + return false; + + return ReadBytes(iter, data, *length); +} + char* SpdyFrameBuilder::BeginWriteData(uint16 length) { DCHECK_EQ(variable_buffer_offset_, 0U) << "There can only be one variable buffer in a SpdyFrameBuilder"; diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h index b53adbc..e828a88 100644 --- a/net/spdy/spdy_frame_builder.h +++ b/net/spdy/spdy_frame_builder.h @@ -61,8 +61,11 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder { bool ReadUInt16(void** iter, uint16* result) const; bool ReadUInt32(void** iter, uint32* result) const; bool ReadString(void** iter, std::string* result) const; - bool ReadBytes(void** iter, const char** data, uint16 length) const; + bool ReadBytes(void** iter, const char** data, uint32 length) const; bool ReadData(void** iter, const char** data, uint16* length) const; + bool ReadReadLen32PrefixedData(void** iter, + const char** data, + uint32* length) const; // Methods for adding to the payload. These values are appended to the end // of the SpdyFrameBuilder payload. When reading values, you must read them diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 8b41a48..f0d4efdf 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -20,8 +20,13 @@ #include "third_party/zlib/zlib.h" #endif +using std::vector; + namespace spdy { +SpdyCredential::SpdyCredential() : slot(0) { } +SpdyCredential::~SpdyCredential() { } + // Compute the id of our dictionary so that we know we're using the // right one when asked for it. uLong CalculateDictionaryId() { @@ -187,6 +192,8 @@ const char* SpdyFramer::StateToString(int state) { return "SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK"; case SPDY_CONTROL_FRAME_HEADER_BLOCK: return "SPDY_CONTROL_FRAME_HEADER_BLOCK"; + case SPDY_CREDENTIAL_FRAME_PAYLOAD: + return "SPDY_CREDENTIAL_FRAME_PAYLOAD"; } return "UNKNOWN_STATE"; } @@ -260,6 +267,8 @@ const char* SpdyFramer::ControlTypeToString(SpdyControlType type) { return "HEADERS"; case WINDOW_UPDATE: return "WINDOW_UPDATE"; + case CREDENTIAL: + return "CREDENTIAL"; case NUM_CONTROL_FRAME_TYPES: break; } @@ -323,6 +332,13 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { continue; } + case SPDY_CREDENTIAL_FRAME_PAYLOAD: { + size_t bytes_read = ProcessCredentialFramePayload(data, len); + len -= bytes_read; + data += bytes_read; + continue; + } + case SPDY_CONTROL_FRAME_PAYLOAD: { size_t bytes_read = ProcessControlFramePayload(data, len); len -= bytes_read; @@ -467,6 +483,11 @@ void SpdyFramer::ProcessControlFrameHeader() { SpdyPingControlFrame::size() - SpdyControlFrame::kHeaderSize) set_error(SPDY_INVALID_CONTROL_FRAME); break; + case CREDENTIAL: + if (current_control_frame.length() < + SpdyCredentialControlFrame::size() - SpdyControlFrame::kHeaderSize) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; default: LOG(WARNING) << "Valid " << display_protocol_ << " control frame with unhandled type: " @@ -490,6 +511,12 @@ void SpdyFramer::ProcessControlFrameHeader() { return; } + if (current_control_frame.type() == CREDENTIAL) { + visitor_->OnControl(¤t_control_frame); + CHANGE_STATE(SPDY_CREDENTIAL_FRAME_PAYLOAD); + return; + } + int32 frame_size_without_header_block; switch (current_control_frame.type()) { case SYN_STREAM: @@ -686,6 +713,18 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { return original_len - len; } +size_t SpdyFramer::ProcessCredentialFramePayload(const char* data, size_t len) { + bool processed_succesfully = visitor_->OnCredentialFrameData(data, len); + remaining_control_payload_ -= len; + remaining_data_ -= len; + if (!processed_succesfully) { + set_error(SPDY_CREDENTIAL_FRAME_CORRUPT); + } else if (remaining_control_payload_ == 0) { + visitor_->OnCredentialFrameData(NULL, 0); + } + return len; +} + size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { size_t original_len = len; @@ -871,6 +910,39 @@ bool SpdyFramer::ParseSettings(const SpdySettingsControlFrame* frame, return true; } +/* static */ +bool SpdyFramer::ParseCredentialData(const char* data, size_t len, + SpdyCredential* credential) { + DCHECK(credential); + + void* iter = NULL; + SpdyFrameBuilder parser(data, len); + if (!parser.ReadUInt16(&iter, &credential->slot)) + return false; + + uint16 origin_len; + const char* origin_data; + if (!parser.ReadData(&iter, &origin_data, &origin_len)) + return false; + credential->origin.assign(origin_data, origin_len); + + uint32 proof_len; + const char* proof_data; + if (!parser.ReadReadLen32PrefixedData(&iter, &proof_data, &proof_len)) + return false; + credential->proof.assign(proof_data, proof_len); + + while (parser.IteratorHasRoomFor(iter, 1)) { + uint32 cert_len; + const char* cert_data; + if (!parser.ReadReadLen32PrefixedData(&iter, &cert_data, &cert_len)) + return false; + credential->certs.push_back(""); + credential->certs.back().assign(cert_data, cert_len); + } + return true; +} + SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority, SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) { @@ -1060,6 +1132,44 @@ SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate( return reinterpret_cast<SpdyWindowUpdateControlFrame*>(frame.take()); } +/* static */ +SpdyCredentialControlFrame* SpdyFramer::CreateCredentialFrame( + const SpdyCredential& credential) { + // Calculate the size of the frame by adding the size of the + // variable length data to the size of the fixed length data. + size_t frame_size = SpdyCredentialControlFrame::size() + + credential.origin.length() + credential.proof.length(); + DCHECK_EQ(SpdyCredentialControlFrame::size(), 16u); + for (vector<std::string>::const_iterator cert = credential.certs.begin(); + cert != credential.certs.end(); + cert++) { + frame_size += sizeof(uint32); // size of the cert_length field + frame_size += cert->length(); // size of the cert_data field + } + size_t payload_size = frame_size - SpdyFrame::kHeaderSize; + + SpdyFrameBuilder frame(frame_size); + // Create our FlagsAndLength. + SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE; + FlagsAndLength flags_length = CreateFlagsAndLength(flags, payload_size); + + frame.WriteUInt16(kControlFlagMask | spdy_version_); + frame.WriteUInt16(CREDENTIAL); + frame.WriteBytes(&flags_length, sizeof(flags_length)); + frame.WriteUInt16(credential.slot); + frame.WriteUInt16(credential.origin.size()); + frame.WriteBytes(credential.origin.c_str(), credential.origin.size()); + frame.WriteUInt32(credential.proof.size()); + frame.WriteBytes(credential.proof.c_str(), credential.proof.size()); + for (vector<std::string>::const_iterator cert = credential.certs.begin(); + cert != credential.certs.end(); + cert++) { + frame.WriteUInt32(cert->length()); + frame.WriteBytes(cert->c_str(), cert->length()); + } + return reinterpret_cast<SpdyCredentialControlFrame*>(frame.take()); +} + SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id, const char* data, uint32 len, SpdyDataFlags flags) { @@ -1645,6 +1755,8 @@ size_t SpdyFramer::GetMinimumControlFrameSize(SpdyControlType type) { return SpdyHeadersControlFrame::size(); case WINDOW_UPDATE: return SpdyWindowUpdateControlFrame::size(); + case CREDENTIAL: + return SpdyCredentialControlFrame::size(); case NUM_CONTROL_FRAME_TYPES: break; } @@ -1686,6 +1798,7 @@ SpdyStreamId SpdyFramer::GetControlFrameStreamId( case NOOP: case PING: case GOAWAY: + case CREDENTIAL: case NUM_CONTROL_FRAME_TYPES: // makes compiler happy break; } @@ -1712,6 +1825,7 @@ size_t SpdyFramer::BytesSafeToRead() const { case SPDY_CONTROL_FRAME_HEADER_BLOCK: return 0; case SPDY_CONTROL_FRAME_PAYLOAD: + case SPDY_CREDENTIAL_FRAME_PAYLOAD: case SPDY_IGNORE_REMAINING_PAYLOAD: case SPDY_FORWARD_STREAM_FRAME: return remaining_data_; diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index c1b47d9..1d4b000e 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -10,6 +10,7 @@ #include <map> #include <string> #include <utility> +#include <vector> #include "base/basictypes.h" #include "base/gtest_prod_util.h" @@ -51,6 +52,17 @@ typedef std::map<std::string, std::string> SpdyHeaderBlock; typedef std::pair<spdy::SettingsFlagsAndId, uint32> SpdySetting; typedef std::list<SpdySetting> SpdySettings; +// A datastrcture for holding the contents of a CREDENTIAL frame. +struct NET_EXPORT_PRIVATE SpdyCredential { + SpdyCredential(); + ~SpdyCredential(); + + uint16 slot; + std::string origin; + std::vector<std::string> certs; + std::string proof; +}; + // SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer. // Implement this interface to receive event callbacks as frames are // decoded from the framer. @@ -99,6 +111,18 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { const char* header_data, size_t len) = 0; + // Called when a chunk of payload data for a credential frame is available. + // This is called after OnControl() is called with the credential frame + // associated with the payload being delivered here. + // |frame_data| A buffer containing the header data chunk received. + // |len| The length of the header data buffer. A length of zero indicates + // that the header data block has been completely sent. + // When this function returns true the visitor indicates that it accepted + // all of the data. Returning false indicates that that an unrecoverable + // error has occurred, such as bad header data or resource exhaustion. + virtual bool OnCredentialFrameData(const char* frame_data, + size_t len) = 0; + // Called when a data frame header is received. The frame's data // payload will be provided via subsequent calls to // OnStreamFrameData(). @@ -132,6 +156,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { SPDY_FORWARD_STREAM_FRAME, SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, SPDY_CONTROL_FRAME_HEADER_BLOCK, + SPDY_CREDENTIAL_FRAME_PAYLOAD, }; // SPDY error codes. @@ -143,6 +168,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { SPDY_UNSUPPORTED_VERSION, // Control frame has unsupported version. SPDY_DECOMPRESS_FAILURE, // There was an error decompressing. SPDY_COMPRESS_FAILURE, // There was an error compressing. + SPDY_CREDENTIAL_FRAME_CORRUPT, // CREDENTIAL frame could not be parsed. LAST_ERROR, // Must be the last entry in the enum. }; @@ -260,10 +286,23 @@ class NET_EXPORT_PRIVATE SpdyFramer { SpdyStreamId stream_id, uint32 delta_window_size); + // Creates an instance of SpdyCredentialControlFrame. The CREDENTIAL + // frame is used to send a client certificate to the server when + // request more than one origin are sent over the same SPDY session. + static SpdyCredentialControlFrame* CreateCredentialFrame( + const SpdyCredential& credential); + // Given a SpdySettingsControlFrame, extract the settings. // Returns true on successful parse, false otherwise. static bool ParseSettings(const SpdySettingsControlFrame* frame, - SpdySettings* settings); + SpdySettings* settings); + + // Given a SpdyCredentialControlFrame's payload, extract the credential. + // Returns true on successful parse, false otherwise. + // TODO(hkhalil): Implement CREDENTIAL frame parsing in SpdyFramer + // and eliminate this method. + static bool ParseCredentialData(const char* data, size_t len, + SpdyCredential* credential); // Create a data frame. // |stream_id| is the stream for this frame @@ -370,6 +409,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { size_t ProcessCommonHeader(const char* data, size_t len); void ProcessControlFrameHeader(); size_t ProcessControlFramePayload(const char* data, size_t len); + size_t ProcessCredentialFramePayload(const char* data, size_t len); size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len); size_t ProcessControlFrameHeaderBlock(const char* data, size_t len); size_t OldProcessControlFrameHeaderBlock(const char* data, size_t len); diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 60d2731..c6fccb3 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -86,6 +86,7 @@ void CompareCharArraysWithHexError( class TestSpdyVisitor : public SpdyFramerVisitorInterface { public: static const size_t kDefaultHeaderBufferSize = 64 * 1024; + static const size_t kDefaultCredentialBufferSize = 16 * 1024; TestSpdyVisitor() : use_compression_(false), @@ -94,6 +95,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { syn_reply_frame_count_(0), headers_frame_count_(0), goaway_count_(0), + credential_count_(0), data_bytes_(0), fin_frame_count_(0), fin_flag_count_(0), @@ -107,7 +109,10 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { header_buffer_size_(kDefaultHeaderBufferSize), header_stream_id_(-1), header_control_type_(NUM_CONTROL_FRAME_TYPES), - header_buffer_valid_(false) { + header_buffer_valid_(false), + credential_buffer_(new char[kDefaultCredentialBufferSize]), + credential_buffer_length_(0), + credential_buffer_size_(kDefaultCredentialBufferSize) { } void OnError(SpdyFramer* f) { @@ -158,6 +163,9 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { case GOAWAY: goaway_count_++; break; + case CREDENTIAL: + credential_count_++; + break; default: DLOG(FATAL); // Error! } @@ -190,6 +198,26 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { return true; } + bool OnCredentialFrameData(const char* credential_data, size_t len) { + if (len == 0) { + if (!framer_.ParseCredentialData(credential_buffer_.get(), + credential_buffer_length_, + &credential_)) { + ++error_count_; + } + return true; + } + const size_t available = + credential_buffer_size_ - credential_buffer_length_; + if (len > available) { + return false; + } + memcpy(credential_buffer_.get() + credential_buffer_length_, + credential_data, len); + credential_buffer_length_ += len; + return true; + } + // Convenience function which runs a framer simulation with particular input. void SimulateInFramer(const unsigned char* input, size_t size) { framer_.set_enable_compression(use_compression_); @@ -244,6 +272,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { int syn_reply_frame_count_; int headers_frame_count_; int goaway_count_; + int credential_count_; int data_bytes_; int fin_frame_count_; // The count of RST_STREAM type frames received. int fin_flag_count_; // The count of frames with the FIN flag set. @@ -262,6 +291,11 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface { SpdyControlType header_control_type_; bool header_buffer_valid_; SpdyHeaderBlock headers_; + + scoped_array<char> credential_buffer_; + size_t credential_buffer_length_; + size_t credential_buffer_size_; + SpdyCredential credential_; }; } // namespace test @@ -1586,6 +1620,86 @@ TEST_F(SpdyFramerTest, CreateWindowUpdate) { } } +TEST_F(SpdyFramerTest, CreateCredential) { + SpdyFramer framer; + + { + const char kDescription[] = "CREDENTIAL frame"; + const unsigned char kFrameData[] = { + 0x80, 0x02, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x3F, + 0x00, 0x03, 0x00, 0x0A, + 'g', 'o', 'o', 'g', + 'l', 'e', '.', 'c', + 'o', 'm', 0x00, 0x00, + 0x00, 0x05, 'p', 'r', + 'o', 'o', 'f', 0x00, + 0x00, 0x00, 0x06, 'a', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, + 0x0C, 'a', 'n', 'o', + 't', 'h', 'e', 'r', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, + 0x0A, 'f', 'i', 'n', + 'a', 'l', ' ', 'c', + 'e', 'r', 't', + }; + SpdyCredential credential; + credential.slot = 3; + credential.origin = "google.com"; + credential.proof = "proof"; + credential.certs.push_back("a cert"); + credential.certs.push_back("another cert"); + credential.certs.push_back("final cert"); + scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential)); + CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + } +} + +TEST_F(SpdyFramerTest, ParseCredentialFrame) { + SpdyFramer framer; + + { + unsigned char kFrameData[] = { + 0x80, 0x02, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x3F, + 0x00, 0x03, 0x00, 0x0A, + 'g', 'o', 'o', 'g', + 'l', 'e', '.', 'c', + 'o', 'm', 0x00, 0x00, + 0x00, 0x05, 'p', 'r', + 'o', 'o', 'f', 0x00, + 0x00, 0x00, 0x06, 'a', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, + 0x0C, 'a', 'n', 'o', + 't', 'h', 'e', 'r', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, + 0x0A, 'f', 'i', 'n', + 'a', 'l', ' ', 'c', + 'e', 'r', 't', + }; + SpdyCredentialControlFrame frame(reinterpret_cast<char*>(kFrameData), + false); + SpdyCredential credential; + EXPECT_TRUE(SpdyFramer::ParseCredentialData(frame.payload(), + frame.length(), + &credential)); + EXPECT_EQ(3u, credential.slot); + EXPECT_EQ("google.com", credential.origin); + EXPECT_EQ("proof", credential.proof); + EXPECT_EQ("a cert", credential.certs.front()); + credential.certs.erase(credential.certs.begin()); + EXPECT_EQ("another cert", credential.certs.front()); + credential.certs.erase(credential.certs.begin()); + EXPECT_EQ("final cert", credential.certs.front()); + credential.certs.erase(credential.certs.begin()); + EXPECT_TRUE(credential.certs.empty()); + } +} + TEST_F(SpdyFramerTest, DuplicateFrame) { SpdyFramer framer; @@ -1637,6 +1751,106 @@ TEST_F(SpdyFramerTest, ExpandBuffer_HeapSmash) { } } +TEST_F(SpdyFramerTest, ReadCredentialFrame) { + SpdyCredential credential; + credential.slot = 3; + credential.origin = "google.com"; + credential.proof = "proof"; + credential.certs.push_back("a cert"); + credential.certs.push_back("another cert"); + credential.certs.push_back("final cert"); + SpdyFramer framer; + scoped_ptr<SpdyFrame> control_frame( + framer.CreateCredentialFrame(credential)); + EXPECT_TRUE(control_frame.get() != NULL); + TestSpdyVisitor visitor; + visitor.use_compression_ = false; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(control_frame.get()->data()), + control_frame.get()->length() + SpdyControlFrame::kHeaderSize); + EXPECT_EQ(0, visitor.error_count_); + EXPECT_EQ(1, visitor.credential_count_); + EXPECT_EQ(control_frame->length(), visitor.credential_buffer_length_); + EXPECT_EQ(credential.slot, visitor.credential_.slot); + EXPECT_EQ(credential.origin, visitor.credential_.origin); + EXPECT_EQ(credential.proof, visitor.credential_.proof); + EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size()); + for (size_t i = 0; i < credential.certs.size(); i++) { + EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]); + } +} + +TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptOrigin) { + SpdyCredential credential; + credential.slot = 3; + credential.origin = "google.com"; + credential.proof = "proof"; + credential.certs.push_back("a cert"); + credential.certs.push_back("another cert"); + credential.certs.push_back("final cert"); + SpdyFramer framer; + scoped_ptr<SpdyFrame> control_frame( + framer.CreateCredentialFrame(credential)); + EXPECT_TRUE(control_frame.get() != NULL); + TestSpdyVisitor visitor; + visitor.use_compression_ = false; + unsigned char* data = + reinterpret_cast<unsigned char*>(control_frame.get()->data()); + // Origin length is past the end of the frame + data[SpdyControlFrame::kHeaderSize + 2] = 0xFF; + visitor.SimulateInFramer( + data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize); + EXPECT_EQ(1, visitor.error_count_); +} + +TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptProof) { + SpdyCredential credential; + credential.slot = 3; + credential.origin = "google.com"; + credential.proof = "proof"; + credential.certs.push_back("a cert"); + credential.certs.push_back("another cert"); + credential.certs.push_back("final cert"); + SpdyFramer framer; + scoped_ptr<SpdyFrame> control_frame( + framer.CreateCredentialFrame(credential)); + EXPECT_TRUE(control_frame.get() != NULL); + TestSpdyVisitor visitor; + visitor.use_compression_ = false; + unsigned char* data = + reinterpret_cast<unsigned char*>(control_frame.get()->data()); + size_t offset = SpdyControlFrame::kHeaderSize + 4 + + credential.origin.length(); + data[offset] = 0xFF; // Proof length is past the end of the frame + visitor.SimulateInFramer( + data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize); + EXPECT_EQ(1, visitor.error_count_); +} + +TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptCertificate) { + SpdyCredential credential; + credential.slot = 3; + credential.origin = "google.com"; + credential.proof = "proof"; + credential.certs.push_back("a cert"); + credential.certs.push_back("another cert"); + credential.certs.push_back("final cert"); + SpdyFramer framer; + scoped_ptr<SpdyFrame> control_frame( + framer.CreateCredentialFrame(credential)); + EXPECT_TRUE(control_frame.get() != NULL); + TestSpdyVisitor visitor; + visitor.use_compression_ = false; + unsigned char* data = + reinterpret_cast<unsigned char*>(control_frame.get()->data()); + size_t offset = SpdyControlFrame::kHeaderSize + 4 + + credential.origin.length() + 4 + credential.proof.length(); + data[offset] = 0xFF; // Certificate length is past the end of the frame + visitor.SimulateInFramer( + data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize); + EXPECT_EQ(1, visitor.error_count_); +} + TEST_F(SpdyFramerTest, ReadGarbage) { SpdyFramer framer; unsigned char garbage_frame[256]; @@ -1691,9 +1905,12 @@ TEST(SpdyFramer, StateToStringTest) { EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK", SpdyFramer::StateToString( SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK)); + EXPECT_STREQ("SPDY_CREDENTIAL_FRAME_PAYLOAD", + SpdyFramer::StateToString( + SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD)); EXPECT_STREQ("UNKNOWN_STATE", SpdyFramer::StateToString( - SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK + 1)); + SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD + 1)); } TEST(SpdyFramer, ErrorCodeToStringTest) { @@ -1761,6 +1978,8 @@ TEST(SpdyFramer, ControlTypeToStringTest) { SpdyFramer::ControlTypeToString(HEADERS)); EXPECT_STREQ("WINDOW_UPDATE", SpdyFramer::ControlTypeToString(WINDOW_UPDATE)); + EXPECT_STREQ("SETTINGS", + SpdyFramer::ControlTypeToString(SETTINGS)); EXPECT_STREQ("UNKNOWN_CONTROL_TYPE", SpdyFramer::ControlTypeToString(NUM_CONTROL_FRAME_TYPES)); } @@ -1784,6 +2003,8 @@ TEST(SpdyFramer, GetMinimumControlFrameSizeTest) { SpdyFramer::GetMinimumControlFrameSize(HEADERS)); EXPECT_EQ(SpdyWindowUpdateControlFrame::size(), SpdyFramer::GetMinimumControlFrameSize(WINDOW_UPDATE)); + EXPECT_EQ(SpdyCredentialControlFrame::size(), + SpdyFramer::GetMinimumControlFrameSize(CREDENTIAL)); EXPECT_EQ(static_cast<size_t>(0x7FFFFFFF), SpdyFramer::GetMinimumControlFrameSize(NUM_CONTROL_FRAME_TYPES)); } diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index f2b5715..21c1d97 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -124,6 +124,25 @@ // | Delta-Window-Size (32 bits) | // +----------------------------------+ // +// Control Frame: CREDENTIAL +// +----------------------------------+ +// |1|000000000000001|0000000000001010| +// +----------------------------------+ +// | flags (8) | Length (24 bits) | >= 12 +// +----------------------------------+ +// | Slot (16 bits) | Origin Len (16)| +// +----------------------------------+ +// | Origin | +// +----------------------------------+ +// | Proof Length (32 bits) | +// +----------------------------------+ +// | Proof | +// +----------------------------------+ <+ +// | Certificate Length (32 bits) | | +// +----------------------------------+ | Repeated until end of frame +// | Certificate | | +// +----------------------------------+ <+ +// namespace spdy { // This implementation of Spdy is version 2; It's like version 1, with some @@ -160,6 +179,7 @@ enum SpdyControlType { GOAWAY, HEADERS, WINDOW_UPDATE, + CREDENTIAL, NUM_CONTROL_FRAME_TYPES }; @@ -291,6 +311,17 @@ struct SpdyPingControlFrameBlock : SpdyFrameBlock { uint32 unique_id_; }; +// A CREDENTIAL Control Frame structure. +struct SpdyCredentialControlFrameBlock : SpdyFrameBlock { + uint16 slot_; + uint16 origin_len_; + uint32 proof_len_; + // Variable data here. + // origin data + // proof data + // for each certificate: unit32 certificate_len + certificate_data[i] +}; + // A GOAWAY Control Frame structure. struct SpdyGoAwayControlFrameBlock : SpdyFrameBlock { SpdyStreamId last_accepted_stream_id_; @@ -687,6 +718,25 @@ class SpdyPingControlFrame : public SpdyControlFrame { } }; +class SpdyCredentialControlFrame : public SpdyControlFrame { + public: + SpdyCredentialControlFrame() : SpdyControlFrame(size()) {} + SpdyCredentialControlFrame(char* data, bool owns_buffer) + : SpdyControlFrame(data, owns_buffer) {} + + const char* payload() const { + return reinterpret_cast<const char*>(block()) + SpdyFrame::kHeaderSize; + } + + static size_t size() { return sizeof(SpdyCredentialControlFrameBlock); } + + private: + const struct SpdyCredentialControlFrameBlock* block() const { + return static_cast<SpdyCredentialControlFrameBlock*>(frame_); + } + DISALLOW_COPY_AND_ASSIGN(SpdyCredentialControlFrame); +}; + class SpdyGoAwayControlFrame : public SpdyControlFrame { public: SpdyGoAwayControlFrame() : SpdyControlFrame(size()) {} diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 6c0a584..de2a198 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -1352,6 +1352,12 @@ bool SpdySession::OnControlFrameHeaderData( return true; } +bool SpdySession::OnCredentialFrameData(const char* frame_data, + size_t len) { + DCHECK(false); + return false; +} + void SpdySession::OnDataFrameHeader(const spdy::SpdyDataFrame* frame) { buffered_spdy_framer_.OnDataFrameHeader(frame); } diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index fcdb193..2fc6621 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -309,6 +309,7 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, void OnPing(const spdy::SpdyPingControlFrame& frame); void OnSettings(const spdy::SpdySettingsControlFrame& frame); void OnWindowUpdate(const spdy::SpdyWindowUpdateControlFrame& frame); + void OnCredential(const spdy::SpdyCredentialControlFrame& frame); // IO Callbacks void OnReadComplete(int result); @@ -318,7 +319,7 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, void SendSettings(); // Handle SETTINGS. Either when we send settings, or when we receive a - // SETTINGS ontrol frame, update our SpdySession accordingly. + // SETTINGS control frame, update our SpdySession accordingly. void HandleSettings(const spdy::SpdySettings& settings); // Send the PING (preface-PING and trailing-PING) frames. @@ -404,6 +405,9 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, const char* header_data, size_t len) OVERRIDE; + virtual bool OnCredentialFrameData(const char* frame_data, + size_t len) OVERRIDE; + virtual void OnDataFrameHeader(const spdy::SpdyDataFrame* frame) OVERRIDE; virtual void OnSyn(const spdy::SpdySynStreamControlFrame& frame, diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc index 6e76e5f..8f7a687 100644 --- a/net/tools/flip_server/spdy_interface.cc +++ b/net/tools/flip_server/spdy_interface.cc @@ -309,6 +309,11 @@ void SpdySM::OnStreamFrameData(SpdyStreamId stream_id, interface->ProcessWriteInput(data, len); } +bool SpdySM::OnCredentialFrameData(const char* frame_data, + size_t len) { + return false; +} + size_t SpdySM::ProcessReadInput(const char* data, size_t len) { return spdy_framer_->ProcessInput(data, len); } @@ -611,4 +616,3 @@ void SpdySM::GetOutput() { } } // namespace net - diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h index 5598070..64562cc 100644 --- a/net/tools/flip_server/spdy_interface.h +++ b/net/tools/flip_server/spdy_interface.h @@ -69,6 +69,8 @@ class SpdySM : public spdy::SpdyFramerVisitorInterface, virtual void OnDataFrameHeader(const spdy::SpdyDataFrame* frame) OVERRIDE; virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id, const char* data, size_t len) OVERRIDE; + virtual bool OnCredentialFrameData(const char* frame_data, + size_t len) OVERRIDE; public: virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE; |