mirror of
https://github.com/ADElectronics/RTL00_WEB_VS.git
synced 2024-11-29 19:10:28 +00:00
246 lines
7.7 KiB
C
246 lines
7.7 KiB
C
|
/******************************************************************************
|
|||
|
* FileName: websock.c
|
|||
|
* Description: websocket for web ESP8266
|
|||
|
* Author: PV`
|
|||
|
* (c) PV` 2016
|
|||
|
*******************************************************************************/
|
|||
|
#include "user_config.h"
|
|||
|
#ifdef WEBSOCKET_ENA
|
|||
|
#include "autoconf.h"
|
|||
|
#include "FreeRTOS.h"
|
|||
|
#include "task.h"
|
|||
|
#include "diag.h"
|
|||
|
#include "tcpsrv/tcp_srv_conn.h"
|
|||
|
#include "web_utils.h" // base64encode()
|
|||
|
#include "web_srv.h"
|
|||
|
//#include "phy/phy.h"
|
|||
|
#include "device_lock.h"
|
|||
|
#include "lwip/tcp.h"
|
|||
|
#include "websock.h"
|
|||
|
#include "rtl8195a/rtl_libc.h"
|
|||
|
#include "esp_comp.h"
|
|||
|
#include "hal_crypto.h"
|
|||
|
|
|||
|
// HTTP/1.1 101 Web Socket Protocol Handshake\r\n
|
|||
|
const uint8 WebSocketHTTPOkKey[] ICACHE_RODATA_ATTR = "HTTP/1.1 101 Switching Protocols\r\nAccess-Control-Allow-Origin: *\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n";
|
|||
|
const uint8 WebSocketAddKey[] ICACHE_RODATA_ATTR = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|||
|
const uint8 *HTTPUpgrade = "Upgrade:";
|
|||
|
const uint8 *HTTPwebsocket = "websocket";
|
|||
|
const uint8 *HTTPSecWebSocketKey = "Sec-WebSocket-Key:";
|
|||
|
|
|||
|
//=============================================================================
|
|||
|
// WebSocketAcceptKey()
|
|||
|
// 1) взять строковое значение из заголовка Sec-WebSocket-Key и объединить со
|
|||
|
// строкой 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
|
|||
|
// 2) вычислить бинарный хеш SHA-1 (бинарная строка из 20 символов) от полученной
|
|||
|
// в первом пункте строки
|
|||
|
// 3) закодировать хеш в Base64
|
|||
|
// skey[24+36]:'dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
|||
|
// cha:'b37a4f2cc0624f1690f64606cf385945b2bec4ea'
|
|||
|
// key[28]:'s3pPLMBiTxaQ9kYGzzhZRbK+xOo='
|
|||
|
//=============================================================================
|
|||
|
uint8 buff[maxsizeWebSocketKey + sizeWebSocketAddKey]; // [68]
|
|||
|
bool ICACHE_FLASH_ATTR WebSocketAcceptKey(uint8* dkey, uint8* skey)
|
|||
|
{
|
|||
|
int len = 0;
|
|||
|
bool ret = false;
|
|||
|
uint8 keybuf[CRYPTO_SHA1_DIGEST_LENGTH];
|
|||
|
uint8 * buff = os_malloc(maxsizeWebSocketKey + sizeWebSocketAddKey);
|
|||
|
if(buff) {
|
|||
|
while(skey[len] >= '+' && len < maxsizeWebSocketKey) {
|
|||
|
buff[len] = skey[len];
|
|||
|
len++;
|
|||
|
};
|
|||
|
if(len > minsizeWebSocketKey) {
|
|||
|
rtl_memcpy(&buff[len], WebSocketAddKey, sizeWebSocketAddKey+1);
|
|||
|
device_mutex_lock(RT_DEV_LOCK_CRYPTO);
|
|||
|
rtl_crypto_sha1(buff, len + sizeWebSocketAddKey, keybuf);
|
|||
|
device_mutex_unlock(RT_DEV_LOCK_CRYPTO);
|
|||
|
// rtl_cryptoEngine_info();
|
|||
|
len = base64encode(dkey, FileNameSize, keybuf, CRYPTO_SHA1_DIGEST_LENGTH);
|
|||
|
#if DEBUGSOO > 2
|
|||
|
os_printf("\ncha:'");
|
|||
|
print_hex_dump(keybuf, CRYPTO_SHA1_DIGEST_LENGTH, '\0');
|
|||
|
os_printf("'\n");
|
|||
|
os_printf("key[%u]:'%s'\n", len, dkey);
|
|||
|
#endif
|
|||
|
ret = true;
|
|||
|
}
|
|||
|
os_free(buff);
|
|||
|
}
|
|||
|
return ret;
|
|||
|
|
|||
|
}
|
|||
|
//=============================================================================
|
|||
|
// websock_mask() размаскирование блока
|
|||
|
//=============================================================================
|
|||
|
void ICACHE_FLASH_ATTR
|
|||
|
WebsocketMask(WS_FRSTAT *ws, uint8 *raw_data, uint32 raw_len)
|
|||
|
{
|
|||
|
uint32 i, x = ws->cur_len;
|
|||
|
#if DEBUGSOO > 3
|
|||
|
os_printf("mask[%u]%u ", raw_len, x);
|
|||
|
#endif
|
|||
|
for (i = 0; i < raw_len; i++) {
|
|||
|
raw_data[i] ^= ws->mask.uc[x++ & 3];
|
|||
|
}
|
|||
|
}
|
|||
|
//=============================================================================
|
|||
|
// websock_head() разбор заголовка
|
|||
|
//=============================================================================
|
|||
|
uint32 ICACHE_FLASH_ATTR
|
|||
|
WebsocketHead(WS_FRSTAT *ws, uint8 *raw_data, uint32 raw_len)
|
|||
|
{
|
|||
|
// определить размер заголовка фрейма
|
|||
|
uint32 head_len = 2;
|
|||
|
if(raw_len < head_len) return 0; // докачивать
|
|||
|
uint32 data_len = raw_data[1] & WS_SIZE1_BITS;
|
|||
|
if(data_len == 127) head_len = 10;
|
|||
|
else if(data_len == 126) head_len = 4;
|
|||
|
if(raw_data[1] & WS_MASK_FLG) head_len += 4;
|
|||
|
if(raw_len < head_len) return 0; // докачивать
|
|||
|
ws->head_len = head_len;
|
|||
|
ws->cur_len = 0;
|
|||
|
ws->flg = 0;
|
|||
|
data_len = raw_data[1] & WS_SIZE1_BITS;
|
|||
|
if(data_len >= 126) {
|
|||
|
if(data_len == 127) {
|
|||
|
uint32 i;
|
|||
|
for(i = 3; i < 6; i++) {
|
|||
|
if(raw_data[i] != 0) {
|
|||
|
ws->status = sw_frs_close;
|
|||
|
ws->frame_len = 0;
|
|||
|
return WS_CLOSE_MESSAGE_TOO_BIG;
|
|||
|
}
|
|||
|
}
|
|||
|
data_len = (raw_data[6] << 24) | (raw_data[7] << 16) | (raw_data[8] << 8) | raw_data[9];
|
|||
|
}
|
|||
|
else {
|
|||
|
data_len = (raw_data[2] << 8) | raw_data[3];
|
|||
|
}
|
|||
|
}
|
|||
|
if(raw_data[1] & WS_MASK_FLG) {
|
|||
|
ws->flg |= WS_FLG_MASK;
|
|||
|
ws->mask.uc[0] = raw_data[head_len-4];
|
|||
|
ws->mask.uc[1] = raw_data[head_len-3];
|
|||
|
ws->mask.uc[2] = raw_data[head_len-2];
|
|||
|
ws->mask.uc[3] = raw_data[head_len-1];
|
|||
|
}
|
|||
|
// else ws->mask = 0;
|
|||
|
uint8 opcode = raw_data[0] & WS_OPCODE_BITS;
|
|||
|
switch(opcode) {
|
|||
|
case WS_OPCODE_PING: // эхо - дублировать прием
|
|||
|
raw_data[0] &= ~WS_FRAGMENT_FIN;
|
|||
|
raw_data[0] |= WS_OPCODE_PONG;
|
|||
|
ws->status = sw_frs_pong;
|
|||
|
ws->frame_len = data_len;
|
|||
|
break;
|
|||
|
case WS_OPCODE_PONG: // эхо - дублировать прием
|
|||
|
ws->status = sw_frs_ping;
|
|||
|
ws->frame_len = data_len;
|
|||
|
break;
|
|||
|
case WS_OPCODE_CONTINUE: // продолжить
|
|||
|
if(ws->status == sw_frs_pong) {
|
|||
|
ws->frame_len = data_len;
|
|||
|
break;
|
|||
|
}
|
|||
|
else ws->frame_len += data_len;
|
|||
|
break;
|
|||
|
case WS_OPCODE_CLOSE: //
|
|||
|
ws->status = sw_frs_close;
|
|||
|
ws->frame_len = data_len;
|
|||
|
break;
|
|||
|
case WS_OPCODE_TEXT:
|
|||
|
ws->status = sw_frs_text;
|
|||
|
ws->frame_len = data_len;
|
|||
|
break;
|
|||
|
case WS_OPCODE_BINARY:
|
|||
|
ws->status = sw_frs_binary;
|
|||
|
ws->frame_len = data_len;
|
|||
|
break;
|
|||
|
default:
|
|||
|
ws->status = sw_frs_close;
|
|||
|
ws->frame_len = 0;
|
|||
|
return WS_CLOSE_WRONG_TYPE;
|
|||
|
}
|
|||
|
// uint32 len = mMIN(raw_len - head_len, data_len);
|
|||
|
// if((ws->flg & WS_FLG_MASK) != 0) websock_mask(ws, &raw_data[head_len], mMIN(raw_len - head_len, len));
|
|||
|
// ws->cur_len += len;
|
|||
|
if((raw_data[0] & WS_FRAGMENT_FIN) != 0) { // конец - данные на обработку
|
|||
|
ws->flg |= WS_FLG_FIN;
|
|||
|
}
|
|||
|
#if DEBUGSOO > 1
|
|||
|
os_printf("ws#%02xrx[%u] ", raw_data[0], data_len);
|
|||
|
#endif
|
|||
|
return 1;
|
|||
|
/*
|
|||
|
if(data_len + head_len <= raw_len) { // весь пакет уже в буфере?
|
|||
|
return 1;
|
|||
|
}
|
|||
|
return 0; // докачивать */
|
|||
|
}
|
|||
|
//=============================================================================
|
|||
|
// websock_tx_frame() - передача фрейма
|
|||
|
//=============================================================================
|
|||
|
err_t ICACHE_FLASH_ATTR
|
|||
|
WebsocketTxFrame(TCP_SERV_CONN *ts_conn, uint32 opcode, uint8 *raw_data, uint32 raw_len)
|
|||
|
{
|
|||
|
union {
|
|||
|
uint8 uc[8];
|
|||
|
uint16 uw[4];
|
|||
|
uint32 ud[2];
|
|||
|
}head;
|
|||
|
union {
|
|||
|
uint8 uc[4];
|
|||
|
uint16 uw[2];
|
|||
|
uint32 ud;
|
|||
|
}mask;
|
|||
|
if(raw_data == NULL) raw_len = 0;
|
|||
|
head.ud[0] = opcode;
|
|||
|
uint32 head_len;
|
|||
|
if(raw_len > 126) {
|
|||
|
head.uc[1] = 126;
|
|||
|
head.uc[2] = raw_len>>8;
|
|||
|
head.uc[3] = raw_len;
|
|||
|
head_len = 4;
|
|||
|
}
|
|||
|
else {
|
|||
|
head.uc[1] = raw_len;
|
|||
|
head_len = 2;
|
|||
|
};
|
|||
|
if(opcode & (WS_MASK_FLG << 8)) {
|
|||
|
mask.ud ^= rand();
|
|||
|
head.uc[1] |= WS_MASK_FLG;
|
|||
|
head.uc[head_len] = mask.uc[0];
|
|||
|
head.uc[head_len+1] = mask.uc[1];
|
|||
|
head.uc[head_len+2] = mask.uc[2];
|
|||
|
head.uc[head_len+3] = mask.uc[3];
|
|||
|
head_len += 4;
|
|||
|
}
|
|||
|
uint32 len = tcp_sndbuf(ts_conn->pcb);
|
|||
|
err_t err = 1; // ERR_BUF;
|
|||
|
if(len >= raw_len + head_len) {
|
|||
|
#if DEBUGSOO > 1
|
|||
|
os_printf("ws#%02xtx[%u] ", head.uc[0], raw_len);
|
|||
|
#endif
|
|||
|
ts_conn->flag.nagle_disabled = 0;
|
|||
|
tcp_nagle_disable(ts_conn->pcb);
|
|||
|
err = tcpsrv_int_sent_data(ts_conn, head.uc, head_len);
|
|||
|
ts_conn->flag.nagle_disabled = 1;
|
|||
|
if(err == ERR_OK && raw_len != 0) {
|
|||
|
if(opcode & (WS_MASK_FLG << 8)) {
|
|||
|
uint32 i;
|
|||
|
for (i = 0; i < raw_len; i++) {
|
|||
|
raw_data[i] ^= mask.uc[i & 3];
|
|||
|
}
|
|||
|
}
|
|||
|
err = tcpsrv_int_sent_data(ts_conn, raw_data, raw_len);
|
|||
|
}
|
|||
|
}
|
|||
|
return err;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#endif // WEBSOCKET_ENA
|
|||
|
|