OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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/websockets/websocket_frame_builder.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/logging.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "base/memory/scoped_vector.h" | |
13 #include "base/rand_util.h" | |
14 #include "net/base/big_endian.h" | |
15 #include "net/websockets/websocket_frame.h" | |
16 | |
17 namespace { | |
18 | |
19 const uint8 kFinalBit = 0x80; | |
20 const uint8 kReserved1Bit = 0x40; | |
21 const uint8 kReserved2Bit = 0x20; | |
22 const uint8 kReserved3Bit = 0x10; | |
23 const uint8 kOpCodeMask = 0xF; | |
24 const uint8 kMaskBit = 0x80; | |
25 const uint64 kMaxPayloadLengthWithoutExtendedLengthField = 125; | |
26 const uint64 kPayloadLengthWithTwoByteExtendedLengthField = 126; | |
27 const uint64 kPayloadLengthWithEightByteExtendedLengthField = 127; | |
28 | |
29 } // Unnamed namespace. | |
30 | |
31 namespace net { | |
32 | |
33 WebSocketFrameBuilder::WebSocketFrameBuilder() | |
34 : frame_offset_(0), | |
35 has_pinned_masking_key_for_testing_(false), | |
36 failed_(false) { | |
37 std::fill(masking_key_, | |
38 masking_key_ + WebSocketFrameHeader::kMaskingKeyLength, | |
39 '\0'); | |
40 std::fill( | |
41 pinned_masking_key_for_testing_, | |
42 pinned_masking_key_for_testing_ + WebSocketFrameHeader::kMaskingKeyLength, | |
43 '\0'); | |
44 } | |
45 | |
46 WebSocketFrameBuilder::~WebSocketFrameBuilder() { | |
47 } | |
48 | |
49 bool WebSocketFrameBuilder::Encode( | |
50 ScopedVector<WebSocketFrameChunk> frame_chunks, | |
51 std::vector<char>* output) { | |
52 if (failed_) | |
53 return false; | |
54 | |
55 // We DCHECK inconsistency of |frame_chunks| such as payload length mismatch | |
mmenke
2012/05/15 18:36:31
nit: Think "We DCHECK on inconsistent |frame_chun
| |
56 // or missing |header| in the first chunk, because these are programming | |
57 // errors we want to avoid. When DCHECK isn't functional (i.e. Release and | |
58 // !DCHECK_ALWAYS_ON), we fail gracefully on these errors. | |
59 for (ScopedVector<WebSocketFrameChunk>::iterator iter = frame_chunks.begin(); | |
60 iter != frame_chunks.end(); ++iter) { | |
61 WebSocketFrameChunk* chunk = *iter; | |
62 if (chunk->header.get()) { // Beginning of a new frame. | |
63 DCHECK(!current_frame_header_.get()); | |
64 DCHECK_EQ(0u, frame_offset_); | |
65 if (current_frame_header_.get() || frame_offset_ != 0u) { | |
66 Fail(); | |
67 return false; | |
68 } | |
69 current_frame_header_ = chunk->header.Pass(); | |
70 if (current_frame_header_->masked) | |
71 GenerateNewMaskingKey(); | |
72 if (!EncodeFrameHeader(current_frame_header_.get(), output)) { | |
73 Fail(); | |
74 return false; | |
75 } | |
76 } | |
77 | |
78 DCHECK(current_frame_header_.get()); | |
79 if (!current_frame_header_.get()) { | |
80 Fail(); | |
81 return false; | |
82 } | |
83 | |
84 if (current_frame_header_->masked) | |
mmenke
2012/05/15 18:36:31
According to specs, it's invalid for this to be fa
| |
85 MaskPayload(&chunk->data); | |
86 // FIXME(yutak): Remove copy here. Ultimately, we probably need to have | |
87 // "gather writes" ability (aka vectored I/O) in the Socket interface | |
88 // so we can send data from multiple local buffers without data copies. | |
89 output->insert(output->end(), chunk->data.begin(), chunk->data.end()); | |
90 frame_offset_ += chunk->data.size(); | |
91 DCHECK_LE(frame_offset_, current_frame_header_->payload_length); | |
92 if (frame_offset_ > current_frame_header_->payload_length) { | |
93 Fail(); | |
94 return false; | |
95 } | |
96 | |
97 if (chunk->final_chunk) { | |
98 // Throw away the information about the current frame to get ready | |
99 // for the next frame. | |
100 DCHECK_EQ(frame_offset_, current_frame_header_->payload_length); | |
101 if (frame_offset_ != current_frame_header_->payload_length) { | |
mmenke
2012/05/15 18:36:31
I'm a bit worried about the interface complexity a
| |
102 Fail(); | |
103 return false; | |
104 } | |
105 current_frame_header_.reset(); | |
106 frame_offset_ = 0; | |
107 } | |
108 } | |
109 | |
110 return true; | |
111 } | |
112 | |
113 bool WebSocketFrameBuilder::EncodeFrameHeader( | |
114 WebSocketFrameHeader* header, | |
115 std::vector<char>* output) const { | |
116 DCHECK(header); | |
117 | |
118 // header->opcode must fit to kOpCodeMask. | |
119 DCHECK((header->opcode & kOpCodeMask) == header->opcode); | |
120 | |
121 uint8 first_byte = 0u; | |
122 first_byte |= header->final ? kFinalBit : 0u; | |
123 first_byte |= header->reserved1 ? kReserved1Bit : 0u; | |
124 first_byte |= header->reserved2 ? kReserved2Bit : 0u; | |
125 first_byte |= header->reserved3 ? kReserved3Bit : 0u; | |
126 first_byte |= header->opcode; | |
127 | |
128 uint8 second_byte = 0; | |
mmenke
2012/05/15 18:36:31
nit: 0u
| |
129 second_byte |= header->masked ? kMaskBit : 0u; | |
130 if (header->payload_length <= | |
131 kMaxPayloadLengthWithoutExtendedLengthField) { | |
132 second_byte |= header->payload_length; | |
133 } else if (header->payload_length <= kuint16max) { | |
134 second_byte |= kPayloadLengthWithTwoByteExtendedLengthField; | |
135 } else if (header->payload_length <= static_cast<uint64>(kint64max)) { | |
136 second_byte |= kPayloadLengthWithEightByteExtendedLengthField; | |
137 } else { | |
138 // WebSocket protocol specification doesn't allow payload length to | |
139 // exceed kint64max (0x7FFFFFFFFFFFFFFF). | |
140 return false; | |
141 } | |
142 | |
143 output->push_back(first_byte); | |
144 output->push_back(second_byte); | |
145 | |
146 // Writes "extended payload length" field. | |
147 if (second_byte == kPayloadLengthWithTwoByteExtendedLengthField) { | |
148 uint16 payload_length_16 = static_cast<uint16>(header->payload_length); | |
149 char encoded[sizeof(uint16)]; | |
150 WriteBigEndian(encoded, payload_length_16); | |
151 output->insert(output->end(), encoded, encoded + sizeof(uint16)); | |
152 } else if (second_byte == kPayloadLengthWithEightByteExtendedLengthField) { | |
153 char encoded[sizeof(uint64)]; | |
154 WriteBigEndian(encoded, header->payload_length); | |
155 output->insert(output->end(), encoded, encoded + sizeof(uint64)); | |
156 } | |
157 | |
158 // Writes "masking key" field, if needed. | |
159 if (header->masked) { | |
160 output->insert(output->end(), masking_key_, | |
161 masking_key_ + WebSocketFrameHeader::kMaskingKeyLength); | |
162 } | |
163 | |
164 return true; | |
165 } | |
166 | |
167 void WebSocketFrameBuilder::PinMaskingKeyForTesting(const char masking_key[]) { | |
168 std::copy(masking_key, masking_key + WebSocketFrameHeader::kMaskingKeyLength, | |
169 pinned_masking_key_for_testing_); | |
170 has_pinned_masking_key_for_testing_ = true; | |
171 } | |
172 | |
173 void WebSocketFrameBuilder::GenerateNewMaskingKey() { | |
174 DCHECK(current_frame_header_->masked); | |
175 | |
176 static const size_t kMaskingKeyLength = | |
177 WebSocketFrameHeader::kMaskingKeyLength; | |
178 | |
179 if (has_pinned_masking_key_for_testing_) { | |
180 std::copy(pinned_masking_key_for_testing_, | |
181 pinned_masking_key_for_testing_ + kMaskingKeyLength, | |
182 masking_key_); | |
183 } else { | |
184 // Masking keys should be generated from a cryptographically secure random | |
185 // number generator, which means web application authors should not be able | |
186 // to guess the next value of masking key. | |
187 base::RandBytes(masking_key_, kMaskingKeyLength); | |
188 } | |
189 } | |
190 | |
191 void WebSocketFrameBuilder::MaskPayload(std::vector<char>* output) { | |
192 static const size_t kMaskingKeyLength = | |
193 WebSocketFrameHeader::kMaskingKeyLength; | |
194 | |
195 // TODO(yutak): Make masking more efficient by XOR'ing every machine word | |
196 // (4 or 8 bytes), instead of XOR'ing every byte. | |
197 uint64 masking_key_offset = frame_offset_ % kMaskingKeyLength; | |
198 for (std::vector<char>::iterator iter = output->begin(); | |
199 iter != output->end(); ++iter) { | |
200 *iter ^= masking_key_[masking_key_offset++]; | |
201 if (masking_key_offset == kMaskingKeyLength) | |
202 masking_key_offset = 0; | |
203 } | |
204 } | |
205 | |
206 void WebSocketFrameBuilder::Fail() { | |
207 current_frame_header_.reset(); | |
208 frame_offset_ = 0; | |
209 failed_ = true; | |
210 } | |
211 | |
212 } // namespace net | |
OLD | NEW |