#define TMPDIR "tmp"
#define TAL_METAFILE "tal.json"
+#define TAGNAME_TYPE "type"
#define TAGNAME_URL "url"
#define TAGNAME_ATTEMPT_TS "attempt-timestamp"
#define TAGNAME_ATTEMPT_ERR "attempt-result"
#define TAGNAME_SUCCESS_TS "success-timestamp"
-#define TAGNAME_IS_NOTIF "is-rrdp-notification"
+
+#define TYPEVALUE_TA_RSYNC "TA (rsync)"
+#define TYPEVALUE_TA_HTTP "TA (HTTP)"
+#define TYPEVALUE_RPP "RPP"
+#define TYPEVALUE_NOTIF "RRDP Notification"
static atomic_uint file_counter;
json2node(struct rpki_cache *cache, json_t *json)
{
struct cache_node *node;
- char const *url;
- bool is_notif;
+ char const *type_str;
enum uri_type type;
+ char const *url;
int error;
node = pzalloc(sizeof(struct cache_node));
- error = json_get_str(json, TAGNAME_URL, &url);
+ error = json_get_str(json, TAGNAME_TYPE, &type_str);
if (error) {
if (error > 0)
- pr_op_err("Node is missing the '" TAGNAME_URL "' tag.");
+ pr_op_err("Node is missing the '" TAGNAME_TYPE "' tag.");
goto fail;
}
- if (str_starts_with(url, "https://"))
- type = UT_HTTPS;
- else if (str_starts_with(url, "rsync://"))
- type = UT_RSYNC;
+ if (strcmp(type_str, TYPEVALUE_TA_RSYNC) == 0)
+ type = UT_TA_RSYNC;
+ else if (strcmp(type_str, TYPEVALUE_TA_HTTP) == 0)
+ type = UT_TA_HTTP;
+ else if (strcmp(type_str, TYPEVALUE_RPP) == 0)
+ type = UT_RPP;
+ else if (strcmp(type_str, TYPEVALUE_NOTIF) == 0)
+ type = UT_NOTIF;
else {
- pr_op_err("Unknown protocol: %s", url);
+ pr_op_err("Unknown node type: %s", type_str);
goto fail;
}
- error = json_get_bool(json, TAGNAME_IS_NOTIF, &is_notif);
- if (error < 0)
+ error = json_get_str(json, TAGNAME_URL, &url);
+ if (error) {
+ if (error > 0)
+ pr_op_err("Node is missing the '" TAGNAME_URL "' tag.");
goto fail;
+ }
- error = uri_create(&node->url, cache->tal, type, is_notif, NULL, url);
+ error = uri_create(&node->url, cache->tal, type, NULL, url);
if (error) {
pr_op_err("Cannot parse '%s' into a URI.", url);
goto fail;
}
static struct cache_node*
-find_node(struct rpki_cache *cache, struct rpki_uri *uri)
+find_node(struct rpki_cache *cache, char const *luri)
{
- char const *key = uri_get_local(uri);
struct cache_node *result;
- HASH_FIND_STR(cache->ht, key, result);
+ HASH_FIND_STR(cache->ht, luri, result);
return result;
}
node2json(struct cache_node *node)
{
json_t *json;
+ char const *type;
json = json_object();
- if (json == NULL) {
- pr_op_err("json object allocation failure.");
- return NULL;
+ if (json == NULL)
+ enomem_panic();
+
+ switch (uri_get_type(node->url)) {
+ case UT_TA_RSYNC:
+ type = TYPEVALUE_TA_RSYNC;
+ break;
+ case UT_TA_HTTP:
+ type = TYPEVALUE_TA_HTTP;
+ break;
+ case UT_RPP:
+ type = TYPEVALUE_RPP;
+ break;
+ case UT_NOTIF:
+ type = TYPEVALUE_NOTIF;
+ break;
+ default:
+ goto cancel;
}
+ if (json_add_str(json, TAGNAME_TYPE, type))
+ goto cancel;
if (json_add_str(json, TAGNAME_URL, uri_get_global(node->url)))
goto cancel;
- if (uri_is_notif(node->url))
- if (json_add_bool(json, TAGNAME_IS_NOTIF, true))
- goto cancel;
if (json_add_date(json, TAGNAME_ATTEMPT_TS, node->attempt.ts))
goto cancel;
if (json_add_int(json, TAGNAME_ATTEMPT_ERR, node->attempt.result))
unsigned int slashes;
int error;
- if (uri_get_type(uri) != UT_RSYNC) {
+ if (uri_get_type(uri) != UT_RPP) {
uri_refget(uri);
*url = uri;
return 0;
if (*c == '/') {
slashes++;
if (slashes == 4)
- return __uri_create(url, tal, UT_RSYNC, false,
+ return __uri_create(url, tal, UT_RPP,
NULL, guri, c - guri + 1);
}
}
if (slashes == 3 && *(c - 1) != '/') {
guri2 = pstrdup(guri); /* Remove const */
guri2[c - guri] = '/';
- error = __uri_create(url, tal, UT_RSYNC, false, NULL, guri2,
+ error = __uri_create(url, tal, UT_RPP, NULL, guri2,
c - guri + 1);
free(guri2);
return error;
}
/**
- * @changed only on HTTP.
+ * @ims and @changed only on HTTP.
*/
int
-cache_download(struct rpki_cache *cache, struct rpki_uri *uri, bool *changed)
+cache_download(struct rpki_cache *cache, struct rpki_uri *uri,
+ curl_off_t ims, bool *changed)
{
struct rpki_uri *url;
struct cache_node *node;
if (error)
return error;
- node = find_node(cache, url);
+ node = find_node(cache, uri_get_local(url));
if (node != NULL) {
uri_refput(url);
if (was_recently_downloaded(cache, node))
}
switch (uri_get_type(url)) {
- case UT_RSYNC:
+ case UT_TA_HTTP:
+ case UT_NOTIF:
+ case UT_TMP:
+ error = config_get_http_enabled()
+ ? http_download(url, ims, changed)
+ : cache_check(url);
+ break;
+ case UT_TA_RSYNC:
+ case UT_RPP:
error = config_get_rsync_enabled()
? rsync_download(url)
: cache_check(url);
break;
- case UT_HTTPS:
- error = config_get_http_enabled()
- ? http_download(url, changed)
- : cache_check(url);
- break;
default:
- pr_crit("Unexpected URI type: %d", uri_get_type(url));
+ pr_crit("URI type not downloadable: %d", uri_get_type(url));
}
node->attempt.ts = time(NULL);
}
static int
-download(struct rpki_cache *cache, struct rpki_uri *uri, bool use_rrdp,
- uris_dl_cb cb, void *arg)
+download(struct rpki_cache *cache, struct rpki_uri *uri, uris_dl_cb cb, void *arg)
{
+ time_t ims = 0;
int error;
pr_val_debug("Trying URL %s...", uri_get_global(uri));
- error = (use_rrdp && (uri_get_type(uri) == UT_HTTPS))
- ? rrdp_update(uri)
- : cache_download(cache, uri, NULL);
- if (error)
- return 1;
+ switch (uri_get_type(uri)) {
+ case UT_TA_HTTP:
+ error = file_get_mtim(uri_get_local(uri), &ims);
+ if (error)
+ break;
+ /* Fall through */
+ case UT_TA_RSYNC:
+ case UT_RPP:
+ error = cache_download(cache, uri, ims, NULL);
+ break;
+ case UT_NOTIF:
+ error = rrdp_update(uri);
+ break;
+ default:
+ pr_crit("URI type is not a legal alt candidate: %u", uri_get_type(uri));
+ }
- return cb(uri, arg);
+ return error ? 1 : cb(uri, arg);
}
static int
download_uris(struct rpki_cache *cache, struct uri_list *uris,
- enum uri_type type, bool use_rrdp, uris_dl_cb cb, void *arg)
+ enum uri_type type, uris_dl_cb cb, void *arg)
{
struct rpki_uri **uri;
int error;
ARRAYLIST_FOREACH(uris, uri) {
if (uri_get_type(*uri) == type) {
- error = download(cache, *uri, use_rrdp, cb, arg);
+ error = download(cache, *uri, cb, arg);
if (error <= 0)
return error;
}
*/
int
cache_download_alt(struct rpki_cache *cache, struct uri_list *uris,
- bool use_rrdp, uris_dl_cb cb, void *arg)
+ enum uri_type http_type, enum uri_type rsync_type, uris_dl_cb cb, void *arg)
{
struct rpki_uri **cursor, *uri;
int error;
if (config_get_http_priority() > config_get_rsync_priority()) {
- error = download_uris(cache, uris, UT_HTTPS, use_rrdp, cb, arg);
+ error = download_uris(cache, uris, http_type, cb, arg);
if (error <= 0)
return error;
- error = download_uris(cache, uris, UT_RSYNC, use_rrdp, cb, arg);
+ error = download_uris(cache, uris, rsync_type, cb, arg);
if (error <= 0)
return error;
} else if (config_get_http_priority() < config_get_rsync_priority()) {
- error = download_uris(cache, uris, UT_RSYNC, use_rrdp, cb, arg);
+ error = download_uris(cache, uris, rsync_type, cb, arg);
if (error <= 0)
return error;
- error = download_uris(cache, uris, UT_HTTPS, use_rrdp, cb, arg);
+ error = download_uris(cache, uris, http_type, cb, arg);
if (error <= 0)
return error;
} else {
ARRAYLIST_FOREACH(uris, cursor) {
- error = download(cache, *cursor, use_rrdp, cb, arg);
+ error = download(cache, *cursor, cb, arg);
if (error <= 0)
return error;
}
}
- uri = cache_recover(cache, uris, use_rrdp);
+ uri = cache_recover(cache, uris);
return (uri != NULL) ? cb(uri, arg) : ESRCH;
}
/* Separated because of unit tests. */
static void
-__cache_recover(struct rpki_cache *cache, struct uri_list *uris, bool use_rrdp,
+__cache_recover(struct rpki_cache *cache, struct uri_list *uris,
struct uri_and_node *best)
{
struct rpki_uri **uri;
if (get_url(cursor.uri, cache->tal, &url) != 0)
continue;
- cursor.node = find_node(cache, url);
+ cursor.node = find_node(cache, uri_get_local(url));
uri_refput(url);
if (cursor.node == NULL)
continue;
}
struct rpki_uri *
-cache_recover(struct rpki_cache *cache, struct uri_list *uris, bool use_rrdp)
+cache_recover(struct rpki_cache *cache, struct uri_list *uris)
{
struct uri_and_node best = { 0 };
- __cache_recover(cache, uris, use_rrdp, &best);
+ __cache_recover(cache, uris, &best);
return best.uri;
}
{
struct rpki_uri *cage;
- if (uri_is_notif(node->url)) {
+ if (uri_get_type(node->url) == UT_NOTIF) {
if (uri_create_cage(&cage, cache->tal, node->url) == 0) {
pr_op_debug("Deleting cage %s.", uri_get_local(cage));
file_rm_rf(uri_get_local(cage));
return last_week;
}
+static void
+cleanup_tmp(struct rpki_cache *cache, struct cache_node *node)
+{
+ char const *path;
+ int error;
+
+ if (uri_get_type(node->url) == UT_TMP) {
+ path = uri_get_local(node->url);
+ pr_op_debug("Deleting temporal file '%s'.", path);
+ error = file_rm_f(path);
+ if (error)
+ pr_op_err("Could not delete '%s': %s", path, strerror(error));
+ delete_node(cache, node);
+ }
+}
+
static void
cleanup_node(struct rpki_cache *cache, struct cache_node *node,
time_t last_week)
{
if (!is_cached(fpath)) {
pr_op_debug("Deleting untracked file or directory %s.", fpath);
- remove(fpath);
+ errno = 0;
+ if (remove(fpath) != 0)
+ pr_op_err("Cannot delete '%s': %s", fpath, strerror(errno));
}
+
return 0;
}
uri_refget(node->url);
uris_add(&dnc, node->url);
- if (!uri_is_notif(node->url))
+ if (uri_get_type(node->url) != UT_NOTIF)
continue;
if (uri_create_cage(&cage, cache->tal, node->url) != 0) {
struct cache_node *node, *tmp;
time_t last_week;
+ pr_op_debug("Cleaning up temporal files.");
+ HASH_ITER(hh, cache->ht, node, tmp)
+ cleanup_tmp(cache, node);
+
pr_op_debug("Cleaning up old abandoned cache files.");
last_week = get_days_ago(7);
HASH_ITER(hh, cache->ht, node, tmp)
#ifndef SRC_CACHE_LOCAL_CACHE_H_
#define SRC_CACHE_LOCAL_CACHE_H_
+#include <curl/curl.h>
#include "types/uri.h"
struct rpki_cache;
void cache_destroy(struct rpki_cache *);
/* Downloads @uri into the cache */
-int cache_download(struct rpki_cache *, struct rpki_uri *uri, bool *);
+int cache_download(struct rpki_cache *, struct rpki_uri *uri, curl_off_t, bool *);
/*
* The callback should return
* - < 0 on hard errors ("Abandon foreach")
*/
typedef int (*uris_dl_cb)(struct rpki_uri *, void *);
-int cache_download_alt(struct rpki_cache *, struct uri_list *, bool,
- uris_dl_cb, void *);
+int cache_download_alt(struct rpki_cache *, struct uri_list *, enum uri_type,
+ enum uri_type, uris_dl_cb, void *);
/* Returns the most recent successfully cached URI of the list */
-struct rpki_uri *cache_recover(struct rpki_cache *, struct uri_list *, bool);
+struct rpki_uri *cache_recover(struct rpki_cache *, struct uri_list *);
/* Prints the cache in standard output. */
void cache_print(struct rpki_cache *);
if (stat(path, &meta) != 0) {
error = errno;
if (error == ENOENT)
- return false;
+ return 0;
pr_op_err_st("stat() failed: %s", strerror(error));
- return error;
+ return -error;
}
if (!S_ISDIR(meta.st_mode)) {
return result;
}
-static int
-remove_file(char const *path)
-{
- int error;
-
- errno = 0;
- if (remove(path) != 0) {
- error = errno;
- /*
- pr_val_err("Couldn't delete '%s': %s", path,
- strerror(error));
- */
- return error;
- }
-
- return 0;
-}
-
/*
- * Delete parent dirs of @path only if dirs are empty, @path must be a file
- * location and will be deleted first.
+ * Delete @path.
+ * If path's parent is now empty, delete parent as well.
+ * If parent's parent is now empty, delete parent's parent.
+ * And so on.
*
- * The algorithm is a bit aggressive, but rmdir() won't delete
- * something unless is empty, so in case the dir still has something in
- * it the cycle is finished.
+ * FIXME this should be done by the cache cleaner instead.
*/
int
delete_dir_recursive_bottom_up(char const *path)
size_t config_len;
int error;
- error = remove_file(path);
- if (error)
+ errno = 0;
+ if (remove(path) != 0) {
+ error = errno;
+ pr_val_err("Couldn't delete '%s': %s", path, strerror(error));
return error;
+ }
config_repo = pstrdup(config_get_local_repository());
return true;
}
+int
+file_get_mtim(char const *file, time_t *ims)
+{
+ struct stat meta;
+ int error;
+
+ if (stat(file, &meta) != 0) {
+ error = errno;
+ /*
+ * This happens to be most convenient for callers,
+ * because they all want to convert the ims to a curl_off_t ATM.
+ */
+ *ims = 0;
+ return (error == ENOENT) ? 0 : error;
+ }
+
+#ifdef __APPLE__
+ *ims = meta.st_mtime; /* Seriously, Apple? */
+#else
+ *ims = meta.st_mtim.tv_sec;
+#endif
+ return 0;
+}
+
+/*
+ * Like remove(), but don't care if the file is already deleted.
+ */
+int
+file_rm_f(char const *path)
+{
+ int error;
+
+ errno = 0;
+ if (remove(path) != 0) {
+ error = errno;
+ if (error != ENOENT)
+ return error;
+ }
+
+ return 0;
+}
+
static int
rm(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
pr_op_debug("Deleting %s.", fpath);
+ errno = 0;
return (remove(fpath) != 0) ? errno : 0;
}
int file_exists(char const *);
bool file_valid(char const *);
+int file_get_mtim(char const *, time_t *);
+int file_rm_f(char const *);
int file_rm_rf(char const *);
/*
#include "http/http.h"
-#include <curl/curl.h>
-
#include "alloc.h"
#include "common.h"
#include "config.h"
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;
- }
-
-#ifdef __APPLE__
- *ims = meta.st_mtime; /* Seriously, Apple? */
-#else
- *ims = meta.st_mtim.tv_sec;
-#endif
- return 0;
-}
-
static void
setopt_str(CURL *curl, CURLoption opt, char const *value)
{
pr_val_debug("HTTP result code: %ld", http_code);
error = 0;
- *changed = true;
+ if (changed != NULL)
+ *changed = true;
end: http_easy_cleanup(&handler);
if (error)
- remove(dst);
+ file_rm_f(dst);
return error;
}
/*
- * Assumes @dst's parent directory has already been created.
+ * Download @uri->global into @uri->local; HTTP assumed.
+ *
+ * If @changed returns true, the file was downloaded normally.
+ * If @changed returns false, the file has not been modified since @ims.
+ *
+ * @ims can be 0, which means "no epoch."
+ * @changed can be NULL, which means "I don't care."
+ * If @changed is not NULL, initialize it to false.
*/
-static int
-do_retries(char const *src, char const *dst, curl_off_t ims, bool *changed)
+int
+http_download(struct rpki_uri *uri, curl_off_t ims, bool *changed)
{
unsigned int r;
int error;
- pr_val_debug("Downloading '%s'.", src);
+ pr_val_info("HTTP GET: %s -> %s", uri_get_global(uri), uri_get_local(uri));
- r = 0;
- do {
+ error = mkdir_p(uri_get_local(uri), false);
+ if (error)
+ return error;
+
+ for (r = 0; true; r++) {
pr_val_debug("Download attempt #%u...", r + 1);
- error = http_fetch(src, dst, ims, changed);
+ error = http_fetch(uri_get_global(uri), uri_get_local(uri),
+ ims, changed);
switch (error) {
case 0:
pr_val_debug("Download successful.");
* down; use a thread pool.
*/
sleep(config_get_http_retry_interval());
- r++;
- } while (true);
-}
-
-/*
- * 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 *tmp_file_name;
- char const *final_file_name;
- time_t ims;
- bool __changed;
- int error;
-
- if (changed == NULL)
- changed = &__changed;
- *changed = false;
-
- 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 end;
-
- pr_val_info("HTTP GET: %s -> %s", uri_get_global(uri), final_file_name);
-
- error = do_retries(uri_get_global(uri), tmp_file_name, (curl_off_t)ims,
- changed);
- if (error || !(*changed))
- goto end;
-
- error = mkdir_p(final_file_name, false);
- 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_name, final_file_name, strerror(error));
- remove(tmp_file_name);
- goto end;
- }
-
-end: free(tmp_file_name);
- return error;
}
/*
* structure is created).
*/
int
-http_direct_download(char const *remote, char const *dest)
+http_download_direct(char const *src, char const *dst)
{
- bool changed;
- pr_val_info("HTTP GET: %s -> %s", remote, dest);
- return http_fetch(remote, dest, 0, &changed);
+ pr_val_info("HTTP GET: %s -> %s", src, dst);
+ return http_fetch(src, dst, 0, NULL);
}
#ifndef SRC_HTTP_HTTP_H_
#define SRC_HTTP_HTTP_H_
+#include <curl/curl.h>
#include "types/uri.h"
int http_init(void);
void http_cleanup(void);
-int http_download(struct rpki_uri *, bool *);
-int http_direct_download(char const *, char const *);
+int http_download(struct rpki_uri *, curl_off_t, bool *);
+int http_download_direct(char const *, char const *);
#endif /* SRC_HTTP_HTTP_H_ */
offset += strlen(dest_file);
dest[offset] = '\0';
- error = http_direct_download(url, dest);
+ error = http_download_direct(url, dest);
if (error) {
fprintf(stderr, "Couldn't fetch '%s'.\n", dest);
free(dest);
return 0;
}
-int
-json_get_bool(json_t *parent, char const *name, bool *result)
-{
- json_t *child;
-
- *result = false;
-
- child = json_object_get(parent, name);
- if (child == NULL)
- return ENOENT;
-
- if (!json_is_boolean(child))
- return pr_op_err("Tag '%s' is not a JSON boolean.", name);
-
- *result = json_boolean_value(child);
- return 0;
-}
-
static int
json_get_int_t(json_t *parent, char const *name, json_int_t *result)
{
return json_object_size(object) == expected_size;
}
-int
-json_add_bool(json_t *parent, char const *name, bool value)
-{
- if (json_object_set_new(parent, name, json_boolean(value)))
- return pr_op_err(
- "Cannot convert %s '%u' to json; unknown cause.",
- name, value
- );
-
- return 0;
-}
-
int
json_add_int(json_t *parent, char const *name, int value)
{
* result < 0: Tag was fatally invalid, outbound param reset, logged
*/
-int json_get_bool(json_t *, char const *, bool *);
int json_get_int(json_t *, char const *, int *);
int json_get_u32(json_t *, char const *, uint32_t *);
int json_get_ts(json_t *, char const *, time_t *);
bool json_valid_members_count(json_t *, size_t);
-int json_add_bool(json_t *, char const *, bool);
int json_add_int(json_t *, char const *, int);
int json_add_str(json_t *, char const *, char const *);
int json_add_date(json_t *, char const *, time_t);
static const struct ad_metadata CA_ISSUERS = {
.name = "caIssuers",
.ia_name = "AIA",
- .type = UT_RSYNC,
+ .type = UT_AIA,
.type_str = "rsync",
.required = true,
};
static const struct ad_metadata SIGNED_OBJECT = {
.name = "signedObject",
.ia_name = "SIA",
- .type = UT_RSYNC,
+ .type = UT_SO,
.type_str = "rsync",
.required = true,
};
static const struct ad_metadata CA_REPOSITORY = {
.name = "caRepository",
.ia_name = "SIA",
- .type = UT_RSYNC,
+ .type = UT_RPP,
.type_str = "rsync",
.required = false,
};
static const struct ad_metadata RPKI_NOTIFY = {
.name = "rpkiNotify",
.ia_name = "SIA",
- .type = UT_HTTPS,
+ .type = UT_NOTIF,
.type_str = "HTTPS",
.required = false,
};
static const struct ad_metadata RPKI_MANIFEST = {
.name = "rpkiManifest",
.ia_name = "SIA",
- .type = UT_RSYNC,
+ .type = UT_MFT,
.type_str = "rsync",
.required = true,
};
* But ask the testers to keep an eye on it anyway.
*/
return __uri_create(uri,
- tal_get_file_name(validation_tal(state_retrieve())), type,
- is_notif, NULL,
+ tal_get_file_name(validation_tal(state_retrieve())), type, NULL,
ASN1_STRING_get0_data(asn1str), ASN1_STRING_length(asn1str));
}
}
cache = validation_cache(state_retrieve());
- error = cache_download_alt(cache, &uris->rpp, true, retrieve_uri, &uri);
+ error = cache_download_alt(cache, &uris->rpp, UT_NOTIF, UT_RPP,
+ retrieve_uri, &uri);
return error ? NULL : uri;
}
cert = NULL; /* Ownership stolen */
error = handle_manifest(sia_uris.mft,
- (uri_get_type(downloaded) == UT_HTTPS) ? downloaded : NULL,
+ (uri_get_type(downloaded) == UT_NOTIF) ? downloaded : NULL,
&pp);
if (error) {
x509stack_cancel(validation_certstack(state));
/**
* Validates the manifest pointed by @uri, returns the RPP described by it in
- * @pp. If @rrdp_workspace is true, use the local RRDP repository.
+ * @pp.
*/
int
handle_manifest(struct rpki_uri *uri, struct rpki_uri *notif, struct rpp **pp)
int error;
if (str_starts_with(uri, "rsync://"))
- error = uri_create(&new, tal->file_name, UT_RSYNC, false, NULL, uri);
+ error = uri_create(&new, tal->file_name, UT_TA_RSYNC, NULL, uri);
else if (str_starts_with(uri, "https://"))
- error = uri_create(&new, tal->file_name, UT_HTTPS, false, NULL, uri);
+ error = uri_create(&new, tal->file_name, UT_TA_HTTP, NULL, uri);
else
- return pr_op_err("TAL has non-RSYNC/HTTPS URI: %s", uri);
+ return pr_op_err("TAL has non-rsync/HTTPS URI: %s", uri);
if (error)
return error;
args.db = db_table_create();
thread->error = cache_download_alt(args.tal.cache, &args.tal.uris,
- false, __handle_tal_uri, &args);
+ UT_TA_HTTP, UT_TA_RSYNC, __handle_tal_uri, &args);
if (thread->error) {
pr_op_err("None of the URIs of the TAL '%s' yielded a successful traversal.",
thread->tal_file);
return -EINVAL;
error = uri_create(&meta->uri,
tal_get_file_name(validation_tal(state_retrieve())),
- (notif != NULL) ? UT_CAGED : UT_HTTPS,
- false, notif, (char const *)uri);
+ (notif != NULL) ? UT_CAGED : UT_TMP, notif, (char const *)uri);
xmlFree(uri);
if (error)
return error;
* Maybe stream it instead.
* Same for deltas.
*/
- error = cache_download(validation_cache(state), uri, NULL);
+ error = cache_download(validation_cache(state), uri, 0, NULL);
if (error)
goto end;
error = validate_hash(¬if->snapshot);
pr_val_debug("Processing delta '%s'.", uri_val_get_printable(uri));
fnstack_push_uri(uri);
- error = cache_download(validation_cache(state_retrieve()), uri, NULL);
+ error = cache_download(validation_cache(state_retrieve()), uri, 0, NULL);
if (error)
goto end;
error = parse_delta(notif, delta);
{
struct rrdp_session old = { 0 };
struct update_notification new;
+ time_t ims;
bool changed;
int error;
pr_val_debug("Old session/serial: %s/%s", old.session_id,
old.serial.str);
- error = cache_download(validation_cache(state_retrieve()), uri, &changed);
+ error = file_get_mtim(uri_get_local(uri), &ims);
+ if (error)
+ return error;
+
+ error = cache_download(validation_cache(state_retrieve()), uri,
+ ims, &changed);
if (error)
goto end;
if (!changed) {
#include "state.h"
#include "str_token.h"
#include "thread_var.h"
+#include "cache/local_cache.h"
#include "config/filename_format.h"
#include "data_structure/path_builder.h"
/* "local_len" is never needed right now. */
enum uri_type type;
- bool is_notif; /* Does it point to an RRDP Notification? */
unsigned int references;
};
struct rpki_uri *notif)
{
switch (uri->type) {
- case UT_RSYNC:
+ case UT_TA_RSYNC:
+ case UT_RPP:
return map_simple(uri, tal, "rsync://", ENOTRSYNC);
- case UT_HTTPS:
+
+ case UT_TA_HTTP:
+ case UT_NOTIF:
return map_simple(uri, tal, "https://", ENOTHTTPS);
+
+ case UT_TMP:
+ return cache_tmpfile(&uri->local);
+
case UT_CAGED:
return map_caged(uri, tal, notif);
+
+ case UT_AIA:
+ case UT_SO:
+ case UT_MFT:
+ uri->local = NULL;
+ return 0;
}
pr_crit("Unknown URI type: %u", uri->type);
*/
int
__uri_create(struct rpki_uri **result, char const *tal, enum uri_type type,
- bool is_notif, struct rpki_uri *notif, void const *guri, size_t guri_len)
+ struct rpki_uri *notif, void const *guri, size_t guri_len)
{
struct rpki_uri *uri;
int error;
}
uri->type = type;
- uri->is_notif = is_notif;
error = autocomplete_local(uri, tal, notif);
if (error) {
return error;
}
- uri->type = (notif == NULL) ? UT_RSYNC : UT_CAGED;
+ uri->type = (notif == NULL) ? UT_RPP : UT_CAGED;
error = autocomplete_local(uri, tal, notif);
if (error) {
return uri_has_extension(uri, ".cer");
}
-bool
-uri_is_notif(struct rpki_uri *uri)
-{
- return uri->is_notif;
-}
-
enum uri_type
uri_get_type(struct rpki_uri *uri)
{
return uri->type;
}
-bool
-uri_is_rsync(struct rpki_uri *uri)
-{
- return uri->type == UT_RSYNC;
-}
-
-bool
-uri_is_https(struct rpki_uri *uri)
-{
- return uri->type == UT_HTTPS;
-}
-
static char const *
get_filename(char const *file_path)
{
#include "data_structure/array_list.h"
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,
+ UT_TA_RSYNC, /* TAL's TA URL; downloaded via rsync. */
+ UT_TA_HTTP, /* TAL's TA URL; downloaded via HTTP. */
+ UT_RPP, /* caRepository; downloaded via rsync. */
+ UT_NOTIF, /* rpkiNotify; downloaded via HTTP. */
+ UT_TMP, /* Snapshot or delta; Downloaded via HTTP. */
+ UT_CAGED, /* Endangered species. */
+
+ UT_AIA, /* caIssuers. Not downloaded. */
+ UT_SO, /* signedObject. Not downloaded. */
+ UT_MFT, /* rpkiManifest. Not downloaded. */
};
struct rpki_uri;
int __uri_create(struct rpki_uri **, char const *, enum uri_type,
- bool, struct rpki_uri *, void const *, size_t);
+ struct rpki_uri *, void const *, size_t);
int uri_create_mft(struct rpki_uri **, char const *, struct rpki_uri *,
struct rpki_uri *, IA5String_t *);
struct rpki_uri *uri_create_cache(char const *);
-#define uri_create(uri, tal, type, is_notif, notif, guri) \
- __uri_create(uri, tal, type, is_notif, notif, guri, strlen(guri))
+#define uri_create(uri, tal, type, notif, guri) \
+ __uri_create(uri, tal, type, notif, guri, strlen(guri))
#define uri_create_caged(uri, tal, notif, guri, guri_len) \
- __uri_create(uri, tal, UT_CAGED, false, notif, guri, guri_len)
+ __uri_create(uri, tal, UT_CAGED, notif, guri, guri_len)
#define uri_create_cage(uri, tal, notif) \
uri_create_caged(uri, tal, notif, "", 0)
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 *);
-bool uri_is_notif(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 *);
return ENOENT;
}
+int
+file_rm_f(char const *file)
+{
+ file_rm_rf(file);
+ return 0;
+}
+
+MOCK_ABORT_INT(file_get_mtim, char const *file, time_t *ims)
+
static int
pretend_download(struct rpki_uri *uri)
{
}
int
-http_download(struct rpki_uri *uri, bool *changed)
+http_download(struct rpki_uri *uri, curl_off_t ims, bool *changed)
{
int error;
https_counter++;
enum uri_type type;
if (str_starts_with(url, "https://"))
- type = UT_HTTPS;
+ type = UT_TA_HTTP;
else if (str_starts_with(url, "rsync://"))
- type = UT_RSYNC;
+ type = UT_RPP;
else
ck_abort_msg("Bad protocol: %s", url);
rsync_counter = 0;
https_counter = 0;
- ck_assert_int_eq(0, uri_create(&uri, TAL_FILE, type, false, NULL, url));
- ck_assert_int_eq(expected_error, cache_download(cache, uri, NULL));
+ ck_assert_int_eq(0, uri_create(&uri, TAL_FILE, type, NULL, url));
+ ck_assert_int_eq(expected_error, cache_download(cache, uri, 0, NULL));
ck_assert_uint_eq(rsync_calls, rsync_counter);
ck_assert_uint_eq(https_calls, https_counter);
struct cache_node *result;
if (str_starts_with(url, "https://"))
- type = UT_HTTPS;
+ type = is_notif ? UT_NOTIF : UT_TA_HTTP;
else if (str_starts_with(url, "rsync://"))
- type = UT_RSYNC;
+ type = UT_RPP;
else
ck_abort_msg("Bad protocol: %s", url);
result = pzalloc(sizeof(struct cache_node));
- ck_assert_int_eq(0, uri_create(&result->url, TAL_FILE, type, is_notif,
- NULL, url));
+ ck_assert_int_eq(0, uri_create(&result->url, TAL_FILE, type, NULL, url));
result->attempt.ts = attempt;
result->attempt.result = err;
result->success.happened = succeeded;
json_decref(json);
ck_assert_str_eq(
- "[{\"url\":\"rsync://a.b.c/d\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
- "{\"url\":\"rsync://a.b.c/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1},"
- "{\"url\":\"rsync://x.y.z/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
- "{\"url\":\"https://a/b\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
- "{\"url\":\"https://a/c\",\"is-rrdp-notification\":true,\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"}]",
+ "[{\"type\":\"RPP\",\"url\":\"rsync://a.b.c/d\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
+ "{\"type\":\"RPP\",\"url\":\"rsync://a.b.c/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1},"
+ "{\"type\":\"RPP\",\"url\":\"rsync://x.y.z/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
+ "{\"type\":\"TA (HTTP)\",\"url\":\"https://a/b\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
+ "{\"type\":\"RRDP Notification\",\"url\":\"https://a/c\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"}]",
str);
free(str);
va_start(args, uris);
while ((str = va_arg(args, char const *)) != NULL) {
if (str_starts_with(str, "https://"))
- type = UT_HTTPS;
+ type = UT_TA_HTTP;
else if (str_starts_with(str, "rsync://"))
- type = UT_RSYNC;
+ type = UT_RPP;
else
ck_abort_msg("Bad protocol: %s", str);
- ck_assert_int_eq(0, uri_create(&uri, TAL_FILE, type, false,
- NULL, str));
+ ck_assert_int_eq(0, uri_create(&uri, TAL_FILE, type, NULL, str));
uris_add(uris, uri);
}
va_end(args);
/* Query on empty database */
PREPARE_URI_LIST(&uris, "rsync://a.b.c/d", "https://a.b.c/d");
- ck_assert_ptr_eq(NULL, cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(NULL, cache_recover(cache, &uris));
uris_cleanup(&uris);
/* Only first URI is cached */
run_cache_download("rsync://a/b/c", 0, 1, 0);
PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
- ck_assert_ptr_eq(uris.array[0], cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[0], cache_recover(cache, &uris));
uris_cleanup(&uris);
/* Only second URI is cached */
run_cache_download("https://d/e", 0, 0, 1);
PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
- ck_assert_ptr_eq(uris.array[1], cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[1], cache_recover(cache, &uris));
uris_cleanup(&uris);
/* Only third URI is cached */
run_cache_download("https://f", 0, 0, 1);
PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
- ck_assert_ptr_eq(uris.array[2], cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[2], cache_recover(cache, &uris));
uris_cleanup(&uris);
/* None was cached */
run_cache_download("rsync://d/e", 0, 1, 0);
PREPARE_URI_LIST(&uris, "rsync://a/b/c", "https://d/e", "https://f");
- ck_assert_ptr_eq(NULL, cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(NULL, cache_recover(cache, &uris));
uris_cleanup(&uris);
/*
/* 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(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[2], cache_recover(cache, &uris));
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(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[0], cache_recover(cache, &uris));
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_eq(NULL, cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(NULL, cache_recover(cache, &uris));
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(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[1], cache_recover(cache, &uris));
uris_cleanup(&uris);
/*
* outdatedness is not that severe.
*/
PREPARE_URI_LIST(&uris, "rsync://a/2", "rsync://b/2");
- ck_assert_ptr_eq(uris.array[0], cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[0], cache_recover(cache, &uris));
uris_cleanup(&uris);
/* Parents of downloaded nodes */
PREPARE_URI_LIST(&uris, "rsync://a", "rsync://b");
- ck_assert_ptr_eq(NULL, cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(NULL, cache_recover(cache, &uris));
uris_cleanup(&uris);
/* Try them all at the same time */
"rsync://b", "rsync://b/1", "rsync://b/2", "rsync://b/3",
"rsync://b/4", "rsync://b/5", "rsync://b/6",
"rsync://e/1");
- ck_assert_ptr_eq(uris.array[5], cache_recover(cache, &uris, false));
+ ck_assert_ptr_eq(uris.array[5], cache_recover(cache, &uris));
uris_cleanup(&uris);
cleanup_test();
#include "types/uri.c"
#include "crypto/hash.c"
+MOCK_ABORT_INT(cache_tmpfile, char **filename)
+
/* Actually mostly tests libcrypto's sanity, not Fort's. */
START_TEST(test_hash)
{
uri.global = "https://example.com/resources/lorem-ipsum.txt";
uri.global_len = strlen(uri.global);
uri.local = "resources/lorem-ipsum.txt";
- uri.type = UT_HTTPS;
- uri.is_notif = false;
+ uri.type = UT_TA_HTTP;
uri.references = 1;
ha = hash_get_sha1();
/* Mocks */
+int
+cache_tmpfile(char **filename)
+{
+ static unsigned int file_counter = 0;
+ char *result;
+ int written;
+
+ result = pmalloc(10);
+ written = snprintf(result, 10, "tmp/%u", file_counter);
+ ck_assert(4 < written && written < 10);
+
+ *filename = result;
+ return 0;
+}
+
MOCK_ABORT_INT(cache_download, struct rpki_cache *cache, struct rpki_uri *uri,
- bool *changed)
+ curl_off_t ims, bool *changed)
MOCK_ABORT_VOID(fnstack_pop, void)
MOCK_ABORT_VOID(fnstack_push_uri, struct rpki_uri *uri)
MOCK_ABORT_PTR(validation_cache, rpki_cache, struct validation *state)
ck_assert_int_eq(0, relax_ng_init());
uri.local = "resources/rrdp/notif-ok.xml";
+ uri.type = UT_NOTIF;
uri.references = 1;
ck_assert_int_eq(0, parse_notification(&uri, ¬if));
ck_assert_int_eq(0, relax_ng_init());
uri.local = "resources/rrdp/notif-0deltas.xml";
+ uri.type = UT_NOTIF;
uri.references = 1;
ck_assert_int_eq(0, parse_notification(&uri, ¬if));
ck_assert_int_eq(0, relax_ng_init());
uri.local = "resources/rrdp/notif-large-serial.xml";
+ uri.type = UT_NOTIF;
uri.references = 1;
ck_assert_int_eq(0, parse_notification(&uri, ¬if));
ck_assert_int_eq(0, relax_ng_init());
uri.local = file;
+ uri.type = UT_NOTIF;
uri.references = 1;
ck_assert_int_eq(-EINVAL, parse_notification(&uri, ¬if));
notif_uri.global = "https://example.com/notification.xml";
notif_uri.global_len = strlen(notif_uri.global);
notif_uri.local = "cache/example.com/notification.xml";
- notif_uri.type = UT_HTTPS;
- notif_uri.is_notif = true;
+ notif_uri.type = UT_NOTIF;
notif_uri.references = 1;
snapshot_uri.local = "resources/rrdp/snapshot-bad-publish.xml";
MOCK(cache_create, struct rpki_cache *, NULL, char const *tal)
MOCK_VOID(cache_destroy, struct rpki_cache *cache)
MOCK_ABORT_INT(cache_download, struct rpki_cache *cache, struct rpki_uri *uri,
- bool *changed)
+ curl_off_t ims, bool *changed)
MOCK_ABORT_INT(cache_download_alt, struct rpki_cache *cache,
- struct uri_list *uris, bool use_rrdp, uris_dl_cb cb, void *arg)
+ struct uri_list *uris, enum uri_type http_type, enum uri_type rsync_type,
+ uris_dl_cb cb, void *arg)
MOCK_ABORT_PTR(cache_recover, rpki_uri, struct rpki_cache *cache,
- struct uri_list *uris, bool use_rrdp)
+ struct uri_list *uris)
+MOCK_ABORT_INT(cache_tmpfile, char **filename)
MOCK_ABORT_VOID(cache_teardown, void)
MOCK_ABORT_INT(certificate_traverse, struct rpp *rpp_parent,
struct rpki_uri *cert_uri)
MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
+int
+cache_tmpfile(char **filename)
+{
+ static bool used = false;
+
+ if (used) {
+ ck_abort_msg("cache_tmpfile() called a second time!");
+ return -EINVAL;
+ }
+
+ *filename = pstrdup("tmp/tmp/0");
+ used = true;
+ return 0;
+}
+
/* Tests */
-#define URI_CREATE_HTTP(uri, str) uri_create(&uri, "test.tal", UT_HTTPS, false, NULL, str)
-#define URI_CREATE_RSYNC(uri, str) uri_create(&uri, "test.tal", UT_RSYNC, false, NULL, str)
+#define URI_CREATE_HTTP(uri, str) uri_create(&uri, "test.tal", UT_TA_HTTP, NULL, str)
+#define URI_CREATE(uri, type, str) uri_create(&uri, "test.tal", type, NULL, str)
START_TEST(test_constructor)
{
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_RSYNC(uri, "https://a.b.c/d"));
+ ck_assert_int_eq(ENOTRSYNC, URI_CREATE(uri, UT_RPP, "https://a.b.c/d"));
+
+ ck_assert_int_eq(0, URI_CREATE(uri, UT_RPP, "rsync://a.b.c/d"));
+ ck_assert_str_eq("rsync://a.b.c/d", uri_get_global(uri));
+ ck_assert_str_eq("tmp/test.tal/rsync/a.b.c/d", uri_get_local(uri));
+ uri_refput(uri);
+
+ ck_assert_int_eq(0, URI_CREATE(uri, UT_TA_RSYNC, "rsync://a.b.c/d.cer"));
+ ck_assert_str_eq("rsync://a.b.c/d.cer", uri_get_global(uri));
+ ck_assert_str_eq("tmp/test.tal/rsync/a.b.c/d.cer", uri_get_local(uri));
+ uri_refput(uri);
+
+ ck_assert_int_eq(0, URI_CREATE(uri, UT_NOTIF, "https://a.b.c/notification.xml"));
+ ck_assert_str_eq("https://a.b.c/notification.xml", uri_get_global(uri));
+ ck_assert_str_eq("tmp/test.tal/https/a.b.c/notification.xml", uri_get_local(uri));
+ uri_refput(uri);
+
+ ck_assert_int_eq(0, URI_CREATE(uri, UT_TMP, "https://a.b.c/snapshot.xml"));
+ ck_assert_str_eq("https://a.b.c/snapshot.xml", uri_get_global(uri));
+ ck_assert_str_eq("tmp/tmp/0", uri_get_local(uri));
+ uri_refput(uri);
}
END_TEST
{
struct rpki_uri *uri;
- ck_assert_int_eq(0, uri_create(¬if, "test.tal", UT_HTTPS, true, NULL, "https://a.b.c/d/e.xml"));
- ck_assert_int_eq(0, uri_create(&uri, "test.tal", UT_CAGED, false, notif, "rsync://x.y.z/v/w.cer"));
+ ck_assert_int_eq(0, uri_create(¬if, "test.tal", UT_NOTIF, NULL, "https://a.b.c/d/e.xml"));
+ ck_assert_int_eq(0, uri_create(&uri, "test.tal", UT_CAGED, notif, "rsync://x.y.z/v/w.cer"));
ck_assert_str_eq("tmp/test.tal/rrdp/a.b.c/d/e.xml/x.y.z/v/w.cer", uri_get_local(uri));
uri_refput(uri);
uri_refput(notif);
- ck_assert_int_eq(0, uri_create(¬if, "test.tal", UT_HTTPS, true, NULL, "https://a.b.c"));
- ck_assert_int_eq(0, uri_create(&uri, "test.tal", UT_CAGED, false, notif, "rsync://w"));
+ ck_assert_int_eq(0, uri_create(¬if, "test.tal", UT_NOTIF, NULL, "https://a.b.c"));
+ ck_assert_int_eq(0, uri_create(&uri, "test.tal", UT_CAGED, notif, "rsync://w"));
ck_assert_str_eq("tmp/test.tal/rrdp/a.b.c/w", uri_get_local(uri));
uri_refput(uri);
uri_refput(notif);