diff options
-rw-r--r-- | builtins.c | 19 | ||||
-rw-r--r-- | builtins.h | 8 | ||||
-rw-r--r-- | makefile | 6 | ||||
-rw-r--r-- | plugin.c | 77 | ||||
-rw-r--r-- | plugin.h | 10 | ||||
-rw-r--r-- | websocket.c | 27 | ||||
-rw-r--r-- | websocksy.c | 41 | ||||
-rw-r--r-- | websocksy.h | 2 |
8 files changed, 165 insertions, 25 deletions
@@ -2,6 +2,7 @@ #include "websocksy.h" #include "builtins.h" +#include "plugin.h" #define UTF8_BYTE(a) ((a & 0xC0) == 0x80) @@ -47,6 +48,13 @@ uint64_t backend_defaultpeer_configure(char* key, char* value){ free(default_peer_proto); default_peer_proto = strdup(value); } + else if(!strcmp(key, "framing")){ + default_peer.framing = plugin_framing(value); + } + else if(!strcmp(key, "framing-config")){ + free(default_peer.framing_config); + default_peer.framing_config = strdup(value); + } return 1; } @@ -61,6 +69,7 @@ ws_peer_info backend_defaultpeer_query(char* endpoint, size_t protocols, char** ws_peer_info peer = default_peer; peer.host = (default_peer.host) ? strdup(default_peer.host) : NULL; peer.port = (default_peer.port) ? strdup(default_peer.port) : NULL; + peer.framing_config = (default_peer.framing_config) ? strdup(default_peer.framing_config) : NULL; //if none set, announce none peer.protocol = protocols; @@ -90,6 +99,8 @@ void backend_defaultpeer_cleanup(){ default_peer.host = NULL; free(default_peer.port); default_peer.port = NULL; + free(default_peer.framing_config); + default_peer.framing_config = NULL; free(default_peer_proto); default_peer_proto = NULL; } @@ -98,7 +109,7 @@ void backend_defaultpeer_cleanup(){ * The `auto` stream framing function forwards all incoming data from the peer immediately, * with the text frame type if the data is valid UTF8 and the binary frame type otherwise. */ -int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){ +int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){ size_t p; uint8_t valid = 1; for(p = 0; p < length; p++){ @@ -150,16 +161,16 @@ int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operatio /* * The `binary` peer stream framing function forwards all incoming data from the peer immediately as binary frames */ -int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){ +int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){ return length; } -int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){ +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; } -int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){ +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; } @@ -5,7 +5,7 @@ ws_peer_info backend_defaultpeer_query(char* endpoint, size_t protocols, char** void backend_defaultpeer_cleanup(); /* Built-in framing functions */ -int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); -int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); -int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); -int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); +int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config); +int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config); +int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config); +int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config); @@ -1,7 +1,9 @@ -CFLAGS=-g -Wall -Wpedantic +PLUGINPATH=plugins/ + +CFLAGS=-g -Wall -Wpedantic -DPLUGINS=\"$(PLUGINPATH)\" LDLIBS=-lnettle -OBJECTS=builtins.o network.o websocket.o +OBJECTS=builtins.o network.o websocket.o plugin.o all: websocksy diff --git a/plugin.c b/plugin.c new file mode 100644 index 0000000..8b61ec3 --- /dev/null +++ b/plugin.c @@ -0,0 +1,77 @@ +#include <string.h> +#include <stdio.h> + +#include "websocksy.h" +#include "plugin.h" + +static size_t framing_functions = 0; +static ws_framing* framing_function = NULL; +static char** framing_function_name = NULL; + +int plugin_framing_load(char* path){ + //TODO load plugins + return 1; +} + +int plugin_backend_load(char* backend_requested, ws_backend* backend){ + //TODO load backend + return 1; +} + +int plugin_register_framing(char* name, ws_framing func){ + size_t u; + + for(u = 0; u < framing_functions; u++){ + if(!strcmp(framing_function_name[u], name)){ + fprintf(stderr, "Replacing framing %s\n", name); + break; + } + } + + if(u == framing_functions){ + framing_function = realloc(framing_function, (framing_functions + 1) * sizeof(ws_framing)); + framing_function_name = realloc(framing_function_name, (framing_functions + 1) * sizeof(char*)); + if(!framing_function || !framing_function_name){ + fprintf(stderr, "Failed to allocate memory for framing function\n"); + return 1; + } + + framing_function_name[u] = strdup(name); + framing_functions++; + } + + framing_function[u] = func; + return 0; +} + +ws_framing plugin_framing(char* name){ + size_t u; + + if(!name){ + return plugin_framing("auto"); + } + + for(u = 0; u < framing_functions; u++){ + if(!strcmp(framing_function_name[u], name)){ + return framing_function[u]; + } + } + + //if unknown framing, return the default + return plugin_framing("auto"); +} + +void plugin_cleanup(){ + size_t u; + //TODO dlclose all plugins + + for(u = 0; u < framing_functions; u++){ + free(framing_function_name[u]); + } + free(framing_function); + framing_function = NULL; + free(framing_function_name); + framing_function_name = 0; + framing_function = NULL; + framing_functions = 0; +} diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000..f7dcd11 --- /dev/null +++ b/plugin.h @@ -0,0 +1,10 @@ +/* Shared object handling */ +int plugin_framing_load(char* path); +int plugin_backend_load(char* backend_requested, ws_backend* backend); + +/* Framing function registry */ +int plugin_register_framing(char* name, ws_framing func); +ws_framing plugin_framing(char* name); + +/* Module management */ +void plugin_cleanup(); diff --git a/websocket.c b/websocket.c index c90c71e..ab33a88 100644 --- a/websocket.c +++ b/websocket.c @@ -26,7 +26,17 @@ int ws_close(websocket* ws, ws_close_reason code, char* reason){ }; if(ws->state == ws_open && reason){ - //TODO send close frame + //send close frame + //FIXME this should prepend the status code to the reason + ws_send_frame(ws, ws_frame_close, (uint8_t*) reason, strlen(reason)); + } + else if(ws->state == ws_http + && code == ws_close_http + && reason){ + //send http response + network_send_str(ws->ws_fd, "HTTP/1.1 "); + network_send_str(ws->ws_fd, reason); + network_send_str(ws->ws_fd, "\r\n\r\n"); } ws->state = ws_closed; @@ -349,17 +359,17 @@ static size_t ws_frame(websocket* ws){ //TODO handle fragmentation //TODO handle control frames within fragmented frames - fprintf(stderr, "Incoming websocket data: %s %s OP %02X LEN %u %lu\n", + /*fprintf(stderr, "Incoming websocket data: %s %s OP %02X LEN %u %lu\n", WS_GET_FIN(ws->read_buffer[0]) ? "FIN" : "CONT", WS_GET_MASK(ws->read_buffer[1]) ? "MASK" : "CLEAR", WS_GET_OP(ws->read_buffer[0]), WS_GET_LEN(ws->read_buffer[1]), - payload_length); + payload_length);*/ //handle data switch(WS_GET_OP(ws->read_buffer[0])){ case ws_frame_text: - fprintf(stderr, "Text payload: %.*s\n", (int) payload_length, (char*) payload); + //fprintf(stderr, "Text payload: %.*s\n", (int) payload_length, (char*) payload); case ws_frame_binary: //forward to peer if(ws->peer_fd >= 0){ @@ -373,12 +383,13 @@ static size_t ws_frame(websocket* ws){ ws_close(ws, ws_close_normal, "Client requested termination"); break; case ws_frame_ping: - break; - case ws_frame_pong: if(ws_send_frame(ws, ws_frame_pong, payload, payload_length)){ - ws_close(ws, ws_close_unexpected, "Failed to send ping"); + ws_close(ws, ws_close_unexpected, "Failed to send pong"); } break; + case ws_frame_pong: + //TODO keep-alive pings + break; default: //unknown frame type received fprintf(stderr, "Unknown WebSocket opcode %02X in frame\n", WS_GET_OP(ws->read_buffer[0])); @@ -390,7 +401,7 @@ static size_t ws_frame(websocket* ws){ } int ws_send_frame(websocket* ws, ws_operation opcode, uint8_t* data, size_t len){ - fprintf(stderr, "Peer -> WS %lu bytes\n", len); + fprintf(stderr, "Peer -> WS %lu bytes (%02X)\n", len, opcode); uint8_t frame_header[WS_FRAME_HEADER_LEN]; size_t header_bytes = 2; uint16_t* payload_len16 = (uint16_t*) (frame_header + 2); diff --git a/websocksy.c b/websocksy.c index 55a6d6a..4846945 100644 --- a/websocksy.c +++ b/websocksy.c @@ -14,6 +14,7 @@ #include "builtins.h" #include "network.h" #include "websocket.h" +#include "plugin.h" #define DEFAULT_HOST "::" #define DEFAULT_PORT "8001" @@ -21,6 +22,7 @@ /* TODO * - TLS * - framing function discovery / registry + * - WS p2p */ /* Main loop condition, to be set from signal handler */ @@ -203,7 +205,18 @@ static int args_parse(int argc, char** argv){ config.host = argv[u + 1]; break; case 'b': - //TODO load peer discovery plugin + //clean up the previously registered backend + if(config.backend.cleanup){ + config.backend.cleanup(); + } + //load the backend plugin + if(plugin_backend_load(argv[u + 1], &(config.backend))){ + return 1; + } + if(config.backend.init() != WEBSOCKSY_API_VERSION){ + fprintf(stderr, "Loaded backend %s was built for a different API version\n", argv[u + 1]); + return 1; + } break; case 'c': if(!strchr(argv[u + 1], '=')){ @@ -286,17 +299,32 @@ int main(int argc, char** argv){ size_t n; int listen_fd = -1, status, max_fd; - //parse command line arguments - if(args_parse(argc - 1, argv + 1)){ - exit(usage(argv[0])); + //register default framing functions before parsing arguments, as they may be assigned within a backend configuration + if(plugin_register_framing("auto", framing_auto) + || plugin_register_framing("binary", framing_binary) + || plugin_register_framing("separator", framing_separator) + || plugin_register_framing("newline", framing_newline)){ + fprintf(stderr, "Failed to initialize builtins\n"); + exit(EXIT_FAILURE); } - //initialize the selected peer discovery backend + //load plugin framing functions + if(plugin_framing_load(PLUGINS)){ + fprintf(stderr, "Failed to load plugins\n"); + exit(EXIT_FAILURE); + } + + //initialize the default backend if(config.backend.init() != WEBSOCKSY_API_VERSION){ - fprintf(stderr, "The selected backend was built for another API version than the core\n"); + fprintf(stderr, "Failed to initialize builtin backend\n"); exit(EXIT_FAILURE); } + //parse command line arguments + if(args_parse(argc - 1, argv + 1)){ + exit(usage(argv[0])); + } + //open listening socket listen_fd = network_socket(config.host, config.port, SOCK_STREAM, 1); if(listen_fd < 0){ @@ -371,6 +399,7 @@ int main(int argc, char** argv){ config.backend.cleanup(); } client_cleanup(); + plugin_cleanup(); close(listen_fd); return 0; } diff --git a/websocksy.h b/websocksy.h index c7311ca..9cf1588 100644 --- a/websocksy.h +++ b/websocksy.h @@ -85,7 +85,7 @@ typedef struct /*_ws_http_header*/ { * NULL `data` pointer as an indication the any allocation within `framing_data` is to be freed. * The return value is the number of bytes to be sent to the peer. */ -typedef int64_t (*ws_framing)(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config); +typedef int64_t (*ws_framing)(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config); /* Peer connection modes */ typedef enum { |