From e60aa0c5779bce199020236c0ac3a4beace571cb Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 20 Jun 2019 08:19:29 +0200 Subject: Implement additional framing functions (Fixes #3) --- builtins.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 7 deletions(-) (limited to 'builtins.c') diff --git a/builtins.c b/builtins.c index 5c1dfb8..41d76d4 100644 --- a/builtins.c +++ b/builtins.c @@ -1,4 +1,6 @@ #include +#include +#include #include "websocksy.h" #include "builtins.h" @@ -151,7 +153,7 @@ int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operatio } //if valid utf8, send as text frame - if(valid){ + if(valid && opcode){ *opcode = ws_frame_text; } @@ -165,12 +167,121 @@ int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operat return length; } -int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){ - //TODO implement separator framer - return length; +/* + * The `separator` framing function waits until a variable-length separator is found in the stream and sends all + * data up to and including that separator as binary frame. The configuration string is used as the separator, with + * the escape sequences \r, \t, \n, \0, \f, \\ being recognized as their ASCII expressions. Arbitrary bytes can be + * specified hexadecimally using the syntax \x + */ +typedef struct /*_separator_framing_config*/ { + size_t length; + uint8_t* separator; +} framing_separator_config; + +int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* framing_config){ + size_t u, p = 0; + unsigned hex; + framing_separator_config* config = framing_data ? *framing_data : NULL; + + if(data && !config){ + //parse configuration + config = calloc(1, sizeof(framing_separator_config)); + config->separator = (uint8_t*) strdup(framing_config); + for(u = 0; config->separator[u]; u++){ + config->separator[p] = config->separator[u]; + if(config->separator[u] == '\\'){ + switch(config->separator[u + 1]){ + case 0: + u--; + //fall through + case '0': + config->separator[p] = 0; + break; + case 't': + config->separator[p] = '\t'; + break; + case 'n': + config->separator[p] = '\n'; + break; + case 'f': + config->separator[p] = '\f'; + break; + case 'r': + config->separator[p] = '\r'; + break; + case '\\': + config->separator[p] = '\\'; + break; + case 'x': + if(!isxdigit(config->separator[u + 2]) + || !isxdigit(config->separator[u + 3])){ + fprintf(stderr, "Prematurely terminated hex byte sequence in separator framing function\n"); + return -1; + } + sscanf((char*) (config->separator + u + 3), "%02x", &hex); + config->separator[p] = hex; + u += 2; + } + u++; + } + p++; + } + config->length = p; + *framing_data = config; + } + else if(!data && config){ + //free parsed configuration + free(config->separator); + config->separator = NULL; + config->length = 0; + free(config); + return 0; + } + + if(config->length){ + for(u = 0; u < last_read && (last_read - u) >= config->length; u++){ + if(!memcmp(data + (length - last_read) + u, config->separator, config->length)){ + return (length - last_read) + u + config->length; + } + } + } + else{ + return length; + } + return 0; } -int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){ - //TODO implement separator framer - return length; +/* + * The `newline` framing function waits until a newline sequence is found in the buffer and sends all data up to and + * including the newline as text frame, if the data is detected as valid UTF-8. Otherwise, a binary frame is used. + * The configuration string may be one of + * * crlf + * * lfcr + * * lf + * * cr + */ +int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* framing_config){ + int64_t bytes = 0; + char* expression = "\\r\\n"; + + if(data && (!framing_data || !(*framing_data))){ + if(!strcmp(framing_config, "crlf")){ + expression = "\\r\\n"; + } + if(!strcmp(framing_config, "lfcr")){ + expression = "\\n\\r"; + } + if(!strcmp(framing_config, "lf")){ + expression = "\\n"; + } + if(!strcmp(framing_config, "cr")){ + expression = "\\r"; + } + bytes = framing_separator(data, length, last_read, opcode, framing_data, expression); + } + else{ + bytes = framing_separator(data, length, last_read, opcode, framing_data, NULL); + } + + return framing_auto(data, bytes, 0, opcode, NULL, NULL); } -- cgit v1.2.3