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.
5 #include "net/websockets/websocket_frame.h"
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/rand_util.h"
10 #include "net/base/big_endian.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/net_errors.h"
16 const uint8 kFinalBit
= 0x80;
17 const uint8 kReserved1Bit
= 0x40;
18 const uint8 kReserved2Bit
= 0x20;
19 const uint8 kReserved3Bit
= 0x10;
20 const uint8 kOpCodeMask
= 0xF;
21 const uint8 kMaskBit
= 0x80;
22 const uint64 kMaxPayloadLengthWithoutExtendedLengthField
= 125;
23 const uint64 kPayloadLengthWithTwoByteExtendedLengthField
= 126;
24 const uint64 kPayloadLengthWithEightByteExtendedLengthField
= 127;
26 inline void MaskWebSocketFramePayloadByBytes(
27 const net::WebSocketMaskingKey
& masking_key
,
28 size_t masking_key_offset
,
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;
38 } // Unnamed namespace.
42 // Definitions for in-struct constants.
43 const WebSocketFrameHeader::OpCode
WebSocketFrameHeader::kOpCodeContinuation
=
45 const WebSocketFrameHeader::OpCode
WebSocketFrameHeader::kOpCodeText
= 0x1;
46 const WebSocketFrameHeader::OpCode
WebSocketFrameHeader::kOpCodeBinary
= 0x2;
47 const WebSocketFrameHeader::OpCode
WebSocketFrameHeader::kOpCodeClose
= 0x8;
48 const WebSocketFrameHeader::OpCode
WebSocketFrameHeader::kOpCodePing
= 0x9;
49 const WebSocketFrameHeader::OpCode
WebSocketFrameHeader::kOpCodePong
= 0xA;
51 WebSocketFrameChunk::WebSocketFrameChunk() : final_chunk(false) {
54 WebSocketFrameChunk::~WebSocketFrameChunk() {
57 int GetWebSocketFrameHeaderSize(const WebSocketFrameHeader
& header
) {
58 int extended_length_size
= 0;
59 if (header
.payload_length
> kMaxPayloadLengthWithoutExtendedLengthField
&&
60 header
.payload_length
<= kuint16max
) {
61 extended_length_size
= 2;
62 } else if (header
.payload_length
> kuint16max
) {
63 extended_length_size
= 8;
66 return (WebSocketFrameHeader::kBaseHeaderSize
+
67 extended_length_size
+
68 (header
.masked
? WebSocketFrameHeader::kMaskingKeyLength
: 0));
71 int WriteWebSocketFrameHeader(const WebSocketFrameHeader
& header
,
72 const WebSocketMaskingKey
* masking_key
,
75 DCHECK((header
.opcode
& kOpCodeMask
) == header
.opcode
)
76 << "header.opcode must fit to kOpCodeMask.";
77 DCHECK(header
.payload_length
<= static_cast<uint64
>(kint64max
))
78 << "WebSocket specification doesn't allow a frame longer than "
79 << "kint64max (0x7FFFFFFFFFFFFFFF) bytes.";
80 DCHECK_GE(buffer_size
, 0);
82 // WebSocket frame format is as follows:
83 // - Common header (2 bytes)
84 // - Optional extended payload length
85 // (2 or 8 bytes, present if actual payload length is more than 125 bytes)
86 // - Optional masking key (4 bytes, present if MASK bit is on)
87 // - Actual payload (XOR masked with masking key if MASK bit is on)
89 // This function constructs frame header (the first three in the list
92 int header_size
= GetWebSocketFrameHeaderSize(header
);
93 if (header_size
> buffer_size
)
94 return ERR_INVALID_ARGUMENT
;
98 uint8 first_byte
= 0u;
99 first_byte
|= header
.final
? kFinalBit
: 0u;
100 first_byte
|= header
.reserved1
? kReserved1Bit
: 0u;
101 first_byte
|= header
.reserved2
? kReserved2Bit
: 0u;
102 first_byte
|= header
.reserved3
? kReserved3Bit
: 0u;
103 first_byte
|= header
.opcode
& kOpCodeMask
;
104 buffer
[buffer_index
++] = first_byte
;
106 int extended_length_size
= 0;
107 uint8 second_byte
= 0u;
108 second_byte
|= header
.masked
? kMaskBit
: 0u;
109 if (header
.payload_length
<=
110 kMaxPayloadLengthWithoutExtendedLengthField
) {
111 second_byte
|= header
.payload_length
;
112 } else if (header
.payload_length
<= kuint16max
) {
113 second_byte
|= kPayloadLengthWithTwoByteExtendedLengthField
;
114 extended_length_size
= 2;
116 second_byte
|= kPayloadLengthWithEightByteExtendedLengthField
;
117 extended_length_size
= 8;
119 buffer
[buffer_index
++] = second_byte
;
121 // Writes "extended payload length" field.
122 if (extended_length_size
== 2) {
123 uint16 payload_length_16
= static_cast<uint16
>(header
.payload_length
);
124 WriteBigEndian(buffer
+ buffer_index
, payload_length_16
);
125 buffer_index
+= sizeof(uint16
);
126 } else if (extended_length_size
== 8) {
127 WriteBigEndian(buffer
+ buffer_index
, header
.payload_length
);
128 buffer_index
+= sizeof(uint64
);
131 // Writes "masking key" field, if needed.
134 std::copy(masking_key
->key
,
135 masking_key
->key
+ WebSocketFrameHeader::kMaskingKeyLength
,
136 buffer
+ buffer_index
);
137 buffer_index
+= WebSocketFrameHeader::kMaskingKeyLength
;
139 DCHECK(!masking_key
);
142 DCHECK_EQ(header_size
, buffer_index
);
146 WebSocketMaskingKey
GenerateWebSocketMaskingKey() {
147 // Masking keys should be generated from a cryptographically secure random
148 // number generator, which means web application authors should not be able
149 // to guess the next value of masking key.
150 WebSocketMaskingKey masking_key
;
151 base::RandBytes(masking_key
.key
, WebSocketFrameHeader::kMaskingKeyLength
);
155 void MaskWebSocketFramePayload(const WebSocketMaskingKey
& masking_key
,
159 static const size_t kMaskingKeyLength
=
160 WebSocketFrameHeader::kMaskingKeyLength
;
162 DCHECK_GE(data_size
, 0);
164 // Most of the masking is done one word at a time, except for the beginning
165 // and the end of the buffer which may be unaligned. We use size_t to get the
166 // word size for this architecture. We require it be a multiple of
167 // kMaskingKeyLength in size.
168 typedef size_t PackedMaskType
;
169 PackedMaskType packed_mask_key
= 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
,
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
)
204 realigned_mask
+ kMaskingKeyLength
);
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
);
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<PackedMaskType
*>(merged
) ^= packed_mask_key
;
224 MaskWebSocketFramePayloadByBytes(masking_key
,
225 (frame_offset
+ (aligned_end
- data
))