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
}
char *
- pstrndup(char const *s, size_t size)
-pstrndup(const char *s, size_t n)
++pstrndup(char const *s, size_t n)
{
char *result;
/* 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_ */
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;
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;
}
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);
#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
}
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");
}
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_ */
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)
{
};
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 *);
/*
#include "http/http.h"
-#include <curl/curl.h>
-
#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;
#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
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_ */
#include <errno.h>
#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
}
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
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 */
--- /dev/null
- error = uri_create(&uri, tal, UT_RSYNC, false, NULL, src);
+ #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;
+
++ /*
++ * 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;
+ }
fnstack_pop();
return error;
}
- if (json_add_obj(json, TAGNAME_DELTAS, deltas))
+
+#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_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);
+}
# 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 += line_file.test
+check_PROGRAMS += hash.test
+ check_PROGRAMS += json.test
check_PROGRAMS += pb.test
check_PROGRAMS += pdu_handler.test
check_PROGRAMS += pdu_stream.test
deltas_array_test_SOURCES = rtr/db/deltas_array_test.c
deltas_array_test_LDADD = ${MY_LDADD}
-line_file_test_SOURCES = line_file_test.c
-line_file_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}
+
pb_test_SOURCES = data_structure/path_builder_test.c
pb_test_LDADD = ${MY_LDADD}
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
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;