]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Strong type-fy URIs
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 29 Apr 2025 23:53:42 +0000 (17:53 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 29 Apr 2025 23:53:42 +0000 (17:53 -0600)
Fixes all the normalization TODOs.

...Well, except the curl URI normalization PR is on hold.
I might have to implement something myself later.

41 files changed:
src/asn1/asn1c/RsyncRequest.c
src/asn1/asn1c/RsyncRequest.h
src/cache.c
src/cache.h
src/certificate_refs.c
src/certificate_refs.h
src/common.h
src/config/string_array.c
src/http.c
src/http.h
src/init.c
src/json_util.c
src/json_util.h
src/object/certificate.c
src/object/ghostbusters.c
src/object/manifest.c
src/object/manifest.h
src/object/roa.c
src/object/tal.c
src/print_file.c
src/rrdp.c
src/rrdp.h
src/rsync.c
src/rsync.h
src/task.c
src/types/map.c
src/types/map.h
src/types/str.c
src/types/str.h
src/types/url.c
src/types/url.h
test/Makefile.am
test/cache_test.c
test/mock.c
test/mock_https.c
test/object/tal_test.c
test/rrdp_test.c
test/rrdp_update_test.c
test/rsync_test.c
test/task_test.c
test/types/url_test.c

index 4349ad2bccf4dbf7bb69c21c3782831932a7fe13..d527079cca7d9f44b61c771a6a4ea046299d531d 100644 (file)
@@ -58,10 +58,11 @@ asn_TYPE_descriptor_t asn_DEF_RsyncRequest = {
 };
 
 int
-RsyncRequest_init(struct RsyncRequest *req, char const *url, char const *path)
+RsyncRequest_init(struct RsyncRequest *req, struct uri const *url,
+    char const *path)
 {
        memset(req, 0, sizeof(*req));
-       if (OCTET_STRING_fromString(&req->url, url) < 0)
+       if (OCTET_STRING_fromBuf(&req->url, uri_str(url), uri_len(url)) < 0)
                return -1;
        if (OCTET_STRING_fromString(&req->path, path) < 0)
                return -1;
index 13ec832950c90c35ca3da096da1cb480f2272877..66f0478042b1e87e6941907d61616e0093deecf4 100644 (file)
@@ -9,6 +9,7 @@
 #define        _RsyncRequest_H_
 
 /* Including external dependencies */
+#include "types/url.h"
 #include "asn1/asn1c/OCTET_STRING.h"
 #include "asn1/asn1c/constr_SEQUENCE.h"
 
@@ -28,7 +29,7 @@ typedef struct RsyncRequest {
 /* Implementation */
 extern asn_TYPE_descriptor_t asn_DEF_RsyncRequest;
 
-int RsyncRequest_init(struct RsyncRequest *, char const *, char const *);
+int RsyncRequest_init(struct RsyncRequest *, struct uri const *, char const *);
 
 #ifdef __cplusplus
 }
index b262f23a676b75d76537b9eb6a8dd503bdcf69bf..91dd653ccdfe70eaba5ea2862d62980e091659fc 100644 (file)
@@ -55,14 +55,18 @@ struct node_key {
         * If node is rsync, @http is NULL.
         * If node is HTTP, @http is the simple URL.
         * If node is RRDP, @http is the rpkiNotify.
+        *
+        * Points to @id; do not clean.
         */
-       char const *http;
+       struct uri http;
        /*
         * If node is rsync, @rsync is the simple URL.
         * If node is HTTP, @rsync is NULL.
         * If node is RRDP, @rsync is the caRepository.
+        *
+        * Points to @id; do not clean.
         */
-       char const *rsync;
+       struct uri rsync;
 };
 
 /*
@@ -140,13 +144,13 @@ static volatile sig_atomic_t lockfile_owned;
 struct cache_cage {
        struct cache_node const *refresh;
        struct cache_node const *fallback;
-       char const *rpkiNotify;
+       struct uri rpkiNotify;
        struct mft_meta *mft;           /* Fallback */
 };
 
 struct cache_commit {
-       char *rpkiNotify;
-       char *caRepository;
+       struct uri rpkiNotify;
+       struct uri caRepository;
        struct cache_mapping *files;
        size_t nfiles;
        struct mft_meta mft;            /* RPPs commits only */
@@ -202,25 +206,35 @@ flush_nodes(void)
        foreach_node(delete_node, NULL);
 }
 
-char *
-get_rsync_module(char const *url)
+/*
+ * - Result must not be cleant.
+ * - strlen(uri_str(module)) should not be trusted.
+ */
+static bool
+get_rsync_module(struct uri const *url, struct uri *module)
 {
+       char const *str;
        array_index u;
        unsigned int slashes;
 
+       str = uri_str(url);
        slashes = 0;
-       for (u = 0; url[u] != 0; u++)
-               if (url[u] == '/') {
+       for (u = 0; str[u] != 0; u++)
+               if (str[u] == '/') {
                        slashes++;
-                       if (slashes == 4)
-                               return pstrndup(url, u);
+                       if (slashes == 4) {
+                               __uri_init(module, str, u);
+                               return true;
+                       }
                }
 
-       if (slashes == 3 && url[u - 1] != '/')
-               return pstrdup(url);
+       if (slashes == 3 && str[u - 1] != '/') {
+               *module = *url;
+               return true;
+       }
 
-       pr_val_err("Url '%s' does not appear to have an rsync module.", url);
-       return NULL;
+       pr_val_err("Url '%s' does not appear to have an rsync module.", str);
+       return false;
 }
 
 char const *
@@ -243,15 +257,18 @@ strip_rsync_module(char const *url)
 static json_t *
 node2json(struct cache_node *node)
 {
+       char const *str;
        json_t *json;
 
        json = json_obj_new();
        if (json == NULL)
                return NULL;
 
-       if (node->key.http && json_add_str(json, "http", node->key.http))
+       str = uri_str(&node->key.http);
+       if (str && json_add_str(json, "http", str))
                goto fail;
-       if (node->key.rsync && json_add_str(json, "rsync", node->key.rsync))
+       str = uri_str(&node->key.rsync);
+       if (str && json_add_str(json, "rsync", str))
                goto fail;
        if (json_add_str(json, "path", node->path))
                goto fail;
@@ -443,39 +460,46 @@ cache_setup(void)
 }
 
 static void
-init_rrdp_fallback_key(struct node_key *key, char const *http, char const *rsync)
+init_rrdp_fallback_key(struct node_key *key, struct uri const *http,
+    struct uri const *rsync)
 {
        size_t hlen, rlen;
 
-       hlen = strlen(http);
-       rlen = strlen(rsync);
+       hlen = uri_len(http);
+       rlen = uri_len(rsync);
 
        key->idlen = hlen + rlen + 1;
        key->id = pmalloc(key->idlen + 1);
-       key->http = key->id;
-       key->rsync = key->id + hlen + 1;
+       __uri_init(&key->http, key->id, hlen);
+       __uri_init(&key->rsync, key->id + hlen + 1, rlen);
 
-       memcpy(key->id, http, hlen + 1);
-       memcpy(key->id + hlen + 1, rsync, rlen + 1);
+       memcpy(key->id, uri_str(http), hlen + 1);
+       memcpy(key->id + hlen + 1, uri_str(rsync), rlen + 1);
 }
 
-static bool
-init_node_key(struct node_key *key, char const *http, char const *rsync)
+static int
+init_node_key(struct node_key *key, struct uri const *http,
+    struct uri const *rsync)
 {
+       if (http && (uri_str(http) == NULL))
+               http = NULL;
+       if (rsync && (uri_str(rsync) == NULL))
+               rsync = NULL;
+
        if (http != NULL && rsync != NULL) {
                init_rrdp_fallback_key(key, http, rsync);
 
        } else if (rsync != NULL) {
-               key->id = pstrdup(rsync);
-               key->idlen = strlen(key->id);
-               key->http = NULL;
-               key->rsync = key->id;
+               key->idlen = uri_len(rsync);
+               key->id = pstrndup(uri_str(rsync), key->idlen);
+               memset(&key->http, 0, sizeof(key->http));
+               __uri_init(&key->rsync, key->id, key->idlen);
 
        } else if (http != NULL) {
-               key->id = pstrdup(http);
-               key->idlen = strlen(key->id);
-               key->http = key->id;
-               key->rsync = NULL;
+               key->idlen = uri_len(http);
+               key->id = pstrndup(uri_str(http), key->idlen);
+               __uri_init(&key->http, key->id, key->idlen);
+               memset(&key->rsync, 0, sizeof(key->rsync));
 
        } else {
                return false;
@@ -488,31 +512,37 @@ static struct cache_node *
 json2node(json_t *json)
 {
        struct cache_node *node;
-       char const *http;
-       char const *rsync;
+       struct uri http;
+       struct uri rsync;
        char const *path;
        json_t *rrdp;
        int error;
 
-       error = json_get_str(json, "http", &http);
+       error = json_get_uri(json, "http", &http);
        if (error && (error != ENOENT)) {
                pr_op_debug("http: %s", strerror(error));
                return NULL;
        }
 
-       error = json_get_str(json, "rsync", &rsync);
+       error = json_get_uri(json, "rsync", &rsync);
        if (error && (error != ENOENT)) {
                pr_op_debug("rsync: %s", strerror(error));
+               uri_cleanup(&http);
                return NULL;
        }
 
        node = pzalloc(sizeof(struct cache_node));
 
-       if (!init_node_key(&node->key, http, rsync)) {
+       if (!init_node_key(&node->key, &http, &rsync)) {
                pr_op_debug("JSON node is missing both http and rsync tags.");
+               uri_cleanup(&rsync);
+               uri_cleanup(&http);
                goto nde;
        }
 
+       uri_cleanup(&http);
+       uri_cleanup(&rsync);
+
        error = json_get_str(json, "path", &path);
        if (error) {
                pr_op_debug("path: %s", strerror(error));
@@ -750,7 +780,7 @@ static int
 dl_rsync(struct cache_node *module)
 {
        int error;
-       error = rsync_queue(module->key.rsync, module->path);
+       error = rsync_queue(&module->key.rsync, module->path);
        return error ? error : EBUSY;
 }
 
@@ -760,7 +790,7 @@ dl_rrdp(struct cache_node *notif)
        bool changed;
        int error;
 
-       error = rrdp_update(notif->key.http, notif->path, notif->success_ts,
+       error = rrdp_update(&notif->key.http, notif->path, notif->success_ts,
            &changed, &notif->rrdp);
        if (error)
                return error;
@@ -776,7 +806,7 @@ dl_http(struct cache_node *file)
        bool changed;
        int error;
 
-       error = http_download(file->key.http, file->path,
+       error = http_download(&file->key.http, file->path,
            file->success_ts, &changed);
        if (error)
                return error;
@@ -796,7 +826,8 @@ find_node(struct cache_table *tbl, char const *url, size_t urlen)
 }
 
 static struct cache_node *
-provide_node(struct cache_table *tbl, char const *http, char const *rsync)
+provide_node(struct cache_table *tbl, struct uri const *http,
+   struct uri const *rsync)
 {
        struct node_key key;
        struct cache_node *node;
@@ -870,12 +901,14 @@ write_metadata(struct cache_node *node)
  * By contract, @result->state will be DLS_FRESH on return 0.
  */
 static int
-do_refresh(struct cache_table *tbl, char const *uri, struct cache_node **result)
+do_refresh(struct cache_table *tbl, struct uri const *uri,
+    struct cache_node **result)
 {
+       struct uri module;
        struct cache_node *node;
        bool downloaded = false;
 
-       pr_val_debug("Trying %s (online)...", uri);
+       pr_val_debug("Trying %s (online)...", uri_str(uri));
 
        if (!tbl->enabled) {
                pr_val_debug("Protocol disabled.");
@@ -883,12 +916,10 @@ do_refresh(struct cache_table *tbl, char const *uri, struct cache_node **result)
        }
 
        if (tbl == &cache.rsync) {
-               char *module = get_rsync_module(uri);
-               if (module == NULL)
+               if (!get_rsync_module(uri, &module))
                        return EINVAL;
                mutex_lock(&tbl->lock);
-               node = provide_node(tbl, NULL, module);
-               free(module);
+               node = provide_node(tbl, NULL, &module);
        } else {
                mutex_lock(&tbl->lock);
                node = provide_node(tbl, uri, NULL);
@@ -951,10 +982,10 @@ find_rrdp_fallback_node(struct sia_uris *sias)
        struct node_key key;
        struct cache_node *result;
 
-       if (!sias->rpkiNotify || !sias->caRepository)
+       if (!uri_str(&sias->rpkiNotify) || !uri_str(&sias->caRepository))
                return NULL;
 
-       init_rrdp_fallback_key(&key, sias->rpkiNotify, sias->caRepository);
+       init_rrdp_fallback_key(&key, &sias->rpkiNotify, &sias->caRepository);
        result = find_node(&cache.fallback, key.id, key.idlen);
        free(key.id);
 
@@ -968,8 +999,8 @@ get_fallback(struct sia_uris *sias)
        struct cache_node *rsync;
 
        rrdp = find_rrdp_fallback_node(sias);
-       rsync = find_node(&cache.fallback, sias->caRepository,
-           strlen(sias->caRepository));
+       rsync = find_node(&cache.fallback, uri_str(&sias->caRepository),
+           uri_len(&sias->caRepository));
 
        if (rrdp == NULL)
                return rsync;
@@ -980,16 +1011,15 @@ get_fallback(struct sia_uris *sias)
 
 /* Do not free nor modify the result. */
 char *
-cache_refresh_by_url(char const *url)
+cache_refresh_by_url(struct uri const *url)
 {
        struct cache_node *node = NULL;
 
        // XXX review result signs
-       // XXX Normalize @url
 
-       if (url_is_https(url))
+       if (uri_is_https(url))
                do_refresh(&cache.https, url, &node);
-       else if (url_is_rsync(url))
+       else if (uri_is_rsync(url))
                do_refresh(&cache.rsync, url, &node);
 
        return node ? node->path : NULL;
@@ -1000,7 +1030,7 @@ cache_refresh_by_url(char const *url)
  * Do not free nor modify the result.
  */
 char *
-cache_get_fallback(char const *url)
+cache_get_fallback(struct uri const *url)
 {
        struct cache_node *node;
 
@@ -1009,9 +1039,9 @@ cache_get_fallback(char const *url)
         * Mutex not needed here.
         */
 
-       pr_val_debug("Trying %s (offline)...", url);
+       pr_val_debug("Trying %s (offline)...", uri_str(url));
 
-       node = find_node(&cache.fallback, url, strlen(url));
+       node = find_node(&cache.fallback, uri_str(url), uri_len(url));
        if (!node) {
                pr_val_debug("Cache data unavailable.");
                return NULL;
@@ -1032,15 +1062,14 @@ cache_refresh_by_sias(struct sia_uris *sias, struct cache_cage **result)
 {
        struct cache_node *node;
        struct cache_cage *cage;
-       char const *rpkiNotify;
+       struct uri rpkiNotify;
 
        // XXX Make sure somewhere validates rpkiManifest matches caRepository.
        // XXX review result signs
-       // XXX normalize rpkiNotify & caRepository?
 
        /* Try RRDP + optional fallback */
-       if (sias->rpkiNotify) {
-               switch (do_refresh(&cache.rrdp, sias->rpkiNotify, &node)) {
+       if (uri_str(&sias->rpkiNotify) != NULL) {
+               switch (do_refresh(&cache.rrdp, &sias->rpkiNotify, &node)) {
                case 0:
                        rpkiNotify = sias->rpkiNotify;
                        goto refresh_success;
@@ -1050,9 +1079,9 @@ cache_refresh_by_sias(struct sia_uris *sias, struct cache_cage **result)
        }
 
        /* Try rsync + optional fallback */
-       switch (do_refresh(&cache.rsync, sias->caRepository, &node)) {
+       switch (do_refresh(&cache.rsync, &sias->caRepository, &node)) {
        case 0:
-               rpkiNotify = NULL;
+               memset(&rpkiNotify, 0, sizeof(rpkiNotify));
                goto refresh_success;
        case EBUSY:
                return EBUSY;
@@ -1076,18 +1105,18 @@ refresh_success:
 }
 
 static char const *
-node2file(struct cache_node const *node, char const *url)
+node2file(struct cache_node const *node, struct uri const *url)
 {
        if (node == NULL)
                return NULL;
        // XXX RRDP is const, rsync needs to be freed
        return (node->rrdp)
            ? /* RRDP  */ rrdp_file(node->rrdp, url)
-           : /* rsync */ path_join(node->path, strip_rsync_module(url));
+           : /* rsync */ path_join(node->path, strip_rsync_module(uri_str(url)));
 }
 
 char const *
-cage_map_file(struct cache_cage *cage, char const *url)
+cage_map_file(struct cache_cage *cage, struct uri const *url)
 {
        /*
         * Remember: In addition to honoring the consts of cache->refresh and
@@ -1141,14 +1170,14 @@ cage_mft_fallback(struct cache_cage *cage)
  * not going to be modified nor deleted until the cache cleanup.
  */
 void
-cache_commit_rpp(char const *rpkiNotify, char const *caRepository,
+cache_commit_rpp(struct uri const *rpkiNotify, struct uri const *caRepository,
     struct rpp *rpp)
 {
        struct cache_commit *commit;
 
        commit = pmalloc(sizeof(struct cache_commit));
-       commit->rpkiNotify = rpkiNotify ? pstrdup(rpkiNotify) : NULL;
-       commit->caRepository = pstrdup(caRepository);
+       uri_copy(&commit->rpkiNotify, rpkiNotify);
+       uri_copy(&commit->caRepository, caRepository);
        commit->files = rpp->files;
        commit->nfiles = rpp->nfiles;
        INTEGER_move(&commit->mft.num, &rpp->mft.num);
@@ -1167,11 +1196,11 @@ cache_commit_file(struct cache_mapping *map)
 {
        struct cache_commit *commit;
 
-       commit = pmalloc(sizeof(struct cache_commit));
-       commit->rpkiNotify = NULL;
-       commit->caRepository = NULL;
+       commit = pzalloc(sizeof(struct cache_commit));
+       memset(&commit->rpkiNotify, 0, sizeof(commit->rpkiNotify));
+       memset(&commit->caRepository, 0, sizeof(commit->caRepository));
        commit->files = pmalloc(sizeof(*map));
-       commit->files[0].url = pstrdup(map->url);
+       uri_copy(&commit->files[0].url, &map->url);
        commit->files[0].path = pstrdup(map->path);
        commit->nfiles = 1;
        memset(&commit->mft, 0, sizeof(commit->mft));
@@ -1182,22 +1211,22 @@ cache_commit_file(struct cache_mapping *map)
 }
 
 void
-rsync_finished(char const *url, char const *path)
+rsync_finished(struct uri const *url, char const *path)
 {
        struct cache_node *node;
 
        mutex_lock(&cache.rsync.lock);
 
-       node = find_node(&cache.rsync, url, strlen(url));
+       node = find_node(&cache.rsync, uri_str(url), uri_len(url));
        if (node == NULL) {
                mutex_unlock(&cache.rsync.lock);
                pr_op_err("rsync '%s -> %s' finished, but cache node does not exist.",
-                   url, path);
+                   uri_str(url), path);
                return;
        }
        if (node->state != DLS_ONGOING)
                pr_op_warn("rsync '%s -> %s' finished, but existing node was not in ONGOING state.",
-                   url, path);
+                   uri_str(url), path);
 
        node->state = DLS_FRESH;
        node->dlerr = 0;
@@ -1207,10 +1236,10 @@ rsync_finished(char const *url, char const *path)
        task_wakeup_dormants();
 }
 
-char const *
+struct uri const *
 cage_rpkiNotify(struct cache_cage *cage)
 {
-       return cage->rpkiNotify;
+       return &cage->rpkiNotify;
 }
 
 static void
@@ -1219,8 +1248,8 @@ cachent_print(struct cache_node *node)
        if (!node)
                return;
 
-       printf("\thttp:%s rsync:%s (%s): ", node->key.http, node->key.rsync,
-           node->path);
+       printf("\thttp:%s rsync:%s (%s): ", uri_str(&node->key.http),
+           uri_str(&node->key.rsync), node->path);
        switch (node->state) {
        case DLS_OUTDATED:
                printf("stale ");
@@ -1288,7 +1317,7 @@ commit_rpp(struct cache_commit *commit, struct cache_node *fb)
                 * Note, this is accidentally working perfectly for rsync too.
                 * Might want to rename some of this.
                 */
-               dst = rrdp_create_fallback(fb->path, &fb->rrdp, src->url);
+               dst = rrdp_create_fallback(fb->path, &fb->rrdp, &src->url);
                if (!dst)
                        goto skip;
 
@@ -1365,12 +1394,14 @@ commit_fallbacks(time_t now)
                commit = STAILQ_FIRST(&commits);
                STAILQ_REMOVE_HEAD(&commits, lh);
 
-               if (commit->caRepository) {
+               if (uri_str(&commit->caRepository) != NULL) {
                        pr_op_debug("Creating fallback for %s (%s)",
-                           commit->caRepository, commit->rpkiNotify);
+                           uri_str(&commit->caRepository),
+                           uri_str(&commit->rpkiNotify));
 
-                       fb = provide_node(&cache.fallback, commit->rpkiNotify,
-                           commit->caRepository);
+                       fb = provide_node(&cache.fallback,
+                           &commit->rpkiNotify,
+                           &commit->caRepository);
                        fb->success_ts = now;
 
                        pr_op_debug("mkdir -f %s", fb->path);
@@ -1391,9 +1422,10 @@ commit_fallbacks(time_t now)
                } else { /* TA */
                        struct cache_mapping *map = &commit->files[0];
 
-                       pr_op_debug("Creating fallback for %s", map->url);
+                       pr_op_debug("Creating fallback for %s",
+                           uri_str(&map->url));
 
-                       fb = provide_node(&cache.fallback, map->url, NULL);
+                       fb = provide_node(&cache.fallback, &map->url, NULL);
                        fb->success_ts = now;
                        if (is_fallback(map->path))
                                goto freshen;
@@ -1404,10 +1436,10 @@ commit_fallbacks(time_t now)
                write_metadata(fb);
 
 freshen:       fb->state = DLS_FRESH;
-skip:          free(commit->rpkiNotify);
-               free(commit->caRepository);
+skip:          uri_cleanup(&commit->rpkiNotify);
+               uri_cleanup(&commit->caRepository);
                for (i = 0; i < commit->nfiles; i++) {
-                       free(commit->files[i].url);
+                       uri_cleanup(&commit->files[i].url);
                        free(commit->files[i].path);
                }
                free(commit->files);
@@ -1499,10 +1531,10 @@ sias_init(struct sia_uris *sias)
 void
 sias_cleanup(struct sia_uris *sias)
 {
-       free(sias->caRepository);
-       free(sias->rpkiNotify);
-       free(sias->rpkiManifest);
-       free(sias->crldp);
-       free(sias->caIssuers);
-       free(sias->signedObject);
+       uri_cleanup(&sias->caRepository);
+       uri_cleanup(&sias->rpkiNotify);
+       uri_cleanup(&sias->rpkiManifest);
+       uri_cleanup(&sias->crldp);
+       uri_cleanup(&sias->caIssuers);
+       uri_cleanup(&sias->signedObject);
 }
index 0be38fff7cfe3f1ebf6a06e6c29941ba0ae09035..0b900b1a78fc22da6558e620e6486612c6d94c2f 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include "types/map.h"
 #include "types/rpp.h"
+#include "types/url.h"
 
 int cache_setup(void);         /* Init this module */
 void cache_atexit(void);
@@ -13,42 +14,42 @@ void cache_commit(void);    /* Finish validation cycle */
 
 /* XXX might wanna rename */
 struct sia_uris {
-       char *caRepository;     /* RPP cage */
-       char *rpkiNotify;       /* RRDP Notification */
-       char *rpkiManifest;
+       struct uri caRepository;        /* RPP cage */
+       struct uri rpkiNotify;          /* RRDP Notification */
+       struct uri rpkiManifest;
 
        /**
         * CRL Distribution Points's fullName. Non-TA certificates only.
         * RFC 6487, section 4.8.6.
         */
-       char *crldp;
+       struct uri crldp;
        /**
         * AIA's caIssuers. Non-TA certificates only.
         * RFC 6487, section 4.8.7.
         */
-       char *caIssuers;
+       struct uri caIssuers;
        /**
         * SIA's signedObject. EE certificates only.
         * RFC 6487, section 4.8.8.2.
         */
-       char *signedObject;
+       struct uri signedObject;
 };
 
 void sias_init(struct sia_uris *);
 void sias_cleanup(struct sia_uris *);
 
-char *cache_refresh_by_url(char const *);
-char *cache_get_fallback(char const *);
+char *cache_refresh_by_url(struct uri const *);
+char *cache_get_fallback(struct uri const *);
 
 struct cache_cage;
 int cache_refresh_by_sias(struct sia_uris *, struct cache_cage **);
-char const *cage_map_file(struct cache_cage *, char const *);
+char const *cage_map_file(struct cache_cage *, struct uri const *);
 bool cage_disable_refresh(struct cache_cage *);
 struct mft_meta const *cage_mft_fallback(struct cache_cage *);
-void cache_commit_rpp(char const *, char const *, struct rpp *);
+void cache_commit_rpp(struct uri const *, struct uri const *, struct rpp *);
 void cache_commit_file(struct cache_mapping *);
 
-char const *cage_rpkiNotify(struct cache_cage *);
+struct uri const *cage_rpkiNotify(struct cache_cage *);
 
 void cache_print(void);                /* Dump cache in stdout */
 
index 0f3403bfd5729e158af1359e759feadfcc8a142b..93c3aa14c874e51ac4b472c6a87d817f7bbdd156 100644 (file)
@@ -7,33 +7,30 @@
 #include "log.h"
 
 int
-validate_cdp(struct sia_uris *sias, char const *crl_url)
+validate_cdp(struct sia_uris const *sias, struct uri const *crl_url)
 {
-       if (sias->crldp == NULL)
+       if (uri_str(&sias->crldp) == NULL)
                pr_crit("Certificate's CRL Distribution Point was not recorded.");
-
-       if (crl_url == NULL)
+       if (uri_str(crl_url) == NULL)
                pr_crit("Manifest's CRL was not recorded.");
 
-       if (strcmp(sias->crldp, crl_url) != 0) {
+       if (uri_equals(&sias->crldp, crl_url) != 0) {
                return pr_val_err("Certificate's CRL Distribution Point ('%s') does not match manifest's CRL ('%s').",
-                   sias->crldp, crl_url);
+                   uri_str(&sias->crldp), uri_str(crl_url));
        }
 
        return 0;
 }
 
 static int
-validate_signedObject(struct sia_uris *sias, char const *url)
+validate_signedObject(struct sia_uris const *sias, struct uri const *url)
 {
-       if (sias->signedObject == NULL)
+       if (uri_str(&sias->signedObject) == NULL)
                pr_crit("Certificate's signedObject was not recorded.");
 
-       /* XXX the left one is no longer normalized */
-       if (strcmp(sias->signedObject, url) != 0) {
+       if (!uri_equals(&sias->signedObject, url))
                return pr_val_err("Certificate's signedObject ('%s') does not match the URI of its own signed object (%s).",
-                   sias->signedObject, url);
-       }
+                   uri_str(&sias->signedObject), uri_str(url));
 
        return 0;
 }
@@ -46,7 +43,8 @@ validate_signedObject(struct sia_uris *sias, char const *url)
  * @url: URL of the signed object that contains the EE certificate.
  */
 int
-refs_validate_ee(struct sia_uris *sias, char const *crl_url, char const *url)
+refs_validate_ee(struct sia_uris const *sias, struct uri const *crl_url,
+    struct uri const *url)
 {
        int error;
 
index 2df0890ba00748c3a0259c1f087ab07f7b95a383..851f8a5eada3617dd4598bc22007db7e29270ad6 100644 (file)
@@ -5,7 +5,8 @@
 
 #include "cache.h"
 
-int validate_cdp(struct sia_uris *, char const *);
-int refs_validate_ee(struct sia_uris *, char const *, char const *);
+int validate_cdp(struct sia_uris const *, struct uri const *);
+int refs_validate_ee(struct sia_uris const *, struct uri const *,
+    struct uri const *);
 
 #endif /* SRC_CERTIFICATE_REFS_H_ */
index b93f622e1692fb2a535568aba8242720558ac224..471910a74595b3751407d326e53717e484105747 100644 (file)
@@ -7,8 +7,6 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-/* "I think this is not supposed to be implemented." */
-#define ENOTSUPPORTED 3172
 /* "I haven't implemented this yet." */
 #define ENOTIMPLEMENTED 3173
 
index 80ccb415d1227315bc21fd3bffc620ae99475adb..b05e62f5606966d8ebd3d14742f17f8e2fdb00cb 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <getopt.h>
 
+#include "alloc.h"
 #include "config/str.h"
 #include "log.h"
 #include "types/path.h"
index 1f22d0da45be934fc8ce608949b1ebc22d5b566b..177f4bd0b5770a66ec53a9336d3338f273060236 100644 (file)
@@ -219,7 +219,7 @@ validate_file_size(struct write_callback_arg *args)
 
 static int
 get_http_response_code(struct http_handler *handler, long *http_code,
-    char const *uri)
+    struct uri const *uri)
 {
        CURLcode res;
 
@@ -228,7 +228,7 @@ get_http_response_code(struct http_handler *handler, long *http_code,
        if (res != CURLE_OK) {
                return pr_op_err_st("curl_easy_getinfo(CURLINFO_RESPONSE_CODE) returned %d (%s). "
                    "I think this is supposed to be illegal, so I'll have to drop URI '%s'.",
-                   res, curl_err_string(handler, res), uri);
+                   res, curl_err_string(handler, res), uri_str(uri));
        }
 
        return 0;
@@ -245,6 +245,24 @@ handle_http_response_code(long http_code)
        return -EINVAL; /* Do not retry */
 }
 
+static int
+check_same_origin(struct uri const *src, char const *redirect)
+{
+       struct uri redirect_url;
+       int error;
+
+       error = uri_init(&redirect_url, redirect);
+       if (error)
+               return error;
+
+       if (!uri_same_origin(src, &redirect_url))
+               error = pr_val_err("%s is redirecting to %s; disallowing because of different origin.",
+                   uri_str(src), uri_str(&redirect_url));
+
+       uri_cleanup(&redirect_url);
+       return error;
+}
+
 /*
  * Download @src into @dst; HTTP assumed.
  *
@@ -256,7 +274,8 @@ handle_http_response_code(long http_code)
  * If @changed is not NULL, initialize it to false.
  */
 int
-http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
+http_download(struct uri const *src, char const *dst,
+    curl_off_t ims, bool *changed)
 {
        struct http_handler handler;
        struct write_callback_arg args;
@@ -266,7 +285,7 @@ http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
        unsigned int r;
        int error;
 
-       pr_val_info("HTTP GET: %s -> %s", src, dst);
+       pr_val_info("HTTP GET: %s -> %s", uri_str(src), dst);
 
        error = http_easy_init(&handler, ims);
        if (error)
@@ -277,7 +296,8 @@ http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
 
        do {
                handler.errbuf[0] = 0;
-               setopt_str(handler.curl, CURLOPT_URL, (redirect != NULL) ? redirect : src);
+               setopt_str(handler.curl, CURLOPT_URL,
+                   (redirect != NULL) ? redirect : uri_str(src));
 
                args.total_bytes = 0;
                args.error = 0;
@@ -288,7 +308,8 @@ http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
                res = curl_easy_perform(handler.curl); /* write_callback() */
                if (args.file != NULL)
                        file_close(args.file);
-               pr_val_debug("Done. Total bytes transferred: %zu", args.total_bytes);
+               pr_val_debug("Done. Total bytes transferred: %zu",
+                   args.total_bytes);
 
                args.error = validate_file_size(&args);
                if (args.error) {
@@ -339,7 +360,8 @@ http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
                        redirect = NULL;
                }
 
-               res = curl_easy_getinfo(handler.curl, CURLINFO_REDIRECT_URL, &redirect);
+               res = curl_easy_getinfo(handler.curl, CURLINFO_REDIRECT_URL,
+                   &redirect);
                if (res != CURLE_OK) {
                        error = pr_op_err("curl_easy_getinfo(CURLINFO_REDIRECT_URL) returned %u.", res);
                        redirect = NULL;
@@ -348,19 +370,15 @@ http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
 
                if (redirect == NULL)
                        break;
-               if (!url_same_origin(src, redirect)) {
-                       error = pr_val_err("%s is redirecting to %s; disallowing because of different origin.",
-                          src, redirect);
-                       redirect = NULL;
-                       goto end;
-               }
-
                r++;
                if (r > config_get_max_redirs()) {
                        error = pr_val_err("Too many redirects.");
                        redirect = NULL;
                        goto end;
                }
+               error = check_same_origin(src, redirect);
+               if (error)
+                       goto end;
 
                /* The original redirect is destroyed during the next curl_easy_perform(). */
                redirect = pstrdup(redirect);
index 22113740e2ca2d6c466f7898a7b03a80992c580f..3992ddeb8dadaba81d8a72ed56639b589888b160 100644 (file)
@@ -4,9 +4,11 @@
 #include <curl/curl.h>
 #include <stdbool.h>
 
+#include "types/url.h"
+
 int http_init(void);
 void http_cleanup(void);
 
-int http_download(char const *, char const *, curl_off_t, bool *);
+int http_download(struct uri const *, char const *, curl_off_t, bool *);
 
 #endif /* SRC_HTTP_H_ */
index 8a3b0fcc02b90f90f5348288b6caedba9a1e22e1..665f12d57a6eaa16a71ee5ef6dea6c1673c66b02 100644 (file)
@@ -7,12 +7,17 @@
 static int
 fetch_url(char const *url, char const *filename)
 {
+       struct uri uri;
        char *path;
        int error;
 
+       error = uri_init(&uri, url);
+       if (error)
+               return error;
+
        path = path_join(config_get_tal(), filename);
 
-       error = http_download(url, path, 0, NULL);
+       error = http_download(&uri, path, 0, NULL);
        if (error) {
                fprintf(stderr, "Couldn't fetch '%s': %s\n",
                    path, strerror(abs(error)));
@@ -21,6 +26,7 @@ fetch_url(char const *url, char const *filename)
 
        fprintf(stdout, "Successfully fetched '%s'!\n\n", path);
 end:   free(path);
+       uri_cleanup(&uri);
        return error;
 }
 
index 1466063203d0118afd61c443bbb45b84bd0bfb76..65217ca2d6bce7f841440bb66450448d279211cf 100644 (file)
@@ -26,6 +26,27 @@ json_get_str(json_t *parent, char const *name, char const **result)
        return 0;
 }
 
+/* Result needs to be cleant up. */
+int
+json_get_uri(json_t *parent, char const *name, struct uri *result)
+{
+       char const *str;
+       int error;
+
+       memset(result, 0, sizeof(*result));
+
+       error = json_get_str(parent, name, &str);
+       if (error)
+               return error;
+       error = uri_init(result, str);
+       if (error) {
+               pr_op_err("Malformed URL: %s", str);
+               return -error;
+       }
+
+       return 0;
+}
+
 static int
 json_get_int_t(json_t *parent, char const *name, json_int_t *result)
 {
index e8f20f9a4e93a68463ac0fea1d670d0a79e0627d..9568aef3d10c0555e71915a56beffabeb5f84628 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "asn1/asn1c/INTEGER.h"
 #include "file.h"
+#include "types/url.h"
 
 /*
  * Contract of get functions:
@@ -33,6 +34,7 @@ int json_get_ulong(json_t *, char const *, unsigned long *);
 int json_get_bigint(json_t *, char const *, INTEGER_t *);
 int json_get_ts(json_t *, char const *, time_t *);
 int json_get_str(json_t *, char const *, char const **);
+int json_get_uri(json_t *, char const *, struct uri *);
 int json_get_array(json_t *, char const *, json_t **);
 int json_get_object(json_t *, char const *, json_t **);
 
index fee9f9b3fb1fd984ecb8b1ab988452648ea62551..db1f8688cd4b8d963a6ecb448dbfc5634513fe21 100644 (file)
@@ -1175,68 +1175,96 @@ certificate_get_resources(struct rpki_certificate *cert)
        pr_crit("Unknown policy: %u", cert->policy);
 }
 
-static bool
-is_rsync(ASN1_IA5STRING *uri)
-{
-       static char const *const PREFIX = "rsync://";
-       size_t prefix_len = strlen(PREFIX);
-
-       return (uri->length >= prefix_len)
-           ? (strncmp((char *) uri->data, PREFIX, strlen(PREFIX)) == 0)
-           : false;
-}
-
-static void
-handle_rpkiManifest(char *uri, void *arg)
+static int
+handle_rpkiManifest(struct uri const *uri, void *arg)
 {
        struct sia_uris *uris = arg;
+       char const *rm;
 
-       pr_clutter("rpkiManifest: %s", uri);
+       rm = uri_str(uri);
+       pr_clutter("rpkiManifest: %s", rm);
 
-       if (uris->rpkiManifest != NULL) {
-               pr_val_warn("Ignoring additional rpkiManifest: %s", uri);
-               free(uri);
-       } else {
-               uris->rpkiManifest = uri;
+       if (!uri_is_rsync(uri)) {
+               pr_val_debug("Ignoring non-rsync rpkiManifest '%s'.", rm);
+               return ENOTSUP;
+       }
+
+       if (uri_str(&uris->rpkiManifest) != NULL) {
+               pr_val_warn("Ignoring additional rpkiManifest: %s", rm);
+               return 0;
        }
+
+       uri_copy(&uris->rpkiManifest, uri);
+       return 0;
 }
 
-static void
-handle_caRepository(char *uri, void *arg)
+static int
+handle_caRepository(struct uri const *uri, void *arg)
 {
        struct sia_uris *uris = arg;
+       char const *cr;
 
-       pr_clutter("caRepository: %s", uri);
+       cr = uri_str(uri);
+       pr_clutter("caRepository: %s", caRepo);
 
-       if (uris->caRepository != NULL) {
-               pr_val_warn("Ignoring additional caRepository: %s", uri);
-               free(uri);
-       } else {
-               uris->caRepository = uri;
+       if (!uri_is_rsync(uri)) {
+               pr_val_debug("Ignoring non-rsync caRepository '%s'.", cr);
+               return ENOTSUP;
        }
+
+       if (uri_str(&uris->caRepository) != NULL) {
+               pr_val_warn("Ignoring additional caRepository: %s", cr);
+               return 0;
+       }
+
+       uri_copy(&uris->caRepository, uri);
+       return 0;
 }
 
-static void
-handle_rpkiNotify(char *uri, void *arg)
+static int
+handle_rpkiNotify(struct uri const *uri, void *arg)
 {
        struct sia_uris *uris = arg;
+       char const *rn;
 
-       pr_clutter("rpkiNotify: %s", uri);
+       rn = uri_str(uri);
+       pr_clutter("rpkiNotify: %s", rn);
 
-       if (uris->rpkiNotify != NULL) {
-               pr_val_warn("Ignoring additional rpkiNotify: %s", uri);
-               free(uri);
-       } else {
-               uris->rpkiNotify = uri;
+       if (!uri_is_https(uri)) {
+               pr_val_debug("Ignoring non-https rpkiNotify '%s'.", rn);
+               return ENOTSUP;
        }
+
+       if (uri_str(&uris->rpkiNotify) != NULL) {
+               pr_val_warn("Ignoring additional rpkiNotify: %s", rn);
+               return 0;
+       }
+
+       uri_copy(&uris->rpkiNotify, uri);
+       return 0;
 }
 
-static void
-handle_signedObject(char *uri, void *arg)
+static int
+handle_signedObject(struct uri const *uri, void *arg)
 {
        struct sia_uris *sias = arg;
-       pr_clutter("signedObject: %s", uri);
-       sias->signedObject = uri;
+       char const *so;
+
+       so = uri_str(uri);
+       pr_clutter("signedObject: %s", so);
+
+       if (!uri_is_rsync(uri)) {
+               pr_val_debug("Ignoring non-rsync signedObject '%s'.", so);
+               return ENOTSUP;
+       }
+
+       if (uri_str(&sias->signedObject) != NULL) {
+               pr_val_warn("Ignoring additional signedObject: %s", so);
+               return 0;
+       }
+
+       uri_copy(&sias->signedObject, uri);
+       return 0;
 }
 
 static int
@@ -1356,6 +1384,37 @@ handle_ku_ee(void *ext, void *arg)
        return handle_ku(ext, 0x80);
 }
 
+static int
+gn2uri(GENERAL_NAME *ad, struct uri *uri)
+{
+       ASN1_STRING *asn1str;
+       int ptype;
+       char *str;
+       int error;
+
+       asn1str = GENERAL_NAME_get0_value(ad, &ptype);
+       if (ptype != GEN_URI) {
+               pr_val_debug("Ignoring unknown GENERAL_NAME type: %d", ptype);
+               return ENOTSUP;
+       }
+
+       /*
+        * TODO (testers) According to RFC 5280, accessLocation can be an IRI
+        * somehow converted into URI form. I don't think that's an issue
+        * because the RSYNC clone operation should not have performed the
+        * conversion, so we should be looking at precisely the IA5String
+        * directory our g2l version of @asn1_string should contain.
+        * But ask the testers to keep an eye on it anyway.
+        */
+       error = ia5s2string(asn1str, &str);
+       if (error)
+               return error;
+       error = uri_init(uri, str);
+       free(str);
+
+       return error;
+}
+
 static int
 handle_cdp(void *ext, void *arg)
 {
@@ -1363,10 +1422,7 @@ handle_cdp(void *ext, void *arg)
        struct sia_uris *sias = arg;
        DIST_POINT *dp;
        GENERAL_NAMES *names;
-       GENERAL_NAME *name;
-       ASN1_IA5STRING *str;
        int i;
-       int type;
        char const *error_msg;
 
        if (sk_DIST_POINT_num(crldp) != 1) {
@@ -1404,25 +1460,25 @@ handle_cdp(void *ext, void *arg)
 
        names = dp->distpoint->name.fullname;
        for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
-               name = sk_GENERAL_NAME_value(names, i);
-               str = GENERAL_NAME_get0_value(name, &type);
-               if (type == GEN_URI && is_rsync(str)) {
-                       /*
-                        * Since we're parsing and validating the manifest's CRL
-                        * at some point, I think that all we need to do now is
-                        * compare this CRL URI to that one's.
-                        *
-                        * But there is a problem:
-                        * The manifest's CRL might not have been parsed at this
-                        * point. In fact, it's guaranteed to not have been
-                        * parsed if the certificate we're validating is the EE
-                        * certificate of the manifest itself.
-                        *
-                        * So we will store the URI in @refs, and validate it
-                        * later.
-                        */
-                       return ia5s2string(str, &sias->crldp);
+               /*
+                * Since we're parsing and validating the manifest's CRL at some
+                * point, I think that all we need to do now is compare this CRL
+                * URI to that one's.
+                *
+                * But there is a problem: The manifest's CRL might not have
+                * been parsed at this point. In fact, it's guaranteed to not
+                * have been parsed if the certificate we're validating is the
+                * EE certificate of the manifest itself.
+                *
+                * So we will store the URI in @sias, and validate it later.
+                */
+               if (gn2uri(sk_GENERAL_NAME_value(names, i), &sias->crldp) != 0)
+                       continue;
+               if (!uri_is_rsync(&sias->crldp)) {
+                       uri_cleanup(&sias->crldp);
+                       continue;
                }
+               return 0;
        }
 
        error_msg = "lacks an RSYNC URI";
@@ -1432,62 +1488,6 @@ dist_point_error:
            ext_cdp()->name, error_msg);
 }
 
-/*
- * Create @map from the @ad
- */
-static int
-ad2uri(char **uri, ACCESS_DESCRIPTION *ad)
-{
-       ASN1_STRING *asn1str;
-       int ptype;
-
-       asn1str = GENERAL_NAME_get0_value(ad->location, &ptype);
-
-       /*
-        * RFC 6487: "This extension MUST have an instance of an
-        * AccessDescription with an accessMethod of id-ad-rpkiManifest, (...)
-        * with an rsync URI [RFC5781] form of accessLocation."
-        *
-        * Ehhhhhh. It's a little annoying in that it seems to be stucking more
-        * than one requirement in a single sentence, which I think is rather
-        * rare for an RFC. Normally they tend to hammer things more.
-        *
-        * Does it imply that the GeneralName CHOICE is constrained to type
-        * "uniformResourceIdentifier"? I guess so, though I don't see anything
-        * stopping a few of the other types from also being capable of storing
-        * URIs.
-        *
-        * Also, nobody seems to be using the other types, and handling them
-        * would be a titanic pain. So this is what I'm committing to.
-        */
-       if (ptype != GEN_URI) {
-               pr_val_err("Unknown GENERAL_NAME type: %d", ptype);
-               return ENOTSUPPORTED;
-       }
-
-       /*
-        * GEN_URI signals an IA5String.
-        * IA5String is a subset of ASCII, so this cast is safe.
-        * No guarantees of a NULL chara though, which is why we need a dup.
-        *
-        * TODO (testers) According to RFC 5280, accessLocation can be an IRI
-        * somehow converted into URI form. I don't think that's an issue
-        * because the RSYNC clone operation should not have performed the
-        * conversion, so we should be looking at precisely the IA5String
-        * directory our g2l version of @asn1_string should contain.
-        * But ask the testers to keep an eye on it anyway.
-        *
-        * XXX There used to be a map_create() here. Make sure validations are
-        * restored somewhere:
-        * 1. ascii
-        * 2. "rsync://" or "https://" prefix (ENOTRSYNC, ENOTHTTPS)
-        * 3. URL normalization
-        */
-       *uri = pstrndup((char const *)ASN1_STRING_get0_data(asn1str),
-           ASN1_STRING_length(asn1str));
-       return 0;
-}
-
 /*
  * The RFC does not explain AD validation very well. This is personal
  * interpretation, influenced by Tim Bruijnzeels's response
@@ -1495,7 +1495,6 @@ ad2uri(char **uri, ACCESS_DESCRIPTION *ad)
  * (I'm being a bit more lax than he suggested.)
  *
  * 1. The NID (@nid) can be found more than once.
- * 2. All access descriptions that match the NID must be URLs.
  * 3. Depending on meta->required, zero or one of those matches will be an URL
  *    of the meta->type we're expecting.
  *    (I would have gone with "at least zero of those matches", but I don't know
@@ -1511,10 +1510,10 @@ ad2uri(char **uri, ACCESS_DESCRIPTION *ad)
  */
 static int
 handle_ad(int nid, struct ad_metadata const *meta, SIGNATURE_INFO_ACCESS *ia,
-    void (*cb)(char *, void *), void *arg)
+    int (*cb)(struct uri const *, void *), void *arg)
 {
        ACCESS_DESCRIPTION *ad;
-       char *uri;
+       struct uri uri;
        bool found;
        unsigned int i;
        int error;
@@ -1523,23 +1522,22 @@ handle_ad(int nid, struct ad_metadata const *meta, SIGNATURE_INFO_ACCESS *ia,
        for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ia); i++) {
                ad = sk_ACCESS_DESCRIPTION_value(ia, i);
                if (OBJ_obj2nid(ad->method) == nid) {
-                       error = ad2uri(&uri, ad);
-                       switch (error) {
-                       case 0:
-                               break;
-                       case ENOTSUPPORTED:
+                       if (gn2uri(ad->location, &uri) != 0)
                                continue;
-                       default:
-                               return error;
-                       }
 
                        if (found) {
-                               free(uri);
+                               uri_cleanup(&uri);
                                return pr_val_err("Extension '%s' has multiple '%s' %s URIs.",
                                    meta->ia_name, meta->name, meta->type);
                        }
 
-                       cb(uri, arg); /* Ownership of uri stolen */
+                       error = cb(&uri, arg);
+                       uri_cleanup(&uri);
+                       if (error == ENOTSUP)
+                               continue;
+                       if (error)
+                               return error;
+
                        found = true;
                }
        }
@@ -1553,18 +1551,23 @@ handle_ad(int nid, struct ad_metadata const *meta, SIGNATURE_INFO_ACCESS *ia,
        return 0;
 }
 
-static void
-handle_caIssuers(char *uri, void *arg)
+static int
+handle_caIssuers(struct uri const *uri, void *arg)
 {
        struct sia_uris *sias = arg;
+
+       if (!uri_is_rsync(uri)) {
+               pr_val_debug("Ignoring non-rsync caIssuers '%s'.", uri_str(uri));
+               return ENOTSUP;
+       }
+
        /*
         * Bringing the parent certificate's URI all the way
         * over here is too much trouble, so do the handle_cdp()
         * hack.
-        *
-        * XXX Uh... it's extremely easy now.
         */
-       sias->caIssuers = uri;
+       uri_copy(&sias->caIssuers, uri);
+       return 0;
 }
 
 static int
@@ -1711,7 +1714,7 @@ validate_ca_extensions(struct rpki_certificate *cert)
        error = certificate_validate_aia(cert);
        if (error)
                return error;
-       return validate_cdp(&cert->sias, cert->parent->rpp.crl.map->url);
+       return validate_cdp(&cert->sias, &cert->parent->rpp.crl.map->url);
 }
 
 int
@@ -1796,11 +1799,11 @@ get_certificate_type(struct rpki_certificate *cert)
 int
 certificate_validate_aia(struct rpki_certificate *cert)
 {
-       /*
-        * FIXME Compare the AIA to the parent's URI.
-        * We're currently not recording the URI, so this can't be solved until
-        * the #78 refactor.
-        */
+       if (!uri_equals(&cert->parent->map.url, &cert->sias.caIssuers))
+               return pr_val_err("Certificate's caIssuers (%s) does not match parent certificate's URL (%s).",
+                   uri_str(&cert->parent->map.url),
+                   uri_str(&cert->sias.caIssuers));
+
        return 0;
 }
 
@@ -1895,10 +1898,9 @@ int
 certificate_traverse(struct rpki_certificate *ca)
 {
        struct cache_cage *cage;
-       char const *mft_path;
+       struct cache_mapping mft;
        array_index i;
        struct cache_mapping *map;
-       char const *ext;
        unsigned int queued;
        int error;
 
@@ -1919,19 +1921,21 @@ certificate_traverse(struct rpki_certificate *ca)
        default:
                return pr_val_err("caRepository '%s' could not be refreshed, "
                    "and there is no fallback in the cache. "
-                   "I'm going to have to skip it.", ca->sias.caRepository);
+                   "I'm going to have to skip it.",
+                   uri_str(&ca->sias.caRepository));
        }
 
-retry: mft_path = cage_map_file(cage, ca->sias.rpkiManifest);
-       if (!mft_path) {
+       mft.url = ca->sias.rpkiManifest;
+retry: mft.path = (char *)cage_map_file(cage, &mft.url); /* Will not edit */
+       if (!mft.path) {
                if (cage_disable_refresh(cage))
                        goto retry;
                error = pr_val_err("caRepository '%s' is missing a manifest.",
-                   ca->sias.caRepository);
+                   uri_str(&ca->sias.caRepository));
                goto end;
        }
 
-       error = manifest_traverse(ca->sias.rpkiManifest, mft_path, cage, ca);
+       error = manifest_traverse(&mft, cage, ca);
        if (error) {
                if (cage_disable_refresh(cage))
                        goto retry;
@@ -1941,18 +1945,18 @@ retry:  mft_path = cage_map_file(cage, ca->sias.rpkiManifest);
        queued = 0;
        for (i = 0; i < ca->rpp.nfiles; i++) {
                map = ca->rpp.files + i;
-               ext = map->url + strlen(map->url) - 4;
-               if (strcmp(ext, ".cer") == 0)
+               if (uri_has_extension(&map->url, ".cer"))
                        queued += task_enqueue_rpp(map, ca);
-               else if (strcmp(ext, ".roa") == 0)
+               else if (uri_has_extension(&map->url, ".roa"))
                        roa_traverse(map, ca);
-               else if (strcmp(ext, ".gbr") == 0)
+               else if (uri_has_extension(&map->url, ".gbr"))
                        ghostbusters_traverse(map, ca);
        }
 
        if (queued > 0)
                task_wakeup();
-       cache_commit_rpp(cage_rpkiNotify(cage), ca->sias.caRepository, &ca->rpp);
+       cache_commit_rpp(cage_rpkiNotify(cage), &ca->sias.caRepository,
+           &ca->rpp);
 
 end:   free(cage);
        return error;
index 305d9d4f73be4fbd3e0bcb07169e8e1b7747abf3..d9cffc8574ffa2ff4264ead76c44723ff4c7adea 100644 (file)
@@ -43,7 +43,7 @@ ghostbusters_traverse(struct cache_mapping *map,
        error = handle_vcard(&sobj);
        if (error)
                goto end3;
-       error = refs_validate_ee(&ee.sias, parent->rpp.crl.map->url, map->url);
+       error = refs_validate_ee(&ee.sias, &parent->rpp.crl.map->url, &map->url);
 
 end3:  rpki_certificate_cleanup(&ee);
        signed_object_cleanup(&sobj);
index a5a3372be08f4785bc4f24a18822aaffcf75fa44..b0ae10a34c4fbde9eadbcf6c84b00b0010a805cc 100644 (file)
@@ -288,12 +288,12 @@ check_file_and_hash(struct FileAndHash *fah, char const *path)
  */
 
 static int
-collect_files(char const *mft_url, char const *mft_path,
+collect_files(struct cache_mapping const *map,
     struct Manifest *mft, struct cache_cage *cage,
     struct rpki_certificate *parent)
 {
        struct rpp *rpp;
-       char *rpp_url;
+       struct uri rpp_url;
        unsigned int m;
        struct FileAndHash *src;
        struct cache_mapping *dst;
@@ -305,7 +305,9 @@ collect_files(char const *mft_url, char const *mft_path,
                return pr_val_err("Manifest's file list is empty.");
 
        rpp = &parent->rpp;
-       rpp_url = url_parent(mft_url); // XXX
+       error = uri_parent(&map->url, &rpp_url);
+       if (error)
+               return error;
        rpp->files = pzalloc((mft->fileList.list.count + 1) * sizeof(*rpp->files));
        rpp->nfiles = 0;
 
@@ -339,15 +341,14 @@ collect_files(char const *mft_url, char const *mft_path,
                        continue;
 
                dst = &rpp->files[rpp->nfiles++];
-               dst->url = path_njoin(rpp_url,
-                   (char const *)src->file.buf,
-                   src->file.size);
+               uri_child(&rpp_url, (char const *)src->file.buf, src->file.size,
+                   &dst->url);
 
-               path = cage_map_file(cage, dst->url);
+               path = cage_map_file(cage, &dst->url);
                if (!path) {
                        error = pr_val_err(
                            "Manifest file '%s' is absent from the cache.",
-                           dst->url);
+                           uri_str(&dst->url));
                        goto revert;
                }
                dst->path = pstrdup(path);
@@ -359,13 +360,12 @@ collect_files(char const *mft_url, char const *mft_path,
 
        /* Manifest */
        dst = &rpp->files[rpp->nfiles++];
-       dst->url = pstrdup(mft_url);
-       dst->path = pstrdup(mft_path);
+       uri_copy(&dst->url, &map->url);
+       dst->path = pstrdup(map->path);
 
        return 0;
 
 revert:        rpp_cleanup(rpp);
-       free(rpp_url);
        return error;
 }
 
@@ -378,7 +378,7 @@ load_crl(struct rpki_certificate *parent)
        rpp = &parent->rpp;
 
        for (f = 0; f < rpp->nfiles; f++)
-               if (str_ends_with(rpp->files[f].url, ".crl")) {
+               if (uri_has_extension(&rpp->files[f].url, ".crl")) {
                        if (rpp->crl.map != NULL)
                                return pr_val_err("Manifest has more than one CRL.");
                        rpp->crl.map = &rpp->files[f];
@@ -392,12 +392,12 @@ load_crl(struct rpki_certificate *parent)
 }
 
 static int
-build_rpp(char const *mft_url, char const *mft_path, struct Manifest *mft,
+build_rpp(struct cache_mapping const *map, struct Manifest *mft,
     struct cache_cage *cage, struct rpki_certificate *parent)
 {
        int error;
 
-       error = collect_files(mft_url, mft_path, mft, cage, parent);
+       error = collect_files(map, mft, cage, parent);
        if (error)
                return error;
 
@@ -411,8 +411,8 @@ build_rpp(char const *mft_url, char const *mft_path, struct Manifest *mft,
 }
 
 int
-manifest_traverse(char const *mft_url, char const *mft_path,
-    struct cache_cage *cage, struct rpki_certificate *parent)
+manifest_traverse(struct cache_mapping const *map, struct cache_cage *cage,
+    struct rpki_certificate *parent)
 {
        static OID oid = OID_MANIFEST;
        struct oid_arcs arcs = OID2ARCS("manifest", oid);
@@ -422,10 +422,10 @@ manifest_traverse(char const *mft_url, char const *mft_path,
        int error;
 
        /* Prepare */
-       fnstack_push(mft_url);
+       fnstack_push_map(map);
 
        /* Decode */
-       error = signed_object_decode(&sobj, mft_path);
+       error = signed_object_decode(&sobj, map->path);
        if (error)
                goto end1;
        error = decode_manifest(&sobj, &mft);
@@ -433,7 +433,7 @@ manifest_traverse(char const *mft_url, char const *mft_path,
                goto end2;
 
        /* Initialize @summary */
-       error = build_rpp(mft_url, mft_path, mft, cage, parent);
+       error = build_rpp(map, mft, cage, parent);
        if (error)
                goto end3;
 
@@ -447,7 +447,7 @@ manifest_traverse(char const *mft_url, char const *mft_path,
        error = validate_manifest(mft, cage, &parent->rpp.mft);
        if (error)
                goto end5;
-       error = refs_validate_ee(&ee.sias, parent->rpp.crl.map->url, mft_url);
+       error = refs_validate_ee(&ee.sias, &parent->rpp.crl.map->url, &map->url);
 
 end5:  rpki_certificate_cleanup(&ee);
        if (error)
index ad09fac1099f3f8c0ba4a2f6e29bbb133b7598f6..58be295df07634b01a7b6756f3fb799232298a8e 100644 (file)
@@ -7,7 +7,7 @@
 #include "cache.h"
 #include "object/certificate.h"
 
-int manifest_traverse(char const *, char const *, struct cache_cage *,
+int manifest_traverse(struct cache_mapping const *, struct cache_cage *,
     struct rpki_certificate *);
 
 #endif /* SRC_OBJECT_MANIFEST_H_ */
index e2ea6dbf3d41b2fe6bce388b00d62a0b2046720f..9907f426fe2cc91f08de6a7e0e66a589b7205c98 100644 (file)
@@ -233,7 +233,7 @@ roa_traverse(struct cache_mapping *map, struct rpki_certificate *parent)
        error = __handle_roa(roa, ee.resources);
        if (error)
                goto end4;
-       error = refs_validate_ee(&ee.sias, parent->rpp.crl.map->url, map->url);
+       error = refs_validate_ee(&ee.sias, &parent->rpp.crl.map->url, &map->url);
 
 end4:  rpki_certificate_cleanup(&ee);
        ASN_STRUCT_FREE(asn_DEF_RouteOriginAttestation, roa);
index 8ec75d5dbd16832c644d7917c7f79b719aefd827..db4de218fda2f877c596e223d7663687a6e9a5e7 100644 (file)
@@ -19,7 +19,7 @@
 
 struct tal {
        char const *file_name;
-       struct strlist urls;
+       struct uris urls;
        unsigned char *spki; /* Decoded; not base64. */
        size_t spki_len;
 };
@@ -51,6 +51,7 @@ read_content(char *fc /* File Content */, struct tal *tal)
 {
        char *nl; /* New Line */
        bool cr; /* Carriage return */
+       struct uri url;
 
        /* Comment section */
        while (fc[0] == '#') {
@@ -71,8 +72,14 @@ read_content(char *fc /* File Content */, struct tal *tal)
                if (is_blank(fc))
                        break;
 
-               if (url_is_https(fc) || url_is_rsync(fc))
-                       strlist_add(&tal->urls, pstrdup(fc));
+               if (uri_init(&url, fc) == 0) {
+                       if (uri_is_https(&url) || uri_is_rsync(&url))
+                               uris_add(&tal->urls, &url);
+                       else
+                               uri_cleanup(&url);
+               } else {
+                       pr_op_debug("Cannot parse '%s' as a URI; ignoring.", fc);
+               }
 
                fc = nl + cr + 1;
                if (*fc == '\0')
@@ -106,10 +113,10 @@ tal_init(struct tal *tal, char const *file_path)
 
        tal->file_name = path_filename(file_path);
 
-       strlist_init(&tal->urls);
+       uris_init(&tal->urls);
        error = read_content((char *)file.buffer, tal);
        if (error)
-               strlist_cleanup(&tal->urls);
+               uris_cleanup(&tal->urls, uri_cleanup);
 
        file_free(&file);
        return error;
@@ -119,7 +126,7 @@ static void
 tal_cleanup(struct tal *tal)
 {
        free(tal->spki);
-       strlist_cleanup(&tal->urls);
+       uris_cleanup(&tal->urls, uri_cleanup);
 }
 
 char const *
@@ -165,18 +172,18 @@ validate_ta(struct tal *tal, struct cache_mapping const *ta_map)
 }
 
 static int
-try_urls(struct tal *tal, bool (*url_is_protocol)(char const *),
-    char *(*get_path)(char const *))
+try_urls(struct tal *tal, bool (*url_is_protocol)(struct uri const *),
+    char *(*get_path)(struct uri const *))
 {
-       char **url;
+       struct uri *url;
        struct cache_mapping map;
        int error;
 
        ARRAYLIST_FOREACH(&tal->urls, url) {
                map.url = *url;
-               if (!url_is_protocol(map.url))
+               if (!url_is_protocol(&map.url))
                        continue;
-               map.path = get_path(*url);
+               map.path = get_path(url);
                if (!map.path)
                        continue;
                error = validate_ta(tal, &map);
@@ -204,17 +211,17 @@ traverse_tal(char const *tal_path)
                goto end1;
 
        /* Online attempts */
-       error = try_urls(&tal, url_is_https, cache_refresh_by_url);
+       error = try_urls(&tal, uri_is_https, cache_refresh_by_url);
        if (!error || error == EBUSY)
                goto end2;
-       error = try_urls(&tal, url_is_rsync, cache_refresh_by_url);
+       error = try_urls(&tal, uri_is_rsync, cache_refresh_by_url);
        if (!error || error == EBUSY)
                goto end2;
        /* Offline fallback attempts */
-       error = try_urls(&tal, url_is_https, cache_get_fallback);
+       error = try_urls(&tal, uri_is_https, cache_get_fallback);
        if (!error || error == EBUSY)
                goto end2;
-       error = try_urls(&tal, url_is_rsync, cache_get_fallback);
+       error = try_urls(&tal, uri_is_rsync, cache_get_fallback);
        if (!error || error == EBUSY)
                goto end2;
 
index 4183f5c66abdad5546304b14e5b8460c94dff482..e7861c96a25ef22af1b38db9db855afa0889ef8e 100644 (file)
 static BIO *
 __rsync2bio(char const *src, char const *dst)
 {
+       struct uri url;
        int error;
 
+       if (uri_init(&url, src) != 0)
+               return NULL;
+
        // XXX use the cache
 
-       error = rsync_queue(src, dst);
+       error = rsync_queue(&url, dst);
+
+       uri_cleanup(&url);
+
        if (error) {
                pr_op_err("rsync download failed: %s", strerror(abs(error)));
                return NULL;
@@ -99,7 +106,7 @@ filename2bio(char const *filename)
        if (filename == NULL || strcmp(filename, "-") == 0)
                return BIO_new_fp(stdin, BIO_NOCLOSE);
 
-       if (url_is_rsync(filename))
+       if (str_starts_with(filename, "rsync://"))
                return rsync2bio(filename);
 
        return BIO_new_file(filename, "rb");
index 4a71c24783641aa02c1cee9eeb335fdc9fadee4d..e7b15f855b91e2c4abc5ebb235800b08ea9485f5 100644 (file)
@@ -78,7 +78,7 @@ struct rrdp_state {
 };
 
 struct file_metadata {
-       char *uri;
+       struct uri uri;
        unsigned char *hash; /* Array. Sometimes omitted. */
        size_t hash_len;
 };
@@ -97,7 +97,7 @@ struct update_notification {
        struct rrdp_session session;
        struct file_metadata snapshot;
        struct notification_deltas deltas;
-       char const *url;
+       struct uri const *url;
 };
 
 /* A deserialized <publish> tag, from a snapshot or delta. */
@@ -144,24 +144,42 @@ session_cleanup(struct rrdp_session *meta)
 }
 
 static struct cache_file *
-state_find_file(struct rrdp_state const *state, char const *url, size_t len)
+state_find_file(struct rrdp_state const *state, struct uri const *url)
 {
+       char const *str;
+       size_t len;
        struct cache_file *file;
-       HASH_FIND(hh, state->files, url, len, file);
+
+       str = uri_str(url);
+       len = uri_len(url);
+
+       HASH_FIND(hh, state->files, str, len, file);
+
        return file;
 }
 
+static void
+state_add_file(struct rrdp_state *state, struct cache_file *file)
+{
+       char const *url;
+       size_t urlen;
+
+       url = uri_str(&file->map.url);
+       urlen = uri_len(&file->map.url);
+
+       HASH_ADD_KEYPTR(hh, state->files, url, urlen, file);
+}
+
 static struct cache_file *
-cache_file_add(struct rrdp_state *state, char *url, char *path)
+cache_file_add(struct rrdp_state *state, struct uri const *url, char *path)
 {
        struct cache_file *file;
-       size_t urlen;
 
        file = pzalloc(sizeof(struct cache_file));
-       file->map.url = url;
+       uri_copy(&file->map.url, url);
        file->map.path = path;
-       urlen = strlen(url);
-       HASH_ADD_KEYPTR(hh, state->files, file->map.url, urlen, file);
+
+       state_add_file(state, file);
 
        return file;
 }
@@ -169,7 +187,7 @@ cache_file_add(struct rrdp_state *state, char *url, char *path)
 static void
 metadata_cleanup(struct file_metadata *meta)
 {
-       free(meta->uri);
+       uri_cleanup(&meta->uri);
        free(meta->hash);
 }
 
@@ -181,7 +199,8 @@ notification_delta_cleanup(struct notification_delta *delta)
 }
 
 static void
-update_notification_init(struct update_notification *notif, char const *url)
+update_notification_init(struct update_notification *notif,
+    struct uri const *url)
 {
        memset(&notif->session, 0, sizeof(notif->session));
        memset(&notif->snapshot, 0, sizeof(notif->snapshot));
@@ -279,22 +298,6 @@ parse_string(xmlTextReaderPtr reader, char const *attr)
        return result;
 }
 
-static char *
-parse_uri(xmlTextReaderPtr reader)
-{
-       xmlChar *xmlattr;
-       char *result;
-
-       xmlattr = parse_string(reader, RRDP_ATTR_URI);
-       if (xmlattr == NULL)
-               return NULL;
-
-       result = pstrdup((char const *)xmlattr);
-
-       xmlFree(xmlattr);
-       return result;
-}
-
 static unsigned int
 hexchar2uint(xmlChar xmlchar)
 {
@@ -473,18 +476,22 @@ end:
 static int
 parse_file_metadata(xmlTextReaderPtr reader, struct file_metadata *meta)
 {
+       xmlChar *xmlattr;
        int error;
 
        memset(meta, 0, sizeof(*meta));
 
-       meta->uri = parse_uri(reader);
-       if (meta->uri == NULL)
+       xmlattr = parse_string(reader, RRDP_ATTR_URI);
+       if (xmlattr == NULL)
+               return -EINVAL;
+       error = uri_init(&meta->uri, (char const *)xmlattr);
+       xmlFree(xmlattr);
+       if (error)
                return -EINVAL;
 
        error = parse_hash(reader, &meta->hash, &meta->hash_len);
        if (error) {
-               free(meta->uri);
-               meta->uri = NULL;
+               uri_cleanup(&meta->uri);
                return error;
        }
 
@@ -492,16 +499,16 @@ parse_file_metadata(xmlTextReaderPtr reader, struct file_metadata *meta)
 }
 
 static bool
-is_known_extension(char const *uri)
+is_known_extension(struct uri const *uri)
 {
        size_t len;
        char const *ext;
 
-       len = strlen(uri);
+       len = uri_len(uri);
        if (len < 4)
                return false;
 
-       ext = uri + len - 4;
+       ext = uri_str(uri) + len - 4;
        return ((strcmp(ext, ".cer") == 0)
             || (strcmp(ext, ".roa") == 0)
             || (strcmp(ext, ".mft") == 0)
@@ -525,11 +532,11 @@ handle_publish(xmlTextReaderPtr reader, struct parser_args *args)
        if (xmlTextReaderRead(reader) != 1) {
                error = pr_val_err(
                    "Couldn't read publish content of element '%s'",
-                   tag.meta.uri
+                   uri_str(&tag.meta.uri)
                );
                goto end;
        }
-       if (!is_known_extension(tag.meta.uri))
+       if (!is_known_extension(&tag.meta.uri))
                goto end; /* Mirror rsync filters */
 
        /* Parse tag content */
@@ -549,7 +556,7 @@ handle_publish(xmlTextReaderPtr reader, struct parser_args *args)
 
        pr_clutter("Publish %s", logv_filename(tag.meta.uri));
 
-       file = state_find_file(args->state, tag.meta.uri, strlen(tag.meta.uri));
+       file = state_find_file(args->state, &tag.meta.uri);
 
        /* rfc8181#section-2.2 */
        if (file) {
@@ -558,7 +565,7 @@ handle_publish(xmlTextReaderPtr reader, struct parser_args *args)
                        error = pr_val_err("RRDP desync: "
                            "<publish> is attempting to create '%s', "
                            "but the file is already cached.",
-                           tag.meta.uri);
+                           uri_str(&tag.meta.uri));
                        goto end;
                }
 
@@ -585,7 +592,7 @@ handle_publish(xmlTextReaderPtr reader, struct parser_args *args)
                        error = pr_val_err("RRDP desync: "
                            "<publish> is attempting to overwrite '%s', "
                            "but the file is absent in the cache.",
-                           tag.meta.uri);
+                           uri_str(&tag.meta.uri));
                        goto end;
                }
 
@@ -594,7 +601,7 @@ handle_publish(xmlTextReaderPtr reader, struct parser_args *args)
                        error = -EINVAL;
                        goto end;
                }
-               file = cache_file_add(args->state, pstrdup(tag.meta.uri), path);
+               file = cache_file_add(args->state, &tag.meta.uri, path);
        }
 
        error = file_write_bin(file->map.path, tag.content, tag.content_len);
@@ -609,29 +616,27 @@ handle_withdraw(xmlTextReaderPtr reader, struct parser_args *args)
 {
        struct withdraw tag = { 0 };
        struct cache_file *file;
-       size_t len;
        int error;
 
        error = parse_file_metadata(reader, &tag.meta);
        if (error)
-               goto end;
-       if (!is_known_extension(tag.meta.uri))
+               return error;
+       if (!is_known_extension(&tag.meta.uri))
                goto end; /* Mirror rsync filters */
        if (!tag.meta.hash) {
                error = pr_val_err("Withdraw '%s' is missing a hash.",
-                   tag.meta.uri);
+                   uri_str(&tag.meta.uri));
                goto end;
        }
 
        pr_clutter("Withdraw %s", logv_filename(tag.meta.uri));
 
-       len = strlen(tag.meta.uri);
-       file = state_find_file(args->state, tag.meta.uri, len);
+       file = state_find_file(args->state, &tag.meta.uri);
 
        if (!file) {
                error = pr_val_err("Broken RRDP: "
                    "<withdraw> is attempting to delete unknown file '%s'.",
-                   tag.meta.uri);
+                   uri_str(&tag.meta.uri));
                goto end;
        }
 
@@ -665,11 +670,11 @@ parse_notification_snapshot(xmlTextReaderPtr reader,
 
        if (!notif->snapshot.hash)
                return pr_val_err("Snapshot '%s' is missing a hash.",
-                   notif->snapshot.uri);
+                   uri_str(&notif->snapshot.uri));
 
-       if (!url_same_origin(notif->url, notif->snapshot.uri))
+       if (!uri_same_origin(notif->url, &notif->snapshot.uri))
                return pr_val_err("Notification '%s' and Snapshot '%s' are not hosted by the same origin.",
-                   notif->url, notif->snapshot.uri);
+                   uri_str(notif->url), uri_str(&notif->snapshot.uri));
 
        return 0;
 }
@@ -687,25 +692,25 @@ parse_notification_delta(xmlTextReaderPtr reader,
 
        error = parse_file_metadata(reader, &delta.meta);
        if (error)
-               goto fail;
+               goto srl;
 
        if (!delta.meta.hash) {
                error = pr_val_err("Delta '%s' is missing a hash.",
-                   delta.meta.uri);
-               goto fail;
+                   uri_str(&delta.meta.uri));
+               goto mta;
        }
 
-       if (!url_same_origin(notif->url, delta.meta.uri)) {
+       if (!uri_same_origin(notif->url, &delta.meta.uri)) {
                error = pr_val_err("Notification %s and Delta %s are not hosted by the same origin.",
-                   notif->url, delta.meta.uri);
-               goto fail;
+                   uri_str(notif->url), uri_str(&delta.meta.uri));
+               goto mta;
        }
 
        notification_deltas_add(&notif->deltas, &delta);
        return 0;
 
-fail:  serial_cleanup(&delta.serial);
-       metadata_cleanup(&delta.meta);
+mta:   metadata_cleanup(&delta.meta);
+srl:   serial_cleanup(&delta.serial);
        return error;
 }
 
@@ -827,7 +832,7 @@ xml_read_notif(xmlTextReaderPtr reader, void *arg)
 }
 
 static int
-parse_notification(char const *url, char const *path,
+parse_notification(struct uri const *url, char const *path,
     struct update_notification *result)
 {
        int error;
@@ -913,7 +918,7 @@ validate_session_desync(struct rrdp_state *old_notif,
 
 /* TODO (performance) Stream instead of caching notifs, snapshots & deltas. */
 static int
-dl_tmp(char const *url, char *path)
+dl_tmp(struct uri const *url, char *path)
 {
        cache_tmpfile(path);
        return http_download(url, path, 0, NULL);
@@ -923,12 +928,14 @@ static int
 handle_snapshot(struct update_notification *new, struct rrdp_state *state)
 {
        char tmppath[CACHE_TMPFILE_BUFLEN];
+       struct uri *url;
        int error;
 
-       pr_val_debug("Processing snapshot.");
-       fnstack_push(new->snapshot.uri);
+       url = &new->snapshot.uri;
+       pr_val_debug("Processing snapshot '%s'.", uri_str(url));
+       fnstack_push(uri_str(url));
 
-       error = dl_tmp(new->snapshot.uri, tmppath);
+       error = dl_tmp(url, tmppath);
        if (error)
                goto end;
        error = validate_hash(&new->snapshot, tmppath);
@@ -995,12 +1002,15 @@ handle_delta(struct update_notification *notif,
     struct notification_delta *delta, struct rrdp_state *state)
 {
        char tmppath[CACHE_TMPFILE_BUFLEN];
+       struct uri const *url;
        int error;
 
-       pr_val_debug("Processing delta '%s'.", delta->meta.uri);
-       fnstack_push(delta->meta.uri);
+       url = &delta->meta.uri;
 
-       error = dl_tmp(delta->meta.uri, tmppath);
+       pr_val_debug("Processing delta '%s'.", uri_str(url));
+       fnstack_push(uri_str(url));
+
+       error = dl_tmp(url, tmppath);
        if (error)
                goto end;
        error = parse_delta(notif, delta, tmppath, state);
@@ -1150,7 +1160,7 @@ update_notif(struct rrdp_state *old, struct update_notification *new)
 }
 
 static int
-dl_notif(char const *url, time_t mtim, bool *changed,
+dl_notif(struct uri const *url, time_t mtim, bool *changed,
     struct update_notification *new)
 {
        char tmppath[CACHE_TMPFILE_BUFLEN];
@@ -1189,7 +1199,7 @@ dl_notif(char const *url, time_t mtim, bool *changed,
  * snapshot, and explodes them into @notif->path.
  */
 int
-rrdp_update(char const *notif, char const *path, time_t mtim,
+rrdp_update(struct uri const *notif, char const *path, time_t mtim,
     bool *changed, struct rrdp_state **state)
 {
        struct rrdp_state *old;
@@ -1197,7 +1207,7 @@ rrdp_update(char const *notif, char const *path, time_t mtim,
        int serial_cmp;
        int error;
 
-       fnstack_push(notif);
+       fnstack_push(uri_str(notif));
        pr_val_debug("Processing notification.");
 
        error = dl_notif(notif, mtim, changed, &new);
@@ -1288,18 +1298,20 @@ end:    fnstack_pop();
 }
 
 char const *
-rrdp_file(struct rrdp_state const *state, char const *url)
+rrdp_file(struct rrdp_state const *state, struct uri const *url)
 {
        struct cache_file *file;
-       file = state_find_file(state, url, strlen(url));
+       file = state_find_file(state, url);
        return file ? file->map.path : NULL;
 }
 
 char const *
-rrdp_create_fallback(char *cage, struct rrdp_state **_state, char const *url)
+rrdp_create_fallback(char *cage, struct rrdp_state **_state,
+    struct uri const *url)
 {
        struct rrdp_state *state;
        struct cache_file *file;
+       char const *str;
        size_t len;
 
        state = *_state;
@@ -1309,16 +1321,17 @@ rrdp_create_fallback(char *cage, struct rrdp_state **_state, char const *url)
        }
 
        file = pzalloc(sizeof(struct cache_file));
-       file->map.url = pstrdup(url);
+       uri_copy(&file->map.url, url);
        file->map.path = cseq_next(&state->seq);
        if (!file->map.path) {
-               free(file->map.url);
+               uri_cleanup(&file->map.url);
                free(file);
                return NULL;
        }
 
-       len = strlen(file->map.url);
-       HASH_ADD_KEYPTR(hh, state->files, file->map.url, len, file);
+       str = uri_str(&file->map.url);
+       len = uri_len(&file->map.url);
+       HASH_ADD_KEYPTR(hh, state->files, str, len, file);
 
        return file->map.path;
 }
@@ -1346,7 +1359,7 @@ files2json(struct rrdp_state *state)
                return NULL;
 
        HASH_ITER(hh, state->files, file, tmp)
-               if (json_add_str(json, file->map.url, file->map.path))
+               if (json_add_str(json, uri_str(&file->map.url), file->map.path))
                        goto fail;
 
        return json;
@@ -1456,6 +1469,7 @@ json2files(json_t *jparent, char *parent, struct rrdp_state *state)
        char const *jkey;
        json_t *jvalue;
        size_t parent_len;
+       struct uri url;
        char const *path;
        unsigned long id, max_id;
        int error;
@@ -1476,25 +1490,33 @@ json2files(json_t *jparent, char *parent, struct rrdp_state *state)
                        pr_op_warn("RRDP file URL '%s' is not a string.", jkey);
                        continue;
                }
+               error = uri_init(&url, jkey);
+               if (error) {
+                       pr_op_warn("Cannot parse '%s' as a URI.", jkey);
+                       continue;
+               }
 
-               // XXX sanitize more
+               // XXX sanitize more?
 
                path = json_string_value(jvalue);
                if (strncmp(path, parent, parent_len) != 0 || path[parent_len] != '/') {
                        pr_op_warn("RRDP path '%s' is not child of '%s'.",
                            path, parent);
+                       uri_cleanup(&url);
                        continue;
                }
 
                error = hex2ulong(path + parent_len + 1, &id);
                if (error) {
                        pr_op_warn("RRDP file '%s' is not a hexadecimal number.", path);
+                       uri_cleanup(&url);
                        continue;
                }
                if (id > max_id)
                        max_id = id;
 
-               cache_file_add(state, pstrdup(jkey), pstrdup(path));
+               cache_file_add(state, &url, pstrdup(path));
+               uri_cleanup(&url);
        }
 
        if (HASH_COUNT(state->files) == 0) {
@@ -1640,10 +1662,11 @@ rrdp_print(struct rrdp_state *rs)
        if (rs == NULL)
                return;
 
-       printf("session:%s/%s\n", rs->session.session_id, rs->session.serial.str);
+       /* printf("session:%s/%s\n", rs->session.session_id, rs->session.serial.str); */
+       printf("\n");
        HASH_ITER(hh, rs->files, file, tmp)
-               printf("\t\tfile: %s\n", /* file->map.url, */ file->map.path);
-       printf("\t\tseq:%s/%lx\n", rs->seq.prefix, rs->seq.next_id);
+               printf("\t\tfile: %s (%s)\n", file->map.path, uri_str(&file->map.url));
+       printf("\t\tseq:  %s/%lx\n", rs->seq.prefix, rs->seq.next_id);
 
        STAILQ_FOREACH(hash, &rs->delta_hashes, hook) {
                printf("\t\thash: ");
index f0977ca8fae203eae439cba4c93c1c8c5127787e..ad67a89878411deee20cebef4a350da33f668151 100644 (file)
@@ -6,14 +6,16 @@
 #include <time.h>
 
 #include "file.h"
+#include "types/url.h"
 
 struct rrdp_state;
 
-int rrdp_update(char const *, char const *, time_t, bool *,
+int rrdp_update(struct uri const *, char const *, time_t, bool *,
     struct rrdp_state **);
-char const *rrdp_file(struct rrdp_state const *, char const *);
+char const *rrdp_file(struct rrdp_state const *, struct uri const *);
 
-char const *rrdp_create_fallback(char *, struct rrdp_state **, char const *);
+char const *rrdp_create_fallback(char *, struct rrdp_state **,
+    struct uri const *);
 
 json_t *rrdp_state2json(struct rrdp_state *);
 int rrdp_json2state(json_t *, char *, struct rrdp_state **);
index 1bde288a019ed9d4c061d9679c5e3b51d77e3cea..a8d869ec4204d7bff10a551c34641332c900fb10 100644 (file)
@@ -49,7 +49,7 @@ struct {
 
 struct rsync_task {
        int pid;
-       char *url;
+       struct uri url;
        char *path;
        int stdoutfd;   /* Child rsync's standard output */
        int stderrfd;   /* Child rsync's standard error */
@@ -131,7 +131,7 @@ notify_parent(struct rsync_task *task)
         * The asn1 code needs better error reporting.
         */
 
-       if (RsyncRequest_init(&req, task->url, task->path) < 0) {
+       if (RsyncRequest_init(&req, &task->url, task->path) < 0) {
                pr_op_err(RSP "Cannot message parent process: "
                    "The request object cannot be created");
                return;
@@ -157,7 +157,7 @@ void_task(struct rsync_task *task)
 {
        notify_parent(task);
 
-       free(task->url);
+       uri_cleanup(&task->url);
        free(task->path);
        free(task);
 }
@@ -220,7 +220,7 @@ create_pipes(int fds[2][2])
 }
 
 static void
-prepare_rsync_args(char **args, char const *url, char const *path)
+prepare_rsync_args(char **args, struct uri const *url, char const *path)
 {
        size_t i;
 
@@ -231,7 +231,7 @@ prepare_rsync_args(char **args, char const *url, char const *path)
 
        for (i = 0; rsync_args[i] != NULL; i++)
                args[i] = (char *)rsync_args[i];
-       args[i++] = (char *)url;
+       args[i++] = (char *)uri_str(url);
        args[i++] = (char *)path;
        args[i++] = NULL;
 }
@@ -257,7 +257,7 @@ duplicate_fds(int fds[2][2])
 }
 
 static int
-execvp_rsync(char const *url, char const *path, int fds[2][2])
+execvp_rsync(struct uri const *url, char const *path, int fds[2][2])
 {
        char *args[20];
 
@@ -296,7 +296,7 @@ fork_rsync(struct rsync_task *task)
        }
 
        if (task->pid == 0) /* Child code */
-               exit(execvp_rsync(task->url, task->path, fork_fds));
+               exit(execvp_rsync(&task->url, task->path, fork_fds));
 
        /* Parent code */
 
@@ -322,6 +322,7 @@ activate_task(struct rsync_tasks *tasks, struct rsync_task *task,
        tasks->a++;
 }
 
+/* Steals ownership of @map. */
 static void
 post_task(struct cache_mapping *map, struct rsync_tasks *tasks,
     struct timespec *now)
@@ -358,7 +359,9 @@ again:      if (pssk.rd.len > 0) {
 
                switch (decres.code) {
                case RC_OK:
-                       result->url = OCTET_STRING_toString(&pssk.rr->url);
+                       __uri_init(&result->url,
+                           OCTET_STRING_toString(&pssk.rr->url),
+                           pssk.rr->url.size);
                        result->path = OCTET_STRING_toString(&pssk.rr->path);
                        ASN_STRUCT_RESET(asn_DEF_RsyncRequest, pssk.rr);
                        return 0;
@@ -557,7 +560,7 @@ activate_queued(struct rsync_tasks *tasks, struct timespec *now)
                return;
 
        pr_op_debug(RSP "Activating queued task %s -> %s.",
-           task->url, task->path);
+           uri_str(&task->url), task->path);
        LIST_REMOVE(task, lh);
        activate_task(tasks, task, now);
 }
@@ -723,7 +726,7 @@ rcv_spawner_responses(void *arg)
        struct cache_mapping map = { 0 };
 
        while (next_task(&map) == 0) {
-               rsync_finished(map.url, map.path);
+               rsync_finished(&map.url, map.path);
                map_cleanup(&map);
        }
 
@@ -823,7 +826,7 @@ fail1:      pr_op_warn("rsync will not be available.");
  * will be automatically called.
  */
 int
-rsync_queue(char const *url, char const *path)
+rsync_queue(struct uri const *url, char const *path)
 {
        struct RsyncRequest req;
        asn_enc_rval_t result;
index 2d85f14708846043009abf35a7da17e4c1e97f2d..5d451ca180a8f26ecfa75bfe77dcb52b1e128c99 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef SRC_RSYNC_RSYNC_H_
 #define SRC_RSYNC_RSYNC_H_
 
+#include "types/url.h"
+
 void rsync_setup(char const *, ...);
-int rsync_queue(char const *, char const *);
-void rsync_finished(char const *, char const *);
+int rsync_queue(struct uri const *, char const *);
+void rsync_finished(struct uri const *, char const *);
 void rsync_teardown(void);
 
 #endif /* SRC_RSYNC_RSYNC_H_ */
index b836f6c0e77c783428481a9d309be486ecfac7c5..db4d65493b6c44b5d08e731290d6e656ac7b6eed 100644 (file)
@@ -143,7 +143,7 @@ task_enqueue_rpp(struct cache_mapping *map, struct rpki_certificate *parent)
        atomic_fetch_add(&parent->refcount, 1);
 
        ca = pzalloc(sizeof(struct rpki_certificate));
-       ca->map.url = pstrdup(map->url);
+       uri_copy(&ca->map.url, &map->url);
        ca->map.path = pstrdup(map->path);
        ca->parent = parent;
        atomic_init(&ca->refcount, 1);
@@ -231,7 +231,7 @@ task_dequeue(struct validation_task *prev)
                        STAILQ_REMOVE_HEAD(&waiting, lh);
                        mutex_unlock(&lock);
                        pr_op_debug("task_dequeue(): Claimed task '%s'.",
-                           task->u.ca->map.url);
+                           uri_str(&task->u.ca->map.url));
                        return task;
                }
 
index 4eddaa56ff0d52d0125ae8c9cd8e52340c94d986..1d3b642a77b48c35b0ff266ef934fd847ae4574a 100644 (file)
@@ -10,11 +10,11 @@ map_get_printable(struct cache_mapping const *map, enum filename_format format)
 {
        switch (format) {
        case FNF_GLOBAL:
-               return map->url;
+               return uri_str(&map->url);
        case FNF_LOCAL:
                return map->path;
        case FNF_NAME:
-               return path_filename(map->url);
+               return path_filename(uri_str(&map->url));
        }
 
        pr_crit("Unknown file name format: %u", format);
@@ -36,13 +36,13 @@ map_op_get_printable(struct cache_mapping const *map)
 void
 map_copy(struct cache_mapping *dst, struct cache_mapping const *src)
 {
-       dst->url = pstrdup(src->url);
+       uri_copy(&dst->url, &src->url);
        dst->path = pstrdup(src->path);
 }
 
 void
 map_cleanup(struct cache_mapping *map)
 {
-       free(map->url);
+       uri_cleanup(&map->url);
        free(map->path);
 }
index c7d9a4e61f96ac6b84c8712f0e2fc307b5bef880..7d0c06a21557a040e452b2c56dd3a6af832f754f 100644 (file)
@@ -1,12 +1,12 @@
 #ifndef SRC_TYPES_MAP_H_
 #define SRC_TYPES_MAP_H_
 
+#include "types/url.h"
+
 // XXX document this better
 struct cache_mapping {
-       /* Normalized, ASCII-only, NULL-terminated. */
-       char *url;
-       /* Normalized, ASCII-only, NULL-terminated. */
-       char *path;
+       struct uri url;         /* Normalized */
+       char *path;             /* Normalized */
 };
 
 char const *map_val_get_printable(struct cache_mapping const *);
index cbc9a17486dad7783d3a7f02bbd242cffe2bd801..671170be4de183328bf07afa1ef8ad9f37c6db07 100644 (file)
@@ -1,8 +1,11 @@
 #include "types/str.h"
 
 #include <errno.h>
+#include <string.h>
 #include <openssl/bio.h>
 
+#include "alloc.h"
+#include "array.h"
 #include "log.h"
 #include "types/path.h"
 
@@ -58,10 +61,28 @@ string_clone(void const *string, size_t size)
 int
 ia5s2string(ASN1_IA5STRING *ia5, char **result)
 {
-       if (ia5->flags & ASN1_STRING_FLAG_BITS_LEFT)
-               return pr_val_err("CRL URI IA5String has unused bits.");
+       unsigned char const *data;
+       size_t len;
+       array_index i;
+
+       /* Implementation-aware */
+       if (ia5->flags & ASN1_STRING_FLAG_BITS_LEFT) {
+               pr_val_warn("CRL URI IA5String has unused bits.");
+               return EINVAL;
+       }
 
-       *result = string_clone(ia5->data, ia5->length);
+       /* TODO (asn1c) This might already be done by the asn1 code. */
+       data = ASN1_STRING_get0_data(ia5);
+       len = ASN1_STRING_length(ia5);
+       for (i = 0; i < len; i++)
+               if (data[i] == 0) {
+                       pr_val_warn("Null character found in IA5String index %zu. (Length: %zu)",
+                           i, len);
+                       return EINVAL;
+               }
+
+       /* No NULL termination guarantee */
+       *result = pstrndup((char const *)data, len);
        return 0;
 }
 
@@ -172,39 +193,3 @@ token_count(struct string_tokenizer *tokenizer)
 
        return count;
 }
-
-void
-strlist_init(struct strlist *list)
-{
-       list->array = NULL;
-       list->len = 0;
-       list->capacity = 0;
-}
-
-void
-strlist_add(struct strlist *list, char *str)
-{
-       if (list->array == NULL) {
-               list->capacity = 8;
-               list->array = pmalloc(list->capacity * sizeof(char *));
-       }
-
-       list->len++;
-       while (list->len >= list->capacity) {
-               list->capacity *= 2;
-               list->array = prealloc(list->array,
-                   list->capacity * sizeof(char *));
-       }
-
-       list->array[list->len - 1] = str;
-}
-
-/* Call strlist_init() again if you want to reuse the list. */
-void
-strlist_cleanup(struct strlist *list)
-{
-       array_index i;
-       for (i = 0; i < list->len; i++)
-               free(list->array[i]);
-       free(list->array);
-}
index 7f6ebc46f30d32daef83eee7892d20cac26e8792..6963ea46605a48bb751f7541a2fcdb5954c0b65e 100644 (file)
@@ -5,8 +5,6 @@
 #include <openssl/bn.h>
 #include <stdbool.h>
 
-#include "types/arraylist.h"
-
 char *str_concat(char const *, char const *);
 
 int hex2ulong(char const *, unsigned long *);
@@ -39,12 +37,4 @@ bool token_equals(struct string_tokenizer *, struct string_tokenizer *);
 char *token_read(struct string_tokenizer *);
 size_t token_count(struct string_tokenizer *);
 
-/* Plural */
-
-DEFINE_ARRAY_LIST_STRUCT(strlist, char *);
-
-void strlist_init(struct strlist *);
-void strlist_cleanup(struct strlist *);
-void strlist_add(struct strlist *, char *);
-
 #endif /* SRC_TYPES_STR_H_ */
index 24ad0a418f726fdd2a9bc2b77d710494ce8f624b..67ccc8863edc29ed16d48453967fed251518585b 100644 (file)
@@ -1,6 +1,7 @@
 #include "types/url.h"
 
 #include <curl/curl.h>
+#include <errno.h>
 
 #include "alloc.h"
 #include "common.h"
@@ -8,15 +9,15 @@
 #include "types/path.h"
 
 bool
-url_is_rsync(char const *url)
+uri_is_rsync(struct uri const *url)
 {
-       return str_starts_with(url, "rsync://");
+       return str_starts_with(url->_str, "rsync://");
 }
 
 bool
-url_is_https(char const *url)
+uri_is_https(struct uri const *url)
 {
-       return str_starts_with(url, "https://");
+       return str_starts_with(url->_str, "https://");
 }
 
 /*
@@ -55,11 +56,11 @@ validate_url_characters(char const *str)
  * whose implementation is somewhat flawed (at least until version 8.12.1):
  * https://github.com/curl/curl/issues/16829
  *
- * On the other hand, since Fort 2 no longer maps URI paths to literal local
- * paths, all normalization does for us is prevent some theoretical redundant
- * downloading, so it might not even be that necessary.
+ * That said, since Fort 2 no longer maps URI paths to literal local paths, all
+ * normalization does for us is prevent some theoretical redundant downloading,
+ * so it's fine.
  */
-char *
+static char *
 url_normalize(char const *url)
 {
        CURLU *curlu;
@@ -99,21 +100,80 @@ einval:    pr_val_err("Error parsing URL: %s", curl_url_strerror(err));
        return NULL;
 }
 
-char *
-url_parent(char const *child)
+int
+uri_init(struct uri *url, char const *str)
 {
-       char *slash = strrchr(child, '/');
-       return (slash != NULL) ? pstrndup(child, slash - child) : NULL;
+       str = url_normalize(str);
+       if (!str)
+               return EINVAL;
+
+       __URI_INIT(url, str);
+       return 0;
+}
+
+/* @str must already be normalized. */
+void
+__uri_init(struct uri *url, char const *str, size_t len)
+{
+       url->_str = (char *)str;
+       url->_len = len;
+}
+
+void
+uri_copy(struct uri *dst, struct uri const *src)
+{
+       dst->_str = src->_str ? pstrdup(src->_str) : NULL;
+       dst->_len = src->_len;
+}
+
+void
+uri_cleanup(struct uri *url)
+{
+       free(url->_str);
+       url->_str = NULL;
+}
+
+bool
+uri_equals(struct uri const *u1, struct uri const *u2)
+{
+       return (u1->_len == u2->_len)
+           ? (memcmp(u1->_str, u2->_str, u1->_len) == 0)
+           : false;
+}
+
+bool
+uri_has_extension(struct uri const *url, char const *ext)
+{
+       return strcmp(url->_str + url->_len - strlen(ext), ext) == 0;
+}
+
+/* Result is a shallow copy; do not clean. */
+int
+uri_parent(struct uri const *child, struct uri *parent)
+{
+       char *slash;
+
+       slash = strrchr(child->_str, '/');
+       if (slash == NULL)
+               return EINVAL;
+
+       parent->_str = child->_str;
+       parent->_len = slash - child->_str;
+       return 0;
 }
 
 bool
-url_same_origin(char const *url1, char const *url2)
+uri_same_origin(struct uri const *uri1, struct uri const *uri2)
 {
+       char const *str1, *str2;
        size_t c, slashes;
 
+       str1 = uri1->_str;
+       str2 = uri2->_str;
        slashes = 0;
-       for (c = 0; url1[c] == url2[c]; c++) {
-               switch (url1[c]) {
+
+       for (c = 0; str1[c] == str2[c]; c++) {
+               switch (str1[c]) {
                case '/':
                        slashes++;
                        if (slashes == 3)
@@ -124,10 +184,29 @@ url_same_origin(char const *url1, char const *url2)
                }
        }
 
-       if (url1[c] == '\0')
-               return (slashes == 2) && url2[c] == '/';
-       if (url2[c] == '\0')
-               return (slashes == 2) && url1[c] == '/';
+       if (str1[c] == '\0')
+               return (slashes == 2) && str2[c] == '/';
+       if (str2[c] == '\0')
+               return (slashes == 2) && str1[c] == '/';
 
        return false;
 }
+
+void
+uri_child(struct uri const *parent, char const *name, size_t len,
+    struct uri *child)
+{
+       size_t slash;
+
+       slash = parent->_str[parent->_len - 1] != '/';
+
+       child->_len = parent->_len + slash + len;
+       child->_str = pmalloc(child->_len + 1);
+       strncpy(child->_str, parent->_str, parent->_len);
+       if (slash)
+               child->_str[parent->_len] = '/';
+       strncpy(child->_str + parent->_len + slash, name, len);
+       child->_str[child->_len] = '\0';
+}
+
+DEFINE_ARRAY_LIST_FUNCTIONS(uris, struct uri, )
index 86c93feb70860510443753f1b3183f4f1e5965e0..f4f761a71419a815f9421b9607ef692c368b120b 100644 (file)
@@ -2,14 +2,40 @@
 #define SRC_TYPES_URL_H_
 
 #include <stdbool.h>
+#include <stddef.h>
+
+#include "types/arraylist.h"
 
 #define RPKI_SCHEMA_LEN 8 /* strlen("rsync://"), strlen("https://") */
 
-bool url_is_rsync(char const *);
-bool url_is_https(char const *);
+struct uri {
+       char *_str;
+       size_t _len;
+};
+
+int uri_init(struct uri *, char const *);
+void __uri_init(struct uri *, char const *, size_t);
+#define __URI_INIT(uri, str) __uri_init(uri, str, strlen(str))
+void uri_copy(struct uri *, struct uri const *);
+void uri_cleanup(struct uri *);
+
+#define uri_str(u) ((char const *)((u)->_str))
+#define uri_len(u) ((u)->_len)
+
+bool uri_is_rsync(struct uri const *);
+bool uri_is_https(struct uri const *);
+
+bool uri_equals(struct uri const *, struct uri const *);
+bool uri_has_extension(struct uri const *, char const *);
+bool uri_same_origin(struct uri const *, struct uri const *);
+
+int uri_parent(struct uri const *, struct uri *);
+void uri_child(struct uri const *, char const *, size_t, struct uri *);
+#define URI_CHILD(uri, name, child) uri_child(uri, name, strlen(name), child)
+
+/* Plural */
 
-char *url_normalize(char const *);
-char *url_parent(char const *);
-bool url_same_origin(char const *, char const *);
+DEFINE_ARRAY_LIST_STRUCT(uris, struct uri);
+DECLARE_ARRAY_LIST_FUNCTIONS(uris, struct uri)
 
 #endif /* SRC_TYPES_URL_H_ */
index 416d13eb0995aba1cd6dd8293b19153fc8c0e4ae..0b44a83e2ab705f375eb12e7393cf255d178d8f3 100644 (file)
@@ -45,6 +45,7 @@ base64_test_LDADD =           ${CHECK_LIBS}
 check_PROGRAMS +=              cache.test
 cache_test_SOURCES =           cache_test.c
 cache_test_LDADD =             ${CHECK_LIBS}
+cache_test_LDADD +=            ${CURL_LIBS}
 cache_test_LDADD +=            ${XML2_LIBS}
 cache_test_LDADD +=            ${JANSSON_LIBS}
 
@@ -79,18 +80,21 @@ pdu_stream_test_LDADD =             ${CHECK_LIBS}
 check_PROGRAMS +=              rrdp.test
 rrdp_test_SOURCES =            rrdp_test.c
 rrdp_test_LDADD =              ${CHECK_LIBS}
+rrdp_test_LDADD +=             ${CURL_LIBS}
 rrdp_test_LDADD +=             ${XML2_LIBS}
 rrdp_test_LDADD +=             ${JANSSON_LIBS}
 
 check_PROGRAMS +=              rrdp_update.test
 rrdp_update_test_SOURCES =     rrdp_update_test.c
 rrdp_update_test_LDADD =       ${CHECK_LIBS}
+rrdp_update_test_LDADD +=      ${CURL_LIBS}
 rrdp_update_test_LDADD +=      ${XML2_LIBS}
 rrdp_update_test_LDADD +=      ${JANSSON_LIBS}
 
 check_PROGRAMS +=              rsync.test
 rsync_test_SOURCES =           rsync_test.c
 rsync_test_LDADD =             ${CHECK_LIBS}
+rsync_test_LDADD +=            ${CURL_LIBS}
 
 check_PROGRAMS +=              serial.test
 serial_test_SOURCES =          types/serial_test.c
@@ -103,6 +107,7 @@ serial_test_LDADD =         ${CHECK_LIBS}
 check_PROGRAMS +=              tal.test
 tal_test_SOURCES =             object/tal_test.c
 tal_test_LDADD =               ${CHECK_LIBS}
+tal_test_LDADD +=              ${CURL_LIBS}
 
 check_PROGRAMS +=              task.test
 task_test_SOURCES =            task_test.c
index 1634624dd2110d62ba2b6a96142ecb4cc29d8332..feb0f1d9fe280a0ec9ecd82a128cd92f3a188d0f 100644 (file)
@@ -48,11 +48,11 @@ touch_file(char const *dir)
        ck_assert_int_eq(0, system(cmd));
 }
 
-static char *queued_url;
+static struct uri queued_url;
 static char *queued_path;
 
 int
-rsync_queue(char const *url, char const *path)
+rsync_queue(struct uri const *url, char const *path)
 {
        rsync_counter++;
 
@@ -61,11 +61,11 @@ rsync_queue(char const *url, char const *path)
                return dl_error;
        }
 
-       printf("Simulating rsync: %s -> %s\n", url, path);
+       printf("Simulating rsync: %s -> %s\n", uri_str(url), path);
        ck_assert_int_eq(0, mkdir(path, CACHE_FILEMODE));
        touch_file(path);
 
-       queued_url = pstrdup(url);
+       uri_copy(&queued_url, url);
        queued_path = pstrdup(path);
 
        return 0;
@@ -108,8 +108,11 @@ setup_test(void)
 static struct cache_cage *
 run_dl_rsync(char *caRepository, int expected_err, unsigned int expected_calls)
 {
-       struct sia_uris sias = { .caRepository = caRepository };
-       struct cache_cage *cage = NULL;
+       struct sia_uris sias = { 0 };
+       struct cache_cage *cage;
+
+       ck_assert_int_eq(0, uri_init(&sias.caRepository, caRepository));
+       cage = NULL;
 
        rsync_counter = 0;
        https_counter = 0;
@@ -119,14 +122,15 @@ run_dl_rsync(char *caRepository, int expected_err, unsigned int expected_calls)
        ck_assert_uint_eq(expected_calls, rsync_counter);
        ck_assert_uint_eq(0, https_counter);
 
+       uri_cleanup(&sias.caRepository);
        return cage;
 }
 
 static void
 finish_rsync(void)
 {
-       rsync_finished(queued_url, queued_path);
-       free(queued_url);
+       rsync_finished(&queued_url, queued_path);
+       uri_cleanup(&queued_url);
        free(queued_path);
 }
 
@@ -142,18 +146,23 @@ static void
 run_dl_https(char const *url, unsigned int expected_calls,
     char const *expected_result)
 {
+       struct uri uri;
        char const *result;
 
+       ck_assert_int_eq(0, uri_init(&uri, url));
+
        rsync_counter = 0;
        https_counter = 0;
        printf("---- Downloading... ----\n");
-       result = cache_refresh_by_url(url);
+       result = cache_refresh_by_url(&uri);
        printf("---- Downloaded. ----\n");
        ck_assert_uint_eq(0, rsync_counter);
        ck_assert_uint_eq(expected_calls, https_counter);
 
        ck_assert_str(expected_result, result);
-       ck_assert_str(NULL, cache_get_fallback(url));
+       ck_assert_str(NULL, cache_get_fallback(&uri));
+
+       uri_cleanup(&uri);
 }
 
 
@@ -161,16 +170,19 @@ static void
 ck_cage(struct cache_cage *cage, char const *url,
     char const *refresh, char const *fallback)
 {
+       struct uri uri;
        struct cache_node const *bkp;
 
-       ck_assert_str(refresh, cage_map_file(cage, url));
+       ck_assert_int_eq(0, uri_init(&uri, url));
+
+       ck_assert_str(refresh, cage_map_file(cage, &uri));
 
        bkp = cage->refresh;
        cage_disable_refresh(cage);
-
-       ck_assert_str(fallback, cage_map_file(cage, url));
-
+       ck_assert_str(fallback, cage_map_file(cage, &uri));
        cage->refresh = bkp;
+
+       uri_cleanup(&uri);
 }
 
 static int
@@ -198,15 +210,28 @@ queue_commit(char const *rpkiNotify, char const *caRepository,
     char const *path1, char const *path2)
 {
        struct rpp rpp = { 0 };
+       struct uri rn, cr;
+
+       if (rpkiNotify)
+               ck_assert_int_eq(0, uri_init(&rn, rpkiNotify));
+       else
+               memset(&rn, 0, sizeof(rn));
+       if (caRepository)
+               ck_assert_int_eq(0, uri_init(&cr, caRepository));
+       else
+               memset(&cr, 0, sizeof(cr));
 
        rpp.nfiles = 2;
        rpp.files = pzalloc(rpp.nfiles * sizeof(struct cache_mapping));
-       rpp.files[0].url = path_join(caRepository, "manifest.mft");
+       URI_CHILD(&cr, "manifest.mft", &rpp.files[0].url);
        rpp.files[0].path = pstrdup(path1);
-       rpp.files[1].url = path_join(caRepository, "cert.cer");
+       URI_CHILD(&cr, "cert.cer", &rpp.files[1].url);
        rpp.files[1].path = pstrdup(path2);
 
-       cache_commit_rpp(rpkiNotify, caRepository, &rpp);
+       cache_commit_rpp(&rn, &cr, &rpp);
+
+       uri_cleanup(&cr);
+       uri_cleanup(&rn);
 }
 
 /* Only validates the first character of the file. */
@@ -277,7 +302,7 @@ init_node_rsync(struct cache_node *node, char *url, char *path,
 {
        node->key.id = url;
        node->key.idlen = strlen(url);
-       node->key.rsync = url;
+       ck_assert_int_eq(0, uri_init(&node->key.rsync, url));
        node->path = path;
        node->state = fresh ? DLS_FRESH : DLS_OUTDATED; /* XXX (test) */
        node->dlerr = dlerr;
@@ -290,7 +315,7 @@ init_node_https(struct cache_node *node, char *url, char *path,
 {
        node->key.id = url;
        node->key.idlen = strlen(url);
-       node->key.http = url;
+       ck_assert_int_eq(0, uri_init(&node->key.http, url));
        node->path = path;
        node->state = fresh ? DLS_FRESH : DLS_OUTDATED;
        node->dlerr = dlerr;
@@ -300,14 +325,8 @@ init_node_https(struct cache_node *node, char *url, char *path,
 static void
 ck_node_key(struct node_key *expected, struct node_key *actual)
 {
-       if (expected->http)
-               ck_assert_str_eq(expected->http, actual->http);
-       else
-               ck_assert_ptr_eq(NULL, actual->http);
-       if (expected->rsync)
-               ck_assert_str_eq(expected->rsync, actual->rsync);
-       else
-               ck_assert_ptr_eq(NULL, actual->rsync);
+       ck_assert(uri_equals(&expected->http, &actual->http));
+       ck_assert(uri_equals(&expected->rsync, &actual->rsync));
        ck_assert_uint_eq(expected->idlen, actual->idlen);
        ck_assert_mem_eq(expected->id, actual->id, expected->idlen);
 }
@@ -663,12 +682,16 @@ START_TEST(test_https_cleanup)
        }
 
        /* 3 */
-       map.url = "https://domain/rpki/ta50.cer";
-       map.path = "https/50";
+       ck_assert_int_eq(0, uri_init(&map.url, "https://domain/rpki/ta50.cer"));
+       map.path = pstrdup("https/50");
        cache_commit_file(&map);
-       map.url = "https://domain/rpki/ta52.cer";
-       map.path = "https/52";
+       map_cleanup(&map);
+
+       ck_assert_int_eq(0, uri_init(&map.url, "https://domain/rpki/ta52.cer"));
+       map.path = pstrdup("https/52");
        cache_commit_file(&map);
+       map_cleanup(&map);
+
        cleanup_cache();
        ck_filesystem("fallback",
            "fallback/0", "A", "fallback/0.json", "{",
@@ -678,12 +701,16 @@ START_TEST(test_https_cleanup)
        new_iteration(false);
 
        /* 4 */
-       map.url = "https://domain/rpki/ta50.cer";
-       map.path = "fallback/0";
+       ck_assert_int_eq(0, uri_init(&map.url, "https://domain/rpki/ta50.cer"));
+       map.path = pstrdup("fallback/0");
        cache_commit_file(&map);
-       map.url = "https://domain/rpki/ta51.cer";
-       map.path = "https/51";
+       map_cleanup(&map);
+
+       ck_assert_int_eq(0, uri_init(&map.url, "https://domain/rpki/ta51.cer"));
+       map.path = pstrdup("https/51");
        cache_commit_file(&map);
+       map_cleanup(&map);
+
        cleanup_cache();
        ck_filesystem("fallback",
            "fallback/0", "A", "fallback/0.json", "{",
@@ -774,6 +801,7 @@ START_TEST(test_context)
        char *FILE_RSYNC_PATH = "rsync/0/rpp3/a.cer";
 
        struct sia_uris sias = { 0 };
+       struct uri file_url;
        struct cache_cage *cage;
        struct rpp rpp = { 0 };
 
@@ -788,58 +816,69 @@ START_TEST(test_context)
        dls[1] = SHDR("3") PBLSH("rsync://x.y.z/mod5/rpp3/a.cer", "Rm9ydAo=") STAIL;
        dls[2] = NULL;
 
-       /* 1. 1st CA succeeds on RRDP */
-       sias.rpkiNotify = RPKI_NOTIFY;
-       sias.caRepository = CA_REPOSITORY;
+       ck_assert_int_eq(0, uri_init(&file_url, FILE_URL));
+
+       printf("1. 1st CA succeeds on RRDP\n");
+       print_tree();
+       ck_assert_int_eq(0, uri_init(&sias.rpkiNotify, RPKI_NOTIFY));
+       ck_assert_int_eq(0, uri_init(&sias.caRepository, CA_REPOSITORY));
        ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
-       ck_assert_str_eq(RPKI_NOTIFY, cage->rpkiNotify);
-       ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_str_eq(RPKI_NOTIFY, uri_str(&cage->rpkiNotify));
+       ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, &file_url));
        ck_assert_int_eq(false, cage_disable_refresh(cage));
-       ck_assert_ptr_eq(NULL, cage_map_file(cage, FILE_URL));
+       ck_assert_ptr_eq(NULL, cage_map_file(cage, &file_url));
 
-       /*
-        * 2. 2nd CA points to the same caRepository,
-        *    but does not provide RRDP as an option.
-        */
-       sias.rpkiNotify = NULL;
+       printf("2. 2nd CA points to the same caRepository,\n");
+       printf("   but does not provide RRDP as an option.\n");
+       print_tree();
+       uri_cleanup(&sias.rpkiNotify);
 
        ck_assert_int_eq(EBUSY, cache_refresh_by_sias(&sias, &cage));
        finish_rsync();
        ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
 
-       ck_assert_ptr_eq(NULL, cage->rpkiNotify);
-       ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_ptr_eq(NULL, uri_str(&cage->rpkiNotify));
+       ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, &file_url));
        ck_assert_int_eq(false, cage_disable_refresh(cage));
-       ck_assert_ptr_eq(NULL, cage_map_file(cage, FILE_URL));
+       ck_assert_ptr_eq(NULL, cage_map_file(cage, &file_url));
+
+       printf("3. Commit\n");
 
-       /* 3. Commit */
+       print_tree();
        rpp.nfiles = 1;
        rpp.files = pzalloc(sizeof(struct cache_mapping));
-       rpp.files->url = pstrdup(FILE_URL);
+       uri_copy(&rpp.files->url, &file_url);
        rpp.files->path = pstrdup(FILE_RRDP_PATH);
-       cache_commit_rpp(RPKI_NOTIFY, CA_REPOSITORY, &rpp);
+       ck_assert_int_eq(0, uri_init(&sias.rpkiNotify, RPKI_NOTIFY));
+       cache_commit_rpp(&sias.rpkiNotify, &sias.caRepository, &rpp);
 
        rpp.nfiles = 1;
        rpp.files = pzalloc(sizeof(struct cache_mapping));
-       rpp.files->url = pstrdup(FILE_URL);
+       uri_copy(&rpp.files->url, &file_url);
        rpp.files->path = pstrdup(FILE_RSYNC_PATH);
-       cache_commit_rpp(NULL, CA_REPOSITORY, &rpp);
+       uri_cleanup(&sias.rpkiNotify);
+       cache_commit_rpp(&sias.rpkiNotify, &sias.caRepository, &rpp);
 
        commit_fallbacks(time_fatal());
 
-       /* 4. Redo both CAs, check the fallbacks too */
+       printf("4. Redo both CAs, check the fallbacks too\n");
+       print_tree();
        ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
-       ck_assert_ptr_eq(NULL, cage->rpkiNotify);
-       ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_ptr_eq(NULL, uri_str(&cage->rpkiNotify));
+       ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, &file_url));
        ck_assert_int_eq(true, cage_disable_refresh(cage));
-       ck_assert_str_eq("fallback/1/0", cage_map_file(cage, FILE_URL));
+       ck_assert_str_eq("fallback/1/0", cage_map_file(cage, &file_url));
 
-       sias.rpkiNotify = RPKI_NOTIFY;
+       ck_assert_int_eq(0, uri_init(&sias.rpkiNotify, RPKI_NOTIFY));
        ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
-       ck_assert_str_eq(RPKI_NOTIFY, cage->rpkiNotify);
-       ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_str_eq(RPKI_NOTIFY, uri_str(&cage->rpkiNotify));
+       ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, &file_url));
        ck_assert_int_eq(true, cage_disable_refresh(cage));
-       ck_assert_str_eq("fallback/0/0", cage_map_file(cage, FILE_URL));
+       ck_assert_str_eq("fallback/0/0", cage_map_file(cage, &file_url));
+
+       uri_cleanup(&sias.rpkiNotify);
+       uri_cleanup(&sias.caRepository);
+       uri_cleanup(&file_url);
 
        cleanup_test();
        relax_ng_cleanup();
@@ -865,9 +904,9 @@ ck_rrdp(struct rrdp_state *expected, struct rrdp_state *actual)
 
        ck_assert_int_eq(HASH_COUNT(expected->files), HASH_COUNT(actual->files));
        HASH_ITER(hh, expected->files, expf, tmp) {
-               HASH_FIND(hh, actual->files, expf->map.url, strlen(expf->map.url), actf);
+               HASH_FIND(hh, actual->files, uri_str(&expf->map.url), uri_len(&expf->map.url), actf);
                ck_assert_ptr_ne(NULL, actf);
-               ck_assert_str_eq(expf->map.url, actf->map.url);
+               ck_assert(uri_equals(&expf->map.url, &actf->map.url));
                ck_assert_str_eq(expf->map.path, actf->map.path);
        }
 
@@ -913,7 +952,7 @@ START_TEST(test_json_min)
 
        node->key.id = pstrdup("https://a.b.c/sample.cer");
        node->key.idlen = strlen(node->key.id);
-       node->key.http = node->key.id;
+       __uri_init(&node->key.http, node->key.id, node->key.idlen);
        node->path = pstrdup("tmp/sample.cer");
        node->state = DLS_FRESH;
        node->dlerr = ENOENT;
@@ -921,13 +960,23 @@ START_TEST(test_json_min)
        ck_json(node);
 }
 
+static void
+CACHE_FILE_ADD(struct rrdp_state *state, char const *url, char *path)
+{
+       struct uri uri;
+
+       ck_assert_int_eq(0, uri_init(&uri, url));
+       ck_assert_ptr_ne(NULL, cache_file_add(state, &uri, pstrdup(path)));
+       uri_cleanup(&uri);
+}
+
 START_TEST(test_json_rrdp_min)
 {
        struct cache_node *node = pzalloc(sizeof(struct cache_node));
 
        node->key.id = pstrdup("https://a.b.c/sample.cer");
        node->key.idlen = strlen(node->key.id);
-       node->key.http = node->key.id;
+       __uri_init(&node->key.http, node->key.id, node->key.idlen);
        node->path = pstrdup("rrdp/123");
        node->state = DLS_FRESH;
        node->dlerr = ENOENT;
@@ -938,7 +987,7 @@ START_TEST(test_json_rrdp_min)
        ck_assert_ptr_ne(NULL, node->rrdp->session.serial.num);
        BN_add_word(node->rrdp->session.serial.num, 1357);
        node->rrdp->session.serial.str = pstrdup("1357");
-       cache_file_add(node->rrdp, pstrdup("rsync://a.b.c/d/e.mft"), pstrdup("rrdp/123/0"));
+       CACHE_FILE_ADD(node->rrdp, "rsync://a.b.c/d/e.mft", "rrdp/123/0");
        cseq_init(&node->rrdp->seq, node->path, 1, false);
        STAILQ_INIT(&node->rrdp->delta_hashes);
 
@@ -952,7 +1001,7 @@ START_TEST(test_json_max)
 
        node->key.id = pstrdup("https://a.b.c/sample.cer");
        node->key.idlen = strlen(node->key.id);
-       node->key.http = node->key.id;
+       __uri_init(&node->key.http, node->key.id, node->key.idlen);
        node->path = pstrdup("rrdp/123");
        node->state = DLS_FRESH;
        node->dlerr = ENOENT;
@@ -967,8 +1016,8 @@ START_TEST(test_json_max)
        ck_assert_ptr_ne(NULL, node->rrdp->session.serial.num);
        BN_add_word(node->rrdp->session.serial.num, 1357);
        node->rrdp->session.serial.str = pstrdup("1357");
-       cache_file_add(node->rrdp, pstrdup("rsync://a.b.c/d/e.mft"), pstrdup("rrdp/123/0"));
-       cache_file_add(node->rrdp, pstrdup("rsync://a.b.c/d/f.crl"), pstrdup("rrdp/123/1"));
+       CACHE_FILE_ADD(node->rrdp, "rsync://a.b.c/d/e.mft", "rrdp/123/0");
+       CACHE_FILE_ADD(node->rrdp, "rsync://a.b.c/d/f.crl", "rrdp/123/1");
        cseq_init(&node->rrdp->seq, node->path, 2, false);
        STAILQ_INIT(&node->rrdp->delta_hashes);
        hash = pmalloc(sizeof(struct rrdp_hash));
@@ -985,15 +1034,20 @@ START_TEST(test_json_weirdurl)
 {
        static char const *NOTIF = "https://a.b.c/notif.xml";
        static char const *CAREPO = "rsync://a.b.c/rpp";
+       size_t nlen;
+       size_t clen;
+       struct cache_node *node;
 
-       struct cache_node *node = pzalloc(sizeof(struct cache_node));
+       nlen = strlen(NOTIF);
+       clen = strlen(CAREPO);
+       node = pzalloc(sizeof(struct cache_node));
 
-       node->key.idlen = strlen(NOTIF) + strlen(CAREPO) + 1;
+       node->key.idlen = nlen + clen + 1;
        node->key.id = pmalloc(node->key.idlen + 1);
        strcpy(node->key.id, NOTIF);
-       strcpy(node->key.id + strlen(NOTIF) + 1, CAREPO);
-       node->key.http = node->key.id;
-       node->key.rsync = node->key.id + strlen(NOTIF) + 1;
+       strcpy(node->key.id + nlen + 1, CAREPO);
+       __uri_init(&node->key.http, node->key.id, nlen);
+       __uri_init(&node->key.rsync, node->key.id + nlen + 1, clen);
        node->path = pstrdup("tmp/sample.cer");
        node->state = DLS_FRESH;
        node->dlerr = ENOENT;
index f79e3199b53c5d8e71a1b4cc1b1a0d866b1b9c0e..b16de80439d73c995d3c840f8b64db5e237c4cf2 100644 (file)
@@ -148,3 +148,10 @@ ck_assert_str(char const *expected, char const *actual)
        else
                ck_assert_ptr_eq(NULL, actual);
 }
+
+void
+ck_assert_uri(char const *expected, struct uri const *actual)
+{
+       ck_assert_str_eq(expected, uri_str(actual));
+       ck_assert_uint_eq(strlen(expected), uri_len(actual));
+}
index e1d4e210c9f7a9a2e31e244cdbf95d8e02e057f6..4f783265dab5a43b2338c088336388f1207ee1dc 100644 (file)
@@ -8,7 +8,8 @@ static char const *dls[8];
 static unsigned int https_counter; /* Times http_download() was called */
 
 int
-http_download(char const *url, char const *path, curl_off_t ims, bool *changed)
+http_download(struct uri const *url, char const *path,
+    curl_off_t ims, bool *changed)
 {
        char const *content;
 
@@ -20,7 +21,7 @@ http_download(char const *url, char const *path, curl_off_t ims, bool *changed)
                return dl_error;
        }
 
-       printf("Simulating HTTP download: %s -> %s\n", url, path);
+       printf("Simulating HTTP download: %s -> %s\n", uri_str(url), path);
 
        content = dls[https_counter++];
        if (!content)
index eca8ab8cc141c1e8b2b4d29c99d2a6fff886d2a9..20135aca8a0faa3def961ac43cbad18ed924e0ec 100644 (file)
@@ -59,7 +59,7 @@ test_1url(char const *file)
        ck_assert_int_eq(0, tal_init(&tal, file));
 
        ck_assert_uint_eq(1, tal.urls.len);
-       ck_assert_str_eq("rsync://example.com/rpki/ta.cer", tal.urls.array[0]);
+       ck_assert_uri("rsync://example.com/rpki/ta.cer", &tal.urls.array[0]);
        check_spki(&tal);
 
        tal_cleanup(&tal);
@@ -80,10 +80,10 @@ test_4urls(char const *file)
        ck_assert_int_eq(0, tal_init(&tal, file));
 
        ck_assert_uint_eq(4, tal.urls.len);
-       ck_assert_str_eq("rsync://example.com/rpki/ta.cer", tal.urls.array[0]);
-       ck_assert_str_eq("https://example.com/rpki/ta.cer", tal.urls.array[1]);
-       ck_assert_str_eq("rsync://www.example.com/potato/ta.cer", tal.urls.array[2]);
-       ck_assert_str_eq("https://wx3.example.com/tomato/ta.cer", tal.urls.array[3]);
+       ck_assert_uri("rsync://example.com/rpki/ta.cer", &tal.urls.array[0]);
+       ck_assert_uri("https://example.com/rpki/ta.cer", &tal.urls.array[1]);
+       ck_assert_uri("rsync://www.example.com/potato/ta.cer", &tal.urls.array[2]);
+       ck_assert_uri("https://wx3.example.com/tomato/ta.cer", &tal.urls.array[3]);
 
        check_spki(&tal);
 
index 445354b08e4560746ea3ae88b61241bffb553479..90c8ed4de6284f3a124efd6619c1d83311dcf5e2 100644 (file)
@@ -288,7 +288,7 @@ init_rrdp_state(struct rrdp_state **result,
        struct rrdp_hash *hash;
        size_t i;
 
-       notif = pmalloc(sizeof(struct rrdp_state));
+       notif = pzalloc(sizeof(struct rrdp_state));
        *result = notif;
 
        init_rrdp_session(&notif->session, serial);
@@ -319,7 +319,7 @@ init_regular_notif(struct update_notification *notif, unsigned long serial, ...)
        va_start(args, serial);
        while ((hash_byte = va_arg(args, int)) >= 0) {
                init_serial(&delta.serial, serial--);
-               delta.meta.uri = NULL; /* Not needed for now */
+               memset(&delta.meta.uri, 0, sizeof(delta.meta.uri)); /* Not needed for now */
                delta.meta.hash = pmalloc(RRDP_HASH_LEN);
                for (i = 0; i < RRDP_HASH_LEN; i++)
                        delta.meta.hash[i] = hash_byte;
@@ -400,30 +400,33 @@ END_TEST
 START_TEST(test_parse_notification_ok)
 {
        struct update_notification notif;
+       struct uri nurl;
 
        ck_assert_int_eq(0, relax_ng_init());
-       ck_assert_int_eq(0, parse_notification("https://host/notification.xml",
+       ck_assert_int_eq(0, uri_init(&nurl, "https://host/notification.xml"));
+       ck_assert_int_eq(0, parse_notification(&nurl,
            "resources/rrdp/notif-ok.xml", &notif));
+       uri_cleanup(&nurl);
 
        ck_assert_str_eq("9df4b597-af9e-4dca-bdda-719cce2c4e28",
            (char const *)notif.session.session_id);
        ck_assert_str_eq("3", (char const *)notif.session.serial.str);
 
-       ck_assert_str_eq("https://host/9d-8/3/snapshot.xml", notif.snapshot.uri);
+       ck_assert_uri("https://host/9d-8/3/snapshot.xml", &notif.snapshot.uri);
        ck_assert_uint_eq(32, notif.snapshot.hash_len);
        validate_aaaa_hash(notif.snapshot.hash);
 
        ck_assert_uint_eq(2, notif.deltas.len);
 
        ck_assert_str_eq("2", (char const *)notif.deltas.array[0].serial.str);
-       ck_assert_str_eq("https://host/9d-8/2/delta.xml",
-           notif.deltas.array[0].meta.uri);
+       ck_assert_uri("https://host/9d-8/2/delta.xml",
+           &notif.deltas.array[0].meta.uri);
        ck_assert_uint_eq(32, notif.deltas.array[0].meta.hash_len);
        validate_01234_hash(notif.deltas.array[0].meta.hash);
 
        ck_assert_str_eq("3", (char const *)notif.deltas.array[1].serial.str);
-       ck_assert_str_eq("https://host/9d-8/3/delta.xml",
-           notif.deltas.array[1].meta.uri);
+       ck_assert_uri("https://host/9d-8/3/delta.xml",
+           &notif.deltas.array[1].meta.uri);
        ck_assert_uint_eq(32, notif.deltas.array[1].meta.hash_len);
        validate_01234_hash(notif.deltas.array[0].meta.hash);
 
@@ -435,16 +438,19 @@ END_TEST
 START_TEST(test_parse_notification_0deltas)
 {
        struct update_notification notif;
+       struct uri nurl;
 
        ck_assert_int_eq(0, relax_ng_init());
-       ck_assert_int_eq(0, parse_notification("https://host/notification.xml",
+       ck_assert_int_eq(0, uri_init(&nurl, "https://host/notification.xml"));
+       ck_assert_int_eq(0, parse_notification(&nurl,
            "resources/rrdp/notif-0deltas.xml", &notif));
+       uri_cleanup(&nurl);
 
        ck_assert_str_eq("9df4b597-af9e-4dca-bdda-719cce2c4e28",
            (char const *)notif.session.session_id);
        ck_assert_str_eq("3", (char const *)notif.session.serial.str);
 
-       ck_assert_str_eq("https://host/9d-8/3/snapshot.xml", notif.snapshot.uri);
+       ck_assert_uri("https://host/9d-8/3/snapshot.xml", &notif.snapshot.uri);
        ck_assert_uint_eq(32, notif.snapshot.hash_len);
        validate_01234_hash(notif.snapshot.hash);
 
@@ -458,10 +464,13 @@ END_TEST
 START_TEST(test_parse_notification_large_serial)
 {
        struct update_notification notif;
+       struct uri nurl;
 
        ck_assert_int_eq(0, relax_ng_init());
-       ck_assert_int_eq(0, parse_notification("https://host/notification.xml",
+       ck_assert_int_eq(0, uri_init(&nurl, "https://host/notification.xml"));
+       ck_assert_int_eq(0, parse_notification(&nurl,
            "resources/rrdp/notif-large-serial.xml", &notif));
+       uri_cleanup(&nurl);
 
        ck_assert_str_eq("9df4b597-af9e-4dca-bdda-719cce2c4e28",
            (char const *)notif.session.session_id);
@@ -474,7 +483,7 @@ START_TEST(test_parse_notification_large_serial)
        ck_assert_str_eq("999999999999999999999999",
            (char const *)notif.session.serial.str);
 
-       ck_assert_str_eq("https://host/9d-8/3/snapshot.xml", notif.snapshot.uri);
+       ck_assert_uri("https://host/9d-8/3/snapshot.xml", &notif.snapshot.uri);
        ck_assert_uint_eq(32, notif.snapshot.hash_len);
        validate_01234_hash(notif.snapshot.hash);
 
@@ -489,10 +498,12 @@ static void
 test_parse_notification_error(char *file)
 {
        struct update_notification notif;
+       struct uri nurl;
 
        ck_assert_int_eq(0, relax_ng_init());
-       ck_assert_int_eq(-EINVAL,
-           parse_notification("https://host/notification.xml", file, &notif));
+       ck_assert_int_eq(0, uri_init(&nurl, "https://host/notification.xml"));
+       ck_assert_int_eq(-EINVAL, parse_notification(&nurl, file, &notif));
+       uri_cleanup(&nurl);
 
        relax_ng_cleanup();
 }
index b84903f9f4c954019f04a343542a7fcecda7fc51..3f5f58ebc0c9e1032f7e49aac8649de93e0cff1f 100644 (file)
@@ -60,13 +60,13 @@ ck_state(char const *session, char const *serial, unsigned long seq_id,
        ck_assert_str_eq(session, actual->session.session_id);
        ck_assert_str_eq(serial, actual->session.serial.str);
 
-       for (m = 0; maps[m].url != NULL; m++)
+       for (m = 0; uri_str(&maps[m].url) != NULL; m++)
                ;
        ck_assert_int_eq(m, HASH_COUNT(actual->files));
 
        m = 0;
        HASH_ITER(hh, actual->files, node, tmp) {
-               ck_assert_str_eq(maps[m].url, node->map.url);
+               ck_assert(uri_equals(&maps[m].url, &node->map.url));
                ck_assert_str_eq(maps[m].path, node->map.path);
                m++;
        }
@@ -79,6 +79,7 @@ START_TEST(startup)
        static char const *URL = "https://host/notification.xml";
        static char const *PATH = "rrdp/0";
        struct cache_sequence seq;
+       struct uri url;
        struct rrdp_state *state = NULL;
        struct cache_mapping maps[4];
        bool changed;
@@ -90,6 +91,8 @@ START_TEST(startup)
        seq.pathlen = strlen(seq.prefix);
        seq.free_prefix = false;
 
+       ck_assert_int_eq(0, uri_init(&url, URL));
+
        dls[0] = NHDR("3")
                NSS("https://host/9d-8/3/snapshot.xml",
                    "0c84fb949e7b5379ae091b86c41bb1a33cb91636b154b86ad1b1dedd44651a25")
@@ -98,20 +101,20 @@ START_TEST(startup)
        dls[2] = NULL;
        https_counter = 0;
 
-       ck_assert_int_eq(0, rrdp_update(URL, PATH, 0, &changed, &state));
+       ck_assert_int_eq(0, rrdp_update(&url, PATH, 0, &changed, &state));
        ck_assert_uint_eq(2, https_counter);
        ck_assert_uint_eq(true, changed);
        ck_file("rrdp/0/0"); /* "rrdp/<first-cage>/<c.cer>" */
 
-       maps[0].url = "rsync://a/b/c.cer";
+       ck_assert_int_eq(0, uri_init(&maps[0].url, "rsync://a/b/c.cer"));
        maps[0].path = "rrdp/0/0";
-       maps[1].url = NULL;
+       memset(&maps[1], 0, sizeof(maps[1]));
        ck_state(TEST_SESSION, "3", 1, maps, state);
 
        /* Attempt to update, server hasn't changed anything. */
        dls[1] = NULL; /* Snapshot should not redownload */
        https_counter = 0;
-       ck_assert_int_eq(0, rrdp_update(URL, PATH, 0, &changed, &state));
+       ck_assert_int_eq(0, rrdp_update(&url, PATH, 0, &changed, &state));
        ck_assert_uint_eq(1, https_counter);
        ck_assert_uint_eq(false, changed);
        ck_file("rrdp/0/0");
@@ -121,6 +124,7 @@ START_TEST(startup)
 
        // XXX Missing a looooooooooooooooooot of tests
 
+       uri_cleanup(&url);
        cleanup_test();
 }
 END_TEST
index 1de30423a79cbc355d7de464349a8aec460dc65f..92cc6e26c0a2c1779cf84c922276c7bba475ae8a 100644 (file)
@@ -6,6 +6,7 @@
 #include "rsync.c"
 #include "stream.c"
 #include "types/map.c"
+#include "types/url.c"
 
 #include "asn1/asn1c/ber_decoder.c"
 #include "asn1/asn1c/ber_tlv_length.c"
@@ -48,7 +49,7 @@ static int rsync_expected_duration = -1;
 static int rsyncs_done = 0;
 
 void
-rsync_finished(char const *url, char const *path)
+rsync_finished(struct uri const *url, char const *path)
 {
        struct timespec now;
        int delta;
@@ -74,14 +75,16 @@ rsync_finished(char const *url, char const *path)
 /* Test RsyncRequest decode, feeding as few bytes as possible every time. */
 START_TEST(test_decode_extremely_fragmented)
 {
+       struct uri uri;
        struct RsyncRequest src, *dst;
        unsigned char encoded[BUFSIZE];
        asn_enc_rval_t encres;
        asn_dec_rval_t decres;
        unsigned int start, end, max;
 
-       ck_assert_int_eq(0, RsyncRequest_init(&src,
-           "aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789",
+       __URI_INIT(&uri, "aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789");
+
+       ck_assert_int_eq(0, RsyncRequest_init(&src, &uri,
            "AbCdEfGhIjKlMnOpQrStUvWxYz1234567890"));
        encres = der_encode_to_buffer(&asn_DEF_RsyncRequest, &src,
            encoded, sizeof(encoded));
@@ -135,7 +138,7 @@ ck_next_task(char const *url, char const *path)
        struct cache_mapping map;
 
        ck_assert_int_eq(0, next_task(&map));
-       ck_assert_str_eq(url, map.url);
+       ck_assert_uri(url, &map.url);
        ck_assert_str_eq(path, map.path);
 
        map_cleanup(&map);
@@ -144,10 +147,13 @@ ck_next_task(char const *url, char const *path)
 static void
 encode_request(char const *url, char const *path, unsigned char *buffer)
 {
+       struct uri uri;
        struct RsyncRequest rr;
        asn_enc_rval_t encres;
 
-       ck_assert_int_eq(0, RsyncRequest_init(&rr, url, path));
+       __URI_INIT(&uri, url);
+
+       ck_assert_int_eq(0, RsyncRequest_init(&rr, &uri, path));
        encres = der_encode_to_buffer(&asn_DEF_RsyncRequest, &rr, buffer, BUFSIZE);
        ck_assert_int_gt(encres.encoded, 0);
        ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RsyncRequest, &rr);
@@ -228,6 +234,14 @@ START_TEST(test_next_task)
 }
 END_TEST
 
+static int
+RSYNC_QUEUE(char const *a, char const *b)
+{
+       struct uri uri;
+       __URI_INIT(&uri, a);
+       return rsync_queue(&uri, b);
+}
+
 /* Makes sure @count rsyncs finish after roughly @millis milliseconds. */
 static void
 wait_rsyncs(unsigned int count, unsigned int millis)
@@ -257,7 +271,7 @@ START_TEST(test_fast_single_rsync)
        ck_assert_int_ne(-1, pssk.rd.fd);
        ck_assert_int_ne(-1, pssk.wr);
 
-       ck_assert_int_eq(0, rsync_queue("A", "B"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("A", "B"));
        wait_rsyncs(1, 0);
 
        rsync_teardown();
@@ -272,7 +286,7 @@ START_TEST(test_stalled_single_rsync)
        ck_assert_int_ne(-1, pssk.rd.fd);
        ck_assert_int_ne(-1, pssk.wr);
 
-       ck_assert_int_eq(0, rsync_queue("A", "B"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("A", "B"));
        wait_rsyncs(1, 3000);
 
        rsync_teardown();
@@ -287,7 +301,7 @@ START_TEST(test_stalled_single_rsync_timeout)
        ck_assert_int_ne(-1, pssk.rd.fd);
        ck_assert_int_ne(-1, pssk.wr);
 
-       ck_assert_int_eq(0, rsync_queue("A", "B"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("A", "B"));
        wait_rsyncs(1, 4000); /* 4000 = timeout */
 
        rsync_teardown();
@@ -302,7 +316,7 @@ START_TEST(test_dripfeed_single_rsync)
        ck_assert_int_ne(-1, pssk.rd.fd);
        ck_assert_int_ne(-1, pssk.wr);
 
-       ck_assert_int_eq(0, rsync_queue("A", "B"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("A", "B"));
        wait_rsyncs(1, 3000);
 
        rsync_teardown();
@@ -317,7 +331,7 @@ START_TEST(test_dripfeed_single_rsync_timeout)
        ck_assert_int_ne(-1, pssk.rd.fd);
        ck_assert_int_ne(-1, pssk.wr);
 
-       ck_assert_int_eq(0, rsync_queue("A", "B"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("A", "B"));
        wait_rsyncs(1, 4000); /* 4000 = timeout */
 
        rsync_teardown();
@@ -346,9 +360,9 @@ START_TEST(test_simultaneous_rsyncs)
        ck_assert_int_ne(-1, pssk.rd.fd);
        ck_assert_int_ne(-1, pssk.wr);
 
-       ck_assert_int_eq(0, rsync_queue("A", "B"));
-       ck_assert_int_eq(0, rsync_queue("C", "D"));
-       ck_assert_int_eq(0, rsync_queue("E", "F"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("A", "B"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("C", "D"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("E", "F"));
 
        wait_rsyncs(3, 1000);
 
@@ -364,10 +378,10 @@ START_TEST(test_queued_rsyncs)
        ck_assert_int_ne(-1, pssk.rd.fd);
        ck_assert_int_ne(-1, pssk.wr);
 
-       ck_assert_int_eq(0, rsync_queue("A", "B"));
-       ck_assert_int_eq(0, rsync_queue("C", "D"));
-       ck_assert_int_eq(0, rsync_queue("E", "F"));
-       ck_assert_int_eq(0, rsync_queue("G", "H"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("A", "B"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("C", "D"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("E", "F"));
+       ck_assert_int_eq(0, RSYNC_QUEUE("G", "H"));
 
        wait_rsyncs(3, 2000);
        /* 2k minus the 100 extra we slept during the previous wait_rsyncs() */
index 54290d0add35ce317b78fb7f60a3ef70099a5484..3e9b33102e432212b2133de731713ec1b35cbf7b 100644 (file)
@@ -7,6 +7,7 @@
 #include "mock.c"
 #include "types/array.h"
 #include "types/map.c"
+#include "types/url.c"
 
 void
 rpki_certificate_free(struct rpki_certificate *cert)
@@ -23,7 +24,8 @@ queue_1(char *mapstr)
        struct cache_mapping map;
        struct rpki_certificate parent = { 0 };
 
-       map.url = map.path = mapstr;
+       __URI_INIT(&map.url, mapstr);
+       map.path = mapstr;
        ck_assert_int_eq(1, task_enqueue_rpp(&map, &parent));
 }
 
@@ -32,7 +34,7 @@ dequeue_1(char *mapstr, struct validation_task *prev)
 {
        struct validation_task *task;
        task = task_dequeue(prev);
-       ck_assert_str_eq(mapstr, task->u.ca->map.url);
+       ck_assert_uri(mapstr, &task->u.ca->map.url);
        return task;
 }
 
@@ -204,12 +206,12 @@ user_thread(void *arg)
        printf("th%d: Started.\n", thid);
 
        while ((task = task_dequeue(task)) != NULL) {
-               printf("- th%d: Dequeued '%s'\n", thid, task->u.ca->map.url);
+               printf("- th%d: Dequeued '%s'\n", thid, uri_str(&task->u.ca->map.url));
                total_dequeued++;
 
                if (certificate_traverse_mock(task->u.ca, thid) == EBUSY) {
                        printf("+ th%d: Requeuing '%s'\n",
-                           thid, task->u.ca->map.url);
+                           thid, uri_str(&task->u.ca->map.url));
                        task_requeue_dormant(task);
                        task = NULL;
                }
index 57bfeee5d4a668e9b6cfa7c4268b861143305d36..ab890a30d35b76b1378622692d1ca98696009bfe 100644 (file)
@@ -65,25 +65,34 @@ START_TEST(test_normalize)
 }
 END_TEST
 
+#define ck_assert_origin(expected, s1, s2)                             \
+       do {                                                            \
+               __URI_INIT(&u1, s1);                                    \
+               __URI_INIT(&u2, s2);                                    \
+               ck_assert_int_eq(expected, uri_same_origin(&u1, &u2));  \
+       } while (0)
+
 START_TEST(test_same_origin)
 {
-       ck_assert_int_eq(true,  url_same_origin("https://a.b.c/d/e/f",  "https://a.b.c/g/h/i"));
-       ck_assert_int_eq(false, url_same_origin("https://a.b.cc/d/e/f", "https://a.b.c/g/h/i"));
-       ck_assert_int_eq(false, url_same_origin("https://a.b.c/d/e/f",  "https://a.b.cc/g/h/i"));
-       ck_assert_int_eq(true,  url_same_origin("https://a.b.c",        "https://a.b.c"));
-       ck_assert_int_eq(true,  url_same_origin("https://a.b.c/",       "https://a.b.c"));
-       ck_assert_int_eq(true,  url_same_origin("https://a.b.c",        "https://a.b.c/"));
-       ck_assert_int_eq(true,  url_same_origin("https://",             "https://"));
-       ck_assert_int_eq(false, url_same_origin("https://",             "https://a"));
-       ck_assert_int_eq(false, url_same_origin("https://a",            "https://b"));
+       struct uri u1, u2;
+
+       ck_assert_origin(true,  "https://a.b.c/d/e/f",  "https://a.b.c/g/h/i");
+       ck_assert_origin(false, "https://a.b.cc/d/e/f", "https://a.b.c/g/h/i");
+       ck_assert_origin(false, "https://a.b.c/d/e/f",  "https://a.b.cc/g/h/i");
+       ck_assert_origin(true,  "https://a.b.c",        "https://a.b.c");
+       ck_assert_origin(true,  "https://a.b.c/",       "https://a.b.c");
+       ck_assert_origin(true,  "https://a.b.c",        "https://a.b.c/");
+       ck_assert_origin(true,  "https://",             "https://");
+       ck_assert_origin(false, "https://",             "https://a");
+       ck_assert_origin(false, "https://a",            "https://b");
 
        /* Undefined, but manhandle the code anyway */
-       ck_assert_int_eq(false, url_same_origin("",                     ""));
-       ck_assert_int_eq(false, url_same_origin("ht",                   "ht"));
-       ck_assert_int_eq(false, url_same_origin("https:",               "https:"));
-       ck_assert_int_eq(false, url_same_origin("https:/",              "https:/"));
-       ck_assert_int_eq(false, url_same_origin("https:/a",             "https:/a"));
-       ck_assert_int_eq(true,  url_same_origin("https:/a/",            "https:/a/"));
+       ck_assert_origin(false, "",                     "");
+       ck_assert_origin(false, "ht",                   "ht");
+       ck_assert_origin(false, "https:",               "https:");
+       ck_assert_origin(false, "https:/",              "https:/");
+       ck_assert_origin(false, "https:/a",             "https:/a");
+       ck_assert_origin(true,  "https:/a/",            "https:/a/");
 }
 END_TEST