diff options
author | cbdev <cb@cbcdn.com> | 2019-06-01 21:27:52 +0200 |
---|---|---|
committer | cbdev <cb@cbcdn.com> | 2019-06-01 21:27:52 +0200 |
commit | b9297647c68c963a977e2c18f421211e24f9e85a (patch) | |
tree | 173d0533f7c76665dea9956ed4da4f3228e01dc9 | |
parent | 92a291b5fae32caba2212192bc94b8ef568c362d (diff) | |
download | websocksy-b9297647c68c963a977e2c18f421211e24f9e85a.tar.gz websocksy-b9297647c68c963a977e2c18f421211e24f9e85a.tar.bz2 websocksy-b9297647c68c963a977e2c18f421211e24f9e85a.zip |
Implement file backend
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | makefile | 6 | ||||
-rw-r--r-- | plugin.c | 15 | ||||
-rw-r--r-- | plugins/backend_file.c | 204 | ||||
-rw-r--r-- | plugins/backend_file.h | 6 | ||||
-rw-r--r-- | plugins/makefile | 13 | ||||
-rw-r--r-- | websocksy.c | 1 |
7 files changed, 234 insertions, 12 deletions
@@ -1,3 +1,4 @@ *.swp websocksy *.o +*.so @@ -1,3 +1,4 @@ +.PHONY: all clean plugins PLUGINPATH=plugins/ CFLAGS=-g -Wall -Wpedantic -DPLUGINS=\"$(PLUGINPATH)\" @@ -7,7 +8,10 @@ OBJECTS=builtins.o network.o websocket.o plugin.o all: websocksy -websocksy: websocksy.c websocksy.h $(OBJECTS) +plugins: + $(MAKE) -C plugins + +websocksy: websocksy.c websocksy.h $(OBJECTS) plugins $(CC) $(CFLAGS) $(LDLIBS) $< -o $@ $(OBJECTS) clean: @@ -43,20 +43,12 @@ int plugin_framing_load(char* path){ DIR* directory = opendir(path); struct dirent* file = NULL; char plugin_path[MAX_PLUGIN_PATH] = ""; - size_t path_len; - if(strlen(path) >= sizeof(plugin_path) - 20){ + if(strlen(path) >= sizeof(plugin_path) - 20 || strlen(path) == 0){ fprintf(stderr, "Plugin path length exceeds limit\n"); return 1; } - strncpy(plugin_path, path, sizeof(plugin_path) - 2); - if(path[strlen(path) - 1] != '/'){ - plugin_path[strlen(path)] = '/'; - plugin_path[strlen(path) + 1] = 0; - } - path_len = strlen(plugin_path); - if(!directory){ fprintf(stderr, "Failed to open directory %s: %s\n", path, strerror(errno)); return 0; @@ -71,7 +63,8 @@ int plugin_framing_load(char* path){ if(strlen(file->d_name) < 4 || strcmp(file->d_name + strlen(file->d_name) - 3, ".so")){ continue; } - strncpy(plugin_path + path_len, file->d_name, sizeof(plugin_path) - path_len - 2); + + snprintf(plugin_path, sizeof(plugin_path), "%s%s%s", path, (path[strlen(path) - 1] == '/') ? "" : "/", file->d_name); if(!plugin_attach(plugin_path)){ return 1; } @@ -85,7 +78,7 @@ int plugin_backend_load(char* path, char* backend_requested, ws_backend* backend char plugin_path[MAX_PLUGIN_PATH] = ""; void* handle = NULL; - if(strlen(path) >= sizeof(plugin_path) - 30){ + if(strlen(path) >= sizeof(plugin_path) - 30 || strlen(path) == 0){ fprintf(stderr, "Plugin path length exceeds limit\n"); return 1; } diff --git a/plugins/backend_file.c b/plugins/backend_file.c new file mode 100644 index 0000000..c3ed8ea --- /dev/null +++ b/plugins/backend_file.c @@ -0,0 +1,204 @@ +#include "backend_file.h" +#include <string.h> +#include <stdio.h> + +//FIXME allocated this statically because i dont want to do it properly right now tbh +#define BACKEND_FILE_MAX_PATH 8192 + +static char* backend_path = NULL; + +size_t expressions = 0; +static char** expression = NULL; + +uint64_t init(){ + //initialize the backend path to empty to be able to use it without problems later + backend_path = strdup(""); + + if(!backend_path){ + fprintf(stderr, "Failed to allocate memory\n"); + return 0; + } + return WEBSOCKSY_API_VERSION; +} + +uint64_t configure(char* key, char* value){ + if(!strcmp(key, "path")){ + free(backend_path); + backend_path = strdup(value); + if(!backend_path){ + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + + if(strlen(backend_path) && backend_path[strlen(backend_path) - 1] == '/'){ + backend_path[strlen(backend_path) - 1] = 0; + } + return 0; + } + else if(!strcmp(key, "expression")){ + expression = realloc(expression, (expressions + 1) * sizeof(char*)); + if(!expression){ + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + expression[expressions] = strdup(value); + if(!expression[expressions]){ + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + expressions++; + return 0; + } + + fprintf(stderr, "Unknown backend configuration option %s\n", key); + return 1; +} + +static int expression_replace(char* buffer, size_t buffer_length, size_t variable_length, char* content, size_t content_length){ + //check whether the replacement fits + if(variable_length < content_length && strlen(buffer) + (content_length - variable_length) >= buffer_length){ + fprintf(stderr, "Expression replacement buffer overrun\n"); + return 1; + } + + //move data after the replacement + memmove(buffer + content_length, buffer + variable_length, strlen(buffer) - variable_length + 1); + + //insert replacement + memcpy(buffer, content, content_length); + + return 0; +} + +static int expression_resolve(char* template, size_t length, char* endpoint, size_t headers, ws_http_header* header){ + size_t u, index_len, p, value_len, variable_len; + char* index, *value; + + for(u = 0; template[u]; u++){ + index_len = 0; + variable_len = 0; + value = NULL; + if(template[u] == '%'){ + if(!strncmp(template + u, "%endpoint%", 10)){ + if(strlen(endpoint) < 1){ + return 1; + } + + //TODO rtrim slash + //replace with sanitized endpoint string + for(p = 0; endpoint[p]; p++){ + if(endpoint[p] == '/'){ + endpoint[p] = '_'; + } + } + + value = endpoint + 1; + value_len = p - 1; + variable_len = 10; + } + else if(!strncmp(template + u, "%cookie:", 8)){ + //scan cookie values + index = template + u + 8; + for(; index[index_len] && index[index_len] != '%'; index_len++){ + } + if(!index[index_len]){ + fprintf(stderr, "Unterminated expression variable: %s\n", index); + return 1; + } + + //TODO iterate cookies + } + else if(!strncmp(template + u, "%header:", 8)){ + //scan headers + index = template + u + 8; + for(; index[index_len] && index[index_len] != '%'; index_len++){ + } + if(!index[index_len]){ + fprintf(stderr, "Unterminated expression variable: %s\n", index); + return 1; + } + //find the correct header + for(p = 0; p < headers; p++){ + if(strlen(header[p].tag) == index_len + && !strncmp(header[p].tag, index, index_len)){ + break; + } + } + + //no such header -> fail the expression + if(p == headers){ + return 1; + } + + value = header[p].value; + value_len = strlen(value); + variable_len = 8 + index_len + 1; + } + + //perform replacement + if(value && expression_replace(template + u, length - u, variable_len, value, value_len)){ + fprintf(stderr, "Expression replacement failed\n"); + return 1; + } + + //skip the inserted value + u += value_len - 1; + } + } + + return 0; +} + +ws_peer_info query(char* endpoint, size_t protocols, char** protocol, size_t headers, ws_http_header* header, websocket* ws){ + size_t u, line_alloc = 0; + ssize_t line_length = 0; + char* line = NULL; + FILE* input = NULL; + char target_path[BACKEND_FILE_MAX_PATH]; + ws_peer_info peer = { + .transport = peer_transport_detect, + }; + + for(u = 0; u < expressions; u++){ + //evaluate the current expression to find a path + snprintf(target_path, sizeof(target_path), "%s/%s", backend_path, expression[u]); + if(expression_resolve(target_path + strlen(backend_path) + 1, sizeof(target_path) - strlen(backend_path) - 1, endpoint, headers, header)){ + continue; + } + + //check whether the file exists + input = fopen(target_path, "r"); + if(!input){ + continue; + } + + //read it + for(line_length = getline(&line, &line_alloc, input); line_length >= 0; line_length = getline(&line, &line_alloc, input)){ + //TODO evaluate line in file + fprintf(stderr, "File %s, line %s\n", target_path, line); + } + fclose(input); + + //if peer found, break + if(peer.host){ + break; + } + } + + free(line); + return peer; +} + +void cleanup(){ + size_t u; + + for(u = 0; u < expressions; u++){ + free(expression[u]); + } + free(expression); + expression = NULL; + expressions = 0; + + free(backend_path); + backend_path = NULL; +} diff --git a/plugins/backend_file.h b/plugins/backend_file.h new file mode 100644 index 0000000..04b3528 --- /dev/null +++ b/plugins/backend_file.h @@ -0,0 +1,6 @@ +#include "../websocksy.h" + +uint64_t init(); +uint64_t configure(char* key, char* value); +ws_peer_info query(char* endpoint, size_t protocols, char** protocol, size_t headers, ws_http_header* header, websocket* ws); +void cleanup(); diff --git a/plugins/makefile b/plugins/makefile new file mode 100644 index 0000000..6b413db --- /dev/null +++ b/plugins/makefile @@ -0,0 +1,13 @@ +.PHONY: all clean +PLUGINS = backend_file.so + +CFLAGS += -fPIC -I../ +LDFLAGS += -shared + +%.so :: %.c %.h + $(CC) $(CFLAGS) $(LDLIBS) $< -o $@ $(LDFLAGS) + +all: $(PLUGINS) + +clean: + $(RM) $(PLUGINS) diff --git a/websocksy.c b/websocksy.c index f625174..c635ca7 100644 --- a/websocksy.c +++ b/websocksy.c @@ -130,6 +130,7 @@ int client_connect(websocket* ws){ ws->peer = config.backend.query(ws->request_path, ws->protocols, ws->protocol, ws->headers, ws->header, ws); if(!ws->peer.host){ //TODO check port if network socket + //TODO try to extract port from host if none given //no peer provided return 1; } |