From: Alberto Leiva Popper Date: Wed, 5 Jun 2024 18:53:38 +0000 (-0600) Subject: Merge branch 'main' into issue82 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=97a5efacbf5e7f04084f2306d95e82566a84e445;p=thirdparty%2FFORT-validator.git Merge branch 'main' into issue82 --- 97a5efacbf5e7f04084f2306d95e82566a84e445 diff --cc src/Makefile.am index 9706d5a9,987acb4b..ac948798 --- a/src/Makefile.am +++ b/src/Makefile.am @@@ -18,6 -18,8 +18,7 @@@ fort_SOURCES += extension.h extension. fort_SOURCES += file.h file.c fort_SOURCES += init.h init.c fort_SOURCES += json_util.c json_util.h -fort_SOURCES += line_file.h line_file.c + fort_SOURCES += libcrypto_util.h libcrypto_util.c fort_SOURCES += log.h log.c fort_SOURCES += nid.h nid.c fort_SOURCES += output_printer.h output_printer.c diff --cc src/alloc.c index 8bc33ff5,e6fd2bd0..6c187663 --- a/src/alloc.c +++ b/src/alloc.c @@@ -62,7 -62,7 +62,7 @@@ pstrdup(const char *s } char * - pstrndup(char const *s, size_t size) -pstrndup(const char *s, size_t n) ++pstrndup(char const *s, size_t n) { char *result; diff --cc src/alloc.h index 5bd04975,2f4fd58b..07d6d07b --- a/src/alloc.h +++ b/src/alloc.h @@@ -20,6 -20,7 +20,7 @@@ void *prealloc(void *ptr, size_t size) /* strdup(), but panic on allocation failure. */ char *pstrdup(char const *s); - char *pstrndup(char const *s, size_t size); + /* strndup(), but panic on allocation failure. */ -char *pstrndup(const char *s, size_t n); ++char *pstrndup(char const *s, size_t n); #endif /* SRC_ALLOC_H_ */ diff --cc src/asn1/content_info.c index a2e0f95c,8088c632..b279160d --- a/src/asn1/content_info.c +++ b/src/asn1/content_info.c @@@ -54,7 -55,7 +55,7 @@@ content_info_load(char const *file, str struct file_contents fc; int error; - error = file_load(uri_get_local(uri), &fc, true); - error = file_load(file, &fc); ++ error = file_load(file, &fc, true); if (error) return error; diff --cc src/cache/local_cache.c index 0085f7d9,506f7309..71d9cf2b --- a/src/cache/local_cache.c +++ b/src/cache/local_cache.c @@@ -416,42 -380,17 +416,40 @@@ static json_t node2json(struct cache_node *node) { json_t *json; + char const *type; + json_t *notification; - json = json_object(); + json = json_obj_new(); if (json == NULL) - enomem_panic(); + return NULL; + switch (uri_get_type(node->url)) { + case UT_TA_RSYNC: + type = TYPEVALUE_TA_RSYNC; + break; + case UT_TA_HTTP: + type = TYPEVALUE_TA_HTTP; + break; + case UT_RPP: + type = TYPEVALUE_RPP; + break; + case UT_NOTIF: + type = TYPEVALUE_NOTIF; + break; + default: + goto cancel; + } + + if (json_add_str(json, TAGNAME_TYPE, type)) + goto cancel; if (json_add_str(json, TAGNAME_URL, uri_get_global(node->url))) goto cancel; - if (uri_is_notif(node->url)) - if (json_add_bool(json, TAGNAME_IS_NOTIF, true)) + if (node->notif != NULL) { + notification = rrdp_notif2json(node->notif); - if (notification == NULL) - goto cancel; - if (json_add_obj(json, TAGNAME_NOTIF, notification)) ++ if (json_object_add(json, TAGNAME_NOTIF, notification)) goto cancel; + } - if (json_add_date(json, TAGNAME_ATTEMPT_TS, node->attempt.ts)) + if (json_add_ts(json, TAGNAME_ATTEMPT_TS, node->attempt.ts)) goto cancel; if (json_add_int(json, TAGNAME_ATTEMPT_ERR, node->attempt.result)) goto cancel; @@@ -666,21 -593,18 +662,21 @@@ cache_download(struct rpki_cache *cache } switch (uri_get_type(url)) { - case UT_RSYNC: + case UT_TA_HTTP: + case UT_NOTIF: + case UT_TMP: + error = config_get_http_enabled() + ? http_download(url, node->success.ts, changed) + : cache_check(url); + break; + case UT_TA_RSYNC: + case UT_RPP: error = config_get_rsync_enabled() - ? rsync_download(url) + ? rsync_download(uri_get_global(url), uri_get_local(url), true) : cache_check(url); break; - case UT_HTTPS: - error = config_get_http_enabled() - ? http_download(url, changed) - : cache_check(url); - break; default: - pr_crit("Unexpected URI type: %d", uri_get_type(url)); + pr_crit("URI type not downloadable: %d", uri_get_type(url)); } node->attempt.ts = time(NULL); diff --cc src/data_structure/path_builder.c index d75daa10,4653379b..e53d4158 --- a/src/data_structure/path_builder.c +++ b/src/data_structure/path_builder.c @@@ -4,9 -4,11 +4,9 @@@ #include "alloc.h" #include "config.h" - #include "log.h" #include "crypto/hash.h" + #include "log.h" -#define SHA256_LEN (256 >> 3) /* 256 / 8, bits -> bytes */ - /* These are arbitrary; feel free to change them. */ #ifndef INITIAL_CAPACITY /* Unit tests want to override this */ #define INITIAL_CAPACITY 128u diff --cc src/extension.c index 97cc6c85,99045fa9..c3e6060a --- a/src/extension.c +++ b/src/extension.c @@@ -306,38 -1000,23 +996,23 @@@ validate_public_key_hash(X509 *cert, AS } int - handle_aki(X509_EXTENSION *ext, void *arg) + handle_aki(void *ext, void *arg) { - AUTHORITY_KEYID *aki; - struct validation *state; + AUTHORITY_KEYID *aki = ext; X509 *parent; - int error; - - aki = X509V3_EXT_d2i(ext); - if (aki == NULL) - return cannot_decode(ext_aki()); if (aki->issuer != NULL) { - error = pr_val_err("%s extension contains an authorityCertIssuer.", + return pr_val_err("%s extension contains an authorityCertIssuer.", ext_aki()->name); - goto end; } if (aki->serial != NULL) { - error = pr_val_err("%s extension contains an authorityCertSerialNumber.", + return pr_val_err("%s extension contains an authorityCertSerialNumber.", ext_aki()->name); - goto end; - } - - state = state_retrieve(); - parent = x509stack_peek(validation_certstack(state)); - if (parent == NULL) { - error = pr_val_err("Certificate has no parent."); - goto end; } - error = validate_public_key_hash(parent, aki->keyid, "AKI"); + parent = x509stack_peek(validation_certstack(state_retrieve())); + if (parent == NULL) + return pr_val_err("Certificate has no parent."); - end: - AUTHORITY_KEYID_free(aki); - return error; - return validate_public_key_hash(parent, aki->keyid); ++ return validate_public_key_hash(parent, aki->keyid, "AKI"); } diff --cc src/extension.h index 6dada3b8,7b9362e2..0baf2d3d --- a/src/extension.h +++ b/src/extension.h @@@ -44,7 -49,7 +49,7 @@@ int handle_extensions(struct extension_ STACK_OF(X509_EXTENSION) const *); int cannot_decode(struct extension_metadata const *); -int validate_public_key_hash(X509 *, ASN1_OCTET_STRING *); +int validate_public_key_hash(X509 *, ASN1_OCTET_STRING *, char const *); - int handle_aki(X509_EXTENSION *, void *); + int handle_aki(void *, void *); #endif /* SRC_EXTENSION_H_ */ diff --cc src/file.c index 768dd955,72c34b49..b23e90e5 --- a/src/file.c +++ b/src/file.c @@@ -133,45 -127,6 +133,24 @@@ file_exists(char const *path return (stat(path, &meta) == 0) ? 0 : errno; } - /* - * Validate @file_name, if it doesn't exist, this function will create it and - * close it. - */ - bool - file_valid(char const *file_name) - { - FILE *tmp; - int error; - - if (file_name == NULL) - return false; - - error = file_write(file_name, &tmp); - if (error) - return false; - - file_close(tmp); - return true; - } - +/* + * Like remove(), but don't care if the file is already deleted. + */ +int +file_rm_f(char const *path) +{ + int error; + + errno = 0; + if (remove(path) != 0) { + error = errno; + if (error != ENOENT) + return error; + } + + return 0; +} + static int rm(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { diff --cc src/file.h index d0d603bd,0379a424..db3e9623 --- a/src/file.h +++ b/src/file.h @@@ -24,16 -23,14 +24,15 @@@ struct file_contents }; int file_open(char const *, FILE **, struct stat *); - int file_write(char const *, FILE **); + int file_write(char const *, char const *, FILE **); void file_close(FILE *); -int file_load(char const *, struct file_contents *); +int file_load(char const *, struct file_contents *, bool); void file_free(struct file_contents *); int file_exists(char const *); - bool file_valid(char const *); +int file_rm_f(char const *); int file_rm_rf(char const *); /* diff --cc src/http/http.c index b783ba1b,bd4069b5..e901f75c --- a/src/http/http.c +++ b/src/http/http.c @@@ -1,12 -1,14 +1,12 @@@ #include "http/http.h" -#include - #include "alloc.h" + #include "cache/local_cache.h" #include "common.h" #include "config.h" + #include "data_structure/uthash.h" #include "file.h" #include "log.h" - #include "data_structure/uthash.h" - #include "cache/local_cache.h" struct http_handler { CURL *curl; diff --cc src/init.c index 4b444eea,dd90fcec..5cb9ff4b --- a/src/init.c +++ b/src/init.c @@@ -1,62 -1,43 +1,43 @@@ #include "init.h" - #include "alloc.h" #include "config.h" - #include "log.h" + #include "data_structure/path_builder.h" #include "http/http.h" + #include "log.h" static int - fetch_url(char const *url) + fetch_url(char const *url, char const *filename) { - char const *prefix = "https://"; - char const *dest_dir; - char const *dest_file; - char *dest; - size_t prefix_len; - size_t url_len; - size_t dest_dir_len; - size_t extra_slash; - size_t offset; + struct path_builder pb; int error; - prefix_len = strlen(prefix); - url_len = strlen(url); - dest_dir = config_get_tal(); - dest_dir_len = strlen(dest_dir); - - if (url_len <= prefix_len || - strncasecmp(url, prefix, prefix_len) != 0) - return pr_op_err("Invalid HTTPS URL: '%s'", url); - - dest_file = strrchr(url, '/') + 1; - if (*dest_file == '\0') - return pr_op_err("HTTPS URL '%s' must be a file location", url); - - extra_slash = (dest_dir[dest_dir_len - 1] == '/') ? 0 : 1; - - dest = pmalloc(dest_dir_len + extra_slash + strlen(dest_file) + 1); - - offset = 0; - strcpy(dest + offset, dest_dir); - offset += dest_dir_len; - if (extra_slash) { - strcpy(dest + offset, "/"); - offset += extra_slash; - } - strcpy(dest + offset, dest_file); - offset += strlen(dest_file); - dest[offset] = '\0'; + pb_init(&pb); + error = pb_append(&pb, config_get_tal()); + if (error) + goto pbfail; + error = pb_append(&pb, filename); + if (error) + goto pbfail; - error = http_download_direct(url, dest); - if (error) { - fprintf(stderr, "Couldn't fetch '%s'.\n", dest); - free(dest); - return error; - } - error = http_direct_download(url, pb.string); ++ error = http_download_direct(url, pb.string); + if (error) + goto dlfail; - fprintf(stdout, "Successfully fetched '%s'!\n\n", dest); - free(dest); + fprintf(stdout, "Successfully fetched '%s'!\n\n", pb.string); + pb_cleanup(&pb); return 0; + + pbfail: + fprintf(stderr, "Cannot determine destination path: %s\n", + strerror(abs(error))); + pb_cleanup(&pb); + return error; + + dlfail: + fprintf(stderr, "Couldn't fetch '%s': %s\n", pb.string, + strerror(abs(error))); + pb_cleanup(&pb); + return error; } int diff --cc src/json_util.h index 93a41d01,23afc304..b67da934 --- a/src/json_util.h +++ b/src/json_util.h @@@ -33,9 -34,17 +33,16 @@@ int json_get_object(json_t *, char cons bool json_valid_members_count(json_t *, size_t); -int json_add_bool(json_t *, char const *, bool); int json_add_int(json_t *, char const *, int); int json_add_str(json_t *, char const *, char const *); - int json_add_date(json_t *, char const *, time_t); - int json_add_obj(json_t *, char const *, json_t *); + int json_add_ts(json_t *, char const *, time_t); + + json_t *json_obj_new(void); + json_t *json_array_new(void); + json_t *json_int_new(json_int_t); + json_t *json_str_new(const char *); + json_t *json_strn_new(const char *, size_t); + int json_object_add(json_t *, char const *, json_t *); + int json_array_add(json_t *, json_t *); #endif /* SRC_JSON_UTIL_H_ */ diff --cc src/main.c index 88c56379,4336a129..7f69d0a7 --- a/src/main.c +++ b/src/main.c @@@ -1,15 -1,15 +1,16 @@@ #include #include "config.h" - #include "extension.h" - #include "log.h" - #include "nid.h" - #include "thread_var.h" +#include "crypto/hash.h" + #include "extension.h" #include "http/http.h" #include "incidence/incidence.h" - #include "rtr/rtr.h" + #include "log.h" + #include "nid.h" + #include "print_file.h" #include "rtr/db/vrps.h" + #include "rtr/rtr.h" + #include "thread_var.h" #include "xml/relax_ng.h" static int diff --cc src/object/certificate.c index d1a7d92c,876eb835..218f35a1 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@@ -1251,22 -1253,9 +1253,9 @@@ handle_bc(void *ext, void *arg } static int - handle_ski_ca(X509_EXTENSION *ext, void *arg) + handle_ski_ca(void *ext, void *arg) { - ASN1_OCTET_STRING *ski; - int error; - - ski = X509V3_EXT_d2i(ext); - if (ski == NULL) - return cannot_decode(ext_ski()); - - error = validate_public_key_hash(arg, ski, "SKI"); - - ASN1_OCTET_STRING_free(ski); - return error; - return validate_public_key_hash(arg, ext); ++ return validate_public_key_hash(arg, ext, "SKI"); } static int @@@ -1277,14 -1266,9 +1266,9 @@@ handle_ski_ee(void *ext, void *arg OCTET_STRING_t *sid; int error; - ski = X509V3_EXT_d2i(ext); - if (ski == NULL) - return cannot_decode(ext_ski()); - - args = arg; - error = validate_public_key_hash(args->cert, ski); + error = validate_public_key_hash(args->cert, ski, "SKI"); if (error) - goto end; + return error; /* rfc6488#section-2.1.6.2 */ /* rfc6488#section-3.1.c 2/2 */ diff --cc src/print_file.c index 00000000,404cd4ac..b5aaaa4d mode 000000,100644..100644 --- a/src/print_file.c +++ b/src/print_file.c @@@ -1,0 -1,312 +1,316 @@@ + #include "print_file.h" + + #include "asn1/asn1c/CRL.h" + #include "asn1/asn1c/Certificate.h" + #include "asn1/asn1c/ber_decoder.h" + #include "asn1/asn1c/json_encoder.h" + #include "asn1/content_info.h" + #include "common.h" + #include "config.h" + #include "data_structure/path_builder.h" + #include "file.h" + #include "log.h" + #include "rsync/rsync.h" + #include "types/bio_seq.h" + #include "types/uri.h" + + #define HDRSIZE 32 + + static BIO * + __rsync2bio(char const *src, char const *dst) + { + int error; + + error = rsync_download(src, dst, false); + if (error) { + pr_op_err("rysnc download failed: %s", strerror(abs(error))); + return NULL; + } + + return BIO_new_file(dst, "rb"); + } + + static BIO * + rsync2bio_tmpdir(char const *src) + { + #define TMPDIR "/tmp/fort-XXXXXX" + + struct path_builder pb; + char buf[strlen(TMPDIR) + 1]; + char *tmpdir; + BIO *result = NULL; + int error; + + strcpy(buf, TMPDIR); + tmpdir = mkdtemp(buf); + if (tmpdir == NULL) { + pr_op_err("Unable to create " TMPDIR ": %s", strerror(errno)); + return NULL; + } + + pb_init(&pb); + error = pb_append(&pb, tmpdir); + if (error) + goto end; + error = pb_append(&pb, strrchr(src, '/') + 1); + if (error) + goto end; + + result = __rsync2bio(src, pb.string); + + end: pb_cleanup(&pb); + return result; + } + + static BIO * + rsync2bio_cache(char const *src) + { + char const *tal; + struct rpki_uri *uri; + BIO *bio; + int error; + + tal = strrchr(config_get_tal(), '/'); + tal = (tal != NULL) ? (tal + 1) : config_get_tal(); + uri = NULL; + - error = uri_create(&uri, tal, UT_RSYNC, false, NULL, src); ++ /* ++ * TODO (#82) maybe rename UT_TA_RSYNC into single rsync. ++ * If applies and it's going to survive. ++ */ ++ error = uri_create(&uri, tal, UT_TA_RSYNC, NULL, src); + if (error) { + pr_op_err("Unparseable rsync URI: %s", strerror(abs(error))); + return NULL; + } + + bio = __rsync2bio(uri_get_global(uri), uri_get_local(uri)); + + uri_refput(uri); + return bio; + } + + static BIO * + rsync2bio(char const *src) + { + return (config_get_tal() && config_get_local_repository()) + ? rsync2bio_cache(src) + : rsync2bio_tmpdir(src); + } + + static BIO * + filename2bio(char const *filename) + { + if (filename == NULL || strcmp(filename, "-") == 0) + return BIO_new_fp(stdin, BIO_NOCLOSE); + + if (str_starts_with(filename, "rsync://")) + return rsync2bio(filename); + + return BIO_new_file(filename, "rb"); + } + + static unsigned char * + skip_sequence(unsigned char *buf, unsigned char *cursor) + { + ber_tlv_len_t len; + ssize_t len_len; + + len_len = ber_fetch_length(1, cursor, HDRSIZE - (cursor - buf), &len); + if (len_len <= 0) + return NULL; + cursor += len_len; + return (cursor <= (buf + HDRSIZE)) ? cursor : NULL; + } + + static unsigned char * + skip_integer(unsigned char *buf, unsigned char *cursor) + { + ber_tlv_len_t len; + ssize_t len_len; + + len_len = ber_fetch_length(0, cursor, HDRSIZE - (cursor - buf), &len); + if (len_len <= 0) + return NULL; + cursor += len_len + len; + return (cursor <= (buf + HDRSIZE)) ? cursor : NULL; + } + + static int + guess_file_type(BIO **bio, unsigned char *hdrbuf) + { + unsigned char *ptr; + int res; + + if (config_get_file_type() != FT_UNK) + return config_get_file_type(); + + res = BIO_read(*bio, hdrbuf, HDRSIZE); + if (res <= 0) + return op_crypto_err("Cannot guess file type; IO error."); + + *bio = BIO_new_seq(BIO_new_mem_buf(hdrbuf, res), *bio); + if ((*bio) == NULL) + return op_crypto_err("BIO_new_seq() returned NULL."); + + if (hdrbuf[0] != 0x30) { + pr_op_debug("File doesn't start with a SEQUENCE."); + return FT_UNK; + } + ptr = skip_sequence(hdrbuf, hdrbuf + 1); + if (ptr == NULL) { + pr_op_debug("Cannot skip first sequence length."); + return FT_UNK; + } + + if (*ptr == 0x06) { + pr_op_debug("SEQ containing OID."); + return FT_ROA; /* Same parser for mfts and gbrs */ + } + if (*ptr != 0x30) { + pr_op_debug("SEQ containing unexpected: 0x%x", *ptr); + return FT_UNK; + } + + ptr = skip_sequence(hdrbuf, ptr + 1); + if (ptr == NULL) { + pr_op_debug("Cannot skip second sequence length."); + return FT_UNK; + } + ptr = skip_integer(hdrbuf, ptr + 1); + if (ptr == NULL) { + pr_op_debug("Cannot skip version number."); + return FT_UNK; + } + + if (*ptr == 0x02) { + pr_op_debug("SEQ containing SEQ containing (INT, INT)."); + return FT_CER; + } + if (*ptr == 0x30) { + pr_op_debug("SEQ containing SEQ containing (INT, SEQ)."); + return FT_CRL; + } + + pr_op_debug("SEQ containing SEQ containing unexpected: 0x%x", *ptr); + return FT_UNK; + } + + static struct ContentInfo * + bio2ci(BIO *bio) + { + #define BUFFER_SIZE 4096 + struct ContentInfo *ci = NULL; + unsigned char buffer[BUFFER_SIZE]; + int res1; + asn_dec_rval_t res2; + + do { + res1 = BIO_read(bio, buffer, BUFFER_SIZE); + if (res1 <= 0) { + op_crypto_err("IO error."); + goto fail; + } + + res2 = ber_decode(&asn_DEF_ContentInfo, (void **)&ci, + buffer, res1); + pr_op_debug("Consumed: %zu", res2.consumed); + + switch (res2.code) { + case RC_OK: + return ci; + + case RC_WMORE: + break; + + case RC_FAIL: + pr_op_err("Unsuccessful parse."); + goto fail; + } + } while (true); + + fail: ASN_STRUCT_FREE(asn_DEF_ContentInfo, ci); + return NULL; + } + + static json_t * + asn1c2json(BIO *bio) + { + struct ContentInfo *ci; + json_t *json; + + ci = bio2ci(bio); + if (ci == NULL) + return NULL; + + json = json_encode(&asn_DEF_ContentInfo, ci); + + ASN_STRUCT_FREE(asn_DEF_ContentInfo, ci); + return json; + } + + static int + __print_file(void) + { + BIO *bio; + unsigned char hdrbuf[HDRSIZE]; + json_t *json = NULL; + int error; + + bio = filename2bio(config_get_payload()); + if (bio == NULL) + return pr_op_err("BIO_new_*() returned NULL."); + + switch (guess_file_type(&bio, hdrbuf)) { + case FT_UNK: + BIO_free_all(bio); + return pr_op_err("Unrecognized file type."); + + case FT_ROA: + case FT_MFT: + case FT_GBR: + json = asn1c2json(bio); + break; + case FT_CER: + json = Certificate_bio2json(bio); + break; + case FT_CRL: + json = CRL_bio2json(bio); + break; + } + + BIO_free_all(bio); + if (json == NULL) + return pr_op_err("Unable to parse."); + + errno = 0; + if (json_dumpf(json, stdout, JSON_INDENT(4)) < 0) { + error = errno; + if (error) + pr_op_err("Error writing JSON to file: %s", strerror(error)); + else + pr_op_err("Unknown error writing JSON to file."); + + } else { + error = 0; + printf("\n"); + } + + json_decref(json); + return error; + } + + int + print_file(void) + { + int error; + + error = bioseq_setup(); + if (error) + return error; + + error = __print_file(); + + bioseq_teardown(); + return error; + } diff --cc src/rrdp.c index f99849d0,4e2a74eb..52c218e5 --- a/src/rrdp.c +++ b/src/rrdp.c @@@ -1182,205 -1091,4 +1182,205 @@@ end fnstack_pop(); return error; } + +#define TAGNAME_SESSION "session_id" +#define TAGNAME_SERIAL "serial" +#define TAGNAME_DELTAS "deltas" + +/* binary to char */ +static char +hash_b2c(unsigned char bin) +{ + bin &= 0xF; + return (bin < 10) ? (bin + '0') : (bin + 'a' - 10); +} + +json_t * +rrdp_notif2json(struct cachefile_notification *notif) +{ + json_t *json; + json_t *deltas; + char hash_str[2 * RRDP_HASH_LEN + 1]; + struct rrdp_hash *hash; + size_t i; + + if (notif == NULL) + return NULL; + + json = json_object(); + if (json == NULL) + enomem_panic(); + + if (json_add_str(json, TAGNAME_SESSION, notif->session.session_id)) + goto fail; + if (json_add_str(json, TAGNAME_SERIAL, notif->session.serial.str)) + goto fail; + + if (STAILQ_EMPTY(¬if->delta_hashes)) + return json; /* Happy path, but unlikely. */ + + deltas = json_array(); + if (deltas == NULL) + enomem_panic(); - if (json_add_obj(json, TAGNAME_DELTAS, deltas)) ++ if (json_object_add(json, TAGNAME_DELTAS, deltas)) + goto fail; + + hash_str[2 * RRDP_HASH_LEN] = '\0'; + STAILQ_FOREACH(hash, ¬if->delta_hashes, hook) { + for (i = 0; i < RRDP_HASH_LEN; i++) { + hash_str[2 * i ] = hash_b2c(hash->bytes[i] >> 4); + hash_str[2 * i + 1] = hash_b2c(hash->bytes[i] ); + } + if (json_array_append(deltas, json_string(hash_str))) + goto fail; + } + + return json; + +fail: + json_decref(json); + return NULL; +} + +static char +hash_c2b(char chara) +{ + if ('a' <= chara && chara <= 'f') + return chara - 'a' + 10; + if ('A' <= chara && chara <= 'F') + return chara - 'A' + 10; + if ('0' <= chara && chara <= '9') + return chara - '0'; + return -1; +} + +static int +json2dh(json_t *json, struct rrdp_hash **result) +{ + char const *src; + size_t srclen; + struct rrdp_hash *dst; + char digit; + size_t i; + + src = json_string_value(json); + if (src == NULL) + return pr_op_err("Hash is not a string."); + + srclen = strlen(src); + if (srclen != 2 * RRDP_HASH_LEN) + return pr_op_err("Hash is not %d characters long.", 2 * RRDP_HASH_LEN); + + dst = pmalloc(sizeof(struct rrdp_hash)); + for (i = 0; i < RRDP_HASH_LEN; i++) { + digit = hash_c2b(src[2 * i]); + if (digit == -1) + goto bad_char; + dst->bytes[i] = digit << 4; + digit = hash_c2b(src[2 * i + 1]); + if (digit == -1) + goto bad_char; + dst->bytes[i] |= digit; + } + + *result = dst; + return 0; + +bad_char: + free(dst); + return pr_op_err("Invalid characters in hash: %c%c", src[2 * i], src[2 * i] + 1); +} + +static void +clear_delta_hashes(struct cachefile_notification *notif) +{ + struct rrdp_hash *hash; + + while (!STAILQ_EMPTY(¬if->delta_hashes)) { + hash = STAILQ_FIRST(¬if->delta_hashes); + STAILQ_REMOVE_HEAD(¬if->delta_hashes, hook); + free(hash); + } +} + +int +rrdp_json2notif(json_t *json, struct cachefile_notification **result) +{ + struct cachefile_notification *notif; + char const *str; + json_t *jdeltas; + size_t d, dn; + struct rrdp_hash *hash; + int error; + + notif = pzalloc(sizeof(struct cachefile_notification)); + STAILQ_INIT(¬if->delta_hashes); + + error = json_get_str(json, TAGNAME_SESSION, &str); + if (error) { + if (error > 0) + pr_op_err("Node is missing the '" TAGNAME_SESSION "' tag."); + goto revert_notif; + } + notif->session.session_id = pstrdup(str); + + error = json_get_str(json, TAGNAME_SERIAL, &str); + if (error) { + if (error > 0) + pr_op_err("Node is missing the '" TAGNAME_SERIAL "' tag."); + goto revert_session; + } + notif->session.serial.str = pstrdup(str); + + notif->session.serial.num = BN_create(); + if (!BN_dec2bn(¬if->session.serial.num, notif->session.serial.str)) { + error = pr_op_err("Not a serial number: %s", notif->session.serial.str); + goto revert_serial; + } + + error = json_get_array(json, TAGNAME_DELTAS, &jdeltas); + if (error) { + if (error > 0) + goto success; + goto revert_serial; + } + + dn = json_array_size(jdeltas); + if (dn == 0) + goto success; + if (dn > config_get_rrdp_delta_threshold()) + dn = config_get_rrdp_delta_threshold(); + + for (d = 0; d < dn; d++) { + error = json2dh(json_array_get(jdeltas, d), &hash); + if (error) + goto revert_deltas; + STAILQ_INSERT_TAIL(¬if->delta_hashes, hash, hook); + } + +success: + *result = notif; + return 0; + +revert_deltas: + clear_delta_hashes(notif); +revert_serial: + BN_free(notif->session.serial.num); + free(notif->session.serial.str); +revert_session: + free(notif->session.session_id); +revert_notif: + free(notif); + return error; +} + +void +rrdp_notif_free(struct cachefile_notification *notif) +{ + if (notif == NULL) + return; + + session_cleanup(¬if->session); + clear_delta_hashes(notif); + free(notif); +} diff --cc test/Makefile.am index a6e9971e,f1d7af94..7419fdb3 --- a/test/Makefile.am +++ b/test/Makefile.am @@@ -20,14 -20,14 +20,15 @@@ AM_CFLAGS += -I../src -DUNIT_TESTING ${ # autotools will even reprehend us if we declare it. Therefore, I came up with # "my" own "ldadd". Unlike AM_CFLAGS, it needs to be manually added to every # target. - MY_LDADD = ${CHECK_LIBS} + MY_LDADD = ${CHECK_LIBS} ${JANSSON_LIBS} check_PROGRAMS = address.test +check_PROGRAMS += base64.test check_PROGRAMS += cache.test check_PROGRAMS += db_table.test check_PROGRAMS += deltas_array.test +check_PROGRAMS += hash.test + check_PROGRAMS += json.test -check_PROGRAMS += line_file.test check_PROGRAMS += pb.test check_PROGRAMS += pdu_handler.test check_PROGRAMS += pdu_stream.test @@@ -57,9 -54,12 +58,12 @@@ db_table_test_LDADD = ${MY_LDADD deltas_array_test_SOURCES = rtr/db/deltas_array_test.c deltas_array_test_LDADD = ${MY_LDADD} +hash_test_SOURCES = crypto/hash_test.c +hash_test_LDADD = ${MY_LDADD} + + json_test_SOURCES = json_util_test.c + json_test_LDADD = ${MY_LDADD} + -line_file_test_SOURCES = line_file_test.c -line_file_test_LDADD = ${MY_LDADD} - pb_test_SOURCES = data_structure/path_builder_test.c pb_test_LDADD = ${MY_LDADD} @@@ -97,10 -97,9 +101,7 @@@ xml_test_SOURCES = xml_test. xml_test_LDADD = ${MY_LDADD} ${XML2_LIBS} EXTRA_DIST = mock.c mock.h -EXTRA_DIST += line_file/core.txt -EXTRA_DIST += line_file/empty.txt -EXTRA_DIST += line_file/error.txt +EXTRA_DIST += resources/lorem-ipsum.txt - EXTRA_DIST += resources/line_file/core.txt - EXTRA_DIST += resources/line_file/empty.txt - EXTRA_DIST += resources/line_file/error.txt EXTRA_DIST += rtr/db/rtr_db_mock.c EXTRA_DIST += tal/lacnic.tal EXTRA_DIST += xml/notification.xml diff --cc test/cache/local_cache_test.c index 824818fb,7d175d06..92d1736a --- a/test/cache/local_cache_test.c +++ b/test/cache/local_cache_test.c @@@ -57,17 -57,8 +57,17 @@@ file_rm_rf(char const *file return ENOENT; } +int +file_rm_f(char const *file) +{ + file_rm_rf(file); + return 0; +} + +MOCK_ABORT_INT(file_get_mtim, char const *file, time_t *ims) + static int - pretend_download(struct rpki_uri *uri) + pretend_download(char const *local) { struct downloaded_path *dl;