From: dhfelix Date: Mon, 11 Feb 2019 22:56:20 +0000 (-0600) Subject: Merge branch 'master' into args-parser X-Git-Tag: v0.0.2~87^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cd082fc8544f393631a35c5681df77ceab4ad690;p=thirdparty%2FFORT-validator.git Merge branch 'master' into args-parser Conflicts: src/config.c src/config.h src/main.c --- cd082fc8544f393631a35c5681df77ceab4ad690 diff --cc src/Makefile.am index fe0cf7fe,afaebd34..2c581e78 --- a/src/Makefile.am +++ b/src/Makefile.am @@@ -21,8 -22,6 +22,7 @@@ rpki_validator_SOURCES += sorted_array. rpki_validator_SOURCES += state.h state.c rpki_validator_SOURCES += thread_var.h thread_var.c rpki_validator_SOURCES += uri.h uri.c - rpki_validator_SOURCES += config.h config.c +rpki_validator_SOURCES += toml_handler.h toml_handler.c rpki_validator_SOURCES += rsync/rsync.h rsync/rsync.c diff --cc src/config.c index df9d526d,f60731ac..6b25b719 --- a/src/config.c +++ b/src/config.c @@@ -1,341 -1,39 +1,451 @@@ #include "config.h" - #include +#include - #include +#include ++#include ++#include + +#include "common.h" +#include "log.h" + +#define OPT_FIELD_ARRAY_LEN(array) ARRAY_LEN(array) - 1 + - static int parse_bool(struct option_field *, char *, void *); - +struct args_flag { + struct option_field *field; + bool is_set; +}; + ++static int parse_bool(struct option_field *, char *, void *); ++static int parse_u_int(struct option_field *, char *, void *); ++ + static struct rpki_config config; + +static struct global_type gt_bool = { + .id = GTI_BOOL, + .name = "Boolean", + .size = sizeof(bool), + .parse = parse_bool, + .candidates = "true false", +}; + +static struct global_type gt_string = { + .id = GTI_STRING, + .name = "String", +}; + ++static struct global_type gt_u_int = { ++ .id = GTI_U_INT, ++ .name = "Unsigned Int", ++ .size = sizeof(unsigned int), ++ .parse = parse_u_int, ++ .candidates = "Unsigned Int", ++}; ++ +static struct option_field global_fields[] = { + { + .name = "local-repository", + .type = >_string, + .doc = "Local repository path.", + .offset = offsetof(struct rpki_config, local_repository), + .has_arg = required_argument, + .short_opt = 0, + .required = true, + .candidates = "path", + }, { + .name = "enable-rsync", + .type = >_bool, + .doc = "Enable or disable rsync downloads.", + .offset = offsetof(struct rpki_config, enable_rsync), + .has_arg = optional_argument, + .short_opt = 0, + .required = false, + }, + { NULL }, +}; + +static struct option_field tal_fields[] = { + { + .name = "tal", + .type = >_string, + .doc = "TAL file path", + .offset = offsetof(struct rpki_config, tal), + .has_arg = required_argument, + .short_opt = 0, + .required = true, + .candidates = "file", + }, { + .name = "shuffle-uris", + .type = >_bool, + .doc = "Shuffle URIs in the TAL.", + .offset = offsetof(struct rpki_config, shuffle_uris), + .has_arg = optional_argument, + .short_opt = 0, + .required = false, ++ }, { ++ .name = "maximum-certificate-depth", ++ .type = >_u_int, ++ .doc = "Prevents arbitrarily long paths and loops.", ++ .offset = offsetof(struct rpki_config, ++ maximum_certificate_depth), ++ .has_arg = required_argument, ++ .short_opt = 0, ++ .min = 1, ++ /** ++ * It cannot be UINT_MAX, because then the actual number will overflow ++ * and will never be bigger than this. ++ */ ++ .max = UINT_MAX - 1, ++ .required = false, + }, + { NULL }, +}; + - static int str_to_bool(const char *str, bool *bool_out) ++static int ++str_to_bool(const char *str, bool *bool_out) +{ + if (strcasecmp(str, "true") == 0 || strcasecmp(str, "1") == 0 || + strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0) { + *bool_out = true; + return 0; + } + + if (strcasecmp(str, "false") == 0 || strcasecmp(str, "0") == 0 || + strcasecmp(str, "no") == 0 || strcasecmp(str, "off") == 0) { + *bool_out = false; + return 0; + } + + return pr_err("Cannot parse '%s' as a bool " + "(true|false|1|0|yes|no|on|off).", str); +} + - static int parse_bool(struct option_field *field, char *str, void *result) ++static int ++parse_bool(struct option_field *field, char *str, void *result) +{ + bool *value = result; + + switch (field->has_arg) { + case no_argument: + *value = true; + return 0; + break; + case required_argument: + case optional_argument: + if (field->has_arg == optional_argument && str == NULL) { + *value = true; + return 0; + } + /** + * XXX: (fine) GETOPT should had ensure that the code did + * not reach here for this particular case. + * */ + return str_to_bool(str, result); + break; + } + + if (str == NULL) { + *value = true; + return 0; + } + + return str_to_bool(str, result); +} + ++static int str_to_ull(const char *str, char **endptr, ++ const unsigned long long int min, ++ const unsigned long long int max, ++ unsigned long long int *result) ++{ ++ unsigned long long int parsed; ++ ++ errno = 0; ++ parsed = strtoull(str, endptr, 10); ++ if (errno) ++ return pr_errno(errno, "'%s' is not an unsigned integer", str); ++ ++ if (parsed < min || max < parsed) ++ return pr_err("'%s' is out of bounds (%llu-%llu).", str, min, ++ max); ++ ++ *result = parsed; ++ return 0; ++} ++ ++static int ++str_to_unsigned_int(const char *str, unsigned int *out, unsigned int min, ++ unsigned int max) ++{ ++ unsigned long long int result = 0; ++ int error; ++ ++ error = str_to_ull(str, NULL, min, max, &result); ++ ++ *out = result; ++ return error; ++} ++ ++static int ++parse_u_int(struct option_field *field, char *str, void *result) ++{ ++ unsigned int *value = result; ++ ++ if (str == NULL) ++ return pr_err("String cannot be NULL"); ++ ++ return str_to_unsigned_int(str, value, field->min, field->max); ++} ++ +static int +construct_options(struct args_flag **flags, struct option **long_options, + int *flags_len) +{ + struct args_flag *result_flags; + struct option_field *global, *tal; + struct option *result_options; + unsigned int global_len, tal_len, total_len, i, result_idx; + + get_global_fields(&global, &global_len); + get_tal_fields(&tal, &tal_len); + + total_len = global_len + tal_len; + + result_flags = calloc(total_len, sizeof(struct args_flag)); + if (result_flags == NULL) + return pr_enomem(); + + /* Long options must end with zeros (+1) */ + result_options = calloc(total_len + 1, sizeof(struct option)); + if (result_options == NULL) { + free(result_flags); + return pr_enomem(); + } + + result_idx = 0; + for(i = 0; i < global_len; i++) { + result_flags[result_idx].field = &(global[i]); + + result_options[result_idx].name = global[i].name; + result_options[result_idx].has_arg = global[i].has_arg; + result_options[result_idx].val = global[i].short_opt; + result_options[result_idx].flag = NULL; + + result_idx++; + } + + for(i = 0; i < tal_len; i++) { + result_flags[result_idx].field = &(tal[i]); + + result_options[result_idx].name = tal[i].name; + result_options[result_idx].has_arg = tal[i].has_arg; + result_options[result_idx].val = tal[i].short_opt; + result_options[result_idx].flag = NULL; + + result_idx++; + } + + *flags = result_flags; + *flags_len = total_len; + *long_options = result_options; + + return 0; +} + +static void +set_string(void **field, char *str) +{ + *field = str; +} + +static void +set_config_param_for_string(void **field, void **config_param) +{ + *config_param = *field; +} + +int +handle_option(struct rpki_config *config, struct option_field *field, char *str) +{ + void *config_param; + int error = 0; + + /** + * TODO Should we use a switch case? + * In order to avoid: + * warning: pointer of type ‘void *’ used in arithmetic + * [-Wpointer-arith] + * https://stackoverflow.com/questions/23357442/ + * dynamically-access-member-variable-of-a-structure + */ + config_param = config; + config_param += field->offset; + + if (field->type == >_string) { + set_string(config_param, str); + set_config_param_for_string(config_param, &config_param); + } else if (field->type->parse != NULL){ + error = field->type->parse(field, str, config_param); + } ++ + if (error) + return error; + + if (field->validate != NULL) + error = field->validate(field, config_param); + else if (field->type->validate != NULL) + error = field->type->validate(field, config_param); + + return error; +} + +int +check_missing_flags(struct args_flag *flag) +{ + char *candidate = NULL; + + if (flag->is_set) + return 0; + if (!flag->field->required) + return 0; + + printf("Missing flag --%s", flag->field->name); + switch (flag->field->has_arg) { + case no_argument: + break; + case optional_argument: + if (flag->field->candidates != NULL) + candidate = flag->field->candidates; + else if (flag->field->type->candidates != NULL) + candidate = flag->field->type->candidates; + if (candidate != NULL) + printf("[=%s]", candidate); + break; + case required_argument: + if (flag->field->candidates != NULL) + candidate = flag->field->candidates; + else if (flag->field->type->candidates != NULL) + candidate = flag->field->type->candidates; + if (candidate != NULL) + printf(" <%s>", candidate); + break; + default: + break; + } + + printf("\n"); + + return -ENOENT; +} + +static void +print_config(struct rpki_config *config) +{ + pr_debug("Program configuration"); + pr_debug_add("{"); + pr_debug("%s: %s", "local-repository", config->local_repository); + pr_debug("%s: %s", "tal", config->tal); + pr_debug("%s: %s", "enable-rsync", + config->enable_rsync ? "true" : "false"); + pr_debug("%s: %s", "tal.shuffle-uris", + config->shuffle_uris ? "true" : "false"); ++ pr_debug("%s: %u", "tal.maximum-certificate-depth", ++ config->maximum_certificate_depth); + pr_debug_rm("}"); +} + +int +handle_flags_config(int argc, char **argv, struct rpki_config *config) +{ + struct args_flag *flags; + struct option *long_options; + int opt, indexptr, flags_len, error = 0; + + flags = NULL; + long_options = NULL; - config->flag_config = true; + + error = construct_options(&flags, &long_options, &flags_len); + if (error) + return error; /* Error msg already printed. */ + + while ((opt = getopt_long(argc, argv, "", long_options, &indexptr)) + != -1) { + switch (opt) { + case 0: + flags[indexptr].is_set = true; + error = handle_option(config, flags[indexptr].field, + optarg); + break; + default: + error = pr_err("some usage hints.");/* TODO */ + break; + } + + if (error) + goto end; + } + + for (indexptr = 0; indexptr < flags_len; indexptr++) { + error |= check_missing_flags(&flags[indexptr]); + } + + print_config(config); + +end: + free(flags); + free(long_options); + return error; + +} + +void +get_global_fields(struct option_field **fields, unsigned int *len) +{ + if (fields) + *fields = global_fields; + if (len) + *len = OPT_FIELD_ARRAY_LEN(global_fields); +} + +void +get_tal_fields(struct option_field **fields, unsigned int *len) +{ + if (fields) + *fields = tal_fields; + if (len) + *len = OPT_FIELD_ARRAY_LEN(tal_fields); +} ++ + void + config_set(struct rpki_config *new) + { + config = *new; + } + + char const * + config_get_tal(void) + { + return config.tal; + } + + char const * + config_get_local_repository(void) + { + return config.local_repository; + } + + bool -config_get_disable_rsync(void) ++config_get_enable_rsync(void) + { - return config.disable_rsync; ++ return config.enable_rsync; + } + + bool + config_get_shuffle_uris(void) + { + return config.shuffle_uris; + } + + unsigned int + config_get_max_cert_depth(void) + { + return config.maximum_certificate_depth; + } diff --cc src/config.h index eb4c01db,b49348d0..54a64e5a --- a/src/config.h +++ b/src/config.h @@@ -9,56 -8,23 +9,68 @@@ struct rpki_config char *tal; /* Local repository path */ char *local_repository; -- /* Disable rsync downloads */ - bool disable_rsync; ++ /* Enable rsync downloads */ + bool enable_rsync; /* Shuffle uris in tal */ bool shuffle_uris; - /* Configuration file path */ - bool flag_config; + /* + * rfc6487#section-7.2, last paragraph. + * Prevents arbitrarily long paths and loops. + */ + unsigned int maximum_certificate_depth; }; +typedef enum global_type_id { + GTI_BOOL, + GTI_STRING, ++ GTI_U_INT, +} global_type_id; + +struct option_field; + +typedef void (*print_function)(void *, bool); +typedef int (*parse_function)(struct option_field *, char *, void *); +/* This function does not need to validate type->size. */ +typedef int (*validate_function)(struct option_field *, void *); + +struct global_type { + global_type_id id; + const char *name; + size_t size; + print_function print; + parse_function parse; + validate_function validate; + char *candidates; +}; + +struct option_field { + char *name; /* This being NULL means the end of the array. */ + struct global_type *type; + const char *doc; + size_t offset; + int has_arg; + char short_opt; + unsigned long min; + unsigned long max; + print_function print; /* Overrides type->print. */ + validate_function validate; /* Overrides type->validate. */ + char *candidates; /* Overrides type->candidates. */ + bool required; +}; + +int handle_option(struct rpki_config *, struct option_field *, char *); +int handle_flags_config(int , char **, struct rpki_config *); + +void get_global_fields(struct option_field **, unsigned int *); + +void get_tal_fields(struct option_field **, unsigned int *); + + void config_set(struct rpki_config *); + + char const *config_get_tal(void); + char const *config_get_local_repository(void); -bool config_get_disable_rsync(void); ++bool config_get_enable_rsync(void); + bool config_get_shuffle_uris(void); + unsigned int config_get_max_cert_depth(void); + #endif /* SRC_CONFIG_H_ */ diff --cc src/main.c index 7f0965b6,dc44fd62..ae826fbd --- a/src/main.c +++ b/src/main.c @@@ -100,46 -111,97 +112,48 @@@ end return error; } - static void - set_default_configuration(struct rpki_config *config) - { - config->enable_rsync = true; - config->shuffle_uris = false; - config->local_repository = NULL; - config->tal = NULL; - } - static int -parse_max_depth(struct rpki_config *config, char *str) +handle_file_config(char *config_file, struct rpki_config *config) { - config->flag_config = false; - /* - * It cannot be UINT_MAX, because then the actual number will overflow - * and will never be bigger than this. - */ - const unsigned int MAX = UINT_MAX - 1; - unsigned long max_depth; - - errno = 0; - max_depth = strtoul(str, NULL, 10); - if (errno) { - return pr_errno(errno, - "'%s' is not an unsigned integer, or is too big (max: %u)", - str, MAX); - } - - if (max_depth > MAX) - return pr_err("The number '%s' is too big (max: %u)", str, MAX); -- - config->maximum_certificate_depth = max_depth; - return 0; + return set_config_from_file(config_file, config); } static int - handle_args(int argc, char **argv, struct rpki_config *config) + handle_args(int argc, char **argv) { + struct rpki_config config; - int opt, error = 0; - - static struct option long_options[] = { - {"tal", no_argument, NULL, 't'}, - {"local_repository", required_argument, NULL, 'l'}, - {"disable_rsync", no_argument, 0, 'r'}, - {"shuffle_uris", no_argument, 0, 's'}, - {0,0,0,} - }; + char *config_file; ++ int error; + - config.disable_rsync = false; - config.shuffle_uris = false; ++ config.enable_rsync = true; + config.local_repository = NULL; - config.tal = NULL; + config.maximum_certificate_depth = 32; ++ config.shuffle_uris = false; ++ config.tal = NULL; - while ((opt = getopt_long(argc, argv, "t:l:rsm:", long_options, NULL)) - != -1) { - switch (opt) { - case 't' : - config.tal = optarg; - break; - case 'l' : - config.local_repository = optarg; - break; - case 'r': - config.disable_rsync = true; - break; - case 's': - config.shuffle_uris = true; - break; - case 'm': - error = parse_max_depth(&config, optarg); - break; - default: - return pr_err("some usage hints.");/* TODO */ - } - } - - if (config.tal == NULL) { - fprintf(stderr, "Missing flag --tal \n"); - error = -EINVAL; + if (argc == 1) { + return pr_err("Show usage"); /*TODO*/ } - if (config.local_repository == NULL) { - fprintf(stderr, "Missing flag --local_repository \n"); - error = -EINVAL; + if (strcasecmp(argv[1], "--configuration_file") == 0) { + if (argc == 2) { + return pr_err("--configuration_file requires a string " + "as argument."); + } + config_file = argv[2]; + argc -= 2; + argv += 2; - return handle_file_config(config_file, config); ++ error = handle_file_config(config_file, &config); ++ } else { ++ error = handle_flags_config(argc, argv, &config); } - return handle_flags_config(argc, argv, config); - pr_debug("TAL file : %s", config.tal); - pr_debug("Local repository : %s", config.local_repository); - pr_debug("Disable rsync : %s", config.disable_rsync - ? "true" : "false"); - pr_debug("shuffle uris : %s", config.shuffle_uris - ? "true" : "false"); - pr_debug("Maximum certificate depth : %u", - config.maximum_certificate_depth); - + if (!error) + config_set(&config); ++ + return error; } + int main(int argc, char **argv) { diff --cc src/rsync/rsync.c index 61361a61,a0770ba1..2072d4e9 --- a/src/rsync/rsync.c +++ b/src/rsync/rsync.c @@@ -27,13 -27,11 +27,11 @@@ static char const *const RSYNC_PREFIX //static const char *rsync_command[] = {"rsync", "--recursive", "--delete", "--times", NULL}; int - rsync_init(bool is_rsync_active) + rsync_init(void) { /* Disabling rsync will forever be a useful debugging feature. */ - if (!is_rsync_active) { - execute_rsync = is_rsync_active; - if (config_get_disable_rsync()) ++ if (!config_get_enable_rsync()) return 0; - } rsync_uris = malloc(sizeof(struct uri_list)); if (rsync_uris == NULL) @@@ -48,7 -46,7 +46,7 @@@ rsync_destroy(void { struct uri *uri; - if (!execute_rsync) - if (config_get_disable_rsync()) ++ if (!config_get_enable_rsync()) return; while (!SLIST_EMPTY(rsync_uris)) { @@@ -378,7 -378,7 +378,7 @@@ download_files(struct rpki_uri const *u prefix_len = strlen(RSYNC_PREFIX); - if (!execute_rsync) - if (config_get_disable_rsync()) ++ if (!config_get_enable_rsync()) return 0; if (uri->global_len < prefix_len || diff --cc src/toml_handler.c index 9526d36b,00000000..58bd754e mode 100644,000000..100644 --- a/src/toml_handler.c +++ b/src/toml_handler.c @@@ -1,152 -1,0 +1,154 @@@ +#include "toml_handler.h" + +#include +#include +#include +#include + +#include + +#include "file.h" +#include "log.h" +#include "thread_var.h" +#include "uri.h" + + +static void +print_config(struct rpki_config *config) +{ + pr_debug("Program configuration"); + pr_debug_add("{"); + pr_debug("%s: %s", "local_repository", config->local_repository); + pr_debug("%s: %s", "tal.file", config->tal); + pr_debug("%s: %s", "enable_rsync", + config->enable_rsync ? "true" : "false"); + pr_debug("%s: %s", "tal.shuffle_uris", + config->shuffle_uris ? "true" : "false"); ++ pr_debug("%s: %u", "tal.maximum-certificate-depth", ++ config->maximum_certificate_depth); + pr_debug_rm("}"); +} + +static int +iterate_fields(struct toml_table_t *table, struct rpki_config *config, + struct option_field *fields, unsigned int field_len) +{ + struct option_field *field; + const char *result; + char *str; + int i, error, missing_param; + + missing_param = 0; + + for (i = 0; i < field_len; i++) { + field = &(fields[i]); + result = toml_raw_in(table, field->name); + if (result == 0) { + if (field->required) { + printf("Required parameter is missing '%s'\n", + field->name); + missing_param |= -ENOENT; + } + continue; + } + + str = (char *) result; + if (field->type->id == GTI_STRING) { + error = toml_rtos(result, &str); + if (error) + return pr_err("Bad value in '%s'", + field->name); + } + + error = handle_option(config, field, str); + if (error) + return error; + + } + + if (missing_param) + return missing_param; + return error; +} + +static int +handle_tal_table(struct toml_table_t *tal, struct rpki_config *config) +{ + struct option_field *tal_fields; + unsigned int tal_len; + + get_tal_fields(&tal_fields, &tal_len); + + return iterate_fields(tal, config, tal_fields, tal_len); +} + +static int +toml_to_config(struct toml_table_t *root, struct rpki_config *config) +{ + struct option_field *globals; + struct toml_table_t *tal; + int error; + unsigned int global_len; + + + get_global_fields(&globals, &global_len); + error = iterate_fields(root, config, globals, global_len); + if (error) + return error; + + tal = toml_table_in(root, "tal"); + if (tal != 0) + error = handle_tal_table(tal, config); + else + return pr_err("Required table '%s' is missing.", "tal"); + + return error; +} + +int +set_config_from_file(char *config_file, struct rpki_config *config) +{ + struct file_contents fc; + struct toml_table_t *root; + struct rpki_uri uri; + char errbuf[200]; + int error; + bool is_config_file; + + /* I think I'm not using this struct correctly but I need it to call + * some functions, so be careful using the struct rpki_uri here. + * Also no needs to be freed. */ + uri.global = config_file; + uri.global_len = strlen(config_file); + uri.local = config_file; + + is_config_file = uri_has_extension(&uri, ".ini"); + is_config_file |= uri_has_extension(&uri, ".toml"); + if (!is_config_file) { + error = pr_err("Invalid Config file extension for file '%s'", + uri.local); + goto end; + } + + error = file_load(&uri, &fc); + if (error) + goto end; /* Error msg already printed. */ + + root = toml_parse((char *) fc.buffer, errbuf, sizeof(errbuf)); + file_free(&fc); + + if (root == NULL) { + error = pr_err("Error while parsing configuration file: %s", + errbuf); + goto end; + } + + error = toml_to_config(root, config); + + toml_free(root); + + print_config(config); + +end: + return error; +}