#include "backend_file.h" #include #include #include //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){ size_t u; //check whether the replacement fits if(variable_length < content_length && strlen(buffer) + (content_length - variable_length) >= buffer_length){ fprintf(stderr, "Expression replacement buffer overrun: replacing %lu bytes with %lu bytes, current buffer used %lu, max %lu\n", variable_length, content_length, strlen(buffer), buffer_length); 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); //sanitize replacement for(u = 0; u < content_length; u++){ if(buffer[u] == '/'){ buffer[u] = '_'; } } 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; } //rtrim slash so test and test/ are the same file if(strlen(endpoint) >= 2 && endpoint[strlen(endpoint) - 1] == '/'){ endpoint[strlen(endpoint) - 1] = 0; } value = endpoint + 1; value_len = strlen(value); 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; } //find the cookie header for(p = 0; p < headers; p++){ if(!strcmp(header[p].tag, "Cookie")){ value = header[p].value; break; } } //no cookie header, fail the expression if(p == headers){ return 1; } do{ //ensure min length of cookie name if(strlen(value) < index_len + 1){ return 1; } //check if cookie found if(value[index_len] == '=' && !strncmp(value, index, index_len)){ value += index_len + 1; for(value_len = 0; value[value_len] && value[value_len] != ';'; value_len++){ } break; } //skip to next cookie for(; *value && strncmp(value, "; ", 2); value++){ } if(*value){ value += 2; } } while(*value); if(!*value){ return 1; } variable_len = 8 + index_len + 1; } 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; } if(value){ //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, p, line_alloc = 0; ssize_t line_length = 0; char* line = NULL, *components[3]; FILE* input = NULL; char target_path[BACKEND_FILE_MAX_PATH]; ws_peer_info peer = { .transport = peer_transport_detect, .protocol = protocols }; 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)){ memset(components, 0, sizeof(components)); //rtrim line p = strlen(line); for(; p > 0 && !isprint(line[p]); p--){ line[p] = 0; } //read lines of host subproto framing framing-config components[0] = strchr(line, ' '); if(protocols && !components[0]){ continue; } //terminate host components[0][0] = 0; components[0]++; //find framing, terminate subprotocol components[1] = strchr(components[0], ' '); if(components[1]){ components[1][0] = 0; components[1]++; //find find framing config, terminate framing components[2] = strchr(components[1], ' '); if(components[2]){ components[2][0] = 0; components[2]++; } } //find a match for any indicated protocol for(p = 0; p < protocols; p++){ if(!strcmp(components[0], protocol[p])){ peer.protocol = p; break; } } //for '*' use the first available protocol if(components[0] && !strcmp(components[0], "*")){ peer.protocol = 0; } //the current line does not match any indicated protocols if(protocols && peer.protocol == protocols){ continue; } //copy data to peer info structure peer.host = strdup(line); peer.framing = core_framing(components[1]); peer.framing_config = components[2] ? strdup(components[2]) : NULL; break; } 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; }