Initial Commit

This commit is contained in:
Kirigaya Kazuto 2018-09-07 16:07:30 +08:00
commit 7764a6ab0b
9 changed files with 811 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
Debug/
Release/
*.vcxproj
*.vcxproj.*

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "GSock-Pro"]
path = GSock-Pro
url = http://kiritow.com:3000/kiritow/GSock-Pro

1
GSock-Pro Submodule

@ -0,0 +1 @@
Subproject commit bd3bd376f6903ca681d8eba15bfbb30f1c6392f7

BIN
KChatWSServer.cpp Normal file

Binary file not shown.

164
base64.hpp Normal file
View File

@ -0,0 +1,164 @@
#include <string>
#include <vector>
// base64 encode list
// 0-25 A-Z
// 26-51 a-z
// 52-61 0-9
inline char _base64_encode_char(unsigned int c, char c62, char c63)
{
static const char* _base64_encode_list =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789";
if (c < 62) return _base64_encode_list[c];
else if (c == 62) return c62;
else return c63;
}
// base64 decode list
// A-Z 0-25
// a-z 26-51
// 0-9 52-61
inline unsigned int _base64_decode_char(char c, char c62, char c63)
{
if (c >= 'A'&&c <= 'Z') return c - 'A';
else if (c >= 'a'&&c <= 'z') return c - 'a' + 26;
else if (c >= '0'&&c <= '9') return c - '0' + 52;
else if (c == c62) return 62;
else return 63;
}
std::string base64_encode(const unsigned char* data, int len, char c62, char c63, char cfill)
{
std::string ans;
int nGroup = len / 3;
int nLeft = len % 3;
for (int i = 0; i < nGroup; i++)
{
unsigned int xa = data[i * 3];
unsigned int xb = data[i * 3 + 1];
unsigned int xc = data[i * 3 + 2];
unsigned int ya = (xa & 0xfc) >> 2; // 11111100,...
unsigned int yb = ((xa & 0x3) << 4) | ((xb & 0xf0) >> 4); // 00000011,11110000,....
unsigned int yc = ((xb & 0xf) << 2) | ((xc & 0xc0) >> 6); // ...,00001111,11000000
unsigned int yd = xc & 0x3f; // ...,00111111
ans.push_back(_base64_encode_char(ya, c62, c63));
ans.push_back(_base64_encode_char(yb, c62, c63));
ans.push_back(_base64_encode_char(yc, c62, c63));
ans.push_back(_base64_encode_char(yd, c62, c63));
}
switch (nLeft)
{
case 1:
{
unsigned int xa = data[nGroup * 3];
unsigned int ya = (xa & 0xfc) >> 2;
unsigned int yb = (xa & 0x3) << 4;
ans.push_back(_base64_encode_char(ya, c62, c63));
ans.push_back(_base64_encode_char(yb, c62, c63));
ans.push_back(cfill);
ans.push_back(cfill);
break;
}
case 2:
{
unsigned int xa = data[nGroup * 3];
unsigned int xb = data[nGroup * 3 + 1];
unsigned int ya = (xa & 0xfc) >> 2;
unsigned int yb = ((xa & 0x3) << 4) | ((xb & 0xf0) >> 4);
unsigned int yc = (xb & 0xf) << 2;
ans.push_back(_base64_encode_char(ya, c62, c63));
ans.push_back(_base64_encode_char(yb, c62, c63));
ans.push_back(_base64_encode_char(yc, c62, c63));
ans.push_back(cfill);
break;
}
}
return ans;
}
std::vector<unsigned char> base64_decode(const std::string& str, char c62, char c63, char cfill)
{
std::vector<unsigned char> data;
int nGroup = str.size() / 4;
for (int i = 0; i < nGroup; i++)
{
unsigned int ya = _base64_decode_char(str[i * 4], c62, c63);
unsigned int yb = _base64_decode_char(str[i * 4 + 1], c62, c63);
unsigned int xa = (ya << 2) | (yb >> 4); // 6 + 2
data.push_back(xa);
if (str[i * 4 + 3] != cfill)
{
unsigned int yc = _base64_decode_char(str[i * 4 + 2], c62, c63);
unsigned int yd = _base64_decode_char(str[i * 4 + 3], c62, c63);
unsigned int xb = ((yb & 0xf) << 4) | (yc >> 2); // 4 + 4
unsigned int xc = ((yc & 0x3) << 6) | yd; // 2 + 6
data.push_back(xb);
data.push_back(xc);
}
else // (str[i * 4 + 3] == cfill)
{
if (str[i * 4 + 2] == cfill)
{
unsigned int xb = (yb & 0xf) << 4; // 4 + 4
data.push_back(xb);
}
else
{
unsigned int yc = _base64_decode_char(str[i * 4 + 2], c62, c63);
unsigned int xb = ((yb & 0xf) << 4) | (yc >> 2); // 4 + 4
unsigned int xc = (yc & 0x3) << 6; // 2 + 6
data.push_back(xb);
data.push_back(xc);
}
}
}
return data;
}
std::string base64_encode_std(const unsigned char* data, int len)
{
return base64_encode(data, len, '+', '/', '=');
}
std::string base64_encode_std(const std::string& data)
{
return base64_encode_std((const unsigned char*)data.data(), data.size());
}
std::string base64_encode_url(const unsigned char* data, int len)
{
return base64_encode(data, len, '-', '_', '=');
}
std::string base64_encode_url(const std::string& data)
{
return base64_encode_std((const unsigned char*)data.data(), data.size());
}
std::vector<unsigned char> base64_decode_std(const std::string& str)
{
return base64_decode(str, '+', '/', '=');
}
std::vector<unsigned char> base64_decode_url(const std::string& str)
{
return base64_decode(str, '-', '_', '=');
}

311
sha1.cpp Normal file
View File

@ -0,0 +1,311 @@
/*
sha1.cpp - source code of
============
SHA-1 in C++
============
100% Public Domain.
Original C Code
-- Steve Reid <steve@edmweb.com>
Small changes to fit into bglibs
-- Bruce Guenter <bruce@untroubled.org>
Translation to simpler C++ Code
-- Volker Diels-Grabsch <v@njh.eu>
Safety fixes
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
*/
#include "sha1.h"
#include <sstream>
#include <iomanip>
#include <fstream>
static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */
static const size_t BLOCK_BYTES = BLOCK_INTS * 4;
static void reset(uint32_t digest[], std::string &buffer, uint64_t &transforms)
{
/* SHA1 initialization constants */
digest[0] = 0x67452301;
digest[1] = 0xefcdab89;
digest[2] = 0x98badcfe;
digest[3] = 0x10325476;
digest[4] = 0xc3d2e1f0;
/* Reset counters */
buffer = "";
transforms = 0;
}
static uint32_t rol(const uint32_t value, const size_t bits)
{
return (value << bits) | (value >> (32 - bits));
}
static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i)
{
return rol(block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i], 1);
}
/*
* (R0+R1), R2, R3, R4 are the different operations used in SHA1
*/
static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
z += ((w&(x^y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5);
w = rol(w, 30);
}
static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += ((w&(x^y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5);
w = rol(w, 30);
}
static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5);
w = rol(w, 30);
}
static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += (((w | x)&y) | (w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
w = rol(w, 30);
}
static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5);
w = rol(w, 30);
}
/*
* Hash a single 512-bit block. This is the core of the algorithm.
*/
static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS], uint64_t &transforms)
{
/* Copy digest[] to working vars */
uint32_t a = digest[0];
uint32_t b = digest[1];
uint32_t c = digest[2];
uint32_t d = digest[3];
uint32_t e = digest[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(block, a, b, c, d, e, 0);
R0(block, e, a, b, c, d, 1);
R0(block, d, e, a, b, c, 2);
R0(block, c, d, e, a, b, 3);
R0(block, b, c, d, e, a, 4);
R0(block, a, b, c, d, e, 5);
R0(block, e, a, b, c, d, 6);
R0(block, d, e, a, b, c, 7);
R0(block, c, d, e, a, b, 8);
R0(block, b, c, d, e, a, 9);
R0(block, a, b, c, d, e, 10);
R0(block, e, a, b, c, d, 11);
R0(block, d, e, a, b, c, 12);
R0(block, c, d, e, a, b, 13);
R0(block, b, c, d, e, a, 14);
R0(block, a, b, c, d, e, 15);
R1(block, e, a, b, c, d, 0);
R1(block, d, e, a, b, c, 1);
R1(block, c, d, e, a, b, 2);
R1(block, b, c, d, e, a, 3);
R2(block, a, b, c, d, e, 4);
R2(block, e, a, b, c, d, 5);
R2(block, d, e, a, b, c, 6);
R2(block, c, d, e, a, b, 7);
R2(block, b, c, d, e, a, 8);
R2(block, a, b, c, d, e, 9);
R2(block, e, a, b, c, d, 10);
R2(block, d, e, a, b, c, 11);
R2(block, c, d, e, a, b, 12);
R2(block, b, c, d, e, a, 13);
R2(block, a, b, c, d, e, 14);
R2(block, e, a, b, c, d, 15);
R2(block, d, e, a, b, c, 0);
R2(block, c, d, e, a, b, 1);
R2(block, b, c, d, e, a, 2);
R2(block, a, b, c, d, e, 3);
R2(block, e, a, b, c, d, 4);
R2(block, d, e, a, b, c, 5);
R2(block, c, d, e, a, b, 6);
R2(block, b, c, d, e, a, 7);
R3(block, a, b, c, d, e, 8);
R3(block, e, a, b, c, d, 9);
R3(block, d, e, a, b, c, 10);
R3(block, c, d, e, a, b, 11);
R3(block, b, c, d, e, a, 12);
R3(block, a, b, c, d, e, 13);
R3(block, e, a, b, c, d, 14);
R3(block, d, e, a, b, c, 15);
R3(block, c, d, e, a, b, 0);
R3(block, b, c, d, e, a, 1);
R3(block, a, b, c, d, e, 2);
R3(block, e, a, b, c, d, 3);
R3(block, d, e, a, b, c, 4);
R3(block, c, d, e, a, b, 5);
R3(block, b, c, d, e, a, 6);
R3(block, a, b, c, d, e, 7);
R3(block, e, a, b, c, d, 8);
R3(block, d, e, a, b, c, 9);
R3(block, c, d, e, a, b, 10);
R3(block, b, c, d, e, a, 11);
R4(block, a, b, c, d, e, 12);
R4(block, e, a, b, c, d, 13);
R4(block, d, e, a, b, c, 14);
R4(block, c, d, e, a, b, 15);
R4(block, b, c, d, e, a, 0);
R4(block, a, b, c, d, e, 1);
R4(block, e, a, b, c, d, 2);
R4(block, d, e, a, b, c, 3);
R4(block, c, d, e, a, b, 4);
R4(block, b, c, d, e, a, 5);
R4(block, a, b, c, d, e, 6);
R4(block, e, a, b, c, d, 7);
R4(block, d, e, a, b, c, 8);
R4(block, c, d, e, a, b, 9);
R4(block, b, c, d, e, a, 10);
R4(block, a, b, c, d, e, 11);
R4(block, e, a, b, c, d, 12);
R4(block, d, e, a, b, c, 13);
R4(block, c, d, e, a, b, 14);
R4(block, b, c, d, e, a, 15);
/* Add the working vars back into digest[] */
digest[0] += a;
digest[1] += b;
digest[2] += c;
digest[3] += d;
digest[4] += e;
/* Count the number of transformations */
transforms++;
}
static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS])
{
/* Convert the std::string (byte buffer) to a uint32_t array (MSB) */
for (size_t i = 0; i < BLOCK_INTS; i++)
{
block[i] = (buffer[4 * i + 3] & 0xff)
| (buffer[4 * i + 2] & 0xff) << 8
| (buffer[4 * i + 1] & 0xff) << 16
| (buffer[4 * i + 0] & 0xff) << 24;
}
}
SHA1::SHA1()
{
reset(digest, buffer, transforms);
}
void SHA1::update(const std::string &s)
{
std::istringstream is(s);
update(is);
}
void SHA1::update(std::istream &is)
{
while (true)
{
char sbuf[BLOCK_BYTES];
is.read(sbuf, BLOCK_BYTES - buffer.size());
buffer.append(sbuf, (std::size_t)is.gcount());
if (buffer.size() != BLOCK_BYTES)
{
return;
}
uint32_t block[BLOCK_INTS];
buffer_to_block(buffer, block);
transform(digest, block, transforms);
buffer.clear();
}
}
/*
* Add padding and return the message digest.
*/
int SHA1::final(uint32_t* arr)
{
/* Total number of hashed bits */
uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8;
/* Padding */
buffer += (char)0x80;
size_t orig_size = buffer.size();
while (buffer.size() < BLOCK_BYTES)
{
buffer += (char)0x00;
}
uint32_t block[BLOCK_INTS];
buffer_to_block(buffer, block);
if (orig_size > BLOCK_BYTES - 8)
{
transform(digest, block, transforms);
for (size_t i = 0; i < BLOCK_INTS - 2; i++)
{
block[i] = 0;
}
}
/* Append total_bits, split this uint64_t into two uint32_t */
block[BLOCK_INTS - 1] = (uint32_t)total_bits;
block[BLOCK_INTS - 2] = (uint32_t)(total_bits >> 32);
transform(digest, block, transforms);
memcpy(arr, digest, sizeof(digest));
/* Reset for next run */
reset(digest, buffer, transforms);
return sizeof(digest) / sizeof(digest[0]);
}
std::string SHA1::final()
{
uint32_t arr[5];
final(arr);
/* Hex std::string */
std::ostringstream result;
for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
result << std::hex << std::setfill('0') << std::setw(8);
result << arr[i];
}
return result.str();
}
std::string SHA1::from_file(const std::string &filename)
{
std::ifstream stream(filename.c_str(), std::ios::binary);
SHA1 checksum;
checksum.update(stream);
return checksum.final();
}

43
sha1.h Normal file
View File

@ -0,0 +1,43 @@
/*
sha1.hpp - header of
============
SHA-1 in C++
============
100% Public Domain.
Original C Code
-- Steve Reid <steve@edmweb.com>
Small changes to fit into bglibs
-- Bruce Guenter <bruce@untroubled.org>
Translation to simpler C++ Code
-- Volker Diels-Grabsch <v@njh.eu>
Safety fixes
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
*/
#ifndef SHA1_HPP
#define SHA1_HPP
#include <cstdint>
#include <iostream>
#include <string>
class SHA1
{
public:
SHA1();
void update(const std::string &s);
void update(std::istream &is);
std::string final();
int final(uint32_t* arr);
static std::string from_file(const std::string &filename);
private:
uint32_t digest[5];
std::string buffer;
uint64_t transforms;
};
#endif /* SHA1_HPP */

245
websocket.cpp Normal file
View File

@ -0,0 +1,245 @@
#include "websocket.h"
#include "gsock_helper.h"
#include <vector>
#include <queue>
#include "base64.hpp"
#include "sha1.h"
#include <WinSock2.h>
using namespace std;
int HandleKey(sock& s, const string& key)
{
string server_key = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1 sha;
sha.update(server_key);
uint32_t arr[5];
sha.final(arr);
for (int i = 0; i < 5; i++) arr[i] = htonl(arr[i]); // 重要
string response_key = base64_encode_std((const unsigned char*)arr, sizeof(arr));
sock_helper sp(s);
string response_header = string("HTTP/1.1 101 Switching Protocols\r\n") +
"Connection: upgrade\r\n" +
"Sec-WebSocket-Accept: " + response_key + "\r\n" +
"Upgrade: websocket\r\n\r\n";
if (sp.sendall(response_header) <= 0)
{
return -1;
}
else
{
return 0;
}
}
int Handshake(sock& s)
{
sock_helper sp(s);
vector<string> lines;
while (true)
{
string str;
int ret = sp.recvline(str);
if (ret <= 0) return -1;
if (str.empty()) break;
lines.push_back(str);
// cout << str << endl;
}
for (auto& str : lines)
{
string target("Sec-WebSocket-Key");
if (str.find(target) != string::npos)
{
string key = str.substr(str.find(target) + 19, 24);
if (HandleKey(s, key) == 0)
{
// Handshake OK
return 0;
}
else
{
// Handshake Failed: Key is not ok
return -1;
}
}
}
// Not websocket protocol
return -2;
}
int ReadFrame(sock& s, WSFrame& f)
{
sock_helper sp(s);
unsigned char c;
if (s.recv(&c, 1) <= 0) return -1;
f.fin = c & 0x80; // 1000 0000
f.rsv1 = c & 0x40; // 0100 0000
f.rsv2 = c & 0x20; // 0010 0000
f.rsv3 = c & 0x10; // 0001 0000
f.opcode = c & 0xF; // 0000 1111
if (s.recv(&c, 1) <= 0) return -1;
f.ismask = c & 0x80; // 1000 0000
int payload_head = c & 0x7F; // 0111 1111
if (payload_head < 126)
{
f.len = payload_head;
}
else if (payload_head == 126)
{
uint16_t x;
if (sp.recvall(&x, sizeof(x)) <= 0) return -1;
f.len = ntohs(x);
}
else if (payload_head == 127)
{
uint64_t x;
if (sp.recvall(&x, sizeof(x)) <= 0) return -1;
f.len = ntohll(x);
}
if (f.ismask)
{
if (sp.recvall(f.mask, sizeof(f.mask)) <= 0) return -1;
}
unique_ptr<char[]> xp(new char[f.len]);
if (xp.get() == nullptr) return -2;
memset(xp.get(), 0, f.len);
if (sp.recvall(xp.get(), f.len) <= 0) return -1;
if (f.ismask)
{
// 处理掩码问题
for (unsigned long long i = 0; i < f.len; i++)
{
xp[i] ^= f.mask[i % 4];
}
}
f.data = string(xp.get(), f.len);
return 0;
}
int SendFrame(sock& s, const WSFrame& f)
{
sock_helper sp(s);
unsigned char c = 0;
if (f.fin) c = 0x80; // 1000 0000
if (f.rsv1) c |= 0x40; // 0100 0000
if (f.rsv2) c |= 0x20; // 0010 0000
if (f.rsv3) c |= 0x10; // 0001 0000
c |= (f.opcode & 0xF); // 0000 1111
if (s.send(&c, 1) <= 0) return -1;
if (f.ismask) c = 0x80; // 1000 0000
else c = 0;
if (f.len < 126)
{
c |= (f.len & 0x7F);
if (s.send(&c, 1) <= 0) return -1;
}
else if (f.len < (2 << 16))
{
c |= 126;
if (s.send(&c, 1) <= 0) return -1;
uint16_t len = f.len;
if (sp.sendall(&len, sizeof(len)) <= 0) return -1;
}
else
{
c |= 127;
if (s.send(&c, 1) <= 0) return -1;
uint64_t len = f.len;
if (sp.sendall(&len, sizeof(len)) <= 0) return -1;
}
if (f.ismask)
{
if (sp.sendall(&(f.mask), sizeof(f.mask)) <= 0) return -1;
vector<char> vec(f.data.begin(), f.data.end());
for (uint64_t i = 0; i < f.len; i++)
{
vec[i] ^= f.mask[i % 4];
}
if (sp.sendall(vec.data(), vec.size()) <= 0) return -1;
}
else
{
if (sp.sendall(f.data) <= 0) return -1;
}
return 0;
}
int SendPong(sock& s, const WSFrame& ping)
{
cout << "Sending pong to " << (&s) << endl;
WSFrame f;
f.fin = true;
f.rsv1 = false;
f.rsv2 = false;
f.rsv3 = false;
f.ismask = false;
f.len = ping.len;
f.data = ping.data;
f.opcode = 0xA;
return SendFrame(s, f);
}
int ReadMsg(sock& s, string& out_data)
{
string data;
WSFrame f;
int pack_cnt = 0;
while (true)
{
int ret = ReadFrame(s, f);
if (ret < 0) return -1;
if (f.opcode == 0x9) // ping包则回复一个pong包
{
cout << "Received ping from " << (&s) << endl;
if (SendPong(s, f) < 0) return -2;
continue;
}
else if (f.opcode == 0xA) // pong包则什么都不做
{
cout << "Received pong from " << (&s) << endl;
continue;
}
else if (f.opcode == 0x8) // 连接主动关闭
{
cout << "Websocket is closing " << (&s) << endl;
return 0;
}
pack_cnt++;
data.append(f.data);
if (f.fin) break;
}
out_data = data;
return pack_cnt;
}
int SendMsg(sock& s, const string& data, bool is_text)
{
WSFrame f;
f.fin = true;
f.rsv1 = false;
f.rsv2 = false;
f.rsv3 = false;
f.ismask = false;
f.len = data.size();
f.data = data;
f.opcode = is_text ? 0x1 : 0x2;
if (SendFrame(s, f) < 0)
{
return -1;
}
else
{
return 1;
}
}

40
websocket.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <string>
#include "gsock.h"
int HandleKey(sock& s, const std::string& key);
int Handshake(sock& s);
struct WSFrame
{
// 1 bit
bool fin;
// 1 bit
bool rsv1, rsv2, rsv3;
// 0x0 附加数据帧
// 0x1 文本数据帧
// 0x2 二进制数据帧
// 0x3~7 保留
// 0x8 连接关闭
// 0x9 ping
// 0xA pong
// 0xB~F 保留
// 4 bit
int opcode;
// 1 bit
bool ismask;
unsigned long long len;
// 4 byte
char mask[4];
// data是未经过编码处理的数据(如果源数据是带掩码的则已经经过解掩码处理)
std::string data;
};
int ReadFrame(sock& s, WSFrame& f);
int SendFrame(sock& s, const WSFrame& f);
int SendPong(sock& s, const WSFrame& ping);
int ReadMsg(sock& s, std::string& out_data);
int SendMsg(sock& s, const std::string& data, bool is_text = true);