1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
4 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this program; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24
25#include "WebSocketFrame.h"
26#include <wtf/CryptographicallyRandomNumber.h>
27#include <wtf/MathExtras.h>
28#include <wtf/text/StringConcatenateNumbers.h>
29
30namespace WebCore {
31
32// Constants for hybi-10 frame format.
33const unsigned char finalBit = 0x80;
34const unsigned char compressBit = 0x40;
35const unsigned char reserved2Bit = 0x20;
36const unsigned char reserved3Bit = 0x10;
37const unsigned char opCodeMask = 0xF;
38const unsigned char maskBit = 0x80;
39const unsigned char payloadLengthMask = 0x7F;
40const size_t maxPayloadLengthWithoutExtendedLengthField = 125;
41const size_t payloadLengthWithTwoByteExtendedLengthField = 126;
42const size_t payloadLengthWithEightByteExtendedLengthField = 127;
43const size_t maskingKeyWidthInBytes = 4;
44
45bool WebSocketFrame::needsExtendedLengthField(size_t payloadLength)
46{
47 return payloadLength > maxPayloadLengthWithoutExtendedLengthField;
48}
49
50WebSocketFrame::ParseFrameResult WebSocketFrame::parseFrame(char* data, size_t dataLength, WebSocketFrame& frame, const char*& frameEnd, String& errorString)
51{
52 char* p = data;
53 const char* bufferEnd = data + dataLength;
54
55 if (dataLength < 2)
56 return FrameIncomplete;
57
58 unsigned char firstByte = *p++;
59 unsigned char secondByte = *p++;
60
61 bool final = firstByte & finalBit;
62 bool compress = firstByte & compressBit;
63 bool reserved2 = firstByte & reserved2Bit;
64 bool reserved3 = firstByte & reserved3Bit;
65 unsigned char opCode = firstByte & opCodeMask;
66
67 bool masked = secondByte & maskBit;
68 uint64_t payloadLength64 = secondByte & payloadLengthMask;
69 if (payloadLength64 > maxPayloadLengthWithoutExtendedLengthField) {
70 int extendedPayloadLengthSize;
71 if (payloadLength64 == payloadLengthWithTwoByteExtendedLengthField)
72 extendedPayloadLengthSize = 2;
73 else {
74 ASSERT(payloadLength64 == payloadLengthWithEightByteExtendedLengthField);
75 extendedPayloadLengthSize = 8;
76 }
77 if (bufferEnd - p < extendedPayloadLengthSize)
78 return FrameIncomplete;
79 payloadLength64 = 0;
80 for (int i = 0; i < extendedPayloadLengthSize; ++i) {
81 payloadLength64 <<= 8;
82 payloadLength64 |= static_cast<unsigned char>(*p++);
83 }
84 if (extendedPayloadLengthSize == 2 && payloadLength64 <= maxPayloadLengthWithoutExtendedLengthField) {
85 errorString = "The minimal number of bytes MUST be used to encode the length";
86 return FrameError;
87 }
88 if (extendedPayloadLengthSize == 8 && payloadLength64 <= 0xFFFF) {
89 errorString = "The minimal number of bytes MUST be used to encode the length";
90 return FrameError;
91 }
92 }
93
94 static const uint64_t maxPayloadLength = UINT64_C(0x7FFFFFFFFFFFFFFF);
95 size_t maskingKeyLength = masked ? maskingKeyWidthInBytes : 0;
96 if (payloadLength64 > maxPayloadLength || payloadLength64 + maskingKeyLength > std::numeric_limits<size_t>::max()) {
97 errorString = makeString("WebSocket frame length too large: ", payloadLength64, " bytes");
98 return FrameError;
99 }
100 size_t payloadLength = static_cast<size_t>(payloadLength64);
101
102 if (static_cast<size_t>(bufferEnd - p) < maskingKeyLength + payloadLength)
103 return FrameIncomplete;
104
105 if (masked) {
106 const char* maskingKey = p;
107 char* payload = p + maskingKeyWidthInBytes;
108 for (size_t i = 0; i < payloadLength; ++i)
109 payload[i] ^= maskingKey[i % maskingKeyWidthInBytes]; // Unmask the payload.
110 }
111
112 frame.opCode = static_cast<WebSocketFrame::OpCode>(opCode);
113 frame.final = final;
114 frame.compress = compress;
115 frame.reserved2 = reserved2;
116 frame.reserved3 = reserved3;
117 frame.masked = masked;
118 frame.payload = p + maskingKeyLength;
119 frame.payloadLength = payloadLength;
120 frameEnd = p + maskingKeyLength + payloadLength;
121 return FrameOK;
122}
123
124static void appendFramePayload(const WebSocketFrame& frame, Vector<char>& frameData)
125{
126 size_t maskingKeyStart = 0;
127 if (frame.masked) {
128 maskingKeyStart = frameData.size();
129 frameData.grow(frameData.size() + maskingKeyWidthInBytes); // Add placeholder for masking key. Will be overwritten.
130 }
131
132 size_t payloadStart = frameData.size();
133 frameData.append(frame.payload, frame.payloadLength);
134
135 if (frame.masked) {
136 cryptographicallyRandomValues(frameData.data() + maskingKeyStart, maskingKeyWidthInBytes);
137 for (size_t i = 0; i < frame.payloadLength; ++i)
138 frameData[payloadStart + i] ^= frameData[maskingKeyStart + i % maskingKeyWidthInBytes];
139 }
140}
141
142void WebSocketFrame::makeFrameData(Vector<char>& frameData)
143{
144 ASSERT(!(opCode & ~opCodeMask)); // Checks whether "opCode" fits in the range of opCodes.
145
146 frameData.resize(2);
147 frameData.at(0) = (final ? finalBit : 0) | (compress ? compressBit : 0) | opCode;
148 frameData.at(1) = masked ? maskBit : 0;
149
150 if (payloadLength <= maxPayloadLengthWithoutExtendedLengthField)
151 frameData.at(1) |= payloadLength;
152 else if (payloadLength <= 0xFFFF) {
153 frameData.at(1) |= payloadLengthWithTwoByteExtendedLengthField;
154 frameData.append((payloadLength & 0xFF00) >> 8);
155 frameData.append(payloadLength & 0xFF);
156 } else {
157 frameData.at(1) |= payloadLengthWithEightByteExtendedLengthField;
158 char extendedPayloadLength[8];
159 size_t remaining = payloadLength;
160 // Fill the length into extendedPayloadLength in the network byte order.
161 for (int i = 0; i < 8; ++i) {
162 extendedPayloadLength[7 - i] = remaining & 0xFF;
163 remaining >>= 8;
164 }
165 ASSERT(!remaining);
166 frameData.append(extendedPayloadLength, 8);
167 }
168
169 appendFramePayload(*this, frameData);
170}
171
172WebSocketFrame::WebSocketFrame(OpCode opCode, bool final, bool compress, bool masked, const char* payload, size_t payloadLength)
173 : opCode(opCode)
174 , final(final)
175 , compress(compress)
176 , reserved2(false)
177 , reserved3(false)
178 , masked(masked)
179 , payload(payload)
180 , payloadLength(payloadLength)
181{
182}
183
184} // namespace WebCore
185