#include "config.h"
- #include <asm-generic/errno-base.h>
+#include <stdio.h>
- #include <getopt.h>
+#include <strings.h>
++#include <errno.h>
++#include <getopt.h>
+
+#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;
+ }
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)
{
- 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,}
- };
+ struct rpki_config config;
- config.disable_rsync = false;
- config.shuffle_uris = false;
+ char *config_file;
++ int error;
+
- config.tal = NULL;
++ config.enable_rsync = true;
+ config.local_repository = 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 <file>\n");
- error = -EINVAL;
+ if (argc == 1) {
+ return pr_err("Show usage"); /*TODO*/
}
- if (config.local_repository == NULL) {
- fprintf(stderr, "Missing flag --local_repository <dir>\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)
{
--- /dev/null
+#include "toml_handler.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <toml.h>
+
+#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;
+}