*.trs
test-driver
+# Developer playground
+sandbox/
+
# Temporal files
*~
tmp
-e .settings \
-e .metadata \
-e Debug/ \
- -e tmp
+ -e tmp/ \
+ -e sandbox/
+
fort_SOURCES += common.c common.h
fort_SOURCES += config.h config.c
fort_SOURCES += daemon.h daemon.c
-fort_SOURCES += delete_dir_daemon.h delete_dir_daemon.c
fort_SOURCES += extension.h extension.c
fort_SOURCES += file.h file.c
fort_SOURCES += init.h init.c
-fort_SOURCES += internal_pool.h internal_pool.c
fort_SOURCES += json_parser.c json_parser.h
fort_SOURCES += line_file.h line_file.c
fort_SOURCES += log.h log.c
fort_SOURCES += nid.h nid.c
fort_SOURCES += notify.c notify.h
fort_SOURCES += output_printer.h output_printer.c
-fort_SOURCES += random.h random.c
fort_SOURCES += resource.h resource.c
fort_SOURCES += rpp.h rpp.c
fort_SOURCES += sorted_array.h sorted_array.c
fort_SOURCES += json_handler.h json_handler.c
fort_SOURCES += validation_handler.h validation_handler.c
fort_SOURCES += validation_run.h validation_run.c
-fort_SOURCES += visited_uris.h visited_uris.c
fort_SOURCES += asn1/content_info.h asn1/content_info.c
fort_SOURCES += asn1/decode.h asn1/decode.c
fort_SOURCES += types/uri.h types/uri.c
fort_SOURCES += types/vrp.c types/vrp.h
+fort_SOURCES += cache/local_cache.c cache/local_cache.h
+fort_SOURCES += cache/tmp.c cache/tmp.h
+
fort_SOURCES += config/boolean.c config/boolean.h
fort_SOURCES += config/filename_format.h config/filename_format.c
fort_SOURCES += config/log_conf.h config/log_conf.c
fort_SOURCES += data_structure/array_list.h
fort_SOURCES += data_structure/common.h
+fort_SOURCES += data_structure/path_builder.h data_structure/path_builder.c
fort_SOURCES += data_structure/uthash.h
fort_SOURCES += http/http.h http/http.c
fort_SOURCES += rrdp/rrdp_objects.h rrdp/rrdp_objects.c
fort_SOURCES += rrdp/rrdp_parser.h rrdp/rrdp_parser.c
-fort_SOURCES += rrdp/db/db_rrdp.h rrdp/db/db_rrdp.c
-fort_SOURCES += rrdp/db/db_rrdp_uris.h rrdp/db/db_rrdp_uris.c
-
fort_SOURCES += rsync/rsync.h rsync/rsync.c
fort_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
This file lists the abbreviations used through the code.
(Standard C, dependency and RFC-defined abbreviations are generally excluded.)
-If you find an abbreviation that is not listed here, feel free to report it as
-a bug.
addr: address
addr4: IPv4 address
cert: certificate
certstack: certificate stack
db: database
+dl: download
eof: end of file
err: error
fd: File Descriptor (see `man 2 accept`)
guri: global URI
hdr: header
+hh: hash (table) hook
+ht: hash table
id: identifier
len: length
max: maximum
refget: reference get (+1 to reference counter)
refput: reference put (-1 to reference counter)
rk: Router Key
-rpp: Repository Publication Point
+rpp: Repository Publication Point (RFC 6481)
str: string
tmp: temporal
+ts: timestamp
uint: unsigned int
vrp: Validated ROA Payload (RFC 6811)
vrps: Validated ROA Payloads (VRP database)
--- /dev/null
+#define _XOPEN_SOURCE 500
+
+#include "cache/local_cache.h"
+
+#include <dirent.h> /* opendir(), readdir(), closedir() */
+#include <strings.h> /* strcasecmp */
+#include <sys/types.h> /* opendir(), closedir(), stat() */
+#include <sys/stat.h> /* stat() */
+#include <sys/queue.h> /* STAILQ */
+#include <unistd.h> /* stat() */
+#include <time.h>
+#include <jansson.h>
+
+#include "alloc.h"
+#include "file.h"
+#include "log.h"
+#include "data_structure/path_builder.h"
+#include "data_structure/uthash.h"
+#include "http/http.h"
+#include "rsync/rsync.h"
+
+/* FIXME needs locking */
+
+/*
+ * Have we ever attempted to download this directly?
+ * Otherwise we actually downloaded a descendant.
+ *
+ * Directly downloaded nodes need to be retained, along with their ancestors.
+ * If the download was successful, they should never have children (as this
+ * would be redundant), though their directory counterparts probably will.
+ */
+#define CNF_DIRECT (1 << 0)
+/* Has it downloaded successfully at some point? */
+#define CNF_SUCCESS (1 << 1)
+/* Has it been traversed during the current cleanup? */
+#define CNF_FOUND (1 << 2)
+/*
+ * If enabled, node represents a file. Otherwise, node is a directory.
+ * Only valid on HTTPs trees; we never know what rsync downloads.
+ */
+#define CNF_FILE (1 << 3)
+
+struct cache_node {
+ char *basename; /* Simple file name, parents not included */
+
+ /* CNF_* */
+ int flags;
+ /*
+ * Last successful download timestamp.
+ * (Only if CNF_DIRECT & CNF_SUCCESS.)
+ * FIXME Intended to later decide whether a file should be deleted,
+ * when the cache is running out of space.
+ */
+ time_t ts_success;
+ /*
+ * Last download attempt timestamp. (Only if CNF_DIRECT.)
+ * Decides whether the file needs to be updated.
+ */
+ time_t ts_attempt;
+ /* Last download attempt's result status. (Only if CNF_DIRECT) */
+ int error;
+
+ struct cache_node *parent; /* Simple pointer */
+ struct cache_node *children; /* Hash table */
+
+ UT_hash_handle hh; /* Hash table hook */
+};
+
+static struct cache_node *rsync;
+static struct cache_node *https;
+
+static time_t startup_time; /* When we started the last validation */
+
+static bool
+is_root(struct cache_node *node)
+{
+ return node->parent == NULL;
+}
+
+/* Minimizes multiple evaluation */
+static struct cache_node *
+add_child(struct cache_node *parent, char const *basename)
+{
+ struct cache_node *child;
+ char *key;
+ size_t keylen;
+
+ child = pzalloc(sizeof(struct cache_node));
+ child->basename = pstrdup(basename);
+ child->parent = parent;
+
+ key = child->basename;
+ keylen = strlen(key);
+
+ HASH_ADD_KEYPTR(hh, parent->children, key, keylen, child);
+
+ return child;
+}
+
+static struct cache_node *
+init_root(struct cache_node *root, char const *name)
+{
+ if (root != NULL)
+ return root;
+
+ root = pzalloc(sizeof(struct cache_node));
+ root->basename = pstrdup(name);
+
+ return root;
+}
+
+/* FIXME recursive */
+static void
+delete_node(struct cache_node *node, bool force)
+{
+ struct cache_node *child, *tmp;
+
+ HASH_ITER(hh, node->children, child, tmp)
+ delete_node(child, force);
+
+ if (force || !is_root(node)) {
+ if (node->parent != NULL)
+ HASH_DEL(node->parent->children, node);
+ free(node->basename);
+ free(node);
+ }
+}
+
+static int
+json_tt_value(struct json_t const *json, time_t *result)
+{
+ char const *str;
+ struct tm tm;
+ time_t tmp;
+
+ if (json == NULL)
+ return -1;
+ str = json_string_value(json);
+ if (str == NULL)
+ return -1;
+ str = strptime(str, "%FT%T%z", &tm);
+ if (str == NULL || *str != 0)
+ return -1;
+ tmp = mktime(&tm);
+ if (tmp == ((time_t) -1))
+ return -1;
+
+ *result = tmp;
+ return 0;
+}
+
+static struct cache_node *
+json2node(json_t *json, struct cache_node *parent)
+{
+ struct cache_node *node, *child;
+ char const *string;
+ json_t *jchild;
+ size_t c;
+
+ if (json == NULL)
+ return NULL;
+
+ node = pzalloc(sizeof(struct cache_node));
+
+ string = json_string_value(json_object_get(json, "basename"));
+ if (string == NULL) {
+ pr_op_warn("Tag 'basename' of a metadata.json's download node cannot be parsed as a string; skipping.");
+ goto cancel;
+ }
+ node->basename = pstrdup(string);
+
+ jchild = json_object_get(json, "flags");
+ if (!json_is_integer(jchild)) {
+ pr_op_warn("Tag 'flags' of metadata.json's download node '%s' cannot be parsed as an integer; skipping.",
+ node->basename);
+ goto cancel;
+ }
+ node->flags = json_integer_value(jchild);
+
+ if (json_tt_value(json_object_get(json, "ts_success"), &node->ts_success)) {
+ pr_op_warn("Tag 'success' of metadata.json's download node '%s' cannot be parsed as a date; skipping.",
+ node->basename);
+ goto cancel;
+ }
+
+ if (json_tt_value(json_object_get(json, "ts_attempt"), &node->ts_attempt)) {
+ pr_op_warn("Tag 'attempt' of metadata.json's download node '%s' cannot be parsed as a date; skipping.",
+ node->basename);
+ goto cancel;
+ }
+
+ jchild = json_object_get(json, "error");
+ if (!json_is_integer(jchild)) {
+ pr_op_warn("Tag 'error' of metadata.json's download node '%s' cannot be parsed as an integer; skipping.",
+ node->basename);
+ goto cancel;
+ }
+ node->error = json_integer_value(jchild);
+
+ jchild = json_object_get(json, "children");
+ if (jchild != NULL && !json_is_array(jchild)) {
+ pr_op_warn("Tag 'children' of metadata.json's download node '%s' cannot be parsed as an array; skipping.",
+ node->basename);
+ goto cancel;
+ }
+
+ for (c = 0; c < json_array_size(jchild); c++) {
+ child = json2node(json_array_get(jchild, c), node);
+ if (child == NULL)
+ goto cancel;
+ HASH_ADD_KEYPTR(hh, node->children, child->basename,
+ strlen(child->basename), child);
+ }
+
+ node->parent = parent;
+ pr_op_debug("Node '%s' successfully loaded from metadata.json.",
+ node->basename);
+ return node;
+
+cancel:
+ delete_node(node, true);
+ return NULL;
+}
+
+static void
+load_metadata_json(void)
+{
+ /*
+ * Note: Loading metadata.json is one of few things Fort can fail at
+ * without killing itself. It's just a cache of a cache.
+ */
+
+ struct path_builder pb;
+ char *filename;
+ json_t *root;
+ json_error_t jerror;
+
+ struct cache_node *node;
+ size_t d;
+
+ int error;
+
+ path_init(&pb);
+ path_append(&pb, config_get_local_repository());
+ path_append(&pb, "metadata.json");
+ error = path_compile(&pb, &filename);
+ if (error) {
+ pr_op_err("Unable to build metadata.json's path: %s",
+ strerror(error));
+ goto end;
+ }
+
+ root = json_load_file(filename, 0, &jerror);
+
+ free(filename);
+
+ if (root == NULL) {
+ pr_op_err("Json parsing failure at metadata.json (%d:%d): %s",
+ jerror.line, jerror.column, jerror.text);
+ goto end;
+ }
+ if (json_typeof(root) != JSON_ARRAY) {
+ pr_op_err("The root tag of metadata.json is not an array.");
+ goto end;
+ }
+
+ for (d = 0; d < json_array_size(root); d++) {
+ node = json2node(json_array_get(root, d), NULL);
+ if (node == NULL)
+ continue;
+ else if (strcasecmp(node->basename, "rsync") == 0)
+ rsync = node;
+ else if (strcasecmp(node->basename, "https") == 0)
+ https = node;
+ else {
+ pr_op_warn("Ignoring unrecognized json node '%s'.",
+ node->basename);
+ delete_node(node, true);
+ }
+ }
+
+end:
+ json_decref(root);
+ if (rsync == NULL)
+ rsync = init_root(rsync, "rsync");
+ if (https == NULL)
+ https = init_root(https, "https");
+}
+
+void
+cache_prepare(void)
+{
+ startup_time = time(NULL);
+ if (startup_time == ((time_t) -1))
+ pr_crit("time(NULL) returned -1");
+
+ if (rsync == NULL)
+ load_metadata_json();
+}
+
+static int
+delete_node_file(struct cache_node *node, bool is_file)
+{
+ struct path_builder pb;
+ struct cache_node *cursor;
+ char *path;
+ int error;
+
+ path_init(&pb);
+ for (cursor = node; cursor != NULL; cursor = cursor->parent)
+ path_append(&pb, cursor->basename);
+ path_append(&pb, config_get_local_repository());
+ path_reverse(&pb);
+ error = path_compile(&pb, &path);
+ if (error) {
+ pr_val_err("Cannot override '%s'; path is bogus: %s",
+ node->basename, strerror(error));
+ return error;
+ }
+
+ if (is_file) {
+ if (remove(path) != 0) {
+ error = errno;
+ pr_val_err("Cannot override file '%s': %s",
+ path, strerror(error));
+ }
+ } else {
+ error = file_rm_rf(path);
+ pr_val_err("Cannot override directory '%s': %s",
+ path, strerror(error));
+ }
+
+ free(path);
+ return error;
+}
+
+static bool
+was_recently_downloaded(struct cache_node *node)
+{
+ return (node->flags & CNF_DIRECT) && (startup_time <= node->ts_attempt);
+}
+
+static void destroy_tree(struct cache_node *);
+
+/* FIXME recursive */
+static void
+drop_children(struct cache_node *node)
+{
+ struct cache_node *child, *tmp;
+
+ HASH_ITER(hh, node->children, child, tmp)
+ destroy_tree(child);
+}
+
+/**
+ * @changed only on HTTP.
+ */
+int
+cache_download(struct rpki_uri *uri, bool *changed)
+{
+ char *luri;
+ char *token;
+ char *saveptr;
+ struct cache_node *node, *child;
+ bool recursive;
+ int error;
+
+ if (changed != NULL)
+ *changed = false;
+ luri = pstrdup(uri_get_local(uri));
+ token = strtok_r(luri, "/", &saveptr);
+
+ switch (uri_get_type(uri)) {
+ case UT_RSYNC:
+ node = rsync;
+ recursive = true;
+ break;
+ case UT_HTTPS:
+ node = https;
+ recursive = false;
+ break;
+ default:
+ pr_crit("Unexpected URI type: %d", uri_get_type(uri));
+ }
+
+ while ((token = strtok_r(NULL, "/", &saveptr)) != NULL) {
+ if (node->flags & CNF_FILE) {
+ /* node used to be a file, now it's a dir. */
+ delete_node_file(node, true);
+ node->flags = 0;
+ }
+
+ HASH_FIND_STR(node->children, token, child);
+
+ if (child == NULL) {
+ /* Create child */
+ do {
+ node = add_child(node, token);
+ token = strtok_r(NULL, "/", &saveptr);
+ } while (token != NULL);
+ goto download;
+
+ } else if (recursive) {
+ if (was_recently_downloaded(child) && !child->error) {
+ error = 0;
+ goto end;
+ }
+
+ }
+
+ node = child;
+ }
+
+ if (was_recently_downloaded(node)) {
+ error = node->error;
+ goto end;
+ }
+
+ if (!recursive && !(node->flags & CNF_FILE)) {
+ /* node used to be a dir, now it's a file. */
+ delete_node_file(node, false);
+ }
+
+download:
+ switch (uri_get_type(uri)) {
+ case UT_RSYNC:
+ error = rsync_download(uri);
+ break;
+ case UT_HTTPS:
+ error = http_download(uri, changed);
+ break;
+ default:
+ pr_crit("Unexpected URI type: %d", uri_get_type(uri));
+ }
+
+ node->error = error;
+ node->flags = CNF_DIRECT;
+ node->ts_attempt = time(NULL);
+ if (node->ts_attempt == ((time_t) -1))
+ pr_crit("time(NULL) returned -1");
+ if (!error) {
+ node->flags |= CNF_SUCCESS | (recursive ? 0 : CNF_FILE);
+ node->ts_success = node->ts_attempt;
+ }
+ drop_children(node);
+
+end:
+ free(luri);
+ return error;
+}
+
+static void
+path_rm_rf(struct path_builder *pb, char const *filename)
+{
+ char const *path;
+ int error;
+
+ error = path_peek(pb, &path);
+ if (error) {
+ pr_op_err("Path builder error code %d; cannot delete directory. (Basename is '%s')",
+ error, filename);
+ return;
+ }
+
+ error = file_rm_rf(path);
+ if (error)
+ pr_op_err("Cannot delete %s: %s", path, strerror(error));
+}
+
+/* FIXME recursive */
+static void
+cleanup_recursive(struct cache_node *node, struct path_builder *pb)
+{
+ char const *path;
+ struct stat meta;
+ DIR *dir;
+ struct dirent *file;
+ struct cache_node *child, *tmp;
+ int error;
+
+ /* FIXME We need to recover from path too long... */
+ path_append(pb, node->basename);
+ error = path_peek(pb, &path);
+ if (error) {
+ pr_op_err("Cannot clean up directory (basename is '%s'): %s",
+ node->basename, strerror(error));
+ goto end;
+ }
+
+ if (stat(path, &meta) != 0) {
+ error = errno;
+ if (error == ENOENT) {
+ /* Node exists but file doesn't: Delete node */
+ delete_node(node, false);
+ goto end;
+ }
+
+ pr_op_err("Cannot clean up '%s'; stat() returned errno %d: %s",
+ path, error, strerror(error));
+ goto end;
+ }
+
+ if (was_recently_downloaded(node) && !node->error)
+ goto end; /* Node is active (ie. used recently): Keep it. */
+
+ /*
+ * From now on, file exists but node is stale.
+ * We'll aim to delete both.
+ */
+
+ if (S_ISREG(meta.st_mode)) {
+ /* Both node and file exist, but inactive: Delete */
+ remove(path);
+ delete_node(node, false);
+
+ } else if (S_ISDIR(meta.st_mode)) {
+ dir = opendir(path);
+ if (dir == NULL) {
+ error = errno;
+ pr_op_err("Cannot clean up '%s'; S_ISDIR() but !opendir(): %s",
+ path, strerror(error));
+ goto end;
+ }
+
+ /*
+ * Directory exists but node is stale.
+ * A child might be fresh, so recurse.
+ */
+
+ FOREACH_DIR_FILE(dir, file) {
+ if (S_ISDOTS(file))
+ continue;
+
+ HASH_FIND_STR(node->children, file->d_name, child);
+ if (child != NULL) {
+ child->flags |= CNF_FOUND;
+ /* File child's node does exist: Recurse. */
+ cleanup_recursive(child, pb);
+ } else {
+ /* File child's node does not exist: Delete. */
+ path_append(pb, file->d_name);
+ path_rm_rf(pb, file->d_name);
+ path_pop(pb, true);
+ }
+
+ }
+ error = errno;
+ closedir(dir);
+ if (error) {
+ pr_op_err("Cannot clean up directory (basename is '%s'): %s",
+ node->basename, strerror(error));
+ goto end;
+ }
+
+ HASH_ITER(hh, node->children, child, tmp) {
+ if (child->flags & CNF_FOUND) {
+ /*
+ * File child still exists, which means there's
+ * at least one active descendant.
+ * Clean the flag and keep the node.
+ */
+ child->flags &= ~CNF_FOUND;
+ } else {
+ /* Node child's file does not exist: Delete. */
+ delete_node(child, false);
+ }
+ }
+
+ if (node->children == NULL && !is_root(node)) {
+ /* Node is inactive and we rm'd its children: Delete. */
+ path_rm_rf(pb, node->basename);
+ delete_node(node, false);
+ }
+
+ } else {
+ /* Outdated, not file nor directory: Delete. */
+ remove(path);
+ delete_node(node, false);
+ }
+
+end:
+ path_pop(pb, true);
+}
+
+static int
+tt2json(time_t tt, json_t **result)
+{
+ char str[32];
+ struct tm tmbuffer, *tm;
+
+ memset(&tmbuffer, 0, sizeof(tmbuffer));
+ tm = localtime_r(&tt, &tmbuffer);
+ if (tm == NULL)
+ return errno;
+ if (strftime(str, sizeof(str) - 1, "%FT%T%z", tm) == 0)
+ return ENOSPC;
+
+ *result = json_string(str);
+ return 0;
+}
+
+/* FIXME recursive */
+static json_t *
+node2json(struct cache_node *node)
+{
+ json_t *json, *date, *children, *jchild;
+ struct cache_node *child, *tmp;
+ int error;
+
+ json = json_object();
+ if (json == NULL) {
+ pr_op_err("json object allocation failure.");
+ return NULL;
+ }
+
+ if (json_object_set_new(json, "basename", json_string(node->basename))) {
+ pr_op_err("Cannot convert string '%s' to json; unknown cause.",
+ node->basename);
+ goto cancel;
+ }
+
+ if (json_object_set_new(json, "flags", json_integer(node->flags))) {
+ pr_op_err("Cannot convert int '%d' to json; unknown cause.",
+ node->flags);
+ goto cancel;
+ }
+
+ error = tt2json(node->ts_success, &date);
+ if (error) {
+ pr_op_err("Cannot convert timestamp %ld to json: %s",
+ node->ts_success, strerror(error));
+ goto cancel;
+ }
+ if (json_object_set_new(json, "ts_success", date)) {
+ pr_op_err("Cannot convert timestamp %ld to json; unknown cause.",
+ node->ts_success);
+ goto cancel;
+ }
+
+ error = tt2json(node->ts_attempt, &date);
+ if (error) {
+ pr_op_err("Cannot convert timestamp %ld to json: %s",
+ node->ts_attempt, strerror(error));
+ goto cancel;
+ }
+ if (json_object_set_new(json, "ts_attempt", date)) {
+ pr_op_err("Cannot convert timestamp %ld to json; unknown cause.",
+ node->ts_attempt);
+ goto cancel;
+ }
+
+ if (json_object_set_new(json, "error", json_integer(node->error))) {
+ pr_op_err("Cannot convert int '%d' to json; unknown cause.",
+ node->error);
+ goto cancel;
+ }
+
+ if (node->children != NULL) {
+ children = json_array();
+ if (children == NULL) {
+ pr_op_err("json array allocation failure.");
+ return NULL;
+ }
+
+ if (json_object_set_new(json, "children", children)) {
+ pr_op_err("Cannot push children array into json node; unknown cause.");
+ goto cancel;
+ }
+
+ HASH_ITER(hh, node->children, child, tmp) {
+ jchild = node2json(child);
+ if (jchild == NULL)
+ goto cancel; /* Error msg already printed */
+ if (json_array_append(children, jchild)) {
+ pr_op_err("Cannot push child into json node; unknown cause.");
+ goto cancel;
+ }
+ }
+ }
+
+ return json;
+
+cancel:
+ json_decref(json);
+ return NULL;
+}
+
+static int
+append_node(json_t *root, struct cache_node *node, char const *name)
+{
+ json_t *child;
+
+ child = node2json(node);
+ if (child == NULL)
+ return -1;
+ if (json_array_append(root, child)) {
+ pr_op_err("Cannot push %s json node into json root; unknown cause.",
+ name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static json_t *
+build_metadata_json(void)
+{
+ json_t *root;
+
+ root = json_array();
+ if (root == NULL) {
+ pr_op_err("json root allocation failure.");
+ return NULL;
+ }
+
+ if (append_node(root, rsync, "rsync")
+ || append_node(root, https, "https")) {
+ json_decref(root);
+ return NULL;
+ }
+
+ return root;
+}
+
+static void
+write_metadata_json(char const *filename)
+{
+ struct json_t *json;
+
+ json = build_metadata_json();
+ if (json == NULL)
+ return;
+
+ if (json_dump_file(json, filename, JSON_COMPACT))
+ pr_op_err("Unable to write metadata.json; unknown cause.");
+
+ json_decref(json);
+}
+
+void
+cache_cleanup(void)
+{
+ struct path_builder pb;
+ char const *json_filename;
+ int error;
+
+ path_init(&pb);
+ path_append(&pb, config_get_local_repository());
+
+ cleanup_recursive(rsync, &pb);
+ cleanup_recursive(https, &pb);
+
+ path_append(&pb, "metadata.json");
+ error = path_peek(&pb, &json_filename);
+ if (error)
+ pr_op_err("Cannot create metadata.json: %s", strerror(error));
+ else
+ write_metadata_json(json_filename);
+
+ path_cancel(&pb);
+}
+
+/* FIXME recursive */
+static void
+destroy_tree(struct cache_node *node)
+{
+ if (node == NULL)
+ return;
+
+ free(node->basename);
+ drop_children(node);
+ if (node->parent != NULL)
+ HASH_DEL(node->parent->children, node);
+ free(node);
+}
+
+void
+cache_teardown(void)
+{
+ destroy_tree(rsync);
+ destroy_tree(https);
+}
--- /dev/null
+#ifndef SRC_CACHE_LOCAL_CACHE_H_
+#define SRC_CACHE_LOCAL_CACHE_H_
+
+#include <stdbool.h>
+#include "types/uri.h"
+
+/* Warms up cache for new validation run */
+void cache_prepare(void);
+
+/* Downloads @uri into the cache */
+int cache_download(struct rpki_uri *uri, bool *);
+
+/* Deletes old untraversed cached files, writes metadata into XML */
+/* FIXME call this */
+void cache_cleanup(void);
+
+/* FIXME call this */
+void cache_teardown(void);
+
+#endif /* SRC_CACHE_LOCAL_CACHE_H_ */
--- /dev/null
+#include "cache/tmp.h"
+
+#include <stdatomic.h>
+#include "config.h"
+#include "data_structure/path_builder.h"
+
+static atomic_uint file_counter;
+
+/*
+ * Returns a unique temporary file name in the local cache.
+ *
+ * The file will not be automatically deleted when it is closed or the program
+ * terminates.
+ *
+ * The name of the function is inherited from tmpfile(3).
+ *
+ * The resulting string needs to be released.
+ */
+int
+cache_tmpfile(char **filename)
+{
+ struct path_builder pb;
+
+ path_init(&pb);
+
+ path_append(&pb, config_get_local_repository());
+ path_append(&pb, "tmp");
+ path_append_uint(&pb, atomic_fetch_add(&file_counter, 1u));
+
+ return path_compile(&pb, filename);
+}
--- /dev/null
+#ifndef SRC_CACHE_TMP_H_
+#define SRC_CACHE_TMP_H_
+
+int cache_tmpfile(char **filename);
+
+#endif /* SRC_CACHE_TMP_H_ */
meta = pmalloc(sizeof(struct metadata_node));
- meta->uri = uri;
- uri_refget(uri);
+ meta->uri = uri_refget(uri);
serial_numbers_init(&meta->serials);
error = init_resources(x509, policy, type, &meta->resources);
#include "config.h"
#include "log.h"
+bool
+str_starts_with(char const *str, char const *prefix)
+{
+ return strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
void
panic_on_fail(int error, char const *function_name)
{
size_t config_len;
int error;
-#ifdef DEBUG_RRDP
- /* Dev will likely need this file in the next offline run. */
- return 0;
-#endif
-
error = remove_file(path);
if (error)
return error;
*result = now;
return 0;
}
-
-/*
- * Maps an absolute @uri that begins with @uri_prefix (either 'rsync://' or
- * 'https://') to a local URI. If a @workspace is set, append such location
- * to the local-repository location (this workspace is used at https URIs).
- *
- * @result is allocated with the local URI.
- *
- * Returns 0 on success, otherwise an error code.
- */
-char *
-map_uri_to_local(char const *uri, char const *uri_prefix)
-{
- char const *repository;
- char *local;
- size_t repository_len;
- size_t uri_prefix_len;
- size_t uri_len;
- size_t extra_slash;
- size_t offset;
-
- repository = config_get_local_repository();
- repository_len = strlen(repository);
- uri_prefix_len = strlen(uri_prefix);
- uri_len = strlen(uri);
-
- uri += uri_prefix_len;
- uri_len -= uri_prefix_len;
- extra_slash = (repository[repository_len - 1] == '/') ? 0 : 1;
-
- local = pmalloc(repository_len + extra_slash + uri_len + 1);
-
- offset = 0;
- strcpy(local + offset, repository);
- offset += repository_len;
- if (extra_slash) {
- strcpy(local + offset, "/");
- offset += extra_slash;
- }
- strncpy(local + offset, uri, uri_len);
- offset += uri_len;
- local[offset] = '\0';
-
- return local;
-}
#define ARRAY_LEN(array) (sizeof(array) / sizeof((array)[0]))
+bool str_starts_with(char const *, char const *);
+
void panic_on_fail(int, char const *);
/*
int get_current_time(time_t *);
-char *map_uri_to_local(char const *, char const *);
-
#endif /* SRC_RTR_COMMON_H_ */
char *tal;
/** Path of our local clone of the repository */
char *local_repository;
- /**
- * Handle TAL URIs in random order?
- * (https://tools.ietf.org/html/rfc8630#section-3, last
- * paragraphs)
- */
+ /* Deprecated; does nothing. */
bool shuffle_tal_uris;
/**
* rfc6487#section-7.2, last paragraph.
struct {
/* Enables the protocol */
bool enabled;
- /*
- * Priority, this will override the order set at the CAs in
- * their accessMethod extension.
- */
+ /* Deprecated; does nothing. */
unsigned int priority;
/* Synchronization download strategy. */
char *strategy;
struct {
/* Enables the protocol */
bool enabled;
- /*
- * Priority, whenever there's an option to sync something via
- * http or rsync, use this priority. When working with CAs, this
- * will override the order set at the CAs in their accessMethod
- * extension.
- */
+ /* Deprecated; does nothing. */
unsigned int priority;
/* Retry conf, utilized on errors */
struct {
.name = "shuffle-uris",
.type = >_bool,
.offset = offsetof(struct rpki_config, shuffle_tal_uris),
- .doc = "Shuffle URIs in the TAL before accessing them",
+ .doc = "Deprecated; does nothing.",
+ .deprecated = true,
}, {
.id = 1002,
.name = "maximum-certificate-depth",
.name = "rsync.priority",
.type = >_uint,
.offset = offsetof(struct rpki_config, rsync.priority),
- .doc = "Priority of execution to fetch repositories files, a higher value means higher priority",
+ .doc = "Deprecated; does nothing.",
+ .deprecated = true,
.min = 0,
.max = 100,
}, {
.name = "rsync.arguments-recursive",
.type = >_string_array,
.offset = offsetof(struct rpki_config, rsync.args.recursive),
- .doc = "Deprecated; does nothing.",
+ .doc = "RSYNC program arguments",
.availability = AVAILABILITY_JSON,
/* Unlimited */
.max = 0,
.name = "rsync.arguments-flat",
.type = >_string_array,
.offset = offsetof(struct rpki_config, rsync.args.flat),
- .doc = "RSYNC program arguments that will trigger a non-recursive RSYNC",
+ .doc = "Deprecated; does nothing.",
.availability = AVAILABILITY_JSON,
/* Unlimited */
.max = 0,
+ .deprecated = true,
},
/* HTTP requests parameters */
.name = "http.priority",
.type = >_uint,
.offset = offsetof(struct rpki_config, http.priority),
- .doc = "Priority of execution to fetch repositories files, a higher value means higher priority",
+ .doc = "Deprecated; does nothing.",
+ .deprecated = true,
.min = 0,
.max = 100,
}, {
static void
set_default_values(void)
{
- static char const *recursive_rsync_args[] = { "<deprecated>" };
- static char const *flat_rsync_args[] = {
- "--times",
- "--contimeout=20",
- "--timeout=15",
- "--max-size=20MB",
- "--dirs",
- "$REMOTE",
- "$LOCAL",
+ static char const *recursive_rsync_args[] = {
+ "-rtz", "--delete",
+
+ "--contimeout=20", "--max-size=20MB", "--timeout=15",
+
+ "--include=*/", "--include=*.cer", "--include=*.crl",
+ "--include=*.gbr", "--include=*.mft", "--include=*.roa",
+ "--exclude=*",
+
+ "$REMOTE", "$LOCAL",
};
+ static char const *flat_rsync_args[] = { "<deprecated>" };
/*
* Values that might need to be freed WILL be freed, so use heap
return rpki_config.local_repository;
}
-bool
-config_get_shuffle_tal_uris(void)
-{
- return rpki_config.shuffle_tal_uris;
-}
-
unsigned int
config_get_max_cert_depth(void)
{
return !rpki_config.work_offline && rpki_config.rsync.enabled;
}
-unsigned int
-config_get_rsync_priority(void)
-{
- return rpki_config.rsync.priority;
-}
-
unsigned int
config_get_rsync_retry_count(void)
{
struct string_array const *
config_get_rsync_args(void)
{
- return &rpki_config.rsync.args.flat;
+ return &rpki_config.rsync.args.recursive;
}
bool
return !rpki_config.work_offline && rpki_config.http.enabled;
}
-unsigned int
-config_get_http_priority(void)
-{
- return rpki_config.http.priority;
-}
-
unsigned int
config_get_http_retry_count(void)
{
char const *config_get_tal(void);
char const *config_get_local_repository(void);
-bool config_get_shuffle_tal_uris(void);
unsigned int config_get_max_cert_depth(void);
enum mode config_get_mode(void);
char const *config_get_http_user_agent(void);
long config_get_http_max_file_size(void);
char const *config_get_http_ca_path(void);
bool config_get_rsync_enabled(void);
-unsigned int config_get_rsync_priority(void);
unsigned int config_get_rsync_retry_count(void);
unsigned int config_get_rsync_retry_interval(void);
char *config_get_rsync_program(void);
struct string_array const *config_get_rsync_args(void);
bool config_get_http_enabled(void);
-unsigned int config_get_http_priority(void);
unsigned int config_get_http_retry_count(void);
unsigned int config_get_http_retry_interval(void);
char const *config_get_output_roa(void);
{
EVP_MD const *md;
EVP_MD_CTX *ctx;
- int error = 0;
+ int error;
error = get_md(algorithm, &md);
if (error)
modifiers void \
name##_add(struct name *list, elem_type *elem) \
{ \
- elem_type *tmp; \
- \
if (list->array == NULL) { \
list->capacity = 8; \
- list->array = malloc(list->capacity \
+ list->array = pmalloc(list->capacity \
* sizeof(elem_type)); \
- if (list->array == NULL) \
- enomem_panic(); \
} \
\
list->len++; \
while (list->len >= list->capacity) { \
list->capacity *= 2; \
- \
- tmp = realloc(list->array, list->capacity \
- * sizeof(elem_type)); \
- if (tmp == NULL) \
- enomem_panic(); \
- list->array = tmp; \
+ list->array = prealloc(list->array, \
+ list->capacity * sizeof(elem_type)); \
} \
\
list->array[list->len - 1] = *elem; \
--- /dev/null
+#include "data_structure/path_builder.h"
+
+#include <openssl/evp.h>
+
+#include "alloc.h"
+#include "log.h"
+#include "crypto/hash.h"
+
+#define SHA256_LEN (256 >> 3) /* 256 / 8, bits -> bytes */
+
+/* These are arbitrary; feel free to change them. */
+#ifndef INITIAL_CAPACITY /* Unit tests want to override this */
+#define INITIAL_CAPACITY 128
+#endif
+#define MAX_CAPACITY 4096
+
+void
+path_init(struct path_builder *pb)
+{
+ pb->string = pmalloc(INITIAL_CAPACITY);
+ pb->len = 0;
+ pb->capacity = INITIAL_CAPACITY;
+ pb->error = 0;
+}
+
+/*
+ * Returns true on success, false on failure.
+ */
+static bool
+path_grow(struct path_builder *pb, size_t total_len)
+{
+ if (total_len > MAX_CAPACITY) {
+ free(pb->string);
+ pr_val_err("Path too long: %zu > %u characters.", total_len,
+ MAX_CAPACITY);
+ pb->error = ENOSPC;
+ return false;
+ }
+
+ do {
+ pb->capacity *= 2;
+ } while (total_len > pb->capacity);
+
+ pb->string = prealloc(pb->string, pb->capacity);
+ return true;
+}
+
+static char const *
+find_slash(char const *str, size_t len)
+{
+ char const *wall;
+
+ for (wall = str + len; str < wall; str++)
+ if (str[0] == '/')
+ return str;
+
+ return str;
+}
+
+/*
+ * Do NOT include the null character in @addlen.
+ * Assumes @addend needs no slashes.
+ */
+static void
+add(struct path_builder *pb, char const *addend, size_t addlen)
+{
+ size_t total_len;
+
+ total_len = pb->len + addlen;
+ if (total_len > pb->capacity && !path_grow(pb, total_len))
+ return;
+
+ memcpy(pb->string + pb->len, addend, addlen);
+ pb->len += addlen;
+}
+
+void
+path_append(struct path_builder *pb, char const *addend)
+{
+ path_append_limited(pb, addend, strlen(addend));
+}
+
+static void
+add_slashed(struct path_builder *pb, char const *addend, size_t addlen)
+{
+ /* Normalize first */
+ switch (addlen) {
+ case 1:
+ if (addend[0] == '.')
+ return;
+ break;
+ case 2:
+ if (addend[0] == '.' && addend[1] == '.') {
+ path_pop(pb, false);
+ return;
+ }
+ break;
+ }
+
+ /* Ok, do */
+ if (pb->len > 0)
+ add(pb, "/", 1);
+ add(pb, addend, addlen);
+}
+
+/* Do NOT include the null character in @addlen. */
+void
+path_append_limited(struct path_builder *pb, char const *addend, size_t addlen)
+{
+ char const *wall;
+ char const *next_slash;
+
+ if (pb->error)
+ return;
+
+ do {
+ for (wall = addend + addlen; addend < wall; addend++, addlen--)
+ if (addend[0] != '/')
+ break;
+ next_slash = find_slash(addend, addlen);
+ if (addend == next_slash)
+ return;
+ add_slashed(pb, addend, next_slash - addend);
+ addlen -= next_slash - addend;
+ addend = next_slash;
+ } while (addlen > 0);
+}
+
+void
+path_append_guri(struct path_builder *pb, struct rpki_uri *uri)
+{
+ char const *guri;
+ char const *colon;
+ size_t schema_len;
+
+ if (pb->error)
+ return;
+
+ guri = uri_get_global(uri);
+
+ colon = strstr(guri, ":");
+ schema_len = colon - guri;
+ path_append_limited(pb, guri, schema_len);
+
+ path_append_limited(pb, colon + 3,
+ uri_get_global_len(uri) - schema_len - 3);
+}
+
+void
+path_append_uint(struct path_builder *pb, unsigned int num)
+{
+ size_t room;
+ int num_len;
+
+ if (pb->error)
+ return;
+
+ if (pb->len != 0 && pb->string[pb->len - 1] != '/')
+ add(pb, "/", 1);
+
+ room = pb->capacity - pb->len;
+ num_len = snprintf(pb->string + pb->len, room, "%X", num);
+ if (num_len < 0)
+ goto bad_print;
+ if (num_len >= room) {
+ if (!path_grow(pb, pb->len + num_len + 1))
+ return;
+
+ room = pb->capacity - pb->len;
+ num_len = snprintf(pb->string + pb->len, room, "%X", num);
+ if (num_len < 0)
+ goto bad_print;
+ if (num_len >= room)
+ pr_crit("pb: %d %zu", num_len, room);
+ }
+
+ pb->len += num_len;
+ return;
+
+bad_print:
+ free(pb->string);
+ pb->error = EIO; /* num_len is not necessarily an error code */
+}
+
+/* Removes the last component added. */
+void
+path_pop(struct path_builder *pb, bool fatal)
+{
+ size_t i;
+
+ if (pb->error)
+ return;
+ if (pb->len == 0) {
+ if (fatal)
+ pr_crit("Programming error: Attempting to pop empty path builder");
+ free(pb->string);
+ pb->error = -pr_val_err("Path cannot '..' over the root.");
+ return;
+ }
+
+ for (i = pb->len - 1; i >= 1; i--) {
+ if (pb->string[i] == '/') {
+ pb->len = i;
+ return;
+ }
+ }
+
+ pb->len = (pb->string[0] == '/') && (pb->len > 1);
+}
+
+static void
+reverse_string(char *str, size_t len)
+{
+ char *b, *e; /* beginning, end */
+ char tmp;
+
+ for (b = str, e = str + len - 1; b < e; b++, e--) {
+ tmp = *b;
+ *b = *e;
+ *e = tmp;
+ }
+}
+
+/* Turns ab/cd/ef/gh into gh/ef/cd/ab. */
+void
+path_reverse(struct path_builder *pb)
+{
+ size_t min;
+ size_t max;
+
+ if (pb->error)
+ return;
+
+ reverse_string(pb->string, pb->len);
+
+ min = 0;
+ for (max = 1; max < pb->len; max++) {
+ if (pb->string[max] == '/') {
+ reverse_string(&pb->string[min], max - min);
+ max++;
+ min = max;
+ }
+ }
+ reverse_string(&pb->string[min], pb->len - min);
+}
+
+/*
+ * Returns @pb's current accumulated path. Do not free it.
+ * Result is a temporary pointer; it becomes junk if you call any other pb
+ * functions on @pb afterwards.
+ */
+int
+path_peek(struct path_builder *pb, char const **result)
+{
+ add(pb, "\0", 1);
+ if (pb->error)
+ return pb->error;
+
+ *result = pb->string;
+ pb->len--;
+ return 0;
+}
+
+/* Should not be called more than once. */
+int
+path_compile(struct path_builder *pb, char **result)
+{
+ add(pb, "\0", 1);
+ if (pb->error)
+ return pb->error;
+
+ *result = pb->string;
+ return 0;
+}
+
+void
+path_cancel(struct path_builder *pb)
+{
+ free(pb->string);
+}
--- /dev/null
+#ifndef SRC_DATA_STRUCTURE_PATH_BUILDER_H_
+#define SRC_DATA_STRUCTURE_PATH_BUILDER_H_
+
+/* FIXME add support for absolute paths */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "types/uri.h"
+
+struct path_builder {
+ char *string;
+ size_t len;
+ size_t capacity;
+ int error;
+};
+
+void path_init(struct path_builder *);
+
+/*
+ * Note, the append()s merge slashes:
+ *
+ * a + b = a/b
+ * a/ + b = a/b
+ * a + /b = a/b
+ * a/ + /b = a/b
+ * a// + ///b = a/b
+ * a///b + c//d = a/b/c/d
+ */
+
+void path_append(struct path_builder *, char const *);
+void path_append_limited(struct path_builder *, char const *, size_t);
+void path_append_guri(struct path_builder *, struct rpki_uri *);
+void path_append_uint(struct path_builder *, unsigned int);
+
+void path_pop(struct path_builder *, bool);
+
+void path_reverse(struct path_builder *);
+
+int path_peek(struct path_builder *, char const **);
+int path_compile(struct path_builder *, char **);
+
+void path_cancel(struct path_builder *);
+
+#endif /* SRC_DATA_STRUCTURE_PATH_BUILDER_H_ */
+++ /dev/null
-#define _XOPEN_SOURCE 500
-
-#include "delete_dir_daemon.h"
-
-#include <sys/stat.h>
-#include <errno.h>
-#include <ftw.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include "alloc.h"
-#include "common.h"
-#include "internal_pool.h"
-#include "log.h"
-#include "random.h"
-
-#define MAX_FD_ALLOWED 20
-
-struct rem_dirs {
- char **arr;
- size_t arr_len;
- size_t arr_set;
-};
-
-static int
-remove_file(char const *location)
-{
- int error;
-
- pr_op_debug("Trying to remove file '%s'.", location);
-
-#ifdef DEBUG_RRDP
- /* Dev will likely need this file in the next offline run. */
- return 0;
-#endif
-
- if (remove(location) != 0) {
- error = errno;
- pr_op_err("Couldn't delete file '%s': %s", location,
- strerror(error));
- return error;
- }
-
- return 0;
-}
-
-static int
-remove_dir(char const *location)
-{
- int error;
-
- pr_op_debug("Trying to remove dir '%s'.", location);
-
-#ifdef DEBUG_RRDP
- /* Dev will likely need this directory in the next offline run. */
- return 0;
-#endif
-
- if (remove(location) != 0) {
- error = errno;
- pr_op_err("Couldn't delete directory '%s': %s", location,
- strerror(error));
- return error;
- }
-
- return 0;
-}
-
-static int
-traverse(char const *path, struct stat const *sb, int flag, struct FTW *ftwbuf)
-{
- /*
- * FTW_SLN:
- * Will never be present since FTW_PHYS flag is utilized
- */
- switch (flag) {
- case FTW_DP:
- return remove_dir(path);
- case FTW_F:
- return remove_file(path);
- case FTW_DNR:
- return pr_op_err("Can't access '%s', stop deletion.", path);
- case FTW_NS:
- return pr_op_err("Can't get information of '%s', stop deletion.",
- path);
- case FTW_SL:
- return pr_op_err("Can't delete '%s' since is a symbolic link, stop deletion.",
- path);
- case FTW_D:
- return pr_op_err("Can't delete '%s' dir before deleting its content.",
- path);
- default:
- return pr_op_warn("Unknown path flag %d, doing nothing to '%s'.",
- flag, path);
- }
-}
-
-static void
-remove_from_root(void *arg)
-{
- struct rem_dirs *root_arg = arg;
- char **dirs_arr;
- size_t len, i;
- int error;
-
- dirs_arr = root_arg->arr;
- len = root_arg->arr_set;
-
- /* Release received arg */
- free(root_arg);
-
- for (i = 0; i < len; i++) {
- error = nftw(dirs_arr[i], traverse, MAX_FD_ALLOWED,
- FTW_DEPTH|FTW_MOUNT|FTW_PHYS);
- if (error) {
- if (errno)
- pr_op_debug("Error deleting directory '%s', please delete it manually: %s",
- dirs_arr[i], strerror(errno));
- else
- pr_op_debug("Couldn't delete directory '%s', please delete it manually",
- dirs_arr[i]);
- }
- /* Release at once, won't be needed anymore */
- free(dirs_arr[i]);
- }
-
- pr_op_debug("Done removing dirs.");
- free(dirs_arr);
-}
-
-/*
- * Soft/hard error logic utilized, beware to prepare caller:
- * - '> 0' is a soft error
- * - '< 0' is a hard error
- * - '= 0' no error
- */
-static int
-get_local_path(char const *rcvd, char **result)
-{
- struct stat attr;
- char *tmp, *local_path;
- size_t tmp_size;
- int error;
-
- /* Currently, only rsync URIs are utilized */
- local_path = map_uri_to_local(rcvd, "rsync://");
-
- error = stat(local_path, &attr);
- if (error) {
- /* Soft error */
- pr_op_debug("Error reading path '%s' (discarding): %s",
- local_path, strerror(errno));
- error = errno;
- goto release_local;
- }
-
- if (!S_ISDIR(attr.st_mode)) {
- /* Soft error */
- pr_op_debug("Path '%s' exists but is not a directory (discarding).",
- local_path);
- error = ENOTDIR;
- goto release_local;
- }
-
- /* Assure that root dir ends without '/' */
- tmp_size = strlen(local_path);
- if (strrchr(local_path, '/') == local_path + strlen(local_path) - 1)
- tmp_size--;
-
- tmp = pmalloc(tmp_size + 1);
- strncpy(tmp, local_path, tmp_size);
- tmp[tmp_size] = '\0';
-
- free(local_path);
-
- *result = tmp;
- return 0;
-release_local:
- free(local_path);
- return error;
-}
-
-static int
-rename_local_path(char const *rcvd, char **result)
-{
- char *tmp;
- long random_sfx;
- size_t rcvd_size, tmp_size;
- int error;
-
- rcvd_size = strlen(rcvd);
- /* original size + one underscore + hex random val (8 chars) */
- tmp_size = rcvd_size + 1 + (sizeof(RAND_MAX) * 2);
- tmp = pmalloc(tmp_size + 1);
-
- /* Rename the path with a random suffix */
- random_init();
- random_sfx = random_at_most(RAND_MAX);
-
- snprintf(tmp, tmp_size + 1, "%s_%08lX", rcvd, random_sfx);
-
- if (rename(rcvd, tmp) != 0) {
- error = errno;
- free(tmp);
- pr_op_debug("Couldn't rename '%s' to delete it (discarding): %s",
- rcvd, strerror(error));
- return error;
- }
-
- *result = tmp;
- return 0;
-}
-
-static int
-rename_all_roots(struct rem_dirs *rem_dirs, char **src)
-{
- char *local_path, *delete_path;
- size_t i;
- int error;
-
- for (i = 0; i < rem_dirs->arr_len; i++) {
- local_path = NULL;
- error = get_local_path(src[(rem_dirs->arr_len - 1) - i],
- &local_path);
- if (error < 0)
- return error;
- if (error > 0)
- continue;
-
- delete_path = NULL;
- error = rename_local_path(local_path, &delete_path);
- free(local_path);
- if (error)
- continue;
- rem_dirs->arr[rem_dirs->arr_set++] = delete_path;
- }
-
- return 0;
-}
-
-static struct rem_dirs *
-rem_dirs_create(size_t arr_len)
-{
- struct rem_dirs *tmp;
-
- tmp = pmalloc(sizeof(struct rem_dirs));
-
- tmp->arr = pcalloc(arr_len, sizeof(char *));
- tmp->arr_len = arr_len;
- tmp->arr_set = 0;
-
- return tmp;
-}
-
-static void
-rem_dirs_destroy(struct rem_dirs *rem_dirs)
-{
- size_t i;
-
- for (i = 0; i < rem_dirs->arr_set; i++)
- free(rem_dirs->arr[i]);
- free(rem_dirs->arr);
- free(rem_dirs);
-}
-
-/*
- * Remove the files listed at @roots array of @roots_len size. The local files
- * will be searched at the specified HTTP local @workspace.
- *
- * The daemon will be as quiet as possible, since most of its job is done
- * asynchronously. Also, it works on the best possible effort; some errors are
- * treated as "soft" errors, since the directory deletion still doesn't
- * considers the relations (parent-child) at dirs.
- */
-int
-delete_dir_daemon_start(char **roots, size_t roots_len)
-{
- struct rem_dirs *arg;
- int error;
-
- arg = rem_dirs_create(roots_len);
-
- error = rename_all_roots(arg, roots);
- if (error) {
- rem_dirs_destroy(arg);
- return error;
- }
-
- /* Thread arg is released at thread */
- internal_pool_push("Directory deleter", remove_from_root, arg);
-
- return 0;
-}
+++ /dev/null
-#ifndef SRC_DELETE_DIR_DAEMON_H_
-#define SRC_DELETE_DIR_DAEMON_H_
-
-#include <stddef.h>
-
-int delete_dir_daemon_start(char **, size_t);
-
-#endif /* SRC_DELETE_DIR_DAEMON_H_ */
#include "extension.h"
#include <errno.h>
+#include "cert_stack.h"
#include "common.h"
#include "log.h"
#include "nid.h"
+#define _XOPEN_SOURCE 500 /* nftw() */
+
#include "file.h"
#include <errno.h>
#include <stdlib.h>
+#include <ftw.h> /* nftw() */
+#include <sys/types.h> /* opendir(), closedir() */
+#include <dirent.h> /* opendir(), readdir(), closedir() */
+
#include "alloc.h"
#include "log.h"
+#include "data_structure/uthash.h"
-static int
-file_get(char const *file_name, FILE **result, struct stat *stat,
- char const *mode)
+int
+file_open(char const *file_name, FILE **result, struct stat *stat)
{
FILE *file;
int error;
- file = fopen(file_name, mode);
+ file = fopen(file_name, "rb");
if (file == NULL) {
error = errno;
pr_val_err("Could not open file '%s': %s", file_name,
return error;
}
-int
-file_open(char const *file_name, FILE **result, struct stat *stat)
-{
- return file_get(file_name, result, stat, "rb");
-}
-
int
file_write(char const *file_name, FILE **result)
{
- struct stat stat;
- return file_get(file_name, result, &stat, "wb");
+ FILE *file;
+ int error;
+
+ file = fopen(file_name, "wb");
+ if (file == NULL) {
+ error = errno;
+ pr_val_err("Could not open file '%s': %s", file_name,
+ strerror(error));
+ *result = NULL;
+ return error;
+ }
+
+ *result = file;
+ return 0;
}
void
* code. It literally doesn't say how to get an error
* code.
*/
- pr_val_err("File reading error. The error message is (apparently) '%s'",
+ pr_val_err("File reading error. The error message is (possibly) '%s'",
strerror(error));
free(fc->buffer);
goto end;
file_close(tmp);
return true;
}
+
+static int
+rm(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
+{
+ return (remove(fpath) != 0) ? errno : 0;
+}
+
+/* Same as `system("rm -rf <path>")`, but more portable and maaaaybe faster. */
+int
+file_rm_rf(char const *path)
+{
+ /* FIXME optimize that 32 */
+ return nftw(path, rm, 32, FTW_DEPTH | FTW_PHYS);
+}
+
+static int
+lsR(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
+{
+ unsigned int i;
+
+ for (i = 0; i < ftwbuf->level; i++)
+ printf("\t");
+ printf("%s\n", &fpath[ftwbuf->base]);
+
+ return 0;
+}
+
+void
+file_ls_R(char const *path)
+{
+ nftw(path, lsR, 32, FTW_PHYS);
+}
bool file_valid(char const *);
+int file_rm_rf(char const *);
+void file_ls_R(char const *);
+
+/*
+ * Remember that this API is awkward:
+ *
+ * 1. Check errno after the loop.
+ * 2. Probably also check S_ISDOTS() during the loop.
+ * 3. Do closedir() even on error.
+ */
+#define FOREACH_DIR_FILE(dir, file) for ( \
+ errno = 0, file = readdir(dir); \
+ file != NULL; \
+ errno = 0, file = readdir(dir) \
+ )
+
+#define S_ISDOTS(file) \
+ (strcmp((file)->d_name, ".") == 0 || strcmp((file)->d_name, "..") == 0)
+
#endif /* SRC_FILE_H_ */
-#include "http.h"
+#include "http/http.h"
#include <errno.h>
#include <stdio.h>
#include "config.h"
#include "file.h"
#include "log.h"
+#include "cache/tmp.h"
+#include "data_structure/uthash.h"
struct http_handler {
CURL *curl;
curl_global_cleanup();
}
+static int
+get_ims(char const *file, time_t *ims)
+{
+ struct stat meta;
+ int error;
+
+ if (stat(file, &meta) != 0) {
+ error = errno;
+ *ims = 0;
+ return (error == ENOENT) ? 0 : error;
+ }
+
+ *ims = meta.st_mtim.tv_sec;
+ return 0;
+}
+
static void
setopt_str(CURL *curl, CURLoption opt, char const *value)
{
struct write_callback_arg {
size_t total_bytes;
int error;
- FILE *dst;
+
+ char const *file_name;
+ FILE *file; /* Initialized lazily */
};
static size_t
return 0; /* Ugh. See fwrite(3) */
}
- return fwrite(data, size, nmemb, arg->dst);
+ if (arg->file == NULL) {
+ arg->error = file_write(arg->file_name, &arg->file);
+ if (arg->error)
+ return 0;
+ }
+
+ return fwrite(data, size, nmemb, arg->file);
}
static void
}
static void
-http_easy_init(struct http_handler *handler, long ims)
+http_easy_init(struct http_handler *handler, curl_off_t ims)
{
CURL *result;
setopt_long(result, CURLOPT_NOSIGNAL, 1L);
if (ims > 0) {
- setopt_long(result, CURLOPT_TIMEVALUE, ims);
+ setopt_long(result, CURLOPT_TIMEVALUE_LARGE, ims);
setopt_long(result, CURLOPT_TIMECONDITION,
CURL_TIMECOND_IFMODSINCE);
}
handler->curl = result;
}
+static void
+http_easy_cleanup(struct http_handler *handler)
+{
+ curl_easy_cleanup(handler->curl);
+}
+
static char const *
curl_err_string(struct http_handler *handler, CURLcode res)
{
}
/*
- * Fetch data from @uri and write result using @cb (which will receive @arg).
+ * Fetch data from @src and write result on @dst.
*/
static int
-http_fetch(struct http_handler *handler, char const *uri, long *response_code,
- FILE *file)
+http_fetch(char const *src, char const *dst, curl_off_t ims, bool *changed)
{
+ struct http_handler handler;
struct write_callback_arg args;
CURLcode res;
long http_code;
+ int error;
- handler->errbuf[0] = 0;
- setopt_str(handler->curl, CURLOPT_URL, uri);
+ http_easy_init(&handler, ims);
+
+ handler.errbuf[0] = 0;
+ setopt_str(handler.curl, CURLOPT_URL, src);
args.total_bytes = 0;
args.error = 0;
- args.dst = file;
- setopt_writedata(handler->curl, &args);
-
- pr_val_info("HTTP GET: %s", uri);
- res = curl_easy_perform(handler->curl);
+ args.file_name = dst;
+ args.file = NULL;
+ setopt_writedata(handler.curl, &args);
+
+ pr_val_info("HTTP GET: %s", src);
+ res = curl_easy_perform(handler.curl);
+ if (args.file != NULL)
+ file_close(args.file);
pr_val_debug("Done. Total bytes transferred: %zu", args.total_bytes);
- args.error = validate_file_size(uri, &args);
- if (args.error)
- return args.error;
+ args.error = validate_file_size(src, &args);
+ if (args.error) {
+ error = args.error;
+ goto end;
+ }
- args.error = get_http_response_code(handler, &http_code, uri);
- if (args.error)
- return args.error;
- *response_code = http_code;
+ args.error = get_http_response_code(&handler, &http_code, src);
+ if (args.error) {
+ error = args.error;
+ goto end;
+ }
if (res != CURLE_OK) {
pr_val_err("Error requesting URL: %s. (HTTP code: %ld)",
- curl_err_string(handler, res), http_code);
+ curl_err_string(&handler, res), http_code);
switch (res) {
case CURLE_FILESIZE_EXCEEDED:
- return -EFBIG; /* Do not retry */
+ error = -EFBIG; /* Do not retry */
+ goto end;
case CURLE_OPERATION_TIMEDOUT:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_COULDNT_RESOLVE_PROXY:
case CURLE_FTP_ACCEPT_TIMEOUT:
- return EREQFAILED; /* Retry */
+ error = EREQFAILED; /* Retry */
+ goto end;
default:
- return handle_http_response_code(http_code);
+ error = handle_http_response_code(http_code);
+ goto end;
}
}
if (http_code >= 400) {
pr_val_err("HTTP result code: %ld", http_code);
- return handle_http_response_code(http_code);
+ error = handle_http_response_code(http_code);
+ goto end;
}
if (http_code == 304) {
+ /* Write callback not called, no file to remove. */
pr_val_debug("Not modified.");
- return 0;
+ error = 0;
+ goto end;
}
if (http_code >= 300) {
/*
*/
pr_val_err("HTTP result code: %ld. I don't follow redirects; discarding file.",
http_code);
- return -EINVAL; /* Do not retry. */
+ error = -EINVAL; /* Do not retry. */
+ goto end;
}
pr_val_debug("HTTP result code: %ld", http_code);
- return 0;
-}
-
-static void
-http_easy_cleanup(struct http_handler *handler)
-{
- curl_easy_cleanup(handler->curl);
-}
-
-static long
-download(char const *src, char const *dst, long ims, long *response_code)
-{
- FILE *file;
- struct http_handler handler;
- long error;
+ error = 0;
+ *changed = true;
- error = file_write(dst, &file);
+end: http_easy_cleanup(&handler);
if (error)
- return error;
- http_easy_init(&handler, ims);
-
- error = http_fetch(&handler, src, response_code, file);
-
- http_easy_cleanup(&handler);
- file_close(file);
-
+ remove(dst);
return error;
}
* Assumes @dst's parent directory has already been created.
*/
static int
-do_retries(char const *src, char const *dst, long ims, long *response_code)
+do_retries(char const *src, char const *dst, curl_off_t ims, bool *changed)
{
unsigned int r;
int error;
+ pr_val_info("Downloading '%s'.", src);
+
r = 0;
do {
pr_val_debug("Download attempt #%u...", r);
- error = download(src, dst, ims, response_code);
+ error = http_fetch(src, dst, ims, changed);
switch (error) {
case 0:
pr_val_debug("Download successful.");
pr_val_warn("Download failed; retrying in %u seconds.",
config_get_http_retry_interval());
/*
- * TODO (#78) Wrong. This is slowing the entire tree traversal
+ * TODO (fine) Wrong. This is slowing the entire tree traversal
* down; use a thread pool.
*/
sleep(config_get_http_retry_interval());
} while (true);
}
-static int
-__http_download_file(struct rpki_uri *uri, long *response_code, long ims_value)
+/*
+ * Download @uri->global into @uri->local; HTTP assumed.
+ *
+ * If @changed returns true, the file was downloaded normally.
+ * If @changed returns false, the file already existed and is already its latest
+ * version.
+ * @changed can be NULL.
+ */
+int
+http_download(struct rpki_uri *uri, bool *changed)
{
- char const *tmp_suffix = "_tmp";
- char const *original_file;
- char *tmp_file;
+ char *tmp_file_name;
+ char const *final_file_name;
+ time_t ims;
+ bool __changed;
int error;
- *response_code = 0;
+ if (changed == NULL)
+ changed = &__changed;
+ *changed = false;
- if (!config_get_http_enabled())
- return 0;
+ if (!config_get_rsync_enabled())
+ return 0; /* Skip; caller will work with existing cache. */
- original_file = uri_get_local(uri);
-
- tmp_file = pmalloc(strlen(original_file) + strlen(tmp_suffix) + 1);
- strcpy(tmp_file, original_file);
- strcat(tmp_file, tmp_suffix);
-
- error = create_dir_recursive(tmp_file);
+ error = cache_tmpfile(&tmp_file_name);
+ if (error)
+ return error;
+ final_file_name = uri_get_local(uri);
+ error = get_ims(final_file_name, &ims);
if (error)
- goto release_tmp;
+ goto end;
- error = do_retries(uri_get_global(uri), tmp_file, ims_value,
- response_code);
- /* TODO (#78) Looks like the temporal file isn't being rm'd. */
- if ((*response_code) == 304)
+ error = do_retries(uri_get_global(uri), tmp_file_name, (curl_off_t)ims,
+ changed);
+ if (error || !(*changed))
goto end;
- if (error)
- goto delete_dir;
- /* Overwrite the original file */
- error = rename(tmp_file, original_file);
+ error = create_dir_recursive(final_file_name);
+ if (error) {
+ remove(tmp_file_name);
+ goto end;
+ }
+
+ error = rename(tmp_file_name, final_file_name);
if (error) {
error = errno;
pr_val_err("Renaming temporal file from '%s' to '%s': %s",
- tmp_file, original_file, strerror(error));
- goto delete_dir;
+ tmp_file_name, final_file_name, strerror(error));
+ remove(tmp_file_name);
+ goto end;
}
-end:
- free(tmp_file);
- return 0;
-delete_dir:
- delete_dir_recursive_bottom_up(tmp_file);
-release_tmp:
- free(tmp_file);
- return ENSURE_NEGATIVE(error);
-}
-
-/*
- * Try to download from global @uri into a local directory structure created
- * from local @uri.
- *
- * Return values: 0 on success, negative value on error, EREQFAILED if the
- * request to the server failed.
- */
-int
-http_download_file(struct rpki_uri *uri)
-{
- long response;
- return __http_download_file(uri, &response, 0);
-}
-
-/*
- * Fetch the file from @uri.
- *
- * The HTTP request is made using the header 'If-Modified-Since' with a value
- * of @value (if @value is 0, the header isn't set).
- *
- * Returns:
- * EREQFAILED the request to the server has failed.
- * > 0 file was requested but wasn't downloaded since the server didn't sent
- * a response due to its policy using the header 'If-Modified-Since'.
- * = 0 file successfully downloaded.
- * < 0 an actual error happened.
- */
-int
-http_download_file_with_ims(struct rpki_uri *uri, long value)
-{
- long response;
- int error;
-
- error = __http_download_file(uri, &response, value);
- if (error)
- return error;
-
- /* rfc7232#section-3.3:
- * "the origin server SHOULD generate a 304 (Not Modified) response"
- */
- if (response == 304)
- return 1;
-
- return 0;
+end: free(tmp_file_name);
+ return error;
}
/*
int
http_direct_download(char const *remote, char const *dest)
{
- char const *tmp_suffix = "_tmp";
- struct http_handler handler;
- FILE *out;
- long response_code;
+ bool changed;
char *tmp_file;
int error;
- tmp_file = pstrdup(dest);
- tmp_file = prealloc(tmp_file, strlen(tmp_file) + strlen(tmp_suffix) + 1);
- strcat(tmp_file, tmp_suffix);
-
- error = file_write(tmp_file, &out);
+ error = cache_tmpfile(&tmp_file);
if (error)
- goto end;
-
- http_easy_init(&handler, 0);
- error = http_fetch(&handler, remote, &response_code, out);
- http_easy_cleanup(&handler);
+ return error;
- file_close(out);
+ error = http_fetch(remote, tmp_file, 0, &changed);
if (error)
goto end;
goto end;
}
-end:
- free(tmp_file);
+end: free(tmp_file);
return error;
}
#ifndef SRC_HTTP_HTTP_H_
#define SRC_HTTP_HTTP_H_
-#include <stddef.h>
#include "types/uri.h"
-/* Init on the main process */
int http_init(void);
void http_cleanup(void);
-int http_download_file(struct rpki_uri *);
-int http_download_file_with_ims(struct rpki_uri *, long);
-
+int http_download(struct rpki_uri *, bool *);
int http_direct_download(char const *, char const *);
#endif /* SRC_HTTP_HTTP_H_ */
+++ /dev/null
-#include "internal_pool.h"
-
-#include <stddef.h>
-
-/*
- * This is a basic wrapper for thread_pool functions, but the allocated pool
- * lives at the main thread.
- *
- * Additional threads that must be spawned during execution (those that aren't
- * related to the validation or server thread pool tasks) can be pushed here.
- */
-
-#define INTERNAL_POOL_MAX 10
-
-static struct thread_pool *pool;
-
-int
-internal_pool_init(void)
-{
- int error;
-
- pool = NULL;
- error = thread_pool_create("Internal", INTERNAL_POOL_MAX, &pool);
- if (error)
- return error;
-
- return 0;
-}
-
-void
-internal_pool_push(char const *task_name, thread_pool_task_cb cb, void *arg)
-{
- thread_pool_push(pool, task_name, cb, arg);
-}
-
-void
-internal_pool_cleanup(void)
-{
- thread_pool_destroy(pool);
-}
+++ /dev/null
-#ifndef SRC_INTERNAL_POOL_H_
-#define SRC_INTERNAL_POOL_H_
-
-#include "thread/thread_pool.h"
-
-int internal_pool_init(void);
-void internal_pool_push(char const *, thread_pool_task_cb, void *);
-void internal_pool_cleanup(void);
-
-#endif /* SRC_INTERNAL_POOL_H_ */
#include <err.h>
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
#include "alloc.h"
return crypto_err(&val_config, pr_val_err);
}
+/* FIXME open call hierarchy */
__dead void
enomem_panic(void)
{
#include "config.h"
#include "extension.h"
-#include "internal_pool.h"
+#include "log.h"
#include "nid.h"
#include "thread_var.h"
#include "validation_run.h"
#include "http/http.h"
+#include "incidence/incidence.h"
#include "rtr/rtr.h"
#include "rtr/db/vrps.h"
#include "xml/relax_ng.h"
-#include "rrdp/db/db_rrdp.h"
static int
run_rtr_server(void)
if (error)
goto revert_nid;
- /*
- * TODO (performance) this looks like a lot of overhead. Is it really
- * necessary when mode is STANDALONE?
- */
- error = internal_pool_init();
- if (error)
- goto revert_http;
-
error = relax_ng_init();
if (error)
- goto revert_pool;
+ goto revert_http;
error = vrps_init();
if (error)
goto revert_relax_ng;
- error = db_rrdp_init();
- if (error)
- goto vrps_cleanup;
/* Do stuff */
switch (config_get_mode()) {
/* End */
- db_rrdp_cleanup();
-vrps_cleanup:
vrps_destroy();
revert_relax_ng:
relax_ng_cleanup();
-revert_pool:
- internal_pool_cleanup();
revert_http:
http_cleanup();
revert_nid:
#include "algorithm.h"
#include "alloc.h"
+#include "cert_stack.h"
#include "config.h"
#include "extension.h"
#include "log.h"
#include "asn1/oid.h"
#include "asn1/asn1c/IPAddrBlocks.h"
#include "crypto/hash.h"
+#include "data_structure/array_list.h"
#include "incidence/incidence.h"
#include "object/bgpsec.h"
#include "object/name.h"
#include "object/manifest.h"
#include "object/signed_object.h"
#include "rrdp/rrdp_loader.h"
-#include "rsync/rsync.h"
+#include "cache/local_cache.h"
/* Just to prevent some line breaking. */
#define GN_URI uniformResourceIdentifier
OCTET_STRING_t *sid;
};
-struct sia_uri {
- uint8_t position;
- struct rpki_uri *uri;
-};
+STATIC_ARRAY_LIST(sia_rpp_uris, struct rpki_uri *)
-struct sia_ca_uris {
- struct sia_uri caRepository;
- struct sia_uri rpkiNotify;
- struct sia_uri mft;
+struct sia_uris {
+ struct sia_rpp_uris rpp;
+ struct rpki_uri *mft;
};
struct bgpsec_ski {
};
/* Callback method to fetch repository objects */
-typedef int (access_method_exec)(struct sia_ca_uris *);
+typedef int (access_method_exec)(struct sia_uris *);
+
+struct ad_metadata {
+ char const *name;
+ char const *ia_name;
+ enum uri_type type;
+ char const *type_str;
+ bool required;
+};
+
+static const struct ad_metadata CA_ISSUERS = {
+ .name = "caIssuers",
+ .ia_name = "AIA",
+ .type = UT_RSYNC,
+ .type_str = "rsync",
+ .required = true,
+};
+
+static const struct ad_metadata SIGNED_OBJECT = {
+ .name = "signedObject",
+ .ia_name = "SIA",
+ .type = UT_RSYNC,
+ .type_str = "rsync",
+ .required = true,
+};
+
+static const struct ad_metadata CA_REPOSITORY = {
+ .name = "caRepository",
+ .ia_name = "SIA",
+ .type = UT_RSYNC,
+ .type_str = "rsync",
+ .required = false,
+};
+
+static const struct ad_metadata RPKI_NOTIFY = {
+ .name = "rpkiNotify",
+ .ia_name = "SIA",
+ .type = UT_HTTPS,
+ .type_str = "HTTPS",
+ .required = false,
+};
+
+static const struct ad_metadata RPKI_MANIFEST = {
+ .name = "rpkiManifest",
+ .ia_name = "SIA",
+ .type = UT_RSYNC,
+ .type_str = "rsync",
+ .required = true,
+};
+
+static void
+sia_uris_init(struct sia_uris *uris)
+{
+ sia_rpp_uris_init(&uris->rpp);
+ uris->mft = NULL;
+}
static void
-sia_ca_uris_init(struct sia_ca_uris *sia_uris)
+cleanup_uri(struct rpki_uri **uri)
{
- sia_uris->caRepository.uri = NULL;
- sia_uris->rpkiNotify.uri = NULL;
- sia_uris->mft.uri = NULL;
+ uri_refput(*uri);
}
static void
-sia_ca_uris_cleanup(struct sia_ca_uris *sia_uris)
+sia_uris_cleanup(struct sia_uris *uris)
{
- if (sia_uris->caRepository.uri != NULL)
- uri_refput(sia_uris->caRepository.uri);
- if (sia_uris->rpkiNotify.uri != NULL)
- uri_refput(sia_uris->rpkiNotify.uri);
- if (sia_uris->mft.uri != NULL)
- uri_refput(sia_uris->mft.uri);
+ sia_rpp_uris_cleanup(&uris->rpp, cleanup_uri);
+ uri_refput(uris->mft);
}
static void
BIO *bio;
int error;
+ *result = NULL;
+
bio = BIO_new(BIO_s_file());
if (bio == NULL)
return val_crypto_err("BIO_new(BIO_s_file()) returned NULL");
}
static int
-handle_rpkiManifest(struct rpki_uri *uri, uint8_t pos, void *arg)
+handle_rpkiManifest(struct rpki_uri *uri, void *arg)
{
- struct sia_uri *mft = arg;
- mft->position = pos;
- mft->uri = uri;
- uri_refget(uri);
+ struct sia_uris *uris = arg;
+ uris->mft = uri_refget(uri);
return 0;
}
static int
-handle_caRepository(struct rpki_uri *uri, uint8_t pos, void *arg)
+handle_caRepository(struct rpki_uri *uri, void *arg)
{
- struct sia_uri *repo = arg;
+ struct sia_uris *uris = arg;
pr_val_debug("caRepository: %s", uri_val_get_printable(uri));
- repo->position = pos;
- repo->uri = uri;
+ sia_rpp_uris_add(&uris->rpp, &uri);
uri_refget(uri);
return 0;
}
static int
-handle_rpkiNotify(struct rpki_uri *uri, uint8_t pos, void *arg)
+handle_rpkiNotify(struct rpki_uri *uri, void *arg)
{
- struct sia_uri *notify = arg;
+ struct sia_uris *uris = arg;
pr_val_debug("rpkiNotify: %s", uri_val_get_printable(uri));
- notify->position = pos;
- notify->uri = uri;
+ sia_rpp_uris_add(&uris->rpp, &uri);
uri_refget(uri);
return 0;
}
static int
-handle_signedObject(struct rpki_uri *uri, uint8_t pos, void *arg)
+handle_signedObject(struct rpki_uri *uri, void *arg)
{
struct certificate_refs *refs = arg;
pr_val_debug("signedObject: %s", uri_val_get_printable(uri));
- refs->signedObject = uri;
- uri_refget(uri);
+ refs->signedObject = uri_refget(uri);
return 0;
}
return error;
}
+/*
+ * Create @uri from the @ad
+ */
+static int
+uri_create_ad(struct rpki_uri **uri, ACCESS_DESCRIPTION *ad, enum uri_type type)
+{
+ ASN1_STRING *asn1_string;
+ int ptype;
+
+ asn1_string = GENERAL_NAME_get0_value(ad->location, &ptype);
+
+ /*
+ * RFC 6487: "This extension MUST have an instance of an
+ * AccessDescription with an accessMethod of id-ad-rpkiManifest, (...)
+ * with an rsync URI [RFC5781] form of accessLocation."
+ *
+ * Ehhhhhh. It's a little annoying in that it seems to be stucking more
+ * than one requirement in a single sentence, which I think is rather
+ * rare for an RFC. Normally they tend to hammer things more.
+ *
+ * Does it imply that the GeneralName CHOICE is constrained to type
+ * "uniformResourceIdentifier"? I guess so, though I don't see anything
+ * stopping a few of the other types from also being capable of storing
+ * URIs.
+ *
+ * Also, nobody seems to be using the other types, and handling them
+ * would be a titanic pain in the ass. So this is what I'm committing
+ * to.
+ */
+ if (ptype != GEN_URI) {
+ pr_val_err("Unknown GENERAL_NAME type: %d", ptype);
+ return ENOTSUPPORTED;
+ }
+
+ /*
+ * GEN_URI signals an IA5String.
+ * IA5String is a subset of ASCII, so this cast is safe.
+ * No guarantees of a NULL chara, though.
+ *
+ * TODO (testers) According to RFC 5280, accessLocation can be an IRI
+ * somehow converted into URI form. I don't think that's an issue
+ * because the RSYNC clone operation should not have performed the
+ * conversion, so we should be looking at precisely the IA5String
+ * directory our g2l version of @asn1_string should contain.
+ * But ask the testers to keep an eye on it anyway.
+ */
+ return __uri_create(uri, type,
+ ASN1_STRING_get0_data(asn1_string),
+ ASN1_STRING_length(asn1_string));
+}
+
/**
* The RFC does not explain AD validation very well. This is personal
* interpretation, influenced by Tim Bruijnzeels's response
* (https://mailarchive.ietf.org/arch/msg/sidr/4ycmff9jEU4VU9gGK5RyhZ7JYsQ)
* (I'm being a bit more lax than he suggested.)
*
- * 1. Only one NID needs to be searched at a time. (This is currently somewhat
- * of a coincidence, and will probably be superseded at some point. But I'm
- * not going to complicate this until it's necessary.)
- * 2. The NID MUST be found, otherwise the certificate is invalid.
- * 3. The NID can be found more than once.
- * 4. All access descriptions that match the NID must be URLs.
- * 5. Precisely one of those matches will be an RSYNC URL, and it's the only one
- * we are required to support.
- * (I would have gone with "at least one of those matches", but I don't know
+ * 1. The NID (@nid) can be found more than once.
+ * 2. All access descriptions that match the NID must be URLs.
+ * 3. Depending on meta->required, zero or one of those matches will be an URL
+ * of the meta->type we're expecting.
+ * (I would have gone with "at least zero of those matches", but I don't know
* what to do with the other ones.)
- * 6. Other access descriptions that do not match the NID are allowed and
+ * 4. Other access descriptions that do not match the NID are allowed and
* supposed to be ignored.
- * 7. Other access descriptions that match the NID but do not have RSYNC URIs
- * are also allowed, and also supposed to be ignored.
+ * 5. Other access descriptions that match the NID but do not have recognized
+ * URLs are also allowed, and also supposed to be ignored.
*/
static int
-handle_ad(char const *ia_name, SIGNATURE_INFO_ACCESS *ia,
- char const *ad_name, int ad_nid, int uri_flags, bool required,
- int (*cb)(struct rpki_uri *, uint8_t, void *), void *arg)
+handle_ad(int nid, struct ad_metadata const *meta, SIGNATURE_INFO_ACCESS *ia,
+ int (*cb)(struct rpki_uri *, void *), void *arg)
{
-# define AD_METHOD ((uri_flags & URI_VALID_RSYNC) == URI_VALID_RSYNC ? \
- "RSYNC" : \
- (((uri_flags & URI_VALID_HTTPS) == URI_VALID_HTTPS) ? \
- "HTTPS" : "RSYNC/HTTPS"))
ACCESS_DESCRIPTION *ad;
struct rpki_uri *uri;
- bool found = false;
+ bool found;
unsigned int i;
int error;
+ found = false;
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ia); i++) {
ad = sk_ACCESS_DESCRIPTION_value(ia, i);
- if (OBJ_obj2nid(ad->method) == ad_nid) {
- error = uri_create_ad(&uri, ad, uri_flags);
+ if (OBJ_obj2nid(ad->method) == nid) {
+ error = uri_create_ad(&uri, ad, meta->type);
switch (error) {
case 0:
break;
case ENOTRSYNC:
- continue;
case ENOTHTTPS:
- continue;
case ENOTSUPPORTED:
continue;
default:
if (found) {
uri_refput(uri);
return pr_val_err("Extension '%s' has multiple '%s' %s URIs.",
- ia_name, ad_name, AD_METHOD);
+ meta->ia_name, meta->name, meta->type_str);
}
- error = cb(uri, i, arg);
+ error = cb(uri, arg);
if (error) {
uri_refput(uri);
return error;
}
}
- if (required && !found) {
- pr_val_err("Extension '%s' lacks a '%s' valid %s URI.", ia_name,
- ad_name, AD_METHOD);
+ if (meta->required && !found) {
+ pr_val_err("Extension '%s' lacks a '%s' valid %s URI.",
+ meta->ia_name, meta->name, meta->type_str);
return -ESRCH;
}
}
static int
-handle_caIssuers(struct rpki_uri *uri, uint8_t pos, void *arg)
+handle_caIssuers(struct rpki_uri *uri, void *arg)
{
struct certificate_refs *refs = arg;
/*
* over here is too much trouble, so do the handle_cdp()
* hack.
*/
- refs->caIssuers = uri;
- uri_refget(uri);
+ refs->caIssuers = uri_refget(uri);
return 0;
}
if (aia == NULL)
return cannot_decode(ext_aia());
- error = handle_ad("AIA", aia, "caIssuers", NID_ad_ca_issuers,
- URI_VALID_RSYNC, true, handle_caIssuers, arg);
+ error = handle_ad(NID_ad_ca_issuers, &CA_ISSUERS, aia, handle_caIssuers,
+ arg);
AUTHORITY_INFO_ACCESS_free(aia);
return error;
handle_sia_ca(X509_EXTENSION *ext, void *arg)
{
SIGNATURE_INFO_ACCESS *sia;
- struct sia_ca_uris *uris = arg;
+ struct sia_uris *uris = arg;
int error;
sia = X509V3_EXT_d2i(ext);
if (sia == NULL)
return cannot_decode(ext_sia());
- /* rsync, still the preferred and required */
- error = handle_ad("SIA", sia, "caRepository", NID_caRepository,
- URI_VALID_RSYNC, true, handle_caRepository, &uris->caRepository);
+ /* rsync */
+ error = handle_ad(NID_caRepository, &CA_REPOSITORY, sia,
+ handle_caRepository, uris);
if (error)
goto end;
- /* HTTPS RRDP */
- error = handle_ad("SIA", sia, "rpkiNotify", nid_rpkiNotify(),
- URI_VALID_HTTPS, false, handle_rpkiNotify, &uris->rpkiNotify);
+ /* RRDP */
+ error = handle_ad(nid_rpkiNotify(), &RPKI_NOTIFY, sia,
+ handle_rpkiNotify, uris);
if (error)
goto end;
- /*
- * Store the manifest URI in @mft.
- * (We won't actually touch the manifest until we know the certificate
- * is fully valid.)
- */
- error = handle_ad("SIA", sia, "rpkiManifest", nid_rpkiManifest(),
- URI_VALID_RSYNC, true, handle_rpkiManifest, &uris->mft);
+ /* Manifest */
+ error = handle_ad(nid_rpkiManifest(), &RPKI_MANIFEST, sia,
+ handle_rpkiManifest, uris);
end:
AUTHORITY_INFO_ACCESS_free(sia);
if (sia == NULL)
return cannot_decode(ext_sia());
- error = handle_ad("SIA", sia, "signedObject", nid_signedObject(),
- URI_VALID_RSYNC, true, handle_signedObject, arg);
+ error = handle_ad(nid_signedObject(), &SIGNED_OBJECT, sia,
+ handle_signedObject, arg);
AUTHORITY_INFO_ACCESS_free(sia);
return error;
* @sia_uris will be allocated.
*/
static int
-certificate_validate_extensions_ta(X509 *cert, struct sia_ca_uris *sia_uris,
+certificate_validate_extensions_ta(X509 *cert, struct sia_uris *sia_uris,
enum rpki_policy *policy)
{
struct extension_handler handlers[] = {
* extensions.
*/
static int
-certificate_validate_extensions_ca(X509 *cert, struct sia_ca_uris *sia_uris,
+certificate_validate_extensions_ca(X509 *cert, struct sia_uris *sia_uris,
enum rpki_policy *policy, struct rpp *rpp_parent)
{
struct certificate_refs refs = { 0 };
certificate_validate_aia(struct rpki_uri *caIssuers, X509 *cert)
{
/*
- * TODO (#78) Compare the AIA to the parent's URI.
+ * FIXME Compare the AIA to the parent's URI.
* We're currently not recording the URI, so this can't be solved until
* the #78 refactor.
*/
return 0;
}
-/*
- * Verify that the manifest file actually exists at the local repository, if it
- * doesn't exist then discard the repository (which can result in a attempt
- * to fetch data from another repository).
- */
-static int
-verify_mft_loc(struct rpki_uri *mft_uri)
-{
- if (!valid_file_or_dir(uri_get_local(mft_uri), true, false, pr_val_err))
- return -EINVAL; /* Error already logged */
-
- return 0;
-}
-
-/*
- * Verify the manifest location at the local RRDP workspace.
- *
- * Don't log in case the @mft_uri doesn't exist at the RRDP workspace.
- */
-static int
-verify_rrdp_mft_loc(struct rpki_uri *mft_uri)
-{
- struct rpki_uri *tmp;
- int error;
-
- tmp = NULL;
- error = uri_create_rsync_str_rrdp(&tmp, uri_get_global(mft_uri),
- uri_get_global_len(mft_uri));
- if (error)
- return error;
-
- if (!valid_file_or_dir(uri_get_local(tmp), true, false, NULL)) {
- uri_refput(tmp);
- return -ENOENT;
- }
-
- uri_refput(tmp);
- return 0;
-}
-
static int
-replace_rrdp_mft_uri(struct sia_uri *sia_mft)
+download_rpp(struct sia_uris *uris)
{
- struct rpki_uri *tmp;
- int error;
-
- tmp = NULL;
- error = uri_create_rsync_str_rrdp(&tmp,
- uri_get_global(sia_mft->uri),
- uri_get_global_len(sia_mft->uri));
- if (error)
- return error;
-
- uri_refput(sia_mft->uri);
- sia_mft->uri = tmp;
-
- return 0;
-}
-
-static int
-exec_rrdp_method(struct sia_ca_uris *sia_uris)
-{
- bool data_updated;
- int error;
-
- data_updated = false;
- error = rrdp_load(sia_uris->rpkiNotify.uri, &data_updated);
- if (error)
- return error;
-
- error = verify_rrdp_mft_loc(sia_uris->mft.uri);
- switch(error) {
- case 0:
- /* MFT exists, great! We're good to go. */
- break;
- case -ENOENT:
- /* Doesn't exist and the RRDP data was updated: error */
- if (data_updated)
- return error;
-
- /* Otherwise, force the snapshot processing and check again */
- error = rrdp_reload_snapshot(sia_uris->rpkiNotify.uri);
- if (error)
- return error;
- error = verify_rrdp_mft_loc(sia_uris->mft.uri);
- if (error)
- return error;
- break;
- default:
- return error;
- }
-
- /* Successfully loaded (or no updates yet), update MFT local URI */
- return replace_rrdp_mft_uri(&sia_uris->mft);
-}
-
-static int
-exec_rsync_method(struct sia_ca_uris *sia_uris)
-{
- int error;
-
- error = rsync_download_files(sia_uris->caRepository.uri, false);
- if (error)
- return error;
-
- return verify_mft_loc(sia_uris->mft.uri);
-}
-
-/*
- * Currently only two access methods are supported, just consider those two:
- * rsync and RRDP. If a new access method is supported, this function must
- * change (and probably the sia_ca_uris struct as well).
- *
- * Both access method callbacks must verify the manifest existence.
- */
-static int
-use_access_method(struct sia_ca_uris *sia_uris, access_method_exec rsync_cb,
- access_method_exec rrdp_cb, bool *retry_repo_sync)
-{
- access_method_exec *cb_primary;
- access_method_exec *cb_secondary;
- rrdp_req_status_t rrdp_req_status;
- bool primary_rrdp;
- int upd_error;
- int error;
-
- /*
- * By default, RRDP has a greater priority than rsync.
- * See "http.priority" default value.
- */
- primary_rrdp = true;
- (*retry_repo_sync) = true;
-
- /*
- * RSYNC will always be present (at least for now, see
- * rfc6487#section-4.8.8.1). If rsync is disabled, the cb will take
- * care of that.
- */
- if (sia_uris->rpkiNotify.uri == NULL) {
- primary_rrdp = false;
- error = rsync_cb(sia_uris);
- if (!error)
- return 0;
- goto verify_mft;
- }
-
- /*
- * There isn't any restriction about the preferred access method of
- * children CAs being the same as the parent CA.
- *
- * Two possible scenarios arise:
- * 1) CA Parent didn't utilized (or didn't had) an RRDP update
- * notification URI.
- * 2) CA Parent successfully utilized an RRDP update notification URI.
- *
- * Step (1) is simple, do the check of the preferred access method.
- * Step (2) must do something different.
- * - If RRDP URI was already successfully visited, don't care
- * preference, don't execute access method.
- */
- error = db_rrdp_uris_get_request_status(
- uri_get_global(sia_uris->rpkiNotify.uri), &rrdp_req_status);
- if (error == 0 && rrdp_req_status == RRDP_URI_REQ_VISITED) {
- (*retry_repo_sync) = false;
- return replace_rrdp_mft_uri(&sia_uris->mft);
- }
-
- /* Use CA's or configured priority? */
- if (config_get_rsync_priority() == config_get_http_priority())
- primary_rrdp = sia_uris->caRepository.position
- > sia_uris->rpkiNotify.position;
- else
- primary_rrdp = config_get_rsync_priority()
- < config_get_http_priority();
-
- cb_primary = primary_rrdp ? rrdp_cb : rsync_cb;
- cb_secondary = primary_rrdp ? rsync_cb : rrdp_cb;
-
- /* Try with the preferred; in case of error, try with the next one */
- error = cb_primary(sia_uris);
- if (!error) {
- (*retry_repo_sync) = !primary_rrdp;
- return 0;
- }
-
- if (primary_rrdp) {
- if (error != -EPERM)
- pr_val_info("Couldn't fetch data from RRDP repository '%s', trying to fetch data now from '%s'.",
- uri_get_global(sia_uris->rpkiNotify.uri),
- uri_get_global(sia_uris->caRepository.uri));
- else
- pr_val_info("RRDP repository '%s' download/processing returned error previously, now I will try to fetch data from '%s'.",
- uri_get_global(sia_uris->rpkiNotify.uri),
- uri_get_global(sia_uris->caRepository.uri));
- } else {
- pr_val_info("Couldn't fetch data from repository '%s', trying to fetch data now from RRDP '%s'.",
- uri_get_global(sia_uris->caRepository.uri),
- uri_get_global(sia_uris->rpkiNotify.uri));
- }
-
- /* Retry if rrdp was the first option but failed */
- (*retry_repo_sync) = primary_rrdp;
- error = cb_secondary(sia_uris);
-
-verify_mft:
- /* Reach here on error or when both access methods were utilized */
- switch (error) {
- case 0:
- break;
- case EREQFAILED:
- /* Log that we'll try to work with a local copy */
- pr_val_warn("Trying to work with the local cache files.");
- (*retry_repo_sync) = false;
- break;
- case -EPERM:
- /*
- * Specific RRPD error: the URI error'd on the first try, so
- * we'll keep trying with the local files
- */
- (*retry_repo_sync) = false;
- break;
- default:
- return error;
- }
-
- /* Error and the primary access method was RRDP? Use its workspace */
- if (error && primary_rrdp) {
- upd_error = replace_rrdp_mft_uri(&sia_uris->mft);
- if (upd_error)
- return upd_error;
+ struct rpki_uri **node, *uri;
+ array_index index;
+
+ if (uris->rpp.len == 0)
+ return pr_val_err("SIA lacks both caRepository and rpkiNotify.");
+
+ ARRAYLIST_FOREACH(&uris->rpp, node, index) {
+ uri = *node;
+ switch (uri_get_type(uri)) {
+ case UT_RSYNC:
+ if (cache_download(uri, NULL) == 0)
+ return 0;
+ break;
+ case UT_HTTPS:
+ if (rrdp_update(uri) == 0)
+ return 0;
+ break;
+ default:
+ pr_crit("Unknown URI type: %u", uri_get_type(uri));
+ }
}
- /* Look for the manifest */
- return verify_mft_loc(sia_uris->mft.uri);
+ return pr_val_err("The RPP could not be downloaded.");
}
/** Boilerplate code for CA certificate validation and recursive traversal. */
int total_parents;
STACK_OF(X509_CRL) *rpp_parent_crl;
X509 *cert;
- struct sia_ca_uris sia_uris;
+ struct sia_uris sia_uris;
enum rpki_policy policy;
enum cert_type certype;
struct rpp *pp;
- bool repo_retry;
int error;
state = state_retrieve();
if (error)
goto revert_cert;
- sia_ca_uris_init(&sia_uris);
+ sia_uris_init(&sia_uris);
error = (certype == CERTYPE_TA)
? certificate_validate_extensions_ta(cert, &sia_uris, &policy)
- : certificate_validate_extensions_ca(cert, &sia_uris, &policy, rpp_parent);
+ : certificate_validate_extensions_ca(cert, &sia_uris, &policy,
+ rpp_parent);
if (error)
goto revert_uris;
- /*
- * RFC 6481 section 5: "when the repository publication point contents
- * are updated, a repository operator cannot assure RPs that the
- * manifest contents and the repository contents will be precisely
- * aligned at all times"
- *
- * Trying to avoid this issue, download the CA repository and validate
- * manifest (and its content) again.
- *
- * Avoid to re-download the repo if the mft was fetched with RRDP.
- */
- repo_retry = true;
- error = use_access_method(&sia_uris, exec_rsync_method,
- exec_rrdp_method, &repo_retry);
+ error = download_rpp(&sia_uris);
if (error)
goto revert_uris;
- do {
- /* Validate the manifest (@mft) pointed by the certificate */
- error = x509stack_push(validation_certstack(state), cert_uri,
- cert, policy, certype);
- if (error)
- goto revert_uris;
-
- cert = NULL; /* Ownership stolen */
-
- error = handle_manifest(sia_uris.mft.uri, &pp);
- if (error == 0 || !repo_retry)
- break;
-
- /*
- * Don't reach here if:
- * - Manifest is valid.
- * - Working with local files due to a download error.
- * - RRDP was utilized to fetch the manifest.
- * - There was a previous attempt to re-fetch the repository.
- */
- pr_val_info("Retrying repository download to discard 'transient inconsistency' manifest issue (see RFC 6481 section 5) '%s'",
- uri_val_get_printable(sia_uris.caRepository.uri));
- error = rsync_download_files(sia_uris.caRepository.uri, true);
- if (error)
- break;
-
- /* Cancel stack, reload certificate (no need to revalidate) */
- x509stack_cancel(validation_certstack(state));
- error = certificate_load(cert_uri, &cert);
- if (error)
- goto revert_uris;
-
- repo_retry = false;
- } while (true);
-
- if (error) {
- x509stack_cancel(validation_certstack(state));
+ error = handle_manifest(sia_uris.mft, &pp);
+ if (error)
goto revert_uris;
- }
/* -- Validate & traverse the RPP (@pp) described by the manifest -- */
rpp_traverse(pp);
-
rpp_refput(pp);
+
revert_uris:
- sia_ca_uris_cleanup(&sia_uris);
+ validation_set_notification_uri(state, NULL);
+ sia_uris_cleanup(&sia_uris);
revert_cert:
if (cert != NULL)
X509_free(cert);
#include "object/roa.h"
#include "object/signed_object.h"
+static int
+cage(struct rpki_uri **uri)
+{
+ if (validation_get_notification_uri(state_retrieve()) == NULL) {
+ /* No need to cage */
+ uri_refget(*uri);
+ return 0;
+ }
+
+ return __uri_create(uri, UT_CAGED, uri_get_global(*uri),
+ uri_get_global_len(*uri));
+}
+
static int
decode_manifest(struct signed_object *sobj, struct Manifest **result)
{
fah = mft->fileList.list.array[i];
error = uri_create_mft(&uri, mft_uri, &fah->file);
- if (error == ESKIP)
- continue;
/*
* Not handling ENOTRSYNC is fine because the manifest URL
* should have been RSYNC. Something went wrong if an RSYNC URL
int error;
/* Prepare */
+ error = cage(&uri); /* ref++ */
+ if (error)
+ return error;
pr_val_debug("Manifest '%s' {", uri_val_get_printable(uri));
fnstack_push_uri(uri);
revert_log:
pr_val_debug("}");
fnstack_pop();
+ uri_refput(uri); /* ref-- */
return error;
}
#include <syslog.h>
#include "alloc.h"
+#include "cert_stack.h"
#include "log.h"
#include "thread_var.h"
-#define _GNU_SOURCE
-
#include "tal.h"
#include <errno.h>
#include "config.h"
#include "line_file.h"
#include "log.h"
-#include "random.h"
#include "state.h"
#include "thread_var.h"
#include "validation_handler.h"
#include "crypto/base64.h"
-#include "http/http.h"
#include "object/certificate.h"
-#include "rsync/rsync.h"
#include "rtr/db/vrps.h"
-#include "rrdp/db/db_rrdp.h"
+#include "cache/local_cache.h"
#define TAL_FILE_EXTENSION ".tal"
typedef int (*foreach_uri_cb)(struct tal *, struct rpki_uri *, void *);
struct rpki_uri *new;
int error;
- error = uri_create_mixed_str(&new, uri, strlen(uri));
- if (error == ENOTSUPPORTED)
- return pr_op_err("TAL has non-RSYNC/HTTPS URI.");
+ if (str_starts_with(uri, "rsync://"))
+ error = uri_create(&new, UT_RSYNC, uri);
+ else if (str_starts_with(uri, "https://"))
+ error = uri_create(&new, UT_HTTPS, uri);
+ else
+ error = pr_op_err("TAL has non-RSYNC/HTTPS URI: %s", uri);
if (error)
return error;
return 0;
}
-static void
-tal_shuffle_uris(struct tal *tal)
-{
- struct rpki_uri **array = tal->uris.array;
- struct rpki_uri *tmp;
- unsigned int count = tal->uris.count;
- long random_index;
- unsigned int i;
-
- random_init();
-
- for (i = 0; i < count; i++) {
- tmp = array[i];
- random_index = random_at_most(count - 1 - i) + i;
- array[i] = array[random_index];
- array[random_index] = tmp;
- }
-}
-
-static void
-tal_order_uris(struct tal *tal)
-{
- struct rpki_uri **ordered;
- struct rpki_uri **tmp;
- bool http_first;
- unsigned int i;
- unsigned int last_rsync;
- unsigned int last_https;
-
- /* First do the shuffle */
- if (config_get_shuffle_tal_uris())
- tal_shuffle_uris(tal);
-
- if (config_get_rsync_priority() == config_get_http_priority())
- return;
-
- /* Now order according to the priority */
- http_first = (config_get_http_priority() > config_get_rsync_priority());
-
- ordered = pmalloc(tal->uris.size * sizeof(struct rpki_uri *));
-
- last_rsync = (http_first ? tal->uris.https_count : 0);
- last_https = (http_first ? 0 : tal->uris.rsync_count);
-
- for (i = 0; i < tal->uris.count; i++) {
- if (uri_is_rsync(tal->uris.array[i]))
- ordered[last_rsync++] = tal->uris.array[i];
- else
- ordered[last_https++] = tal->uris.array[i];
- }
-
- /* Everything is ok, point to the ordered array */
- tmp = tal->uris.array;
- tal->uris.array = ordered;
- free(tmp);
-}
-
char const *
tal_get_file_name(struct tal *tal)
{
return ENSURE_NEGATIVE(error);
if (thread->sync_files) {
- if (uri_is_rsync(uri)) {
- if (!config_get_rsync_enabled()) {
- validation_destroy(state);
- return 0; /* Try some other TAL URI */
- }
- error = rsync_download_files(uri, false);
- } else /* HTTPS */ {
- if (!config_get_http_enabled()) {
- validation_destroy(state);
- return 0; /* Try some other TAL URI */
- }
- error = http_download_file(uri);
- }
-
+ error = cache_download(uri, NULL);
/* Reminder: there's a positive error: EREQFAILED */
if (error) {
validation_destroy(state);
goto fail;
}
- /*
- * Set all RRDPs URIs to non-requested, this way we will force the
- * request on every cycle (to check if there are updates).
- */
- db_rrdp_uris_set_all_unvisited();
-
/* Handle root certificate. */
error = certificate_traverse(NULL, uri);
if (error) {
if (error)
goto end;
- tal_order_uris(tal);
-
error = foreach_uri(tal, handle_tal_uri, thread);
if (error > 0) {
error = 0;
struct tal_param *t_param = arg;
struct validation_thread *thread;
- db_rrdp_add_tal(tal_file);
-
thread = pmalloc(sizeof(struct validation_thread));
thread->tal_file = pstrdup(tal_file);
struct validation_thread *thread;
int error;
- /* Set existent tal RRDP info to non visited */
- db_rrdp_reset_visited_tals();
-
param.pool = pool;
param.db = table;
SLIST_INIT(¶m.threads);
thread_destroy(thread);
}
- /* One thread has errors, validation can't keep the resulting table */
- if (error)
- return error;
-
- /* Remove non-visited rrdps URIS by tal */
- db_rrdp_rem_nonvisited_tals();
-
- return 0;
+ /* If one thread has errors, we can't keep the resulting table. */
+ return error;
}
+++ /dev/null
-#include "random.h"
-#include <stdlib.h>
-#include <time.h>
-
-void
-random_init(void)
-{
- /*
- * time() has second precision, which is fine.
- * I don't think that anyone will legitimately need to run this program
- * more than once a second.
- */
- srandom(time(NULL));
-}
-
-/**
- * Assumes 0 <= max <= RAND_MAX
- * Returns in the closed interval [0, max]
- *
- * Source: https://stackoverflow.com/questions/2509679
- */
-long random_at_most(long max)
-{
- /* max <= RAND_MAX < ULONG_MAX, so this is okay. */
- unsigned long num_bins = (unsigned long) max + 1;
- unsigned long num_rand = (unsigned long) RAND_MAX + 1;
- unsigned long bin_size = num_rand / num_bins;
- unsigned long defect = num_rand % num_bins;
- long x;
-
- do {
- x = random();
- /* This is carefully written not to overflow */
- } while (num_rand - defect <= (unsigned long) x);
-
- /* Truncated division is intentional */
- return x / bin_size;
-}
+++ /dev/null
-#ifndef SRC_RANDOM_H_
-#define SRC_RANDOM_H_
-
-void random_init(void);
-long random_at_most(long max);
-
-#endif /* SRC_RANDOM_H_ */
#include <stdint.h> /* UINT32_MAX */
#include "alloc.h"
+#include "cert_stack.h"
#include "log.h"
#include "sorted_array.h"
#include "thread_var.h"
#ifndef SRC_RPP_H_
#define SRC_RPP_H_
+#include <openssl/x509.h>
#include "types/uri.h"
struct rpp;
+++ /dev/null
-#include "rrdp/db/db_rrdp.h"
-
-#include <sys/queue.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include "alloc.h"
-#include "crypto/hash.h"
-#include "common.h"
-#include "log.h"
-
-struct tal_elem {
- char *file_name;
- struct db_rrdp_uri *uris;
- bool visited;
- SLIST_ENTRY(tal_elem) next;
-};
-
-SLIST_HEAD(tal_list, tal_elem);
-
-struct db_rrdp {
- struct tal_list tals;
-};
-
-static struct db_rrdp db;
-
-/** Read/write lock, which protects @db. */
-static pthread_rwlock_t lock;
-
-/*
- * Creates an ID for the RRDP local workspace.
- *
- * The ID is generated using the hash (sha-1) of @base. The first 4 bytes of
- * the hash are "stringified" (8 chars) and a '/' is added at the end to
- * (later) facilitate the concatenation of the ID at --local-repository.
- *
- * The ID is allocated at @result.
- *
- * TODO (#78) Improve and use this.
- */
-static int
-get_workspace_path(char const *base, char **result)
-{
-/* SHA1 produces 20 bytes */
-#define HASH_LEN 20
-/* We'll use the first 4 bytes (8 chars) */
-#define OUT_LEN 8
- unsigned char *hash;
- unsigned int hash_len;
- unsigned int i;
- char *tmp;
- char *ptr;
- int error;
-
- hash = pmalloc(HASH_LEN * sizeof(unsigned char));
-
- hash_len = 0;
- error = hash_str("sha1", base, hash, &hash_len);
- if (error) {
- free(hash);
- return error;
- }
-
- /* Get the first bytes + one slash + NUL char */
- tmp = pmalloc(OUT_LEN + 2);
-
- ptr = tmp;
- for (i = 0; i < OUT_LEN / 2; i++) {
- sprintf(ptr, "%02X", hash[i]);
- ptr += 2;
- }
- tmp[OUT_LEN] = '/';
- tmp[OUT_LEN + 1] = '\0';
-
- free(hash);
- *result = tmp;
- return 0;
-}
-
-static struct tal_elem *
-tal_elem_create(char const *name)
-{
- struct tal_elem *result;
-
- result = pmalloc(sizeof(struct tal_elem));
-
- result->uris = db_rrdp_uris_create();
- result->visited = true;
- result->file_name = pstrdup(name);
-
- return result;
-}
-
-static void
-tal_elem_destroy(struct tal_elem *elem, bool remove_local)
-{
- db_rrdp_uris_destroy(elem->uris);
- free(elem->file_name);
- free(elem);
-}
-
-int
-db_rrdp_init(void)
-{
- int error;
-
- error = pthread_rwlock_init(&lock, NULL);
- if (error) {
- pr_op_err("DB RRDP pthread_rwlock_init() errored: %s",
- strerror(error));
- return error;
- }
-
- SLIST_INIT(&db.tals);
- return 0;
-}
-
-void
-db_rrdp_cleanup(void)
-{
- struct tal_elem *elem;
-
- while (!SLIST_EMPTY(&db.tals)) {
- elem = db.tals.slh_first;
- SLIST_REMOVE_HEAD(&db.tals, next);
- tal_elem_destroy(elem, false);
- }
- pthread_rwlock_destroy(&lock);
-}
-
-static struct tal_elem *
-db_rrdp_find_tal(char const *tal_name)
-{
- struct tal_elem *found;
-
- rwlock_read_lock(&lock);
- SLIST_FOREACH(found, &db.tals, next) {
- if (strcmp(tal_name, found->file_name) == 0) {
- rwlock_unlock(&lock);
- return found;
- }
- }
- rwlock_unlock(&lock);
-
- return NULL;
-}
-
-void
-db_rrdp_add_tal(char const *tal_name)
-{
- struct tal_elem *elem, *found;
-
- /* Element exists, no need to create it again */
- found = db_rrdp_find_tal(tal_name);
- if (found != NULL) {
- found->visited = true;
- return;
- }
-
- elem = tal_elem_create(tal_name);
-
- rwlock_write_lock(&lock);
- SLIST_INSERT_HEAD(&db.tals, elem, next);
- rwlock_unlock(&lock);
-}
-
-void
-db_rrdp_rem_tal(char const *tal_name)
-{
- struct tal_elem *found;
-
- found = db_rrdp_find_tal(tal_name);
- if (found == NULL)
- return;
-
- rwlock_write_lock(&lock);
- SLIST_REMOVE(&db.tals, found, tal_elem, next);
- rwlock_unlock(&lock);
-
- tal_elem_destroy(found, true);
-}
-
-/* Returns the reference to RRDP URIs of a TAL */
-struct db_rrdp_uri *
-db_rrdp_get_uris(char const *tal_name)
-{
- struct tal_elem *found;
-
- found = db_rrdp_find_tal(tal_name);
- if (found == NULL)
- pr_crit("db_rrdp_find_tal() returned NULL, means it hasn't been initialized");
-
- return found->uris;
-}
-
-/* Set all tals to non-visited */
-void
-db_rrdp_reset_visited_tals(void)
-{
- struct tal_elem *found;
-
- rwlock_write_lock(&lock);
- SLIST_FOREACH(found, &db.tals, next)
- found->visited = false;
- rwlock_unlock(&lock);
-}
-
-/* Remove non-visited tals */
-void
-db_rrdp_rem_nonvisited_tals(void)
-{
- struct tal_elem *found;
-
- rwlock_write_lock(&lock);
- SLIST_FOREACH(found, &db.tals, next) {
- if (!found->visited) {
- SLIST_REMOVE(&db.tals, found, tal_elem, next);
- tal_elem_destroy(found, true);
- }
- }
- rwlock_unlock(&lock);
-}
+++ /dev/null
-#ifndef SRC_RRDP_DB_DB_RRDP_H_
-#define SRC_RRDP_DB_DB_RRDP_H_
-
-#include "rrdp/db/db_rrdp_uris.h"
-
-int db_rrdp_init(void);
-void db_rrdp_cleanup(void);
-
-void db_rrdp_add_tal(char const *);
-void db_rrdp_rem_tal(char const *);
-
-struct db_rrdp_uri *db_rrdp_get_uris(char const *);
-
-void db_rrdp_reset_visited_tals(void);
-void db_rrdp_rem_nonvisited_tals(void);
-
-#endif /* SRC_RRDP_DB_DB_RRDP_H_ */
+++ /dev/null
-#include "rrdp/db/db_rrdp_uris.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include "data_structure/uthash.h"
-#include "alloc.h"
-#include "common.h"
-#include "log.h"
-#include "thread_var.h"
-
-struct uris_table {
- /* Key */
- char *uri;
- /* Last session ID and serial from the URI */
- struct global_data data;
- /* Last local update of the URI (after a successful processing) */
- long last_update;
- /* The URI has been requested (HTTPS) at this cycle? */
- rrdp_req_status_t request_status;
- /* MFT URIs loaded from the @uri */
- struct visited_uris *visited_uris;
- UT_hash_handle hh;
-};
-
-struct db_rrdp_uri {
- struct uris_table *table;
-};
-
-static struct uris_table *
-uris_table_create(char const *uri, char const *session_id,
- unsigned long serial, rrdp_req_status_t req_status)
-{
- struct uris_table *tmp;
-
- tmp = pzalloc(sizeof(struct uris_table)); /* Zero needed by uthash */
-
- tmp->uri = pstrdup(uri);
- tmp->data.session_id = pstrdup(session_id);
- tmp->data.serial = serial;
- tmp->last_update = 0;
- tmp->request_status = req_status;
- tmp->visited_uris = NULL;
-
- return tmp;
-}
-
-static void
-uris_table_destroy(struct uris_table *uri)
-{
- visited_uris_refput(uri->visited_uris);
- free(uri->data.session_id);
- free(uri->uri);
- free(uri);
-}
-
-static struct uris_table *
-find_rrdp_uri(struct db_rrdp_uri *uris, const char *search)
-{
- struct uris_table *found;
- HASH_FIND_STR(uris->table, search, found);
- return found;
-}
-
-static void
-add_rrdp_uri(struct db_rrdp_uri *uris, struct uris_table *new_uri)
-{
- struct uris_table *old_uri;
-
- HASH_REPLACE_STR(uris->table, uri, new_uri, old_uri);
- if (old_uri != NULL)
- uris_table_destroy(old_uri);
-}
-
-static struct db_rrdp_uri *
-get_thread_rrdp_uris(void)
-{
- return validation_get_rrdp_uris(state_retrieve());
-}
-
-struct db_rrdp_uri *
-db_rrdp_uris_create(void)
-{
- struct db_rrdp_uri *tmp;
-
- tmp = pmalloc(sizeof(struct db_rrdp_uri));
- tmp->table = NULL;
-
- return tmp;
-}
-
-void
-db_rrdp_uris_destroy(struct db_rrdp_uri *uris)
-{
- struct uris_table *uri_node, *uri_tmp;
-
- HASH_ITER(hh, uris->table, uri_node, uri_tmp) {
- HASH_DEL(uris->table, uri_node);
- uris_table_destroy(uri_node);
- }
- free(uris);
-}
-
-rrdp_uri_cmp_result_t
-db_rrdp_uris_cmp(char const *uri, char const *session_id, unsigned long serial)
-{
- struct uris_table *found;
-
- found = find_rrdp_uri(get_thread_rrdp_uris(), uri);
- if (found == NULL) {
- pr_val_debug("I don't have state for this Update Notification; downloading snapshot...");
- return RRDP_URI_NOTFOUND;
- }
-
- if (strcmp(session_id, found->data.session_id) != 0) {
- pr_val_debug("session_id changed from '%s' to '%s'.",
- found->data.session_id, session_id);
- return RRDP_URI_DIFF_SESSION;
- }
-
- if (serial != found->data.serial) {
- pr_val_debug("The serial changed from %lu to %lu.",
- found->data.serial, serial);
- return RRDP_URI_DIFF_SERIAL;
- }
-
- pr_val_debug("The new Update Notification has the same session_id (%s) and serial (%lu) as the old one.",
- session_id, serial);
- return RRDP_URI_EQUAL;
-}
-
-void
-db_rrdp_uris_update(char const *uri, char const *session_id,
- unsigned long serial, rrdp_req_status_t req_status,
- struct visited_uris *visited_uris)
-{
- struct uris_table *db_uri;
-
- db_uri = uris_table_create(uri, session_id, serial, req_status);
- db_uri->visited_uris = visited_uris; /* Ownership transfered */
- add_rrdp_uri(get_thread_rrdp_uris(), db_uri);
-}
-
-int
-db_rrdp_uris_get_serial(char const *uri, unsigned long *serial)
-{
- struct uris_table *found;
-
- found = find_rrdp_uri(get_thread_rrdp_uris(), uri);
- if (found == NULL)
- return -ENOENT;
-
- *serial = found->data.serial;
- return 0;
-}
-
-int
-db_rrdp_uris_get_last_update(char const *uri, long *date)
-{
- struct uris_table *found;
-
- found = find_rrdp_uri(get_thread_rrdp_uris(), uri);
- if (found == NULL)
- return -ENOENT;
-
- *date = found->last_update;
- return 0;
-}
-
-/* Set the last update to now */
-int
-db_rrdp_uris_set_last_update(char const *uri)
-{
- struct uris_table *found;
- time_t now;
- int error;
-
- found = find_rrdp_uri(get_thread_rrdp_uris(), uri);
- if (found == NULL)
- return -ENOENT;
-
- now = 0;
- error = get_current_time(&now);
- if (error)
- return error;
-
- found->last_update = (long)now;
- return 0;
-}
-
-int
-db_rrdp_uris_get_request_status(char const *uri, rrdp_req_status_t *result)
-{
- struct uris_table *found;
-
- found = find_rrdp_uri(get_thread_rrdp_uris(), uri);
- if (found == NULL)
- return -ENOENT;
-
- *result = found->request_status;
- return 0;
-}
-
-int
-db_rrdp_uris_set_request_status(char const *uri, rrdp_req_status_t value)
-{
- struct uris_table *found;
-
- found = find_rrdp_uri(get_thread_rrdp_uris(), uri);
- if (found == NULL)
- return -ENOENT;
-
- found->request_status = value;
- return 0;
-}
-
-void
-db_rrdp_uris_set_all_unvisited(void)
-{
- struct db_rrdp_uri *uris;
- struct uris_table *uri_node, *uri_tmp;
-
- uris = get_thread_rrdp_uris();
- HASH_ITER(hh, uris->table, uri_node, uri_tmp)
- uri_node->request_status = RRDP_URI_REQ_UNVISITED;
-}
-
-/*
- * Returns a pointer to the visited_uris of the current thread.
- */
-struct visited_uris *
-db_rrdp_uris_get_visited_uris(char const *uri)
-{
- struct uris_table *found;
-
- found = find_rrdp_uri(get_thread_rrdp_uris(), uri);
- if (found == NULL)
- return NULL;
-
- return found->visited_uris;
-}
+++ /dev/null
-#ifndef SRC_RRDP_DB_DB_RRDP_URIS_H_
-#define SRC_RRDP_DB_DB_RRDP_URIS_H_
-
-#include <stdbool.h>
-#include "rrdp/rrdp_objects.h"
-#include "visited_uris.h"
-
-typedef enum {
- RRDP_URI_REQ_ERROR,
- RRDP_URI_REQ_UNVISITED,
- RRDP_URI_REQ_VISITED,
-} rrdp_req_status_t;
-
-/*
- * RRDP URI fetched from 'rpkiNotify' OID at a CA certificate, each TAL thread
- * may have a reference to one of these (it holds information such as update
- * notification URI, session ID, serial, visited mft uris).
- */
-struct db_rrdp_uri;
-
-struct db_rrdp_uri *db_rrdp_uris_create(void);
-void db_rrdp_uris_destroy(struct db_rrdp_uri *);
-
-rrdp_uri_cmp_result_t db_rrdp_uris_cmp(char const *, char const *,
- unsigned long);
-void db_rrdp_uris_update(char const *, char const *session_id, unsigned long,
- rrdp_req_status_t, struct visited_uris *);
-int db_rrdp_uris_get_serial(char const *, unsigned long *);
-
-int db_rrdp_uris_get_last_update(char const *, long *);
-int db_rrdp_uris_set_last_update(char const *);
-
-int db_rrdp_uris_get_request_status(char const *, rrdp_req_status_t *);
-int db_rrdp_uris_set_request_status(char const *, rrdp_req_status_t);
-void db_rrdp_uris_set_all_unvisited(void);
-
-struct visited_uris *db_rrdp_uris_get_visited_uris(char const *);
-
-char const *db_rrdp_uris_workspace_get(void);
-
-#endif /* SRC_RRDP_DB_DB_RRDP_URIS_H_ */
#include "rrdp_loader.h"
-#include "rrdp/db/db_rrdp_uris.h"
-#include "rrdp/rrdp_objects.h"
-#include "rrdp/rrdp_parser.h"
-#include "rsync/rsync.h"
-#include "common.h"
-#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
#include "log.h"
#include "thread_var.h"
-#include "visited_uris.h"
-
-/* Fetch and process the deltas from the @notification */
-static int
-process_diff_serial(struct update_notification *notification,
- struct visited_uris **visited)
-{
- unsigned long serial;
- int error;
-
- error = db_rrdp_uris_get_serial(notification->uri, &serial);
- if (error)
- return error;
-
- /* Work with the existent visited uris */
- *visited = db_rrdp_uris_get_visited_uris(notification->uri);
- if ((*visited) == NULL)
- return -ENOENT;
-
- return rrdp_process_deltas(notification, serial, *visited);
-}
+#include "rrdp/rrdp_objects.h"
+#include "rrdp/rrdp_parser.h"
+#include "cache/local_cache.h"
-/* Fetch and process the snapshot from the @notification */
static int
-process_snapshot(struct update_notification *notification,
- struct visited_uris **visited)
+get_metadata(struct rpki_uri *uri, struct notification_metadata *result)
{
- struct visited_uris *tmp;
+ struct stat st;
+ struct update_notification notification;
int error;
- /* Use a new allocated visited_uris struct */
- tmp = visited_uris_create();
+ result->session_id = NULL;
+ result->serial = 0;
- error = rrdp_parse_snapshot(notification, tmp);
- if (error) {
- visited_uris_refput(tmp);
- return error;
+ if (stat(uri_get_local(uri), &st) != 0) {
+ error = errno;
+ return (error == ENOENT) ? 0 : error;
}
- *visited = tmp;
- return 0;
-}
-
-static int
-remove_rrdp_uri_files(char const *notification_uri)
-{
- struct visited_uris *tmp;
-
- /* Work with the existent visited uris */
- tmp = db_rrdp_uris_get_visited_uris(notification_uri);
- if (tmp == NULL)
- return -ENOENT;
-
- return visited_uris_delete_local(tmp);
-}
-
-/* Mark the URI as errored with dummy data, so it won't be requested again */
-static void
-mark_rrdp_uri_request_err(char const *notification_uri)
-{
- pr_val_debug("RRDP data of '%s' won't be requested again during this cycle due to previous error.",
- notification_uri);
- db_rrdp_uris_update(notification_uri, "", 0, RRDP_URI_REQ_ERROR,
- visited_uris_create());
-}
-
-static int
-process_diff_session(struct update_notification *notification,
- struct visited_uris **visited)
-{
- int error;
-
- error = remove_rrdp_uri_files(notification->uri);
+ /*
+ * TODO (fine) optimize by not reading everything,
+ * or maybe keep it if it doesn't change.
+ */
+ error = rrdp_parse_notification(uri, ¬ification);
if (error)
return error;
- return process_snapshot(notification, visited);
+ *result = notification.meta;
+
+ update_notification_destroy(¬ification);
+ return 0;
}
/*
* Calling code can then access the files, just as if they had been downloaded
* via rsync.
*/
-static int
-__rrdp_load(struct rpki_uri *uri, bool force_snapshot, bool *data_updated)
+int
+rrdp_update(struct rpki_uri *uri)
{
- struct update_notification *upd_notification;
- struct visited_uris *visited;
- rrdp_req_status_t requested;
- rrdp_uri_cmp_result_t res;
+ struct notification_metadata old;
+ struct update_notification new;
+ bool changed;
int error;
- (*data_updated) = false;
+ if (uri == NULL || !uri_is_https(uri))
+ pr_crit("Wrong call, trying to parse a non HTTPS URI");
-#ifndef DEBUG_RRDP
- /*
- * In normal mode (DEBUG_RRDP disabled), RRDP files (notifications,
- * snapshots and deltas) are not cached.
- * I think it was implemented this way to prevent the cache from growing
- * indefinitely. (Because otherwise Fort would lose track of RRDP files
- * from disappearing CAs. RRDP files are designed to be relevant on
- * single validation runs anyway.)
- * Note that __rrdp_load() includes the RRDP file explosion. Exploded
- * files (manifests, certificates, ROAs and ghostbusters) are cached as
- * usual.
- *
- * Therefore, in normal offline mode, the entirety of __rrdp_load()
- * needs to be skipped because it would otherwise error out while
- * attempting to access the nonexistent RRDP files.
- *
- * But if you need to debug RRDP files specifically, their persistent
- * deletions will force you to debug them in online mode.
- *
- * That's why DEBUG_RRDP exists. When it's enabled, RRDP files will not
- * be deleted, and config_get_http_enabled() will kick off during
- * __http_download_file(). This will allow you to reach the RRDP file
- * parsing code in offline mode.
- *
- * I know this is somewhat convoluted, but I haven't found a more
- * elegant way to do it.
- *
- * Simple enable example: `make FORT_FLAGS=-DDEBUG_RRDP`
- */
- if (!config_get_http_enabled()) {
- (*data_updated) = true;
- return 0;
- }
-#endif
+ fnstack_push_uri(uri);
+ pr_val_debug("Processing notification.");
- /* Avoid multiple requests on the same run */
- requested = RRDP_URI_REQ_UNVISITED;
- error = db_rrdp_uris_get_request_status(uri_get_global(uri),
- &requested);
- if (error && error != -ENOENT)
- return error;
+ error = get_metadata(uri, &old);
+ if (error)
+ goto end;
+ pr_val_debug("Old session/serial: %s/%lu", old.session_id, old.serial);
- if (!force_snapshot) {
- switch(requested) {
- case RRDP_URI_REQ_VISITED:
- (*data_updated) = true;
- return 0;
- case RRDP_URI_REQ_UNVISITED:
- break;
- case RRDP_URI_REQ_ERROR:
- /* Log has been done before this call */
- return -EPERM;
- }
- } else {
- if (requested != RRDP_URI_REQ_VISITED) {
- pr_val_info("Skipping RRDP snapshot reload");
- return -EINVAL;
- }
+ error = cache_download(uri, &changed);
+ if (error)
+ goto end;
+ if (!changed) {
+ pr_val_debug("The Notification has not changed.");
+ goto end;
}
- pr_val_debug("Downloading RRDP Update Notification...");
- error = rrdp_parse_notification(uri, force_snapshot, &upd_notification);
+ error = rrdp_parse_notification(uri, &new);
if (error)
- goto upd_end;
-
- if (upd_notification == NULL) {
- pr_val_debug("The Update Notification has not changed.");
- return 0;
+ goto end; /* FIXME fall back to previous? */
+ pr_val_debug("New session/serial: %s/%lu", new.meta.session_id,
+ new.meta.serial);
+
+ if (old.session_id == NULL) {
+ pr_val_debug("This is a new Notification.");
+ error = rrdp_parse_snapshot(&new);
+ goto revert_notification;
}
- pr_val_debug("The Update Notification changed.");
+ if (strcmp(old.session_id, new.meta.session_id) != 0) {
+ pr_val_debug("The Notification's session ID changed.");
+ error = rrdp_parse_snapshot(&new);
+ goto revert_notification;
+ }
- do {
- /* Same flow as a session update */
- if (force_snapshot) {
- error = process_diff_session(upd_notification,
- &visited);
- if (error)
- goto upd_destroy;
- (*data_updated) = true;
- break;
- }
+ if (old.serial != new.meta.serial) {
+ pr_val_debug("The Notification' serial changed.");
+ error = rrdp_process_deltas(&new, old.serial);
+ goto revert_notification;
+ }
- res = db_rrdp_uris_cmp(uri_get_global(uri),
- upd_notification->global_data.session_id,
- upd_notification->global_data.serial);
+ pr_val_debug("The Notification changed, but the session ID and serial didn't.");
- switch (res) {
- case RRDP_URI_EQUAL:
- goto set_update;
- case RRDP_URI_DIFF_SESSION:
- /* Delete the old session files */
- error = process_diff_session(upd_notification,
- &visited);
- if (error)
- goto upd_destroy;
- (*data_updated) = true;
- break;
- case RRDP_URI_DIFF_SERIAL:
- error = process_diff_serial(upd_notification, &visited);
- if (!error) {
- visited_uris_refget(visited);
- (*data_updated) = true;
- break;
- }
- /* Something went wrong, use snapshot */
- pr_val_info("There was an error processing RRDP deltas, using the snapshot instead.");
- case RRDP_URI_NOTFOUND:
- error = process_snapshot(upd_notification, &visited);
- if (error)
- goto upd_destroy;
- (*data_updated) = true;
- break;
- default:
- pr_crit("Unexpected RRDP URI comparison result");
- }
- } while (0);
+revert_notification:
+ update_notification_destroy(&new);
- /* Any update, and no error during the process, update db as well */
- pr_val_debug("Updating local RRDP data of '%s' to:", uri_get_global(uri));
- pr_val_debug("- Session ID: %s", upd_notification->global_data.session_id);
- pr_val_debug("- Serial: %lu", upd_notification->global_data.serial);
- db_rrdp_uris_update(uri_get_global(uri),
- upd_notification->global_data.session_id,
- upd_notification->global_data.serial,
- RRDP_URI_REQ_VISITED,
- visited);
+end: notification_metadata_cleanup(&old);
-set_update:
- /* Set the last update to now */
- pr_val_debug("Set last update of RRDP data of '%s' to now.",
- uri_get_global(uri));
- db_rrdp_uris_set_last_update(uri_get_global(uri));
-upd_destroy:
- if (upd_notification != NULL)
- update_notification_destroy(upd_notification);
-upd_end:
- /* Just return on success */
+ /* TODO (fine) hideous function side effect; find a better way. */
if (!error)
- return 0;
-
- /* Request failed, store the repository URI */
- if (error != EREQFAILED) {
- /* Reset RSYNC visited URIs, this may force the update */
- /* TODO um, what? */
- reset_downloaded();
- }
-
- mark_rrdp_uri_request_err(uri_get_global(uri));
+ validation_set_notification_uri(state_retrieve(), uri);
+ fnstack_pop();
return error;
}
-
-/*
- * Try to get RRDP Update Notification file and process it accordingly.
- *
- * If there's an error that could lead to an inconsistent local repository
- * state, marks the @uri as error'd so that it won't be requested again during
- * the same validation cycle.
- *
- * If there are no errors, updates the local DB and marks the @uri as visited.
- *
- * If the @uri is being visited again, verify its previous visit state. If there
- * were no errors, just return success; otherwise, return error code -EPERM.
- *
- * @data_updated will be true if:
- * - Delta files were processed,
- * - Snapshot file was processed,
- * - or @uri was already visited at this cycle
- */
-int
-rrdp_load(struct rpki_uri *uri, bool *data_updated)
-{
- return __rrdp_load(uri, false, data_updated);
-}
-
-/*
- * Force the processing of the snapshot. The update notification is requested
- * again, omitting the 'If-Modified-Since' header at the HTTP request.
- *
- * Shouldn't be called if @uri had a previous error or hasn't been requested,
- * still the check is done.
- */
-int
-rrdp_reload_snapshot(struct rpki_uri *uri)
-{
- bool tmp;
-
- tmp = false;
- return __rrdp_load(uri, true, &tmp);
-}
#ifndef SRC_RRDP_RRDP_LOADER_H_
#define SRC_RRDP_RRDP_LOADER_H_
-#include <stdbool.h>
#include "types/uri.h"
-int rrdp_load(struct rpki_uri *, bool *);
-int rrdp_reload_snapshot(struct rpki_uri *);
+int rrdp_update(struct rpki_uri *);
#endif /* SRC_RRDP_RRDP_LOADER_H_ */
#include <string.h>
#include "alloc.h"
#include "log.h"
+#include "types/uri.h"
DEFINE_ARRAY_LIST_FUNCTIONS(deltas_head, struct delta_head, )
void
-global_data_init(struct global_data *data)
+notification_metadata_init(struct notification_metadata *meta)
{
- data->session_id = NULL;
+ meta->session_id = NULL;
}
void
-global_data_cleanup(struct global_data *data)
+notification_metadata_cleanup(struct notification_metadata *meta)
{
- free(data->session_id);
+ free(meta->session_id);
}
void
-doc_data_init(struct doc_data *data)
+metadata_init(struct file_metadata *meta)
{
- data->uri = NULL;
- data->hash = NULL;
- data->hash_len = 0;
+ meta->uri = NULL;
+ meta->hash = NULL;
+ meta->hash_len = 0;
}
void
-doc_data_cleanup(struct doc_data *data)
+metadata_cleanup(struct file_metadata *meta)
{
- free(data->hash);
- free(data->uri);
+ free(meta->hash);
+ free(meta->uri);
}
/* Do the @cb to the delta head elements from @from_serial to @max_serial */
return 0;
}
-struct update_notification *
-update_notification_create(char const *uri)
+void
+update_notification_init(struct update_notification *notification,
+ struct rpki_uri *uri)
{
- struct update_notification *result;
-
- result = pmalloc(sizeof(struct update_notification));
-
- global_data_init(&result->global_data);
- doc_data_init(&result->snapshot);
- deltas_head_init(&result->deltas_list);
- result->uri = pstrdup(uri);
-
- return result;
+ notification_metadata_init(¬ification->meta);
+ metadata_init(¬ification->snapshot);
+ deltas_head_init(¬ification->deltas_list);
+ notification->uri = uri_refget(uri);
}
static void
delta_head_destroy(struct delta_head *delta)
{
- doc_data_cleanup(&delta->doc_data);
+ metadata_cleanup(&delta->meta);
}
void
update_notification_destroy(struct update_notification *file)
{
- doc_data_cleanup(&file->snapshot);
- global_data_cleanup(&file->global_data);
+ metadata_cleanup(&file->snapshot);
+ notification_metadata_cleanup(&file->meta);
deltas_head_cleanup(&file->deltas_list, delta_head_destroy);
- free(file->uri);
- free(file);
+ uri_refput(file->uri);
}
struct snapshot *
struct snapshot *tmp;
tmp = pmalloc(sizeof(struct snapshot));
- global_data_init(&tmp->global_data);
+ notification_metadata_init(&tmp->meta);
return tmp;
}
void
snapshot_destroy(struct snapshot *file)
{
- global_data_cleanup(&file->global_data);
+ notification_metadata_cleanup(&file->meta);
free(file);
}
struct delta *tmp;
tmp = pmalloc(sizeof(struct delta));
- global_data_init(&tmp->global_data);
+ notification_metadata_init(&tmp->meta);
return tmp;
}
void
delta_destroy(struct delta *file)
{
- global_data_cleanup(&file->global_data);
+ notification_metadata_cleanup(&file->meta);
free(file);
}
struct publish *tmp;
tmp = pmalloc(sizeof(struct publish));
- doc_data_init(&tmp->doc_data);
+ metadata_init(&tmp->meta);
tmp->content = NULL;
tmp->content_len = 0;
void
publish_destroy(struct publish *file)
{
- doc_data_cleanup(&file->doc_data);
+ metadata_cleanup(&file->meta);
free(file->content);
free(file);
}
struct withdraw *tmp;
tmp = pmalloc(sizeof(struct withdraw));
- doc_data_init(&tmp->doc_data);
+ metadata_init(&tmp->meta);
return tmp;
}
void
withdraw_destroy(struct withdraw *file)
{
- doc_data_cleanup(&file->doc_data);
+ metadata_cleanup(&file->meta);
free(file);
}
#include <stdbool.h>
#include "data_structure/array_list.h"
-/* Possible results for an RRDP URI comparison */
-typedef enum {
- /* The URI exists and has the same session ID and serial */
- RRDP_URI_EQUAL,
-
- /* The URI exists but has distinct serial */
- RRDP_URI_DIFF_SERIAL,
-
- /* The URI exists but has distinct session ID */
- RRDP_URI_DIFF_SESSION,
-
- /* The URI doesn't exists */
- RRDP_URI_NOTFOUND,
-} rrdp_uri_cmp_result_t;
-
/* Global RRDP files data */
-struct global_data {
+struct notification_metadata {
char *session_id;
unsigned long serial;
};
/* Specific RRDP files data, in some cases the hash can be omitted */
-struct doc_data {
+struct file_metadata {
char *uri;
unsigned char *hash;
size_t hash_len;
/* Represents a <publish> element */
struct publish {
- struct doc_data doc_data;
+ struct file_metadata meta;
unsigned char *content;
size_t content_len;
};
/* Represents a <withdraw> element */
struct withdraw {
- struct doc_data doc_data;
+ struct file_metadata meta;
};
/*
* Publish/withdraw list aren't remember, they are processed ASAP.
*/
struct delta {
- struct global_data global_data;
+ struct notification_metadata meta;
};
/*
* Publish list isn't remember, is processed ASAP.
*/
struct snapshot {
- struct global_data global_data;
+ struct notification_metadata meta;
};
/* Delta element located at an update notification file */
* so we should probably handle it as a string.
*/
unsigned long serial;
- struct doc_data doc_data;
+ struct file_metadata meta;
};
/* List of deltas inside an update notification file */
/* Update notification file content and location URI */
struct update_notification {
- struct global_data global_data;
- struct doc_data snapshot;
+ struct notification_metadata meta;
+ struct file_metadata snapshot;
struct deltas_head deltas_list;
- char *uri;
+ struct rpki_uri *uri;
};
-void global_data_init(struct global_data *);
-void global_data_cleanup(struct global_data *);
+void notification_metadata_init(struct notification_metadata *);
+void notification_metadata_cleanup(struct notification_metadata *);
-void doc_data_init(struct doc_data *);
-void doc_data_cleanup(struct doc_data *);
+void metadata_init(struct file_metadata *);
+void metadata_cleanup(struct file_metadata *);
-struct update_notification *update_notification_create(char const *);
+void update_notification_init(struct update_notification *, struct rpki_uri *);
void update_notification_destroy(struct update_notification *);
typedef int (*delta_head_cb)(struct delta_head *, void *);
#include <string.h>
#include <unistd.h>
-#include "rrdp/db/db_rrdp_uris.h"
#include "crypto/base64.h"
#include "crypto/hash.h"
-#include "http/http.h"
#include "xml/relax_ng.h"
#include "alloc.h"
#include "common.h"
#include "file.h"
#include "log.h"
#include "thread_var.h"
+#include "cache/local_cache.h"
/* XML Common Namespace of files */
#define RRDP_NAMESPACE "http://www.ripe.net/rpki/rrdp"
/* Data being parsed */
struct snapshot *snapshot;
/* Parent data to validate session ID and serial */
- struct update_notification *parent;
- /* Visited URIs related to this thread */
- struct visited_uris *visited_uris;
+ struct update_notification *notification;
};
/* Context while reading a delta */
/* Data being parsed */
struct delta *delta;
/* Parent data to validate session ID */
- struct update_notification *parent;
+ struct update_notification *notification;
/* Current serial loaded from update notification deltas list */
unsigned long expected_serial;
- /* Visited URIs related to this thread */
- struct visited_uris *visited_uris;
};
-/* Args to send on update (snapshot/delta) files parsing */
-struct proc_upd_args {
- struct update_notification *parent;
- struct visited_uris *visited_uris;
-};
-
-static void
-add_mft_to_list(struct visited_uris *visited_uris, char const *uri)
-{
- if (strcmp(".mft", strrchr(uri, '.')) == 0)
- visited_uris_add(visited_uris, uri);
-}
-
-static int
-rem_mft_from_list(struct visited_uris *visited_uris, char const *uri)
-{
- if (strcmp(".mft", strrchr(uri, '.')) != 0)
- return 0;
-
- return visited_uris_remove(visited_uris, uri);
-}
-
-static int
-download_file(struct rpki_uri *uri, long last_update)
-{
- int error;
-
- if (last_update > 0)
- error = http_download_file_with_ims(uri, last_update);
- else
- error = http_download_file(uri);
-
- /*
- * Since distinct files can be downloaded (notification, snapshot,
- * delta) just return the error and let the caller to add only the
- * update notification URI to the request errors DB.
- */
- if (error == -EREQFAILED)
- return EREQFAILED;
-
- /* Remember: positive values are expected */
- return error;
-}
+typedef enum {
+ HR_MANDATORY,
+ HR_OPTIONAL,
+ HR_IGNORE,
+} hash_requirement;
/* Left trim @from, setting the result at @result pointer */
static int
}
static int
-parse_hex_string(xmlTextReaderPtr reader, bool required, char const *attr,
+parse_hex_string(xmlTextReaderPtr reader, hash_requirement hr, char const *attr,
unsigned char **result, size_t *result_len)
{
xmlChar *xml_value;
xml_value = xmlTextReaderGetAttribute(reader, BAD_CAST attr);
if (xml_value == NULL)
- return required ?
- pr_val_err("RRDP file: Couldn't find xml attribute '%s'", attr)
+ return (hr == HR_MANDATORY)
+ ? pr_val_err("RRDP file: Couldn't find xml attribute '%s'", attr)
: 0;
/* The rest of the checks are done at the schema */
/* @gdata elements are allocated */
static int
-parse_global_data(xmlTextReaderPtr reader, struct global_data *gdata,
+parse_metadata(xmlTextReaderPtr reader, struct notification_metadata *meta,
char const *expected_session, unsigned long expected_serial)
{
char *session_id;
}
return_val:
- gdata->session_id = session_id;
- gdata->serial = serial;
+ meta->session_id = session_id;
+ meta->serial = serial;
return error;
}
-/* @data elements are allocated */
+/*
+ * Extracts the following two attributes from @reader's current tag:
+ *
+ * 1. "uri"
+ * 2. "hash" (optional, depending on @hr)
+ */
static int
-parse_doc_data(xmlTextReaderPtr reader, bool parse_hash, bool hash_req,
- struct doc_data *data)
+parse_doc_data(xmlTextReaderPtr reader, hash_requirement hr,
+ struct file_metadata *data)
{
char *uri;
unsigned char *hash;
if (error)
return error;
- if (!parse_hash)
+ if (hr == HR_IGNORE)
goto end;
- error = parse_hex_string(reader, hash_req, RRDP_ATTR_HASH, &hash,
- &hash_len);
+ error = parse_hex_string(reader, hr, RRDP_ATTR_HASH, &hash, &hash_len);
if (error) {
free(uri);
return error;
}
end:
- /* Function called just to do the validation */
- if (data == NULL) {
- free(hash);
- free(uri);
- return 0;
- }
data->uri = uri;
data->hash = hash;
data->hash_len = hash_len;
}
static int
-parse_publish(xmlTextReaderPtr reader, bool parse_hash, bool hash_required,
+parse_publish(xmlTextReaderPtr reader, hash_requirement hr,
struct publish **publish)
{
- struct publish *tmp;
+ struct publish *result;
struct rpki_uri *uri;
char *base64_str;
int error;
- tmp = publish_create();
+ result = publish_create();
- error = parse_doc_data(reader, parse_hash, hash_required,
- &tmp->doc_data);
+ error = parse_doc_data(reader, hr, &result->meta);
if (error)
goto release_tmp;
/* Read the text */
if (xmlTextReaderRead(reader) != 1) {
error = pr_val_err("Couldn't read publish content of element '%s'",
- tmp->doc_data.uri);
+ result->meta.uri);
goto release_tmp;
}
if (error)
goto release_tmp;
- error = base64_read(base64_str, &tmp->content, &tmp->content_len);
+ error = base64_read(base64_str, &result->content, &result->content_len);
if (error)
goto release_base64;
/* rfc8181#section-2.2 but considering optional hash */
uri = NULL;
- if (tmp->doc_data.hash_len > 0) {
+ if (result->meta.hash_len > 0) {
/* Get the current file from the uri */
- error = uri_create_rsync_str_rrdp(&uri, tmp->doc_data.uri,
- strlen(tmp->doc_data.uri));
+ error = uri_create(&uri, UT_CAGED, result->meta.uri);
if (error)
goto release_base64;
- error = hash_validate_file(uri, tmp->doc_data.hash,
- tmp->doc_data.hash_len);
+ error = hash_validate_file(uri, result->meta.hash,
+ result->meta.hash_len);
uri_refput(uri);
if (error != 0) {
pr_val_info("Hash of base64 decoded element from URI '%s' doesn't match <publish> element hash",
- tmp->doc_data.uri);
+ result->meta.uri);
error = EINVAL;
goto release_base64;
}
}
free(base64_str);
- *publish = tmp;
+ *publish = result;
return 0;
release_base64:
free(base64_str);
release_tmp:
- publish_destroy(tmp);
+ publish_destroy(result);
return error;
}
tmp = withdraw_create();
- error = parse_doc_data(reader, true, true, &tmp->doc_data);
+ error = parse_doc_data(reader, HR_MANDATORY, &tmp->meta);
if (error)
goto release_tmp;
/* rfc8181#section-2.2, get the file from the uri */
- error = uri_create_rsync_str_rrdp(&uri, tmp->doc_data.uri,
- strlen(tmp->doc_data.uri));
+ error = uri_create(&uri, UT_CAGED, tmp->meta.uri);
if (error)
goto release_tmp;
- error = hash_validate_file(uri, tmp->doc_data.hash,
- tmp->doc_data.hash_len);
+ error = hash_validate_file(uri, tmp->meta.hash,
+ tmp->meta.hash_len);
if (error)
goto release_uri;
}
static int
-write_from_uri(char const *location, unsigned char *content, size_t content_len,
- struct visited_uris *visited_uris)
+write_from_uri(char const *location, unsigned char *content, size_t content_len)
{
struct rpki_uri *uri;
FILE *out;
int error;
/* rfc8181#section-2.2 must be an rsync URI */
- error = uri_create_rsync_str_rrdp(&uri, location, strlen(location));
+ error = uri_create(&uri, UT_CAGED, location);
if (error)
return error;
uri_get_local(uri));
}
- add_mft_to_list(visited_uris, uri_get_global(uri));
-
uri_refput(uri);
file_close(out);
return 0;
/* Remove a local file and its directory tree (if empty) */
static int
-delete_from_uri(struct rpki_uri *uri, struct visited_uris *visited_uris)
+delete_from_uri(struct rpki_uri *uri)
{
- int error;
-
- if (visited_uris) {
- error = rem_mft_from_list(visited_uris, uri_get_global(uri));
- if (error)
- return error;
- }
-
/* Delete parent dirs only if empty. */
return delete_dir_recursive_bottom_up(uri_get_local(uri));
}
static int
-__delete_from_uri(char const *location, struct visited_uris *visited_uris)
+__delete_from_uri(char const *location)
{
struct rpki_uri *uri;
int error;
/* rfc8181#section-2.2 must be an rsync URI */
- error = uri_create_rsync_str_rrdp(&uri, location, strlen(location));
+ error = uri_create(&uri, UT_CAGED, location);
if (error)
return error;
- error = delete_from_uri(uri, visited_uris);
+ error = delete_from_uri(uri);
/* Error 0 is ok */
uri_refput(uri);
* other type at the caller.
*/
static int
-parse_publish_elem(xmlTextReaderPtr reader, bool parse_hash, bool hash_required,
- struct visited_uris *visited_uris)
+parse_publish_elem(xmlTextReaderPtr reader, hash_requirement hr)
{
struct publish *tmp;
int error;
tmp = NULL;
- error = parse_publish(reader, parse_hash, hash_required, &tmp);
+ error = parse_publish(reader, hr, &tmp);
if (error)
return error;
- error = write_from_uri(tmp->doc_data.uri, tmp->content,
- tmp->content_len, visited_uris);
- publish_destroy(tmp);
- if (error)
- return error;
+ error = write_from_uri(tmp->meta.uri, tmp->content, tmp->content_len);
- return 0;
+ publish_destroy(tmp);
+ return error;
}
/*
* other type at the caller.
*/
static int
-parse_withdraw_elem(xmlTextReaderPtr reader, struct visited_uris *visited_uris)
+parse_withdraw_elem(xmlTextReaderPtr reader)
{
struct withdraw *tmp;
int error;
if (error)
return error;
- error = __delete_from_uri(tmp->doc_data.uri, visited_uris);
- withdraw_destroy(tmp);
- if (error)
- return error;
+ error = __delete_from_uri(tmp->meta.uri);
- return 0;
+ withdraw_destroy(tmp);
+ return error;
}
static int
parse_notification_delta(xmlTextReaderPtr reader,
- struct update_notification *update)
+ struct update_notification *notification)
{
struct delta_head delta;
int error;
error = parse_long(reader, RRDP_ATTR_SERIAL, &delta.serial);
if (error)
return error;
- error = parse_doc_data(reader, true, true, &delta.doc_data);
+ error = parse_doc_data(reader, HR_MANDATORY, &delta.meta);
if (error)
return error;
- deltas_head_add(&update->deltas_list, &delta);
+ deltas_head_add(¬ification->deltas_list, &delta);
return 0;
}
if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_DELTA)) {
return parse_notification_delta(reader, update);
} else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_SNAPSHOT)) {
- return parse_doc_data(reader, true, true,
+ return parse_doc_data(reader, HR_MANDATORY,
&update->snapshot);
} else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_NOTIFICATION)) {
/* No need to validate session ID and serial */
- return parse_global_data(reader,
- &update->global_data, NULL, 0);
+ return parse_metadata(reader, &update->meta, NULL, 0);
}
return pr_val_err("Unexpected '%s' element", name);
case XML_READER_TYPE_END_ELEMENT:
if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_NOTIFICATION))
return deltas_head_sort(&update->deltas_list,
- update->global_data.serial);
+ update->meta.serial);
break;
}
return 0;
}
-static int
-parse_notification(struct rpki_uri *uri, struct update_notification **file)
+int
+rrdp_parse_notification(struct rpki_uri *uri,
+ struct update_notification *result)
{
- struct update_notification *result;
int error;
- result = update_notification_create(uri_get_global(uri));
- if (result == NULL)
- enomem_panic();
+ update_notification_init(result, uri);
- error = relax_ng_parse(uri_get_local(uri), xml_read_notification,
- result);
- if (error) {
+ error = relax_ng_parse(uri_get_local(uri), xml_read_notification, result);
+ if (error)
update_notification_destroy(result);
- return error;
- }
- *file = result;
- return 0;
+ return error;
}
static int
switch (type) {
case XML_READER_TYPE_ELEMENT:
if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_PUBLISH))
- error = parse_publish_elem(reader, false, false,
- ctx->visited_uris);
+ error = parse_publish_elem(reader, HR_IGNORE);
else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_SNAPSHOT))
- error = parse_global_data(reader,
- &ctx->snapshot->global_data,
- ctx->parent->global_data.session_id,
- ctx->parent->global_data.serial);
+ error = parse_metadata(reader,
+ &ctx->snapshot->meta,
+ ctx->notification->meta.session_id,
+ ctx->notification->meta.serial);
else
return pr_val_err("Unexpected '%s' element", name);
}
static int
-parse_snapshot(struct rpki_uri *uri, struct proc_upd_args *args)
+parse_snapshot(struct rpki_uri *uri, struct update_notification *notification)
{
struct rdr_snapshot_ctx ctx;
int error;
fnstack_push_uri(uri);
/* Hash validation */
- error = hash_validate_file(uri, args->parent->snapshot.hash,
- args->parent->snapshot.hash_len);
+ error = hash_validate_file(uri, notification->snapshot.hash,
+ notification->snapshot.hash_len);
if (error)
goto pop;
ctx.snapshot = snapshot_create();
- ctx.parent = args->parent;
- ctx.visited_uris = args->visited_uris;
+ ctx.notification = notification;
error = relax_ng_parse(uri_get_local(uri), xml_read_snapshot, &ctx);
switch (type) {
case XML_READER_TYPE_ELEMENT:
if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_PUBLISH))
- error = parse_publish_elem(reader, true, false,
- ctx->visited_uris);
+ error = parse_publish_elem(reader, HR_OPTIONAL);
else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_WITHDRAW))
- error = parse_withdraw_elem(reader, ctx->visited_uris);
+ error = parse_withdraw_elem(reader);
else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_DELTA))
- error = parse_global_data(reader,
- &ctx->delta->global_data,
- ctx->parent->global_data.session_id,
+ error = parse_metadata(reader,
+ &ctx->delta->meta,
+ ctx->notification->meta.session_id,
ctx->expected_serial);
else
return pr_val_err("Unexpected '%s' element", name);
static int
parse_delta(struct rpki_uri *uri, struct delta_head *parents_data,
- struct proc_upd_args *args)
+ struct update_notification *notification)
{
struct rdr_delta_ctx ctx;
- struct doc_data *expected_data;
+ struct file_metadata *expected_data;
int error;
- expected_data = &parents_data->doc_data;
+ expected_data = &parents_data->meta;
fnstack_push_uri(uri);
error = hash_validate_file(uri, expected_data->hash,
goto pop_fnstack;
ctx.delta = delta_create();
- ctx.parent = args->parent;
- ctx.visited_uris = args->visited_uris;
+ ctx.notification = notification;
ctx.expected_serial = parents_data->serial;
error = relax_ng_parse(uri_get_local(uri), xml_read_delta, &ctx);
static int
process_delta(struct delta_head *delta_head, void *arg)
{
- struct proc_upd_args *args = arg;
struct rpki_uri *uri;
- struct doc_data *head_data;
+ struct file_metadata *head_data;
int error;
- head_data = &delta_head->doc_data;
+ head_data = &delta_head->meta;
pr_val_debug("Processing delta '%s'.", head_data->uri);
- error = uri_create_https_str_rrdp(&uri, head_data->uri,
- strlen(head_data->uri));
+ error = uri_create(&uri, UT_HTTPS, head_data->uri);
if (error)
return error;
fnstack_push_uri(uri);
- error = download_file(uri, 0);
+ error = cache_download(uri, NULL);
if (error)
goto release_uri;
- error = parse_delta(uri, delta_head, args);
+ error = parse_delta(uri, delta_head, arg);
- delete_from_uri(uri, NULL);
+ delete_from_uri(uri);
/* Error 0 its ok */
release_uri:
fnstack_pop();
return error;
}
-/*
- * Download from @uri and set result file contents to @result, the file name
- * is pushed into fnstack, so don't forget to do the pop when done working
- * with the file.
- *
- * If the server didn't sent the file, due to the validation of
- * 'If-Modified-Since' header, return 0 and set @result to NULL.
- *
- * Set @force to true to omit 'If-Modified-Since' header.
- */
int
-rrdp_parse_notification(struct rpki_uri *uri, bool force,
- struct update_notification **result)
+rrdp_parse_snapshot(struct update_notification *notification)
{
- long last_update;
- int error, vis_err;
-
- if (uri == NULL || uri_is_rsync(uri))
- pr_crit("Wrong call, trying to parse a non HTTPS URI");
-
- fnstack_push_uri(uri);
- pr_val_debug("Processing notification.");
-
- last_update = 0;
- if (!force) {
- error = db_rrdp_uris_get_last_update(uri_get_global(uri), &last_update);
- if (error && error != -ENOENT)
- goto end;
- }
-
- error = download_file(uri, last_update);
- if (error < 0)
- goto end;
-
- /* Request error, stop processing to handle as such */
- if (error == EREQFAILED)
- goto end;
-
- /*
- * Mark as visited, if it doesn't exists yet, there's no problem since
- * this is probably the first time is visited (first run), so it will
- * be marked as visited when the URI is stored at DB.
- */
- vis_err = db_rrdp_uris_set_request_status(uri_get_global(uri),
- RRDP_URI_REQ_VISITED);
- if (vis_err && vis_err != -ENOENT) {
- error = pr_val_err("Couldn't mark file as visited.");
- goto end;
- }
-
- /* No updates yet */
- if (error > 0) {
- delete_from_uri(uri, NULL);
- *result = NULL;
- error = 0;
- goto end;
- }
-
- error = parse_notification(uri, result);
-
- delete_from_uri(uri, NULL);
-end: fnstack_pop();
- return error;
-}
-
-int
-rrdp_parse_snapshot(struct update_notification *parent,
- struct visited_uris *visited_uris)
-{
- struct proc_upd_args args;
struct rpki_uri *uri;
int error;
- args.parent = parent;
- args.visited_uris = visited_uris;
-
- pr_val_debug("Processing snapshot '%s'.", parent->snapshot.uri);
- error = uri_create_https_str_rrdp(&uri, parent->snapshot.uri,
- strlen(parent->snapshot.uri));
+ pr_val_debug("Processing snapshot '%s'.", notification->snapshot.uri);
+ error = uri_create(&uri, UT_HTTPS, notification->snapshot.uri);
if (error)
return error;
fnstack_push_uri(uri);
- error = download_file(uri, 0);
+ error = cache_download(uri, NULL);
if (error)
goto release_uri;
- error = parse_snapshot(uri, &args);
+ error = parse_snapshot(uri, notification);
- delete_from_uri(uri, NULL);
+ delete_from_uri(uri);
/* Error 0 is ok */
release_uri:
fnstack_pop();
}
int
-rrdp_process_deltas(struct update_notification *parent,
- unsigned long cur_serial, struct visited_uris *visited_uris)
+rrdp_process_deltas(struct update_notification *notification,
+ unsigned long cur_serial)
{
- struct proc_upd_args args;
-
- args.parent = parent;
- args.visited_uris = visited_uris;
-
- return deltas_head_for_each(&parent->deltas_list,
- parent->global_data.serial, cur_serial, process_delta, &args);
+ return deltas_head_for_each(¬ification->deltas_list,
+ notification->meta.serial, cur_serial, process_delta, notification);
}
#include <stdbool.h>
#include "types/uri.h"
#include "rrdp/rrdp_objects.h"
-#include "visited_uris.h"
-int rrdp_parse_notification(struct rpki_uri *, bool,
- struct update_notification **);
-int rrdp_parse_snapshot(struct update_notification *, struct visited_uris *);
-
-int rrdp_process_deltas(struct update_notification *, unsigned long,
- struct visited_uris *);
+int rrdp_parse_notification(struct rpki_uri *, struct update_notification *);
+int rrdp_parse_snapshot(struct update_notification *);
+int rrdp_process_deltas(struct update_notification *, unsigned long);
#endif /* SRC_RRDP_RRDP_PARSER_H_ */
-#include "rsync.h"
+#include "rsync/rsync.h"
#include <errno.h>
#include <stdlib.h>
#include "log.h"
#include "thread_var.h"
-struct uri {
- struct rpki_uri *uri;
- SLIST_ENTRY(uri) next;
-};
-
-/** URIs that we have already downloaded. */
-SLIST_HEAD(uri_list, uri);
-
-/* static char const *const RSYNC_PREFIX = "rsync://"; */
-
-struct uri_list *
-rsync_create(void)
-{
- struct uri_list *visited_uris;
-
- visited_uris = pmalloc(sizeof(struct uri_list));
- SLIST_INIT(visited_uris);
-
- return visited_uris;
-}
-
-void
-rsync_destroy(struct uri_list *list)
-{
- struct uri *uri;
-
- while (!SLIST_EMPTY(list)) {
- uri = SLIST_FIRST(list);
- SLIST_REMOVE_HEAD(list, next);
- uri_refput(uri->uri);
- free(uri);
- }
- free(list);
-}
-
-/*
- * Returns whether @uri has already been rsync'd during the current validation
- * run.
- */
-static bool
-is_already_downloaded(struct rpki_uri *uri, struct uri_list *visited_uris)
-{
- struct uri *cursor;
-
- /* TODO (next iteration) this is begging for a hash set. */
- SLIST_FOREACH(cursor, visited_uris, next)
- if (uri_equals(cursor->uri, uri))
- return true;
-
- return false;
-}
-
-static void
-mark_as_downloaded(struct rpki_uri *uri, struct uri_list *visited_uris)
-{
- struct uri *node;
-
- node = pmalloc(sizeof(struct uri));
- node->uri = uri;
- uri_refget(uri);
-
- SLIST_INSERT_HEAD(visited_uris, node, next);
-}
-
/*
* Duplicate parent FDs, to pipe rsync output:
* - fds[0] = stderr
/*
* Downloads the @uri->global file into the @uri->local path.
*/
-static int
-do_rsync(struct rpki_uri *uri)
+int
+rsync_download(struct rpki_uri *uri)
{
/* Descriptors to pipe stderr (first element) and stdout (second) */
char **args;
int child_status;
int error;
+ if (!config_get_rsync_enabled())
+ return 0; /* Skip; caller will work with existing cache. */
+
/* Prepare everything for the child exec */
args = NULL;
args_len = 0;
release_args(args, args_len);
return error;
}
-
-int
-rsync_download_files(struct rpki_uri *uri, bool force)
-{
- struct validation *state;
- struct uri_list *visited_uris;
- int error;
-
- if (!config_get_rsync_enabled())
- return 0;
-
- state = state_retrieve();
- visited_uris = validation_rsync_visited_uris(state);
-
- if (!force && is_already_downloaded(uri, visited_uris)) {
- pr_val_debug("No need to redownload '%s'.",
- uri_val_get_printable(uri));
- return 0;
- }
-
- pr_val_debug("Going to RSYNC '%s'.", uri_val_get_printable(uri));
-
- error = do_rsync(uri);
- switch (error) {
- case 0:
- /* Don't store when "force" and if its already downloaded */
- if (!(force && is_already_downloaded(uri, visited_uris)))
- mark_as_downloaded(uri, visited_uris);
- break;
- case EREQFAILED:
- mark_as_downloaded(uri, visited_uris);
- break;
- }
-
- return error;
-}
-
-void
-reset_downloaded(void)
-{
- struct uri_list *list;
- struct uri *uri;
-
- list = validation_rsync_visited_uris(state_retrieve());
-
- while (!SLIST_EMPTY(list)) {
- uri = SLIST_FIRST(list);
- SLIST_REMOVE_HEAD(list, next);
- uri_refput(uri->uri);
- free(uri);
- }
-}
#ifndef SRC_RSYNC_RSYNC_H_
#define SRC_RSYNC_RSYNC_H_
-#include <stdbool.h>
#include "types/uri.h"
-struct uri_list;
-
-int rsync_download_files(struct rpki_uri *, bool);
-struct uri_list *rsync_create(void);
-void rsync_destroy(struct uri_list *);
-
-void reset_downloaded(void);
+int rsync_download(struct rpki_uri *);
#endif /* SRC_RSYNC_RSYNC_H_ */
return 0;
}
-void
-find_bad_vrp(char const *prefix, struct db_table *table)
-{
- struct hashable_roa *node;
- struct hashable_roa *tmp;
- struct vrp const *vrp;
- unsigned int roa_counter;
- unsigned int roa_count;
- char buffer[INET6_ADDRSTRLEN];
-
- if (table == NULL)
- return;
-
- roa_counter = 0;
- roa_count = HASH_COUNT(table->roas);
-
- HASH_ITER(hh, table->roas, node, tmp) {
- vrp = &node->data;
- if (vrp->addr_fam != AF_INET && vrp->addr_fam != AF_INET6) {
- pr_crit("%s: VRP corrupted! [%u %s/%u-%u %u] %u/%u "
- "(Please report this output to https://github.com/NICMx/FORT-validator/issues/89)",
- prefix,
- vrp->asn,
- addr2str6(&vrp->prefix.v6, buffer),
- vrp->prefix_length,
- vrp->max_prefix_length,
- vrp->addr_fam,
- roa_counter,
- roa_count);
- }
- roa_counter++;
- }
-}
-
static int
add_router_key_delta(struct deltas *deltas, struct hashable_key *key, int op)
{
uint32_t, unsigned char const *);
int compute_deltas(struct db_table *, struct db_table *, struct deltas **);
-void find_bad_vrp(char const *, struct db_table *);
-
#endif /* SRC_RTR_DB_DB_TABLE_H_ */
#include "rtr/db/db_table.h"
#include "slurm/slurm_loader.h"
#include "thread/thread_pool.h"
+#include "cache/local_cache.h"
struct vrp_node {
struct delta_vrp delta;
struct db_table *db;
int error;
+ cache_prepare();
+
db = db_table_create();
- if (db == NULL)
- enomem_panic();
error = perform_standalone_validation(pool, db);
if (error) {
return 0;
}
+/*
+ * High level validator function.
+ *
+ * - Downloads tree
+ * - Validates tree
+ * - Updates RTR state
+ */
static int
__vrps_update(bool *notify_clients)
{
*notify_clients = false;
old_base = state.base;
new_base = NULL;
- find_bad_vrp("Old base", old_base);
error = __perform_standalone_validation(&new_base);
if (error)
return error;
- find_bad_vrp("After standalone (old)", old_base);
- find_bad_vrp("After standalone (new)", new_base);
-
error = slurm_apply(new_base, &state.slurm);
if (error) {
db_table_destroy(new_base);
return error;
}
- find_bad_vrp("After SLURM (old)", old_base);
- find_bad_vrp("After SLURM (new)", new_base);
-
/*
* At this point, new_base is completely valid. Even if we error out
* later, report the ROAs.
*/
output_print_data(new_base);
- find_bad_vrp("After CSV (old)", old_base);
- find_bad_vrp("After CSV (new)", new_base);
-
error = __compute_deltas(old_base, new_base, notify_clients,
&new_deltas);
if (error) {
return 0;
}
+/*
+ * Highest level validator function.
+ *
+ * - Downloads tree
+ * - Validates tree
+ * - Updates RTR state
+ * - Logs status
+ *
+ * TODO (#50) remove this wrapper once Prometheus is implemented
+ */
int
vrps_update(bool *changed)
{
time_t start, finish;
- long int exec_time;
+ unsigned int roas, rks;
serial_t serial;
int error;
- /*
- * This wrapper is mainly intended to log informational data, so if
- * there's no need, don't do unnecessary calls.
- */
- if (!log_op_enabled(LOG_INFO))
- return __vrps_update(changed);
-
- pr_op_info("Starting validation.");
- if (config_get_mode() == SERVER) {
- error = get_last_serial_number(&serial);
- if (!error)
- pr_op_info("- Serial before validation: %u", serial);
- }
-
- time(&start);
+ start = time(NULL);
error = __vrps_update(changed);
- time(&finish);
- exec_time = finish - start;
+ finish = time(NULL);
- pr_op_info("Validation finished:");
rwlock_read_lock(&state_lock);
- do {
- if (state.base == NULL) {
- rwlock_unlock(&state_lock);
- pr_op_info("- Valid ROAs: 0");
- pr_op_info("- Valid Router Keys: 0");
- if (config_get_mode() == SERVER)
- pr_op_info("- No serial number.");
- break;
- }
+ if (state.base == NULL) {
+ roas = 0;
+ rks = 0;
+ serial = 0;
+ } else {
+ roas = db_table_roa_count(state.base);
+ rks = db_table_router_key_count(state.base);
+ serial = state.serial;
+ }
+ rwlock_unlock(&state_lock);
- pr_op_info("- Valid ROAs: %u", db_table_roa_count(state.base));
- pr_op_info("- Valid Router Keys: %u",
- db_table_router_key_count(state.base));
- if (config_get_mode() == SERVER)
- pr_op_info("- Serial: %u", state.serial);
- rwlock_unlock(&state_lock);
- } while(0);
- pr_op_info("- Real execution time: %ld secs.", exec_time);
+ pr_op_info("Validation finished:");
+ pr_op_info("- Valid ROAs: %u", roas);
+ pr_op_info("- Valid Router Keys: %u", rks);
+ if (config_get_mode() == SERVER)
+ pr_op_info("- Serial: %u", serial);
+ if (start != ((time_t) -1) && finish != ((time_t) -1))
+ pr_op_info("- Real execution time: %ld secs.", finish - start);
return error;
}
}
return send_end_of_data_pdu(fd, args.rtr_version, final_serial);
case -EAGAIN: /* Database still under construction */
- error = err_pdu_send_no_data_available(fd, args.rtr_version);
- return error;
+ return err_pdu_send_no_data_available(fd, args.rtr_version);
case -ESRCH: /* Invalid serial */
/* https://tools.ietf.org/html/rfc6810#section-6.3 */
return send_cache_reset_pdu(fd, args.rtr_version);
#include "state.h"
#include <errno.h>
-#include "rrdp/db/db_rrdp.h"
#include "alloc.h"
+#include "cert_stack.h"
#include "log.h"
#include "thread_var.h"
struct cert_stack *certstack;
- struct uri_list *rsync_visited_uris;
-
- /* Shallow copy of RRDP URIs and its corresponding visited uris */
- struct db_rrdp_uri *rrdp_uris;
-
/* Did the TAL's public key match the root certificate's public key? */
enum pubkey_state pubkey_state;
char addr_buffer2[INET6_ADDRSTRLEN];
struct validation_handler validation_handler;
+
+ /* URL of the Notification file currently being traversed. */
+ struct rpki_uri *notification_uri;
};
/*
if (error)
goto abort3;
- result->rsync_visited_uris = rsync_create();
- result->rrdp_uris = db_rrdp_get_uris(tal_get_file_name(tal));
result->pubkey_state = PKS_UNTESTED;
result->validation_handler = *validation_handler;
result->x509_data.params = params; /* Ownership transfered */
X509_VERIFY_PARAM_free(state->x509_data.params);
X509_STORE_free(state->x509_data.store);
certstack_destroy(state->certstack);
- rsync_destroy(state->rsync_visited_uris);
free(state);
}
return state->certstack;
}
-struct uri_list *
-validation_rsync_visited_uris(struct validation *state)
-{
- return state->rsync_visited_uris;
-}
-
void
validation_pubkey_valid(struct validation *state)
{
return &state->validation_handler;
}
-struct db_rrdp_uri *
-validation_get_rrdp_uris(struct validation *state)
+struct rpki_uri *
+validation_get_notification_uri(struct validation *state)
+{
+ return state->notification_uri;
+}
+
+void
+validation_set_notification_uri(struct validation *state, struct rpki_uri *uri)
{
- return state->rrdp_uris;
+ if (state->notification_uri != NULL)
+ uri_refput(state->notification_uri);
+ state->notification_uri = uri;
+ if (uri != NULL)
+ uri_refget(uri);
}
#define SRC_STATE_H_
#include <openssl/x509.h>
-#include "cert_stack.h"
#include "validation_handler.h"
#include "object/tal.h"
-#include "rsync/rsync.h"
-#include "rrdp/db/db_rrdp_uris.h"
struct validation;
struct tal *validation_tal(struct validation *);
X509_STORE *validation_store(struct validation *);
struct cert_stack *validation_certstack(struct validation *);
-struct uri_list *validation_rsync_visited_uris(struct validation *);
enum pubkey_state {
PKS_VALID,
struct db_rrdp_uri *validation_get_rrdp_uris(struct validation *);
+struct rpki_uri *validation_get_notification_uri(struct validation *);
+void validation_set_notification_uri(struct validation *, struct rpki_uri *);
+
#endif /* SRC_STATE_H_ */
#include "alloc.h"
#include "config.h"
+#include "log.h"
static pthread_key_t state_key;
static pthread_key_t filenames_key;
#include <errno.h>
#include <strings.h>
-#include "rrdp/db/db_rrdp_uris.h"
#include "alloc.h"
#include "common.h"
-#include "config.h"
#include "log.h"
+#include "state.h"
#include "str_token.h"
-
-/* Expected URI types */
-enum rpki_uri_type {
- URI_RSYNC,
- URI_HTTPS,
-};
-
-static char const *const PFX_RSYNC = "rsync://";
-static char const *const PFX_HTTPS = "https://";
+#include "thread_var.h"
+#include "data_structure/path_builder.h"
/**
* Design notes:
* according to ASCII, we assume that we can just dump it into the
* output without trouble, because the input should have the same
* encoding as the output.
+ *
+ * Technically, "global" URI "https://a.b.c/d/..///./d" is not the same
+ * identifier as "https://a.b.c/d", but since we're supposed to download
+ * to a filesystem where "https/a.b.c/d" is the same file as
+ * "https/a.b.c/d/..///./d", @local will always be normalized.
*/
char *local;
/* "local_len" is never needed right now. */
- /* Type, currently rysnc and https are valid */
- enum rpki_uri_type type;
+ enum uri_type type;
unsigned int references;
};
return 0;
}
+/*
+ * Maps "rsync://a.b.c/d/e.cer" into "<local-repository>/rsync/a.b.c/d/e.cer".
+ */
static int
-validate_uri_begin(char const *uri_pfx, const size_t uri_pfx_len,
- char const *global, size_t global_len, int error)
-{
- if (global_len < uri_pfx_len
- || strncasecmp(uri_pfx, global, uri_pfx_len) != 0) {
- if (!error)
- return -EINVAL;
- pr_val_err("Global URI '%s' does not begin with '%s'.",
- global, uri_pfx);
- return error;
- }
-
- return 0;
-}
-
-static int
-validate_gprefix(char const *global, size_t global_len, uint8_t flags,
- enum rpki_uri_type *type)
+map_simple(struct rpki_uri *uri, char const *gprefix, char const *lprefix,
+ int err)
{
- size_t const PFX_RSYNC_LEN = strlen(PFX_RSYNC);
- size_t const PFX_HTTPS_LEN = strlen(PFX_HTTPS);
+ struct path_builder pb;
int error;
- if (flags == URI_VALID_RSYNC) {
- (*type) = URI_RSYNC;
- return validate_uri_begin(PFX_RSYNC, PFX_RSYNC_LEN, global,
- global_len, ENOTRSYNC);
+ if (!str_starts_with(uri->global, gprefix)) {
+ pr_val_err("URI '%s' does not begin with '%s'.",
+ uri->global, lprefix);
+ return err;
}
- if (flags == URI_VALID_HTTPS) {
- (*type) = URI_HTTPS;
- return validate_uri_begin(PFX_HTTPS, PFX_HTTPS_LEN, global,
- global_len, ENOTHTTPS);
- }
- if (flags != (URI_VALID_RSYNC | URI_VALID_HTTPS))
- pr_crit("Unknown URI flag");
-
- /* It has both flags */
- error = validate_uri_begin(PFX_RSYNC, PFX_RSYNC_LEN, global, global_len,
- 0);
- if (!error) {
- (*type) = URI_RSYNC;
- return 0;
- }
- error = validate_uri_begin(PFX_HTTPS, PFX_HTTPS_LEN, global, global_len,
- 0);
- if (error) {
- pr_val_warn("URI '%s' does not begin with '%s' nor '%s'.",
- global, PFX_RSYNC, PFX_HTTPS);
- return ENOTSUPPORTED;
+
+ path_init(&pb);
+ path_append_guri(&pb, uri);
+ error = path_compile(&pb, &uri->local);
+ if (error)
+ return error;
+
+ if (!str_starts_with(uri->local, lprefix)) {
+ pr_val_err("URI '%s' seems to be dot-dotting to its scheme.",
+ uri->global);
+ free(uri->local);
+ return -EINVAL;
}
- /* @size was already set */
- (*type) = URI_HTTPS;
return 0;
}
-/**
- * Initializes @uri->local by converting @uri->global.
- *
- * For example, given local cache repository "/tmp/rpki" and global uri
- * "rsync://rpki.ripe.net/repo/manifest.mft", initializes @uri->local as
- * "/tmp/rpki/rpki.ripe.net/repo/manifest.mft".
- *
- * By contract, if @guri is not RSYNC nor HTTPS, this will return ENOTRSYNC.
- * This often should not be treated as an error; please handle gracefully.
+/*
+ * Maps "rsync://a.b.c/d/e.cer" into
+ * "<local-repository>/rrdp/<notification-path>/a.b.c/d/e.cer".
*/
static int
-g2l(char const *global, size_t global_len, uint8_t flags, char **result,
- enum rpki_uri_type *result_type)
+map_caged(struct rpki_uri *uri)
{
- enum rpki_uri_type type;
- int error;
+ struct path_builder builder;
+ struct rpki_uri *notification;
- error = validate_gprefix(global, global_len, flags, &type);
- if (error)
- return error;
+ notification = validation_get_notification_uri(state_retrieve());
+ if (notification == NULL)
+ pr_crit("Programming error: Notification not recorded.");
- *result = map_uri_to_local(global,
- type == URI_RSYNC ? PFX_RSYNC : PFX_HTTPS);
- (*result_type) = type;
- return 0;
+ path_init(&builder);
+
+ path_append(&builder, "rrdp");
+ path_append_guri(&builder, notification);
+ path_append_guri(&builder, uri);
+
+ return path_compile(&builder, &uri->local);
}
static int
-autocomplete_local(struct rpki_uri *uri, uint8_t flags)
+autocomplete_local(struct rpki_uri *uri)
{
- return g2l(uri->global, uri->global_len, flags, &uri->local,
- &uri->type);
+ switch (uri->type) {
+ case UT_RSYNC:
+ return map_simple(uri, "rsync://", "rsync/", ENOTRSYNC);
+ case UT_HTTPS:
+ return map_simple(uri, "https://", "https/", ENOTHTTPS);
+ case UT_CAGED:
+ return map_caged(uri);
+ }
+
+ pr_crit("Unknown URI type: %u", uri->type);
}
-static int
-uri_create(struct rpki_uri **result, uint8_t flags, void const *guri,
+/*
+ * I think the reason why @guri is not a char * is to convey that it doesn't
+ * need to be NULL terminated, but I'm not sure.
+ */
+int
+__uri_create(struct rpki_uri **result, enum uri_type type, void const *guri,
size_t guri_len)
{
struct rpki_uri *uri;
return error;
}
- error = autocomplete_local(uri, flags);
+ uri->type = type;
+
+ error = autocomplete_local(uri);
if (error) {
free(uri->global);
free(uri);
}
uri->references = 1;
+
*result = uri;
return 0;
}
int
-uri_create_rsync_str_rrdp(struct rpki_uri **uri, char const *guri,
- size_t guri_len)
+uri_create(struct rpki_uri **result, enum uri_type type, char const *guri)
{
- return uri_create(uri, URI_VALID_RSYNC, guri, guri_len);
-}
-
-int
-uri_create_https_str_rrdp(struct rpki_uri **uri, char const *guri,
- size_t guri_len)
-{
- return uri_create(uri, URI_VALID_HTTPS, guri, guri_len);
-}
-
-int
-uri_create_rsync_str(struct rpki_uri **uri, char const *guri, size_t guri_len)
-{
- return uri_create(uri, URI_VALID_RSYNC, guri, guri_len);
-}
-
-/*
- * A URI that can be rsync or https.
- *
- * Return ENOTSUPPORTED if not an rsync or https URI.
- */
-int
-uri_create_mixed_str(struct rpki_uri **uri, char const *guri, size_t guri_len)
-{
- return uri_create(uri, URI_VALID_RSYNC | URI_VALID_HTTPS, guri,
- guri_len);
+ return __uri_create(result, type, guri, strlen(guri));
}
/*
return error;
}
- error = autocomplete_local(uri, URI_VALID_RSYNC);
+ uri->type = UT_RSYNC;
+
+ error = autocomplete_local(uri);
if (error) {
free(uri->global);
free(uri);
}
uri->references = 1;
+
*result = uri;
return 0;
}
-/*
- * Create @uri from the @ad, validating that the uri is of type(s) indicated
- * at @flags (can be URI_VALID_RSYNC and/or URI_VALID_HTTPS)
- */
-int
-uri_create_ad(struct rpki_uri **uri, ACCESS_DESCRIPTION *ad, int flags)
-{
- ASN1_STRING *asn1_string;
- int type;
-
- asn1_string = GENERAL_NAME_get0_value(ad->location, &type);
-
- /*
- * RFC 6487: "This extension MUST have an instance of an
- * AccessDescription with an accessMethod of id-ad-rpkiManifest, (...)
- * with an rsync URI [RFC5781] form of accessLocation."
- *
- * Ehhhhhh. It's a little annoying in that it seems to be stucking more
- * than one requirement in a single sentence, which I think is rather
- * rare for an RFC. Normally they tend to hammer things more.
- *
- * Does it imply that the GeneralName CHOICE is constrained to type
- * "uniformResourceIdentifier"? I guess so, though I don't see anything
- * stopping a few of the other types from also being capable of storing
- * URIs.
- *
- * Also, nobody seems to be using the other types, and handling them
- * would be a titanic pain in the ass. So this is what I'm committing
- * to.
- */
- if (type != GEN_URI) {
- pr_val_err("Unknown GENERAL_NAME type: %d", type);
- return ENOTSUPPORTED;
- }
-
- /*
- * GEN_URI signals an IA5String.
- * IA5String is a subset of ASCII, so this cast is safe.
- * No guarantees of a NULL chara, though.
- *
- * TODO (testers) According to RFC 5280, accessLocation can be an IRI
- * somehow converted into URI form. I don't think that's an issue
- * because the RSYNC clone operation should not have performed the
- * conversion, so we should be looking at precisely the IA5String
- * directory our g2l version of @asn1_string should contain.
- * But ask the testers to keep an eye on it anyway.
- */
- return uri_create(uri, flags,
- ASN1_STRING_get0_data(asn1_string),
- ASN1_STRING_length(asn1_string));
-}
-
-void
+struct rpki_uri *
uri_refget(struct rpki_uri *uri)
{
uri->references++;
+ return uri;
}
void
return uri_has_extension(uri, ".cer");
}
+enum uri_type
+uri_get_type(struct rpki_uri *uri)
+{
+ return uri->type;
+}
+
bool
uri_is_rsync(struct rpki_uri *uri)
{
- return uri->type == URI_RSYNC;
+ return uri->type == UT_RSYNC;
+}
+
+bool
+uri_is_https(struct rpki_uri *uri)
+{
+ return uri->type == UT_HTTPS;
}
static char const *
#define SRC_TYPES_URI_H_
#include <stdbool.h>
-#include <openssl/x509v3.h>
#include "asn1/asn1c/IA5String.h"
-/* Flags to indicate expected uri type */
-#define URI_VALID_RSYNC 0x01
-#define URI_VALID_HTTPS 0x02
-
-#define ESKIP 85830
+enum uri_type {
+ /* rsync URL */
+ UT_RSYNC,
+ /* HTTPS URL */
+ UT_HTTPS,
+ /*
+ * URI (not URL).
+ * In practice it's always rsync, but it doesn't matter.
+ */
+ UT_CAGED,
+};
struct rpki_uri;
-/* Maps RSYNC URIs of RRDP to a local workspace */
-int uri_create_rsync_str_rrdp(struct rpki_uri **, char const *, size_t);
-int uri_create_https_str_rrdp(struct rpki_uri **, char const *, size_t);
-
-int uri_create_rsync_str(struct rpki_uri **, char const *, size_t);
-int uri_create_mixed_str(struct rpki_uri **, char const *, size_t);
+int __uri_create(struct rpki_uri **, enum uri_type, void const *, size_t);
+int uri_create(struct rpki_uri **, enum uri_type, char const *);
int uri_create_mft(struct rpki_uri **, struct rpki_uri *, IA5String_t *);
-int uri_create_ad(struct rpki_uri **, ACCESS_DESCRIPTION *, int);
-void uri_refget(struct rpki_uri *);
+struct rpki_uri *uri_refget(struct rpki_uri *);
void uri_refput(struct rpki_uri *);
/*
bool uri_equals(struct rpki_uri *, struct rpki_uri *);
bool uri_has_extension(struct rpki_uri *, char const *);
bool uri_is_certificate(struct rpki_uri *);
+
+enum uri_type uri_get_type(struct rpki_uri *);
bool uri_is_rsync(struct rpki_uri *);
+bool uri_is_https(struct rpki_uri *);
char const *uri_val_get_printable(struct rpki_uri *);
char const *uri_op_get_printable(struct rpki_uri *);
int
validation_run_first(void)
{
- int error;
-
- if (config_get_mode() == SERVER)
- pr_op_warn("First validation cycle has begun, wait until the next notification to connect your router(s)");
- else
- pr_op_warn("The validation has begun.");
+ pr_op_info("Please wait. Validating...");
- error = vrps_update(NULL);
- if (error)
- return pr_op_err("First validation wasn't successful.");
+ if (vrps_update(NULL) != 0)
+ return pr_op_err("Validation unsuccessful; results unusable.");
if (config_get_mode() == SERVER)
- pr_op_warn("First validation cycle successfully ended, now you can connect your router(s)");
+ pr_op_info("Validation complete; waiting for routers.");
else
- pr_op_warn("The validation has successfully ended.");
+ pr_op_info("Validation complete.");
return 0;
}
+++ /dev/null
-#include "visited_uris.h"
-
-#include <sys/queue.h>
-#include <stddef.h>
-#include <string.h>
-#include "alloc.h"
-#include "log.h"
-#include "delete_dir_daemon.h"
-#include "data_structure/array_list.h"
-#include "data_structure/uthash.h"
-
-struct visited_elem {
- /* key */
- char *uri;
- UT_hash_handle hh;
-};
-
-struct visited_uris {
- struct visited_elem *table;
- unsigned int refs;
-};
-
-DEFINE_ARRAY_LIST_STRUCT(uris_roots, char *);
-DEFINE_ARRAY_LIST_FUNCTIONS(uris_roots, char *, static)
-
-static struct visited_elem *
-visited_elem_create(char const *uri)
-{
- struct visited_elem *tmp;
-
- tmp = pzalloc(sizeof(struct visited_elem)); /* Zero needed by uthash */
- tmp->uri = pstrdup(uri);
-
- return tmp;
-}
-
-static void
-visited_elem_destroy(struct visited_elem *elem)
-{
- free(elem->uri);
- free(elem);
-}
-
-struct visited_uris *
-visited_uris_create(void)
-{
- struct visited_uris *tmp;
-
- tmp = pmalloc(sizeof(struct visited_uris));
-
- tmp->table = NULL;
- tmp->refs = 1;
-
- return tmp;
-}
-
-static void
-visited_uris_destroy(struct visited_uris *uris)
-{
- struct visited_elem *elm_node, *elm_tmp;
-
- HASH_ITER(hh, uris->table, elm_node, elm_tmp) {
- HASH_DEL(uris->table, elm_node);
- visited_elem_destroy(elm_node);
- }
- free(uris);
-}
-
-void
-visited_uris_refget(struct visited_uris *uris)
-{
- uris->refs++;
-}
-
-void
-visited_uris_refput(struct visited_uris *uris)
-{
- uris->refs--;
- if (uris->refs == 0)
- visited_uris_destroy(uris);
-}
-
-static struct visited_elem *
-elem_find(struct visited_uris *list, char const *uri)
-{
- struct visited_elem *found;
- HASH_FIND_STR(list->table, uri, found);
- return found;
-}
-
-void
-visited_uris_add(struct visited_uris *uris, char const *uri)
-{
- struct visited_elem *node;
-
- if (elem_find(uris, uri) == NULL) {
- /* Do not inline; HASH_ADD_STR expands "add" multiple times. */
- node = visited_elem_create(uri);
- HASH_ADD_STR(uris->table, uri, node);
- }
-}
-
-int
-visited_uris_remove(struct visited_uris *uris, char const *uri)
-{
- struct visited_elem *elem;
-
- elem = elem_find(uris, uri);
- if (elem == NULL)
- return pr_val_err("Trying to remove a nonexistent URI '%s'", uri);
-
- HASH_DEL(uris->table, elem);
- visited_elem_destroy(elem);
-
- return 0;
-}
-
-static void
-visited_uris_to_arr(struct visited_uris *uris, struct uris_roots *roots)
-{
- struct visited_elem *elem;
- char *tmp, *last_slash;
- size_t size;
-
- for (elem = uris->table; elem != NULL; elem = elem->hh.next) {
- last_slash = strrchr(elem->uri, '/');
- size = last_slash - elem->uri;
- tmp = pmalloc(size + 1);
- strncpy(tmp, elem->uri, size);
- tmp[size] = '\0';
- uris_roots_add(roots, &tmp);
- }
-}
-
-static void
-uris_root_destroy(char **elem)
-{
- free(*elem);
-}
-
-/*
- * Delete all the corresponding local files of @uris located at @workspace
- */
-int
-visited_uris_delete_local(struct visited_uris *uris)
-{
- struct uris_roots roots;
- int error;
-
- uris_roots_init(&roots);
- visited_uris_to_arr(uris, &roots);
- if (roots.len == 0)
- goto success;
-
- error = delete_dir_daemon_start(roots.array, roots.len);
- if (error) {
- uris_roots_cleanup(&roots, uris_root_destroy);
- return error;
- }
-
-success:
- uris_roots_cleanup(&roots, uris_root_destroy);
- return 0;
-}
+++ /dev/null
-#ifndef SRC_VISITED_URIS_H_
-#define SRC_VISITED_URIS_H_
-
-struct visited_uris;
-
-struct visited_uris *visited_uris_create(void);
-void visited_uris_refput(struct visited_uris *);
-void visited_uris_refget(struct visited_uris *);
-
-void visited_uris_add(struct visited_uris *, char const *);
-int visited_uris_remove(struct visited_uris *, char const *);
-int visited_uris_delete_local(struct visited_uris *);
-
-#endif /* SRC_VISITED_URIS_H_ */
MY_LDADD = ${CHECK_LIBS}
check_PROGRAMS = address.test
-check_PROGRAMS += deltas_array.test
+check_PROGRAMS += cache.test
check_PROGRAMS += db_table.test
+check_PROGRAMS += deltas_array.test
check_PROGRAMS += line_file.test
+check_PROGRAMS += pb.test
+check_PROGRAMS += pdu.test
check_PROGRAMS += pdu_handler.test
+check_PROGRAMS += primitive_reader.test
check_PROGRAMS += rrdp_objects.test
-check_PROGRAMS += rsync.test
check_PROGRAMS += serial.test
check_PROGRAMS += tal.test
check_PROGRAMS += thread_pool.test
check_PROGRAMS += uri.test
+check_PROGRAMS += uthash.test
check_PROGRAMS += vcard.test
check_PROGRAMS += vrps.test
check_PROGRAMS += xml.test
-check_PROGRAMS += rtr/pdu.test
-check_PROGRAMS += rtr/primitive_reader.test
-check_PROGRAMS += data_structure/uthash.test
TESTS = ${check_PROGRAMS}
address_test_SOURCES = types/address_test.c
address_test_LDADD = ${MY_LDADD}
-deltas_array_test_SOURCES = rtr/db/deltas_array_test.c
-deltas_array_test_LDADD = ${MY_LDADD}
+cache_test_SOURCES = cache/local_cache_test.c
+cache_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
db_table_test_SOURCES = rtr/db/db_table_test.c
db_table_test_LDADD = ${MY_LDADD}
+deltas_array_test_SOURCES = rtr/db/deltas_array_test.c
+deltas_array_test_LDADD = ${MY_LDADD}
+
line_file_test_SOURCES = line_file_test.c
line_file_test_LDADD = ${MY_LDADD}
+pb_test_SOURCES = data_structure/path_builder_test.c
+pb_test_LDADD = ${MY_LDADD}
+
+pdu_test_SOURCES = rtr/pdu_test.c
+pdu_test_LDADD = ${MY_LDADD}
+
pdu_handler_test_SOURCES = rtr/pdu_handler_test.c
pdu_handler_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
+primitive_reader_test_SOURCES = rtr/primitive_reader_test.c
+primitive_reader_test_LDADD = ${MY_LDADD}
+
rrdp_objects_test_SOURCES = rrdp_objects_test.c
rrdp_objects_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS} ${XML2_LIBS}
-rsync_test_SOURCES = rsync_test.c
-rsync_test_LDADD = ${MY_LDADD}
-
serial_test_SOURCES = types/serial_test.c
serial_test_LDADD = ${MY_LDADD}
uri_test_SOURCES = types/uri_test.c
uri_test_LDADD = ${MY_LDADD}
+uthash_test_SOURCES = data_structure/uthash_test.c
+uthash_test_LDADD = ${MY_LDADD}
+
vcard_test_SOURCES = vcard_test.c
vcard_test_LDADD = ${MY_LDADD}
xml_test_SOURCES = xml_test.c
xml_test_LDADD = ${MY_LDADD} ${XML2_LIBS}
-rtr_pdu_test_SOURCES = rtr/pdu_test.c
-rtr_pdu_test_LDADD = ${MY_LDADD}
-
-rtr_primitive_reader_test_SOURCES = rtr/primitive_reader_test.c
-rtr_primitive_reader_test_LDADD = ${MY_LDADD}
-
-data_structure_uthash_test_SOURCES = data_structure/uthash_test.c
-data_structure_uthash_test_LDADD = ${MY_LDADD}
-
EXTRA_DIST = mock.c
EXTRA_DIST += line_file/core.txt
EXTRA_DIST += line_file/empty.txt
--- /dev/null
+/* This test will create temporal directory "tmp/". Needs permissions. */
+
+#include "cache/local_cache.c"
+
+#include <check.h>
+#include <stdarg.h>
+
+#include "alloc.c"
+#include "common.c"
+#include "file.c"
+#include "mock.c"
+#include "data_structure/path_builder.c"
+#include "types/uri.c"
+
+/* Mocks */
+
+MOCK_ABORT_PTR(state_retrieve, validation, void)
+
+static unsigned int dl_count; /* Times the download function was called */
+static bool dl_error; /* Download should return error? */
+
+int
+rsync_download(struct rpki_uri *uri)
+{
+ char *cmd;
+ int printed;
+
+ dl_count++;
+ if (dl_error)
+ return -EINVAL;
+
+ cmd = pmalloc(128);
+ printed = snprintf(cmd, 128, "mkdir -p tmp/%s", uri_get_local(uri));
+ ck_assert(printed < 128);
+
+ ck_assert_int_eq(0, system(cmd));
+
+ free(cmd);
+ return 0;
+}
+
+int
+http_download(struct rpki_uri *uri, bool *changed)
+{
+ char *cmd;
+ int printed;
+ int error;
+
+ dl_count++;
+ if (dl_error)
+ return -EINVAL;
+
+ cmd = pmalloc(128);
+ printed = snprintf(cmd, 128,
+ /* "create file, but only if it's not already a directory" */
+ "test ! -d tmp/%s && install -D /dev/null tmp/%s",
+ uri_get_local(uri), uri_get_local(uri));
+ ck_assert(printed < 128);
+
+ error = system(cmd);
+
+ free(cmd);
+ return error;
+}
+
+/* Helpers */
+
+static const int SUCCESS = CNF_DIRECT | CNF_SUCCESS;
+static const int HTTP_SUCCESS = SUCCESS | CNF_FILE;
+
+static bool
+is_rsync(struct cache_node *node)
+{
+ while (node->parent != NULL)
+ node = node->parent;
+ return strcmp(node->basename, "rsync") == 0;
+}
+
+static bool
+is_https(struct cache_node *node)
+{
+ while (node->parent != NULL)
+ node = node->parent;
+ return strcmp(node->basename, "https") == 0;
+}
+
+static void
+__download(char const *url, enum uri_type uritype, int expected_error,
+ unsigned int expected_cb_count)
+{
+ struct rpki_uri *uri;
+
+ ck_assert_int_eq(0, uri_create(&uri, uritype, url));
+ dl_count = 0;
+
+ ck_assert_int_eq(expected_error, cache_download(uri, NULL));
+ ck_assert_uint_eq(expected_cb_count, dl_count);
+
+ uri_refput(uri);
+}
+
+#define download_rsync(url, err, ecc) __download(url, UT_RSYNC, err, ecc)
+#define download_https(url, err, ecc) __download(url, UT_HTTPS, err, ecc)
+
+static struct cache_node *
+__NODE(char const *basename, int flags, time_t success, time_t attempt,
+ int error, ...)
+{
+ struct cache_node *result;
+ struct cache_node *child;
+ va_list args;
+
+ result = pzalloc(sizeof(struct cache_node));
+ result->basename = pstrdup(basename);
+ result->flags = flags;
+ result->ts_success = success;
+ result->ts_attempt = attempt;
+ result->error = error;
+
+ va_start(args, error);
+ while ((child = va_arg(args, struct cache_node *)) != NULL) {
+ HASH_ADD_KEYPTR(hh, result->children, child->basename,
+ strlen(child->basename), child);
+ child->parent = result;
+ }
+ va_end(args);
+
+ return result;
+}
+
+#define NODE(bs, f, ...) __NODE(bs, f, 0, 0, __VA_ARGS__, NULL)
+/* "Timed" node */
+#define TNODE(bs, f, s, a, ...) __NODE(bs, f, s, a, __VA_ARGS__, NULL)
+
+static void
+actual_not_found(struct cache_node *expected, char *parent_basename)
+{
+ ck_abort_msg("Parent '%s' is missing child '%s'", parent_basename,
+ expected->basename);
+}
+
+static void
+expected_not_found(struct cache_node *actual)
+{
+ ck_abort_msg("Parent '%s' has unexpected node '%s'",
+ (actual->parent == NULL) ? "root" : actual->parent->basename,
+ actual->basename);
+}
+
+static void
+print_tree(struct cache_node *root, unsigned int tabs)
+{
+ struct cache_node *cursor, *tmp;
+ unsigned int t;
+
+ if (root == NULL)
+ return;
+
+ for (t = 0; t < tabs; t++)
+ printf("\t");
+ printf("%s\n", root->basename);
+
+ HASH_ITER(hh, root->children, cursor, tmp)
+ print_tree(cursor, tabs + 1);
+}
+
+static void
+validate_node(struct cache_node *expected, struct cache_node *expected_parent,
+ struct cache_node *actual, struct path_builder *pb)
+{
+ struct cache_node *expected_child, *actual_child, *tmp;
+
+ ck_assert_str_eq(expected->basename, actual->basename);
+ ck_assert_int_eq(expected->flags, actual->flags);
+ if (expected->flags & CNF_DIRECT) {
+ /* ck_assert_int_ne(0, actual->ts_attempt); */
+ /* ck_assert_int_eq(actual->ts_attempt, actual->ts_success); */
+ if (expected->error)
+ ck_assert_int_ne(0, actual->error);
+ else
+ ck_assert_int_eq(0, actual->error);
+ } else {
+ /* ck_assert_int_eq(0, actual->ts_attempt); */
+ /* ck_assert_int_eq(0, actual->ts_success); */
+ ck_assert_int_eq(0, actual->error);
+ }
+ ck_assert_ptr_eq(expected_parent, actual->parent);
+
+ path_append(pb, expected->basename);
+
+ HASH_ITER(hh, expected->children, expected_child, tmp) {
+ HASH_FIND_STR(actual->children, expected_child->basename,
+ actual_child);
+ if (actual_child == NULL)
+ actual_not_found(expected_child, actual->basename);
+ validate_node(expected_child, actual, actual_child, pb);
+ }
+
+ HASH_ITER(hh, actual->children, actual_child, tmp) {
+ HASH_FIND_STR(expected->children, actual_child->basename,
+ expected_child);
+ if (expected_child == NULL)
+ expected_not_found(actual_child);
+ }
+
+ path_pop(pb, true);
+}
+
+static void
+search_dir(DIR *parent, char const *path, char const *name)
+{
+ struct dirent *file;
+ int error;
+
+ rewinddir(parent);
+ FOREACH_DIR_FILE(parent, file) {
+ if (S_ISDOTS(file))
+ continue;
+
+ if (strcmp(name, file->d_name) == 0)
+ return;
+ }
+
+ error = errno;
+ ck_assert_int_eq(0, error);
+
+ ck_abort_msg("File %s/%s doesn't exist", path, name);
+}
+
+static void
+validate_file(struct cache_node *expected, struct path_builder *pb)
+{
+ char const *path;
+ struct stat meta;
+ DIR *dir;
+ struct dirent *file;
+ struct cache_node *child, *tmp;
+ int error;
+
+ if (expected == NULL)
+ return;
+
+ path_append(pb, expected->basename);
+ ck_assert_int_eq(0, path_peek(pb, &path));
+
+ if (is_rsync(expected)) {
+ /* Currently, the unit tests do not fake rsync files */
+ goto must_be_dir;
+
+ } else if (is_https(expected)) {
+ if (expected->flags & CNF_DIRECT) {
+ if (expected->error == 0)
+ goto must_be_file; /* Because HTTP */
+ else
+ goto end;
+ } else {
+ goto must_be_dir; /* Because HTTP */
+ }
+ } else {
+ ck_abort_msg("Not rsync nor httpd");
+ }
+
+must_be_file:
+ ck_assert_int_eq(0, stat(path, &meta));
+ ck_assert_int_eq(1, S_ISREG(meta.st_mode));
+ goto end;
+
+must_be_dir:
+ errno = 0;
+ dir = opendir(path);
+ error = errno;
+ ck_assert_int_eq(0, error);
+ ck_assert_ptr_nonnull(dir);
+
+ FOREACH_DIR_FILE(dir, file) {
+ if (S_ISDOTS(file))
+ continue;
+
+ HASH_FIND_STR(expected->children, file->d_name, child);
+ if (child == NULL) {
+ ck_abort_msg("file %s/%s is not supposed to exist.",
+ path, file->d_name);
+ }
+
+ validate_file(child, pb);
+ }
+ error = errno;
+ ck_assert_int_eq(0, error);
+
+ HASH_ITER(hh, expected->children, child, tmp)
+ search_dir(dir, path, child->basename);
+
+ closedir(dir);
+end:
+ path_pop(pb, true);
+}
+
+static void
+validate_trees(struct cache_node *nodes, struct cache_node *files)
+{
+ struct cache_node *actual;
+ struct path_builder pb;
+
+ if (is_rsync(nodes))
+ actual = rsync;
+ else if (is_https(nodes))
+ actual = https;
+ else
+ ck_abort_msg("unknown root node: %s", nodes->basename);
+
+ printf("------------------------------\n");
+ printf("Expected nodes:\n");
+ print_tree(nodes, 1);
+ printf("Actual nodes:\n");
+ print_tree(actual, 1);
+ if (files != NULL) {
+ if (nodes != files) {
+ printf("Expected files:\n");
+ print_tree(files, 0);
+ }
+ printf("Actual files:\n");
+ file_ls_R("tmp");
+ }
+
+ path_init(&pb);
+ path_append(&pb, "tmp");
+
+ validate_node(nodes, NULL, actual, &pb);
+ validate_file(files, &pb);
+
+ path_cancel(&pb);
+
+ destroy_tree(nodes);
+ if (nodes != files)
+ destroy_tree(files);
+}
+
+static void
+validate_tree(struct cache_node *expected)
+{
+ validate_trees(expected, expected);
+}
+
+static void
+backtrack_times(struct cache_node *node)
+{
+ struct cache_node *child, *tmp;
+ node->ts_success -= 1000;
+ node->ts_attempt -= 1000;
+ HASH_ITER(hh, node->children, child, tmp)
+ backtrack_times(child);
+}
+
+static void
+__cache_prepare(void)
+{
+ cache_prepare();
+ /* Ensure the old ts_successes and ts_attempts are outdated */
+ backtrack_times(rsync);
+ backtrack_times(https);
+}
+
+/* Tests */
+
+START_TEST(test_cache_download_rsync)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+ dl_error = false;
+
+ cache_prepare();
+
+ download_rsync("rsync://a.b.c/d/e", 0, 1);
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", SUCCESS, 0)))));
+
+ /* Redownload same file, nothing should happen */
+ download_rsync("rsync://a.b.c/d/e", 0, 0);
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", SUCCESS, 0)))));
+
+ /*
+ * For better *and* worse, rsyncs are recursive, which means if we've
+ * been recently asked to download e, we needn't bother redownloading
+ * e/f.
+ */
+ download_rsync("rsync://a.b.c/d/e/f", 0, 0);
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", SUCCESS, 0)))));
+
+ /*
+ * The trees will *look* different, because the tree will get trimmed,
+ * while the filesystem will not.
+ */
+ download_rsync("rsync://a.b.c/d", 0, 1);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", 0, 0))))
+ );
+
+ download_rsync("rsync://a.b.c/e", 0, 1);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", SUCCESS, 0))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", 0, 0)),
+ NODE("e", 0, 0)))
+ );
+
+ download_rsync("rsync://x.y.z/e", 0, 1);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", SUCCESS, 0)),
+ NODE("x.y.z", 0, 0,
+ NODE("e", SUCCESS, 0))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", 0, 0)),
+ NODE("e", 0, 0)),
+ NODE("x.y.z", 0, 0,
+ NODE("e", 0, 0))));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_cache_download_rsync_error)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+
+ cache_prepare();
+
+ dl_error = false;
+ download_rsync("rsync://a.b.c/d", 0, 1);
+ dl_error = true;
+ download_rsync("rsync://a.b.c/e", -EINVAL, 1);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))));
+
+ /* Regardless of error, not reattempted because same iteration */
+ dl_error = true;
+ download_rsync("rsync://a.b.c/e", -EINVAL, 0);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))));
+
+ dl_error = false;
+ download_rsync("rsync://a.b.c/e", -EINVAL, 0);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_cache_cleanup_rsync)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+ dl_error = false;
+
+ /*
+ * First iteration: Tree is created. No prunes, because nothing's
+ * outdated.
+ */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/d", 0, 1);
+ download_rsync("rsync://a.b.c/e", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", SUCCESS, 0))));
+
+ /* One iteration with no changes, for paranoia */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/d", 0, 1);
+ download_rsync("rsync://a.b.c/e", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", SUCCESS, 0))));
+
+ /* Add one sibling */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/d", 0, 1);
+ download_rsync("rsync://a.b.c/e", 0, 1);
+ download_rsync("rsync://a.b.c/f", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", SUCCESS, 0),
+ NODE("f", SUCCESS, 0))));
+
+ /* Remove some branches */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/d", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))));
+
+ /* Remove old branch and add sibling at the same time */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/e", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0))));
+
+ /* Add a child to the same branch, do not update the old one */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/e/f/g", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0,
+ NODE("f", 0, 0,
+ NODE("g", SUCCESS, 0))))));
+
+ /*
+ * Download parent, do not update child.
+ * Child's node should be deleted (because we don't need it anymore),
+ * but its file should persist (because it should be retained as its
+ * parent's descendant).
+ */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/e/f", 0, 1);
+ cache_cleanup();
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0,
+ NODE("f", SUCCESS, 0)))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0,
+ NODE("f", 0, 0,
+ NODE("g", SUCCESS, 0))))));
+
+ /* Do it again. Node should die, all descendant files should persist. */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/e", 0, 1);
+ cache_cleanup();
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0,
+ NODE("f", 0, 0,
+ NODE("g", SUCCESS, 0))))));
+
+ /* Empty the tree */
+ __cache_prepare();
+ cache_cleanup();
+ validate_tree(NODE("rsync", 0, 0));
+
+ /* Node exists, but file doesn't */
+ __cache_prepare();
+ download_rsync("rsync://a.b.c/e", 0, 1);
+ download_rsync("rsync://a.b.c/f/g/h", 0, 1);
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0),
+ NODE("f", 0, 0,
+ NODE("g", 0, 0,
+ NODE("h", SUCCESS, 0))))));
+ ck_assert_int_eq(0, system("rm -rf tmp/rsync/a.b.c/f/g"));
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", SUCCESS, 0))));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_cache_cleanup_rsync_error)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+
+ cache_prepare();
+
+ /* Set up */
+ dl_error = false;
+ download_rsync("rsync://a.b.c/d", 0, 1);
+ dl_error = true;
+ download_rsync("rsync://a.b.c/e", -EINVAL, 1);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))));
+
+ /*
+ * I originally intended this test to delete e because of the error,
+ * but it actually gets deleted because the file doesn't exist.
+ * Which is fine; we should test that too. We'll try d next, which
+ * does have a file.
+ */
+ cache_cleanup();
+ validate_tree(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))));
+
+ /* Fail d */
+ __cache_prepare();
+ dl_error = true;
+ download_rsync("rsync://a.b.c/d", -EINVAL, 1);
+ validate_trees(
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", CNF_DIRECT, -EINVAL))),
+ NODE("rsync", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", SUCCESS, 0))));
+
+ /* Clean up d because of error */
+ cache_cleanup();
+ validate_tree(NODE("rsync", 0, 0));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_cache_download_https)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+ dl_error = false;
+
+ cache_prepare();
+
+ /* Download *file* e. */
+ download_https("https://a.b.c/d/e", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", HTTP_SUCCESS, 0)))));
+
+ /* e is now a dir; need to replace it. */
+ download_https("https://a.b.c/d/e/f", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", 0, 0,
+ NODE("e", 0, 0,
+ NODE("f", HTTP_SUCCESS, 0))))));
+
+ /* d is now a file; need to replace it. */
+ download_https("https://a.b.c/d", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ /* Download something else 1 */
+ download_https("https://a.b.c/e", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0),
+ NODE("e", HTTP_SUCCESS, 0))));
+
+ /* Download something else 2 */
+ download_https("https://x.y.z/e", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0),
+ NODE("e", HTTP_SUCCESS, 0)),
+ NODE("x.y.z", 0, 0,
+ NODE("e", HTTP_SUCCESS, 0))));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_cache_download_https_error)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+
+ cache_prepare();
+
+ dl_error = false;
+ download_https("https://a.b.c/d", 0, 1);
+ dl_error = true;
+ download_https("https://a.b.c/e", -EINVAL, 1);
+ validate_trees(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ /* Regardless of error, not reattempted because same iteration */
+ dl_error = true;
+ download_https("https://a.b.c/e", -EINVAL, 0);
+ validate_trees(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ dl_error = false;
+ download_https("https://a.b.c/e", -EINVAL, 0);
+ validate_trees(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_cache_cleanup_https)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+ dl_error = false;
+
+ /* First iteration; make a tree and clean it */
+ __cache_prepare();
+ download_https("https://a.b.c/d", 0, 1);
+ download_https("https://a.b.c/e", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0),
+ NODE("e", HTTP_SUCCESS, 0))));
+
+ /* Remove one branch */
+ __cache_prepare();
+ download_https("https://a.b.c/d", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ /* Change the one branch */
+ __cache_prepare();
+ download_https("https://a.b.c/e", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", HTTP_SUCCESS, 0))));
+
+ /*
+ * Add a child to the same branch, do not update the old one
+ */
+ __cache_prepare();
+ download_https("https://a.b.c/e/f/g", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", 0, 0,
+ NODE("f", 0, 0,
+ NODE("g", HTTP_SUCCESS, 0))))));
+
+ /*
+ * Download parent, do not update child.
+ * Children need to die, because parent is now a file.
+ */
+ __cache_prepare();
+ download_https("https://a.b.c/e/f", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", 0, 0,
+ NODE("f", HTTP_SUCCESS, 0)))));
+
+ /* Do it again. */
+ __cache_prepare();
+ download_https("https://a.b.c/e", 0, 1);
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", HTTP_SUCCESS, 0))));
+
+ /* Empty the tree */
+ __cache_prepare();
+ cache_cleanup();
+ validate_tree(NODE("https", 0, 0));
+
+ /* Node exists, but file doesn't */
+ __cache_prepare();
+ download_https("https://a.b.c/e", 0, 1);
+ download_https("https://a.b.c/f/g/h", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", HTTP_SUCCESS, 0),
+ NODE("f", 0, 0,
+ NODE("g", 0, 0,
+ NODE("h", HTTP_SUCCESS, 0))))));
+ ck_assert_int_eq(0, system("rm -rf tmp/https/a.b.c/f/g"));
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", HTTP_SUCCESS, 0))));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_cache_cleanup_https_error)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+
+ cache_prepare();
+
+ /* Set up */
+ dl_error = false;
+ download_https("https://a.b.c/d", 0, 1);
+ dl_error = true;
+ download_https("https://a.b.c/e", -EINVAL, 1);
+ validate_trees(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0),
+ NODE("e", CNF_DIRECT, -EINVAL))),
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ /* Deleted because file ENOENT. */
+ cache_cleanup();
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ /* Fail d */
+ __cache_prepare();
+ dl_error = true;
+ download_https("https://a.b.c/d", -EINVAL, 1);
+ validate_trees(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", CNF_DIRECT, -EINVAL))),
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ /* Clean up d because of error */
+ cache_cleanup();
+ validate_tree(NODE("https", 0, 0));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_dots)
+{
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+ dl_error = false;
+
+ cache_prepare();
+
+ download_https("https://a.b.c/d", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ download_https("https://a.b.c/d/.", 0, 0);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("d", HTTP_SUCCESS, 0))));
+
+ download_https("https://a.b.c/d/..", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", HTTP_SUCCESS, 0)));
+
+ download_https("https://a.b.c/./d/../e", 0, 1);
+ validate_tree(
+ NODE("https", 0, 0,
+ NODE("a.b.c", 0, 0,
+ NODE("e", HTTP_SUCCESS, 0))));
+
+ cache_teardown();
+}
+END_TEST
+
+START_TEST(test_metadata_json)
+{
+ const time_t NOW = 1693952610;
+ json_t *json;
+ char *str;
+
+ rsync = TNODE("rsync", 0, NOW + 0, NOW + 1, 0,
+ TNODE("a.b.c", 0, NOW + 2, NOW + 3, 0,
+ TNODE("d", SUCCESS, NOW + 4, NOW + 5, 0),
+ TNODE("e", SUCCESS, NOW + 6, NOW + 7, 0)),
+ TNODE("x.y.z", 0, NOW + 8, NOW + 9, 0,
+ TNODE("w", SUCCESS, NOW + 0, NOW + 1, 0)));
+ https = TNODE("https", 0, NOW + 2, NOW + 3, 0,
+ TNODE("a", 0, NOW + 4, NOW + 5, 0,
+ TNODE("b", HTTP_SUCCESS, NOW + 6, NOW + 7, 0),
+ TNODE("c", HTTP_SUCCESS, NOW + 8, NOW + 9, 0)));
+
+ json = build_metadata_json();
+ ck_assert_int_eq(0, json_dump_file(json, "tmp/metadata.json", JSON_COMPACT));
+
+ str = json_dumps(json, /* JSON_INDENT(4) */ JSON_COMPACT);
+ /* printf("%s\n", str); */
+ json_decref(json);
+
+ ck_assert_str_eq(
+ "[{\"basename\":\"rsync\",\"flags\":0,\"ts_success\":\"2023-09-05T16:23:30-0600\",\"ts_attempt\":\"2023-09-05T16:23:31-0600\",\"error\":0,\"children\":["
+ "{\"basename\":\"a.b.c\",\"flags\":0,\"ts_success\":\"2023-09-05T16:23:32-0600\",\"ts_attempt\":\"2023-09-05T16:23:33-0600\",\"error\":0,\"children\":["
+ "{\"basename\":\"d\",\"flags\":3,\"ts_success\":\"2023-09-05T16:23:34-0600\",\"ts_attempt\":\"2023-09-05T16:23:35-0600\",\"error\":0},"
+ "{\"basename\":\"e\",\"flags\":3,\"ts_success\":\"2023-09-05T16:23:36-0600\",\"ts_attempt\":\"2023-09-05T16:23:37-0600\",\"error\":0}]},"
+ "{\"basename\":\"x.y.z\",\"flags\":0,\"ts_success\":\"2023-09-05T16:23:38-0600\",\"ts_attempt\":\"2023-09-05T16:23:39-0600\",\"error\":0,\"children\":["
+ "{\"basename\":\"w\",\"flags\":3,\"ts_success\":\"2023-09-05T16:23:30-0600\",\"ts_attempt\":\"2023-09-05T16:23:31-0600\",\"error\":0}]}]},"
+ "{\"basename\":\"https\",\"flags\":0,\"ts_success\":\"2023-09-05T16:23:32-0600\",\"ts_attempt\":\"2023-09-05T16:23:33-0600\",\"error\":0,\"children\":["
+ "{\"basename\":\"a\",\"flags\":0,\"ts_success\":\"2023-09-05T16:23:34-0600\",\"ts_attempt\":\"2023-09-05T16:23:35-0600\",\"error\":0,\"children\":["
+ "{\"basename\":\"b\",\"flags\":11,\"ts_success\":\"2023-09-05T16:23:36-0600\",\"ts_attempt\":\"2023-09-05T16:23:37-0600\",\"error\":0},"
+ "{\"basename\":\"c\",\"flags\":11,\"ts_success\":\"2023-09-05T16:23:38-0600\",\"ts_attempt\":\"2023-09-05T16:23:39-0600\",\"error\":0}]}]}]",
+ str);
+ free(str);
+
+ cache_teardown();
+ rsync = https = NULL;
+
+ load_metadata_json();
+ validate_trees(
+ TNODE("rsync", 0, NOW + 0, NOW + 1, 0,
+ TNODE("a.b.c", 0, NOW + 2, NOW + 3, 0,
+ TNODE("d", SUCCESS, NOW + 4, NOW + 5, 0),
+ TNODE("e", SUCCESS, NOW + 6, NOW + 7, 0)),
+ TNODE("x.y.z", 0, NOW + 8, NOW + 9, 0,
+ TNODE("w", SUCCESS, NOW + 0, NOW + 1, 0))),
+ NULL);
+ validate_trees(
+ TNODE("https", 0, NOW + 2, NOW + 3, 0,
+ TNODE("a", 0, NOW + 4, NOW + 5, 0,
+ TNODE("b", HTTP_SUCCESS, NOW + 6, NOW + 7, 0),
+ TNODE("c", HTTP_SUCCESS, NOW + 8, NOW + 9, 0))),
+ NULL);
+}
+END_TEST
+
+/* Boilerplate */
+
+Suite *thread_pool_suite(void)
+{
+ Suite *suite;
+ TCase *rsync , *https, *dot, *meta;
+
+ rsync = tcase_create("rsync");
+ tcase_add_test(rsync, test_cache_download_rsync);
+ tcase_add_test(rsync, test_cache_download_rsync_error);
+ tcase_add_test(rsync, test_cache_cleanup_rsync);
+ tcase_add_test(rsync, test_cache_cleanup_rsync_error);
+
+ https = tcase_create("https");
+ tcase_add_test(https, test_cache_download_https);
+ tcase_add_test(https, test_cache_download_https_error);
+ tcase_add_test(https, test_cache_cleanup_https);
+ tcase_add_test(https, test_cache_cleanup_https_error);
+
+ dot = tcase_create("dot");
+ tcase_add_test(https, test_dots);
+
+ meta = tcase_create("metadata.json");
+ tcase_add_test(https, test_metadata_json);
+
+ suite = suite_create("local-cache");
+ suite_add_tcase(suite, rsync);
+ suite_add_tcase(suite, https);
+ suite_add_tcase(suite, dot);
+ suite_add_tcase(suite, meta);
+
+ return suite;
+}
+
+int main(void)
+{
+ Suite *suite;
+ SRunner *runner;
+ int tests_failed;
+
+ suite = thread_pool_suite();
+
+ runner = srunner_create(suite);
+ srunner_run_all(runner, CK_NORMAL);
+ tests_failed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+
+ return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
--- /dev/null
+#include <check.h>
+#include <stdlib.h>
+
+#define INITIAL_CAPACITY 8
+
+#include "alloc.c"
+#include "mock.c"
+#include "data_structure/path_builder.c"
+
+/* Mocks */
+
+__MOCK_ABORT(uri_get_global, char const *, NULL, struct rpki_uri *uri)
+__MOCK_ABORT(uri_get_global_len, size_t, 0, struct rpki_uri *uri)
+
+/* Tests */
+
+#define CHECK_PB(_len, _capacity, _error) \
+ ck_assert_uint_eq(_len, pb.len); \
+ ck_assert_uint_eq(_capacity, pb.capacity); \
+ ck_assert_int_eq(_error, pb.error)
+
+#define CHECK_RESULTS(expected) \
+ ck_assert_uint_eq(0, path_peek(&pb, &peek_result)); \
+ ck_assert_str_eq(expected, peek_result); \
+ ck_assert_uint_eq(0, path_compile(&pb, &compile_result)); \
+ ck_assert_str_eq(expected, compile_result); \
+ free(compile_result);
+
+#define CHECK_ERROR \
+ ck_assert_uint_eq(EINVAL, path_peek(&pb, &peek_result)); \
+ ck_assert_uint_eq(EINVAL, path_compile(&pb, &compile_result));
+
+START_TEST(test_append)
+{
+ struct path_builder pb;
+ char const *peek_result;
+ char *compile_result;
+
+ path_init(&pb);
+ path_append(&pb, "");
+ CHECK_PB(0, 8, 0);
+ CHECK_RESULTS("");
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ CHECK_RESULTS("a");
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ path_init(&pb);
+ path_append(&pb, "a/b");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ path_init(&pb);
+ path_append(&pb, "a/");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b/");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ /* notes from .h */
+ path_init(&pb);
+ path_append(&pb, "a/");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "/b");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ path_init(&pb);
+ path_append(&pb, "a/");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "/b");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ path_init(&pb);
+ path_append(&pb, "//a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "///");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b////");
+ CHECK_PB(3, 8, 0);
+ path_append(&pb, "/////c//////");
+ CHECK_PB(5, 8, 0);
+ CHECK_RESULTS("a/b/c");
+
+ path_init(&pb);
+ path_append(&pb, "//a///b//c//");
+ CHECK_PB(5, 8, 0);
+ CHECK_RESULTS("a/b/c");
+}
+END_TEST
+
+/* Actually mainly designed to manhandle capacity expansion */
+START_TEST(test_uint)
+{
+ struct path_builder pb;
+ char const *peek_result;
+ char *compile_result;
+
+ path_init(&pb);
+ path_append_uint(&pb, 291);
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("123"); /* hex */
+
+ path_init(&pb);
+ path_append_uint(&pb, 19088743);
+ CHECK_PB(7, 8, 0);
+ CHECK_RESULTS("1234567");
+
+ path_init(&pb);
+ path_append_uint(&pb, 305419896);
+ CHECK_PB(8, 16, 0);
+ CHECK_RESULTS("12345678");
+
+ path_init(&pb);
+ path_append_uint(&pb, 74565);
+ CHECK_PB(5, 8, 0);
+ path_append_uint(&pb, 7);
+ CHECK_PB(7, 8, 0);
+ CHECK_RESULTS("12345/7");
+
+ path_init(&pb);
+ path_append_uint(&pb, 74565);
+ CHECK_PB(5, 8, 0);
+ path_append_uint(&pb, 120);
+ CHECK_PB(8, 16, 0);
+ CHECK_RESULTS("12345/78");
+
+ path_init(&pb);
+ path_append_uint(&pb, 74565);
+ CHECK_PB(5, 8, 0);
+ path_append_uint(&pb, 1929);
+ CHECK_PB(9, 16, 0);
+ CHECK_RESULTS("12345/789");
+}
+END_TEST
+
+START_TEST(test_pop)
+{
+ struct path_builder pb;
+ char const *peek_result;
+ char *compile_result;
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b");
+ CHECK_PB(3, 8, 0);
+ path_pop(&pb, false);
+ CHECK_PB(1, 8, 0);
+ CHECK_RESULTS("a");
+
+ path_init(&pb);
+ path_append(&pb, "abc");
+ CHECK_PB(3, 8, 0);
+ path_append(&pb, "def");
+ CHECK_PB(7, 8, 0);
+ path_pop(&pb, false);
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("abc");
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_pop(&pb, false);
+ CHECK_PB(0, 8, 0);
+ CHECK_RESULTS("");
+
+ path_init(&pb);
+ path_append(&pb, "/a");
+ CHECK_PB(1, 8, 0);
+ path_pop(&pb, false);
+ CHECK_PB(0, 8, 0);
+ CHECK_RESULTS("");
+
+ path_init(&pb);
+ path_pop(&pb, false);
+ CHECK_PB(0, 8, EINVAL);
+ CHECK_ERROR;
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ path_pop(&pb, false);
+ CHECK_PB(0, 8, 0);
+ path_pop(&pb, false);
+ CHECK_PB(0, 8, EINVAL);
+ CHECK_ERROR;
+
+// path_init(&pb);
+// path_append(&pb, "/");
+// CHECK_PB(1, 8, 0);
+// path_pop(&pb);
+// CHECK_PB(0, 8, 0);
+// CHECK_RESULTS("");
+//
+// path_init(&pb);
+// path_append(&pb, "///");
+// CHECK_PB(3, 8, 0);
+// path_pop(&pb);
+// CHECK_PB(2, 8, 0);
+// path_pop(&pb);
+// CHECK_PB(1, 8, 0);
+// path_pop(&pb);
+// CHECK_PB(0, 8, 0);
+// CHECK_RESULTS("");
+}
+END_TEST
+
+START_TEST(test_peek)
+{
+ struct path_builder pb;
+ char const *peek_result;
+
+ /*
+ * Most of path_peek() has already been tested above,
+ * just check it leaves the pb in a stable state.
+ */
+
+ path_init(&pb);
+
+ path_peek(&pb, &peek_result);
+ ck_assert_str_eq("", peek_result);
+
+ path_append(&pb, "a");
+ path_peek(&pb, &peek_result);
+ ck_assert_str_eq("a", peek_result);
+
+ path_append(&pb, "b");
+ path_peek(&pb, &peek_result);
+ ck_assert_str_eq("a/b", peek_result);
+
+ path_pop(&pb, true);
+ path_peek(&pb, &peek_result);
+ ck_assert_str_eq("a", peek_result);
+
+ path_pop(&pb, true);
+ path_peek(&pb, &peek_result);
+ ck_assert_str_eq("", peek_result);
+
+ free(pb.string);
+}
+END_TEST
+
+START_TEST(test_reverse)
+{
+ struct path_builder pb;
+ char const *peek_result;
+ char *compile_result;
+
+ /* 0 components */
+ path_init(&pb);
+ path_reverse(&pb);
+ CHECK_PB(0, 8, 0);
+ CHECK_RESULTS("");
+
+ /* 1 component */
+ path_init(&pb);
+ path_append(&pb, "a");
+ path_reverse(&pb);
+ CHECK_PB(1, 8, 0);
+ CHECK_RESULTS("a");
+
+ /* 2 components */
+ path_init(&pb);
+ path_append(&pb, "a");
+ path_append(&pb, "b");
+ path_reverse(&pb);
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("b/a");
+
+ path_init(&pb);
+ path_append(&pb, "abc");
+ path_append(&pb, "def");
+ path_reverse(&pb);
+ CHECK_PB(7, 8, 0);
+ CHECK_RESULTS("def/abc");
+
+ path_init(&pb);
+ path_append(&pb, "abcd");
+ path_append(&pb, "efgh");
+ path_reverse(&pb);
+ CHECK_PB(9, 16, 0);
+ CHECK_RESULTS("efgh/abcd");
+
+ path_init(&pb);
+ path_append(&pb, "abc");
+ path_append(&pb, "efgh");
+ path_reverse(&pb);
+ CHECK_PB(8, 8, 0);
+ CHECK_RESULTS("efgh/abc");
+
+ path_init(&pb);
+ path_append(&pb, "abcd");
+ path_append(&pb, "fgh");
+ path_reverse(&pb);
+ CHECK_PB(8, 8, 0);
+ CHECK_RESULTS("fgh/abcd");
+
+ /* 3 components */
+ path_init(&pb);
+ path_append(&pb, "abc");
+ path_append(&pb, "def");
+ path_append(&pb, "ghi");
+ path_reverse(&pb);
+ CHECK_PB(11, 16, 0);
+ CHECK_RESULTS("ghi/def/abc");
+
+ path_init(&pb);
+ path_append(&pb, "ab");
+ path_append(&pb, "cde");
+ path_append(&pb, "fghi");
+ path_reverse(&pb);
+ CHECK_PB(11, 16, 0);
+ CHECK_RESULTS("fghi/cde/ab");
+
+ /* 4 components */
+ path_init(&pb);
+ path_append(&pb, "a");
+ path_append(&pb, "b");
+ path_append(&pb, "c");
+ path_append(&pb, "d");
+ path_reverse(&pb);
+ CHECK_PB(7, 8, 0);
+ CHECK_RESULTS("d/c/b/a");
+
+ path_init(&pb);
+ path_append(&pb, "ab");
+ path_append(&pb, "cd");
+ path_append(&pb, "ef");
+ path_append(&pb, "gh");
+ path_reverse(&pb);
+ CHECK_PB(11, 16, 0);
+ CHECK_RESULTS("gh/ef/cd/ab");
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ path_append(&pb, "bcd");
+ path_append(&pb, "efgh");
+ path_append(&pb, "ijklm");
+ path_reverse(&pb);
+ CHECK_PB(16, 16, 0);
+ CHECK_RESULTS("ijklm/efgh/bcd/a");
+
+ path_init(&pb);
+ path_append(&pb, "abcdefghijklmnopq");
+ path_append(&pb, "r");
+ path_append(&pb, "stu");
+ path_append(&pb, "vx");
+ path_reverse(&pb);
+ CHECK_PB(26, 32, 0);
+ CHECK_RESULTS("vx/stu/r/abcdefghijklmnopq");
+}
+END_TEST
+
+START_TEST(test_normalization)
+{
+ struct path_builder pb;
+ char const *peek_result;
+ char *compile_result;
+
+ path_init(&pb);
+ path_append(&pb, ".");
+ CHECK_PB(0, 8, 0);
+ CHECK_RESULTS("");
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, ".");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ path_init(&pb);
+ path_append(&pb, ".");
+ CHECK_PB(0, 8, 0);
+ path_append(&pb, ".");
+ CHECK_PB(0, 8, 0);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, ".");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, ".");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b");
+ CHECK_PB(3, 8, 0);
+ path_append(&pb, ".");
+ CHECK_PB(3, 8, 0);
+ path_append(&pb, ".");
+ CHECK_PB(3, 8, 0);
+ CHECK_RESULTS("a/b");
+
+ path_init(&pb);
+ path_append(&pb, "..");
+ CHECK_PB(0, 8, EINVAL);
+ CHECK_ERROR;
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b");
+ CHECK_PB(3, 8, 0);
+ path_append(&pb, "..");
+ CHECK_PB(1, 8, 0);
+ CHECK_RESULTS("a");
+
+ path_init(&pb);
+ path_append(&pb, "a");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "b");
+ CHECK_PB(3, 8, 0);
+ path_append(&pb, "..");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, ".");
+ CHECK_PB(1, 8, 0);
+ path_append(&pb, "..");
+ CHECK_PB(0, 8, 0);
+ CHECK_RESULTS("");
+
+ /* dot dot injection */
+ path_init(&pb);
+ path_append(&pb, "a/../b");
+ CHECK_PB(1, 8, 0);
+ CHECK_RESULTS("b");
+}
+END_TEST
+
+Suite *
+pdu_suite(void)
+{
+ Suite *suite;
+ TCase *core;
+
+ core = tcase_create("functions");
+ tcase_add_test(core, test_append);
+ tcase_add_test(core, test_uint);
+ tcase_add_test(core, test_pop);
+ tcase_add_test(core, test_peek);
+ tcase_add_test(core, test_reverse);
+ tcase_add_test(core, test_normalization);
+
+ suite = suite_create("path_builder");
+ suite_add_tcase(suite, core);
+ return suite;
+}
+
+int
+main(int argc, char **argv)
+{
+ Suite *suite;
+ SRunner *runner;
+ int tests_failed;
+
+ suite = pdu_suite();
+
+ runner = srunner_create(suite);
+ srunner_run_all(runner, CK_NORMAL);
+ tests_failed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+
+ return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
}
END_TEST
+/*
+ * To assure myself I can hash nodes using an rpki_uri's global field as key.
+ * (Given that they're private.)
+ *
+ * ie. Neither the node nor the key contains the key, but the key points to it
+ * somewhere else.
+ */
+START_TEST(test_uri)
+{
+ struct test2_key {
+ char *outer_string;
+ int something_else;
+ };
+
+ struct test2_node {
+ struct test2_key *key;
+ int value;
+ UT_hash_handle hh;
+ };
+
+ struct test2_node *table = NULL;
+
+ char *keystr;
+ unsigned int keystrlen;
+ struct test2_node *node, *node2;
+
+ /* Try finding a nonexistent node, because paranoia */
+ keystr = strdup("potato");
+ keystrlen = strlen(keystr);
+
+ HASH_FIND(hh, table, keystr, keystrlen, node);
+ ck_assert_ptr_null(node);
+
+ /* Add a node */
+ node = malloc(sizeof(struct test2_node));
+ node->key = malloc(sizeof(struct test2_key));
+ memset(node->key, 0, sizeof(struct test2_key));
+ node->key->outer_string = keystr;
+ node->key->something_else = 1;
+
+ HASH_ADD_KEYPTR(hh, table, keystr, keystrlen, node);
+
+ /* Try finding the node using the same string */
+ node2 = NULL;
+ HASH_FIND(hh, table, keystr, keystrlen, node2);
+ ck_assert_ptr_eq(node, node2);
+
+ /* Try finding the node using a different (but equal) string */
+ keystr = strdup("potato");
+ node2 = NULL;
+ HASH_FIND(hh, table, keystr, keystrlen, node2);
+ ck_assert_ptr_eq(node, node2);
+ free(keystr);
+
+ /* Try finding some other string */
+ keystr = strdup("potato2");
+ keystrlen = strlen(keystr);
+ node2 = NULL;
+ HASH_FIND(hh, table, keystr, keystrlen, node2);
+ ck_assert_ptr_null(node2);
+ free(keystr);
+
+ /* free the hash table contents */
+ HASH_ITER(hh, table, node, node2) {
+ HASH_DEL(table, node);
+ free(node->key->outer_string);
+ free(node->key);
+ free(node);
+ }
+}
+END_TEST
+
Suite *pdu_suite(void)
{
Suite *suite;
- TCase *core;
+ TCase *core, *uri;
core = tcase_create("simple");
tcase_add_test(core, test_replace);
+ uri = tcase_create("uri");
+ tcase_add_test(uri, test_uri);
+
suite = suite_create("uthash");
suite_add_tcase(suite, core);
+ suite_add_tcase(suite, uri);
return suite;
}
+#define _XOPEN_SOURCE 500
+
#include <check.h>
#include <errno.h>
#include <stdlib.h>
#include "mock.h"
+#include <errno.h>
#include <arpa/inet.h>
#include "state.h"
+#include "incidence/incidence.h"
/**
* Some core functions, as linked from unit tests.
va_start(args, format); \
vfprintf(stdout, format, args); \
va_end(args); \
+ printf("\n"); \
} while (0)
#define MOCK_VOID_PRINT(name) \
pr_crit(const char *format, ...)
{
va_list args;
+ fprintf(stderr, "pr_crit() called!\n");
va_start(args, format);
- vfprintf(stdout, format, args);
+ vfprintf(stderr, format, args);
va_end(args);
+ fprintf(stderr, "\n");
ck_abort();
}
MOCK_NULL(config_get_slurm, char const *, void)
MOCK(config_get_tal, char const *, "tal/", void)
-MOCK(config_get_local_repository, char const *, "repository/", void)
-MOCK_FALSE(config_get_shuffle_tal_uris, void)
+MOCK(config_get_local_repository, char const *, "tmp", void)
MOCK(config_get_mode, enum mode, STANDALONE, void)
MOCK_TRUE(config_get_rsync_enabled, void)
MOCK_NULL(config_get_output_roa, char const *, void)
#define MOCK_VOID(name, ...) \
void name(__VA_ARGS__) {}
+#define __MOCK_ABORT_MSG ck_abort_msg("%s() called.", __func__)
+
#define __MOCK_ABORT(name, type, result, ...) \
- type name(__VA_ARGS__) { ck_abort(); return result; }
+ type name(__VA_ARGS__) { __MOCK_ABORT_MSG; return result; }
#define MOCK_ABORT_INT(name, ...) \
__MOCK_ABORT(name, int, 0, __VA_ARGS__)
+/* FIXME delete? */
+#define MOCK_ABORT_BOOL(name, ...) \
+ __MOCK_ABORT(name, bool, false, __VA_ARGS__)
#define MOCK_ABORT_ENUM(name, type, ...) \
__MOCK_ABORT(name, enum type, 0, __VA_ARGS__)
#define MOCK_ABORT_PTR(name, type, ...) \
__MOCK_ABORT(name, struct type *, NULL, __VA_ARGS__)
#define MOCK_ABORT_VOID(name, ...) \
- void name(__VA_ARGS__) { ck_abort(); }
+ void name(__VA_ARGS__) { __MOCK_ABORT_MSG; }
#endif /* TEST_MOCK_H_ */
#include "mock.c"
#include "rrdp/rrdp_objects.c"
+/* Mocks */
+
+MOCK_ABORT_PTR(uri_refget, rpki_uri, struct rpki_uri *uri)
+MOCK_ABORT_VOID(uri_refput, struct rpki_uri *uri)
+
+/* Mocks end */
+
#define END 0xFFFF
static void
struct delta_head delta;
va_list vl;
- doc_data_init(&delta.doc_data);
+ metadata_init(&delta.meta);
va_start(vl, deltas);
while ((delta.serial = va_arg(vl, unsigned long)) != END)
+++ /dev/null
-#include <check.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include "alloc.c"
-#include "common.c"
-#include "mock.c"
-#include "str_token.c"
-#include "types/uri.c"
-#include "rsync/rsync.c"
-
-/* Mocks */
-
-MOCK_NULL(state_retrieve, struct validation *, void)
-MOCK_ABORT_PTR(validation_rsync_visited_uris, uri_list, struct validation *s)
-
-/* Tests */
-
-static void
-__mark_as_downloaded(char *uri_str, struct uri_list *visited_uris)
-{
- struct rpki_uri *uri;
- ck_assert_int_eq(0, uri_create_rsync_str(&uri, uri_str, strlen(uri_str)));
- mark_as_downloaded(uri, visited_uris);
- uri_refput(uri);
-}
-
-static void
-assert_downloaded(char *uri_str, struct uri_list *visited_uris, bool expected)
-{
- struct rpki_uri *uri;
- ck_assert_int_eq(0, uri_create_rsync_str(&uri, uri_str, strlen(uri_str)));
- ck_assert_int_eq(is_already_downloaded(uri, visited_uris), expected);
- uri_refput(uri);
-}
-
-START_TEST(rsync_test_list)
-{
- struct uri_list *visited_uris;
-
- visited_uris = rsync_create();
- ck_assert_ptr_nonnull(visited_uris);
-
- __mark_as_downloaded("rsync://example.foo/repository/", visited_uris);
- __mark_as_downloaded("rsync://example.foo/member_repository/",
- visited_uris);
- __mark_as_downloaded("rsync://example.foz/repository/", visited_uris);
- __mark_as_downloaded("rsync://example.boo/repo/", visited_uris);
- __mark_as_downloaded("rsync://example.potato/rpki/", visited_uris);
-
- assert_downloaded("rsync://example.foo/repository/", visited_uris,
- true);
- assert_downloaded("rsync://example.foo/repository/abc/cdfg",
- visited_uris, false);
- assert_downloaded("rsync://example.foo/member_repository/bca",
- visited_uris, false);
- assert_downloaded("rsync://example.boo/repository/", visited_uris,
- false);
- assert_downloaded("rsync://example.potato/repository/", visited_uris,
- false);
- assert_downloaded("rsync://example.potato/rpki/abc/", visited_uris,
- false);
-
- rsync_destroy(visited_uris);
-}
-END_TEST
-
-Suite *rsync_load_suite(void)
-{
- Suite *suite;
- TCase *uri_list;
-
- uri_list = tcase_create("uriList");
- tcase_add_test(uri_list, rsync_test_list);
-
- suite = suite_create("rsync_test()");
- suite_add_tcase(suite, uri_list);
-
- return suite;
-}
-
-int main(void)
-{
- Suite *suite;
- SRunner *runner;
- int tests_failed;
-
- suite = rsync_load_suite();
-
- runner = srunner_create(suite);
- srunner_run_all(runner, CK_NORMAL);
- tests_failed = srunner_ntests_failed(runner);
- srunner_free(runner);
-
- return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}
unsigned char const *spk, void *arg)
{
uint64_t as;
- int error;
+ int error = 0;
for (as = range->min; as <= range->max; as++) {
error = rtrhandler_handle_router_key(arg, ski, as, spk);
+#define _XOPEN_SOURCE 500 /* nftw() */
+
#include <check.h>
#include <stdbool.h>
#include <stdlib.h>
unsigned int deltas_lifetime = 5;
MOCK_UINT(config_get_deltas_lifetime, deltas_lifetime, void)
+MOCK_VOID(cache_prepare, void)
/* Test functions */
+#define _XOPEN_SOURCE 500
+
#include <check.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/queue.h>
-#include "algorithm.c"
#include "alloc.c"
#include "common.c"
-#include "file.c"
-#include "json_parser.c"
#include "mock.c"
-#include "output_printer.c"
-#include "crypto/base64.c"
#include "types/delta.c"
#include "types/router_key.c"
#include "types/serial.c"
#include "types/vrp.c"
-#include "rtr/pdu.c"
#include "rtr/pdu_handler.c"
-#include "rtr/primitive_reader.c"
#include "rtr/primitive_writer.c"
#include "rtr/err_pdu.c"
#include "rtr/db/delta.c"
#include "rtr/db/db_table.c"
#include "rtr/db/rtr_db_mock.c"
#include "rtr/db/vrps.c"
-#include "slurm/db_slurm.c"
-#include "slurm/slurm_loader.c"
-#include "slurm/slurm_parser.c"
#include "thread/thread_pool.c"
+/* Mocks */
+
+MOCK_ABORT_INT(read_int32, struct pdu_reader *reader, uint32_t *result)
+MOCK_ABORT_INT(read_int8, struct pdu_reader *reader, uint8_t *result)
+
+MOCK_VOID(cache_prepare, void)
+
+MOCK_INT(slurm_apply, 0, struct db_table *base, struct db_slurm **slurm)
+MOCK_ABORT_VOID(db_slurm_destroy, struct db_slurm *db)
+
+MOCK_VOID(output_print_data, struct db_table const *db)
+
+/* Mocks end */
+
struct expected_pdu {
uint8_t pdu_type;
STAILQ_ENTRY(expected_pdu) list_hook;
+#define _XOPEN_SOURCE 500 /* nftw() */
+
#include "object/tal.c"
#include <check.h>
#include "alloc.c"
+#include "common.c"
#include "file.c"
#include "line_file.c"
#include "mock.c"
+#include "data_structure/path_builder.c"
#include "types/uri.c"
#include "crypto/base64.c"
/* Mocks */
-static unsigned int rsync_priority;
-static unsigned int http_priority;
+MOCK_ABORT_VOID(thread_pool_push, struct thread_pool *pool,
+ char const *task_name, thread_pool_task_cb cb, void *arg)
+MOCK_ABORT_VOID(thread_pool_wait, struct thread_pool *pool)
+
+MOCK_ABORT_INT(handle_roa_v4, uint32_t as, struct ipv4_prefix const *prefix,
+ uint8_t max_length, void *arg)
+MOCK_ABORT_INT(handle_roa_v6, uint32_t as, struct ipv6_prefix const *prefix,
+ uint8_t max_length, void *arg)
+MOCK_ABORT_INT(handle_router_key, unsigned char const *ski,
+ struct asn_range const *asns, unsigned char const *spk, void *arg)
-MOCK_UINT(config_get_rsync_priority, rsync_priority, void)
-MOCK_UINT(config_get_http_priority, http_priority, void)
-/* These tests focus on global URIs, so set a dummy value */
-MOCK(map_uri_to_local, char *, pstrdup("dummy"), char const *u, char const *p)
-MOCK_ABORT_VOID(db_rrdp_reset_visited_tals, void)
+MOCK_ABORT_PTR(state_retrieve, validation, void)
+MOCK_ABORT_PTR(validation_get_notification_uri, rpki_uri,
+ struct validation *state)
+
+MOCK_ABORT_VOID(fnstack_init, void)
+MOCK_ABORT_VOID(fnstack_cleanup, void)
+MOCK_ABORT_VOID(fnstack_push, char const *f)
/* Tests */
}
END_TEST
-START_TEST(tal_order_http_first)
-{
- struct tal *tal;
-
- ck_assert_int_eq(tal_load("tal/lacnic.tal", &tal), 0);
-
- http_priority = 60;
- rsync_priority = 50;
- tal_order_uris(tal);
-
- ck_assert_str_eq(tal->uris.array[0]->global, "https://potato");
- ck_assert_str_eq(tal->uris.array[1]->global,
- "rsync://repository.lacnic.net/rpki/lacnic/rta-lacnic-rpki.cer");
- ck_assert_str_eq(tal->uris.array[2]->global, "rsync://potato");
-
- tal_destroy(tal);
-}
-END_TEST
-
-START_TEST(tal_order_http_last)
-{
- struct tal *tal;
-
- ck_assert_int_eq(tal_load("tal/lacnic.tal", &tal), 0);
-
- http_priority = 50;
- rsync_priority = 60;
- tal_order_uris(tal);
-
- ck_assert_str_eq(tal->uris.array[0]->global,
- "rsync://repository.lacnic.net/rpki/lacnic/rta-lacnic-rpki.cer");
- ck_assert_str_eq(tal->uris.array[1]->global, "rsync://potato");
- ck_assert_str_eq(tal->uris.array[2]->global, "https://potato");
-
- tal_destroy(tal);
-}
-END_TEST
-
Suite *tal_load_suite(void)
{
Suite *suite;
- TCase *core, *order;
+ TCase *core;
core = tcase_create("Core");
tcase_add_test(core, tal_load_normal);
- order = tcase_create("Order");
- tcase_add_test(order, tal_order_http_first);
- tcase_add_test(order, tal_order_http_last);
-
suite = suite_create("tal_load()");
suite_add_tcase(suite, core);
- suite_add_tcase(suite, order);
return suite;
}
#include "common.c"
#include "mock.c"
#include "types/uri.c"
+#include "data_structure/path_builder.c"
+
+/* Mocks */
+
+struct rpki_uri *notification;
+
+MOCK(state_retrieve, struct validation *, NULL, void)
+MOCK(validation_get_notification_uri, struct rpki_uri *, notification,
+ struct validation *state)
+
+/* Tests */
+
+START_TEST(test_constructor)
+{
+ struct rpki_uri *uri;
+
+ ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/d/e"));
+ ck_assert_str_eq("https://a.b.c/d/e", uri_get_global(uri));
+ ck_assert_str_eq("https/a.b.c/d/e", uri_get_local(uri));
+ uri_refput(uri);
+
+ ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c"));
+ ck_assert_str_eq("https://a.b.c", uri_get_global(uri));
+ ck_assert_str_eq("https/a.b.c", uri_get_local(uri));
+ uri_refput(uri);
+
+ ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://"));
+
+ ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/d/.."));
+ ck_assert_str_eq("https://a.b.c/d/..", uri_get_global(uri));
+ ck_assert_str_eq("https/a.b.c", uri_get_local(uri));
+ uri_refput(uri);
+
+ ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://a.b.c/.."));
+ ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://a.b.c/d/../.."));
+
+ ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/."));
+ ck_assert_str_eq("https://a.b.c/.", uri_get_global(uri));
+ ck_assert_str_eq("https/a.b.c", uri_get_local(uri));
+ uri_refput(uri);
+
+ ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "rsync://a.b.c/d"));
+ ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, ""));
+ ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://.."));
+ ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://a.b.c/../.."));
+
+ ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "rsync://a.b.c/d"));
+ ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "http://a.b.c/d"));
+ ck_assert_int_eq(ENOTRSYNC, uri_create(&uri, UT_RSYNC, "https://a.b.c/d"));
+}
+END_TEST
#define BUFFER_LEN 128
static uint8_t buffer[BUFFER_LEN];
}
END_TEST
+START_TEST(check_caged)
+{
+ struct rpki_uri *uri;
+
+ ck_assert_int_eq(0, uri_create(¬ification, UT_HTTPS, "https://a.b.c/d/e.xml"));
+ ck_assert_int_eq(0, uri_create(&uri, UT_CAGED, "rsync://x.y.z/v/w.cer"));
+ ck_assert_str_eq("rrdp/https/a.b.c/d/e.xml/rsync/x.y.z/v/w.cer", uri_get_local(uri));
+ uri_refput(uri);
+ uri_refput(notification);
+
+ ck_assert_int_eq(0, uri_create(¬ification, UT_RSYNC, "rsync://a.b.c"));
+ ck_assert_int_eq(0, uri_create(&uri, UT_CAGED, "https://w"));
+ ck_assert_str_eq("rrdp/rsync/a.b.c/https/w", uri_get_local(uri));
+ uri_refput(uri);
+ uri_refput(notification);
+}
+END_TEST
+
Suite *address_load_suite(void)
{
Suite *suite;
TCase *core;
core = tcase_create("Core");
+ tcase_add_test(core, test_constructor);
tcase_add_test(core, check_validate_current_directory);
+ tcase_add_test(core, check_caged);
suite = suite_create("Encoding checking");
suite_add_tcase(suite, core);