Scales a little better, removes clutter from config.c.
rpki_validator_SOURCES += asn1/oid.h asn1/oid.c
rpki_validator_SOURCES += asn1/signed_data.h asn1/signed_data.c
+rpki_validator_SOURCES += config/boolean.c config/boolean.h
+rpki_validator_SOURCES += config/filename_format.h config/filename_format.c
+rpki_validator_SOURCES += config/out_file.h config/out_file.c
+rpki_validator_SOURCES += config/str.c config/str.h
+rpki_validator_SOURCES += config/string_array.h config/string_array.c
+rpki_validator_SOURCES += config/sync_strategy.h config/sync_strategy.c
+rpki_validator_SOURCES += config/types.h
+rpki_validator_SOURCES += config/uint.c config/uint.h
+
rpki_validator_SOURCES += crypto/base64.h crypto/base64.c
rpki_validator_SOURCES += crypto/hash.h crypto/hash.c
#include "config.h"
#include <limits.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "common.h"
#include "log.h"
#include "toml_handler.h"
+#include "config/boolean.h"
+#include "config/out_file.h"
+#include "config/str.h"
+#include "config/uint.h"
/**
* Please note that this is actually two `for`s stacked together, so don't use
if ((opt->availability == 0) || \
(opt->availability & type))
-struct config_out_file {
- FILE *fd;
- char *file_name;
-};
-
/**
* To add a member to this structure,
*
static void print_usage(FILE *, bool);
-#define DECLARE_PRINT_FN(name) \
- static void name( \
- struct group_fields const *, \
- struct option_field const *, \
- void * \
- )
-DECLARE_PRINT_FN(print_bool);
-DECLARE_PRINT_FN(print_u_int);
-DECLARE_PRINT_FN(print_string);
-DECLARE_PRINT_FN(print_string_array);
-DECLARE_PRINT_FN(print_sync_strategy);
-DECLARE_PRINT_FN(print_filename_format);
-DECLARE_PRINT_FN(print_out_file);
-
-#define DECLARE_PARSE_ARGV_FN(name) \
- static int name( \
- struct option_field const *, \
- char const *, \
- void * \
- )
-DECLARE_PARSE_ARGV_FN(parse_argv_bool);
-DECLARE_PARSE_ARGV_FN(parse_argv_u_int);
-DECLARE_PARSE_ARGV_FN(parse_argv_string);
-DECLARE_PARSE_ARGV_FN(parse_argv_sync_strategy);
-DECLARE_PARSE_ARGV_FN(parse_argv_filename_format);
-DECLARE_PARSE_ARGV_FN(parse_argv_out_file);
-
-#define DECLARE_PARSE_TOML_FN(name) \
- static int name( \
- struct option_field const *, \
- struct toml_table_t *, \
- void * \
- )
-DECLARE_PARSE_TOML_FN(parse_toml_bool);
-DECLARE_PARSE_TOML_FN(parse_toml_u_int);
-DECLARE_PARSE_TOML_FN(parse_toml_string);
-DECLARE_PARSE_TOML_FN(parse_toml_sync_strategy);
-DECLARE_PARSE_TOML_FN(parse_toml_string_array);
-DECLARE_PARSE_TOML_FN(parse_toml_filename_format);
-DECLARE_PARSE_TOML_FN(parse_toml_out_file);
-
#define DECLARE_HANDLE_FN(name) \
static int name( \
struct option_field const *, \
DECLARE_HANDLE_FN(handle_version);
DECLARE_HANDLE_FN(handle_toml);
-#define DECLARE_FREE_FN(name) static void name(void *)
-DECLARE_FREE_FN(free_string);
-DECLARE_FREE_FN(free_string_array);
-DECLARE_FREE_FN(free_out_file);
-
static char const *program_name;
static struct rpki_config rpki_config;
-static const struct global_type gt_bool = {
- .has_arg = no_argument,
- .size = sizeof(bool),
- .print = print_bool,
- .parse.argv = parse_argv_bool,
- .parse.toml = parse_toml_bool,
- .arg_doc = "true|false",
-};
-
-static const struct global_type gt_u_int = {
- .has_arg = required_argument,
- .size = sizeof(unsigned int),
- .print = print_u_int,
- .parse.argv = parse_argv_u_int,
- .parse.toml = parse_toml_u_int,
- .arg_doc = "<unsigned integer>",
-};
-
-static const struct global_type gt_string = {
- .has_arg = required_argument,
- .size = sizeof(char *),
- .print = print_string,
- .parse.argv = parse_argv_string,
- .parse.toml = parse_toml_string,
- .free = free_string,
- .arg_doc = "<string>",
-};
-
-static const struct global_type gt_string_array = {
- .has_arg = required_argument,
- .size = sizeof(char *const *),
- .print = print_string_array,
- .parse.toml = parse_toml_string_array,
- .free = free_string_array,
- .arg_doc = "<sequence of strings>",
-};
-
-static const struct global_type gt_sync_strategy = {
- .has_arg = required_argument,
- .size = sizeof(enum sync_strategy),
- .print = print_sync_strategy,
- .parse.argv = parse_argv_sync_strategy,
- .parse.toml = parse_toml_sync_strategy,
- .arg_doc = SYNC_VALUE_OFF "|" SYNC_VALUE_STRICT "|" SYNC_VALUE_ROOT,
-};
-
-static const struct global_type gt_filename_format = {
- .has_arg = required_argument,
- .size = sizeof(enum filename_format),
- .print = print_filename_format,
- .parse.argv = parse_argv_filename_format,
- .parse.toml = parse_toml_filename_format,
- .arg_doc = FNF_VALUE_GLOBAL "|" FNF_VALUE_LOCAL "|" FNF_VALUE_NAME,
-};
-
-static const struct global_type gt_out_file = {
- .has_arg = required_argument,
- .size = sizeof(struct config_out_file),
- .print = print_out_file,
- .parse.argv = parse_argv_out_file,
- .parse.toml = parse_toml_out_file,
- .free = free_out_file,
- .arg_doc = "<file>",
-};
-
/**
* An option that takes no arguments, is not correlated to any rpki_config
* fields, and is entirely managed by its handler function.
return ((unsigned char *) &rpki_config) + field->offset;
}
-static void
-print_bool(struct group_fields const *group, struct option_field const *field,
- void *_value)
-{
- bool *value = _value;
- pr_info("%s.%s: %s", group->name, field->name,
- (*value) ? "true" : "false");
-}
-
-static void
-print_u_int(struct group_fields const *group, struct option_field const *field,
- void *value)
-{
- pr_info("%s.%s: %u", group->name, field->name,
- *((unsigned int *) value));
-}
-
-static void
-print_string(struct group_fields const *group, struct option_field const *field,
- void *value)
-{
- pr_info("%s.%s: %s", group->name, field->name, *((char **) value));
-}
-
-static void
-print_string_array(struct group_fields const *group,
- struct option_field const *field, void *_value)
-{
- struct string_array *value = _value;
- size_t i;
-
- pr_info("%s.%s:", group->name, field->name);
- pr_indent_add();
-
- if (value->length == 0)
- pr_info("<Nothing>");
- else for (i = 0; i < value->length; i++)
- pr_info("%s", value->array[i]);
-
- pr_indent_rm();
-}
-
-static void
-print_sync_strategy(struct group_fields const *group,
- struct option_field const *field, void *value)
-{
- enum sync_strategy *strategy = value;
- char const *str = "<unknown>";
-
- switch (*strategy) {
- case SYNC_OFF:
- str = SYNC_VALUE_OFF;
- break;
- case SYNC_STRICT:
- str = SYNC_VALUE_STRICT;
- break;
- case SYNC_ROOT:
- str = SYNC_VALUE_ROOT;
- break;
- }
-
- pr_info("%s.%s: %s", group->name, field->name, str);
-}
-
-static void
-print_filename_format(struct group_fields const *group,
- struct option_field const *field, void *value)
-{
- enum filename_format *format = value;
- char const *str = "<unknown>";
-
- switch (*format) {
- case FNF_GLOBAL:
- str = FNF_VALUE_GLOBAL;
- break;
- case FNF_LOCAL:
- str = FNF_VALUE_LOCAL;
- break;
- case FNF_NAME:
- str = FNF_VALUE_NAME;
- break;
- }
-
- pr_info("%s.%s: %s", group->name, field->name, str);
-}
-
-static void
-print_out_file(struct group_fields const *group,
- struct option_field const *field, void *value)
-{
- struct config_out_file *file = value;
- pr_info("%s.%s: %s", group->name, field->name, file->file_name);
-}
-
-static int
-parse_argv_bool(struct option_field const *field, char const *str, void *result)
-{
- bool *value = result;
-
- if (str == NULL) {
- *value = true;
- return 0;
- }
-
- if (strcmp(str, "true") == 0) {
- *value = true;
- return 0;
- }
-
- if (strcmp(str, "false") == 0) {
- *value = false;
- return 0;
- }
-
- return pr_err("Cannot parse '%s' as a bool (true|false).", str);
-}
-
-static int
-parse_argv_u_int(struct option_field const *field, char const *str, void *_result)
-{
- unsigned long parsed;
- int *result;
-
- if (field->type->has_arg != required_argument || str == NULL) {
- return pr_err("Integer options ('%s' in this case) require an argument.",
- field->name);
- }
-
- errno = 0;
- parsed = strtoul(str, NULL, 10);
- if (errno)
- return pr_errno(errno, "'%s' is not an unsigned integer", str);
-
- if (parsed < field->min || field->max < parsed) {
- return pr_err("'%lu' is out of bounds (%u-%u).", parsed,
- field->min, field->max);
- }
-
- result = _result;
- *result = parsed;
- return 0;
-}
-
-static int
-parse_argv_string(struct option_field const *field, char const *str, void *_result)
-{
- char **result = _result;
-
- /* Remove the previous value (usually the default). */
- field->type->free(result);
-
- if (field->type->has_arg != required_argument || str == NULL) {
- return pr_err("String options ('%s' in this case) require an argument.",
- field->name);
- }
-
- /* tomlc99 frees @str early, so work with a copy. */
- *result = strdup(str);
- return ((*result) != NULL) ? 0 : pr_enomem();
-}
-
-static int
-parse_argv_sync_strategy(struct option_field const *field, char const *str,
- void *_result)
-{
- enum sync_strategy *result = _result;
-
- if (strcmp(str, SYNC_VALUE_OFF) == 0)
- *result = SYNC_OFF;
- else if (strcmp(str, SYNC_VALUE_STRICT) == 0)
- *result = SYNC_STRICT;
- else if (strcmp(str, SYNC_VALUE_ROOT) == 0)
- *result = SYNC_ROOT;
- else
- return pr_err("Unknown synchronization strategy: '%s'", str);
-
- return 0;
-}
-
-static int
-parse_argv_filename_format(struct option_field const *field, char const *str,
- void *_result)
-{
- enum filename_format *result = _result;
-
- if (strcmp(str, FNF_VALUE_GLOBAL) == 0)
- *result = FNF_GLOBAL;
- else if (strcmp(str, FNF_VALUE_LOCAL) == 0)
- *result = FNF_LOCAL;
- else if (strcmp(str, FNF_VALUE_NAME) == 0)
- *result = FNF_NAME;
- else
- return pr_err("Unknown file name format: '%s'", str);
-
- return 0;
-}
-
-static int
-parse_argv_out_file(struct option_field const *field, char const *file_name,
- void *_result)
-{
- struct config_out_file *file = _result;
-
- field->type->free(file);
-
- file->file_name = strdup(file_name);
- if (file->file_name == NULL)
- return pr_enomem();
-
- file->fd = fopen(file_name, "w");
- if (file->fd == NULL) {
- free(file->file_name);
- file->file_name = NULL;
- return pr_errno(errno, "Could not open file '%s'", file_name);
- }
-
- return 0;
-}
-
-static int
-parse_toml_bool(struct option_field const *opt, struct toml_table_t *toml,
- void *_result)
-{
- const char *raw;
- int value;
- bool *result;
-
- raw = toml_raw_in(toml, opt->name);
- if (raw == NULL)
- return pr_err("TOML boolean '%s' was not found.", opt->name);
- if (toml_rtob(raw, &value) == -1)
- return pr_err("Cannot parse '%s' as a boolean.", raw);
-
- result = _result;
- *result = value;
- return 0;
-}
-
-static int
-parse_toml_u_int(struct option_field const *opt, struct toml_table_t *toml,
- void *_result)
-{
- const char *raw;
- int64_t value;
- unsigned int *result;
-
- raw = toml_raw_in(toml, opt->name);
- if (raw == NULL)
- return pr_err("TOML integer '%s' was not found.", opt->name);
- if (toml_rtoi(raw, &value) == -1)
- return pr_err("Cannot parse '%s' as an integer.", raw);
-
- if (value < opt->min || opt->max < value) {
- return pr_err("Integer '%s' is out of range (%u-%u).",
- opt->name, opt->min, opt->max);
- }
-
- result = _result;
- *result = value;
- return 0;
-}
-
-static int
-parse_toml_string(struct option_field const *opt, struct toml_table_t *toml,
- void *_result)
-{
- const char *raw;
- char *value;
- char **result;
-
- /* Remove the previous value (usually the default). */
- opt->type->free(_result);
-
- raw = toml_raw_in(toml, opt->name);
- if (raw == NULL)
- return pr_err("TOML string '%s' was not found.", opt->name);
- if (toml_rtos(raw, &value) == -1)
- return pr_err("Cannot parse '%s' as a string.", raw);
-
- result = _result;
- *result = value;
- return 0;
-}
-
-static int
-parse_toml_sync_strategy(struct option_field const *opt,
- struct toml_table_t *toml, void *_result)
-{
- int error;
- char *string;
-
- string = NULL;
- error = parse_toml_string(opt, toml, &string);
- if (error)
- return error;
-
- error = parse_argv_sync_strategy(opt, string, _result);
-
- free(string);
- return error;
-}
-
-static int
-parse_toml_string_array(struct option_field const *opt,
- struct toml_table_t *toml, void *_result)
-{
- toml_array_t *array;
- int array_len;
- int i;
- const char *raw;
- struct string_array *result = _result;
- int error;
-
- /* Remove the previous value (usually the default). */
- opt->type->free(_result);
-
- array = toml_array_in(toml, opt->name);
- if (array == NULL)
- return pr_err("TOML array '%s' was not found.", opt->name);
- array_len = toml_array_nelem(array);
-
- result->array = malloc(array_len * sizeof(char *));
- if (result->array == NULL)
- return pr_enomem();
- result->length = array_len;
-
- for (i = 0; i < array_len; i++) {
- raw = toml_raw_at(array, i);
- if (raw == NULL) {
- error = pr_crit("Array index %d is NULL.", i);
- goto fail;
- }
- if (toml_rtos(raw, &result->array[i]) == -1) {
- error = pr_err("Cannot parse '%s' as a string.", raw);
- goto fail;
- }
- }
-
- return 0;
-
-fail:
- free(result->array);
- result->length = 0;
- return error;
-}
-
-static int
-parse_toml_filename_format(struct option_field const *opt,
- struct toml_table_t *toml, void *_result)
-{
- int error;
- char *string;
-
- string = NULL;
- error = parse_toml_string(opt, toml, &string);
- if (error)
- return error;
-
- error = parse_argv_filename_format(opt, string, _result);
-
- free(string);
- return error;
-}
-
-static int
-parse_toml_out_file(struct option_field const *opt, struct toml_table_t *toml,
- void *_result)
-{
- char *file_name;
- int error;
-
- file_name = NULL;
- error = parse_toml_string(opt, toml, &file_name);
- if (error)
- return error;
-
- error = parse_argv_out_file(opt, file_name, _result);
-
- free(file_name);
- return error;
-}
-
static int
handle_help(struct option_field const *field, char *arg)
{
return set_config_from_file(file_name);
}
-static void
-free_string(void *_string)
-{
- char **string = _string;
- free(*string);
- *string = NULL;
-}
-
-static void
-free_string_array(void *_array)
-{
- struct string_array *array = _array;
- size_t i;
-
- for (i = 0; i < array->length; i++)
- free(array->array[i]);
- free(array->array);
-
- array->array = NULL;
- array->length = 0;
-}
-
-static void
-free_out_file(void *_file)
-{
- struct config_out_file *file = _file;
-
- if (file->fd != NULL) {
- fclose(file->fd);
- file->fd = NULL;
- }
-
- free(file->file_name);
- file->file_name = NULL;
-}
-
static bool
is_alphanumeric(int chara)
{
#define SRC_CONFIG_H_
#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <toml.h>
-#include <openssl/bio.h>
-/**
- * Note: The only repository synchronization protocol implemented so far is
- * RSYNC. Whenever you see "sync", think "rsync."
- */
-enum sync_strategy {
- /**
- * Synchronization is turned off.
- * The validator will work on an already downloaded repository.
- */
- SYNC_OFF,
- /**
- * Strictly correct download strategy.
- *
- * The validator will sync each repository publication point separately
- * as requested by each caRepository contained in the CA certificates'
- * SIA extensions.
- *
- * No risk of downloading unneeded files, but otherwise slow, as every
- * different repository publication point requires a separate sync call.
- */
- SYNC_STRICT,
- /**
- * Always download the likely root of the entire repository.
- *
- * For example, if we get the following caRepositories:
- *
- * - `rsync://a.b.c/d/e/f/g/h/i`
- * - `rsync://a.b.c/d/e/f/g/h/j`
- * - `rsync://a.b.c/d/e/f/k`
- *
- * This strategy will synchronize `rsync://a.b.c/d` while parsing the
- * first caRepository, and then skip synchronization during the second
- * and third ones. (Because they are already downloaded.)
- *
- * This strategy risks downloading unneeded files, and even failing due
- * to lack of read permissions on stray subdirectories. On the flip
- * side, if the repository holds no unnecessary subdirectories, then
- * this strategy is the fastest one, since it generally only requires
- * one sync call per domain, which often translates into one sync call
- * per validation cycle.
- *
- * Currently, all of the official repositories are actually specifically
- * structured to benefit this strategy.
- */
- SYNC_ROOT,
-};
+#include "config/filename_format.h"
+#include "config/sync_strategy.h"
+#include "config/string_array.h"
+#include "config/types.h"
-#define SYNC_VALUE_OFF "off"
-#define SYNC_VALUE_STRICT "strict"
-#define SYNC_VALUE_ROOT "root"
-
-enum filename_format {
- /** Example: "rsync://repository.lacnic.net/rpki/foo/bar/baz.cer" */
- FNF_GLOBAL,
- /** Example: "/tmp/repo/repository.lacnic.net/rpki/foo/bar/baz.cer" */
- FNF_LOCAL,
- /** Example: "baz.cer" */
- FNF_NAME,
-};
-
-#define FNF_VALUE_GLOBAL "global-url"
-#define FNF_VALUE_LOCAL "local-path"
-#define FNF_VALUE_NAME "file-name"
-
-struct rpki_config;
-
-struct group_fields;
-struct option_field;
-
-typedef void (*print_function)(
- struct group_fields const *,
- struct option_field const *,
- void *
-);
-typedef int (*argv_parse_function)(
- struct option_field const *,
- char const *,
- void *
-);
-typedef int (*toml_parse_function)(
- struct option_field const *,
- struct toml_table_t *,
- void *
-);
-typedef int (*handler_function)(
- struct option_field const *,
- char *
-);
-
-struct global_type {
- /** Same as struct option.has_arg. Mandatory. */
- int has_arg;
- /**
- * Number of bytes this data type uses in the rpki_config structure.
- * Optional. Defaults to zero, obviously.
- */
- size_t size;
-
- /**
- * Prints this data type during the print_config() function.
- * Optional.
- */
- print_function print;
-
- /** If the option's handler is not NULL, this is optional. */
- struct {
- /**
- * Convers from string to this data type.
- * Optional if there are no fields of this type that are read
- * from argv.
- */
- argv_parse_function argv;
- /**
- * Converts from a TOML node to this data type.
- * Optional if there are no fields of this type that are read
- * from TOML files.
- */
- toml_parse_function toml;
- } parse;
-
- /**
- * Function that will release this data type.
- * If the option's handler is not NULL, this is optional.
- *
- * IMPORTANT: This function might be called twice in succession.
- * Therefore, make sure that it nullifies the value, and reacts properly
- * when the input is NULL.
- */
- void (*free)(void *);
-
- /**
- * Descriptor of this type's payload. Printed in usage documentation.
- * For example, in `--tal=<file>`, @arg_doc is "<file>".
- * The type might have no payload, so this is optional.
- */
- char const *arg_doc;
-};
-
-/** This option can be set from the command line. */
-#define AVAILABILITY_GETOPT (1 << 0)
-/** This option can be set from the TOML file. */
-#define AVAILABILITY_TOML (1 << 1)
-
-struct option_field {
- /*
- * Must be zero, alphanumeric or >= 1000.
- * If zero, signals the end of the array.
- * If alphanumeric, it's the short option name character.
- * Otherwise it's just a non-printable identifier.
- * Must be unique across all option fields.
- * Mandatory.
- */
- int id;
- /**
- * For example, if the option name is '--potato', then @name is
- * "potato".
- * Mandatory.
- */
- char const *name;
-
- /** Data type. Mandatory. */
- struct global_type const *type;
- /**
- * Number of bytes between the beginning of the struct rpki_config
- * and the position where this option is stored.
- * Only relevant when @handler == NULL.
- */
- size_t offset;
- /** Overrides @type->parser and @offset. Optional. */
- handler_function handler;
-
- /**
- * Explanation of the field, for user consumption during --help.
- * Meant to be short; the bulk of it should be found in the manpage.
- * Probably should not include punctuation at the end.
- * Mandatory.
- */
- const char *doc;
- /** Overrides type->arg_doc. Optional. */
- char const *arg_doc;
- /**
- * AVAILABILITY_* flags above.
- * Default availability is everywhere.
- * Optional.
- */
- int availability;
- unsigned int min;
- unsigned int max;
-};
-
-struct group_fields {
- char const *name;
- struct option_field const *options;
-};
-
-struct string_array {
- char **array;
- size_t length;
-};
-
-void *get_rpki_config_field(struct option_field const *);
+/* Init/destroy */
int handle_flags_config(int , char **);
+void free_rpki_config(void);
-void get_group_fields(struct group_fields const **);
-
+/* Getters */
char const *config_get_tal(void);
char const *config_get_local_repository(void);
enum sync_strategy config_get_sync_strategy(void);
char *config_get_rsync_program(void);
struct string_array const *config_get_rsync_args(void);
-void free_rpki_config(void);
+/* Needed public by the TOML module */
+void *get_rpki_config_field(struct option_field const *);
+void get_group_fields(struct group_fields const **);
#endif /* SRC_CONFIG_H_ */
--- /dev/null
+#include "config/boolean.h"
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <string.h>
+#include "log.h"
+
+static void
+print_bool(struct group_fields const *group, struct option_field const *field,
+ void *_value)
+{
+ bool *value = _value;
+ pr_info("%s.%s: %s", group->name, field->name,
+ (*value) ? "true" : "false");
+}
+
+static int
+parse_argv_bool(struct option_field const *field, char const *str, void *result)
+{
+ bool *value = result;
+
+ if (str == NULL) {
+ *value = true;
+ return 0;
+ }
+
+ if (strcmp(str, "true") == 0) {
+ *value = true;
+ return 0;
+ }
+
+ if (strcmp(str, "false") == 0) {
+ *value = false;
+ return 0;
+ }
+
+ return pr_err("Cannot parse '%s' as a bool (true|false).", str);
+}
+
+static int
+parse_toml_bool(struct option_field const *opt, struct toml_table_t *toml,
+ void *_result)
+{
+ const char *raw;
+ int value;
+ bool *result;
+
+ raw = toml_raw_in(toml, opt->name);
+ if (raw == NULL)
+ return pr_err("TOML boolean '%s' was not found.", opt->name);
+ if (toml_rtob(raw, &value) == -1)
+ return pr_err("Cannot parse '%s' as a boolean.", raw);
+
+ result = _result;
+ *result = value;
+ return 0;
+}
+
+const struct global_type gt_bool = {
+ .has_arg = no_argument,
+ .size = sizeof(bool),
+ .print = print_bool,
+ .parse.argv = parse_argv_bool,
+ .parse.toml = parse_toml_bool,
+ .arg_doc = "true|false",
+};
--- /dev/null
+#ifndef SRC_CONFIG_BOOLEAN_H_
+#define SRC_CONFIG_BOOLEAN_H_
+
+#include "config/types.h"
+
+extern const struct global_type gt_bool;
+
+#endif /* SRC_CONFIG_BOOLEAN_H_ */
--- /dev/null
+#include "config/filename_format.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "config/str.h"
+
+#define FNF_VALUE_GLOBAL "global-url"
+#define FNF_VALUE_LOCAL "local-path"
+#define FNF_VALUE_NAME "file-name"
+
+static void
+print_filename_format(struct group_fields const *group,
+ struct option_field const *field, void *value)
+{
+ enum filename_format *format = value;
+ char const *str = "<unknown>";
+
+ switch (*format) {
+ case FNF_GLOBAL:
+ str = FNF_VALUE_GLOBAL;
+ break;
+ case FNF_LOCAL:
+ str = FNF_VALUE_LOCAL;
+ break;
+ case FNF_NAME:
+ str = FNF_VALUE_NAME;
+ break;
+ }
+
+ pr_info("%s.%s: %s", group->name, field->name, str);
+}
+
+static int
+parse_argv_filename_format(struct option_field const *field, char const *str,
+ void *_result)
+{
+ enum filename_format *result = _result;
+
+ if (strcmp(str, FNF_VALUE_GLOBAL) == 0)
+ *result = FNF_GLOBAL;
+ else if (strcmp(str, FNF_VALUE_LOCAL) == 0)
+ *result = FNF_LOCAL;
+ else if (strcmp(str, FNF_VALUE_NAME) == 0)
+ *result = FNF_NAME;
+ else
+ return pr_err("Unknown file name format: '%s'", str);
+
+ return 0;
+}
+
+static int
+parse_toml_filename_format(struct option_field const *opt,
+ struct toml_table_t *toml, void *_result)
+{
+ char *string;
+ int error;
+
+ string = NULL;
+ error = parse_toml_string(opt, toml, &string);
+ if (error)
+ return error;
+
+ error = parse_argv_filename_format(opt, string, _result);
+
+ free(string);
+ return error;
+}
+
+const struct global_type gt_filename_format = {
+ .has_arg = required_argument,
+ .size = sizeof(enum filename_format),
+ .print = print_filename_format,
+ .parse.argv = parse_argv_filename_format,
+ .parse.toml = parse_toml_filename_format,
+ .arg_doc = FNF_VALUE_GLOBAL "|" FNF_VALUE_LOCAL "|" FNF_VALUE_NAME,
+};
--- /dev/null
+#ifndef SRC_CONFIG_FILENAME_FORMAT_H_
+#define SRC_CONFIG_FILENAME_FORMAT_H_
+
+#include "config/types.h"
+
+enum filename_format {
+ /** Example: "rsync://repository.lacnic.net/rpki/foo/bar/baz.cer" */
+ FNF_GLOBAL,
+ /** Example: "/tmp/repo/repository.lacnic.net/rpki/foo/bar/baz.cer" */
+ FNF_LOCAL,
+ /** Example: "baz.cer" */
+ FNF_NAME,
+};
+
+extern const struct global_type gt_filename_format;
+
+#endif /* SRC_CONFIG_FILENAME_FORMAT_H_ */
--- /dev/null
+#include "config/out_file.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "config/str.h"
+
+static void
+print_out_file(struct group_fields const *group,
+ struct option_field const *field, void *value)
+{
+ struct config_out_file *file = value;
+ pr_info("%s.%s: %s", group->name, field->name, file->file_name);
+}
+
+static int
+parse_argv_out_file(struct option_field const *field, char const *file_name,
+ void *_result)
+{
+ struct config_out_file *file = _result;
+ int error;
+
+ field->type->free(file);
+
+ file->file_name = strdup(file_name);
+ if (file->file_name == NULL)
+ return pr_enomem();
+
+ file->fd = fopen(file_name, "w");
+ if (file->fd == NULL) {
+ error = errno;
+ free(file->file_name);
+ file->file_name = NULL;
+ return pr_errno(error, "Could not open file '%s'", file_name);
+ }
+
+ return 0;
+}
+
+static int
+parse_toml_out_file(struct option_field const *opt, struct toml_table_t *toml,
+ void *_result)
+{
+ char *file_name;
+ int error;
+
+ file_name = NULL;
+ error = parse_toml_string(opt, toml, &file_name);
+ if (error)
+ return error;
+
+ error = parse_argv_out_file(opt, file_name, _result);
+
+ free(file_name);
+ return error;
+}
+
+static void
+free_out_file(void *_file)
+{
+ struct config_out_file *file = _file;
+
+ if (file->fd != NULL) {
+ fclose(file->fd);
+ file->fd = NULL;
+ }
+
+ free(file->file_name);
+ file->file_name = NULL;
+}
+
+const struct global_type gt_out_file = {
+ .has_arg = required_argument,
+ .size = sizeof(struct config_out_file),
+ .print = print_out_file,
+ .parse.argv = parse_argv_out_file,
+ .parse.toml = parse_toml_out_file,
+ .free = free_out_file,
+ .arg_doc = "<file>",
+};
--- /dev/null
+#ifndef SRC_CONFIG_OUT_FILE_H_
+#define SRC_CONFIG_OUT_FILE_H_
+
+#include <stdio.h>
+#include "config/types.h"
+
+struct config_out_file {
+ FILE *fd;
+ char *file_name;
+};
+
+extern const struct global_type gt_out_file;
+
+#endif /* SRC_CONFIG_OUT_FILE_H_ */
--- /dev/null
+#include "config/str.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include "log.h"
+
+static void
+print_string(struct group_fields const *group, struct option_field const *field,
+ void *value)
+{
+ pr_info("%s.%s: %s", group->name, field->name, *((char **) value));
+}
+
+static int
+parse_argv_string(struct option_field const *field, char const *str,
+ void *_result)
+{
+ char **result = _result;
+
+ /* Remove the previous value (usually the default). */
+ field->type->free(result);
+
+ if (field->type->has_arg != required_argument || str == NULL) {
+ return pr_err("String options ('%s' in this case) require an argument.",
+ field->name);
+ }
+
+ /* tomlc99 frees @str early, so work with a copy. */
+ *result = strdup(str);
+ return ((*result) != NULL) ? 0 : pr_enomem();
+}
+
+int
+parse_toml_string(struct option_field const *opt, struct toml_table_t *toml,
+ void *_result)
+{
+ const char *raw;
+ char *value;
+ char **result;
+
+ /* Remove the previous value (usually the default). */
+ opt->type->free(_result);
+
+ raw = toml_raw_in(toml, opt->name);
+ if (raw == NULL)
+ return pr_err("TOML string '%s' was not found.", opt->name);
+ if (toml_rtos(raw, &value) == -1)
+ return pr_err("Cannot parse '%s' as a string.", raw);
+
+ result = _result;
+ *result = value;
+ return 0;
+}
+
+static void
+free_string(void *_string)
+{
+ char **string = _string;
+ free(*string);
+ *string = NULL;
+}
+
+const struct global_type gt_string = {
+ .has_arg = required_argument,
+ .size = sizeof(char *),
+ .print = print_string,
+ .parse.argv = parse_argv_string,
+ .parse.toml = parse_toml_string,
+ .free = free_string,
+ .arg_doc = "<string>",
+};
--- /dev/null
+#ifndef SRC_CONFIG_STR_H_
+#define SRC_CONFIG_STR_H_
+
+#include "config/types.h"
+
+extern const struct global_type gt_string;
+
+int parse_toml_string(struct option_field const *, struct toml_table_t *,
+ void *);
+
+#endif /* SRC_CONFIG_STR_H_ */
--- /dev/null
+#include "config/string_array.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include "log.h"
+
+static void
+print_string_array(struct group_fields const *group,
+ struct option_field const *field, void *_value)
+{
+ struct string_array *value = _value;
+ size_t i;
+
+ pr_info("%s.%s:", group->name, field->name);
+ pr_indent_add();
+
+ if (value->length == 0)
+ pr_info("<Nothing>");
+ else for (i = 0; i < value->length; i++)
+ pr_info("%s", value->array[i]);
+
+ pr_indent_rm();
+}
+
+static int
+parse_toml_string_array(struct option_field const *opt,
+ struct toml_table_t *toml, void *_result)
+{
+ toml_array_t *array;
+ int array_len;
+ int i;
+ const char *raw;
+ struct string_array *result = _result;
+ int error;
+
+ /* Remove the previous value (usually the default). */
+ opt->type->free(_result);
+
+ array = toml_array_in(toml, opt->name);
+ if (array == NULL)
+ return pr_err("TOML array '%s' was not found.", opt->name);
+ array_len = toml_array_nelem(array);
+
+ result->array = malloc(array_len * sizeof(char *));
+ if (result->array == NULL)
+ return pr_enomem();
+ result->length = array_len;
+
+ for (i = 0; i < array_len; i++) {
+ raw = toml_raw_at(array, i);
+ if (raw == NULL) {
+ error = pr_crit("Array index %d is NULL.", i);
+ goto fail;
+ }
+ if (toml_rtos(raw, &result->array[i]) == -1) {
+ error = pr_err("Cannot parse '%s' as a string.", raw);
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ free(result->array);
+ result->length = 0;
+ return error;
+}
+
+static void
+free_string_array(void *_array)
+{
+ struct string_array *array = _array;
+ size_t i;
+
+ for (i = 0; i < array->length; i++)
+ free(array->array[i]);
+ free(array->array);
+
+ array->array = NULL;
+ array->length = 0;
+}
+
+const struct global_type gt_string_array = {
+ .has_arg = required_argument,
+ .size = sizeof(char *const *),
+ .print = print_string_array,
+ .parse.toml = parse_toml_string_array,
+ .free = free_string_array,
+ .arg_doc = "<sequence of strings>",
+};
--- /dev/null
+#ifndef SRC_CONFIG_STRING_ARRAY_H_
+#define SRC_CONFIG_STRING_ARRAY_H_
+
+#include <stddef.h>
+#include "config/types.h"
+
+struct string_array {
+ char **array;
+ size_t length;
+};
+
+extern const struct global_type gt_string_array;
+
+#endif /* SRC_CONFIG_STRING_ARRAY_H_ */
--- /dev/null
+#include "config/sync_strategy.h"
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "config/str.h"
+
+#define SYNC_VALUE_OFF "off"
+#define SYNC_VALUE_STRICT "strict"
+#define SYNC_VALUE_ROOT "root"
+
+static void
+print_sync_strategy(struct group_fields const *group,
+ struct option_field const *field, void *value)
+{
+ enum sync_strategy *strategy = value;
+ char const *str = "<unknown>";
+
+ switch (*strategy) {
+ case SYNC_OFF:
+ str = SYNC_VALUE_OFF;
+ break;
+ case SYNC_STRICT:
+ str = SYNC_VALUE_STRICT;
+ break;
+ case SYNC_ROOT:
+ str = SYNC_VALUE_ROOT;
+ break;
+ }
+
+ pr_info("%s.%s: %s", group->name, field->name, str);
+}
+
+static int
+parse_argv_sync_strategy(struct option_field const *field, char const *str,
+ void *_result)
+{
+ enum sync_strategy *result = _result;
+
+ if (strcmp(str, SYNC_VALUE_OFF) == 0)
+ *result = SYNC_OFF;
+ else if (strcmp(str, SYNC_VALUE_STRICT) == 0)
+ *result = SYNC_STRICT;
+ else if (strcmp(str, SYNC_VALUE_ROOT) == 0)
+ *result = SYNC_ROOT;
+ else
+ return pr_err("Unknown synchronization strategy: '%s'", str);
+
+ return 0;
+}
+
+static int
+parse_toml_sync_strategy(struct option_field const *opt,
+ struct toml_table_t *toml, void *_result)
+{
+ int error;
+ char *string;
+
+ string = NULL;
+ error = parse_toml_string(opt, toml, &string);
+ if (error)
+ return error;
+
+ error = parse_argv_sync_strategy(opt, string, _result);
+
+ free(string);
+ return error;
+}
+
+const struct global_type gt_sync_strategy = {
+ .has_arg = required_argument,
+ .size = sizeof(enum sync_strategy),
+ .print = print_sync_strategy,
+ .parse.argv = parse_argv_sync_strategy,
+ .parse.toml = parse_toml_sync_strategy,
+ .arg_doc = SYNC_VALUE_OFF "|" SYNC_VALUE_STRICT "|" SYNC_VALUE_ROOT,
+};
--- /dev/null
+#ifndef SRC_CONFIG_SYNC_STRATEGY_H_
+#define SRC_CONFIG_SYNC_STRATEGY_H_
+
+#include "config/types.h"
+
+/**
+ * Note: The only repository synchronization protocol implemented so far is
+ * RSYNC. Whenever you see "sync", think "rsync."
+ */
+enum sync_strategy {
+ /**
+ * Synchronization is turned off.
+ * The validator will work on an already downloaded repository.
+ */
+ SYNC_OFF,
+ /**
+ * Strictly correct download strategy.
+ *
+ * The validator will sync each repository publication point separately
+ * as requested by each caRepository contained in the CA certificates'
+ * SIA extensions.
+ *
+ * No risk of downloading unneeded files, but otherwise slow, as every
+ * different repository publication point requires a separate sync call.
+ */
+ SYNC_STRICT,
+ /**
+ * Always download the likely root of the entire repository.
+ *
+ * For example, if we get the following caRepositories:
+ *
+ * - `rsync://a.b.c/d/e/f/g/h/i`
+ * - `rsync://a.b.c/d/e/f/g/h/j`
+ * - `rsync://a.b.c/d/e/f/k`
+ *
+ * This strategy will synchronize `rsync://a.b.c/d` while parsing the
+ * first caRepository, and then skip synchronization during the second
+ * and third ones. (Because they are already downloaded.)
+ *
+ * This strategy risks downloading unneeded files, and even failing due
+ * to lack of read permissions on stray subdirectories. On the flip
+ * side, if the repository holds no unnecessary subdirectories, then
+ * this strategy is the fastest one, since it generally only requires
+ * one sync call per domain, which often translates into one sync call
+ * per validation cycle.
+ *
+ * Currently, all of the official repositories are actually specifically
+ * structured to benefit this strategy.
+ */
+ SYNC_ROOT,
+};
+
+extern const struct global_type gt_sync_strategy;
+
+#endif /* SRC_CONFIG_SYNC_STRATEGY_H_ */
--- /dev/null
+#ifndef SRC_CONFIG_TYPES_H_
+#define SRC_CONFIG_TYPES_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <toml.h>
+
+struct option_field;
+struct group_fields;
+
+/** This option can be set from the command line. */
+#define AVAILABILITY_GETOPT (1 << 0)
+/** This option can be set from the TOML file. */
+#define AVAILABILITY_TOML (1 << 1)
+
+typedef void (*print_function)(
+ struct group_fields const *,
+ struct option_field const *,
+ void *
+);
+typedef int (*argv_parse_function)(
+ struct option_field const *,
+ char const *,
+ void *
+);
+typedef int (*toml_parse_function)(
+ struct option_field const *,
+ struct toml_table_t *,
+ void *
+);
+typedef int (*handler_function)(
+ struct option_field const *,
+ char *
+);
+
+struct option_field {
+ /*
+ * Must be zero, alphanumeric or >= 1000.
+ * If zero, signals the end of the array.
+ * If alphanumeric, it's the short option name character.
+ * Otherwise it's just a non-printable identifier.
+ * Must be unique across all option fields.
+ * Mandatory.
+ */
+ int id;
+ /**
+ * For example, if the option name is '--potato', then @name is
+ * "potato".
+ * Mandatory.
+ */
+ char const *name;
+
+ /** Data type. Mandatory. */
+ struct global_type const *type;
+ /**
+ * Number of bytes between the beginning of the struct rpki_config
+ * and the position where this option is stored.
+ * Only relevant when @handler == NULL.
+ */
+ size_t offset;
+ /** Overrides @type->parser and @offset. Optional. */
+ handler_function handler;
+
+ /**
+ * Explanation of the field, for user consumption during --help.
+ * Meant to be short; the bulk of it should be found in the manpage.
+ * Probably should not include punctuation at the end.
+ * Mandatory.
+ */
+ const char *doc;
+ /** Overrides type->arg_doc. Optional. */
+ char const *arg_doc;
+ /**
+ * AVAILABILITY_* flags above.
+ * Default availability is everywhere.
+ * Optional.
+ */
+ int availability;
+ unsigned int min;
+ unsigned int max;
+};
+
+struct group_fields {
+ char const *name;
+ struct option_field const *options;
+};
+
+struct global_type {
+ /** Same as struct option.has_arg. Mandatory. */
+ int has_arg;
+ /**
+ * Number of bytes this data type uses in the rpki_config structure.
+ * Optional. Defaults to zero, obviously.
+ */
+ size_t size;
+
+ /**
+ * Prints this data type during the print_config() function.
+ * Optional.
+ */
+ print_function print;
+
+ /** If the option's handler is not NULL, this is optional. */
+ struct {
+ /**
+ * Convers from string to this data type.
+ * Optional if there are no fields of this type that are read
+ * from argv.
+ */
+ argv_parse_function argv;
+ /**
+ * Converts from a TOML node to this data type.
+ * Optional if there are no fields of this type that are read
+ * from TOML files.
+ */
+ toml_parse_function toml;
+ } parse;
+
+ /**
+ * Function that will release this data type.
+ * If the option's handler is not NULL, this is optional.
+ *
+ * IMPORTANT: This function might be called twice in succession.
+ * Therefore, make sure that it nullifies the value, and reacts properly
+ * when the input is NULL.
+ */
+ void (*free)(void *);
+
+ /**
+ * Descriptor of this type's payload. Printed in usage documentation.
+ * For example, in `--tal=<file>`, @arg_doc is "<file>".
+ * The type might have no payload, so this is optional.
+ */
+ char const *arg_doc;
+};
+
+#endif /* SRC_CONFIG_TYPES_H_ */
--- /dev/null
+#include "config/uint.h"
+
+#include <getopt.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "log.h"
+
+static void
+print_u_int(struct group_fields const *group, struct option_field const *field,
+ void *value)
+{
+ pr_info("%s.%s: %u", group->name, field->name,
+ *((unsigned int *) value));
+}
+
+static int
+parse_argv_u_int(struct option_field const *field, char const *str,
+ void *_result)
+{
+ unsigned long parsed;
+ int *result;
+
+ if (field->type->has_arg != required_argument || str == NULL) {
+ return pr_err("Integer options ('%s' in this case) require an argument.",
+ field->name);
+ }
+
+ errno = 0;
+ parsed = strtoul(str, NULL, 10);
+ if (errno)
+ return pr_errno(errno, "'%s' is not an unsigned integer", str);
+
+ if (parsed < field->min || field->max < parsed) {
+ return pr_err("'%lu' is out of bounds (%u-%u).", parsed,
+ field->min, field->max);
+ }
+
+ result = _result;
+ *result = parsed;
+ return 0;
+}
+
+static int
+parse_toml_u_int(struct option_field const *opt, struct toml_table_t *toml,
+ void *_result)
+{
+ const char *raw;
+ int64_t value;
+ unsigned int *result;
+
+ raw = toml_raw_in(toml, opt->name);
+ if (raw == NULL)
+ return pr_err("TOML integer '%s' was not found.", opt->name);
+ if (toml_rtoi(raw, &value) == -1)
+ return pr_err("Cannot parse '%s' as an integer.", raw);
+
+ if (value < opt->min || opt->max < value) {
+ return pr_err("Integer '%s' is out of range (%u-%u).",
+ opt->name, opt->min, opt->max);
+ }
+
+ result = _result;
+ *result = value;
+ return 0;
+}
+
+const struct global_type gt_u_int = {
+ .has_arg = required_argument,
+ .size = sizeof(unsigned int),
+ .print = print_u_int,
+ .parse.argv = parse_argv_u_int,
+ .parse.toml = parse_toml_u_int,
+ .arg_doc = "<unsigned integer>",
+};
--- /dev/null
+#ifndef SRC_CONFIG_UINT_H_
+#define SRC_CONFIG_UINT_H_
+
+#include "config/types.h"
+
+extern const struct global_type gt_u_int;
+
+#endif /* SRC_CONFIG_UINT_H_ */
#include "file.h"
#include "log.h"
#include "uri.h"
+#include "config/types.h"
static int
toml_to_config(struct toml_table_t *root)