blob: 31c26cfdccf38932042c71f4468789d5e6b9c0ed [file] [log] [blame]
// 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 {
namespace {
const size_t kCryptoTagSize = sizeof(uint32);
const size_t kNumEntriesSize = sizeof(uint16);
const size_t kValueLenSize = sizeof(uint16);
} // namespace
CryptoFramer::CryptoFramer()
: visitor_(NULL),
message_tag_(0),
num_entries_(0),
values_len_(0) {
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() < kCryptoTagSize) {
break;
}
reader.ReadUInt32(&message_tag_);
state_ = STATE_READING_NUM_ENTRIES;
case STATE_READING_NUM_ENTRIES:
if (reader.BytesRemaining() < kNumEntriesSize) {
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_ * kCryptoTagSize) {
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_ * kValueLenSize) {
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 remaining data.
buffer_ = reader.PeekRemainingPayload().as_string();
return true;
}
QuicData* CryptoFramer::ConstructHandshakeMessage(
const CryptoHandshakeMessage& message) {
if (message.tag_value_map.size() > kMaxEntries) {
return NULL;
}
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 NULL;
}
++it;
}
if (message.tag_value_map.size() % 2 == 1) {
len += sizeof(uint16); // padding
}
QuicDataWriter writer(len);
if (!writer.WriteUInt32(message.tag)) {
DCHECK(false) << "Failed to write message tag.";
return NULL;
}
if (!writer.WriteUInt16(message.tag_value_map.size())) {
DCHECK(false) << "Failed to write size.";
return NULL;
}
// Tags
for (it = message.tag_value_map.begin();
it != message.tag_value_map.end(); ++it) {
if (!writer.WriteUInt32(it->first)) {
DCHECK(false) << "Failed to write tag.";
return NULL;
}
}
// Lengths
for (it = message.tag_value_map.begin();
it != message.tag_value_map.end(); ++it) {
if (!writer.WriteUInt16(it->second.length())) {
DCHECK(false) << "Failed to write length.";
return NULL;
}
}
// Possible padding
if (message.tag_value_map.size() % 2 == 1) {
if (!writer.WriteUInt16(0xABAB)) {
DCHECK(false) << "Failed to write padding.";
return NULL;
}
}
// Values
for (it = message.tag_value_map.begin();
it != message.tag_value_map.end(); ++it) {
if (!writer.WriteBytes(it->second.data(), it->second.length())) {
DCHECK(false) << "Failed to write value.";
return NULL;
}
}
return new QuicData(writer.take(), len, true);
}
void CryptoFramer::Clear() {
tag_value_map_.clear();
tag_length_map_.clear();
tags_.clear();
error_ = QUIC_NO_ERROR;
state_ = STATE_READING_TAG;
}
} // namespace net