I am writing a websocket server in C++ and am not able to get the handshake to work. Chrome reports the error is due to a bad accept header, but I believe the value to be correct.
As one example exchange, the client sends the following key:
Sec-WebSocket-Key: ypX0m2zum/pt80mxlVo8PA==
and my server sends back:
Sec-WebSocket-Accept: Kl4mnqm5QA6bBmGf3EAN0nyGXws=
I have tested my server against the example in the RFC and it checks out. I don't know why its not being accepted. My theory is that I must be doing something else that generates the same error as a bad accept value.
Here is different request from a wireshark capture:
Hypertext Transfer Protocol
GET /websocket HTTP/1.1\r\n
Host: 127.0.0.1:8443\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
Upgrade: websocket\r\n
Origin: chrome-extension://eajaahbjpnhghjcdaclbkeamlkepinbl\r\n
Sec-WebSocket-Version: 13\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: en-US,en;q=0.9\r\n
Sec-WebSocket-Key: +zJ3/KI/Zrumgh+AjxopRQ==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
\r\n
[Full request URI: http://127.0.0.1:8443/websocket]
[HTTP request 1/1]
[Response in frame: 6]
And here is the response:
Hypertext Transfer Protocol
HTTP/1.1 101 Switching Protocols\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: anTEIFyI/gTepr8Q3okBj81M2/4=\r\n
\r\n
[HTTP response 1/1]
[Time since request: 0.000245010 seconds]
[Request in frame: 4]
Can someone tell me what is wrong with the response? Is my accept value incorrect?
EDIT 1:
The code I use to create the response value. The websocket_key is grabbed from the request prior to this.
const char *magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
int pre_hash_size = 36 + websocket_key.size();
char pre_hash[pre_hash_size];
memcpy(pre_hash, websocket_key.c_str(), websocket_key.size());
memcpy(pre_hash + websocket_key.size(), magic_string, 36);
unique_ptr<Botan::HashFunction> hash1(Botan::HashFunction::create("SHA-1"));
Botan::secure_vector<uint8_t> post_hash = hash1->process(reinterpret_cast<const uint8_t *>(pre_hash), pre_hash_size);
string accept_response = base64_encode(post_hash.data(), post_hash.size());
Here is the base 64 function:
/*
base64.cpp and base64.h
base64 encoding and decoding with C++.
Version: 1.01.00
Copyright (C) 2004-2017 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger [email protected]
*/
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string base64_encode(unsigned char const *bytes_to_encode, unsigned int in_len)
{
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--)
{
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3)
{
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}