From a78815cb329cbbdee3f8682828d961ae5a9f612e Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Sat, 13 Jun 2020 19:21:32 +0200 Subject: [PATCH] add: endpoint /tags fix: leaks from config (now using static length for config strings) --- config.c | 5 --- endpoints/api_v1_tags.c | 35 ++++++++++++++++++++ include/config.h | 10 +++--- include/endpoints/api_v1_tags.h | 9 +++++ include/models/tag.h | 4 ++- main.c | 9 +++-- models/tag.c | 44 +++++++++++++++++++++++++ router.c | 4 +++ tests/tavern_tests/3.0.tags.tavern.yaml | 9 +++++ tests/tavern_utils/validate_tag.py | 34 +++++++++++++++++++ 10 files changed, 147 insertions(+), 16 deletions(-) create mode 100644 endpoints/api_v1_tags.c create mode 100644 include/endpoints/api_v1_tags.h create mode 100644 tests/tavern_utils/validate_tag.py diff --git a/config.c b/config.c index 60f9d23..c363b7a 100644 --- a/config.c +++ b/config.c @@ -61,31 +61,26 @@ config_load(IniDispatch *disp, void *config_void) } if(CONFINI_IS_KEY("core", "database")) { - config->database = malloc(sizeof(char) * (strlen(disp->value) + 1)); strcpy(config->database, disp->value); return 0; } if(CONFINI_IS_KEY("core", "not-found-file")) { - config->not_found_file = malloc(sizeof(char) * (strlen(disp->value) + 1)); strcpy(config->not_found_file, disp->value); return 0; } if(CONFINI_IS_KEY("core", "not-found-file-type")) { - config->not_found_file_type = malloc(sizeof(char) * (strlen(disp->value) + 1)); strcpy(config->not_found_file_type, disp->value); return 0; } if(CONFINI_IS_KEY("core", "not-found-content")) { - config->not_found_content = malloc(sizeof(char) * (strlen(disp->value) + 1)); strcpy(config->not_found_content, disp->value); return 0; } if(CONFINI_IS_KEY("core", "not-found-content-type")) { - config->not_found_content_type = malloc(sizeof(char) * (strlen(disp->value) + 1)); strcpy(config->not_found_content_type, disp->value); return 0; } diff --git a/endpoints/api_v1_tags.c b/endpoints/api_v1_tags.c new file mode 100644 index 0000000..6ee6226 --- /dev/null +++ b/endpoints/api_v1_tags.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include + +void +api_v1_tags_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response) +{ + (void)args; + (void)hm; + (void)nc; + + char** all_tags = tag_get_all(); + + cJSON *json = cJSON_CreateArray(); + + for(int i = 0; all_tags[i] != NULL; ++i) + { + cJSON *json_tag = cJSON_CreateString(all_tags[i]); + if (json_tag == NULL) + { + LOG_DEBUG("failed to add tag from string '%s'\n", all_tags[i]); + free(all_tags[i]); + continue; + } + cJSON_AddItemToArray(json, json_tag); + free(all_tags[i]); + } + + endpoint_response_json(response, 200, json); + cJSON_Delete(json); + free(all_tags); +} diff --git a/include/config.h b/include/config.h index 17d6db5..1749cbe 100644 --- a/include/config.h +++ b/include/config.h @@ -25,15 +25,15 @@ typedef enum typedef struct { char *file; - char *database; + char database[256]; log_level_t log_level; run_type_t run_type; char server_port[6]; uint16_t discovery_port; - char *not_found_file; - char *not_found_file_type; - char *not_found_content; - char *not_found_content_type; + char not_found_file[256]; + char not_found_file_type[256]; + char not_found_content[256]; + char not_found_content_type[256]; struct mg_serve_http_opts http_server_opts; } config_t; diff --git a/include/endpoints/api_v1_tags.h b/include/endpoints/api_v1_tags.h new file mode 100644 index 0000000..fa716c8 --- /dev/null +++ b/include/endpoints/api_v1_tags.h @@ -0,0 +1,9 @@ +#ifndef CORE_ENDPOINTS_API_V1_TAGS_H +#define CORE_ENDPOINTS_API_V1_TAGS_H + +#include + +void +api_v1_tags_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response); + +#endif /* CORE_ENDPOINTS_API_V1_TAGS_H */ diff --git a/include/models/tag.h b/include/models/tag.h index 79f96fe..fb5580f 100644 --- a/include/models/tag.h +++ b/include/models/tag.h @@ -10,8 +10,10 @@ tag_remove(int id); char* tag_get_tag(int id); +char** +tag_get_all(); + int tag_get_id(const char* tag); - #endif /* CORE_MODELS_TAG_H */ diff --git a/main.c b/main.c index f470e0a..303a238 100644 --- a/main.c +++ b/main.c @@ -25,8 +25,6 @@ terminate(int signum) sqlite3_close(global_database); - free(global_config.database); - router_free(); exit(signum); @@ -51,9 +49,10 @@ main(int argc, const char** argv) /******************** LOAD CONFIG ********************/ global_config.file = "core.ini"; - global_config.not_found_file = "404.html"; - global_config.not_found_content = "404 - NOT FOUND"; - global_config.not_found_content_type = "text/plain"; + strcpy(global_config.not_found_file, "404.html"); + strcpy(global_config.not_found_file_type, "text/html"); + strcpy(global_config.not_found_content, "404 - NOT FOUND"); + strcpy(global_config.not_found_content_type, "text/plain"); global_config.log_level = LOG_LEVEL_INFO; helper_parse_cli(argc, argv, &global_config); diff --git a/models/tag.c b/models/tag.c index 1a0802d..fda995e 100644 --- a/models/tag.c +++ b/models/tag.c @@ -77,6 +77,50 @@ tag_get_tag(int id) return result; } +char** +tag_get_all() +{ + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT tag FROM tags;", -1, &stmt, NULL); + + char **all_tags = malloc(sizeof(char*)); + + int row = 0; + + while(true) + { + int s; + + s = sqlite3_step(stmt); + if (s == SQLITE_ROW) + { + const char *new_tag = (const char *)sqlite3_column_text(stmt, 0); + int new_tag_len = strlen(new_tag); + row++; + + all_tags = (char**)realloc(all_tags, sizeof(char*) * (row + 1)); + all_tags[row - 1] = malloc(sizeof(char) * (new_tag_len + 1)); + strcpy(all_tags[row - 1], new_tag); + } + else + { + if(s == SQLITE_DONE) + { + break; + } + else + { + LOG_ERROR("error selecting tags from database: %s\n", sqlite3_errstr(s)); + break; + } + } + } + sqlite3_finalize(stmt); + all_tags[row] = NULL; + return all_tags; +} + int tag_get_id(const char *tag) { diff --git a/router.c b/router.c index a4b477a..13d7d19 100644 --- a/router.c +++ b/router.c @@ -8,6 +8,7 @@ #include #include #include +#include static endpoint_t endpoints[ROUTER_ENDPOINTS_MAX_COUNT]; static endpoint_t endpoint_not_found; @@ -75,6 +76,8 @@ router_init() router_register_endpoint("/api/v1/relays/", HTTP_METHOD_GET, api_v1_relays_GET); router_register_endpoint("/api/v1/relays/tag/{str}", HTTP_METHOD_GET, api_v1_relays_tag_STR_GET); + + router_register_endpoint("/api/v1/tags/", HTTP_METHOD_GET, api_v1_tags_GET); } endpoint_t* @@ -304,6 +307,7 @@ router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_ if(best_endpoint == NULL) { + free(uri); return NULL; } diff --git a/tests/tavern_tests/3.0.tags.tavern.yaml b/tests/tavern_tests/3.0.tags.tavern.yaml index 08a28db..d4ef67f 100644 --- a/tests/tavern_tests/3.0.tags.tavern.yaml +++ b/tests/tavern_tests/3.0.tags.tavern.yaml @@ -97,3 +97,12 @@ stages: number: !int "{returned_number:d}" controller_id: "{returned_id}" tag: "{returned_tag}" + +- name: "[tags] get tags" + request: + method: GET + url: "http://localhost:5000/api/v1/tags/" + response: + status_code: 200 + verify_response_with: + function: validate_tag:multiple diff --git a/tests/tavern_utils/validate_tag.py b/tests/tavern_utils/validate_tag.py new file mode 100644 index 0000000..1e907e5 --- /dev/null +++ b/tests/tavern_utils/validate_tag.py @@ -0,0 +1,34 @@ +import json + +def _verify_single(tag): + assert isinstance(tag, str), "tag is not a string" + +def single(response): + _verify_single(response.json()) + +def multiple(response): + assert isinstance(response.json(), list), "response is not a list" + for tag in response.json(): + _verify_single(tag) + +#def find(response, name=None, number=None, controller_id=None, tag=None): +# print(response.json()) +# for tag in response.json(): +# if number != None and number != tag.get("number"): +# continue +# +# if name != None and name != tag.get("name"): +# continue +# +# if controller_id != None and controller_id != tag.get("controller_id"): +# continue +# +# if tag != None: +# found_in_response = False +# for response_tag in tag.get("tags"): +# if response_tag == tag: +# found_in_response = True +# if not found_in_response: +# continue +# return +# assert False, "tag not found in list"