aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtins.c19
-rw-r--r--builtins.h8
-rw-r--r--makefile6
-rw-r--r--plugin.c77
-rw-r--r--plugin.h10
-rw-r--r--websocket.c27
-rw-r--r--websocksy.c41
-rw-r--r--websocksy.h2
8 files changed, 165 insertions, 25 deletions
diff --git a/builtins.c b/builtins.c
index 3461521..5c1dfb8 100644
--- a/builtins.c
+++ b/builtins.c
@@ -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;
}
diff --git a/builtins.h b/builtins.h
index 926d5bb..ebc1f87 100644
--- a/builtins.h
+++ b/builtins.h
@@ -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);
diff --git a/makefile b/makefile
index 4623e49..eb7a579 100644
--- a/makefile
+++ b/makefile
@@ -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 {