From: Alberto Leiva Popper Date: Sat, 21 Sep 2024 01:04:29 +0000 (-0600) Subject: Separate TA caching logic from RPP caching logic X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5da329d9b95209c5ebbfa1afced7bb7052a68a57;p=thirdparty%2FFORT-validator.git Separate TA caching logic from RPP caching logic (Was getting shot in the foot by overly reusing code.) Testing and course correcting, week 2. Unstable. Fixes (from the previous commit) > 2. Fall back to rsync only on RRDP download failure; it's wasteful to > also do it on RRDP validation failure. RPPs: RRDP download failure triggers rsync download fallback. RPP validation failure no longer triggers rsync download fallback. TAs: HTTP download failure triggers rsync download fallback. HTTP validation failure still triggers rsync download fallback. (Because it's the TA, and needs to be treated more paranoically.) This commit is also a startup for > 3. Must commit repository content at the RPP level, not at the module > level. (One failing RPP should not invalidate the whole module.) Because I'm headed into a more explicit separation between "Repository" and "RPP." --- diff --git a/src/cache.c b/src/cache.c index 25d9514d..5b07e973 100644 --- a/src/cache.c +++ b/src/cache.c @@ -19,6 +19,7 @@ #include "rpp.h" #include "rsync.h" #include "types/path.h" +#include "types/url.h" /* XXX force RRDP if one RPP fails to validate by rsync? */ @@ -594,124 +595,115 @@ cancel: pb_cleanup(&pb); } /* @uri is either a caRepository or a rpkiNotify */ -static int -try_uri(char const *uri, struct cache_node *root, - dl_cb download, validate_cb validate, void *arg) +static struct cache_node * +do_refresh(char const *uri, struct cache_node *root, dl_cb download) { - struct cache_node *rpp; - struct cache_mapping map; - int error; + struct cache_node *node; if (!uri) - return 1; /* Protocol unavailable; ignore */ + return NULL; /* Protocol unavailable; ignore */ - pr_val_debug("Trying %s (%s)...", uri, download ? "online" : "offline"); + pr_val_debug("Trying %s (online)...", uri); - rpp = cachent_provide(root, uri); - if (!rpp) - return pr_val_err("Malformed URL: %s", uri); - - if (download != NULL) { - if (rpp->flags & CNF_FRESH) { - if (rpp->dlerr) - return rpp->dlerr; - } else { - rpp->flags |= CNF_FRESH; - error = rpp->dlerr = download(rpp); - if (error) - return error; - } + node = cachent_provide(root, uri); + if (!node) { + pr_val_err("Malformed URL: %s", uri); + return NULL; } - map.url = rpp->url; - map.path = (download != NULL) ? get_tmppath(rpp) : rpp->path; - error = validate(&map, arg); - if (error) { - pr_val_debug("RPP validation failed."); - return error; + if (!(node->flags & CNF_FRESH)) { + node->flags |= CNF_FRESH; + node->dlerr = download(node); } + if (node->dlerr) + pr_val_debug("Refresh failed."); - pr_val_debug("RPP validated successfully."); - rpp->flags |= CNF_VALID; - return 0; + return node; } -static int -try_uris(struct strlist *uris, struct cache_node *root, - char const *prefix, dl_cb dl, validate_cb cb, void *arg) +/* @url needs to outlive @map. */ +int +cache_refresh_url(char *url, struct cache_mapping *map) { - char **str; - int error; + struct cache_node *node = NULL; - ARRAYLIST_FOREACH(uris, str) - if (str_starts_with(*str, prefix)) { - error = try_uri(*str, root, dl, cb, arg); - if (error <= 0) - return error; - } + // XXX mutex + // XXX review result signs - return 1; + if (url_is_https(url)) + node = do_refresh(url, cache.https, dl_http); + else if (url_is_rsync(url)) + node = do_refresh(url, cache.rsync, dl_rsync); + if (!node) + return EINVAL; + + // XXX might want to const url and path. + // Alternatively, strdup path so the caller can't corrupt our string. + map->url = url; + map->path = get_tmppath(node); + return (map->path != NULL) ? 0 : EINVAL; } +/* @url needs to outlive @map. */ int -cache_download_uri(struct strlist *uris, validate_cb cb, void *arg) +cache_fallback_url(char *url, struct cache_mapping *map) { - int error; + struct cache_node *node = NULL; - // XXX mutex - // XXX review result signs - - /* Online attempts */ - error = try_uris(uris, cache.https, "https://", dl_http, cb, arg); - if (error <= 0) - return error; - error = try_uris(uris, cache.rsync, "rsync://", dl_rsync, cb, arg); - if (error <= 0) - return error; + if (url_is_https(url)) + node = cachent_provide(cache.https, url); + else if (url_is_rsync(url)) + node = cachent_provide(cache.rsync, url); + if (!node) + return EINVAL; - /* Offline attempts */ - error = try_uris(uris, cache.https, "https://", NULL, cb, arg); - if (error <= 0) - return error; - return try_uris(uris, cache.rsync, "rsync://", NULL, cb, arg); + map->url = url; + map->path = node->path; + return 0; } /* - * XXX outdated comment - * - * Assumes the URIs represent different ways to access the same content. - * - * Sequentially (in the order dictated by their priorities) attempts to update - * (in the cache) the content pointed by each URL. - * If a download succeeds, calls cb on it. If cb succeeds, returns without - * trying more URLs. - * - * If none of the URLs download and callback properly, attempts to find one - * that's already cached, and callbacks it. + * Attempts to refresh the RPP described by @sias, returns the resulting + * repository's mapping. */ int -cache_download_alt(struct sia_uris *sias, validate_cb cb, void *arg) +cache_refresh_sias(struct sia_uris *sias, struct cache_mapping *map) { - int error; + struct cache_node *hnode; + struct cache_node *rnode; // XXX Make sure somewhere validates rpkiManifest matches caRepository. - /* XXX mutex */ - - /* Online attempts */ + // XXX mutex // XXX review result signs // XXX normalize rpkiNotify & caRepository? - error = try_uri(sias->rpkiNotify, cache.https, dl_rrdp, cb, arg); - if (error <= 0) - return error; - error = try_uri(sias->caRepository, cache.rsync, dl_rsync, cb, arg); - if (error <= 0) - return error; - /* Offline attempts */ - error = try_uri(sias->rpkiNotify, cache.https, NULL, cb, arg); - if (error <= 0) - return error; - return try_uri(sias->caRepository, cache.rsync, NULL, cb, arg); + hnode = do_refresh(sias->rpkiNotify, cache.https, dl_rrdp); + if (hnode && !hnode->dlerr) { + map->url = hnode->url; + map->path = hnode->tmppath; + return 0; + } + + rnode = do_refresh(sias->caRepository, cache.rsync, dl_rsync); + if (rnode && !rnode->dlerr) { + map->url = rnode->url; + map->path = rnode->tmppath; + return 0; + } + + if (hnode && cachent_is_cached(hnode)) { + map->url = hnode->url; + map->path = hnode->path; + return 0; + } + + if (hnode && cachent_is_cached(rnode)) { + map->url = hnode->url; + map->path = hnode->path; + return 0; + } + + return EINVAL; } void diff --git a/src/cache.h b/src/cache.h index fdf46a02..4cfbf321 100644 --- a/src/cache.h +++ b/src/cache.h @@ -2,7 +2,6 @@ #define SRC_CACHE_LOCAL_CACHE_H_ #include "types/map.h" -#include "types/str.h" int cache_setup(void); /* Init this module */ void cache_teardown(void); /* Destroy this module */ @@ -19,16 +18,9 @@ struct sia_uris { void sias_init(struct sia_uris *); void sias_cleanup(struct sia_uris *); -/* - * The callback should return - * - * - 0 on success ("Mapping handled successfully") - * - > 0 on soft errors ("Try another mapping") - * - < 0 on hard errors ("Abandon foreach") - */ -typedef int (*validate_cb)(struct cache_mapping *, void *); -int cache_download_uri(struct strlist *, validate_cb, void *); -int cache_download_alt(struct sia_uris *, validate_cb, void *); +int cache_refresh_url(char *, struct cache_mapping *); +int cache_fallback_url(char *, struct cache_mapping *); +int cache_refresh_sias(struct sia_uris *, struct cache_mapping *); void cache_print(void); /* Dump cache in stdout. Recursive; tests only */ diff --git a/src/cachent.c b/src/cachent.c index 72aa2bd8..46c593e6 100644 --- a/src/cachent.c +++ b/src/cachent.c @@ -33,6 +33,18 @@ cachent_root_https(void) return cachent_root("https://", "https"); } +bool +cachent_is_cached(struct cache_node *node) +{ + if (cachent_is_https(node)) + return node->flags & CNF_CACHED; + + for (; node != NULL; node = node->parent) + if (node->flags & CNF_CACHED) + return true; + return false; +} + /* Preorder. @cb returns whether the children should be traversed. */ void cachent_traverse(struct cache_node *root, bool (*cb)(struct cache_node *)) diff --git a/src/cachent.h b/src/cachent.h index 8d297101..2bed18f2 100644 --- a/src/cachent.h +++ b/src/cachent.h @@ -78,6 +78,11 @@ struct cache_node { struct cache_node *cachent_root_rsync(void); struct cache_node *cachent_root_https(void); +#define cachent_is_rsync(node) ((node)->flags & CNF_RSYNC) +#define cachent_is_https(node) (!cachent_is_rsync(node)) + +bool cachent_is_cached(struct cache_node *); + void cachent_traverse(struct cache_node *, bool (*cb)(struct cache_node *)); struct cache_node *cachent_find(struct cache_node *, char const *, diff --git a/src/cert_stack.c b/src/cert_stack.c index f863ec09..dbce8280 100644 --- a/src/cert_stack.c +++ b/src/cert_stack.c @@ -249,8 +249,8 @@ create_separator(void) /* Steals ownership of @x509 on success. */ int -x509stack_push(struct cert_stack *stack, struct cache_mapping *map, X509 *x509, - enum rpki_policy policy, enum cert_type type) +x509stack_push(struct cert_stack *stack, struct cache_mapping const *map, + X509 *x509, enum rpki_policy policy, enum cert_type type) { struct metadata_node *meta; struct defer_node *defer_separator; diff --git a/src/cert_stack.h b/src/cert_stack.h index 5c818aef..411bac3a 100644 --- a/src/cert_stack.h +++ b/src/cert_stack.h @@ -19,8 +19,8 @@ void certstack_destroy(struct cert_stack *); void deferstack_push(struct cert_stack *, struct cache_mapping *, struct rpp *); int deferstack_pop(struct cert_stack *, struct deferred_cert *cert); -int x509stack_push(struct cert_stack *, struct cache_mapping *, X509 *, - enum rpki_policy, enum cert_type); +int x509stack_push(struct cert_stack *, struct cache_mapping const *, + X509 *, enum rpki_policy, enum cert_type); void x509stack_cancel(struct cert_stack *); X509 *x509stack_peek(struct cert_stack *); struct resources *x509stack_peek_resources(struct cert_stack *); diff --git a/src/object/certificate.c b/src/object/certificate.c index 1e1e5b1e..3968af30 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -24,6 +24,7 @@ #include "object/manifest.h" #include "thread_var.h" #include "types/path.h" +#include "types/str.h" #include "types/url.h" /* @@ -794,7 +795,7 @@ end: } static int -certificate_load(struct cache_mapping *map, X509 **result) +certificate_load(struct cache_mapping const *map, X509 **result) { X509 *cert = NULL; BIO *bio; @@ -1847,7 +1848,7 @@ certificate_validate_aia(char const *caIssuers, X509 *cert) } static int -check_rpp(struct cache_mapping *map_rpp, void *rpkiManifest) +check_rpp(struct cache_mapping const *map_rpp, char *rpkiManifest) { struct cache_mapping mft; struct rpp *pp; @@ -1869,14 +1870,16 @@ end: free(mft.path); /* Boilerplate code for CA certificate validation and recursive traversal. */ int -certificate_traverse(struct rpp *rpp_parent, struct cache_mapping *cert_map) +certificate_traverse(struct rpp *rpp_parent, + struct cache_mapping const *cert_map) { struct validation *state; int total_parents; X509 *x509; - struct sia_uris sia_uris; + struct sia_uris sias; enum rpki_policy policy; enum cert_type certype; + struct cache_mapping rpp; int error; state = state_retrieve(); @@ -1920,26 +1923,32 @@ certificate_traverse(struct rpp *rpp_parent, struct cache_mapping *cert_map) if (error) goto revert_cert; - sias_init(&sia_uris); + sias_init(&sias); error = (certype == CERTYPE_TA) - ? certificate_validate_extensions_ta(x509, &sia_uris, &policy) - : certificate_validate_extensions_ca(x509, &sia_uris, &policy, + ? certificate_validate_extensions_ta(x509, &sias, &policy) + : certificate_validate_extensions_ca(x509, &sias, &policy, rpp_parent); if (error) - goto revert_uris; + goto revert_sias; error = x509stack_push(validation_certstack(state), cert_map, x509, policy, certype); if (error) - goto revert_uris; + goto revert_sias; x509 = NULL; /* Ownership stolen */ - error = cache_download_alt(&sia_uris, check_rpp, sia_uris.rpkiManifest); + error = cache_refresh_sias(&sias, &rpp); + if (error) { + x509stack_cancel(validation_certstack(state)); + goto revert_sias; + } + + error = check_rpp(&rpp, sias.rpkiManifest); if (error) x509stack_cancel(validation_certstack(state)); -revert_uris: - sias_cleanup(&sia_uris); +revert_sias: + sias_cleanup(&sias); revert_cert: if (x509 != NULL) X509_free(x509); diff --git a/src/object/certificate.h b/src/object/certificate.h index 9bc4e56e..5e9aa6f6 100644 --- a/src/object/certificate.h +++ b/src/object/certificate.h @@ -55,6 +55,6 @@ int certificate_validate_extensions_bgpsec(X509 *, unsigned char **, */ int certificate_validate_aia(char const *, X509 *); -int certificate_traverse(struct rpp *, struct cache_mapping *); +int certificate_traverse(struct rpp *, struct cache_mapping const *); #endif /* SRC_OBJECT_CERTIFICATE_H_ */ diff --git a/src/object/tal.c b/src/object/tal.c index 489d2a89..0802e28a 100644 --- a/src/object/tal.c +++ b/src/object/tal.c @@ -12,6 +12,8 @@ #include "log.h" #include "thread_var.h" #include "types/path.h" +#include "types/str.h" +#include "types/url.h" struct tal { char const *file_name; @@ -32,10 +34,13 @@ struct validation_thread { /* List of threads, one per TAL file */ SLIST_HEAD(threads_list, validation_thread); -struct handle_tal_args { - struct tal tal; - struct db_table *db; -}; +#define TS_SUCCESS 0 /* TA looks sane. */ +#define TS_NEXT 1 /* TA seems compromised; try some other URL. */ +#define TS_FALLBACK 2 /* TA is broken; fall back to old cache. */ +typedef struct { int v; } ta_status; +static ta_status ts_success = { .v = TS_SUCCESS }; +static ta_status ts_next = { .v = TS_NEXT }; +static ta_status ts_fallback = { .v = TS_FALLBACK }; static char * find_newline(char *str) @@ -84,8 +89,7 @@ read_content(char *fc /* File Content */, struct tal *tal) if (is_blank(fc)) break; - if (str_starts_with(fc, "https://") || - str_starts_with(fc, "rsync://")) + if (url_is_https(fc) || url_is_rsync(fc)) strlist_add(&tal->urls, pstrdup(fc)); fc = nl + cr + 1; @@ -149,47 +153,31 @@ tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len) *len = tal->spki_len; } -/* - * Performs the whole validation walkthrough that starts with Trust Anchor @ta, - * which is assumed to have been extracted from TAL @arg->tal. - */ -static int -handle_ta(struct cache_mapping *ta, void *arg) +static ta_status +handle_ta(struct cache_mapping const *ta, struct validation *state) { - struct handle_tal_args *args = arg; - struct validation_handler validation_handler; - struct validation *state; struct cert_stack *certstack; struct deferred_cert deferred; int error; - validation_handler.handle_roa_v4 = handle_roa_v4; - validation_handler.handle_roa_v6 = handle_roa_v6; - validation_handler.handle_router_key = handle_router_key; - validation_handler.arg = args->db; - - error = validation_prepare(&state, &args->tal, &validation_handler); - if (error) - return ENSURE_NEGATIVE(error); - - if (!str_ends_with(ta->url, ".cer")) { - pr_op_err("TAL URI lacks '.cer' extension: %s", ta->url); - error = EINVAL; - goto end; - } - - /* Handle root certificate. */ - error = certificate_traverse(NULL, ta); - if (error) { + /* == Root certificate == */ + if (certificate_traverse(NULL, ta) != 0) { switch (validation_pubkey_state(state)) { case PKS_INVALID: - error = EINVAL; - goto end; + /* Signature invalid; probably an impersonator. */ + return ts_next; case PKS_VALID: + /* No impersonator but still error: Broken tree. */ + /* + * XXX Change to ts_next. This is the TA; + * we can't really afford to panic-fallback. + */ + return ts_fallback; case PKS_UNTESTED: - error = ENSURE_NEGATIVE(error); - goto end; + /* We don't know; try some other URL. */ + return ts_next; } + pr_crit("Unknown public key state: %u", validation_pubkey_state(state)); } @@ -200,15 +188,14 @@ handle_ta(struct cache_mapping *ta, void *arg) * (the root validated successfully; subtrees are isolated problems.) */ - /* Handle every other certificate. */ + /* == Every other certificate == */ certstack = validation_certstack(state); do { error = deferstack_pop(certstack, &deferred); - if (error == -ENOENT) { - error = 0; /* No more certificates left; we're done */ - goto end; - } else if (error) /* All other errors are critical, currently */ + if (error == -ENOENT) + return ts_success; /* No more certificates left */ + else if (error) /* All other errors are critical, currently */ pr_crit("deferstack_pop() returned illegal %d.", error); /* @@ -220,16 +207,72 @@ handle_ta(struct cache_mapping *ta, void *arg) map_cleanup(&deferred.map); rpp_refput(deferred.pp); } while (true); +} -end: validation_destroy(state); - return error; +static void +__do_file_validation(struct validation_thread *thread) +{ + struct tal tal; + struct validation_handler collector; + struct db_table *db; + struct validation *state; + char **url; + struct cache_mapping map; + ta_status status; + + thread->error = tal_init(&tal, thread->tal_file); + if (thread->error) + return; + + collector.handle_roa_v4 = handle_roa_v4; + collector.handle_roa_v6 = handle_roa_v6; + collector.handle_router_key = handle_router_key; + collector.arg = db = db_table_create(); + + thread->error = validation_prepare(&state, &tal, &collector); + if (thread->error) { + db_table_destroy(db); + goto end2; + } + + ARRAYLIST_FOREACH(&tal.urls, url) { + if (cache_refresh_url(*url, &map) != 0) + continue; + + status = handle_ta(&map, state); + switch (status.v) { + case TS_SUCCESS: goto end1; + case TS_FALLBACK: goto fallback; + case TS_NEXT: ; /* Fall through */ + } + } + +fallback: + ARRAYLIST_FOREACH(&tal.urls, url) { + if (cache_fallback_url(*url, &map) != 0) + continue; + + status = handle_ta(&map, state); + switch (status.v) { + case TS_SUCCESS: goto end1; + case TS_FALLBACK: /* Already fallbacking */ + case TS_NEXT: ; /* Fall through */ + } + } + + pr_op_err("None of the TAL URIs yielded a successful traversal."); + thread->error = EINVAL; + db_table_destroy(db); + db = NULL; + +end1: thread->db = db; +end2: tal_cleanup(&tal); } static void * do_file_validation(void *arg) { struct validation_thread *thread = arg; - struct handle_tal_args args; time_t start, finish; start = time(NULL); @@ -237,26 +280,15 @@ do_file_validation(void *arg) fnstack_init(); fnstack_push(thread->tal_file); - thread->error = tal_init(&args.tal, thread->tal_file); - if (thread->error) - goto end; - - args.db = db_table_create(); - thread->error = cache_download_uri(&args.tal.urls, handle_ta, &args); - if (thread->error) { - pr_op_err("None of the TAL URIs yielded a successful traversal."); - db_table_destroy(args.db); - } else { - thread->db = args.db; - } + __do_file_validation(thread); - tal_cleanup(&args.tal); -end: fnstack_cleanup(); + fnstack_cleanup(); finish = time(NULL); if (start != ((time_t) -1) && finish != ((time_t) -1)) pr_op_debug("The %s tree took %.0lf seconds.", - args.tal.file_name, difftime(finish, start)); + path_filename(thread->tal_file), + difftime(finish, start)); return NULL; } diff --git a/src/print_file.c b/src/print_file.c index 7d0f534d..27476c93 100644 --- a/src/print_file.c +++ b/src/print_file.c @@ -13,6 +13,7 @@ #include "rsync.h" #include "types/bio_seq.h" #include "types/path.h" +#include "types/url.h" #define HDRSIZE 32 @@ -98,7 +99,7 @@ filename2bio(char const *filename) if (filename == NULL || strcmp(filename, "-") == 0) return BIO_new_fp(stdin, BIO_NOCLOSE); - if (str_starts_with(filename, "rsync://")) + if (url_is_rsync(filename)) return rsync2bio(filename); return BIO_new_file(filename, "rb"); diff --git a/src/thread_var.c b/src/thread_var.c index 13d5ec5f..ab09259c 100644 --- a/src/thread_var.c +++ b/src/thread_var.c @@ -158,7 +158,7 @@ fnstack_push(char const *file) /* See fnstack_push(). @map needs to outlive the push/pop. */ void -fnstack_push_map(struct cache_mapping *map) +fnstack_push_map(struct cache_mapping const *map) { fnstack_push(map_val_get_printable(map)); } diff --git a/src/thread_var.h b/src/thread_var.h index 0d5d389c..d2b0c72a 100644 --- a/src/thread_var.h +++ b/src/thread_var.h @@ -12,7 +12,7 @@ void fnstack_init(void); void fnstack_cleanup(void); void fnstack_push(char const *); -void fnstack_push_map(struct cache_mapping *); +void fnstack_push_map(struct cache_mapping const *); char const *fnstack_peek(void); void fnstack_pop(void); diff --git a/src/types/map.c b/src/types/map.c index 2a6c13af..7377f402 100644 --- a/src/types/map.c +++ b/src/types/map.c @@ -6,7 +6,7 @@ #include "types/path.h" static char const * -map_get_printable(struct cache_mapping *map, enum filename_format format) +map_get_printable(struct cache_mapping const *map, enum filename_format format) { switch (format) { case FNF_GLOBAL: @@ -22,26 +22,26 @@ map_get_printable(struct cache_mapping *map, enum filename_format format) } char const * -map_val_get_printable(struct cache_mapping *map) +map_val_get_printable(struct cache_mapping const *map) { return map_get_printable(map, config_get_val_log_file_format()); } char const * -map_op_get_printable(struct cache_mapping *map) +map_op_get_printable(struct cache_mapping const *map) { return map_get_printable(map, config_get_op_log_file_format()); } void -map_parent(struct cache_mapping *child, struct cache_mapping *parent) +map_parent(struct cache_mapping const *child, struct cache_mapping *parent) { parent->url = path_parent(child->url); parent->path = path_parent(child->path); } struct cache_mapping * -map_child(struct cache_mapping *parent, char const *name) +map_child(struct cache_mapping const *parent, char const *name) { struct cache_mapping *child; @@ -53,7 +53,7 @@ map_child(struct cache_mapping *parent, char const *name) } void -map_copy(struct cache_mapping *dst, struct cache_mapping *src) +map_copy(struct cache_mapping *dst, struct cache_mapping const *src) { dst->url = pstrdup(src->url); dst->path = pstrdup(src->path); diff --git a/src/types/map.h b/src/types/map.h index c35b27ff..3ecc56fa 100644 --- a/src/types/map.h +++ b/src/types/map.h @@ -9,13 +9,13 @@ struct cache_mapping { char *path; }; -char const *map_val_get_printable(struct cache_mapping *); -char const *map_op_get_printable(struct cache_mapping *); +char const *map_val_get_printable(struct cache_mapping const *); +char const *map_op_get_printable(struct cache_mapping const *); -void map_parent(struct cache_mapping *, struct cache_mapping *); -struct cache_mapping *map_child(struct cache_mapping *, char const *); +void map_parent(struct cache_mapping const *, struct cache_mapping *); +struct cache_mapping *map_child(struct cache_mapping const *, char const *); -void map_copy(struct cache_mapping *, struct cache_mapping *); +void map_copy(struct cache_mapping *, struct cache_mapping const *); void map_cleanup(struct cache_mapping *); #endif /* SRC_TYPES_MAP_H_ */ diff --git a/src/types/url.c b/src/types/url.c index 3e03b68c..46c4dffc 100644 --- a/src/types/url.c +++ b/src/types/url.c @@ -1,8 +1,21 @@ #include "types/url.h" #include "alloc.h" +#include "common.h" #include "types/path.h" +bool +url_is_rsync(char const *url) +{ + return str_starts_with(url, "rsync://"); +} + +bool +url_is_https(char const *url) +{ + return str_starts_with(url, "https://"); +} + /* * XXX use this: * diff --git a/src/types/url.h b/src/types/url.h index 9ac9fffa..a2b0ea5d 100644 --- a/src/types/url.h +++ b/src/types/url.h @@ -5,6 +5,9 @@ #define RPKI_SCHEMA_LEN 8 /* strlen("rsync://"), strlen("https://") */ +bool url_is_rsync(char const *); +bool url_is_https(char const *); + char *url_normalize(char const *); bool url_same_origin(char const *, char const *); diff --git a/test/cache_test.c b/test/cache_test.c index 6c47f9fd..bb4c3cf0 100644 --- a/test/cache_test.c +++ b/test/cache_test.c @@ -62,7 +62,7 @@ setup_test(void) } static int -okay(struct cache_mapping *map, void *arg) +okay(struct cache_mapping const *map, void *arg) { return 0; } @@ -952,9 +952,9 @@ END_TEST // // va_start(args, maps); // while ((str = va_arg(args, char const *)) != NULL) { -// if (str_starts_with(str, "https://")) +// if (url_is_https(str)) // type = MAP_HTTP; -// else if (str_starts_with(str, "rsync://")) +// else if (url_is_rsync(str)) // type = MAP_RSYNC; // else // ck_abort_msg("Bad protocol: %s", str);