OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/websockets/websocket_frame.h" | 5 #include "net/websockets/websocket_frame.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/rand_util.h" | 9 #include "base/rand_util.h" |
10 #include "net/base/big_endian.h" | 10 #include "net/base/big_endian.h" |
11 #include "net/base/io_buffer.h" | 11 #include "net/base/io_buffer.h" |
12 #include "net/base/net_errors.h" | 12 #include "net/base/net_errors.h" |
13 | 13 |
14 namespace { | 14 namespace { |
15 | 15 |
16 const uint8 kFinalBit = 0x80; | 16 const uint8 kFinalBit = 0x80; |
17 const uint8 kReserved1Bit = 0x40; | 17 const uint8 kReserved1Bit = 0x40; |
18 const uint8 kReserved2Bit = 0x20; | 18 const uint8 kReserved2Bit = 0x20; |
19 const uint8 kReserved3Bit = 0x10; | 19 const uint8 kReserved3Bit = 0x10; |
20 const uint8 kOpCodeMask = 0xF; | 20 const uint8 kOpCodeMask = 0xF; |
21 const uint8 kMaskBit = 0x80; | 21 const uint8 kMaskBit = 0x80; |
22 const uint64 kMaxPayloadLengthWithoutExtendedLengthField = 125; | 22 const uint64 kMaxPayloadLengthWithoutExtendedLengthField = 125; |
23 const uint64 kPayloadLengthWithTwoByteExtendedLengthField = 126; | 23 const uint64 kPayloadLengthWithTwoByteExtendedLengthField = 126; |
24 const uint64 kPayloadLengthWithEightByteExtendedLengthField = 127; | 24 const uint64 kPayloadLengthWithEightByteExtendedLengthField = 127; |
25 | 25 |
26 inline void MaskWebSocketFramePayloadByBytes( | |
27 const net::WebSocketMaskingKey& masking_key, | |
28 size_t masking_key_offset, | |
29 char* const begin, | |
30 char* const end) { | |
31 for (char* masked = begin; masked != end; ++masked) { | |
32 *masked ^= masking_key.key[masking_key_offset++]; | |
33 if (masking_key_offset == net::WebSocketFrameHeader::kMaskingKeyLength) | |
34 masking_key_offset = 0; | |
35 } | |
36 } | |
37 | |
26 } // Unnamed namespace. | 38 } // Unnamed namespace. |
27 | 39 |
28 namespace net { | 40 namespace net { |
29 | 41 |
30 // Definitions for in-struct constants. | 42 // Definitions for in-struct constants. |
31 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeContinuation = | 43 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeContinuation = |
32 0x0; | 44 0x0; |
33 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeText = 0x1; | 45 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeText = 0x1; |
34 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeBinary = 0x2; | 46 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeBinary = 0x2; |
35 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeClose = 0x8; | 47 const WebSocketFrameHeader::OpCode WebSocketFrameHeader::kOpCodeClose = 0x8; |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
135 // Masking keys should be generated from a cryptographically secure random | 147 // Masking keys should be generated from a cryptographically secure random |
136 // number generator, which means web application authors should not be able | 148 // number generator, which means web application authors should not be able |
137 // to guess the next value of masking key. | 149 // to guess the next value of masking key. |
138 WebSocketMaskingKey masking_key; | 150 WebSocketMaskingKey masking_key; |
139 base::RandBytes(masking_key.key, WebSocketFrameHeader::kMaskingKeyLength); | 151 base::RandBytes(masking_key.key, WebSocketFrameHeader::kMaskingKeyLength); |
140 return masking_key; | 152 return masking_key; |
141 } | 153 } |
142 | 154 |
143 void MaskWebSocketFramePayload(const WebSocketMaskingKey& masking_key, | 155 void MaskWebSocketFramePayload(const WebSocketMaskingKey& masking_key, |
144 uint64 frame_offset, | 156 uint64 frame_offset, |
145 char* data, | 157 char* const data, |
Takashi Toyoshima
2012/12/14 07:04:11
Sorry, I'm not familiar with this 'const' usage.
Adam Rice
2012/12/14 08:05:37
Resolved offline.
| |
146 int data_size) { | 158 int data_size) { |
147 static const size_t kMaskingKeyLength = | 159 static const size_t kMaskingKeyLength = |
148 WebSocketFrameHeader::kMaskingKeyLength; | 160 WebSocketFrameHeader::kMaskingKeyLength; |
149 | 161 |
150 DCHECK_GE(data_size, 0); | 162 DCHECK_GE(data_size, 0); |
151 | 163 |
152 // TODO(yutak): Make masking more efficient by XOR'ing every machine word | 164 // Most of the masking is done one word at a time, except for the beginning |
153 // (4 or 8 bytes), instead of XOR'ing every byte. | 165 // and the end of the buffer which may be unaligned. We use size_t to get the |
154 size_t masking_key_offset = frame_offset % kMaskingKeyLength; | 166 // word size for this architecture. We require it be a multiple of |
155 for (int i = 0; i < data_size; ++i) { | 167 // kMaskingKeyLength in size. |
156 data[i] ^= masking_key.key[masking_key_offset++]; | 168 typedef size_t PackedMaskType; |
157 if (masking_key_offset == kMaskingKeyLength) | 169 PackedMaskType packed_mask_key = 0; |
158 masking_key_offset = 0; | 170 static const size_t kPackedMaskKeySize = sizeof(packed_mask_key); |
171 COMPILE_ASSERT((kPackedMaskKeySize >= kMaskingKeyLength && | |
172 kPackedMaskKeySize % kMaskingKeyLength == 0), | |
173 word_size_is_not_multiple_of_mask_length); | |
174 char* const end = data + data_size; | |
175 // If the buffer is too small for the vectorised version to be useful, revert | |
176 // to the byte-at-a-time implementation early. | |
177 if (data_size <= static_cast<int>(kPackedMaskKeySize * 2)) { | |
178 MaskWebSocketFramePayloadByBytes(masking_key, | |
179 frame_offset % kMaskingKeyLength, | |
180 data, end); | |
181 return; | |
159 } | 182 } |
183 const size_t data_modulus = | |
184 reinterpret_cast<size_t>(data) % kPackedMaskKeySize; | |
185 char* const aligned_begin = data_modulus == 0 ? data : | |
186 (data + kPackedMaskKeySize - data_modulus); | |
187 // Guaranteed by the above check for small data_size. | |
188 DCHECK(aligned_begin < end); | |
189 MaskWebSocketFramePayloadByBytes(masking_key, | |
190 frame_offset % kMaskingKeyLength, | |
191 data, aligned_begin); | |
192 const size_t end_modulus = reinterpret_cast<size_t>(end) % kPackedMaskKeySize; | |
193 char* const aligned_end = end - end_modulus; | |
194 // Guaranteed by the above check for small data_size. | |
195 DCHECK(aligned_end > aligned_begin); | |
196 // Create a version of the mask which is rotated by the appropriate offset | |
197 // for our alignment. The "trick" here is that 0 XORed with the mask will | |
198 // give the value of the mask for the appropriate byte. | |
199 char realigned_mask[kMaskingKeyLength] = { 0 }; | |
200 MaskWebSocketFramePayloadByBytes(masking_key, | |
201 (frame_offset + aligned_begin - data) | |
202 % kMaskingKeyLength, | |
203 realigned_mask, | |
204 realigned_mask + kMaskingKeyLength); | |
205 | |
206 for (size_t i = 0; i < kPackedMaskKeySize; i += kMaskingKeyLength) { | |
207 // memcpy() is allegedly blessed by the C++ standard for type-punning. | |
208 memcpy(reinterpret_cast<char*>(&packed_mask_key) + i, | |
209 realigned_mask, kMaskingKeyLength); | |
210 } | |
211 | |
212 // The main loop. | |
213 for (char* merged = aligned_begin; | |
214 merged != aligned_end; | |
215 merged += kPackedMaskKeySize) { | |
216 // This is not quite standard-compliant C++. However, the standard-compliant | |
217 // equivalent (using memcpy()) compiles to slower code using g++. In | |
218 // practice, this will work for the compilers and architectures currently | |
219 // supported by Chromium, and the tests are extremely unlikely to pass if a | |
220 // future compiler/architecture breaks it. | |
221 *reinterpret_cast<size_t*>(merged) ^= packed_mask_key; | |
Takashi Toyoshima
2012/12/14 07:04:11
You should apply cast<PackedMaskType> here.
Adam Rice
2012/12/14 08:05:37
Done.
| |
222 } | |
223 | |
224 MaskWebSocketFramePayloadByBytes(masking_key, | |
225 (frame_offset + (aligned_end - data)) | |
226 % kMaskingKeyLength, | |
227 aligned_end, end); | |
160 } | 228 } |
161 | 229 |
162 } // namespace net | 230 } // namespace net |
OLD | NEW |