]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Improve the cache fallback algorithm
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 31 Oct 2023 18:17:34 +0000 (12:17 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Fri, 3 Nov 2023 23:01:39 +0000 (17:01 -0600)
If all of a RPP's URLs fail, fall back to most sensible cached
candidate.
It seems this used to be only implemented for TAs, and the heuristics
for choosing a suitable fallback were rudimentary.
Elaborate, centralize and extend implementation to all cache content.

Side maintenance tweaks:

- Remove EREQFAILED, because it largely evolved from meaning "don't try
  again" to "try again." So now it was just a redundant EAGAIN.
- Ditch redundant arguments from valid_file_or_dir().
- Merge the three URI arraylist implementations (certificate.c, tal.c
  and manifest.c) into one.
- Move RRDP workspace URI, from a thread variable to the stack.
  (Code smell. It used to be awkward to follow this variable's lifespan
  through the tree traversal.)
- Move struct publish and struct withdraw from the heap to the stack.
  (Eliminate pointless allocations. These are not the only RRDP
  structures I want to move to the stack.)
- Change file_metadata.uri from `char *` to `struct rpki_uri *`.
  (This string was forcing the RRDP code to recompute the URI
  repeatedly.)

26 files changed:
src/abbreviations.txt
src/cache/local_cache.c
src/cache/local_cache.h
src/common.c
src/common.h
src/config.c
src/data_structure/array_list.h
src/http/http.c
src/object/certificate.c
src/object/manifest.c
src/object/manifest.h
src/object/tal.c
src/rpp.c
src/rrdp/rrdp_loader.c
src/rrdp/rrdp_objects.c
src/rrdp/rrdp_objects.h
src/rrdp/rrdp_parser.c
src/rsync/rsync.c
src/state.c
src/state.h
src/types/uri.c
src/types/uri.h
test/cache/local_cache_test.c
test/mock.c
test/tal_test.c
test/types/uri_test.c

index 4f6fe2bddcee8ff03632c90dfedbef7a774dd9d8..bbb5b147e723ba5f323c0137c5af9ec7c21e95c0 100644 (file)
@@ -21,6 +21,7 @@ len: length
 max: maximum
 min: minimum
 msg: message
+notif: Notification
 pdu: Protocol Data Unit (RFC 6810)
 pp: Publication Point
 pr: print
index e40e4d5c93accb5ae5ce29ef2b99ab4f8533e623..2780c618cccfdf4af50fc751c47921d9a7c28f57 100644 (file)
@@ -444,13 +444,13 @@ cache_download(struct rpki_uri *uri, bool *changed)
                                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;
@@ -494,6 +494,144 @@ end:
        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
  */
index d826ae7573cec14471a6b56e4980fd18194dabf6..cb6db6b95d5a1d993ef32b389d7f05feebc789d7 100644 (file)
@@ -8,6 +8,10 @@ int cache_prepare(void); /* No revert needed */
 
 /* 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 */
index 0aba47b52795b141d9678086a5bb934e6760dd68..9d3d3cbce2805797a1cd91ef988cbb08a93c91ff 100644 (file)
@@ -201,32 +201,24 @@ foreach_file(char const *location, char const *file_ext, bool empty_err,
 }
 
 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;
 }
index 15bf84ff16dbf1aecc25868f170591bc609ed1af..6ccd70f3566dc724395dd6de6012b0880a2d698b 100644 (file)
@@ -6,29 +6,15 @@
 #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.
@@ -61,8 +47,7 @@ void rwlock_unlock(pthread_rwlock_t *);
 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 *);
index c5387092b635d3fe14cb57809caa81c215cceb9a..941c238e294c16de656d3ccc0787c1c6a4fbd7a0 100644 (file)
@@ -971,8 +971,7 @@ validate_config(void)
                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 */
@@ -993,8 +992,7 @@ validate_config(void)
            !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;
index 7aa56fa88fae915df1d387f7bff8bd7489b0ba93..96948a4a9fc994e5d77fc73f0119d5a86d7dd94d 100644 (file)
                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)
index f193d3683fb9713a5a9953ba50fd8ca269efec38..34fafdd4ede1e6038665a9793c09e8b557e08b8d 100644 (file)
@@ -250,9 +250,9 @@ handle_http_response_code(long http_code)
 {
        /* 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 */
 }
 
@@ -309,7 +309,7 @@ http_fetch(char const *src, char const *dst, curl_off_t ims, bool *changed)
                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);
@@ -371,7 +371,7 @@ do_retries(char const *src, char const *dst, curl_off_t ims, bool *changed)
                        pr_val_debug("Download successful.");
                        return 0; /* Happy path */
 
-               case EREQFAILED:
+               case EAGAIN:
                        break;
 
                default:
@@ -381,7 +381,7 @@ do_retries(char const *src, char const *dst, curl_off_t ims, bool *changed)
 
                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.",
index 1004c14856534343cb4a9cb69224ae41cdae3f51..7687e273d517c0bccf35cb51594fbcaefbfd0122 100644 (file)
@@ -49,10 +49,8 @@ struct ski_arguments {
        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;
 };
 
@@ -115,20 +113,14 @@ static const struct ad_metadata RPKI_MANIFEST = {
 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);
 }
 
@@ -1214,7 +1206,7 @@ handle_caRepository(struct rpki_uri *uri, void *arg)
 {
        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;
 }
@@ -1224,7 +1216,7 @@ handle_rpkiNotify(struct rpki_uri *uri, void *arg)
 {
        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;
 }
@@ -1531,7 +1523,7 @@ uri_create_ad(struct rpki_uri **uri, ACCESS_DESCRIPTION *ad, enum uri_type type)
         * 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));
 }
@@ -1927,63 +1919,15 @@ certificate_validate_aia(struct rpki_uri *caIssuers, X509 *cert)
        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. */
@@ -1995,6 +1939,7 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri)
        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;
@@ -2061,17 +2006,21 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri)
        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;
@@ -2082,7 +2031,6 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri)
        rpp_refput(pp);
 
 revert_uris:
-       validation_set_notification_uri(state, NULL);
        sia_uris_cleanup(&sia_uris);
 revert_cert:
        if (cert != NULL)
index 39309a5d9e8008a11564a95b78e0568dea17f542..7c82ad2ee9bf64e3148337c70517fca2e49fd226 100644 (file)
 #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));
 }
 
@@ -188,7 +188,8 @@ validate_manifest(struct Manifest *manifest)
 }
 
 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;
@@ -202,7 +203,7 @@ build_rpp(struct Manifest *mft, struct rpki_uri *mft_uri, struct rpp **pp)
        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
@@ -266,7 +267,7 @@ fail:
  * @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);
@@ -277,7 +278,7 @@ handle_manifest(struct rpki_uri *uri, struct rpp **pp)
        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));
@@ -292,7 +293,7 @@ handle_manifest(struct rpki_uri *uri, struct rpp **pp)
                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;
 
index 8cadf6da0fb3dfe677241c42c8584d71b6ef21e2..fa55a6f32bd6c2f80c6ac2197f50ed10db290681 100644 (file)
@@ -3,6 +3,6 @@
 
 #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_ */
index 221eac9a6c5be9fd616b6c3610c269828d6a13a7..b3fc82f9a3767c2be4199ffe8a71bd7a7178f668 100644 (file)
 
 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. */
@@ -59,50 +45,27 @@ struct tal_param {
        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;
@@ -136,7 +99,7 @@ read_uris(struct line_file *lfile, struct uris *uris)
        }
 
        do {
-               error = uris_add(uris, uri);
+               error = add_uri(uris, uri);
                free(uri); /* Won't be needed anymore */
                if (error)
                        return error;
@@ -335,7 +298,7 @@ tal_load(char const *file_name, struct tal **result)
        return 0;
 
 fail:
-       uris_destroy(&tal->uris);
+       uris_cleanup(&tal->uris);
        free(tal);
        lfile_close(lfile);
        return error;
@@ -347,56 +310,11 @@ tal_destroy(struct tal *tal)
        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)
 {
@@ -415,7 +333,7 @@ tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len)
  * 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
@@ -432,46 +350,22 @@ handle_tal_uri(struct tal *tal, struct rpki_uri *uri, void *arg)
         */
 
        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));
@@ -535,6 +429,7 @@ do_file_validation(void *arg)
 {
        struct validation_thread *thread = arg;
        struct tal *tal;
+       struct rpki_uri *ta_uri;
        int error;
 
        fnstack_init();
@@ -544,29 +439,14 @@ do_file_validation(void *arg)
        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);
@@ -594,8 +474,6 @@ spawn_tal_thread(char const *tal_file, void *arg)
        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(&param->threads, thread, next);
index d9592cd7d4e1efcfa85eb81be4a37a7170756eec..e8d795f93e9dca9802467a80eb1f50b2fd194d6a 100644 (file)
--- a/src/rpp.c
+++ b/src/rpp.c
 #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.
@@ -39,9 +37,9 @@ struct rpp {
 
        /* 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
@@ -74,24 +72,18 @@ rpp_refget(struct rpp *pp)
        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);
        }
 }
@@ -100,21 +92,21 @@ rpp_refput(struct rpp *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. */
index 449d8ed18d499db33f94405a10c446af2f1869c9..7b54daa6926ae002bcad782deff13fd6cb474e83 100644 (file)
@@ -34,7 +34,7 @@ get_metadata(struct rpki_uri *uri, struct notification_metadata *result)
 
        *result = notification.meta;
        memset(&notification.meta, 0, sizeof(notification.meta));
-       update_notification_destroy(&notification);
+       update_notification_cleanup(&notification);
        return 0;
 }
 
@@ -55,16 +55,6 @@ rrdp_update(struct rpki_uri *uri)
        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.");
 
@@ -108,7 +98,7 @@ rrdp_update(struct rpki_uri *uri)
        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();
index e05bdd398a7ecdf1713a1ae22e76d3bf286e837e..48789ea9d799a0e44b844672f06080109ba26acd 100644 (file)
@@ -31,7 +31,7 @@ void
 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 */
@@ -113,13 +113,13 @@ deltas_head_sort(struct deltas_head *deltas, unsigned long max_serial)
 }
 
 void
-update_notification_init(struct update_notification *notification,
+update_notification_init(struct update_notification *notif,
     struct rpki_uri *uri)
 {
-       notification_metadata_init(&notification->meta);
-       metadata_init(&notification->snapshot);
-       deltas_head_init(&notification->deltas_list);
-       notification->uri = uri_refget(uri);
+       notification_metadata_init(&notif->meta);
+       metadata_init(&notif->snapshot);
+       deltas_head_init(&notif->deltas_list);
+       notif->uri = uri_refget(uri);
 }
 
 static void
@@ -129,7 +129,7 @@ delta_head_destroy(struct delta_head *delta)
 }
 
 void
-update_notification_destroy(struct update_notification *file)
+update_notification_cleanup(struct update_notification *file)
 {
        metadata_cleanup(&file->snapshot);
        notification_metadata_cleanup(&file->meta);
@@ -173,41 +173,29 @@ delta_destroy(struct delta *file)
        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);
 }
index 6f89d3ba1e6d9b4eb356725a0eb0609782b13bc8..5ded377e37c0d55cb2d9686fd92ea8a7a3868f3a 100644 (file)
@@ -11,7 +11,7 @@ struct notification_metadata {
 
 /* 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;
 };
@@ -73,7 +73,7 @@ void metadata_init(struct file_metadata *);
 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,
@@ -86,10 +86,10 @@ void snapshot_destroy(struct snapshot *);
 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_ */
index 721b7d931f3d7abe673ed4f53cd168a8c9716dfe..a0b3222b164725c0f0e3b927ec6543697be4fcfa 100644 (file)
@@ -35,7 +35,7 @@ struct rdr_snapshot_ctx {
        /* 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 */
@@ -43,7 +43,7 @@ struct rdr_delta_ctx {
        /* 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;
 };
@@ -351,19 +351,26 @@ return_val:
  * 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;
 
@@ -383,24 +390,22 @@ end:
 }
 
 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;
        }
 
@@ -408,108 +413,85 @@ parse_publish(xmlTextReaderPtr reader, hash_requirement hr,
        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;
 }
@@ -522,42 +504,24 @@ delete_from_uri(struct rpki_uri *uri)
        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;
 }
 
@@ -566,24 +530,24 @@ parse_publish_elem(xmlTextReaderPtr reader, hash_requirement hr)
  * 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;
@@ -591,39 +555,39 @@ parse_notification_delta(xmlTextReaderPtr reader,
        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(&notification->deltas_list, &delta);
+       deltas_head_add(&notif->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,
+                           &notif->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, &notif->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(&notif->deltas_list,
+                           notif->meta.serial);
                break;
        }
 
@@ -638,9 +602,9 @@ rrdp_parse_notification(struct rpki_uri *uri,
 
        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;
 }
@@ -658,12 +622,13 @@ xml_read_snapshot(xmlTextReaderPtr reader, void *arg)
        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);
 
@@ -678,20 +643,20 @@ xml_read_snapshot(xmlTextReaderPtr reader, void *arg)
 }
 
 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);
 
@@ -713,13 +678,14 @@ xml_read_delta(xmlTextReaderPtr reader, void *arg)
        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);
@@ -736,7 +702,7 @@ xml_read_delta(xmlTextReaderPtr reader, void *arg)
 
 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;
@@ -751,7 +717,7 @@ parse_delta(struct rpki_uri *uri, struct delta_head *parents_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);
@@ -763,66 +729,52 @@ pop_fnstack:
 }
 
 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(&notification->deltas_list,
-           notification->meta.serial, cur_serial, process_delta, notification);
+       return deltas_head_for_each(&notif->deltas_list, notif->meta.serial,
+           serial, process_delta, notif);
 }
index e7564a6a5ffa48a6b7a062a471100a804be54b89..9c1a874e7f9dcdedbc528937b164bd7fa23094de 100644 (file)
@@ -353,7 +353,7 @@ rsync_download(struct rpki_uri *uri)
                                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.",
index bb10200703dd82faf578c658b0f28adbad1bf677..e6f7e12202ddc0c18e007d33cd9e2cd8089b1d24 100644 (file)
@@ -37,9 +37,6 @@ struct validation {
        char addr_buffer2[INET6_ADDRSTRLEN];
 
        struct validation_handler validation_handler;
-
-       /* URL of the Notification file currently being traversed. */
-       struct rpki_uri *notification_uri;
 };
 
 /*
@@ -118,7 +115,6 @@ validation_prepare(struct validation **out, struct tal *tal,
        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;
@@ -192,19 +188,3 @@ validation_get_validation_handler(struct validation *state)
 {
        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);
-}
index b1386ccf617dfccae7ee78bfc2b9321b11f28bd8..8fc48b095e78e47cb896c6e1645565d9fa364d37 100644 (file)
@@ -32,7 +32,4 @@ validation_get_validation_handler(struct validation *);
 
 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_ */
index 7776b1a169d3263519c3a44d3b3255add7ff9496..6ea1f4c77fcf7fb86b491819682e94f8f1b0fdc2 100644 (file)
@@ -1,5 +1,7 @@
 #include "types/uri.h"
 
+#include <errno.h>
+
 #include "alloc.h"
 #include "common.h"
 #include "config.h"
@@ -9,6 +11,8 @@
 #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:
@@ -331,20 +335,15 @@ map_simple(struct rpki_uri *uri, char const *gprefix, int err)
  * "<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);
@@ -356,7 +355,7 @@ map_caged(struct rpki_uri *uri)
 }
 
 static int
-autocomplete_local(struct rpki_uri *uri)
+autocomplete_local(struct rpki_uri *uri, struct rpki_uri *notif)
 {
        switch (uri->type) {
        case UT_RSYNC:
@@ -364,7 +363,7 @@ autocomplete_local(struct rpki_uri *uri)
        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);
@@ -375,8 +374,8 @@ autocomplete_local(struct rpki_uri *uri)
  * 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;
@@ -391,7 +390,7 @@ __uri_create(struct rpki_uri **result, enum uri_type type, void const *guri,
 
        uri->type = type;
 
-       error = autocomplete_local(uri);
+       error = autocomplete_local(uri, notif);
        if (error) {
                free(uri->global);
                free(uri);
@@ -405,9 +404,10 @@ __uri_create(struct rpki_uri **result, enum uri_type type, void const *guri,
 }
 
 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));
 }
 
 /*
@@ -415,7 +415,8 @@ uri_create(struct rpki_uri **result, enum uri_type type, char const *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;
@@ -428,11 +429,9 @@ uri_create_mft(struct rpki_uri **result, struct rpki_uri *mft, IA5String_t *ia5)
                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);
@@ -455,6 +454,9 @@ uri_refget(struct rpki_uri *uri)
 void
 uri_refput(struct rpki_uri *uri)
 {
+       if (uri == NULL)
+               return;
+
        uri->references--;
        if (uri->references == 0) {
                free(uri->global);
@@ -566,3 +568,95 @@ uri_op_get_printable(struct rpki_uri *uri)
        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);
+}
index a0d6070051673378b28dcb3135796fea1fa6c288..ba03a0e6f2e44526d236ff45bfca50583db18082 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <stdbool.h>
 #include "asn1/asn1c/IA5String.h"
+#include "data_structure/array_list.h"
 
 enum uri_type {
        /* rsync URL */
@@ -18,9 +19,12 @@ enum uri_type {
 
 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 *);
@@ -44,4 +48,14 @@ 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 *);
 
+/* 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_ */
index 761dcacddc05ce91beecafc41b3632627cd178ae..0a663475020a96c41e4558764f32ddb11ebd64f2 100644 (file)
@@ -63,6 +63,8 @@ http_download(struct rpki_uri *uri, bool *changed)
        return error;
 }
 
+MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
+
 /* Helpers */
 
 static const int SUCCESS = CNF_DIRECT | CNF_SUCCESS;
@@ -90,7 +92,7 @@ __download(char const *url, enum uri_type uritype, int expected_error,
 {
        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));
@@ -1167,6 +1169,177 @@ START_TEST(test_ctt_traversal)
 }
 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)
@@ -1195,6 +1368,9 @@ 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);
index 9d63e9a44458897881d7d0a6f4616e002c133ab5..328d580a34e1d86f95eea22efea9ff5eec14644d 100644 (file)
@@ -108,6 +108,8 @@ MOCK(config_get_tal, char const *, "tal/", void)
 MOCK(config_get_local_repository, char const *, "tmp", void)
 MOCK(config_get_mode, enum mode, STANDALONE, void)
 MOCK_TRUE(config_get_rsync_enabled, void)
+MOCK_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)
index 60ef03217bf5af74e4ae7238f4179c93a4de44f8..7d46f45ea03a33a04afdf5717d3dc4d5b8b5c87e 100644 (file)
@@ -28,6 +28,11 @@ MOCK_ABORT_VOID(fnstack_init, 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)
@@ -67,7 +72,7 @@ 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");
index 66852180981138b67427a0e4a8f500118876ed5a..b6764cc1d249bdfcfcb1f2c28b690ddf7d7162e1 100644 (file)
 
 /* 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
 
@@ -142,17 +149,17 @@ START_TEST(check_caged)
 {
        struct rpki_uri *uri;
 
-       ck_assert_int_eq(0, uri_create(&notification, 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(&notif, 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(&notification, 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(&notif, 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