use binary messages and fix bugs in utf sanitizer

http-comm
Ondřej Hruška 7 years ago
parent a6ee808a1d
commit faffa68794
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2
      front-end
  2. 56
      user/ansi_parser.c
  3. 6
      user/ansi_parser.rl
  4. 146
      user/apars_utf8.c
  5. 4
      user/cgi_sockets.c
  6. 10
      user/cgi_term_cfg.c
  7. 6
      user/screen.c
  8. 4
      user/screen.h

@ -1 +1 @@
Subproject commit cee23ca951bd1c07b9c1d1d1334f18c0894cc028 Subproject commit 55e2def6e3899a708a9afca099252bdf44919b7c

@ -5,10 +5,11 @@
#include "ansi_parser_callbacks.h" #include "ansi_parser_callbacks.h"
#include "ascii.h" #include "ascii.h"
#include "apars_logging.h" #include "apars_logging.h"
#include "screen.h"
/* Ragel constants block */ /* Ragel constants block */
/* #line 12 "user/ansi_parser.c" */ /* #line 13 "user/ansi_parser.c" */
static const char _ansi_actions[] ESP_CONST_DATA = { static const char _ansi_actions[] ESP_CONST_DATA = {
0, 1, 0, 1, 1, 1, 2, 1, 0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 6, 1, 3, 1, 4, 1, 5, 1, 6, 1,
@ -32,7 +33,7 @@ static const int ansi_en_charsetcmd_body = 10;
static const int ansi_en_main = 1; static const int ansi_en_main = 1;
/* #line 11 "user/ansi_parser.rl" */ /* #line 12 "user/ansi_parser.rl" */
// Max nr of CSI parameters // Max nr of CSI parameters
@ -114,15 +115,20 @@ ansi_parser(char newchar)
// This is used to detect timeout delay (time since last rx char) // This is used to detect timeout delay (time since last rx char)
ansi_parser_char_cnt++; ansi_parser_char_cnt++;
if (termconf->ascii_debug) {
apars_handle_plainchar(newchar);
return;
}
// Init Ragel on the first run // Init Ragel on the first run
if (cs == -1) { if (cs == -1) {
/* #line 121 "user/ansi_parser.c" */ /* #line 127 "user/ansi_parser.c" */
{ {
cs = ansi_start; cs = ansi_start;
} }
/* #line 95 "user/ansi_parser.rl" */ /* #line 101 "user/ansi_parser.rl" */
#if DEBUG_ANSI #if DEBUG_ANSI
memset(history, 0, sizeof(history)); memset(history, 0, sizeof(history));
@ -202,7 +208,7 @@ ansi_parser(char newchar)
// The parser // The parser
/* #line 206 "user/ansi_parser.c" */ /* #line 212 "user/ansi_parser.c" */
{ {
const char *_acts; const char *_acts;
unsigned int _nacts; unsigned int _nacts;
@ -392,7 +398,7 @@ execFuncs:
while ( _nacts-- > 0 ) { while ( _nacts-- > 0 ) {
switch ( *_acts++ ) { switch ( *_acts++ ) {
case 0: case 0:
/* #line 182 "user/ansi_parser.rl" */ /* #line 188 "user/ansi_parser.rl" */
{ {
ansi_warn("Parser error."); ansi_warn("Parser error.");
apars_show_context(); apars_show_context();
@ -401,7 +407,7 @@ execFuncs:
} }
break; break;
case 1: case 1:
/* #line 191 "user/ansi_parser.rl" */ /* #line 197 "user/ansi_parser.rl" */
{ {
if ((*p) != 0) { if ((*p) != 0) {
apars_handle_plainchar((*p)); apars_handle_plainchar((*p));
@ -409,7 +415,7 @@ execFuncs:
} }
break; break;
case 2: case 2:
/* #line 199 "user/ansi_parser.rl" */ /* #line 205 "user/ansi_parser.rl" */
{ {
// Reset the CSI builder // Reset the CSI builder
leadchar = NUL; leadchar = NUL;
@ -426,13 +432,13 @@ execFuncs:
} }
break; break;
case 3: case 3:
/* #line 214 "user/ansi_parser.rl" */ /* #line 220 "user/ansi_parser.rl" */
{ {
leadchar = (*p); leadchar = (*p);
} }
break; break;
case 4: case 4:
/* #line 218 "user/ansi_parser.rl" */ /* #line 224 "user/ansi_parser.rl" */
{ {
if (arg_cnt == 0) arg_cnt = 1; if (arg_cnt == 0) arg_cnt = 1;
// x10 + digit // x10 + digit
@ -442,7 +448,7 @@ execFuncs:
} }
break; break;
case 5: case 5:
/* #line 226 "user/ansi_parser.rl" */ /* #line 232 "user/ansi_parser.rl" */
{ {
if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty
arg_cnt++; arg_cnt++;
@ -450,20 +456,20 @@ execFuncs:
} }
break; break;
case 6: case 6:
/* #line 232 "user/ansi_parser.rl" */ /* #line 238 "user/ansi_parser.rl" */
{ {
interchar = (*p); interchar = (*p);
} }
break; break;
case 7: case 7:
/* #line 236 "user/ansi_parser.rl" */ /* #line 242 "user/ansi_parser.rl" */
{ {
apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p)); apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 8: case 8:
/* #line 248 "user/ansi_parser.rl" */ /* #line 254 "user/ansi_parser.rl" */
{ {
leadchar = (*p); leadchar = (*p);
str_ni = 0; str_ni = 0;
@ -473,13 +479,13 @@ execFuncs:
} }
break; break;
case 9: case 9:
/* #line 256 "user/ansi_parser.rl" */ /* #line 262 "user/ansi_parser.rl" */
{ {
string_buffer[str_ni++] = (*p); string_buffer[str_ni++] = (*p);
} }
break; break;
case 10: case 10:
/* #line 260 "user/ansi_parser.rl" */ /* #line 266 "user/ansi_parser.rl" */
{ {
inside_string = false; inside_string = false;
string_buffer[str_ni++] = '\0'; string_buffer[str_ni++] = '\0';
@ -488,41 +494,41 @@ execFuncs:
} }
break; break;
case 11: case 11:
/* #line 273 "user/ansi_parser.rl" */ /* #line 279 "user/ansi_parser.rl" */
{ {
apars_handle_hash_cmd((*p)); apars_handle_hash_cmd((*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 12: case 12:
/* #line 278 "user/ansi_parser.rl" */ /* #line 284 "user/ansi_parser.rl" */
{ {
apars_handle_short_cmd((*p)); apars_handle_short_cmd((*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 13: case 13:
/* #line 283 "user/ansi_parser.rl" */ /* #line 289 "user/ansi_parser.rl" */
{ {
apars_handle_space_cmd((*p)); apars_handle_space_cmd((*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 14: case 14:
/* #line 290 "user/ansi_parser.rl" */ /* #line 296 "user/ansi_parser.rl" */
{ {
leadchar = (*p); leadchar = (*p);
{cs = 10;goto _again;} {cs = 10;goto _again;}
} }
break; break;
case 15: case 15:
/* #line 295 "user/ansi_parser.rl" */ /* #line 301 "user/ansi_parser.rl" */
{ {
apars_handle_chs_designate(leadchar, (*p)); apars_handle_chs_designate(leadchar, (*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
/* #line 526 "user/ansi_parser.c" */ /* #line 532 "user/ansi_parser.c" */
} }
} }
goto _again; goto _again;
@ -540,7 +546,7 @@ _again:
while ( __nacts-- > 0 ) { while ( __nacts-- > 0 ) {
switch ( *__acts++ ) { switch ( *__acts++ ) {
case 0: case 0:
/* #line 182 "user/ansi_parser.rl" */ /* #line 188 "user/ansi_parser.rl" */
{ {
ansi_warn("Parser error."); ansi_warn("Parser error.");
apars_show_context(); apars_show_context();
@ -550,7 +556,7 @@ _again:
goto _again;} goto _again;}
} }
break; break;
/* #line 554 "user/ansi_parser.c" */ /* #line 560 "user/ansi_parser.c" */
} }
} }
} }
@ -558,6 +564,6 @@ goto _again;}
_out: {} _out: {}
} }
/* #line 318 "user/ansi_parser.rl" */ /* #line 324 "user/ansi_parser.rl" */
} }

@ -3,6 +3,7 @@
#include "ansi_parser_callbacks.h" #include "ansi_parser_callbacks.h"
#include "ascii.h" #include "ascii.h"
#include "apars_logging.h" #include "apars_logging.h"
#include "screen.h"
/* Ragel constants block */ /* Ragel constants block */
%%{ %%{
@ -89,6 +90,11 @@ ansi_parser(char newchar)
// This is used to detect timeout delay (time since last rx char) // This is used to detect timeout delay (time since last rx char)
ansi_parser_char_cnt++; ansi_parser_char_cnt++;
if (termconf->ascii_debug) {
apars_handle_plainchar(newchar);
return;
}
// Init Ragel on the first run // Init Ragel on the first run
if (cs == -1) { if (cs == -1) {
%% write init; %% write init;

@ -11,19 +11,12 @@
#include "uart_driver.h" #include "uart_driver.h"
#include "ansi_parser_callbacks.h" #include "ansi_parser_callbacks.h"
#include "ansi_parser.h" #include "ansi_parser.h"
#include "ascii.h"
static u8 bytes[4]; static u8 bytes[4];
static int utf_len = 0; static int utf_len = 0;
static int utf_j = 0; static int utf_j = 0;
ETSTimer timerResumeRx;
void ICACHE_FLASH_ATTR resumeRxCb(void *unused)
{
ansi_dbg("Parser recover.");
ansi_parser_inhibit = false;
}
/** /**
* Clear the buffer where we collect pieces of a code point. * Clear the buffer where we collect pieces of a code point.
* This is used for parser reset. * This is used for parser reset.
@ -47,6 +40,105 @@ apars_reset_utf8buffer(void)
// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF // U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF // U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF
static void ICACHE_FLASH_ATTR screen_print_ascii(const char *str)
{
char gly[2];
gly[1] = 0;
for(int j = 0;str[j]!=0;j++) {
gly[0] = str[j];
screen_putchar(gly);
}
}
static void ICACHE_FLASH_ATTR hdump_spaces_eol(int needed)
{
if (needed == 0) needed = 5;
int x, y;
screen_cursor_get(&y, &x);
if (x > termconf_live.width - needed) {
screen_clear_in_line(CLEAR_FROM_CURSOR);
screen_putchar("\n");
screen_putchar("\r");
}
}
static void ICACHE_FLASH_ATTR hdump_good(const char *ch)
{
char buf[10];
hdump_spaces_eol(6);
screen_set_fg(7);
screen_set_bg(0);
if(ch[0]<32) {
screen_set_fg(7);
screen_set_bg(2);
switch (ch[0]) {
case NUL: screen_print_ascii("NUL"); break;
case SOH: screen_print_ascii("SOH"); break;
case STX: screen_print_ascii("STX"); break;
case ETX: screen_print_ascii("ETX"); break;
case EOT: screen_print_ascii("EOT"); break;
case ENQ: screen_print_ascii("ENQ"); break;
case ACK: screen_print_ascii("ACK"); break;
case BEL: screen_print_ascii("BEL"); break;
case BS: screen_print_ascii("BS"); break;
case TAB: screen_print_ascii("TAB"); break;
case LF: screen_print_ascii("LF"); break;
case VT: screen_print_ascii("VT"); break;
case FF: screen_print_ascii("FF"); break;
case CR: screen_print_ascii("CR"); break;
case SO: screen_print_ascii("SO"); break;
case SI: screen_print_ascii("SI"); break;
case DLE: screen_print_ascii("DLE"); break;
case DC1: screen_print_ascii("DC1"); break;
case DC2: screen_print_ascii("DC2"); break;
case DC3: screen_print_ascii("DC3"); break;
case DC4: screen_print_ascii("DC4"); break;
case NAK: screen_print_ascii("NAK"); break;
case SYN: screen_print_ascii("SYN"); break;
case ETB: screen_print_ascii("ETB"); break;
case CAN: screen_print_ascii("CAN"); break;
case EM: screen_print_ascii("EM"); break;
case SUB: screen_print_ascii("SUB"); break;
case ESC: screen_print_ascii("ESC"); break;
case FS: screen_print_ascii("FS"); break;
case GS: screen_print_ascii("GS"); break;
case RS: screen_print_ascii("RS"); break;
case US: screen_print_ascii("US"); break;
case SP: screen_print_ascii("SP"); break;
case DEL: screen_print_ascii("DEL"); break;
default:
sprintf(buf, "%02Xh", ch[0]);
screen_print_ascii(buf);
}
} else {
screen_putchar(ch);
}
screen_set_default_bg();
screen_set_default_fg();
screen_print_ascii(" ");
}
static void ICACHE_FLASH_ATTR hdump_bad(const char *ch, int len)
{
char buf[10];
hdump_spaces_eol(len*5);
screen_set_fg(7);
screen_set_bg(1);
for (int i=0;i<len;i++) {
sprintf(buf, "%02Xh", ch[i]);
screen_print_ascii(buf);
if(i<len-1) screen_print_ascii(" ");
}
screen_set_default_bg();
screen_set_default_fg();
screen_print_ascii(" ");
}
/** /**
* Handle a received plain character * Handle a received plain character
* @param c - received character * @param c - received character
@ -54,38 +146,40 @@ apars_reset_utf8buffer(void)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
apars_handle_plainchar(char c) apars_handle_plainchar(char c)
{ {
u8 uc = (u8)c;
// collecting unicode glyphs... // collecting unicode glyphs...
if (c & 0x80) { if (uc & 0x80) {
if (utf_len == 0) { if (utf_len == 0) {
bytes[0] = uc;
utf_j = 1;
// start // start
if (c == 0xC0 || c == 0xC1 || c > 0xF4) { if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) {
// forbidden start codes // forbidden start codes
goto fail; goto fail;
} }
if ((c & 0xE0) == 0xC0) { if ((uc & 0xE0) == 0xC0) {
utf_len = 2; utf_len = 2;
} }
else if ((c & 0xF0) == 0xE0) { else if ((uc & 0xF0) == 0xE0) {
utf_len = 3; utf_len = 3;
} }
else if ((c & 0xF8) == 0xF0) { else if ((uc & 0xF8) == 0xF0) {
utf_len = 4; utf_len = 4;
} }
else { else {
// chars over 127 that don't start unicode sequences // chars over 127 that don't start unicode sequences
goto fail; goto fail;
} }
bytes[0] = c;
utf_j = 1;
} }
else { else {
if ((c & 0xC0) != 0x80) { if ((uc & 0xC0) != 0x80) {
bytes[utf_j++] = uc;
goto fail; goto fail;
} }
else { else {
bytes[utf_j++] = c; bytes[utf_j++] = uc;
if (utf_j >= utf_len) { if (utf_j >= utf_len) {
// check for bad sequences - overlong or some other problem // check for bad sequences - overlong or some other problem
if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail; if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail;
@ -96,25 +190,31 @@ apars_handle_plainchar(char c)
// trap for surrogates - those break javascript // trap for surrogates - those break javascript
if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail; if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail;
if (termconf_live.ascii_debug) {
hdump_good((const char *) bytes);
} else {
screen_putchar((const char *) bytes); screen_putchar((const char *) bytes);
}
apars_reset_utf8buffer(); apars_reset_utf8buffer();
} }
} }
} }
} }
else { else {
bytes[0] = c; bytes[0] = uc;
bytes[1] = 0; // just to make sure it's closed... bytes[1] = 0; // just to make sure it's closed...
if (termconf_live.ascii_debug) {
hdump_good((const char *) bytes);
} else {
screen_putchar((const char *) bytes); screen_putchar((const char *) bytes);
} }
apars_reset_utf8buffer();
}
return; return;
fail: fail:
ansi_parser_inhibit = true; hdump_bad((const char *) bytes, utf_j);
ansi_warn("BAD UTF8!"); ansi_warn("BAD UTF8!");
apars_show_context(); apars_show_context();
apars_reset_utf8buffer(); apars_reset_utf8buffer();
ansi_dbg("Temporarily inhibiting parser...");
TIMER_START(&timerResumeRx, resumeRxCb, 500, 0);
} }

@ -69,7 +69,7 @@ updateNotify_do(Websock *ws, ScreenNotifyTopics topics)
} }
httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, topics, &data); httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, topics, &data);
int flg = 0; int flg = WEBSOCK_FLAG_BIN;
if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE; if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE;
if (i > 0) flg |= WEBSOCK_FLAG_CONT; if (i > 0) flg |= WEBSOCK_FLAG_CONT;
if (ws) { if (ws) {
@ -134,7 +134,7 @@ notify_growl(char *msg)
// here's some potential for a race error with the other broadcast functions // here's some potential for a race error with the other broadcast functions
// - we assume app won't send notifications in the middle of updating content // - we assume app won't send notifications in the middle of updating content
cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), 0); cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), WEBSOCK_FLAG_BIN);
resetHeartbeatTimer(); resetHeartbeatTimer();
} }

@ -222,6 +222,13 @@ cgiTermCfgSetParams(HttpdConnData *connData)
termconf->allow_decopt_12 = (bool)n; termconf->allow_decopt_12 = (bool)n;
} }
if (GET_ARG("ascii_debug")) {
cgi_dbg("ascii_debug: %s", buff);
n = atoi(buff);
termconf->ascii_debug = (bool)n;
shall_clear_screen = true;
}
if (GET_ARG("theme")) { if (GET_ARG("theme")) {
cgi_dbg("Screen color theme: %s", buff); cgi_dbg("Screen color theme: %s", buff);
n = atoi(buff); n = atoi(buff);
@ -453,6 +460,9 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
else if (streq(token, "allow_decopt_12")) { else if (streq(token, "allow_decopt_12")) {
sprintf(buff, "%d", (int)termconf->allow_decopt_12); sprintf(buff, "%d", (int)termconf->allow_decopt_12);
} }
else if (streq(token, "ascii_debug")) {
sprintf(buff, "%d", (int)termconf->ascii_debug);
}
else if (streq(token, "loopback")) { else if (streq(token, "loopback")) {
sprintf(buff, "%d", (int)termconf->loopback); sprintf(buff, "%d", (int)termconf->loopback);
} }

@ -212,6 +212,7 @@ terminal_restore_defaults(void)
termconf->want_all_fn = SCR_DEF_ALLFN; termconf->want_all_fn = SCR_DEF_ALLFN;
termconf->debugbar = SCR_DEF_DEBUGBAR; termconf->debugbar = SCR_DEF_DEBUGBAR;
termconf->allow_decopt_12 = SCR_DEF_DECOPT12; termconf->allow_decopt_12 = SCR_DEF_DECOPT12;
termconf->ascii_debug = SCR_DEF_ASCIIDEBUG;
} }
/** /**
@ -241,6 +242,11 @@ terminal_apply_settings_noclear(void)
termconf->allow_decopt_12 = SCR_DEF_DECOPT12; termconf->allow_decopt_12 = SCR_DEF_DECOPT12;
changed = 1; changed = 1;
} }
if (termconf->config_version < 3) {
persist_dbg("termconf: Updating to version %d", 1);
termconf->ascii_debug = SCR_DEF_ASCIIDEBUG;
changed = 1;
}
termconf->config_version = TERMCONF_VERSION; termconf->config_version = TERMCONF_VERSION;

@ -67,6 +67,7 @@ enum CursorShape {
#define SCR_DEF_ALLFN 0 // capture F5 etc #define SCR_DEF_ALLFN 0 // capture F5 etc
#define SCR_DEF_DEBUGBAR 0 #define SCR_DEF_DEBUGBAR 0
#define SCR_DEF_DECOPT12 0 #define SCR_DEF_DECOPT12 0
#define SCR_DEF_ASCIIDEBUG 0
// --- Persistent Settings --- // --- Persistent Settings ---
#define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL) #define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL)
@ -74,7 +75,7 @@ enum CursorShape {
// Size designed for the terminal config structure // Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade // Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300 #define TERMCONF_SIZE 300
#define TERMCONF_VERSION 2 #define TERMCONF_VERSION 3
typedef struct { typedef struct {
u32 width; u32 width;
@ -98,6 +99,7 @@ typedef struct {
bool want_all_fn; bool want_all_fn;
bool debugbar; bool debugbar;
bool allow_decopt_12; bool allow_decopt_12;
bool ascii_debug;
} TerminalConfigBundle; } TerminalConfigBundle;
// Live config // Live config

Loading…
Cancel
Save