max: maximum
min: minimum
msg: message
+notif: Notification
pdu: Protocol Data Unit (RFC 6810)
pp: Publication Point
pr: print
token = strtok_r(NULL, "/", &saveptr);
} while (token != NULL);
goto download;
+ }
- } else if (recursive) {
+ if (recursive) {
if (was_recently_downloaded(child) && !child->error) {
error = 0;
goto end;
}
-
}
node = child;
return error;
}
+static struct cache_node *
+find_uri(struct rpki_uri *uri)
+{
+ char *luri, *token, *saveptr;
+ struct cache_node *parent, *node;
+ bool recursive;
+ struct cache_node *result;
+
+ luri = uri2luri(uri);
+ token = strtok_r(luri, "/", &saveptr);
+ node = NULL;
+ result = NULL;
+
+ switch (uri_get_type(uri)) {
+ case UT_RSYNC:
+ parent = rsync;
+ recursive = true;
+ break;
+ case UT_HTTPS:
+ parent = https;
+ recursive = false;
+ break;
+ default:
+ pr_crit("Unexpected URI type: %d", uri_get_type(uri));
+ }
+
+ if (parent == NULL)
+ goto end;
+
+ while ((token = strtok_r(NULL, "/", &saveptr)) != NULL) {
+ HASH_FIND_STR(parent->children, token, node);
+ if (node == NULL)
+ goto end;
+ if (recursive && (node->flags & CNF_DIRECT))
+ result = node;
+ parent = node;
+ }
+
+ if ((node != NULL) && (node->flags & CNF_DIRECT))
+ result = node;
+
+end:
+ free(luri);
+ return result;
+}
+
+static unsigned int
+get_score(struct cache_node *node)
+{
+ unsigned int score;
+
+ /*
+ * Highest to lowest priority:
+ *
+ * 1. Recent Success: !error, CNF_SUCCESS, high ts_success.
+ * 2. Old Success: !error, CNF_SUCCESS, low ts_success.
+ * 3. Previous Recent Success: error, CNF_SUCCESS, high ts_success.
+ * 4. Previous Old Success: error, CNF_SUCCESS, old ts_success.
+ * 5. No Success: error, !CNF_SUCCESS (completely unviable)
+ */
+
+ if (node == NULL)
+ return 0;
+
+ score = 0;
+ if (!node->error)
+ score |= (1 << 1);
+ if (node->flags & CNF_SUCCESS)
+ score |= (1 << 0);
+ return score;
+}
+
+/*
+ * Returns true if @n1's success happened earlier than n2's.
+ */
+static bool
+earlier_success(struct cache_node *n1, struct cache_node *n2)
+{
+ return difftime(n1->ts_success, n2->ts_success) < 0;
+}
+
+struct rpki_uri *
+cache_recover(struct uri_list *uris, bool use_rrdp)
+{
+ struct scr {
+ struct rpki_uri *uri;
+ struct cache_node *node;
+ unsigned int score;
+ };
+
+ struct rpki_uri **uri;
+ struct scr cursor;
+ struct scr best = { 0 };
+
+ ARRAYLIST_FOREACH(uris, uri) {
+ cursor.uri = *uri;
+ cursor.node = find_uri(cursor.uri);
+ cursor.score = get_score(cursor.node);
+ if (cursor.score == 0)
+ continue;
+ if (cursor.score > best.score)
+ best = cursor;
+ else if (cursor.score == best.score
+ && earlier_success(best.node, cursor.node))
+ best = cursor;
+ }
+
+ return best.uri;
+}
+
+static void
+__cache_print(struct cache_node *node, unsigned int tabs)
+{
+ unsigned int i;
+ struct cache_node *child, *tmp;
+
+ if (node == NULL)
+ return;
+
+ for (i = 0; i < tabs; i++)
+ printf("\t");
+ printf("%s: %sdirect %ssuccess %sfile error:%d\n",
+ node->basename,
+ (node->flags & CNF_DIRECT) ? "" : "!",
+ (node->flags & CNF_SUCCESS) ? "" : "!",
+ (node->flags & CNF_FILE) ? "" : "!",
+ node->error);
+ HASH_ITER(hh, node->children, child, tmp)
+ __cache_print(child, tabs + 1);
+}
+
+void
+cache_print(void)
+{
+ __cache_print(rsync, 0);
+ __cache_print(https, 0);
+}
+
/*
* @force: ignore nonexistent files
*/
/* Downloads @uri into the cache */
int cache_download(struct rpki_uri *uri, bool *);
+/* Returns the most recent successfully cached URI of the list */
+struct rpki_uri *cache_recover(struct uri_list *, bool);
+/* Prints the cache in standard output. */
+void cache_print(void);
/* Deletes old untraversed cached files, writes metadata into XML */
/* FIXME call this */
}
bool
-valid_file_or_dir(char const *location, bool check_file, bool check_dir,
- int (*error_fn)(const char *format, ...))
+valid_file_or_dir(char const *location, bool check_file)
{
struct stat attr;
bool is_file, is_dir;
bool result;
- if (!check_file && !check_dir)
- pr_crit("Wrong usage, at least one check must be 'true'.");
-
if (stat(location, &attr) == -1) {
- if (error_fn != NULL) {
- error_fn("stat(%s) failed: %s", location,
- strerror(errno));
- }
+ pr_op_err("stat(%s) failed: %s", location, strerror(errno));
return false;
}
is_file = check_file && S_ISREG(attr.st_mode);
- is_dir = check_dir && S_ISDIR(attr.st_mode);
+ is_dir = S_ISDIR(attr.st_mode);
result = is_file || is_dir;
if (!result)
pr_op_err("'%s' does not seem to be a %s", location,
- (check_file && check_dir) ? "file or directory" :
- (check_file) ? "file" : "directory");
+ check_file ? "file or directory" : "directory");
return result;
}
#include <sys/stat.h>
#include <sys/types.h>
-/* "I think that this is not supposed to be implemented." */
+/* "I think this is not supposed to be implemented." */
#define ENOTSUPPORTED 3172
/* "I haven't implemented this yet." */
#define ENOTIMPLEMENTED 3173
-/*
- * "URI was not RSYNC; ignore it."
- * Not really an error. The RFCs usually declare URI lists; usually only one of
- * them is required to be RSYNC and the others should be skipped (until we
- * start supporting them.)
- */
+/* "URI was not RSYNC." */
#define ENOTRSYNC 3174
-/*
- * "URI was not HTTPS; ignore it."
- * Not necessarily an error (just as ENOTRSYNC), since both type of URIs can
- * still coexist in most scenarios.
- */
+/* "URI was not HTTPS." */
#define ENOTHTTPS 3175
-/*
- * A request made to a server (eg. rsync, http) has failed, even after retrying
- */
-#define EREQFAILED 3176
-
/*
* If you're wondering why I'm not using -abs(error), it's because abs(INT_MIN)
* overflows, so gcc complains sometimes.
typedef int (*foreach_file_cb)(char const *, void *);
int foreach_file(char const *, char const *, bool, foreach_file_cb, void *);
-typedef int (*pr_errno_cb)(const char *, ...);
-bool valid_file_or_dir(char const *, bool, bool, pr_errno_cb);
+bool valid_file_or_dir(char const *, bool);
int create_dir_recursive(char const *, bool);
int delete_dir_recursive_bottom_up(char const *);
return pr_op_err("The TAL(s) location (--tal) is mandatory.");
/* A file location at --tal isn't valid when --init-tals is set */
- if (!valid_file_or_dir(rpki_config.tal, !rpki_config.init_tals, true,
- pr_op_err))
+ if (!valid_file_or_dir(rpki_config.tal, !rpki_config.init_tals))
return pr_op_err("Invalid TAL(s) location.");
/* Ignore the other checks */
!valid_output_file(rpki_config.output.bgpsec))
return pr_op_err("Invalid output.bgpsec file.");
- if (rpki_config.slurm != NULL &&
- !valid_file_or_dir(rpki_config.slurm, true, true, pr_op_err))
+ if (rpki_config.slurm != NULL && !valid_file_or_dir(rpki_config.slurm, true))
return pr_op_err("Invalid slurm location.");
return 0;
list->array[list->len - 1] = *elem; \
}
-#define ARRAY_LIST(name, elem_type) \
- DEFINE_ARRAY_LIST_STRUCT(name, elem_type); \
- DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type, )
-
#define STATIC_ARRAY_LIST(name, elem_type) \
DEFINE_ARRAY_LIST_STRUCT(name, elem_type); \
DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type, static)
{
/* This is the same logic from CURL, according to its documentation. */
if (http_code == 408 || http_code == 429)
- return EREQFAILED; /* Retry */
+ return EAGAIN; /* Retry */
if (500 <= http_code && http_code < 600)
- return EREQFAILED; /* Retry */
+ return EAGAIN; /* Retry */
return -EINVAL; /* Do not retry */
}
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_COULDNT_RESOLVE_PROXY:
case CURLE_FTP_ACCEPT_TIMEOUT:
- error = EREQFAILED; /* Retry */
+ error = EAGAIN; /* Retry */
goto end;
default:
error = handle_http_response_code(http_code);
pr_val_debug("Download successful.");
return 0; /* Happy path */
- case EREQFAILED:
+ case EAGAIN:
break;
default:
if (r >= config_get_http_retry_count()) {
pr_val_debug("Download failed: Retries exhausted.");
- return -EREQFAILED;
+ return EIO;
}
pr_val_warn("Download failed; retrying in %u seconds.",
OCTET_STRING_t *sid;
};
-STATIC_ARRAY_LIST(sia_rpp_uris, struct rpki_uri *)
-
struct sia_uris {
- struct sia_rpp_uris rpp;
+ struct uri_list rpp;
struct rpki_uri *mft;
};
static void
sia_uris_init(struct sia_uris *uris)
{
- sia_rpp_uris_init(&uris->rpp);
+ uris_init(&uris->rpp);
uris->mft = NULL;
}
-static void
-cleanup_uri(struct rpki_uri **uri)
-{
- uri_refput(*uri);
-}
-
static void
sia_uris_cleanup(struct sia_uris *uris)
{
- sia_rpp_uris_cleanup(&uris->rpp, cleanup_uri);
+ uris_cleanup(&uris->rpp);
uri_refput(uris->mft);
}
{
struct sia_uris *uris = arg;
pr_val_debug("caRepository: %s", uri_val_get_printable(uri));
- sia_rpp_uris_add(&uris->rpp, &uri);
+ uris_add(&uris->rpp, uri);
uri_refget(uri);
return 0;
}
{
struct sia_uris *uris = arg;
pr_val_debug("rpkiNotify: %s", uri_val_get_printable(uri));
- sia_rpp_uris_add(&uris->rpp, &uri);
+ uris_add(&uris->rpp, uri);
uri_refget(uri);
return 0;
}
* 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,
+ return __uri_create(uri, type, NULL,
ASN1_STRING_get0_data(asn1_string),
ASN1_STRING_length(asn1_string));
}
return 0;
}
-static bool
-try_uris(struct sia_uris *uris, enum uri_type const *filter)
-{
- struct rpki_uri **node, *uri;
- enum uri_type type;
-
- ARRAYLIST_FOREACH(&uris->rpp, node) {
- uri = *node;
- type = uri_get_type(uri);
-
- if (filter != NULL && (*filter) != type)
- continue;
-
- switch (type) {
- case UT_RSYNC:
- if (cache_download(uri, NULL) == 0)
- return true;
- break;
- case UT_HTTPS:
- if (rrdp_update(uri) == 0)
- return true;
- break;
- default:
- pr_crit("Unknown URI type: %u", type);
- }
- }
-
- return false;
-}
-
-static int
+static struct rpki_uri *
download_rpp(struct sia_uris *uris)
{
- static const enum uri_type HTTP = UT_HTTPS;
- static const enum uri_type RSYNC = UT_RSYNC;
-
- if (uris->rpp.len == 0)
- return pr_val_err("SIA lacks both caRepository and rpkiNotify.");
-
- if (config_get_http_priority() > config_get_rsync_priority()) {
- if (try_uris(uris, &HTTP))
- return 0;
- if (try_uris(uris, &RSYNC))
- return 0;
-
- } else if (config_get_http_priority() < config_get_rsync_priority()) {
- if (try_uris(uris, &RSYNC))
- return 0;
- if (try_uris(uris, &HTTP))
- return 0;
-
- } else {
- if (try_uris(uris, NULL))
- return 0;
+ if (uris->rpp.len == 0) {
+ pr_val_err("SIA lacks both caRepository and rpkiNotify.");
+ return NULL;
}
- return pr_val_err("The RPP could not be downloaded.");
+ return uris_download(&uris->rpp, true);
}
/** Boilerplate code for CA certificate validation and recursive traversal. */
STACK_OF(X509_CRL) *rpp_parent_crl;
X509 *cert;
struct sia_uris sia_uris;
+ struct rpki_uri *downloaded;
enum rpki_policy policy;
enum cert_type certype;
struct rpp *pp;
if (error)
goto revert_uris;
- error = download_rpp(&sia_uris);
- if (error)
+ downloaded = download_rpp(&sia_uris);
+ if (downloaded == NULL) {
+ error = EINVAL;
goto revert_uris;
+ }
- error = x509stack_push(validation_certstack(state), cert_uri,
- cert, policy, certype);
+ 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, &pp);
+ error = handle_manifest(sia_uris.mft,
+ (uri_get_type(downloaded) == UT_HTTPS) ? downloaded : NULL,
+ &pp);
if (error) {
x509stack_cancel(validation_certstack(state));
goto revert_uris;
rpp_refput(pp);
revert_uris:
- validation_set_notification_uri(state, NULL);
sia_uris_cleanup(&sia_uris);
revert_cert:
if (cert != NULL)
#include "object/signed_object.h"
static int
-cage(struct rpki_uri **uri)
+cage(struct rpki_uri **uri, struct rpki_uri *notif)
{
- if (validation_get_notification_uri(state_retrieve()) == NULL) {
+ if (notif == NULL) {
/* No need to cage */
uri_refget(*uri);
return 0;
}
- return __uri_create(uri, UT_CAGED, uri_get_global(*uri),
+ return __uri_create(uri, UT_CAGED, notif, uri_get_global(*uri),
uri_get_global_len(*uri));
}
}
static int
-build_rpp(struct Manifest *mft, struct rpki_uri *mft_uri, struct rpp **pp)
+build_rpp(struct Manifest *mft, struct rpki_uri *notif,
+ struct rpki_uri *mft_uri, struct rpp **pp)
{
int i;
struct FileAndHash *fah;
for (i = 0; i < mft->fileList.list.count; i++) {
fah = mft->fileList.list.array[i];
- error = uri_create_mft(&uri, mft_uri, &fah->file);
+ error = uri_create_mft(&uri, notif, mft_uri, &fah->file);
/*
* Not handling ENOTRSYNC is fine because the manifest URL
* should have been RSYNC. Something went wrong if an RSYNC URL
* @pp. If @rrdp_workspace is true, use the local RRDP repository.
*/
int
-handle_manifest(struct rpki_uri *uri, struct rpp **pp)
+handle_manifest(struct rpki_uri *uri, struct rpki_uri *notif, struct rpp **pp)
{
static OID oid = OID_MANIFEST;
struct oid_arcs arcs = OID2ARCS("manifest", oid);
int error;
/* Prepare */
- error = cage(&uri); /* ref++ */
+ error = cage(&uri, notif); /* ref++ */
if (error)
return error;
pr_val_debug("Manifest '%s' {", uri_val_get_printable(uri));
goto revert_sobj;
/* Initialize out parameter (@pp) */
- error = build_rpp(mft, uri, pp);
+ error = build_rpp(mft, notif, uri, pp);
if (error)
goto revert_manifest;
#include "rpp.h"
-int handle_manifest(struct rpki_uri *, struct rpp **);
+int handle_manifest(struct rpki_uri *, struct rpki_uri *, struct rpp **);
#endif /* SRC_OBJECT_MANIFEST_H_ */
typedef int (*foreach_uri_cb)(struct tal *, struct rpki_uri *, void *);
-struct uris {
- struct rpki_uri **array; /* This is an array of rpki URIs. */
- unsigned int count;
- unsigned int size;
-};
-
struct tal {
char const *file_name;
- struct uris uris;
+ struct uri_list uris;
unsigned char *spki; /* Decoded; not base64. */
size_t spki_len;
};
struct validation_thread {
pthread_t pid;
-
/* TAL file name */
char *tal_file;
- /*
- * Try to use the TA from the local cache? Only if none of the URIs
- * was sync'd.
- */
- bool retry_local;
- /* Try to sync the current TA URI? */
- bool sync_files;
struct db_table *db;
int exit_status;
/* This should also only be manipulated by the parent thread. */
struct threads_list threads;
};
-static void
-uris_init(struct uris *uris)
-{
- uris->count = 0;
- uris->size = 4; /* Most TALs only define one. */
- uris->array = pmalloc(uris->size * sizeof(struct rpki_uri *));
-}
-
-static void
-uris_destroy(struct uris *uris)
-{
- unsigned int i;
- for (i = 0; i < uris->count; i++)
- uri_refput(uris->array[i]);
- free(uris->array);
-}
-
static int
-uris_add(struct uris *uris, char *uri)
+add_uri(struct uri_list *uris, char *uri)
{
struct rpki_uri *new;
int error;
if (str_starts_with(uri, "rsync://"))
- error = uri_create(&new, UT_RSYNC, uri);
+ error = uri_create(&new, UT_RSYNC, NULL, uri);
else if (str_starts_with(uri, "https://"))
- error = uri_create(&new, UT_HTTPS, uri);
+ error = uri_create(&new, UT_HTTPS, NULL, uri);
else
error = pr_op_err("TAL has non-RSYNC/HTTPS URI: %s", uri);
if (error)
return error;
- if (uris->count + 1 >= uris->size) {
- uris->size *= 2;
- uris->array = realloc(uris->array,
- uris->size * sizeof(struct rpki_uri *));
- }
-
- uris->array[uris->count++] = new;
+ uris_add(uris, new);
return 0;
}
static int
-read_uris(struct line_file *lfile, struct uris *uris)
+read_uris(struct line_file *lfile, struct uri_list *uris)
{
char *uri;
int error;
}
do {
- error = uris_add(uris, uri);
+ error = add_uri(uris, uri);
free(uri); /* Won't be needed anymore */
if (error)
return error;
return 0;
fail:
- uris_destroy(&tal->uris);
+ uris_cleanup(&tal->uris);
free(tal);
lfile_close(lfile);
return error;
if (tal == NULL)
return;
- uris_destroy(&tal->uris);
+ uris_cleanup(&tal->uris);
free(tal->spki);
free(tal);
}
-static int
-foreach(enum uri_type const *filter, struct tal *tal,
- foreach_uri_cb cb, void *arg)
-{
- struct rpki_uri *uri;
- unsigned int i;
- int error;
-
- for (i = 0; i < tal->uris.count; i++) {
- uri = tal->uris.array[i];
- if (filter == NULL || (*filter) == uri_get_type(uri)) {
- error = cb(tal, uri, arg);
- if (error)
- return error;
- }
- }
-
- return 0;
-}
-
-static int
-foreach_uri(struct tal *tal, foreach_uri_cb cb, void *arg)
-{
- static const enum uri_type HTTP = UT_HTTPS;
- static const enum uri_type RSYNC = UT_RSYNC;
- int error;
-
- if (config_get_http_priority() > config_get_rsync_priority()) {
- error = foreach(&HTTP, tal, cb, arg);
- if (!error)
- error = foreach(&RSYNC, tal, cb, arg);
-
- } else if (config_get_http_priority() < config_get_rsync_priority()) {
- error = foreach(&RSYNC, tal, cb, arg);
- if (!error)
- error = foreach(&HTTP, tal, cb, arg);
-
- } else {
- error = foreach(NULL, tal, cb, arg);
-
- }
-
- return error;
-}
-
char const *
tal_get_file_name(struct tal *tal)
{
* have been extracted from a TAL.
*/
static int
-handle_tal_uri(struct tal *tal, struct rpki_uri *uri, void *arg)
+handle_tal_uri(struct tal *tal, struct rpki_uri *uri, struct db_table *db)
{
/*
* Because of the way the foreach iterates, this function must return
*/
struct validation_handler validation_handler;
- struct validation_thread *thread;
struct validation *state;
struct cert_stack *certstack;
struct deferred_cert deferred;
int error;
- thread = arg;
+ pr_val_debug("TAL URI '%s' {", uri_val_get_printable(uri));
validation_handler.handle_roa_v4 = handle_roa_v4;
validation_handler.handle_roa_v6 = handle_roa_v6;
validation_handler.handle_router_key = handle_router_key;
- validation_handler.arg = thread->db;
+ validation_handler.arg = db;
error = validation_prepare(&state, tal, &validation_handler);
if (error)
return ENSURE_NEGATIVE(error);
- if (thread->sync_files) {
- error = cache_download(uri, NULL);
- /* Reminder: there's a positive error: EREQFAILED */
- if (error) {
- validation_destroy(state);
- return pr_val_warn(
- "TAL URI '%s' could not be downloaded.",
- uri_val_get_printable(uri));
- }
- } else {
- /* Look for local files */
- if (!valid_file_or_dir(uri_get_local(uri), true, false,
- pr_val_err)) {
- validation_destroy(state);
- return 0; /* Error already logged */
- }
- }
-
- /* At least one URI was sync'd */
- thread->retry_local = false;
-
- pr_val_debug("TAL URI '%s' {", uri_val_get_printable(uri));
-
if (!uri_is_certificate(uri)) {
error = pr_op_err("TAL URI does not point to a certificate. (Expected .cer, got '%s')",
uri_op_get_printable(uri));
{
struct validation_thread *thread = arg;
struct tal *tal;
+ struct rpki_uri *ta_uri;
int error;
fnstack_init();
if (error)
goto end;
- error = foreach_uri(tal, handle_tal_uri, thread);
- if (error > 0) {
- error = 0;
- goto destroy_tal;
- } else if (error < 0) {
- goto destroy_tal;
- }
-
- if (!thread->retry_local) {
+ ta_uri = uris_download(&tal->uris, false);
+ if (ta_uri == NULL) {
error = pr_op_err("None of the URIs of the TAL '%s' yielded a successful traversal.",
thread->tal_file);
goto destroy_tal;
}
- thread->sync_files = false;
- pr_val_warn("Looking for the TA certificate at the local files.");
-
- error = foreach_uri(tal, handle_tal_uri, thread);
- if (error > 0)
- error = 0;
- else if (error == 0)
- error = pr_op_err("None of the URIs of the TAL '%s' yielded a successful traversal.",
- thread->tal_file);
+ error = handle_tal_uri(tal, ta_uri, thread->db);
destroy_tal:
tal_destroy(tal);
thread = pmalloc(sizeof(struct validation_thread));
thread->tal_file = pstrdup(tal_file);
- thread->retry_local = true;
- thread->sync_files = true;
thread->db = param->db;
thread->exit_status = -EINTR;
SLIST_INSERT_HEAD(¶m->threads, thread, next);
#include "object/ghostbusters.h"
#include "object/roa.h"
-STATIC_ARRAY_LIST(uris, struct rpki_uri *)
-
/** A Repository Publication Point (RFC 6481), as described by some manifest. */
struct rpp {
- struct uris certs; /* Certificates */
+ struct uri_list certs; /* Certificates */
/*
* uri NULL implies stack NULL and error 0.
/* The Manifest is not needed for now. */
- struct uris roas; /* Route Origin Attestations */
+ struct uri_list roas; /* Route Origin Attestations */
- struct uris ghostbusters;
+ struct uri_list ghostbusters;
/*
* Note that the reference counting functions are not prepared for
pp->references++;
}
-static void
-__uri_refput(struct rpki_uri **uri)
-{
- uri_refput(*uri);
-}
-
void
rpp_refput(struct rpp *pp)
{
pp->references--;
if (pp->references == 0) {
- uris_cleanup(&pp->certs, __uri_refput);
+ uris_cleanup(&pp->certs);
if (pp->crl.uri != NULL)
uri_refput(pp->crl.uri);
if (pp->crl.stack != NULL)
sk_X509_CRL_pop_free(pp->crl.stack, X509_CRL_free);
- uris_cleanup(&pp->roas, __uri_refput);
- uris_cleanup(&pp->ghostbusters, __uri_refput);
+ uris_cleanup(&pp->roas);
+ uris_cleanup(&pp->ghostbusters);
free(pp);
}
}
void
rpp_add_cert(struct rpp *pp, struct rpki_uri *uri)
{
- uris_add(&pp->certs, &uri);
+ uris_add(&pp->certs, uri);
}
/** Steals ownership of @uri. */
void
rpp_add_roa(struct rpp *pp, struct rpki_uri *uri)
{
- uris_add(&pp->roas, &uri);
+ uris_add(&pp->roas, uri);
}
/** Steals ownership of @uri. */
void
rpp_add_ghostbusters(struct rpp *pp, struct rpki_uri *uri)
{
- uris_add(&pp->ghostbusters, &uri);
+ uris_add(&pp->ghostbusters, uri);
}
/** Steals ownership of @uri. */
*result = notification.meta;
memset(¬ification.meta, 0, sizeof(notification.meta));
- update_notification_destroy(¬ification);
+ update_notification_cleanup(¬ification);
return 0;
}
bool changed;
int error;
- if (uri == NULL || !uri_is_https(uri))
- pr_crit("Wrong call, trying to parse a non HTTPS URI");
-
- /*
- * TODO (fine) this is dirty and error prone.
- * Find a better way to deliver the notification URI to the RRDP objects
- * and manifest.
- */
- validation_set_notification_uri(state_retrieve(), uri);
-
fnstack_push_uri(uri);
pr_val_debug("Processing notification.");
pr_val_debug("The Notification changed, but the session ID and serial didn't.");
revert_notification:
- update_notification_destroy(&new);
+ update_notification_cleanup(&new);
end: notification_metadata_cleanup(&old);
fnstack_pop();
metadata_cleanup(struct file_metadata *meta)
{
free(meta->hash);
- free(meta->uri);
+ uri_refput(meta->uri);
}
/* Do the @cb to the delta head elements from @from_serial to @max_serial */
}
void
-update_notification_init(struct update_notification *notification,
+update_notification_init(struct update_notification *notif,
struct rpki_uri *uri)
{
- notification_metadata_init(¬ification->meta);
- metadata_init(¬ification->snapshot);
- deltas_head_init(¬ification->deltas_list);
- notification->uri = uri_refget(uri);
+ notification_metadata_init(¬if->meta);
+ metadata_init(¬if->snapshot);
+ deltas_head_init(¬if->deltas_list);
+ notif->uri = uri_refget(uri);
}
static void
}
void
-update_notification_destroy(struct update_notification *file)
+update_notification_cleanup(struct update_notification *file)
{
metadata_cleanup(&file->snapshot);
notification_metadata_cleanup(&file->meta);
free(file);
}
-struct publish *
-publish_create(void)
+void
+publish_init(struct publish *tag)
{
- struct publish *tmp;
-
- tmp = pmalloc(sizeof(struct publish));
- metadata_init(&tmp->meta);
- tmp->content = NULL;
- tmp->content_len = 0;
-
- return tmp;
+ metadata_init(&tag->meta);
+ tag->content = NULL;
+ tag->content_len = 0;
}
void
-publish_destroy(struct publish *file)
+publish_cleanup(struct publish *tag)
{
- metadata_cleanup(&file->meta);
- free(file->content);
- free(file);
+ metadata_cleanup(&tag->meta);
+ free(tag->content);
}
-struct withdraw *
-withdraw_create(void)
+void
+withdraw_init(struct withdraw *tag)
{
- struct withdraw *tmp;
-
- tmp = pmalloc(sizeof(struct withdraw));
- metadata_init(&tmp->meta);
-
- return tmp;
+ metadata_init(&tag->meta);
}
void
-withdraw_destroy(struct withdraw *file)
+withdraw_cleanup(struct withdraw *tag)
{
- metadata_cleanup(&file->meta);
- free(file);
+ metadata_cleanup(&tag->meta);
}
/* Specific RRDP files data, in some cases the hash can be omitted */
struct file_metadata {
- char *uri;
+ struct rpki_uri *uri;
unsigned char *hash;
size_t hash_len;
};
void metadata_cleanup(struct file_metadata *);
void update_notification_init(struct update_notification *, struct rpki_uri *);
-void update_notification_destroy(struct update_notification *);
+void update_notification_cleanup(struct update_notification *);
typedef int (*delta_head_cb)(struct delta_head *, void *);
int deltas_head_for_each(struct deltas_head *, unsigned long, unsigned long,
struct delta *delta_create(void);
void delta_destroy(struct delta *);
-struct publish *publish_create(void);
-void publish_destroy(struct publish *);
+void publish_init(struct publish *);
+void publish_cleanup(struct publish *);
-struct withdraw *withdraw_create(void);
-void withdraw_destroy(struct withdraw *);
+void withdraw_init(struct withdraw *);
+void withdraw_cleanup(struct withdraw *);
#endif /* SRC_RRDP_RRDP_OBJECTS_H_ */
/* Data being parsed */
struct snapshot *snapshot;
/* Parent data to validate session ID and serial */
- struct update_notification *notification;
+ struct update_notification *notif;
};
/* Context while reading a delta */
/* Data being parsed */
struct delta *delta;
/* Parent data to validate session ID */
- struct update_notification *notification;
+ struct update_notification *notif;
/* Current serial loaded from update notification deltas list */
unsigned long expected_serial;
};
* 2. "hash" (optional, depending on @hr)
*/
static int
-parse_doc_data(xmlTextReaderPtr reader, hash_requirement hr,
- struct file_metadata *data)
+parse_doc_data(xmlTextReaderPtr reader, struct rpki_uri *notif,
+ hash_requirement hr, struct file_metadata *data)
{
- char *uri;
+ char *uri_str;
+ struct rpki_uri *uri;
unsigned char *hash;
size_t hash_len;
int error;
+ uri_str = NULL;
uri = NULL;
hash = NULL;
hash_len = 0;
- error = parse_string(reader, RRDP_ATTR_URI, &uri);
+ error = parse_string(reader, RRDP_ATTR_URI, &uri_str);
+ if (error)
+ return error;
+ error = uri_create(&uri, (notif != NULL) ? UT_CAGED : UT_HTTPS, notif,
+ uri_str);
+ free(uri_str);
if (error)
return error;
}
static int
-parse_publish(xmlTextReaderPtr reader, hash_requirement hr,
- struct publish **publish)
+parse_publish(xmlTextReaderPtr reader, struct rpki_uri *notif,
+ hash_requirement hr, struct publish *tag)
{
- struct publish *result;
- struct rpki_uri *uri;
char *base64_str;
int error;
- result = publish_create();
+ publish_init(tag);
- error = parse_doc_data(reader, hr, &result->meta);
+ error = parse_doc_data(reader, notif, hr, &tag->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'",
- result->meta.uri);
+ uri_get_global(tag->meta.uri));
goto release_tmp;
}
if (error)
goto release_tmp;
- error = base64_read(base64_str, &result->content, &result->content_len);
+ error = base64_read(base64_str, &tag->content, &tag->content_len);
if (error)
goto release_base64;
/* rfc8181#section-2.2 but considering optional hash */
- uri = NULL;
- if (result->meta.hash_len > 0) {
- /* Get the current file from the uri */
- error = uri_create(&uri, UT_CAGED, result->meta.uri);
- if (error)
- goto release_base64;
+ if (tag->meta.hash_len > 0) {
+ /*
+ * FIXME How come you're checking the hash of the file?
+ * You haven't written the file yet.
+ */
- error = hash_validate_file(uri, result->meta.hash,
- result->meta.hash_len);
- uri_refput(uri);
- if (error != 0) {
+ /* Get the current file from the uri */
+ error = hash_validate_file(tag->meta.uri, tag->meta.hash,
+ tag->meta.hash_len);
+ if (error) {
pr_val_info("Hash of base64 decoded element from URI '%s' doesn't match <publish> element hash",
- result->meta.uri);
+ uri_get_global(tag->meta.uri));
error = EINVAL;
goto release_base64;
}
}
free(base64_str);
- *publish = result;
return 0;
release_base64:
free(base64_str);
release_tmp:
- publish_destroy(result);
+ publish_cleanup(tag);
return error;
}
static int
-parse_withdraw(xmlTextReaderPtr reader, struct withdraw **withdraw)
+parse_withdraw(xmlTextReaderPtr reader, struct rpki_uri *notif,
+ struct withdraw *tag)
{
- struct withdraw *tmp;
- struct rpki_uri *uri;
int error;
- tmp = withdraw_create();
+ withdraw_init(tag);
- error = parse_doc_data(reader, HR_MANDATORY, &tmp->meta);
+ error = parse_doc_data(reader, notif, HR_MANDATORY, &tag->meta);
if (error)
- goto release_tmp;
-
- /* rfc8181#section-2.2, get the file from the uri */
- error = uri_create(&uri, UT_CAGED, tmp->meta.uri);
- if (error)
- goto release_tmp;
+ goto fail;
- error = hash_validate_file(uri, tmp->meta.hash,
- tmp->meta.hash_len);
+ error = hash_validate_file(tag->meta.uri, tag->meta.hash,
+ tag->meta.hash_len);
if (error)
- goto release_uri;
+ goto fail;
- uri_refput(uri);
- *withdraw = tmp;
return 0;
-release_uri:
- uri_refput(uri);
-release_tmp:
- withdraw_destroy(tmp);
+
+fail:
+ withdraw_cleanup(tag);
return error;
}
static int
-write_from_uri(char const *location, unsigned char *content, size_t content_len)
+write_from_uri(struct rpki_uri *uri, unsigned char *content, size_t content_len)
{
- struct rpki_uri *uri;
FILE *out;
size_t written;
int error;
- /* rfc8181#section-2.2 must be an rsync URI */
- error = uri_create(&uri, UT_CAGED, location);
- if (error)
- return error;
-
- /* pr_val_debug("Expanding %s.", uri_get_local(uri)); */
-
error = create_dir_recursive(uri_get_local(uri), false);
- if (error) {
- uri_refput(uri);
+ if (error)
return error;
- }
error = file_write(uri_get_local(uri), &out);
- if (error) {
- uri_refput(uri);
+ if (error)
return error;
- }
written = fwrite(content, sizeof(unsigned char), content_len, out);
if (written != content_len) {
- uri_refput(uri);
file_close(out);
- return pr_val_err("Couldn't write bytes to file %s",
- uri_get_local(uri));
+ return pr_val_err(
+ "Couldn't write file '%s' (error code not available)",
+ uri_get_local(uri)
+ );
}
- uri_refput(uri);
file_close(out);
return 0;
}
return delete_dir_recursive_bottom_up(uri_get_local(uri));
}
-static int
-__delete_from_uri(char const *location)
-{
- struct rpki_uri *uri;
- int error;
-
- /* rfc8181#section-2.2 must be an rsync URI */
- error = uri_create(&uri, UT_CAGED, location);
- if (error)
- return error;
-
- error = delete_from_uri(uri);
-
- /* Error 0 is ok */
- uri_refput(uri);
- return error;
-}
-
/*
* This function will call 'xmlTextReaderRead' so there's no need to expect any
* other type at the caller.
*/
static int
-parse_publish_elem(xmlTextReaderPtr reader, hash_requirement hr)
+parse_publish_elem(xmlTextReaderPtr reader, struct rpki_uri *notif,
+ hash_requirement hr)
{
- struct publish *tmp;
+ struct publish tag;
int error;
- tmp = NULL;
- error = parse_publish(reader, hr, &tmp);
+ error = parse_publish(reader, notif, hr, &tag);
if (error)
return error;
- error = write_from_uri(tmp->meta.uri, tmp->content, tmp->content_len);
+ error = write_from_uri(tag.meta.uri, tag.content, tag.content_len);
- publish_destroy(tmp);
+ publish_cleanup(&tag);
return error;
}
* other type at the caller.
*/
static int
-parse_withdraw_elem(xmlTextReaderPtr reader)
+parse_withdraw_elem(xmlTextReaderPtr reader, struct rpki_uri *notif)
{
- struct withdraw *tmp;
+ struct withdraw tag;
int error;
- error = parse_withdraw(reader, &tmp);
+ error = parse_withdraw(reader, notif, &tag);
if (error)
return error;
- error = __delete_from_uri(tmp->meta.uri);
+ error = delete_from_uri(tag.meta.uri);
- withdraw_destroy(tmp);
+ withdraw_cleanup(&tag);
return error;
}
static int
parse_notification_delta(xmlTextReaderPtr reader,
- struct update_notification *notification)
+ struct update_notification *notif)
{
struct delta_head delta;
int error;
error = parse_long(reader, RRDP_ATTR_SERIAL, &delta.serial);
if (error)
return error;
- error = parse_doc_data(reader, HR_MANDATORY, &delta.meta);
+ error = parse_doc_data(reader, NULL, HR_MANDATORY, &delta.meta);
if (error)
return error;
- deltas_head_add(¬ification->deltas_list, &delta);
+ deltas_head_add(¬if->deltas_list, &delta);
return 0;
}
static int
-xml_read_notification(xmlTextReaderPtr reader, void *arg)
+xml_read_notif(xmlTextReaderPtr reader, void *arg)
{
- struct update_notification *update = arg;
+ struct update_notification *notif = arg;
xmlChar const *name;
name = xmlTextReaderConstLocalName(reader);
switch (xmlTextReaderNodeType(reader)) {
case XML_READER_TYPE_ELEMENT:
if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_DELTA)) {
- return parse_notification_delta(reader, update);
+ return parse_notification_delta(reader, notif);
} else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_SNAPSHOT)) {
- return parse_doc_data(reader, HR_MANDATORY,
- &update->snapshot);
+ return parse_doc_data(reader, NULL, HR_MANDATORY,
+ ¬if->snapshot);
} else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_NOTIFICATION)) {
/* No need to validate session ID and serial */
- return parse_metadata(reader, &update->meta, NULL, 0);
+ return parse_metadata(reader, ¬if->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->meta.serial);
+ return deltas_head_sort(¬if->deltas_list,
+ notif->meta.serial);
break;
}
update_notification_init(result, uri);
- error = relax_ng_parse(uri_get_local(uri), xml_read_notification, result);
+ error = relax_ng_parse(uri_get_local(uri), xml_read_notif, result);
if (error)
- update_notification_destroy(result);
+ update_notification_cleanup(result);
return error;
}
switch (type) {
case XML_READER_TYPE_ELEMENT:
if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_PUBLISH))
- error = parse_publish_elem(reader, HR_IGNORE);
+ error = parse_publish_elem(reader, ctx->notif->uri,
+ HR_IGNORE);
else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_SNAPSHOT))
error = parse_metadata(reader,
&ctx->snapshot->meta,
- ctx->notification->meta.session_id,
- ctx->notification->meta.serial);
+ ctx->notif->meta.session_id,
+ ctx->notif->meta.serial);
else
return pr_val_err("Unexpected '%s' element", name);
}
static int
-parse_snapshot(struct rpki_uri *uri, struct update_notification *notification)
+parse_snapshot(struct rpki_uri *uri, struct update_notification *notif)
{
struct rdr_snapshot_ctx ctx;
int error;
fnstack_push_uri(uri);
/* Hash validation */
- error = hash_validate_file(uri, notification->snapshot.hash,
- notification->snapshot.hash_len);
+ error = hash_validate_file(uri, notif->snapshot.hash,
+ notif->snapshot.hash_len);
if (error)
goto pop;
ctx.snapshot = snapshot_create();
- ctx.notification = notification;
+ ctx.notif = notif;
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, HR_OPTIONAL);
+ error = parse_publish_elem(reader, ctx->notif->uri,
+ HR_OPTIONAL);
else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_WITHDRAW))
- error = parse_withdraw_elem(reader);
+ error = parse_withdraw_elem(reader, ctx->notif->uri);
else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_DELTA))
error = parse_metadata(reader,
&ctx->delta->meta,
- ctx->notification->meta.session_id,
+ ctx->notif->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 update_notification *notification)
+ struct update_notification *notif)
{
struct rdr_delta_ctx ctx;
struct file_metadata *expected_data;
goto pop_fnstack;
ctx.delta = delta_create();
- ctx.notification = notification;
+ ctx.notif = notif;
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)
+process_delta(struct delta_head *delta, void *arg)
{
struct rpki_uri *uri;
- struct file_metadata *head_data;
int error;
- head_data = &delta_head->meta;
-
- pr_val_debug("Processing delta '%s'.", head_data->uri);
- error = uri_create(&uri, UT_HTTPS, head_data->uri);
- if (error)
- return error;
+ uri = delta->meta.uri;
+ pr_val_debug("Processing delta '%s'.", uri_val_get_printable(uri));
fnstack_push_uri(uri);
error = cache_download(uri, NULL);
if (error)
- goto release_uri;
-
- error = parse_delta(uri, delta_head, arg);
-
+ goto end;
+ error = parse_delta(uri, delta, arg);
delete_from_uri(uri);
- /* Error 0 its ok */
-release_uri:
+
+end:
fnstack_pop();
- uri_refput(uri);
return error;
}
int
-rrdp_parse_snapshot(struct update_notification *notification)
+rrdp_parse_snapshot(struct update_notification *notif)
{
struct rpki_uri *uri;
int error;
- pr_val_debug("Processing snapshot '%s'.", notification->snapshot.uri);
- error = uri_create(&uri, UT_HTTPS, notification->snapshot.uri);
- if (error)
- return error;
+ uri = notif->snapshot.uri;
+ pr_val_debug("Processing snapshot '%s'.", uri_val_get_printable(uri));
fnstack_push_uri(uri);
error = cache_download(uri, NULL);
if (error)
- goto release_uri;
-
- error = parse_snapshot(uri, notification);
-
+ goto end;
+ error = parse_snapshot(uri, notif);
delete_from_uri(uri);
- /* Error 0 is ok */
-release_uri:
+
+end:
fnstack_pop();
- uri_refput(uri);
return error;
}
int
-rrdp_process_deltas(struct update_notification *notification,
- unsigned long cur_serial)
+rrdp_process_deltas(struct update_notification *notif, unsigned long serial)
{
- return deltas_head_for_each(¬ification->deltas_list,
- notification->meta.serial, cur_serial, process_delta, notification);
+ return deltas_head_for_each(¬if->deltas_list, notif->meta.serial,
+ serial, process_delta, notif);
}
if (retries > 0)
pr_val_warn("Max RSYNC retries (%u) reached on '%s', won't retry again.",
retries, uri_get_global(uri));
- error = EREQFAILED;
+ error = EIO;
goto release_args;
}
pr_val_warn("Retrying RSYNC '%s' in %u seconds, %u attempts remaining.",
char addr_buffer2[INET6_ADDRSTRLEN];
struct validation_handler validation_handler;
-
- /* URL of the Notification file currently being traversed. */
- struct rpki_uri *notification_uri;
};
/*
result->pubkey_state = PKS_UNTESTED;
result->validation_handler = *validation_handler;
result->x509_data.params = params; /* Ownership transfered */
- result->notification_uri = NULL;
*out = result;
return 0;
{
return &state->validation_handler;
}
-
-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)
-{
- if (state->notification_uri != NULL)
- uri_refput(state->notification_uri);
- state->notification_uri = uri;
- if (uri != NULL)
- uri_refget(uri);
-}
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 "types/uri.h"
+#include <errno.h>
+
#include "alloc.h"
#include "common.h"
#include "config.h"
#include "thread_var.h"
#include "config/filename_format.h"
#include "data_structure/path_builder.h"
+#include "cache/local_cache.h"
+#include "rrdp/rrdp_loader.h"
/**
* Design notes:
* "<local-repository>/rrdp/<notification-path>/a.b.c/d/e.cer".
*/
static int
-map_caged(struct rpki_uri *uri)
+map_caged(struct rpki_uri *uri, struct rpki_uri *notif)
{
struct path_builder pb;
- struct rpki_uri *notification;
int error;
- notification = validation_get_notification_uri(state_retrieve());
- if (notification == NULL)
- pr_crit("Programming error: Notification not recorded.");
-
error = pb_init_cache(&pb, "rrdp");
if (error)
return error;
- error = append_guri(&pb, notification->global, "https://", ENOTHTTPS, true);
+ error = append_guri(&pb, notif->global, "https://", ENOTHTTPS, true);
if (error)
return error;
error = append_guri(&pb, uri->global, "rsync://", ENOTRSYNC, true);
}
static int
-autocomplete_local(struct rpki_uri *uri)
+autocomplete_local(struct rpki_uri *uri, struct rpki_uri *notif)
{
switch (uri->type) {
case UT_RSYNC:
case UT_HTTPS:
return map_simple(uri, "https://", ENOTHTTPS);
case UT_CAGED:
- return map_caged(uri);
+ return map_caged(uri, notif);
}
pr_crit("Unknown URI type: %u", uri->type);
* 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)
+__uri_create(struct rpki_uri **result, enum uri_type type,
+ struct rpki_uri *notif, void const *guri, size_t guri_len)
{
struct rpki_uri *uri;
int error;
uri->type = type;
- error = autocomplete_local(uri);
+ error = autocomplete_local(uri, notif);
if (error) {
free(uri->global);
free(uri);
}
int
-uri_create(struct rpki_uri **result, enum uri_type type, char const *guri)
+uri_create(struct rpki_uri **result, enum uri_type type, struct rpki_uri *notif,
+ char const *guri)
{
- return __uri_create(result, type, guri, strlen(guri));
+ return __uri_create(result, type, notif, guri, strlen(guri));
}
/*
* names. This function will infer the rest of the URL.
*/
int
-uri_create_mft(struct rpki_uri **result, struct rpki_uri *mft, IA5String_t *ia5)
+uri_create_mft(struct rpki_uri **result, struct rpki_uri *notif,
+ struct rpki_uri *mft, IA5String_t *ia5)
{
struct rpki_uri *uri;
int error;
return error;
}
- uri->type = (validation_get_notification_uri(state_retrieve()) == NULL)
- ? UT_RSYNC
- : UT_CAGED;
+ uri->type = (notif == NULL) ? UT_RSYNC : UT_CAGED;
- error = autocomplete_local(uri);
+ error = autocomplete_local(uri, notif);
if (error) {
free(uri->global);
free(uri);
void
uri_refput(struct rpki_uri *uri)
{
+ if (uri == NULL)
+ return;
+
uri->references--;
if (uri->references == 0) {
free(uri->global);
format = config_get_op_log_filename_format();
return uri_get_printable(uri, format);
}
+
+DEFINE_ARRAY_LIST_FUNCTIONS(uri_list, struct rpki_uri *, static)
+
+void
+uris_init(struct uri_list *uris)
+{
+ uri_list_init(uris);
+}
+
+static void
+__uri_refput(struct rpki_uri **uri)
+{
+ uri_refput(*uri);
+}
+
+void
+uris_cleanup(struct uri_list *uris)
+{
+ uri_list_cleanup(uris, __uri_refput);
+}
+
+/* Swallows @uri. */
+void
+uris_add(struct uri_list *uris, struct rpki_uri *uri)
+{
+ uri_list_add(uris, &uri);
+}
+
+static int
+download(struct rpki_uri *uri, bool use_rrdp)
+{
+ return (use_rrdp && (uri_get_type(uri) == UT_HTTPS))
+ ? rrdp_update(uri)
+ : cache_download(uri, NULL);
+}
+
+static struct rpki_uri *
+download_uris(struct uri_list *uris, enum uri_type type, bool use_rrdp)
+{
+ struct rpki_uri **cursor, *uri;
+ ARRAYLIST_FOREACH(uris, cursor) {
+ uri = *cursor;
+ if (uri_get_type(uri) == type && download(uri, use_rrdp) == 0)
+ return uri;
+ }
+ return NULL;
+}
+
+/**
+ * Assumes all the URIs are URLs, and represent different ways to access the
+ * same content.
+ *
+ * Sequentially (in the order dictated by their priorities) attempts to update
+ * (in the cache) the content pointed by each URL.
+ * Stops on the first success, returning the corresponding URI.
+ *
+ * If there's no successful update, attempts to find one that's already cached.
+ * Returns the newest successfully cached URI.
+ *
+ * Does not grab any references.
+ */
+struct rpki_uri *
+uris_download(struct uri_list *uris, bool use_rrdp)
+{
+ struct rpki_uri **cursor, *uri;
+
+ if (config_get_http_priority() > config_get_rsync_priority()) {
+ uri = download_uris(uris, UT_HTTPS, use_rrdp);
+ if (uri != NULL)
+ return uri;
+ uri = download_uris(uris, UT_RSYNC, use_rrdp);
+ if (uri != NULL)
+ return uri;
+
+ } else if (config_get_http_priority() < config_get_rsync_priority()) {
+ uri = download_uris(uris, UT_RSYNC, use_rrdp);
+ if (uri != NULL)
+ return uri;
+ uri = download_uris(uris, UT_HTTPS, use_rrdp);
+ if (uri != NULL)
+ return uri;
+
+ } else {
+ ARRAYLIST_FOREACH(uris, cursor) {
+ uri = *cursor;
+ if (download(uri, use_rrdp) == 0)
+ return uri;
+ }
+ }
+
+ return cache_recover(uris, use_rrdp);
+}
#include <stdbool.h>
#include "asn1/asn1c/IA5String.h"
+#include "data_structure/array_list.h"
enum uri_type {
/* rsync URL */
struct rpki_uri;
-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(struct rpki_uri **, enum uri_type, struct rpki_uri *,
+ void const *, size_t);
+int uri_create(struct rpki_uri **, enum uri_type, struct rpki_uri *,
+ char const *);
+int uri_create_mft(struct rpki_uri **, struct rpki_uri *, struct rpki_uri *,
+ IA5String_t *);
struct rpki_uri *uri_refget(struct rpki_uri *);
void uri_refput(struct rpki_uri *);
char const *uri_val_get_printable(struct rpki_uri *);
char const *uri_op_get_printable(struct rpki_uri *);
+/* Plural */
+
+DEFINE_ARRAY_LIST_STRUCT(uri_list, struct rpki_uri *);
+
+void uris_init(struct uri_list *);
+void uris_cleanup(struct uri_list *);
+
+void uris_add(struct uri_list *, struct rpki_uri *);
+struct rpki_uri *uris_download(struct uri_list *, bool);
+
#endif /* SRC_TYPES_URI_H_ */
return error;
}
+MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
+
/* Helpers */
static const int SUCCESS = CNF_DIRECT | CNF_SUCCESS;
{
struct rpki_uri *uri;
- ck_assert_int_eq(0, uri_create(&uri, uritype, url));
+ ck_assert_int_eq(0, uri_create(&uri, uritype, NULL, url));
dl_count = 0;
ck_assert_int_eq(expected_error, cache_download(uri, NULL));
}
END_TEST
+static void
+prepare_uri_list(struct uri_list *uris, ...)
+{
+ char const *str;
+ enum uri_type type;
+ struct rpki_uri *uri;
+ va_list args;
+
+ uris_init(uris);
+
+ va_start(args, uris);
+ while ((str = va_arg(args, char const *)) != NULL) {
+ if (str_starts_with(str, "https://"))
+ type = UT_HTTPS;
+ else if (str_starts_with(str, "rsync://"))
+ type = UT_RSYNC;
+ else
+ ck_abort_msg("Bad protocol: %s", str);
+ ck_assert_int_eq(0, uri_create(&uri, type, NULL, str));
+ uris_add(uris, uri);
+ }
+ va_end(args);
+}
+
+#define PREPARE_URI_LIST(uris, ...) prepare_uri_list(uris, ##__VA_ARGS__, NULL)
+
+START_TEST(test_recover)
+{
+ struct uri_list uris;
+
+ ck_assert_int_eq(0, system("rm -rf tmp/"));
+ dl_error = false;
+
+ /* Query on empty database */
+ PREPARE_URI_LIST(&uris, "rsync://a.b.c/d", "https://a.b.c/d");
+ ck_assert_ptr_null(cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ /* Only first URI is cached */
+ ck_assert_int_eq(0, cache_prepare());
+ download_rsync("rsync://a/b/c", 0, 1);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
+ ck_assert_ptr_eq(uris.array[0], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ cache_teardown();
+
+ /* Only second URI is cached */
+ ck_assert_int_eq(0, cache_prepare());
+ download_https("https://d/e", 0, 1);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
+ ck_assert_ptr_eq(uris.array[1], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ cache_teardown();
+
+ /* Only third URI is cached */
+ ck_assert_int_eq(0, cache_prepare());
+ download_https("https://f", 0, 1);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
+ ck_assert_ptr_eq(uris.array[2], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ cache_teardown();
+
+ /* None was cached */
+ ck_assert_int_eq(0, cache_prepare());
+ download_rsync("rsync://d/e", 0, 1);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
+ ck_assert_ptr_null(cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ cache_teardown();
+
+ /*
+ * At present, cache_recover() can only be called after all of a
+ * download's URLs yielded failure.
+ * However, node.error can still be zero. This happens when the download
+ * was successful, but the RRDP code wasn't able to expand the snapshot
+ * or deltas.
+ */
+ rsync = NODE("rsync", 0, 0,
+ NODE("a", 0, 0,
+ TNODE("1", CNF_DIRECT | CNF_SUCCESS, 100, 100, 0),
+ TNODE("2", CNF_DIRECT | CNF_SUCCESS, 100, 100, 1),
+ TNODE("3", CNF_DIRECT | CNF_SUCCESS, 100, 200, 0),
+ TNODE("4", CNF_DIRECT | CNF_SUCCESS, 100, 200, 1),
+ TNODE("5", CNF_DIRECT | CNF_SUCCESS, 200, 100, 0),
+ TNODE("6", CNF_DIRECT | CNF_SUCCESS, 200, 100, 1)),
+ NODE("b", 0, 0,
+ TNODE("1", CNF_DIRECT, 100, 100, 0),
+ TNODE("2", CNF_DIRECT, 100, 100, 1),
+ TNODE("3", CNF_DIRECT, 100, 200, 0),
+ TNODE("4", CNF_DIRECT, 100, 200, 1),
+ TNODE("5", CNF_DIRECT, 200, 100, 0),
+ TNODE("6", CNF_DIRECT, 200, 100, 1)),
+ TNODE("c", CNF_DIRECT | CNF_SUCCESS, 300, 300, 0,
+ TNODE("1", 0, 0, 0, 0)),
+ TNODE("d", CNF_DIRECT | CNF_SUCCESS, 50, 50, 0,
+ TNODE("1", 0, 0, 0, 0)));
+
+ /* Multiple successful caches: Prioritize the most recent one */
+ PREPARE_URI_LIST(&uris, "rsync://a/1", "rsync://a/3", "rsync://a/5");
+ ck_assert_ptr_eq(uris.array[2], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/5", "rsync://a/1", "rsync://a/3");
+ ck_assert_ptr_eq(uris.array[0], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ /* No successful caches: No viable candidates */
+ PREPARE_URI_LIST(&uris, "rsync://b/2", "rsync://b/4", "rsync://b/6");
+ ck_assert_ptr_null(cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ /* Status: CNF_SUCCESS is better than 0. */
+ PREPARE_URI_LIST(&uris, "rsync://b/1", "rsync://a/1");
+ ck_assert_ptr_eq(uris.array[1], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ /*
+ * If CNF_SUCCESS && error, Fort will probably run into a problem
+ * reading the cached directory, because it's either outdated or
+ * recently corrupted.
+ * But it should still TRY to read it, as there's a chance the
+ * outdatedness is not that severe.
+ */
+ PREPARE_URI_LIST(&uris, "rsync://a/2", "rsync://b/2");
+ ck_assert_ptr_eq(uris.array[0], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ /* Parents of downloaded nodes */
+ PREPARE_URI_LIST(&uris, "rsync://a", "rsync://b");
+ ck_assert_ptr_null(cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ /* Children of downloaded nodes */
+ PREPARE_URI_LIST(&uris, "rsync://a/5", "rsync://c/1");
+ ck_assert_ptr_eq(uris.array[1], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/5", "rsync://c/2");
+ ck_assert_ptr_eq(uris.array[1], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/1", "rsync://d/1");
+ ck_assert_ptr_eq(uris.array[0], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ PREPARE_URI_LIST(&uris, "rsync://a/1", "rsync://d/2");
+ ck_assert_ptr_eq(uris.array[0], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ /* Try them all at the same time */
+ PREPARE_URI_LIST(&uris,
+ "rsync://a", "rsync://a/1", "rsync://a/2", "rsync://a/3",
+ "rsync://a/4", "rsync://a/5", "rsync://a/6",
+ "rsync://b", "rsync://b/1", "rsync://b/2", "rsync://b/3",
+ "rsync://b/4", "rsync://b/5", "rsync://b/6",
+ "rsync://c/2", "rsync://d/1", "rsync://e/1");
+ ck_assert_ptr_eq(uris.array[14], cache_recover(&uris, false));
+ uris_cleanup(&uris);
+
+ cache_teardown();
+}
+END_TEST
+
/* Boilerplate */
Suite *thread_pool_suite(void)
ctt = tcase_create("ctt");
tcase_add_test(ctt, test_ctt_traversal);
+ ctt = tcase_create("recover");
+ tcase_add_test(ctt, test_recover);
+
suite = suite_create("local-cache");
suite_add_tcase(suite, rsync);
suite_add_tcase(suite, https);
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_UINT(config_get_rsync_priority, 50, void)
+MOCK_UINT(config_get_http_priority, 60, void)
MOCK_NULL(config_get_output_roa, char const *, void)
MOCK_NULL(config_get_output_bgpsec, char const *, void)
MOCK_UINT(config_get_thread_pool_validation_max, 10, void)
MOCK_ABORT_VOID(fnstack_cleanup, void)
MOCK_ABORT_VOID(fnstack_push, char const *f)
+MOCK_ABORT_INT(cache_download, struct rpki_uri *uri, bool *changed)
+MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
+MOCK_ABORT_PTR(cache_recover, rpki_uri, struct uri_list *uris,
+ bool use_rrdp)
+
/* Tests */
START_TEST(tal_load_normal)
ck_assert_int_eq(tal_load("tal/lacnic.tal", &tal), 0);
- ck_assert_uint_eq(tal->uris.count, 3);
+ ck_assert_uint_eq(tal->uris.len, 3);
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, "https://potato");
/* Mocks */
-struct rpki_uri *notification;
+struct rpki_uri *notif;
MOCK(state_retrieve, struct validation *, NULL, void)
-MOCK(validation_get_notification_uri, struct rpki_uri *, notification,
+MOCK(validation_get_notification_uri, struct rpki_uri *, notif,
struct validation *state)
+MOCK_ABORT_INT(cache_download, struct rpki_uri *uri, bool *changed)
+MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
+MOCK_ABORT_PTR(cache_recover, rpki_uri, struct uri_list *uris,
+ bool use_rrdp)
+
/* Tests */
+#define URI_CREATE_HTTP(uri, str) uri_create(&uri, UT_HTTPS, NULL, str)
+
START_TEST(test_constructor)
{
struct rpki_uri *uri;
- ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, ""));
- ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "h"));
- ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "http"));
- ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "https"));
- ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "https:"));
- ck_assert_int_eq(ENOTHTTPS, uri_create(&uri, UT_HTTPS, "https:/"));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://"));
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, ""));
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, "h"));
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, "http"));
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, "https"));
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, "https:"));
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, "https:/"));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://"));
- ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c"));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c"));
ck_assert_str_eq("https://a.b.c", uri_get_global(uri));
ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
- ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/"));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/"));
ck_assert_str_eq("https://a.b.c/", uri_get_global(uri));
ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
- ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/d"));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/d"));
ck_assert_str_eq("https://a.b.c/d", uri_get_global(uri));
ck_assert_str_eq("tmp/https/a.b.c/d", uri_get_local(uri));
uri_refput(uri);
- ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/d/e"));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/d/e"));
ck_assert_str_eq("https://a.b.c/d/e", uri_get_global(uri));
ck_assert_str_eq("tmp/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/d/.."));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/d/.."));
ck_assert_str_eq("https://a.b.c/d/..", uri_get_global(uri));
ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
- ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/."));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/."));
ck_assert_str_eq("https://a.b.c/.", uri_get_global(uri));
ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
- ck_assert_int_eq(0, uri_create(&uri, UT_HTTPS, "https://a.b.c/././d/././e/./."));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/././d/././e/./."));
ck_assert_str_eq("https://a.b.c/././d/././e/./.", uri_get_global(uri));
ck_assert_str_eq("tmp/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/a/b/.././.."));
+ ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/a/b/.././.."));
ck_assert_str_eq("https://a.b.c/a/b/.././..", uri_get_global(uri));
ck_assert_str_eq("tmp/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/../.."));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://a.b.c/d/../.."));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://a.b.c/d/../../.."));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://."));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://./."));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://.."));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://../.."));
- ck_assert_int_eq(-EINVAL, uri_create(&uri, UT_HTTPS, "https://../../.."));
-
- 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"));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://a.b.c/.."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://a.b.c/../.."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://a.b.c/d/../.."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://a.b.c/d/../../.."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://./."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://.."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://../.."));
+ ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://../../.."));
+
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, "rsync://a.b.c/d"));
+ ck_assert_int_eq(ENOTHTTPS, URI_CREATE_HTTP(uri, "http://a.b.c/d"));
+ ck_assert_int_eq(ENOTRSYNC, uri_create(&uri, UT_RSYNC, NULL, "https://a.b.c/d"));
}
END_TEST
{
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_int_eq(0, uri_create(¬if, UT_HTTPS, NULL, "https://a.b.c/d/e.xml"));
+ ck_assert_int_eq(0, uri_create(&uri, UT_CAGED, notif, "rsync://x.y.z/v/w.cer"));
ck_assert_str_eq("tmp/rrdp/a.b.c/d/e.xml/x.y.z/v/w.cer", uri_get_local(uri));
uri_refput(uri);
- uri_refput(notification);
+ uri_refput(notif);
- ck_assert_int_eq(0, uri_create(¬ification, UT_HTTPS, "https://a.b.c"));
- ck_assert_int_eq(0, uri_create(&uri, UT_CAGED, "rsync://w"));
+ ck_assert_int_eq(0, uri_create(¬if, UT_HTTPS, NULL, "https://a.b.c"));
+ ck_assert_int_eq(0, uri_create(&uri, UT_CAGED, notif, "rsync://w"));
ck_assert_str_eq("tmp/rrdp/a.b.c/w", uri_get_local(uri));
uri_refput(uri);
- uri_refput(notification);
+ uri_refput(notif);
}
END_TEST