ekt: remove bad ws implementation

This commit is contained in:
2025-09-04 18:47:05 +02:00
parent 05f68962cd
commit 0f87e7510e
6 changed files with 1 additions and 996 deletions

View File

@ -1,69 +0,0 @@
/*
Copyright (c) 2014 Malte Hildingsson, malte (at) afterwi.se
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef AW_BASE64_H
#define AW_BASE64_H
#include <stddef.h>
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define PROGMEM
#endif
#ifdef __cplusplus
extern "C" {
#endif
static inline size_t base64len(size_t n) {
return (n + 2) / 3 * 4;
}
static size_t base64(char *buf, size_t nbuf, const unsigned char *p, size_t n) {
const char t[64] PROGMEM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t i, m = base64len(n);
unsigned x;
if (nbuf >= m)
for (i = 0; i < n; ++i) {
x = p[i] << 0x10;
x |= (++i < n ? p[i] : 0) << 0x08;
x |= (++i < n ? p[i] : 0) << 0x00;
*buf++ = t[x >> 3 * 6 & 0x3f];
*buf++ = t[x >> 2 * 6 & 0x3f];
*buf++ = (((n - 0 - i) >> 31) & '=') |
(~((n - 0 - i) >> 31) & t[x >> 1 * 6 & 0x3f]);
*buf++ = (((n - 1 - i) >> 31) & '=') |
(~((n - 1 - i) >> 31) & t[x >> 0 * 6 & 0x3f]);
}
return m;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AW_BASE64_H */

View File

@ -1,137 +0,0 @@
/*
Copyright (c) 2014 Malte Hildingsson, malte (at) afterwi.se
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef AW_SHA1_H
#define AW_SHA1_H
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wparentheses"
#include <string.h>
#if _MSC_VER
# define _sha1_restrict __restrict
#else
# define _sha1_restrict __restrict__
#endif
#define SHA1_SIZE 20
#ifdef __cplusplus
extern "C" {
#endif
static inline void sha1mix(unsigned *_sha1_restrict r, unsigned *_sha1_restrict w) {
unsigned a = r[0];
unsigned b = r[1];
unsigned c = r[2];
unsigned d = r[3];
unsigned e = r[4];
unsigned t, i = 0;
#define rol(x,s) ((x) << (s) | (unsigned) (x) >> (32 - (s)))
#define mix(f,v) do { \
t = (f) + (v) + rol(a, 5) + e + w[i & 0xf]; \
e = d; \
d = c; \
c = rol(b, 30); \
b = a; \
a = t; \
} while (0)
for (; i < 16; ++i)
mix(d ^ (b & (c ^ d)), 0x5a827999);
for (; i < 20; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix(d ^ (b & (c ^ d)), 0x5a827999);
}
for (; i < 40; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix(b ^ c ^ d, 0x6ed9eba1);
}
for (; i < 60; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix((b & c) | (d & (b | c)), 0x8f1bbcdc);
}
for (; i < 80; ++i) {
w[i & 0xf] = rol(w[i + 13 & 0xf] ^ w[i + 8 & 0xf] ^ w[i + 2 & 0xf] ^ w[i & 0xf], 1);
mix(b ^ c ^ d, 0xca62c1d6);
}
#undef mix
#undef rol
r[0] += a;
r[1] += b;
r[2] += c;
r[3] += d;
r[4] += e;
}
static void sha1(unsigned char h[static SHA1_SIZE], const void *_sha1_restrict p, size_t n) {
size_t i = 0;
unsigned w[16], r[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
for (; i < (n & ~0x3f);) {
do w[i >> 2 & 0xf] =
((const unsigned char *) p)[i + 3] << 0x00 |
((const unsigned char *) p)[i + 2] << 0x08 |
((const unsigned char *) p)[i + 1] << 0x10 |
((const unsigned char *) p)[i + 0] << 0x18;
while ((i += 4) & 0x3f);
sha1mix(r, w);
}
memset(w, 0, sizeof w);
for (; i < n; ++i)
w[i >> 2 & 0xf] |= ((const unsigned char *) p)[i] << ((3 ^ i & 3) << 3);
w[i >> 2 & 0xf] |= 0x80 << ((3 ^ i & 3) << 3);
if ((n & 0x3f) > 56) {
sha1mix(r, w);
memset(w, 0, sizeof w);
}
w[15] = n << 3;
sha1mix(r, w);
for (i = 0; i < 5; ++i)
h[(i << 2) + 0] = (unsigned char) (r[i] >> 0x18),
h[(i << 2) + 1] = (unsigned char) (r[i] >> 0x10),
h[(i << 2) + 2] = (unsigned char) (r[i] >> 0x08),
h[(i << 2) + 3] = (unsigned char) (r[i] >> 0x00);
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#pragma GCC diagnostic pop
#endif /* AW_SHA1_H */

View File

@ -1,328 +0,0 @@
/*
* Copyright (c) 2014 Putilov Andrey
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <stdio.h>
#include <windows.h>
#include <wtypes.h>
#include <winsock2.h>
#include "websocket.h"
static char rn[] PROGMEM = "\r\n";
void nullHandshake(struct handshake *hs)
{
hs->host = NULL;
hs->origin = NULL;
hs->resource = NULL;
hs->key = NULL;
hs->frameType = WS_EMPTY_FRAME;
}
void freeHandshake(struct handshake *hs)
{
if (hs->host) {
free(hs->host);
}
if (hs->origin) {
free(hs->origin);
}
if (hs->resource) {
free(hs->resource);
}
if (hs->key) {
free(hs->key);
}
nullHandshake(hs);
}
static char* getUptoLinefeed(const char *startFrom)
{
char *writeTo = NULL;
uint8_t newLength = strstr_P(startFrom, rn) - startFrom;
assert(newLength);
writeTo = (char *)malloc(newLength+1); //+1 for '\x00'
assert(writeTo);
memcpy(writeTo, startFrom, newLength);
writeTo[ newLength ] = 0;
return writeTo;
}
enum wsFrameType wsParseHandshake(const uint8_t *inputFrame, size_t inputLength,
struct handshake *hs)
{
const char *inputPtr = (const char *)inputFrame;
const char *endPtr = (const char *)inputFrame + inputLength;
if (!strstr((const char *)inputFrame, "\r\n\r\n"))
return WS_INCOMPLETE_FRAME;
if (memcmp_P(inputFrame, PSTR("GET "), 4) != 0)
return WS_ERROR_FRAME;
// measure resource size
char *first = strchr((const char *)inputFrame, ' ');
if (!first)
return WS_ERROR_FRAME;
first++;
char *second = strchr(first, ' ');
if (!second)
return WS_ERROR_FRAME;
if (hs->resource) {
free(hs->resource);
hs->resource = NULL;
}
hs->resource = (char *)malloc(second - first + 1); // +1 is for \x00 symbol
assert(hs->resource);
if (sscanf_P(inputPtr, PSTR("GET %s HTTP/1.1\r\n"), hs->resource) != 1)
return WS_ERROR_FRAME;
inputPtr = strstr_P(inputPtr, rn) + 2;
/*
parse next lines
*/
#define prepare(x) do {if (x) { free(x); x = NULL; }} while(0)
#define strtolower(x) do { int i; for (i = 0; x[i]; i++) x[i] = tolower(x[i]); } while(0)
uint8_t connectionFlag = FALSE;
uint8_t upgradeFlag = FALSE;
uint8_t subprotocolFlag = FALSE;
uint8_t versionMismatch = FALSE;
while (inputPtr < endPtr && inputPtr[0] != '\r' && inputPtr[1] != '\n') {
if (memcmp_P(inputPtr, hostField, strlen_P(hostField)) == 0) {
inputPtr += strlen_P(hostField);
prepare(hs->host);
hs->host = getUptoLinefeed(inputPtr);
} else
if (memcmp_P(inputPtr, originField, strlen_P(originField)) == 0) {
inputPtr += strlen_P(originField);
prepare(hs->origin);
hs->origin = getUptoLinefeed(inputPtr);
} else
if (memcmp_P(inputPtr, protocolField, strlen_P(protocolField)) == 0) {
inputPtr += strlen_P(protocolField);
subprotocolFlag = TRUE;
} else
if (memcmp_P(inputPtr, keyField, strlen_P(keyField)) == 0) {
inputPtr += strlen_P(keyField);
prepare(hs->key);
hs->key = getUptoLinefeed(inputPtr);
} else
if (memcmp_P(inputPtr, versionField, strlen_P(versionField)) == 0) {
inputPtr += strlen_P(versionField);
char *versionString = NULL;
versionString = getUptoLinefeed(inputPtr);
if (memcmp_P(versionString, version, strlen_P(version)) != 0)
versionMismatch = TRUE;
free(versionString);
} else
if (memcmp_P(inputPtr, connectionField, strlen_P(connectionField)) == 0) {
inputPtr += strlen_P(connectionField);
char *connectionValue = NULL;
connectionValue = getUptoLinefeed(inputPtr);
strtolower(connectionValue);
assert(connectionValue);
if (strstr_P(connectionValue, upgrade) != NULL)
connectionFlag = TRUE;
free(connectionValue);
} else
if (memcmp_P(inputPtr, upgradeField, strlen_P(upgradeField)) == 0) {
inputPtr += strlen_P(upgradeField);
char *compare = NULL;
compare = getUptoLinefeed(inputPtr);
strtolower(compare);
assert(compare);
if (memcmp_P(compare, websocket, strlen_P(websocket)) == 0)
upgradeFlag = TRUE;
free(compare);
};
inputPtr = strstr_P(inputPtr, rn) + 2;
}
// we have read all data, so check them
if (!hs->host || !hs->key || !connectionFlag || !upgradeFlag || subprotocolFlag
|| versionMismatch)
{
hs->frameType = WS_ERROR_FRAME;
} else {
hs->frameType = WS_OPENING_FRAME;
}
return hs->frameType;
}
void wsGetHandshakeAnswer(const struct handshake *hs, uint8_t *outFrame,
size_t *outLength)
{
assert(outFrame && *outLength);
assert(hs->frameType == WS_OPENING_FRAME);
assert(hs && hs->key);
char *responseKey = NULL;
uint8_t length = strlen(hs->key)+strlen_P(secret);
responseKey = malloc(length);
memcpy(responseKey, hs->key, strlen(hs->key));
memcpy_P(&(responseKey[strlen(hs->key)]), secret, strlen_P(secret));
unsigned char shaHash[20];
memset(shaHash, 0, sizeof(shaHash));
sha1(shaHash, responseKey, length);
size_t base64Length = base64(responseKey, length, shaHash, 20);
responseKey[base64Length] = '\0';
int written = sprintf_P((char *)outFrame,
PSTR("HTTP/1.1 101 Switching Protocols\r\n"
"%s%s\r\n"
"%s%s\r\n"
"Sec-WebSocket-Accept: %s\r\n\r\n"),
upgradeField,
websocket,
connectionField,
upgrade2,
responseKey);
free(responseKey);
// if assert fail, that means, that we corrupt memory
assert(written <= *outLength);
*outLength = written;
}
void wsMakeFrame(const uint8_t *data, size_t dataLength,
uint8_t *outFrame, size_t *outLength, enum wsFrameType frameType)
{
assert(outFrame && *outLength);
assert(frameType < 0x10);
if (dataLength > 0)
assert(data);
outFrame[0] = 0x80 | frameType;
if (dataLength <= 125) {
outFrame[1] = dataLength;
*outLength = 2;
} else if (dataLength <= 0xFFFF) {
outFrame[1] = 126;
uint16_t payloadLength16b = htons(dataLength);
memcpy(&outFrame[2], &payloadLength16b, 2);
*outLength = 4;
} else {
assert(dataLength <= 0xFFFF);
/* implementation for 64bit systems
outFrame[1] = 127;
dataLength = htonll(dataLength);
memcpy(&outFrame[2], &dataLength, 8);
*outLength = 10;
*/
}
memcpy(&outFrame[*outLength], data, dataLength);
*outLength+= dataLength;
}
static size_t getPayloadLength(const uint8_t *inputFrame, size_t inputLength,
uint8_t *payloadFieldExtraBytes, enum wsFrameType *frameType)
{
size_t payloadLength = inputFrame[1] & 0x7F;
*payloadFieldExtraBytes = 0;
if ((payloadLength == 0x7E && inputLength < 4) || (payloadLength == 0x7F && inputLength < 10)) {
*frameType = WS_INCOMPLETE_FRAME;
return 0;
}
if (payloadLength == 0x7F && (inputFrame[3] & 0x80) != 0x0) {
*frameType = WS_ERROR_FRAME;
return 0;
}
if (payloadLength == 0x7E) {
uint16_t payloadLength16b = 0;
*payloadFieldExtraBytes = 2;
memcpy(&payloadLength16b, &inputFrame[2], *payloadFieldExtraBytes);
payloadLength = ntohs(payloadLength16b);
} else if (payloadLength == 0x7F) {
*frameType = WS_ERROR_FRAME;
return 0;
/* // implementation for 64bit systems
uint64_t payloadLength64b = 0;
*payloadFieldExtraBytes = 8;
memcpy(&payloadLength64b, &inputFrame[2], *payloadFieldExtraBytes);
if (payloadLength64b > SIZE_MAX) {
*frameType = WS_ERROR_FRAME;
return 0;
}
payloadLength = (size_t)ntohll(payloadLength64b);
*/
}
return payloadLength;
}
enum wsFrameType wsParseInputFrame(uint8_t *inputFrame, size_t inputLength,
uint8_t **dataPtr, size_t *dataLength)
{
assert(inputFrame && inputLength);
if (inputLength < 2)
return WS_INCOMPLETE_FRAME;
if ((inputFrame[0] & 0x70) != 0x0) // checks extensions off
return WS_ERROR_FRAME;
if ((inputFrame[0] & 0x80) != 0x80) // we haven't continuation frames support
return WS_ERROR_FRAME; // so, fin flag must be set
if ((inputFrame[1] & 0x80) != 0x80) // checks masking bit
return WS_ERROR_FRAME;
uint8_t opcode = inputFrame[0] & 0x0F;
if (opcode == WS_TEXT_FRAME ||
opcode == WS_BINARY_FRAME ||
opcode == WS_CLOSING_FRAME ||
opcode == WS_PING_FRAME ||
opcode == WS_PONG_FRAME
){
enum wsFrameType frameType = opcode;
uint8_t payloadFieldExtraBytes = 0;
size_t payloadLength = getPayloadLength(inputFrame, inputLength,
&payloadFieldExtraBytes, &frameType);
if (payloadLength > 0) {
if (payloadLength + 6 + payloadFieldExtraBytes > inputLength) // 4-maskingKey, 2-header
return WS_INCOMPLETE_FRAME;
uint8_t *maskingKey = &inputFrame[2 + payloadFieldExtraBytes];
assert(payloadLength == inputLength - 6 - payloadFieldExtraBytes);
*dataPtr = &inputFrame[2 + payloadFieldExtraBytes + 4];
*dataLength = payloadLength;
size_t i;
for (i = 0; i < *dataLength; i++) {
(*dataPtr)[i] = (*dataPtr)[i] ^ maskingKey[i%4];
}
}
return frameType;
}
return WS_ERROR_FRAME;
}

View File

@ -1,151 +0,0 @@
/*
* Copyright (c) 2014 Putilov Andrey
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef WEBSOCKET_H
#define WEBSOCKET_H
#ifdef __cplusplus
extern "C" {
#endif
#include <assert.h>
#include <stdint.h> /* uint8_t */
#include <stdlib.h> /* strtoul */
#include <string.h>
#include <stdio.h> /* sscanf */
#include <ctype.h> /* isdigit */
//#include <stddef.h> /* size_t */
#include "aw-base64.h"
#include "aw-sha1.h"
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define PROGMEM
#define PSTR
#define strstr_P strstr
#define sscanf_P sscanf
#define sprintf_P sprintf
#define strlen_P strlen
#define memcmp_P memcmp
#define memcpy_P memcpy
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
static const char connectionField[] PROGMEM = "Connection: ";
static const char upgrade[] PROGMEM = "upgrade";
static const char upgrade2[] PROGMEM = "Upgrade";
static const char upgradeField[] PROGMEM = "Upgrade: ";
static const char websocket[] PROGMEM = "websocket";
static const char hostField[] PROGMEM = "Host: ";
static const char originField[] PROGMEM = "Origin: ";
static const char keyField[] PROGMEM = "Sec-WebSocket-Key: ";
static const char protocolField[] PROGMEM = "Sec-WebSocket-Protocol: ";
static const char versionField[] PROGMEM = "Sec-WebSocket-Version: ";
static const char version[] PROGMEM = "13";
static const char secret[] PROGMEM = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
enum wsFrameType { // errors starting from 0xF0
WS_EMPTY_FRAME = 0xF0,
WS_ERROR_FRAME = 0xF1,
WS_INCOMPLETE_FRAME = 0xF2,
WS_TEXT_FRAME = 0x01,
WS_BINARY_FRAME = 0x02,
WS_PING_FRAME = 0x09,
WS_PONG_FRAME = 0x0A,
WS_OPENING_FRAME = 0xF3,
WS_CLOSING_FRAME = 0x08
};
enum wsState {
WS_STATE_OPENING,
WS_STATE_NORMAL,
WS_STATE_CLOSING
};
struct handshake {
char *host;
char *origin;
char *key;
char *resource;
enum wsFrameType frameType;
};
/**
* @param inputFrame Pointer to input frame
* @param inputLength Length of input frame
* @param hs Cleared with nullHandshake() handshake structure
* @return Type of parsed frame
*/
enum wsFrameType wsParseHandshake(const uint8_t *inputFrame, size_t inputLength,
struct handshake *hs);
/**
* @param hs Filled handshake structure
* @param outFrame Pointer to frame buffer
* @param outLength Length of frame buffer. Return length of out frame
*/
void wsGetHandshakeAnswer(const struct handshake *hs, uint8_t *outFrame,
size_t *outLength);
/**
* @param data Pointer to input data array
* @param dataLength Length of data array
* @param outFrame Pointer to frame buffer
* @param outLength Length of out frame buffer. Return length of out frame
* @param frameType [WS_TEXT_FRAME] frame type to build
*/
void wsMakeFrame(const uint8_t *data, size_t dataLength,
uint8_t *outFrame, size_t *outLength, enum wsFrameType frameType);
/**
*
* @param inputFrame Pointer to input frame. Frame will be modified.
* @param inputLen Length of input frame
* @param outDataPtr Return pointer to extracted data in input frame
* @param outLen Return length of extracted data
* @return Type of parsed frame
*/
enum wsFrameType wsParseInputFrame(uint8_t *inputFrame, size_t inputLength,
uint8_t **dataPtr, size_t *dataLength);
/**
* @param hs NULL handshake structure
*/
void nullHandshake(struct handshake *hs);
/**
* @param hs free and NULL handshake structure
*/
void freeHandshake(struct handshake *hs);
#ifdef __cplusplus
}
#endif
#endif /* WEBSOCKET_H */

View File

@ -1,310 +0,0 @@
#include <stdint.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "3rdparty/websockets/websocket.h"
#include "3rdparty/cjson/cJSON.h"
#include "util/dprintf.h"
#include "../../../hooklib/y3.h"
DWORD __stdcall y3_websocket_thread_proc(LPVOID ctx);
// TODO: make these settings configurable
#define Y3WS_DEBUG true
#define PORT 3497
#define WS_PATH "/y3io"
#define PROTOCOL_VERSION 1
static bool is_initialized = false;
static SOCKET ws_socket = 0;
static HANDLE server_thread = INVALID_HANDLE_VALUE;
static struct CardInfo card_info[32];
static int card_info_size = 0;
static CRITICAL_SECTION card_info_lock;
uint16_t y3_io_get_api_version() {
return 0x0100;
}
HRESULT y3_io_init() {
if (is_initialized) {
return S_FALSE;
}
memset(card_info, 0, sizeof(card_info));
InitializeCriticalSection(&card_info_lock);
ws_socket = socket(AF_INET, SOCK_STREAM, 0);
if (ws_socket == -1) {
dprintf("Y3WS: Failed to create socket\n");
return E_FAIL;
}
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(PORT);
if (bind(ws_socket, (struct sockaddr *) &local, sizeof(local)) == -1) {
dprintf("Y3WS: Failed to bind socket\n");
return E_FAIL;
}
if (listen(ws_socket, 1) == -1) {
dprintf("Y3WS: Failed to listen on socket\n");
return E_FAIL;
}
server_thread = CreateThread(NULL, 0, y3_websocket_thread_proc, NULL, 0, NULL);
is_initialized = true;
dprintf("Y3WS: Started server on port %d\n", PORT);
return S_OK;
}
HRESULT y3_io_close() {
dprintf("Y3WS: Exiting\n");
if (ws_socket != 0) {
closesocket(ws_socket);
ws_socket = 0;
}
if (server_thread != NULL) {
WaitForSingleObject(server_thread, INFINITE);
server_thread = INVALID_HANDLE_VALUE;
}
is_initialized = false;
return S_OK;
}
int safeSend(SOCKET client, const uint8_t* buffer, size_t bufferSize) {
ssize_t written = send(client, (char *) buffer, (int) bufferSize, 0);
if (written == -1) {
closesocket(client);
dprintf("Y3WS: send failed");
return EXIT_FAILURE;
}
if (written != bufferSize) {
closesocket(client);
perror("Y3WS: connection interrupted");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static void y3ws_make_error_packet(const char* message, uint8_t* output_data, size_t* output_size) {
dprintf("Y3WS: Error: %s\n", message);
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":\"%d\", \"success\":false,\"error\":\"%s\"}", PROTOCOL_VERSION, message);
}
static void y3ws_make_success_packet(uint8_t* output_data, size_t* output_size) {
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":\"%d\", \"success\":true}", PROTOCOL_VERSION);
}
static void y3ws_handle(const char* input_data, uint32_t input_length, uint8_t* output_data, size_t* output_size) {
#if Y3WS_DEBUG
dprintf("Y3WS: Message was: %s\n", input_data);
#endif
cJSON* json = cJSON_ParseWithLength(input_data, input_length);
if (json == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
dprintf("Y3WS: Invalid JSON received!\n");
dprintf("Y3WS: Message was: %s\n", input_data);
dprintf("Y3WS: Error at: %s\n", error_ptr);
}
const cJSON* ver = cJSON_GetObjectItemCaseSensitive(json, "version");
const cJSON* cmd = cJSON_GetObjectItemCaseSensitive(json, "command");
if (!cJSON_IsNumber(ver) || ver->valueint <= 0) {
y3ws_make_error_packet("Missing version attribute", output_data, output_size);
goto end;
}
if (ver->valueint != PROTOCOL_VERSION) {
y3ws_make_error_packet("Incompatible version", output_data, output_size);
goto end;
}
if (strcmp(cmd->valuestring, "get_game_id") == 0) {
// TODO: send current game id (SDGY, SDDD)
} else if (strcmp(cmd->valuestring, "get_cards") == 0) {
// TODO: get array of {image_filename, card_id}
} else if (strcmp(cmd->valuestring, "set_field") == 0) {
const cJSON* cards = cJSON_GetObjectItemCaseSensitive(json, "cards");
const cJSON* card = NULL;
card_info_size = 0;
EnterCriticalSection(&card_info_lock);
memset(card_info, 0, sizeof(card_info));
cJSON_ArrayForEach(card, cards) {
cJSON* x = cJSON_GetObjectItemCaseSensitive(card, "x");
cJSON* y = cJSON_GetObjectItemCaseSensitive(card, "y");
cJSON* r = cJSON_GetObjectItemCaseSensitive(card, "rotation");
cJSON* id = cJSON_GetObjectItemCaseSensitive(card, "id");
if (cJSON_IsNumber(x) && cJSON_IsNumber(y) && cJSON_IsNumber(r) && cJSON_IsNumber(id)) {
card_info[card_info_size].eCardStatus = VALID;
card_info[card_info_size].fX = (float)x->valuedouble;
card_info[card_info_size].fY = (float)y->valuedouble;
card_info[card_info_size].fAngle = (float)r->valuedouble;
card_info[card_info_size].uID = r->valueint;
}
card_info_size++;
}
LeaveCriticalSection(&card_info_lock);
y3ws_make_success_packet(output_data, output_size);
} else {
y3ws_make_error_packet("Unknown command", output_data, output_size);
}
end:
cJSON_Delete(json);
}
static void y3ws_handle_messages(SOCKET client) {
enum wsFrameType frameType = WS_INCOMPLETE_FRAME;
enum wsState state = WS_STATE_OPENING;
const int BUFFER_SIZE = 4096;
uint8_t in_buf[BUFFER_SIZE];
uint8_t out_buf[BUFFER_SIZE];
size_t out_size;
uint8_t* message = NULL;
size_t message_size = 0;
struct handshake hs;
nullHandshake(&hs);
while (frameType == WS_INCOMPLETE_FRAME) {
ssize_t read = recv(client, (char *) in_buf, BUFFER_SIZE, 0);
if (!read) {
dprintf("Y3WS: Accept failed\n");
break;
}
if (state == WS_STATE_OPENING) {
frameType = wsParseHandshake(in_buf, read, &hs);
} else {
frameType = wsParseInputFrame(in_buf, read, &message, &message_size);
}
if ((frameType == WS_INCOMPLETE_FRAME && read == BUFFER_SIZE) || frameType == WS_ERROR_FRAME) {
if (frameType == WS_INCOMPLETE_FRAME) {
dprintf("Y3WS: buffer too small");
} else {
dprintf("Y3WS: error in incoming frame\n");
}
if (state == WS_STATE_OPENING) {
memset(out_buf, 0, BUFFER_SIZE);
out_size = sprintf((char *) out_buf,
"HTTP/1.1 400 Bad Request\r\n"
"%s%s\r\n\r\n",
versionField,
version);
safeSend(client, out_buf, out_size);
break;
} else {
memset(out_buf, 0, BUFFER_SIZE);
wsMakeFrame(NULL, 0, out_buf, &out_size, WS_CLOSING_FRAME);
if (safeSend(client, out_buf, out_size) == EXIT_FAILURE) {
break;
}
state = WS_STATE_CLOSING;
frameType = WS_INCOMPLETE_FRAME;
}
}
if (state == WS_STATE_OPENING) {
assert(frameType == WS_OPENING_FRAME);
if (frameType == WS_OPENING_FRAME) {
if (strcmp(hs.resource, WS_PATH) != 0) {
dprintf("Y3WS: Invalid path: %s\n", hs.resource);
memset(out_buf, 0, BUFFER_SIZE);
out_size = sprintf((char *) out_buf, "HTTP/1.1 404 Not Found\r\n\r\n");
safeSend(client, out_buf, out_size);
break;
}
memset(out_buf, 0, BUFFER_SIZE);
wsGetHandshakeAnswer(&hs, out_buf, &out_size);
freeHandshake(&hs);
if (safeSend(client, out_buf, out_size) == EXIT_FAILURE)
break;
state = WS_STATE_NORMAL;
frameType = WS_INCOMPLETE_FRAME;
}
} else {
if (frameType == WS_CLOSING_FRAME) {
if (state == WS_STATE_CLOSING) {
break;
} else {
memset(out_buf, 0, BUFFER_SIZE);
wsMakeFrame(NULL, 0, out_buf, &out_size, WS_CLOSING_FRAME);
safeSend(client, out_buf, out_size);
break;
}
} else if (frameType == WS_TEXT_FRAME) {
uint8_t* input_data = malloc(message_size + 1);
memcpy(input_data, message, message_size);
input_data[message_size] = 0;
uint8_t* output_data = malloc(BUFFER_SIZE);
size_t output_size = BUFFER_SIZE;
y3ws_handle((const char*)input_data, message_size, output_data, &output_size);
wsMakeFrame(output_data, output_size, out_buf, &out_size, WS_TEXT_FRAME);
free(input_data);
if (safeSend(client, out_buf, out_size) == EXIT_FAILURE) {
break;
}
frameType = WS_INCOMPLETE_FRAME;
}
}
}
closesocket(client);
}
DWORD __stdcall y3_websocket_thread_proc(LPVOID ctx) {
while (true) {
struct sockaddr_in remote;
socklen_t sockaddr_len = sizeof(remote);
SOCKET client = accept(ws_socket, (struct sockaddr *) &remote, &sockaddr_len);
if (client == -1) {
break;
}
dprintf("Y3WS: Connected %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
y3ws_handle_messages(client);
dprintf("Y3WS: Disconnected\n");
}
closesocket(ws_socket);
return 0;
}
HRESULT y3_io_get_cards(struct CardInfo* pCardInfo, int* numCards) {
EnterCriticalSection(&card_info_lock);
for (int i = 0; i < card_info_size; i++) {
memcpy(&pCardInfo[i], &card_info[i], sizeof(struct CardInfo));
}
*numCards = card_info_size;
LeaveCriticalSection(&card_info_lock);
return S_OK;
}

View File

@ -3,7 +3,7 @@
#include <stdint.h>
#include <windows.h>
#include "y3io/y3.h"
#include "hooklib/y3.h"
/* Get the version of the Y3 IO API that this DLL supports. This
function should return a positive 16-bit integer, where the high byte is