- Remember old delta hashes.
- If one of the delta hashes changes in the notification, the session
needs to be re-snapshot'd, even if the session ID and serial seem
consistent.
.P
(If the RRDP notification lists more than this amount of unprocessed deltas,
Fort will reset the session, exploding the snapshot instead.)
+.P
+Per draft-spaghetti-sidrops-rrdp-desynchronization's recommendation, this is
+also the maximum number of delta hashes Fort will remember per RRDP session, to
+detect session desynchronization.
.RE
.P
time_t ts;
} success;
+ struct cachefile_notification *notif;
+
UT_hash_handle hh; /* Hash table hook */
};
#define TAGNAME_ATTEMPT_TS "attempt-timestamp"
#define TAGNAME_ATTEMPT_ERR "attempt-result"
#define TAGNAME_SUCCESS_TS "success-timestamp"
+#define TAGNAME_NOTIF "notification"
#define TYPEVALUE_TA_RSYNC "TA (rsync)"
#define TYPEVALUE_TA_HTTP "TA (HTTP)"
error = pb_init_cache(&pb, NULL, TMPDIR);
if (error)
return error;
+
error = pb_append_u32(&pb, atomic_fetch_add(&file_counter, 1u));
if (error) {
pb_cleanup(&pb);
get_tal_json_filename(struct rpki_cache *cache)
{
struct path_builder pb;
- return pb_init_cache(&pb, cache->tal, TAL_METAFILE)
- ? NULL : pb.string;
+ return pb_init_cache(&pb, cache->tal, TAL_METAFILE) ? NULL : pb.string;
}
static struct cache_node *
char const *type_str;
enum uri_type type;
char const *url;
+ json_t *notif;
int error;
node = pzalloc(sizeof(struct cache_node));
goto fail;
}
+ if (type == UT_NOTIF) {
+ error = json_get_object(json, TAGNAME_NOTIF, ¬if);
+ switch (error) {
+ case 0:
+ error = rrdp_json2notif(notif, &node->notif);
+ if (error)
+ goto fail;
+ break;
+ case ENOENT:
+ node->notif = NULL;
+ break;
+ default:
+ goto fail;
+ }
+ }
+
error = uri_create(&node->url, cache->tal, type, NULL, url);
if (error) {
pr_op_err("Cannot parse '%s' into a URI.", url);
fail:
uri_refput(node->url);
+ rrdp_notif_free(node->notif);
free(node);
return NULL;
}
-static struct cache_node*
-find_node(struct rpki_cache *cache, char const *luri)
+static struct cache_node *
+find_node(struct rpki_cache *cache, struct rpki_uri *uri)
{
+ char const *key;
struct cache_node *result;
- HASH_FIND_STR(cache->ht, luri, result);
+
+ key = uri_get_global(uri);
+ HASH_FIND_STR(cache->ht, key, result);
+
return result;
}
static void
add_node(struct rpki_cache *cache, struct cache_node *node)
{
- char const *key = uri_get_local(node->url);
+ char const *key = uri_get_global(node->url);
size_t keylen = strlen(key);
HASH_ADD_KEYPTR(hh, cache->ht, key, keylen, node);
}
{
json_t *json;
char const *type;
+ json_t *notification;
json = json_object();
if (json == NULL)
goto cancel;
if (json_add_str(json, TAGNAME_URL, uri_get_global(node->url)))
goto cancel;
+ if (node->notif != NULL) {
+ notification = rrdp_notif2json(node->notif);
+ if (notification == NULL)
+ goto cancel;
+ if (json_add_obj(json, TAGNAME_NOTIF, notification))
+ 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))
/**
* @ims and @changed only on HTTP.
+ * @ims can be zero, which means "no IMS."
+ * @changed can be NULL.
*/
int
-cache_download(struct rpki_cache *cache, struct rpki_uri *uri,
- curl_off_t ims, bool *changed)
+cache_download(struct rpki_cache *cache, struct rpki_uri *uri, bool *changed,
+ struct cachefile_notification ***notif)
{
struct rpki_uri *url;
struct cache_node *node;
if (error)
return error;
- node = find_node(cache, uri_get_local(url));
+ node = find_node(cache, url);
if (node != NULL) {
- uri_refput(url);
- if (was_recently_downloaded(cache, node))
- return node->attempt.result;
- url = node->url;
+ if (was_recently_downloaded(cache, node)) {
+ error = node->attempt.result;
+ goto end;
+ }
} else {
node = pzalloc(sizeof(struct cache_node));
node->url = url;
+ uri_refget(url);
add_node(cache, node);
}
case UT_NOTIF:
case UT_TMP:
error = config_get_http_enabled()
- ? http_download(url, ims, changed)
+ ? http_download(url, node->success.ts, changed)
: cache_check(url);
break;
case UT_TA_RSYNC:
node->success.ts = node->attempt.ts;
}
+end:
+ uri_refput(url);
+ if (!error && (notif != NULL))
+ *notif = &node->notif;
return error;
}
static int
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));
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);
+ error = cache_download(cache, uri, NULL, NULL);
break;
case UT_NOTIF:
error = rrdp_update(uri);
if (get_url(cursor.uri, cache->tal, &url) != 0)
continue;
- cursor.node = find_node(cache, uri_get_local(url));
+ cursor.node = find_node(cache, url);
uri_refput(url);
if (cursor.node == NULL)
continue;
{
HASH_DEL(cache->ht, node);
uri_refput(node->url);
+ rrdp_notif_free(node->notif);
free(node);
}
static void
cleanup_tmp(struct rpki_cache *cache, struct cache_node *node)
{
+ enum uri_type type;
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));
+ type = uri_get_type(node->url);
+ if (type != UT_NOTIF && type != UT_TMP)
+ return;
+
+ 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));
+
+ if (type != UT_NOTIF)
delete_node(cache, node);
- }
}
static void
int error;
path = uri_get_local(node->url);
+ if (uri_get_type(node->url) == UT_NOTIF)
+ goto skip_file;
+
error = file_exists(path);
switch (error) {
case 0:
uri_op_get_printable(node->url), error, strerror(error));
}
+skip_file:
if (!is_node_fresh(node, last_week)) {
pr_op_debug("Deleting expired cache element %s.", path);
file_rm_rf(path);
#ifndef SRC_CACHE_LOCAL_CACHE_H_
#define SRC_CACHE_LOCAL_CACHE_H_
-#include <curl/curl.h>
#include "types/uri.h"
struct rpki_cache;
/* Will destroy the cache object, but not the cache directory itself, obv. */
void cache_destroy(struct rpki_cache *);
+struct cachefile_notification; /* FIXME */
+
/* Downloads @uri into the cache */
-int cache_download(struct rpki_cache *, struct rpki_uri *uri, curl_off_t, bool *);
+int cache_download(struct rpki_cache *, struct rpki_uri *uri, bool *,
+ struct cachefile_notification ***);
/*
* The callback should return
* (If the RRDP notification lists more than this amount of
* unprocessed deltas, Fort will reset the session, exploding
* the snapshot instead.)
+ *
+ * Per draft-spaghetti-sidrops-rrdp-desynchronization's
+ * recommendation, this is also the maximum number of delta
+ * hashes Fort will remember per RRDP session, to detect session
+ * desynchronization.
*/
unsigned int delta_threshold;
} rrdp;
#include "log.h"
#include "crypto/hash.h"
-#define SHA256_LEN (256 >> 3) /* 256 / 8, bits -> bytes */
-
/* These are arbitrary; feel free to change them. */
#ifndef INITIAL_CAPACITY /* Unit tests want to override this */
#define INITIAL_CAPACITY 128u
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_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 *);
return 0;
}
+
+int json_add_obj(json_t *parent, char const *name, json_t *value)
+{
+ if (json_object_set_new(parent, name, value))
+ return pr_op_err("Cannot add '%s' to json; unknown cause.", name);
+
+ return 0;
+}
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);
+int json_add_obj(json_t *, char const *, json_t *);
#endif /* SRC_JSON_UTIL_H_ */
download_rpp(struct sia_uris *uris)
{
struct rpki_uri *uri;
- struct rpki_cache *cache;
int error;
if (uris->rpp.len == 0) {
return NULL;
}
- cache = validation_cache(state_retrieve());
- error = cache_download_alt(cache, &uris->rpp, UT_NOTIF, UT_RPP,
- retrieve_uri, &uri);
+ error = cache_download_alt(validation_cache(state_retrieve()),
+ &uris->rpp, UT_NOTIF, UT_RPP, retrieve_uri, &uri);
return error ? NULL : uri;
}
#include <ctype.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
+#include <sys/queue.h>
#include "alloc.h"
#include "common.h"
#include "config.h"
#include "file.h"
+#include "json_util.h"
#include "log.h"
#include "thread_var.h"
#include "cache/local_cache.h"
/* These are supposed to be unbounded */
struct rrdp_serial {
BIGNUM *num;
- char *str; /* for printing */
+ char *str; /* String version of @num. */
};
struct rrdp_session {
struct rrdp_serial serial;
};
-/* The hash is sometimes omitted. */
struct file_metadata {
struct rpki_uri *uri;
- unsigned char *hash;
+ unsigned char *hash; /* Array. Sometimes omitted. */
size_t hash_len;
};
HR_IGNORE,
} hash_requirement;
+#define RRDP_HASH_LEN SHA256_DIGEST_LENGTH
+
+struct rrdp_hash {
+ unsigned char bytes[RRDP_HASH_LEN];
+ STAILQ_ENTRY(rrdp_hash) hook;
+};
+
+/*
+ * Subset of the notification that is relevant to the TAL's cachefile.
+ */
+struct cachefile_notification {
+ struct rrdp_session session;
+ /*
+ * The 1st one contains the hash of the session.serial delta.
+ * The 2nd one contains the hash of the session.serial - 1 delta.
+ * The 3rd one contains the hash of the session.serial - 2 delta.
+ * And so on.
+ */
+ STAILQ_HEAD(, rrdp_hash) delta_hashes;
+};
+
static BIGNUM *
BN_create(void)
{
return result;
}
+static int
+parse_uri(xmlTextReaderPtr reader, struct rpki_uri *notif,
+ struct rpki_uri **result)
+{
+ xmlChar *xmlattr;
+ int error;
+
+ xmlattr = parse_string(reader, RRDP_ATTR_URI);
+ if (xmlattr == NULL)
+ return -EINVAL;
+
+ error = uri_create(result,
+ tal_get_file_name(validation_tal(state_retrieve())),
+ (notif != NULL) ? UT_CAGED : UT_TMP,
+ notif, (char const *)xmlattr);
+
+ xmlFree(xmlattr);
+ return error;
+}
+
static unsigned int
hexchar2uint(xmlChar xmlchar)
{
}
static int
-hexstr2sha256(xmlChar *hexstr, unsigned char **result)
+hexstr2sha256(xmlChar *hexstr, unsigned char **result, size_t *hash_len)
{
unsigned char *hash;
unsigned int digit;
size_t i;
- if (xmlStrlen(hexstr) != 2 * SHA256_DIGEST_LENGTH)
+ if (xmlStrlen(hexstr) != 2 * RRDP_HASH_LEN)
return EINVAL;
- hash = pmalloc(SHA256_DIGEST_LENGTH);
+ hash = pmalloc(RRDP_HASH_LEN);
- for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ for (i = 0; i < RRDP_HASH_LEN; i++) {
digit = hexchar2uint(hexstr[2 * i]);
if (digit > 15)
goto fail;
}
*result = hash;
+ *hash_len = RRDP_HASH_LEN;
return 0;
fail:
}
static int
-parse_hash(xmlTextReaderPtr reader, hash_requirement hr, char const *attr,
+parse_hash(xmlTextReaderPtr reader, hash_requirement hr,
unsigned char **result, size_t *result_len)
{
xmlChar *xmlattr;
if (hr == HR_IGNORE)
return 0;
- xmlattr = xmlTextReaderGetAttribute(reader, BAD_CAST attr);
+ xmlattr = xmlTextReaderGetAttribute(reader, BAD_CAST RRDP_ATTR_HASH);
if (xmlattr == NULL)
return (hr == HR_MANDATORY)
- ? pr_val_err("Tag is missing the '%s' attribute.", attr)
+ ? pr_val_err("Tag is missing the '" RRDP_ATTR_HASH "' attribute.")
: 0;
- error = hexstr2sha256(xmlattr, result);
+ error = hexstr2sha256(xmlattr, result, result_len);
xmlFree(xmlattr);
if (error)
- return pr_val_err("The '%s' xml attribute does not appear to be a SHA-256 hash.",
- attr);
- *result_len = SHA256_DIGEST_LENGTH;
+ return pr_val_err("The '" RRDP_ATTR_HASH "' xml attribute does not appear to be a SHA-256 hash.");
return 0;
}
parse_file_metadata(xmlTextReaderPtr reader, struct rpki_uri *notif,
hash_requirement hr, struct file_metadata *meta)
{
- xmlChar *uri;
int error;
memset(meta, 0, sizeof(*meta));
- uri = parse_string(reader, RRDP_ATTR_URI);
- if (uri == NULL)
- return -EINVAL;
- error = uri_create(&meta->uri,
- tal_get_file_name(validation_tal(state_retrieve())),
- (notif != NULL) ? UT_CAGED : UT_TMP, notif, (char const *)uri);
- xmlFree(uri);
+ error = parse_uri(reader, notif, &meta->uri);
if (error)
return error;
- if (hr == HR_IGNORE)
- return 0;
-
- error = parse_hash(reader, hr, RRDP_ATTR_HASH, &meta->hash,
- &meta->hash_len);
+ error = parse_hash(reader, hr, &meta->hash, &meta->hash_len);
if (error) {
uri_refput(meta->uri);
meta->uri = NULL;
return error;
/* rfc8181#section-2.2 but considering optional hash */
- if (tag->meta.hash_len > 0)
- return validate_hash(&tag->meta);
-
- return 0;
+ return (tag->meta.hash != NULL) ? validate_hash(&tag->meta) : 0;
}
static int
xml_read_snapshot, &ctx);
}
+static int
+validate_session_desync(struct cachefile_notification *old_notif,
+ struct update_notification *new_notif)
+{
+ struct rrdp_hash *old_delta;
+ struct file_metadata *new_delta;
+ size_t i;
+ size_t delta_threshold;
+
+ if (strcmp(old_notif->session.session_id, new_notif->session.session_id) != 0) {
+ pr_val_debug("The Notification's session ID changed.");
+ return EINVAL;
+ }
+
+ old_delta = STAILQ_FIRST(&old_notif->delta_hashes);
+ delta_threshold = config_get_rrdp_delta_threshold();
+
+ for (i = 0; i < delta_threshold; i++) {
+ if (old_delta == NULL)
+ return 0; /* Cache has few deltas */
+ if (i >= new_notif->deltas.len)
+ return 0; /* Notification has few deltas */
+
+ new_delta = &new_notif->deltas.array[i].meta;
+ if (memcmp(old_delta->bytes, new_delta->hash, RRDP_HASH_LEN) != 0) {
+ pr_val_debug("Notification delta hash does not match cached delta hash; RRDP session desynchronization detected.");
+ return EINVAL;
+ }
+
+ old_delta = STAILQ_NEXT(old_delta, hook);
+ }
+
+ return 0; /* First $delta_threshold delta hashes match */
+}
+
static int
handle_snapshot(struct update_notification *notif)
{
* Maybe stream it instead.
* Same for deltas.
*/
- error = cache_download(validation_cache(state), uri, 0, NULL);
+ error = cache_download(validation_cache(state), uri, NULL, 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, 0, NULL);
+ error = cache_download(validation_cache(state_retrieve()), uri, NULL, NULL);
if (error)
goto end;
error = parse_delta(notif, delta);
array_index d;
int error;
- /* No elements, send error so that the snapshot is processed */
if (notif->deltas.len == 0) {
pr_val_warn("There's no delta list to process.");
return -ENOENT;
return 0;
}
-static int
-get_metadata(struct rpki_uri *uri, struct rrdp_session *result)
+static void
+init_notif(struct update_notification *new, struct cachefile_notification *old)
{
- struct stat st;
- struct update_notification notification;
- int error;
+ size_t dn;
+ size_t i;
+ struct rrdp_hash *hash;
+
+ old->session = new->session;
+ memset(&new->session, 0, sizeof(new->session));
+ STAILQ_INIT(&old->delta_hashes);
+
+ dn = config_get_rrdp_delta_threshold();
+ if (new->deltas.len < dn)
+ dn = new->deltas.len;
- if (stat(uri_get_local(uri), &st) != 0) {
- error = errno;
- return (error == ENOENT) ? 0 : error;
+ for (i = 0; i < dn; i++) {
+ hash = pmalloc(sizeof(struct rrdp_hash));
+ memcpy(hash->bytes, new->deltas.array[i].meta.hash, RRDP_HASH_LEN);
+ STAILQ_INSERT_TAIL(&old->delta_hashes, hash, hook);
}
+}
- /*
- * TODO (fine) optimize by not reading everything,
- * or maybe keep it if it doesn't change.
- */
- error = parse_notification(uri, ¬ification);
- if (error)
- return error;
+static void
+drop_notif(struct cachefile_notification *notif)
+{
+ struct rrdp_hash *hash;
- *result = notification.session;
- memset(¬ification.session, 0, sizeof(notification.session));
- update_notification_cleanup(¬ification);
- return 0;
+ session_cleanup(¬if->session);
+ while (!STAILQ_EMPTY(¬if->delta_hashes)) {
+ hash = STAILQ_FIRST(¬if->delta_hashes);
+ STAILQ_REMOVE_HEAD(¬if->delta_hashes, hook);
+ free(hash);
+ }
+}
+
+static void
+update_notif(struct update_notification *new, struct cachefile_notification *old)
+{
+ BIGNUM *delta_bn;
+ BN_ULONG delta;
+ size_t d, dn;
+ struct rrdp_hash *hash;
+
+ delta_bn = BN_new();
+ if (!BN_sub(delta_bn, new->session.serial.num, old->session.serial.num)) {
+ // FIXME
+ }
+ if (BN_is_negative(delta_bn)) {
+ // FIXME
+ }
+
+ delta = BN_get_word(delta_bn);
+ if (delta > new->deltas.len) {
+ // FIXME
+ }
+
+ BN_free(old->session.serial.num);
+ free(old->session.serial.str);
+ old->session.serial = new->session.serial;
+ new->session.serial.num = NULL;
+ new->session.serial.str = NULL;
+
+ dn = delta;
+ STAILQ_FOREACH(hash, &old->delta_hashes, hook)
+ dn++;
+
+ for (d = new->deltas.len - delta; d < new->deltas.len; d++) {
+ hash = pmalloc(sizeof(struct rrdp_hash));
+ memcpy(hash->bytes, new->deltas.array[d].meta.hash, RRDP_HASH_LEN);
+ STAILQ_INSERT_TAIL(&old->delta_hashes, hash, hook);
+ }
+
+ while (dn > config_get_rrdp_delta_threshold()) {
+ hash = STAILQ_FIRST(&old->delta_hashes);
+ STAILQ_REMOVE_HEAD(&old->delta_hashes, hook);
+ free(hash);
+ dn--;
+ }
}
/*
int
rrdp_update(struct rpki_uri *uri)
{
- struct rrdp_session old = { 0 };
+ struct cachefile_notification **__old, *old;
struct update_notification new;
- time_t ims;
bool changed;
int error;
fnstack_push_uri(uri);
pr_val_debug("Processing notification.");
- error = get_metadata(uri, &old);
- if (error)
- goto end;
- pr_val_debug("Old session/serial: %s/%s", old.session_id,
- old.serial.str);
-
- error = file_get_mtim(uri_get_local(uri), &ims);
- if (error)
- return error;
-
error = cache_download(validation_cache(state_retrieve()), uri,
- ims, &changed);
+ &changed, &__old);
if (error)
goto end;
if (!changed) {
pr_val_debug("New session/serial: %s/%s", new.session.session_id,
new.session.serial.str);
- if (old.session_id == NULL || old.serial.num == NULL) {
+ old = *__old;
+ if (old == NULL) {
pr_val_debug("This is a new Notification.");
error = handle_snapshot(&new);
+ if (!error) {
+ *__old = pmalloc(sizeof(struct cachefile_notification));
+ init_notif(&new, *__old);
+ }
goto revert_notification;
}
- if (strcmp(old.session_id, new.session.session_id) != 0) {
- pr_val_debug("The Notification's session ID changed.");
+ error = validate_session_desync(old, &new);
+ if (error) {
+ pr_val_debug("Falling back to snapshot.");
error = handle_snapshot(&new);
+ if (!error) {
+ drop_notif(old);
+ init_notif(&new, old);
+ }
goto revert_notification;
}
- if (BN_cmp(old.serial.num, new.session.serial.num) != 0) {
+ if (BN_cmp(old->session.serial.num, new.session.serial.num) != 0) {
pr_val_debug("The Notification's serial changed.");
- error = handle_deltas(&new, &old.serial);
- if (error) {
+ error = handle_deltas(&new, &old->session.serial);
+ if (!error) {
+ update_notif(&new, old);
+ } else {
+ /* Error msg already printed. */
pr_val_debug("Falling back to snapshot.");
error = handle_snapshot(&new);
+ if (!error) {
+ drop_notif(old);
+ init_notif(&new, old);
+ }
}
goto revert_notification;
}
revert_notification:
update_notification_cleanup(&new);
end:
- session_cleanup(&old);
fnstack_pop();
return error;
}
+
+#define TAGNAME_SESSION "session_id"
+#define TAGNAME_SERIAL "serial"
+#define TAGNAME_DELTAS "deltas"
+
+/* binary to char */
+static char
+hash_b2c(unsigned char bin)
+{
+ bin &= 0xF;
+ return (bin < 10) ? (bin + '0') : (bin + 'a' - 10);
+}
+
+json_t *
+rrdp_notif2json(struct cachefile_notification *notif)
+{
+ json_t *json;
+ json_t *deltas;
+ char hash_str[2 * RRDP_HASH_LEN + 1];
+ struct rrdp_hash *hash;
+ size_t i;
+
+ if (notif == NULL)
+ return NULL;
+
+ json = json_object();
+ if (json == NULL)
+ enomem_panic();
+
+ if (json_add_str(json, TAGNAME_SESSION, notif->session.session_id))
+ goto fail;
+ if (json_add_str(json, TAGNAME_SERIAL, notif->session.serial.str))
+ goto fail;
+
+ if (STAILQ_EMPTY(¬if->delta_hashes))
+ return json; /* Happy path, but unlikely. */
+
+ deltas = json_array();
+ if (deltas == NULL)
+ enomem_panic();
+ if (json_add_obj(json, TAGNAME_DELTAS, deltas))
+ goto fail;
+
+ hash_str[2 * RRDP_HASH_LEN] = '\0';
+ STAILQ_FOREACH(hash, ¬if->delta_hashes, hook) {
+ for (i = 0; i < RRDP_HASH_LEN; i++) {
+ hash_str[2 * i ] = hash_b2c(hash->bytes[i] >> 4);
+ hash_str[2 * i + 1] = hash_b2c(hash->bytes[i] );
+ }
+ if (json_array_append(deltas, json_string(hash_str)))
+ goto fail;
+ }
+
+ return json;
+
+fail:
+ json_decref(json);
+ return NULL;
+}
+
+static char
+hash_c2b(char chara)
+{
+ if ('a' <= chara && chara <= 'f')
+ return chara - 'a' + 10;
+ if ('A' <= chara && chara <= 'F')
+ return chara - 'A' + 10;
+ if ('0' <= chara && chara <= '9')
+ return chara - '0';
+ return -1;
+}
+
+static int
+json2dh(json_t *json, struct rrdp_hash **result)
+{
+ char const *src;
+ size_t srclen;
+ struct rrdp_hash *dst;
+ char digit;
+ size_t i;
+
+ src = json_string_value(json);
+ if (src == NULL)
+ return pr_op_err("Hash is not a string.");
+
+ srclen = strlen(src);
+ if (srclen != 2 * RRDP_HASH_LEN)
+ return pr_op_err("Hash is not %d characters long.", 2 * RRDP_HASH_LEN);
+
+ dst = pmalloc(sizeof(struct rrdp_hash));
+ for (i = 0; i < RRDP_HASH_LEN; i++) {
+ digit = hash_c2b(src[2 * i]);
+ if (digit == -1)
+ goto bad_char;
+ dst->bytes[i] = digit << 4;
+ digit = hash_c2b(src[2 * i + 1]);
+ if (digit == -1)
+ goto bad_char;
+ dst->bytes[i] |= digit;
+ }
+
+ *result = dst;
+ return 0;
+
+bad_char:
+ free(dst);
+ return pr_op_err("Invalid characters in hash: %c%c", src[2 * i], src[2 * i] + 1);
+}
+
+static void
+clear_delta_hashes(struct cachefile_notification *notif)
+{
+ struct rrdp_hash *hash;
+
+ while (!STAILQ_EMPTY(¬if->delta_hashes)) {
+ hash = STAILQ_FIRST(¬if->delta_hashes);
+ STAILQ_REMOVE_HEAD(¬if->delta_hashes, hook);
+ free(hash);
+ }
+}
+
+int
+rrdp_json2notif(json_t *json, struct cachefile_notification **result)
+{
+ struct cachefile_notification *notif;
+ char const *str;
+ json_t *jdeltas;
+ size_t d, dn;
+ struct rrdp_hash *hash;
+ int error;
+
+ notif = pzalloc(sizeof(struct cachefile_notification));
+ STAILQ_INIT(¬if->delta_hashes);
+
+ error = json_get_str(json, TAGNAME_SESSION, &str);
+ if (error) {
+ if (error > 0)
+ pr_op_err("Node is missing the '" TAGNAME_SESSION "' tag.");
+ goto revert_notif;
+ }
+ notif->session.session_id = pstrdup(str);
+
+ error = json_get_str(json, TAGNAME_SERIAL, &str);
+ if (error) {
+ if (error > 0)
+ pr_op_err("Node is missing the '" TAGNAME_SERIAL "' tag.");
+ goto revert_session;
+ }
+ notif->session.serial.str = pstrdup(str);
+
+ notif->session.serial.num = BN_new();
+ if (notif->session.serial.num == NULL)
+ enomem_panic();
+ if (!BN_dec2bn(¬if->session.serial.num, notif->session.serial.str)) {
+ error = pr_op_err("Not a serial number: %s", notif->session.serial.str);
+ goto revert_serial;
+ }
+
+ error = json_get_array(json, TAGNAME_DELTAS, &jdeltas);
+ if (error) {
+ if (error > 0)
+ goto success;
+ goto revert_serial;
+ }
+
+ dn = json_array_size(jdeltas);
+ if (dn == 0)
+ goto success;
+ if (dn > config_get_rrdp_delta_threshold())
+ dn = config_get_rrdp_delta_threshold();
+
+ for (d = 0; d < dn; d++) {
+ error = json2dh(json_array_get(jdeltas, d), &hash);
+ if (error)
+ goto revert_deltas;
+ STAILQ_INSERT_TAIL(¬if->delta_hashes, hash, hook);
+ }
+
+success:
+ *result = notif;
+ return 0;
+
+revert_deltas:
+ clear_delta_hashes(notif);
+revert_serial:
+ BN_free(notif->session.serial.num);
+ free(notif->session.serial.str);
+revert_session:
+ free(notif->session.session_id);
+revert_notif:
+ free(notif);
+ return error;
+}
+
+void
+rrdp_notif_free(struct cachefile_notification *notif)
+{
+ if (notif == NULL)
+ return;
+
+ session_cleanup(¬if->session);
+ clear_delta_hashes(notif);
+ free(notif);
+}
#include "types/uri.h"
+struct cachefile_notification;
+
int rrdp_update(struct rpki_uri *);
+json_t *rrdp_notif2json(struct cachefile_notification *);
+int rrdp_json2notif(json_t *, struct cachefile_notification **);
+
+void rrdp_notif_free(struct cachefile_notification *);
+
#endif /* SRC_RRDP_H_ */
#include "slurm/slurm_loader.h"
#include <errno.h>
+#include <openssl/sha.h>
+
#include "alloc.h"
#include "log.h"
#include "config.h"
SLIST_FOREACH(newcsum, new_list, next) {
SLIST_FOREACH(old, old_list, next) {
-
- if (newcsum->csum_len != old->csum_len)
- continue;
-
- if (memcmp(newcsum->csum, old->csum,
- newcsum->csum_len) == 0) {
+ if (memcmp(newcsum->csum, old->csum, SHA256_DIGEST_LENGTH) == 0) {
found = true;
break;
}
switch (uri->type) {
case UT_TA_RSYNC:
case UT_RPP:
+ case UT_MFT:
return map_simple(uri, tal, "rsync");
case UT_TA_HTTP:
- case UT_NOTIF:
return map_simple(uri, tal, "https");
+ case UT_NOTIF:
case UT_TMP:
return cache_tmpfile(&uri->local);
case UT_AIA:
case UT_SO:
- case UT_MFT:
uri->local = NULL;
return 0;
}
}
MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
+__MOCK_ABORT(rrdp_notif2json, json_t *, NULL, struct cachefile_notification *notif)
+MOCK_VOID(rrdp_notif_free, struct cachefile_notification *notif)
+MOCK_ABORT_INT(rrdp_json2notif, json_t *json, struct cachefile_notification **result)
/* Helpers */
https_counter = 0;
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_int_eq(expected_error, cache_download(cache, uri, NULL, NULL));
ck_assert_uint_eq(rsync_calls, rsync_counter);
ck_assert_uint_eq(https_calls, https_counter);
#include "alloc.c"
#include "common.c"
#include "file.c"
+#include "json_util.c"
#include "mock.c"
#include "rrdp.c"
#include "crypto/base64.c"
}
MOCK_ABORT_INT(cache_download, struct rpki_cache *cache, struct rpki_uri *uri,
- curl_off_t ims, bool *changed)
+ bool *changed, struct cachefile_notification ***notif)
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)
/* Mocks end */
+static void
+ck_rrdp_session(char const *session, char const *serial, struct rrdp_session *actual)
+{
+ BIGNUM *bn;
+
+ ck_assert_str_eq(session, actual->session_id);
+ ck_assert_str_eq(serial, actual->serial.str);
+
+ bn = BN_new();
+ ck_assert_ptr_ne(NULL, bn);
+ ck_assert_int_eq(strlen(serial), BN_dec2bn(&bn, serial));
+ ck_assert_int_eq(0, BN_cmp(bn, actual->serial.num));
+ BN_free(bn);
+}
+
+static struct cachefile_notification *
+create_cachefile_notif(char const *session, char const *serial, ...)
+{
+ struct cachefile_notification *notif;
+ struct rrdp_hash *hash;
+ int dh_byte;
+ va_list args;
+
+ notif = pmalloc(sizeof(struct cachefile_notification));
+
+ notif->session.session_id = pstrdup(session);
+ notif->session.serial.str = pstrdup(serial);
+ notif->session.serial.num = NULL; /* Not needed for now. */
+ STAILQ_INIT(¬if->delta_hashes);
+
+ va_start(args, serial);
+ while ((dh_byte = va_arg(args, int)) != 0) {
+ hash = pmalloc(sizeof(struct rrdp_hash));
+ memset(hash->bytes, dh_byte, sizeof(hash->bytes));
+ STAILQ_INSERT_TAIL(¬if->delta_hashes, hash, hook);
+ }
+ va_end(args);
+
+ return notif;
+}
+
START_TEST(test_xmlChar_NULL_assumption)
{
xmlChar *xmlstr;
{
char *hex;
unsigned char *sha = NULL;
+ size_t sha_len;
unsigned int i;
hex = "01";
- ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha));
+ ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha, &sha_len));
ck_assert_ptr_eq(NULL, sha);
hex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
- ck_assert_int_eq(0, hexstr2sha256((xmlChar *) hex, &sha));
+ sha_len = 0;
+ ck_assert_int_eq(0, hexstr2sha256((xmlChar *) hex, &sha, &sha_len));
ck_assert_ptr_ne(NULL, sha);
for (i = 0; i < 32; i++)
ck_assert_uint_eq(i, sha[i]);
+ ck_assert_uint_eq(32, sha_len);
free(sha);
sha = NULL;
hex = "0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
- ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha));
+ ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha, &sha_len));
ck_assert_ptr_eq(NULL, sha);
hex = " 00102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
- ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha));
+ ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha, &sha_len));
ck_assert_ptr_eq(NULL, sha);
hex = "0001020g0405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
- ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha));
+ ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha, &sha_len));
ck_assert_ptr_eq(NULL, sha);
hex = "0001020g0405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1";
- ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha));
+ ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha, &sha_len));
ck_assert_ptr_eq(NULL, sha);
hex = "0001020g0405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2";
- ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha));
+ ck_assert_int_eq(EINVAL, hexstr2sha256((xmlChar *) hex, &sha, &sha_len));
ck_assert_ptr_eq(NULL, sha);
}
END_TEST
return error;
}
+/* Not really designed to be used with args > END (because of @str). */
static void
add_serials(struct notification_deltas *deltas, ...)
{
struct notification_delta delta = { 0 };
- unsigned long cursor;
+ int cursor;
+ char str[6];
+ int written;
va_list vl;
va_start(vl, deltas);
- while ((cursor = va_arg(vl, unsigned long)) != END) {
+ while ((cursor = va_arg(vl, int)) != END) {
delta.serial.num = BN_create();
if (!BN_set_word(delta.serial.num, cursor))
ck_abort_msg("BN_set_word() returned zero.");
+
+ written = snprintf(str, sizeof(str), "%d", cursor) < sizeof(str);
+ ck_assert(1 <= written && written <= sizeof(str));
+ delta.serial.str = pstrdup(str);
+
notification_deltas_add(deltas, &delta);
}
va_end(vl);
{
struct notification_deltas deltas;
+ /* No deltas */
notification_deltas_init(&deltas);
ck_assert_int_eq(0, __sort_deltas(&deltas, 0, "0"));
ck_assert_int_eq(0, __sort_deltas(&deltas, 1, "1"));
ck_assert_int_eq(0, __sort_deltas(&deltas, 2, "2"));
- add_serials(&deltas, 0, END);
- ck_assert_int_eq(0, __sort_deltas(&deltas, 0, "0"));
- validate_serials(&deltas, 0, END);
+ /* 1 delta */
+ add_serials(&deltas, 2, END);
+ ck_assert_int_eq(0, __sort_deltas(&deltas, 2, "2"));
+ validate_serials(&deltas, 2, END);
- ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 2, "2"));
+ /* Delta serial doesn't match session serial */
+ ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 4, "4"));
+ ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 3, "3"));
ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 1, "1"));
+ ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 0, "0"));
- add_serials(&deltas, 1, 2, 3, END);
- ck_assert_int_eq(0, __sort_deltas(&deltas, 3, "3"));
- validate_serials(&deltas, 0, 1, 2, 3, END);
+ /* More than 1 delta, already sorted */
+ add_serials(&deltas, 1, 3, 4, END);
+ ck_assert_int_eq(0, __sort_deltas(&deltas, 4, "4"));
+ validate_serials(&deltas, 1, 2, 3, 4, END);
- ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 4, "4"));
- ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 2, "2"));
+ /* More than 1 delta, they don't match session serial */
+ ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 5, "5"));
+ ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 3, "3"));
notification_deltas_cleanup(&deltas, notification_delta_cleanup);
notification_deltas_init(&deltas);
+ /* More than 1 delta, not already sorted but otherwise functional */
add_serials(&deltas, 3, 0, 1, 2, END);
ck_assert_int_eq(0, __sort_deltas(&deltas, 3, "3"));
validate_serials(&deltas, 0, 1, 2, 3, END);
notification_deltas_cleanup(&deltas, notification_delta_cleanup);
notification_deltas_init(&deltas);
+ /* Same, but order completely backwards */
add_serials(&deltas, 4, 3, 2, 1, 0, END);
ck_assert_int_eq(0, __sort_deltas(&deltas, 4, "4"));
validate_serials(&deltas, 0, 1, 2, 3, 4, END);
+ /* Same, but deltas don't match session serial */
ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 5, "5"));
ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 3, "3"));
notification_deltas_cleanup(&deltas, notification_delta_cleanup);
+ notification_deltas_init(&deltas);
+
+ /* More than 1 delta, 1 serial missing */
+ add_serials(&deltas, 1, 2, 4, END);
+ ck_assert_int_eq(-EINVAL, __sort_deltas(&deltas, 4, "4"));
}
END_TEST
}
END_TEST
+START_TEST(test_2s_simple)
+{
+ struct cachefile_notification *notif;
+ json_t *json, *jdeltas;
+ char const *str;
+
+ notif = create_cachefile_notif("session", "1234", 0);
+
+ json = rrdp_notif2json(notif);
+ ck_assert_ptr_ne(NULL, json);
+
+ rrdp_notif_free(notif);
+ notif = NULL;
+
+ ck_assert_int_eq(0, json_get_str(json, TAGNAME_SESSION, &str));
+ ck_assert_str_eq("session", str);
+ ck_assert_int_eq(0, json_get_str(json, TAGNAME_SERIAL, &str));
+ ck_assert_str_eq("1234", str);
+ ck_assert_int_eq(ENOENT, json_get_array(json, TAGNAME_DELTAS, &jdeltas));
+
+ ck_assert_int_eq(0, rrdp_json2notif(json, ¬if));
+ ck_rrdp_session("session", "1234", ¬if->session);
+ ck_assert_uint_eq(true, STAILQ_EMPTY(¬if->delta_hashes));
+
+ json_decref(json);
+ rrdp_notif_free(notif);
+}
+END_TEST
+
+static void
+ck_hash(struct rrdp_hash *hash, unsigned char chara)
+{
+ size_t i;
+ for (i = 0; i < sizeof(hash->bytes); i++)
+ ck_assert_uint_eq(chara, hash->bytes[i]);
+}
+
+START_TEST(test_2s_more)
+{
+ struct cachefile_notification *notif;
+ struct rrdp_hash *hash;
+ json_t *json, *jdeltas;
+ char const *str;
+
+ notif = create_cachefile_notif("session",
+ "123456789012345678901234567890123456789012",
+ 0xAA, 0xBB, 0xCD, 0);
+
+ json = rrdp_notif2json(notif);
+ ck_assert_ptr_ne(NULL, json);
+
+ rrdp_notif_free(notif);
+ notif = NULL;
+
+ ck_assert_int_eq(0, json_get_str(json, TAGNAME_SESSION, &str));
+ ck_assert_str_eq("session", str);
+ ck_assert_int_eq(0, json_get_str(json, TAGNAME_SERIAL, &str));
+ ck_assert_str_eq("123456789012345678901234567890123456789012", str);
+ ck_assert_int_eq(0, json_get_array(json, TAGNAME_DELTAS, &jdeltas));
+ ck_assert_uint_eq(3, json_array_size(jdeltas));
+ ck_assert_str_eq("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ json_string_value(json_array_get(jdeltas, 0)));
+ ck_assert_str_eq("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ json_string_value(json_array_get(jdeltas, 1)));
+ ck_assert_str_eq("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ json_string_value(json_array_get(jdeltas, 2)));
+
+ ck_assert_int_eq(0, rrdp_json2notif(json, ¬if));
+ ck_rrdp_session("session", "123456789012345678901234567890123456789012", ¬if->session);
+ hash = STAILQ_FIRST(¬if->delta_hashes);
+ ck_assert_ptr_ne(NULL, hash);
+ ck_hash(hash, 0xAA);
+ hash = STAILQ_NEXT(hash, hook);
+ ck_assert_ptr_ne(NULL, hash);
+ ck_hash(hash, 0xBB);
+ hash = STAILQ_NEXT(hash, hook);
+ ck_assert_ptr_ne(NULL, hash);
+ ck_hash(hash, 0xCD);
+ hash = STAILQ_NEXT(hash, hook);
+ ck_assert_ptr_eq(NULL, hash);
+
+ json_decref(json);
+ rrdp_notif_free(notif);
+}
+END_TEST
+
+void
+ck_json2notif(int expected, char const *json_str)
+{
+ json_t *json;
+ json_error_t error;
+ struct cachefile_notification *notif;
+
+ json = json_loads(json_str, 0, &error);
+ ck_assert_ptr_ne(NULL, json);
+
+ notif = NULL;
+ ck_assert_int_eq(expected, rrdp_json2notif(json, ¬if));
+
+ json_decref(json);
+ if (notif == NULL)
+ rrdp_notif_free(notif);
+}
+
+START_TEST(test_2s_errors)
+{
+ struct cachefile_notification notif = { 0 };
+
+ ck_assert_ptr_eq(NULL, rrdp_notif2json(NULL));
+ ck_assert_ptr_eq(NULL, rrdp_notif2json(¬if));
+ notif.session.session_id = "sid";
+ ck_assert_ptr_eq(NULL, rrdp_notif2json(¬if));
+
+ ck_json2notif(ENOENT, "{}");
+ ck_json2notif(0, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":\"123\" }");
+ ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":null, \"" TAGNAME_SERIAL "\":\"123\" }");
+ ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":null }");
+ ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":123, \"" TAGNAME_SERIAL "\":\"123\" }");
+ ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":123 }");
+ ck_json2notif(ENOENT, "{ \"" TAGNAME_SESSION "\":\"sss\" }");
+ ck_json2notif(ENOENT, "{ \"" TAGNAME_SERIAL "\":\"123\" }");
+ ck_json2notif(-EINVAL,
+ "{ \"" TAGNAME_SESSION "\":\"sss\","
+ "\"" TAGNAME_SERIAL "\":\"123\","
+ "\"" TAGNAME_DELTAS "\":null }");
+ ck_json2notif(-EINVAL,
+ "{ \"" TAGNAME_SESSION "\":\"sss\","
+ "\"" TAGNAME_SERIAL "\":\"123\","
+ "\"" TAGNAME_DELTAS "\":\"123\" }");
+ ck_json2notif(-EINVAL,
+ "{ \"" TAGNAME_SESSION "\":\"sss\","
+ "\"" TAGNAME_SERIAL "\":\"123\","
+ "\"" TAGNAME_DELTAS "\":{} }");
+ ck_json2notif(0,
+ "{ \"" TAGNAME_SESSION "\":\"sss\","
+ "\"" TAGNAME_SERIAL "\":\"123\","
+ "\"" TAGNAME_DELTAS "\":[] }");
+ ck_json2notif(-EINVAL,
+ "{ \"" TAGNAME_SESSION "\":\"sss\","
+ "\"" TAGNAME_SERIAL "\":\"123\","
+ "\"" TAGNAME_DELTAS "\":[ 1 ] }");
+ ck_json2notif(-EINVAL,
+ "{ \"" TAGNAME_SESSION "\":\"sss\","
+ "\"" TAGNAME_SERIAL "\":\"123\","
+ "\"" TAGNAME_DELTAS "\":[ \"111\" ] }");
+ ck_json2notif(0,
+ "{ \"" TAGNAME_SESSION "\":\"sss\","
+ "\"" TAGNAME_SERIAL "\":\"123\","
+ "\"" TAGNAME_DELTAS "\":[ \"1111111111111111111111111111111111111111111111111111111111111111\" ] }");
+}
+END_TEST
+
static Suite *xml_load_suite(void)
{
Suite *suite;
- TCase *misc;
+ TCase *misc, *parse, *cf;
misc = tcase_create("misc");
tcase_add_test(misc, test_xmlChar_NULL_assumption);
tcase_add_test(misc, test_hexstr2sha256);
tcase_add_test(misc, test_sort_deltas);
- tcase_add_test(misc, test_parse_notification_ok);
- tcase_add_test(misc, test_parse_notification_0deltas);
- tcase_add_test(misc, test_parse_notification_large_serial);
- tcase_add_test(misc, test_parse_notification_bad_xmlns);
- tcase_add_test(misc, test_parse_notification_bad_session_id);
- tcase_add_test(misc, test_parse_notification_bad_serial);
- tcase_add_test(misc, test_parse_notification_bad_hash);
- tcase_add_test(misc, test_parse_notification_bad_uri);
- tcase_add_test(misc, test_parse_snapshot_bad_publish);
+
+ parse = tcase_create("parse");
+ tcase_add_test(parse, test_parse_notification_ok);
+ tcase_add_test(parse, test_parse_notification_0deltas);
+ tcase_add_test(parse, test_parse_notification_large_serial);
+ tcase_add_test(parse, test_parse_notification_bad_xmlns);
+ tcase_add_test(parse, test_parse_notification_bad_session_id);
+ tcase_add_test(parse, test_parse_notification_bad_serial);
+ tcase_add_test(parse, test_parse_notification_bad_hash);
+ tcase_add_test(parse, test_parse_notification_bad_uri);
+ tcase_add_test(parse, test_parse_snapshot_bad_publish);
+
+ cf = tcase_create("cachefile");
+ tcase_add_test(parse, test_2s_simple);
+ tcase_add_test(parse, test_2s_more);
+ tcase_add_test(parse, test_2s_errors);
suite = suite_create("RRDP");
suite_add_tcase(suite, misc);
+ suite_add_tcase(suite, parse);
+ suite_add_tcase(suite, cf);
return suite;
}
MOCK_UINT(config_get_deltas_lifetime, deltas_lifetime, void)
MOCK_ABORT_ENUM(config_get_output_format, output_format, void)
-MOCK_ABORT_INT(hash_local_file, char const *uri, unsigned char *result,
- unsigned int *result_len)
+MOCK_ABORT_INT(hash_local_file, char const *uri, unsigned char *result)
/* Test functions */
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,
- curl_off_t ims, bool *changed)
+ bool *changed, struct cachefile_notification ***notif)
MOCK_ABORT_INT(cache_download_alt, struct rpki_cache *cache,
struct uri_list *uris, enum uri_type http_type, enum uri_type rsync_type,
uris_dl_cb cb, void *arg)
int
cache_tmpfile(char **filename)
{
- static bool used = false;
+ static unsigned int used = 1;
- if (used) {
- ck_abort_msg("cache_tmpfile() called a second time!");
+ if (used > 2) {
+ ck_abort_msg("cache_tmpfile() called a third time!");
return -EINVAL;
}
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));
+ ck_assert_str_eq("tmp/tmp/0", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE(uri, UT_TMP, "https://a.b.c/snapshot.xml"));