1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/quic/crypto/crypto_handshake_message.h"
6
7#include "base/strings/stringprintf.h"
8#include "base/strings/string_number_conversions.h"
9#include "net/quic/crypto/crypto_framer.h"
10#include "net/quic/crypto/crypto_protocol.h"
11#include "net/quic/quic_socket_address_coder.h"
12#include "net/quic/quic_utils.h"
13
14using base::StringPiece;
15using base::StringPrintf;
16using std::string;
17using std::vector;
18
19namespace net {
20
21CryptoHandshakeMessage::CryptoHandshakeMessage()
22    : tag_(0),
23      minimum_size_(0) {}
24
25CryptoHandshakeMessage::CryptoHandshakeMessage(
26    const CryptoHandshakeMessage& other)
27    : tag_(other.tag_),
28      tag_value_map_(other.tag_value_map_),
29      minimum_size_(other.minimum_size_) {
30  // Don't copy serialized_. scoped_ptr doesn't have a copy constructor.
31  // The new object can lazily reconstruct serialized_.
32}
33
34CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
35
36CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
37    const CryptoHandshakeMessage& other) {
38  tag_ = other.tag_;
39  tag_value_map_ = other.tag_value_map_;
40  // Don't copy serialized_. scoped_ptr doesn't have an assignment operator.
41  // However, invalidate serialized_.
42  serialized_.reset();
43  minimum_size_ = other.minimum_size_;
44  return *this;
45}
46
47void CryptoHandshakeMessage::Clear() {
48  tag_ = 0;
49  tag_value_map_.clear();
50  minimum_size_ = 0;
51  serialized_.reset();
52}
53
54const QuicData& CryptoHandshakeMessage::GetSerialized() const {
55  if (!serialized_.get()) {
56    serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this));
57  }
58  return *serialized_.get();
59}
60
61void CryptoHandshakeMessage::MarkDirty() {
62  serialized_.reset();
63}
64
65void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) {
66  // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break
67  // because the terminating 0 will only be promoted to int.
68  COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int),
69                 crypto_tag_may_not_be_larger_than_int_or_varargs_will_break);
70
71  vector<QuicTag> tags;
72  va_list ap;
73
74  va_start(ap, tag);
75  for (;;) {
76    QuicTag list_item = va_arg(ap, QuicTag);
77    if (list_item == 0) {
78      break;
79    }
80    tags.push_back(list_item);
81  }
82
83  // Because of the way that we keep tags in memory, we can copy the contents
84  // of the vector and get the correct bytes in wire format. See
85  // crypto_protocol.h. This assumes that the system is little-endian.
86  SetVector(tag, tags);
87
88  va_end(ap);
89}
90
91void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) {
92  tag_value_map_[tag] = value.as_string();
93}
94
95void CryptoHandshakeMessage::Erase(QuicTag tag) {
96  tag_value_map_.erase(tag);
97}
98
99QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
100                                                 const QuicTag** out_tags,
101                                                 size_t* out_len) const {
102  QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
103  QuicErrorCode ret = QUIC_NO_ERROR;
104
105  if (it == tag_value_map_.end()) {
106    ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
107  } else if (it->second.size() % sizeof(QuicTag) != 0) {
108    ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
109  }
110
111  if (ret != QUIC_NO_ERROR) {
112    *out_tags = NULL;
113    *out_len = 0;
114    return ret;
115  }
116
117  *out_tags = reinterpret_cast<const QuicTag*>(it->second.data());
118  *out_len = it->second.size() / sizeof(QuicTag);
119  return ret;
120}
121
122bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
123                                            StringPiece* out) const {
124  QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
125  if (it == tag_value_map_.end()) {
126    return false;
127  }
128  *out = it->second;
129  return true;
130}
131
132QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag,
133                                                    unsigned index,
134                                                    StringPiece* out) const {
135  StringPiece value;
136  if (!GetStringPiece(tag, &value)) {
137    return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
138  }
139
140  for (unsigned i = 0;; i++) {
141    if (value.empty()) {
142      return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
143    }
144    if (value.size() < 3) {
145      return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
146    }
147
148    const unsigned char* data =
149        reinterpret_cast<const unsigned char*>(value.data());
150    size_t size = static_cast<size_t>(data[0]) |
151                  (static_cast<size_t>(data[1]) << 8) |
152                  (static_cast<size_t>(data[2]) << 16);
153    value.remove_prefix(3);
154
155    if (value.size() < size) {
156      return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
157    }
158
159    if (i == index) {
160      *out = StringPiece(value.data(), size);
161      return QUIC_NO_ERROR;
162    }
163
164    value.remove_prefix(size);
165  }
166}
167
168QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag,
169                                                uint16* out) const {
170  return GetPOD(tag, out, sizeof(uint16));
171}
172
173QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
174                                                uint32* out) const {
175  return GetPOD(tag, out, sizeof(uint32));
176}
177
178QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
179                                                uint64* out) const {
180  return GetPOD(tag, out, sizeof(uint64));
181}
182
183size_t CryptoHandshakeMessage::size() const {
184  size_t ret = sizeof(QuicTag) +
185               sizeof(uint16) /* number of entries */ +
186               sizeof(uint16) /* padding */;
187  ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) *
188         tag_value_map_.size();
189  for (QuicTagValueMap::const_iterator i = tag_value_map_.begin();
190       i != tag_value_map_.end(); ++i) {
191    ret += i->second.size();
192  }
193
194  return ret;
195}
196
197void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
198  if (min_bytes == minimum_size_) {
199    return;
200  }
201  serialized_.reset();
202  minimum_size_ = min_bytes;
203}
204
205size_t CryptoHandshakeMessage::minimum_size() const {
206  return minimum_size_;
207}
208
209string CryptoHandshakeMessage::DebugString() const {
210  return DebugStringInternal(0);
211}
212
213QuicErrorCode CryptoHandshakeMessage::GetPOD(
214    QuicTag tag, void* out, size_t len) const {
215  QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
216  QuicErrorCode ret = QUIC_NO_ERROR;
217
218  if (it == tag_value_map_.end()) {
219    ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
220  } else if (it->second.size() != len) {
221    ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
222  }
223
224  if (ret != QUIC_NO_ERROR) {
225    memset(out, 0, len);
226    return ret;
227  }
228
229  memcpy(out, it->second.data(), len);
230  return ret;
231}
232
233string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
234  string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n";
235  ++indent;
236  for (QuicTagValueMap::const_iterator it = tag_value_map_.begin();
237       it != tag_value_map_.end(); ++it) {
238    ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": ";
239
240    bool done = false;
241    switch (it->first) {
242      case kICSL:
243      case kIFCW:
244      case kCFCW:
245      case kSFCW:
246      case kIRTT:
247      case kKATO:
248      case kMSPC:
249      case kSWND:
250        // uint32 value
251        if (it->second.size() == 4) {
252          uint32 value;
253          memcpy(&value, it->second.data(), sizeof(value));
254          ret += base::UintToString(value);
255          done = true;
256        }
257        break;
258      case kKEXS:
259      case kAEAD:
260      case kCGST:
261      case kCOPT:
262      case kLOSS:
263      case kPDMD:
264      case kVER:
265        // tag lists
266        if (it->second.size() % sizeof(QuicTag) == 0) {
267          for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
268            QuicTag tag;
269            memcpy(&tag, it->second.data() + j, sizeof(tag));
270            if (j > 0) {
271              ret += ",";
272            }
273            ret += "'" + QuicUtils::TagToString(tag) + "'";
274          }
275          done = true;
276        }
277        break;
278      case kCADR:
279        // IP address and port
280        if (!it->second.empty()) {
281          QuicSocketAddressCoder decoder;
282          if (decoder.Decode(it->second.data(), it->second.size())) {
283            ret += IPAddressToStringWithPort(decoder.ip(), decoder.port());
284            done = true;
285          }
286        }
287        break;
288      case kSCFG:
289        // nested messages.
290        if (!it->second.empty()) {
291          scoped_ptr<CryptoHandshakeMessage> msg(
292              CryptoFramer::ParseMessage(it->second));
293          if (msg.get()) {
294            ret += "\n";
295            ret += msg->DebugStringInternal(indent + 1);
296
297            done = true;
298          }
299        }
300        break;
301      case kPAD:
302        ret += StringPrintf("(%d bytes of padding)",
303                            static_cast<int>(it->second.size()));
304        done = true;
305        break;
306      case kUAID:
307        ret += it->second;
308        done = true;
309        break;
310    }
311
312    if (!done) {
313      // If there's no specific format for this tag, or the value is invalid,
314      // then just use hex.
315      ret += "0x" + base::HexEncode(it->second.data(), it->second.size());
316    }
317    ret += "\n";
318  }
319  --indent;
320  ret += string(2 * indent, ' ') + ">";
321  return ret;
322}
323
324}  // namespace net
325