RTL00_WEB_VS/RTLGDB/Project/web/websock.c

246 lines
7.7 KiB
C
Raw Permalink Normal View History

2017-11-28 19:31:40 +00:00
/******************************************************************************
* 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