]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Rewrite the base64 decoder
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Sat, 24 Feb 2024 01:19:34 +0000 (19:19 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Sat, 24 Feb 2024 01:19:34 +0000 (19:19 -0600)
The old decoding was done using libcrypto's BIO API, even though the
code didn't need to perform any stream chaining. (Which seems to be the
whole point.)

Trying to figure out why it wasn't properly erroring on invalid base64,
I eventually realized I could dramatically simplify everything by
shifting the API layer. I'm now using `EVP_DecodeUpdate()` and friends.
It's so much cleaner and more correct now; it's incredible.

This had a cascade effect. The complexity of the base64.h API also
collapsed, which resulted in massive refactors on the callers.

Also adds some unit tests for base64 and TAL.

30 files changed:
src/Makefile.am
src/asn1/content_info.c
src/crypto/base64.c
src/crypto/base64.h
src/file.c
src/file.h
src/line_file.c [deleted file]
src/line_file.h [deleted file]
src/object/tal.c
src/rrdp.c
src/slurm/slurm_parser.c
test/Makefile.am
test/crypto/base64_test.c [new file with mode: 0644]
test/crypto/hash_test.c
test/line_file_test.c [deleted file]
test/resources/line_file/core.txt [deleted file]
test/resources/line_file/empty.txt [deleted file]
test/resources/line_file/error.txt [deleted file]
test/resources/tal/1url-crlf.tal [moved from test/tal/lacnic.tal with 81% similarity]
test/resources/tal/1url-lf.tal [new file with mode: 0644]
test/resources/tal/4urls-crlf.tal [new file with mode: 0644]
test/resources/tal/4urls-lf-comment-space-1.tal [new file with mode: 0644]
test/resources/tal/4urls-lf-comment-space-2.tal [new file with mode: 0644]
test/resources/tal/4urls-lf-comment-space-3.tal [new file with mode: 0644]
test/resources/tal/4urls-lf-comment-space-4.tal [new file with mode: 0644]
test/resources/tal/4urls-lf-comment-utf8.tal [new file with mode: 0644]
test/resources/tal/4urls-lf-comment.tal [new file with mode: 0644]
test/resources/tal/4urls-lf.tal [new file with mode: 0644]
test/rrdp_test.c
test/tal_test.c

index 25ee95af6cea9129796a6aa43a319cf51ff1ba1e..9706d5a91dd4b102896a37b3b6f368d58128890f 100644 (file)
@@ -18,7 +18,6 @@ fort_SOURCES += extension.h extension.c
 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 += log.h log.c
 fort_SOURCES += nid.h nid.c
 fort_SOURCES += output_printer.h output_printer.c
index 1e3e489f819ad083832257d16dde804c988f0033..a2e0f95c3a20336c0dadf86f06cad81d0f4fe058 100644 (file)
@@ -54,7 +54,7 @@ content_info_load(struct rpki_uri *uri, struct ContentInfo **result)
        struct file_contents fc;
        int error;
 
-       error = file_load(uri_get_local(uri), &fc);
+       error = file_load(uri_get_local(uri), &fc, true);
        if (error)
                return error;
 
index 096a44f5567d8d6cfaab8401428d9e674604a1ec..c6fb61705e9281f73936d79e718c5e101a81c05a 100644 (file)
 #include <openssl/buffer.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
+
 #include "alloc.h"
+#include "log.h"
 
-/*
- * Reference: openbsd/src/usr.bin/openssl/enc.c
- *
- * @in: The BIO that will stream the base64 encoded string you want to decode.
- * @out: Buffer where this function will write the decoded string.
- * @has_nl: Encoded string has newline char?
- * @out_len: Total allocated size of @out. It's supposed to be the result of
- *     EVP_DECODE_LENGTH(<size of the encoded string>).
- * @out_written: This function will write the actual number of decoded bytes
- *     here.
- *
- * Returns true on success, false on failure. If the caller wants to print
- * errors, do it with the crypto functions. If not, remember to clean
- * libcrypto's error queue somehow.
- *
- * TODO (fine) Callers always do a bunch of boilerplate; refactor.
- */
+/* Simple decode base64 string. Returns true on success, false on failure. */
 bool
-base64_decode(BIO *in, unsigned char *out, bool has_nl, size_t out_len,
-    size_t *out_written)
+base64_decode(char *in, size_t in_len, unsigned char **out, size_t *out_len)
 {
-       BIO *b64;
-       size_t offset = 0;
-       int written = 0;
-       bool success = false;
+       unsigned char *result;
+       EVP_ENCODE_CTX *ctx;
+       int outl;
+       int status;
 
-       /*
-        * BTW: The libcrypto API is perplexing.
-        * Peeking at the error stack is the only way I found to figure out
-        * whether some of the functions error'd.
-        * But since it's not documented that it's supposed to work this way,
-        * there's no guarantee that it will catch all errors.
-        * But it will have to do. It's better than nothing.
-        */
-
-       /* Assume that the caller took care of handling any previous errors. */
-       ERR_clear_error();
+       if (in_len == 0)
+               in_len = strlen(in);
 
        /*
-        * BIO_f_base64() cannot fail because it's dead-simple by definition.
-        * BIO_new() can, and it will lead to NULL. But only *some* errors will
-        * populate the error stack.
+        * Will usually allocate more because of the newlines,
+        * but I'm at peace with it.
         */
-       b64 = BIO_new(BIO_f_base64());
-       if (b64 == NULL)
-               return false;
+       result = pmalloc(EVP_DECODE_LENGTH(in_len));
 
-       /*
-        * BIO_push() can technically fail through BIO_ctrl(), but it ignores
-        * the error. This will not cause it to revert the push, so we have to
-        * do it ourselves.
-        *
-        * BTW: I'm assigning the result of BIO_push() to @in (instead of @b64
-        * or, more logically, throwing it away) because the sample reference in
-        * enc.c does it that way.
-        * But the writer of enc.c probably overcomplicated things.
-        * It shouldn't make a difference. We don't need @in anymore; just
-        * assume both @b64 and @in now point to the same BIO, which is @b64.
-        */
-       in = BIO_push(b64, in);
+       ctx = EVP_ENCODE_CTX_new();
+       if (ctx == NULL)
+               enomem_panic();
 
-       /*
-        * Should we ignore this error? BIO_ctrl(BIO_CTRL_PUSH) performs some
-        * "internal, used to signify change" thing, whose importance is
-        * undefined due to BIO_ctrl()'s callback spaghetti.
-        * Let's be strict, I guess.
-        */
-       if (ERR_peek_last_error() != 0)
-               goto end;
+       EVP_DecodeInit(ctx);
 
-       if (!has_nl)
-               BIO_set_flags(in, BIO_FLAGS_BASE64_NO_NL); /* Cannot fail */
+       status = EVP_DecodeUpdate(ctx, result, &outl, (unsigned char *)in, in_len);
+       if (status == -1)
+               return false;
 
-       do {
-               /*
-                * Do not move this after BIO_read().
-                * BIO_read() can return negative, which does not necessarily
-                * imply error, and which ruins the counter.
-                */
-               offset += written;
-               written = BIO_read(in, out + offset, out_len - offset);
-       } while (written > 0);
-
-       /* BIO_read() can fail. It does not return status. */
-       if (ERR_peek_last_error() != 0)
-               goto end;
-
-       *out_written = offset;
-       success = true;
-
-end:
-       /*
-        * BIO_pop() can also fail due to BIO_ctrl(), but we will ignore this
-        * because whatever "signify change" crap happens, it can't possibly be
-        * damaging enough to prevent us from releasing b64. I hope.
-        */
-       BIO_pop(b64);
-       /* Returns 0 on failure, but that's only if b64 is NULL. Meaningless. */
-       BIO_free(b64);
+       *out_len = outl;
 
-       return success;
+       status = EVP_DecodeFinal(ctx, result + outl, &outl);
+       if (status != 1)
+               return false;
+
+       *out = result;
+       *out_len += outl;
+       return true;
 }
 
 /*
@@ -119,10 +57,11 @@ bool
 base64url_decode(char const *str_encoded, unsigned char **result,
     size_t *result_len)
 {
-       BIO *encoded; /* base64 encoded. */
        char *str_copy;
-       size_t encoded_len, alloc_size, dec_len;
-       int pad, i;
+       size_t encoded_len;
+       size_t pad;
+       size_t i;
+       bool success;
 
        /*
         * Apparently there isn't a base64url decoder, and there isn't
@@ -152,31 +91,10 @@ base64url_decode(char const *str_encoded, unsigned char **result,
        }
 
        /* Now decode as regular base64 */
-       encoded =  BIO_new_mem_buf(str_copy, -1);
-       if (encoded == NULL)
-               goto free_copy;
-
-       alloc_size = EVP_DECODE_LENGTH(strlen(str_copy));
-       *result = pzalloc(alloc_size + 1);
-
-       if (!base64_decode(encoded, *result, false, alloc_size, &dec_len))
-               goto free_all;
-
-       if (dec_len == 0)
-               goto free_all;
-
-       *result_len = dec_len;
+       success = base64_decode(str_copy, encoded_len + pad, result, result_len);
 
        free(str_copy);
-       BIO_free(encoded);
-       return true;
-
-free_all:
-       free(*result);
-       BIO_free(encoded);
-free_copy:
-       free(str_copy);
-       return false;
+       return success;
 }
 
 static char *
index 20e78276b4b8450f0e4790630fc3654e39520e1e..5dbae8f875da25ddffa7c72b188e5f676a331314 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef SRC_BASE64_H_
 #define SRC_BASE64_H_
 
-#include <openssl/bio.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
@@ -11,9 +10,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-bool base64_decode(BIO *, unsigned char *, bool, size_t, size_t *);
-bool base64url_decode(char const *, unsigned char **, size_t *);
+bool base64_decode(char *, size_t, unsigned char **, size_t *);
 
+bool base64url_decode(char const *, unsigned char **, size_t *);
 bool base64url_encode(unsigned char const *, int, char **);
 
 #endif /* SRC_BASE64_H_ */
index 40a2da8270f6a6efcaa5e04e999dea5f57ff86bc..ad8bbda99122febed133e49afea82884f21186f1 100644 (file)
@@ -64,8 +64,11 @@ file_close(FILE *file)
                pr_val_err("fclose() failed: %s", strerror(errno));
 }
 
+/*
+ * If !is_binary, will append a null character. That's all.
+ */
 int
-file_load(char const *file_name, struct file_contents *fc)
+file_load(char const *file_name, struct file_contents *fc, bool is_binary)
 {
        FILE *file;
        struct stat stat;
@@ -77,7 +80,10 @@ file_load(char const *file_name, struct file_contents *fc)
                return error;
 
        fc->buffer_size = stat.st_size;
-       fc->buffer = pmalloc(fc->buffer_size);
+       fc->buffer = pmalloc(fc->buffer_size + !is_binary);
+
+       if (!is_binary)
+               fc->buffer[stat.st_size] = '\0';
 
        fread_result = fread(fc->buffer, 1, fc->buffer_size, file);
        if (fread_result < fc->buffer_size) {
index cdcd01167ab880bc3b004359a06ce48fc6748da3..67f95b06c0503033ab7f41f98541b9c6799d45b8 100644 (file)
@@ -27,7 +27,7 @@ int file_open(char const *, FILE **, struct stat *);
 int file_write(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 *);
diff --git a/src/line_file.c b/src/line_file.c
deleted file mode 100644 (file)
index 8dc44c4..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-#include "line_file.h"
-
-#include "alloc.h"
-#include "file.h"
-#include "log.h"
-
-struct line_file {
-       FILE *file;
-       const char *file_name;
-       size_t offset;
-};
-
-/*
- * @file_name is expected to outlive the lfile.
- */
-int
-lfile_open(const char *file_name, struct line_file **result)
-{
-       struct line_file *lfile;
-       int error;
-
-       lfile = pmalloc(sizeof(struct line_file));
-
-       lfile->file = fopen(file_name, "r");
-       if (lfile->file == NULL) {
-               error = errno;
-               free(lfile);
-               return error;
-       }
-       lfile->file_name = file_name;
-       lfile->offset = 0;
-
-       *result = lfile;
-       return 0;
-}
-
-void
-lfile_close(struct line_file *lf)
-{
-       file_close(lf->file);
-       free(lf);
-}
-
-/*
- * On success, places the string in *result.
- * On failure, returns error code.
- * On EOF reached, returns zero but nullifies result.
- *
- * @result is allocated in the heap.
- */
-int
-lfile_read(struct line_file *lfile, char **result)
-{
-       char *string;
-       size_t alloc_len;
-       ssize_t len;
-       ssize_t i;
-       int error;
-
-       /*
-        * Note to myself:
-        *
-        * getline() is very convoluted. I really don't like it. I'm actually
-        * considering getting rid of it and pulling off something that doesn't
-        * seem like it was designed by an alien, but it doesn't warrant going
-        * that far yet. Do not read its Linux man page; it didn't answer my
-        * questions. Go straight to POSIX instead.
-        *
-        * - If the file is empty, or all that's left is an empty line, it
-        *   (confusingly) returns -1. errno will be 0, feof() should return
-        *   1, ferror() should return 0.
-        * - It includes the newline in the result, which is puzzling. You will
-        *   want to remove it, BUT DON'T SWEAT IT IF IT'S NOT THERE, because
-        *   the last line of the file might not be newline-terminated.
-        * - The string WILL be NULL-terminated, but the NULL chara will not be
-        *   included in the returned length. BUT IT'S THERE. Don't worry about
-        *   writing past the allocated space on the last line.
-        * - Newline is `\n` according to POSIX, which is good, because RFC 8630
-        *   agrees. You will have to worry about `\r`, though.
-        *
-        * Also, the Linux man page claims the following:
-        *
-        *    [The out] buffer should be freed by the user program even if
-        *    getline() failed.
-        *
-        * This... does not exist in the POSIX spec. But it does make sense
-        * because getline is normally meant to be used repeatedly with a
-        * recycled buffer. (free() is a no-op if its argument is NULL so go
-        * nuts.)
-        *
-        * Update: If you remove getline(), consider downgrading _POSIX_C_SOURCE
-        * to 200112L. (And _XOPEN_SOURCE to 600.)
-        */
-
-       string = NULL;
-       alloc_len = 0;
-       len = getline(&string, &alloc_len, lfile->file);
-
-       if (len == -1) {
-               error = errno;
-               free(string);
-               *result = NULL;
-               if (ferror(lfile->file)) {
-                       pr_op_err("Error while reading file: %s",
-                           strerror(error));
-                       return error;
-               }
-               if (feof(lfile->file))
-                       return 0;
-               pr_crit("Supposedly unreachable code reached. ferror:%d feof:%d",
-                   ferror(lfile->file), feof(lfile->file));
-       }
-
-       lfile->offset += len;
-
-       /*
-        * Make sure that strlen() matches len.
-        * We should make the best out of the fact that we didn't use fgets(),
-        * after all.
-        */
-       for (i = 0; i < len; i++) {
-               if (string[i] == '\0') {
-                       pr_op_err("File '%s' has an illegal null character in its body. Please remove it.",
-                           lfile_name(lfile));
-                       free(string);
-                       return -EINVAL;
-               }
-       }
-
-       if (len >= 2) {
-               if (string[len - 2] == '\r' && string[len - 1] == '\n')
-                       string[len - 2] = '\0';
-       }
-       if (len >= 1) {
-               if (string[len - 1] == '\n')
-                       string[len - 1] = '\0';
-       }
-
-       *result = string;
-       return 0;
-}
-
-FILE *
-lfile_fd(struct line_file *lfile)
-{
-       return lfile->file;
-}
-
-const char *
-lfile_name(struct line_file *lfile)
-{
-       return lfile->file_name;
-}
-
-size_t
-lfile_offset(struct line_file *lfile)
-{
-       return lfile->offset;
-}
diff --git a/src/line_file.h b/src/line_file.h
deleted file mode 100644 (file)
index 4152550..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef LINE_FILE_H_
-#define LINE_FILE_H_
-
-/*
- * A "line file" is a text file that you want to read line-by-line.
- *
- * Lines are terminated by either CRLF or LF.
- * (...which is the same as saying "lines are terminated by LF.")
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-struct line_file;
-
-int lfile_open(const char *, struct line_file **);
-void lfile_close(struct line_file *lf);
-
-int lfile_read(struct line_file *, char **);
-
-FILE *lfile_fd(struct line_file *);
-const char *lfile_name(struct line_file *);
-size_t lfile_offset(struct line_file *);
-
-#endif /* LINE_FILE_H_ */
index ebc588f40f2c288d75711a116081fd4d1d1a8b55..14e62c5b5a4447ab2fb693561ffdc08493ed1cea 100644 (file)
@@ -1,5 +1,6 @@
 #include "object/tal.h"
 
+#include <ctype.h>
 #include <errno.h>
 #include <openssl/evp.h>
 #include <sys/queue.h>
@@ -9,7 +10,7 @@
 #include "cert_stack.h"
 #include "common.h"
 #include "config.h"
-#include "line_file.h"
+#include "file.h"
 #include "log.h"
 #include "state.h"
 #include "thread_var.h"
@@ -47,228 +48,94 @@ struct handle_tal_args {
        struct db_table *db;
 };
 
+static char *
+find_newline(char *str)
+{
+       for (; true; str++) {
+               if (str[0] == '\0')
+                       return NULL;
+               if (str[0] == '\n')
+                       return str;
+               if (str[0] == '\r' && str[1] == '\n')
+                       return str;
+       }
+}
+
+static bool
+is_blank(char const *str)
+{
+       for (; str[0] != '\0'; str++)
+               if (!isspace(str[0]))
+                       return false;
+       return true;
+}
+
 static int
-add_uri(struct uri_list *uris, char const *tal, char *uri)
+add_uri(struct tal *tal, char *uri)
 {
        struct rpki_uri *new = NULL;
        int error;
 
        if (str_starts_with(uri, "rsync://"))
-               error = uri_create(&new, tal, UT_RSYNC, false, NULL, uri);
+               error = uri_create(&new, tal->file_name, UT_RSYNC, false, NULL, uri);
        else if (str_starts_with(uri, "https://"))
-               error = uri_create(&new, tal, UT_HTTPS, false, NULL, uri);
+               error = uri_create(&new, tal->file_name, UT_HTTPS, false, NULL, uri);
        else
                return pr_op_err("TAL has non-RSYNC/HTTPS URI: %s", uri);
        if (error)
                return error;
 
-       uris_add(uris, new);
+       uris_add(&tal->uris, new);
        return 0;
 }
 
 static int
-read_uris(struct line_file *lfile, char const *tal, struct uri_list *uris)
+read_content(char *fc /* File Content */, struct tal *tal)
 {
-       char *uri;
+       char *nl; /* New Line */
+       bool cr; /* Carriage return */
        int error;
 
-       error = lfile_read(lfile, &uri);
-       if (error)
-               return error;
-
-       if (uri == NULL)
-               return pr_op_err("TAL file is empty.");
-       if (strcmp(uri, "") == 0) {
-               free(uri);
-               return pr_op_err("There's no URI in the first line of the TAL.");
-       } else if (strncmp(uri, "#", 1) == 0) {
-               /* More comments expected, or an URI */
-               do {
-                       free(uri); /* Ignore the comment */
-                       error = lfile_read(lfile, &uri);
-                       if (error)
-                               return error;
-                       if (uri == NULL)
-                               return pr_op_err("TAL file ended prematurely. (Expected more comments or an URI list.)");
-                       if (strcmp(uri, "") == 0) {
-                               free(uri);
-                               return pr_op_err("TAL file comments syntax error. (Expected more comments or an URI list.)");
-                       }
-                       /* Not a comment, probably the URI(s) */
-                       if (strncmp(uri, "#", 1) != 0)
-                               break;
-               } while (true);
+       /* Comment section */
+       while (fc[0] == '#') {
+               nl = strchr(fc, '\n');
+               if (!nl)
+                       goto premature;
+               fc = nl + 1;
        }
 
+       /* URI section */
        do {
-               error = add_uri(uris, tal, uri);
-               free(uri); /* Won't be needed anymore */
-               if (error)
-                       return error;
+               nl = find_newline(fc);
+               if (!nl)
+                       goto premature;
 
-               error = lfile_read(lfile, &uri);
+               cr = (nl[0] == '\r');
+               nl[0] = '\0';
+               if (is_blank(fc))
+                       break;
+
+               error = add_uri(tal, fc);
                if (error)
                        return error;
 
-               if (uri == NULL)
-                       return pr_op_err("TAL file ended prematurely. (Expected URI list, blank line and public key.)");
-               if (strcmp(uri, "") == 0) {
-                       free(uri);
-                       return 0; /* Happy path */
-               }
+               fc = nl + cr + 1;
+               if (*fc == '\0')
+                       return pr_op_err("The TAL seems to be missing the public key.");
        } while (true);
-}
-
-static size_t
-get_spki_orig_size(struct line_file *lfile)
-{
-       struct stat st;
-       size_t result;
-
-       stat(lfile_name(lfile), &st);
-       result = st.st_size - lfile_offset(lfile);
-       return result;
-}
-
-/*
- * Will usually allocate slightly more because of the newlines, but I'm fine
- * with it.
- */
-static size_t
-get_spki_alloc_size(struct line_file *lfile)
-{
-       return EVP_DECODE_LENGTH(get_spki_orig_size(lfile));
-}
-
-static char *
-locate_char(char *str, size_t len, char find)
-{
-       size_t i;
-
-       for (i = 0; i < len; i++)
-               if (str[i] == find)
-                       return str + i;
-       return NULL;
-}
-
-/*
- * Get the base64 chars from @lfile and allocate to @out with lines no greater
- * than 65 chars (including line feed).
- *
- * Why? LibreSSL doesn't like lines greater than 80 chars, so use a common
- * length per line.
- */
-static int
-base64_sanitize(struct line_file *lfile, char **out)
-{
-#define BUF_SIZE 65
-       FILE *fd;
-       char *buf, *result, *eol;
-       size_t original_size, new_size;
-       size_t fread_result, offset;
-       int error;
 
-       /*
-        * lfile_read() isn't called since the lines aren't returned as needed
-        * "sanitized" (a.k.a. each line with a desired length)
-        */
-       original_size = get_spki_orig_size(lfile);
-       new_size = original_size + (original_size / BUF_SIZE);
-       result = pmalloc(new_size + 1);
-       buf = pmalloc(BUF_SIZE);
-
-       fd = lfile_fd(lfile);
-       offset = 0;
-       while ((fread_result = fread(buf, 1,
-           (original_size > BUF_SIZE) ? BUF_SIZE : original_size, fd)) > 0) {
-               error = ferror(lfile_fd(lfile));
-               if (error) {
-                       /*
-                        * The manpage doesn't say that the result is an error
-                        * code. It literally doesn't say how to get an error
-                        * code.
-                        */
-                       pr_op_err("File reading error. Presumably, the error message is '%s.'",
-                           strerror(error));
-                       goto free_result;
-               }
-
-               original_size -= fread_result;
-               eol = locate_char(buf, fread_result, '\n');
-               /* Larger than buffer length, add LF and copy last char */
-               if (eol == NULL) {
-                       memcpy(&result[offset], buf, fread_result - 1);
-                       offset += fread_result - 1;
-                       result[offset] = '\n';
-                       result[offset + 1] = buf[fread_result - 1];
-                       offset += 2;
-                       continue;
-               }
-               /* Copy till last LF */
-               memcpy(&result[offset], buf, eol - buf + 1);
-               offset += eol - buf + 1;
-               if (eol - buf + 1 < fread_result) {
-                       /* And add new line with remaining chars */
-                       memcpy(&result[offset], eol + 1,
-                           buf + fread_result - 1 - eol);
-                       offset += buf + fread_result -1 - eol;
-                       result[offset] = '\n';
-                       offset++;
-               }
-       }
-       /* Reallocate to exact size and add nul char */
-       if (offset != new_size)
-               result = prealloc(result, offset + 1);
-       free(buf);
-       result[offset] = '\0';
-
-       *out = result;
-       return 0;
-free_result:
-       free(buf);
-       free(result);
-       return error;
-#undef BUF_SIZE
-}
-
-static int
-read_spki(struct line_file *lfile, struct tal *tal)
-{
-       BIO *encoded; /* base64 encoded. */
-       char *tmp;
-       size_t size;
-       int error;
-
-       size = get_spki_alloc_size(lfile);
-       tal->spki = pmalloc(size);
-
-       tmp = NULL;
-       error = base64_sanitize(lfile, &tmp);
-       if (error)
-               goto revert_spki;
+       if (tal->uris.len == 0)
+               return pr_op_err("There seems to be an empty/blank line before the end of the URI section.");
 
-       encoded = BIO_new_mem_buf(tmp, -1);
-       if (encoded == NULL) {
-               error = op_crypto_err("BIO_new_mem_buf() returned NULL.");
-               goto revert_tmp;
-       }
-
-       if (!base64_decode(encoded, tal->spki, true, size, &tal->spki_len)) {
-               error = op_crypto_err("Cannot decode SPKI.");
-               goto revert_encoded;
-       }
+       /* subjectPublicKeyInfo section */
+       if (!base64_decode(nl + cr + 1, 0, &tal->spki, &tal->spki_len))
+               return pr_op_err("Cannot decode the public key.");
 
-       free(tmp);
-       BIO_free(encoded);
        return 0;
 
-revert_encoded:
-       BIO_free(encoded);
-revert_tmp:
-       free(tmp);
-revert_spki:
-       free(tal->spki);
-       return error;
+/* This label requires fc to make sense */
+premature:
+       return pr_op_err("The TAL seems to end prematurely at line '%s'.", fc);
 }
 
 /**
@@ -277,38 +144,29 @@ revert_spki:
 static int
 tal_init(struct tal *tal, char const *file_path)
 {
-       struct line_file *lfile;
        char const *file_name;
+       struct file_contents file;
        int error;
 
-       lfile = NULL; /* Warning shutupper */
-       error = lfile_open(file_path, &lfile);
-       if (error) {
-               pr_op_err("Error opening file '%s': %s", file_path,
-                   strerror(abs(error)));
+       error = file_load(file_path, &file, false);
+       if (error)
                return error;
-       }
 
        file_name = strrchr(file_path, '/');
        file_name = (file_name != NULL) ? (file_name + 1) : file_path;
-
        tal->file_name = file_name;
+
        uris_init(&tal->uris);
-       error = read_uris(lfile, file_name, &tal->uris);
-       if (error)
-               goto fail;
-       error = read_spki(lfile, tal);
-       if (error)
-               goto fail;
+       error = read_content((char *)file.buffer, tal);
+       if (error) {
+               uris_cleanup(&tal->uris);
+               goto end;
+       }
 
        tal->cache = cache_create(file_name);
 
-       lfile_close(lfile);
-       return 0;
-
-fail:
-       uris_cleanup(&tal->uris);
-       lfile_close(lfile);
+end:
+       file_free(&file);
        return error;
 }
 
index e285335aedb165c5334d501ea673dc567a23a5ba..e9c76ea95c5795c158a05b55ce7c40add2c05eef 100644 (file)
@@ -156,125 +156,6 @@ validate_hash(struct file_metadata *meta)
            meta->hash_len);
 }
 
-/* Left trim @from, setting the result at @result pointer */
-static int
-ltrim(char const *from, char const **result, size_t *result_size)
-{
-       char const *start;
-       size_t tmp_size;
-
-       start = from;
-       tmp_size = strlen(from);
-       while (isspace(*start)) {
-               start++;
-               tmp_size--;
-       }
-       if (*start == '\0')
-               return pr_val_err("Invalid base64 encoded string (seems to be empty or full of spaces).");
-
-       *result = start;
-       *result_size = tmp_size;
-       return 0;
-}
-
-/*
- * Get the base64 chars from @content and allocate to @out with lines no greater
- * than 65 chars (including line feed).
- *
- * Why? LibreSSL doesn't like lines greater than 80 chars, so use a common
- * length per line.
- */
-static int
-base64_sanitize(char const *content, char **out)
-{
-#define BUF_SIZE 65
-       char *result;
-       char const *tmp;
-       size_t original_size, new_size;
-       size_t offset, buf_len;
-       int error;
-
-       original_size = 0;
-       error = ltrim(content, &tmp, &original_size);
-       if (error)
-               return error;
-
-       if (original_size <= BUF_SIZE) {
-               *out = pstrdup(content);
-               return 0;
-       }
-
-       new_size = original_size + (original_size / BUF_SIZE);
-       result = pmalloc(new_size + 1);
-
-       offset = 0;
-       while (original_size > 0){
-               buf_len = original_size > BUF_SIZE ? BUF_SIZE : original_size;
-               memcpy(&result[offset], tmp, buf_len);
-               tmp += buf_len;
-               offset += buf_len;
-               original_size -= buf_len;
-
-               if (original_size <= 0)
-                       break;
-               result[offset] = '\n';
-               offset++;
-       }
-
-       /* Reallocate to exact size and add nul char */
-       if (offset != new_size + 1)
-               result = prealloc(result, offset + 1);
-
-       result[offset] = '\0';
-       *out = result;
-       return 0;
-#undef BUF_SIZE
-}
-
-static int
-base64_read(char const *content, unsigned char **out, size_t *out_len)
-{
-       BIO *encoded; /* base64 encoded. */
-       unsigned char *result;
-       char *sanitized;
-       size_t alloc_size;
-       size_t result_len;
-       int error;
-
-       sanitized = NULL;
-       error = base64_sanitize(content, &sanitized);
-       if (error)
-               return error;
-
-       encoded = BIO_new_mem_buf(sanitized, -1);
-       if (encoded == NULL) {
-               error = val_crypto_err("BIO_new_mem_buf() returned NULL");
-               goto release_sanitized;
-       }
-
-       alloc_size = EVP_DECODE_LENGTH(strlen(content));
-       result = pmalloc(alloc_size);
-
-       if (!base64_decode(encoded, result, true, alloc_size, &result_len)) {
-               error = val_crypto_err("Cannot decode publish tag's base64.");
-               goto release_result;
-       }
-
-       free(sanitized);
-       BIO_free(encoded);
-
-       (*out) = result;
-       (*out_len) = result_len;
-       return 0;
-
-release_result:
-       free(result);
-       BIO_free(encoded);
-release_sanitized:
-       free(sanitized);
-       return error;
-}
-
 static int
 parse_ulong(xmlTextReaderPtr reader, char const *attr, unsigned long *result)
 {
@@ -551,17 +432,17 @@ parse_publish(xmlTextReaderPtr reader, struct rpki_uri *notif,
        base64_str = parse_string(reader, NULL);
        if (base64_str == NULL)
                return -EINVAL;
-       error = base64_read((char const *)base64_str, &tag->content,
-           &tag->content_len);
+       if (!base64_decode((char *)base64_str, 0, &tag->content, &tag->content_len))
+               error = pr_val_err("Cannot decode publish tag's base64.");
        xmlFree(base64_str);
        if (error)
                return error;
 
        /* rfc8181#section-2.2 but considering optional hash */
        if (tag->meta.hash_len > 0)
-               error = validate_hash(&tag->meta);
+               return validate_hash(&tag->meta);
 
-       return error;
+       return 0;
 }
 
 static int
index 0c5ce11bf95c0005d91cf40520cead9b95739f35..4107a8722406bfb9e010b5edd4aef5c81c4ea018 100644 (file)
@@ -242,7 +242,7 @@ set_ski(json_t *object, bool is_assertion, struct slurm_bgpsec *result,
                return error;
 
        if (!base64url_decode(str_encoded, &result->ski, &ski_len))
-               return op_crypto_err("The " SKI " could not be decoded.");
+               return pr_op_err("The " SKI " could not be decoded.");
 
        /* Validate that's at least 20 octects long */
        if (ski_len != RK_SKI_LEN) {
@@ -312,7 +312,7 @@ set_router_pub_key(json_t *object, bool is_assertion,
                return error;
 
        if (!base64url_decode(encoded, &result->router_public_key, &spk_len))
-               return op_crypto_err("The " ROUTER_PUBLIC_KEY " could not be decoded.");
+               return pr_op_err("The " ROUTER_PUBLIC_KEY " could not be decoded.");
 
        /*
         * Validate that "is the full ASN.1 DER encoding of the
index b9d1a415178d26cf3bef5e67d8b857e6f3b5a877..a6e9971eec3f35af28966e0647cec81e2e8cb7d4 100644 (file)
@@ -23,11 +23,11 @@ AM_CFLAGS += -I../src -DUNIT_TESTING ${CHECK_CFLAGS} ${XML2_CFLAGS}
 MY_LDADD = ${CHECK_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 += line_file.test
 check_PROGRAMS += pb.test
 check_PROGRAMS += pdu_handler.test
 check_PROGRAMS += pdu_stream.test
@@ -45,6 +45,9 @@ TESTS = ${check_PROGRAMS}
 address_test_SOURCES = types/address_test.c
 address_test_LDADD = ${MY_LDADD}
 
+base64_test_SOURCES = crypto/base64_test.c
+base64_test_LDADD = ${MY_LDADD}
+
 cache_test_SOURCES = cache/local_cache_test.c
 cache_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
 
@@ -57,9 +60,6 @@ deltas_array_test_LDADD = ${MY_LDADD}
 hash_test_SOURCES = crypto/hash_test.c
 hash_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}
 
diff --git a/test/crypto/base64_test.c b/test/crypto/base64_test.c
new file mode 100644 (file)
index 0000000..dc40b33
--- /dev/null
@@ -0,0 +1,144 @@
+#include <check.h>
+
+#include "alloc.c"
+#include "common.h"
+#include "mock.c"
+#include "crypto/base64.c"
+
+static void
+ck_uchar_array(unsigned char *expected, size_t expected_len,
+    unsigned char *actual, size_t actual_len)
+{
+       size_t i;
+       ck_assert_uint_eq(expected_len, actual_len);
+       for (i = 0; i < expected_len; i++)
+               ck_assert_uint_eq(expected[i], actual[i]);
+}
+
+
+static void
+ck_char_array(char *expected, unsigned char *actual, size_t actual_len)
+{
+       ck_uchar_array((unsigned char *)expected, strlen(expected), actual,
+           actual_len);
+}
+
+START_TEST(test_base64_decode)
+{
+       static char *fort = "Fort";
+       static char *potato = "potato";
+       static unsigned char emojis[] = { 0xf0, 0x9f, 0x98, 0x80, 0xf0, 0x9f, 0xab, 0xa0 };
+       static char *lorem_ipsum = "Lorem ipsum dolor sit amet, consectetur "
+           "adipiscing elit. In a malesuada neque. Nunc efficitur at leo ac "
+           "feugiat. Aliquam velit erat, molestie nec nulla vitae, accumsan "
+           "accumsan ipsum. Nunc mattis quam sit amet turpis sollicitudin "
+           "fringilla. Sed id ante finibus, finibus erat in, vestibulum "
+           "lectus. Aenean sed massa ut lacus efficitur sollicitudin. Mauris "
+           "at imperdiet augue. Maecenas tempus ornare odio, egestas faucibus "
+           "ante commodo id. Morbi at urna nisl. Phasellus gravida felis non "
+           "erat ornare, at mattis magna venenatis. In ac lorem vel est "
+           "euismod finibus. Nam mauris felis, laoreet id eros sed, suscipit "
+           "gravida justo. In a dictum erat. Pellentesque habitant morbi "
+           "tristique senectus et netus et malesuada fames ac turpis egestas.";
+       unsigned char *dec;
+       size_t declen;
+
+       /* Empty */
+       ck_assert_int_eq(true, base64_decode("", 0, &dec, &declen));
+       ck_assert_uint_eq(0, declen);
+       free(dec);
+
+       /* With padding */
+       ck_assert_int_eq(true, base64_decode("Rm9ydA==", 0, &dec, &declen));
+       ck_char_array(fort, dec, declen);
+       free(dec);
+
+       /* No padding */
+       ck_assert_int_eq(true, base64_decode("cG90YXRv", 0, &dec, &declen));
+       ck_char_array(potato, dec, declen);
+       free(dec);
+
+       /* Not ASCII */
+       ck_assert_int_eq(true, base64_decode("8J+YgPCfq6A=", 0, &dec, &declen));
+       ck_uchar_array(emojis, ARRAY_LEN(emojis), dec, declen);
+       free(dec);
+
+       /* Illegal character */
+       ck_assert_int_eq(false, base64_decode("R!m9ydA=", 0, &dec, &declen));
+       /* Length not multiple of 4 */
+       ck_assert_int_eq(false, base64_decode("Rm9ydA=", 0, &dec, &declen));
+
+       /* Long, no whitespace */
+       ck_assert_int_eq(true, base64_decode("TG9yZW0gaXBzdW0gZG9sb3Igc2l0I"
+           "GFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gSW4gYSBtYWxlc"
+           "3VhZGEgbmVxdWUuIE51bmMgZWZmaWNpdHVyIGF0IGxlbyBhYyBmZXVnaWF0L"
+           "iBBbGlxdWFtIHZlbGl0IGVyYXQsIG1vbGVzdGllIG5lYyBudWxsYSB2aX"
+           "RhZSwgYWNjdW1zYW4gYWNjdW1zYW4gaXBzdW0uIE51bmMgbWF0dGlzIHF1YW0gc2"
+           "l0IGFtZXQgdHVycGlzIHNvbGxpY2l0dWRpbiBmcmluZ2lsbGEuIFNlZCBpZCBhbn"
+           "RlIGZpbmlidXMsIGZpbmlidXMgZXJhdCBpbiwgdmVzdGlidWx1bSBsZWN0dXMuIE"
+           "FlbmVhbiBzZWQgbWFzc2EgdXQgbGFjdXMgZWZmaWNpdHVyIHNvbGxpY2l0dWRpbi"
+           "4gTWF1cmlzIGF0IGltcGVyZGlldCBhdWd1ZS4gTWFlY2VuYXMgdGVtcHVzIG9ybm"
+           "FyZSBvZGlvLCBlZ2VzdGFzIGZhdWNpYnVzIGFudGUgY29tbW9kbyBpZC4gTW9yYm"
+           "kgYXQgdXJuYSBuaXNsLiBQaGFzZWxsdXMgZ3JhdmlkYSBmZWxpcyBub24gZXJhdC"
+           "Bvcm5hcmUsIGF0IG1hdHRpcyBtYWduYSB2ZW5lbmF0aXMuIEluIGFjIGxvcmVtIH"
+           "ZlbCBlc3QgZXVpc21vZCBmaW5pYnVzLiBOYW0gbWF1cmlzIGZlbGlzLCBsYW9yZW"
+           "V0IGlkIGVyb3Mgc2VkLCBzdXNjaXBpdCBncmF2aWRhIGp1c3RvLiBJbiBhIGRpY3"
+           "R1bSBlcmF0LiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIH"
+           "NlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZW"
+           "dlc3Rhcy4=", 0, &dec, &declen));
+       ck_char_array(lorem_ipsum, dec, declen);
+       free(dec);
+
+       /* Long, whitespace */
+       ck_assert_int_eq(true, base64_decode("  TG9yZW0gaXBzdW0gZG9sb3Igc2l0I\n"
+           "    GFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gSW4gYSBtYWxlc\n"
+           "3VhZGEgbmVxdWUuIE51bmMgZWZmaWNpdHVyIGF0IGxlbyBhYyBmZXVnaWF0L\n\n\n"
+           "iBBbGlxdWFtIHZlbGl0I       GVyYXQsIG1vbGVzdGllIG5lYyBudWxsYSB2aX\n"
+           "RhZSwgYWNjdW1zYW4gYWNjdW1zYW4gaXBzdW0uIE51bmMgbWF0dGlzIHF1YW0gc2\n"
+           "l0IGFtZXQgdHVycGlzIHNvbGxpY2l0dWRpbiBmcmluZ2lsbGEuIFNlZCBpZCBhbn\n"
+           "RlIGZpbmlidXMsIGZpbmlidXMgZXJhdCBpbiwgdmVzdGlidWx1bSBsZWN0dXMuIE\n"
+           "FlbmVhbiBzZWQgbWFzc2EgdXQgbGFjdXMgZWZmaWNpdHVyIHNvbGxpY2l0dWRpbi\n"
+           "4gTWF1cmlzIGF0IGltcGVyZGlldCBhdWd1ZS4gTWFlY2VuYXMgdGVtcHVzIG9ybm\n"
+           "FyZSBvZGlvLCBlZ2VzdGFzIGZhdWNpYnVzIGFudGUgY29tbW9kbyBpZC4gTW9yYm\n"
+           "kgYXQgdXJuYSBuaXNsLiBQaGFzZWxsdXMgZ3JhdmlkYSBmZWxpcyBub24gZXJhdC\n"
+           "Bvcm5hcmUsIGF0IG1hdHRpcyBtYWduYSB2ZW5lbmF0aXMuIEluIGFjIGxvcmVtIH\n"
+           "ZlbCBlc3QgZXVpc21vZCBmaW5pYnVzLiBOYW0gbWF1cmlzIGZlbGlzLCBsYW9yZW\n"
+           "V0IGlkIGVyb3Mgc2VkLCBzdXNjaXBpdCBncmF2aWRhIGp1c3RvLiBJbiBhIGRpY3\n"
+           "R1bSBlcmF0LiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkgdHJpc3RpcXVlIH\n"
+           "NlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBhYyB0dXJwaXMgZW\n"
+           "dlc3Rhcy4=\n\n\n", 0, &dec, &declen));
+       ck_char_array(lorem_ipsum, dec, declen);
+       free(dec);
+}
+END_TEST
+
+static Suite *
+pdu_suite(void)
+{
+       Suite *suite;
+       TCase *core;
+
+       core = tcase_create("core");
+       tcase_add_test(core, test_base64_decode);
+
+       suite = suite_create("base64");
+       suite_add_tcase(suite, core);
+       return suite;
+}
+
+int
+main(int argc, char **argv)
+{
+       Suite *suite;
+       SRunner *runner;
+       int tests_failed;
+
+       suite = pdu_suite();
+
+       runner = srunner_create(suite);
+       srunner_run_all(runner, CK_NORMAL);
+       tests_failed = srunner_ntests_failed(runner);
+       srunner_free(runner);
+
+       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
index ac284dbaa4de89f562affb400f670685b19dd2d7..cbd4a86c7bc15e85bdb706bf79051c2af24ef381 100644 (file)
@@ -42,7 +42,12 @@ START_TEST(test_hash)
 
        hash_setup();
 
+       uri.global = "https://example.com/resources/lorem-ipsum.txt";
+       uri.global_len = strlen(uri.global);
        uri.local = "resources/lorem-ipsum.txt";
+       uri.type = UT_HTTPS;
+       uri.is_notif = false;
+       uri.references = 1;
 
        ha = hash_get_sha1();
        ck_assert_uint_eq(20, hash_get_size(ha));
diff --git a/test/line_file_test.c b/test/line_file_test.c
deleted file mode 100644 (file)
index ff0d00c..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-#include <check.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include "alloc.c"
-#include "file.c"
-#include "line_file.c"
-#include "mock.c"
-
-START_TEST(file_line_normal)
-{
-       struct line_file *lfile;
-       char *string, *long_string;
-       char *SENTENCE;
-       size_t SENTENCE_LEN;
-       unsigned int i;
-
-       ck_assert_int_eq(lfile_open("resources/line_file/core.txt", &lfile), 0);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert_str_eq(string, "This is a normal line.");
-       free(string);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert_str_eq(string, "This is also a normal line, but the following one is empty.");
-       free(string);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert_str_eq(string, "");
-       free(string);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert_str_eq(string, "This one ends with \\r\\n.");
-       free(string);
-
-       SENTENCE = "This is a very long line. ";
-       SENTENCE_LEN = strlen(SENTENCE);
-       long_string = malloc(316 * SENTENCE_LEN + 1);
-       ck_assert(long_string);
-       for (i = 0; i < 316; i++)
-               strcpy(long_string + i * SENTENCE_LEN, SENTENCE);
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert_str_eq(string, long_string);
-       free(long_string);
-       free(string);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert_str_eq(string, "This line does not end with a newline.");
-       free(string);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert(string == NULL);
-
-       lfile_close(lfile);
-}
-END_TEST
-
-START_TEST(file_line_empty)
-{
-       struct line_file *lfile;
-       char *string;
-
-       ck_assert_int_eq(lfile_open("resources/line_file/empty.txt", &lfile), 0);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert(string == NULL);
-
-       lfile_close(lfile);
-}
-END_TEST
-
-START_TEST(file_line_null_chara)
-{
-       struct line_file *lfile;
-       char *string;
-
-       ck_assert_int_eq(lfile_open("resources/line_file/error.txt", &lfile), 0);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), 0);
-       ck_assert_str_eq(string, "This is a normal line.");
-       free(string);
-
-       ck_assert_int_eq(lfile_read(lfile, &string), -EINVAL);
-
-       lfile_close(lfile);
-}
-END_TEST
-
-static Suite *ghostbusters_suite(void)
-{
-       Suite *suite;
-       TCase *core, *limits, *errors;
-
-       core = tcase_create("Core");
-       tcase_add_test(core, file_line_normal);
-
-       limits = tcase_create("Limits");
-       tcase_add_test(limits, file_line_empty);
-
-       errors = tcase_create("Errors");
-       tcase_add_test(errors, file_line_null_chara);
-
-       suite = suite_create("lfile_read()");
-       suite_add_tcase(suite, core);
-       suite_add_tcase(suite, limits);
-       suite_add_tcase(suite, errors);
-       return suite;
-}
-
-int main(void)
-{
-       Suite *suite;
-       SRunner *runner;
-       int tests_failed;
-
-       suite = ghostbusters_suite();
-
-       runner = srunner_create(suite);
-       srunner_run_all(runner, CK_NORMAL);
-       tests_failed = srunner_ntests_failed(runner);
-       srunner_free(runner);
-
-       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/test/resources/line_file/core.txt b/test/resources/line_file/core.txt
deleted file mode 100644 (file)
index 211243d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a normal line.
-This is also a normal line, but the following one is empty.
-
-This one ends with \r\n.\r
-This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. This is a very long line. 
-This line does not end with a newline.
\ No newline at end of file
diff --git a/test/resources/line_file/empty.txt b/test/resources/line_file/empty.txt
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/test/resources/line_file/error.txt b/test/resources/line_file/error.txt
deleted file mode 100644 (file)
index f657862..0000000
Binary files a/test/resources/line_file/error.txt and /dev/null differ
similarity index 81%
rename from test/tal/lacnic.tal
rename to test/resources/tal/1url-crlf.tal
index 86e72d45b355060f7b3b30450ee2232ecb20868a..122abfe860bdf65592ad05586f70f26102f61193 100644 (file)
@@ -1,11 +1,9 @@
-rsync://repository.lacnic.net/rpki/lacnic/rta-lacnic-rpki.cer
-https://potato
-rsync://potato
-
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
-c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
-gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
-5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
-M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
-fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
-DQIDAQAB
+rsync://example.com/rpki/ta.cer\r
+\r
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR\r
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9\r
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+\r
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1\r
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd\r
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj\r
+DQIDAQAB\r
diff --git a/test/resources/tal/1url-lf.tal b/test/resources/tal/1url-lf.tal
new file mode 100644 (file)
index 0000000..a912420
--- /dev/null
@@ -0,0 +1,9 @@
+rsync://example.com/rpki/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/resources/tal/4urls-crlf.tal b/test/resources/tal/4urls-crlf.tal
new file mode 100644 (file)
index 0000000..78f1776
--- /dev/null
@@ -0,0 +1,12 @@
+rsync://example.com/rpki/ta.cer\r
+https://example.com/rpki/ta.cer\r
+rsync://www.example.com/potato/ta.cer\r
+https://wx3.example.com/tomato/ta.cer\r
+\r
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR\r
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9\r
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+\r
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1\r
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd\r
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj\r
+DQIDAQAB\r
diff --git a/test/resources/tal/4urls-lf-comment-space-1.tal b/test/resources/tal/4urls-lf-comment-space-1.tal
new file mode 100644 (file)
index 0000000..4815e22
--- /dev/null
@@ -0,0 +1,11 @@
+
+rsync://example.com/rpki/ta.cer
+https://example.com/rpki/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/resources/tal/4urls-lf-comment-space-2.tal b/test/resources/tal/4urls-lf-comment-space-2.tal
new file mode 100644 (file)
index 0000000..834ef7e
--- /dev/null
@@ -0,0 +1,14 @@
+
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu sapien
+#facilisis, sagittis elit quis, lobortis odio. Morbi feugiat congue bibendum.
+# Vivamus eu quam.
+rsync://example.com/rpki/ta.cer
+https://example.com/rpki/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/resources/tal/4urls-lf-comment-space-3.tal b/test/resources/tal/4urls-lf-comment-space-3.tal
new file mode 100644 (file)
index 0000000..a6a2391
--- /dev/null
@@ -0,0 +1,14 @@
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu sapien
+#facilisis, sagittis elit quis, lobortis odio. Morbi feugiat congue bibendum.
+
+# Vivamus eu quam.
+rsync://example.com/rpki/ta.cer
+https://example.com/rpki/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/resources/tal/4urls-lf-comment-space-4.tal b/test/resources/tal/4urls-lf-comment-space-4.tal
new file mode 100644 (file)
index 0000000..0a0ecf0
--- /dev/null
@@ -0,0 +1,14 @@
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu sapien
+#facilisis, sagittis elit quis, lobortis odio. Morbi feugiat congue bibendum.
+# Vivamus eu quam.
+
+rsync://example.com/rpki/ta.cer
+https://example.com/rpki/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/resources/tal/4urls-lf-comment-utf8.tal b/test/resources/tal/4urls-lf-comment-utf8.tal
new file mode 100644 (file)
index 0000000..dc20400
--- /dev/null
@@ -0,0 +1,15 @@
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu sapien
+# facilisis, αβγδϵζηθικλμνξoπρστυϕχψω. Morbi feugiat congue bibendum.
+# Vivamus eu quam.
+rsync://example.com/rpki/ta.cer
+https://example.com/rpki/ta.cer
+rsync://www.example.com/potato/ta.cer
+https://wx3.example.com/tomato/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/resources/tal/4urls-lf-comment.tal b/test/resources/tal/4urls-lf-comment.tal
new file mode 100644 (file)
index 0000000..1612c15
--- /dev/null
@@ -0,0 +1,15 @@
+# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu sapien
+#facilisis, sagittis elit quis, lobortis odio. Morbi feugiat congue bibendum.
+# Vivamus eu quam.
+rsync://example.com/rpki/ta.cer
+https://example.com/rpki/ta.cer
+rsync://www.example.com/potato/ta.cer
+https://wx3.example.com/tomato/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/resources/tal/4urls-lf.tal b/test/resources/tal/4urls-lf.tal
new file mode 100644 (file)
index 0000000..bf5df06
--- /dev/null
@@ -0,0 +1,12 @@
+rsync://example.com/rpki/ta.cer
+https://example.com/rpki/ta.cer
+rsync://www.example.com/potato/ta.cer
+https://wx3.example.com/tomato/ta.cer
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
index 6e292cb84b3068d891350066563ca0712b7e23e4..ce3a196e1b6b0241ff225e85438480cbcabc09fb 100644 (file)
@@ -11,8 +11,8 @@
 MOCK_ABORT_INT(__uri_create, struct rpki_uri **result, char const *tal,
     enum uri_type type, bool is_notif, struct rpki_uri *notif, void const *guri,
     size_t guri_len)
-__MOCK_ABORT(base64_decode, bool, false, BIO *in, unsigned char *out,
-    bool has_nl, size_t out_len, size_t *out_written)
+__MOCK_ABORT(base64_decode, bool, false, char *in, size_t inl,
+    unsigned char **out, size_t *outl)
 MOCK_ABORT_INT(cache_download, struct rpki_cache *cache, struct rpki_uri *uri,
     bool *changed)
 MOCK_ABORT_VOID(file_close, FILE *file)
index fb43067f84209da24bc4e70b8b95cd7b791f90df..5001f8febabdf360e903d29ef8d38bed0af4bc33 100644 (file)
@@ -5,7 +5,6 @@
 #include "alloc.c"
 #include "common.c"
 #include "file.c"
-#include "line_file.c"
 #include "mock.c"
 #include "data_structure/path_builder.c"
 #include "types/uri.c"
@@ -51,12 +50,11 @@ MOCK(validation_tal, struct tal *, NULL, struct validation *state)
 
 /* Tests */
 
-START_TEST(tal_load_normal)
+static void
+check_spki(struct tal *tal)
 {
-       struct tal tal;
-       unsigned int i;
        /* Got this by feeding the subjectPublicKeyInfo to `base64 -d`. */
-       unsigned char decoded[] = {
+       static unsigned char spki_raw[] = {
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48,
            0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
            0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00,
@@ -85,21 +83,70 @@ START_TEST(tal_load_normal)
            0xD4, 0x32, 0xB7, 0x11, 0x38, 0x71, 0xCF, 0xF3, 0xA4, 0x0F, 0x64,
            0x83, 0x63, 0x0D, 0x02, 0x03, 0x01, 0x00, 0x01
        };
+       unsigned int i;
+
+       ck_assert_uint_eq(ARRAY_LEN(spki_raw), tal->spki_len);
+       for (i = 0; i < ARRAY_LEN(spki_raw); i++)
+               ck_assert_uint_eq(tal->spki[i], spki_raw[i]);
+}
 
-       ck_assert_int_eq(tal_init(&tal, "tal/lacnic.tal"), 0);
+static void
+test_1url(char const *file)
+{
+       struct tal tal;
 
-       ck_assert_uint_eq(tal.uris.len, 3);
-       ck_assert_str_eq(tal.uris.array[0]->global,
-           "rsync://repository.lacnic.net/rpki/lacnic/rta-lacnic-rpki.cer");
-       ck_assert_str_eq(tal.uris.array[1]->global, "https://potato");
-       ck_assert_str_eq(tal.uris.array[2]->global, "rsync://potato");
+       ck_assert_int_eq(0, tal_init(&tal, file));
 
-       ck_assert_uint_eq(ARRAY_LEN(decoded), tal.spki_len);
-       for (i = 0; i < ARRAY_LEN(decoded); i++)
-               ck_assert_uint_eq(tal.spki[i], decoded[i]);
+       ck_assert_uint_eq(1, tal.uris.len);
+       ck_assert_str_eq("rsync://example.com/rpki/ta.cer", tal.uris.array[0]->global);
+       check_spki(&tal);
 
        tal_cleanup(&tal);
 }
+
+START_TEST(test_tal_load_1url)
+{
+       test_1url("resources/tal/1url-lf.tal");
+       test_1url("resources/tal/1url-crlf.tal");
+}
+END_TEST
+
+static void
+test_4urls(char const *file)
+{
+       struct tal tal;
+
+       ck_assert_int_eq(0, tal_init(&tal, file));
+
+       ck_assert_uint_eq(4, tal.uris.len);
+       ck_assert_str_eq("rsync://example.com/rpki/ta.cer", tal.uris.array[0]->global);
+       ck_assert_str_eq("https://example.com/rpki/ta.cer", tal.uris.array[1]->global);
+       ck_assert_str_eq("rsync://www.example.com/potato/ta.cer", tal.uris.array[2]->global);
+       ck_assert_str_eq("https://wx3.example.com/tomato/ta.cer", tal.uris.array[3]->global);
+
+       check_spki(&tal);
+
+       tal_cleanup(&tal);
+}
+
+START_TEST(test_tal_load_4urls)
+{
+       test_4urls("resources/tal/4urls-lf.tal");
+       test_4urls("resources/tal/4urls-crlf.tal");
+       test_4urls("resources/tal/4urls-lf-comment.tal");
+       test_4urls("resources/tal/4urls-lf-comment-utf8.tal");
+}
+END_TEST
+
+START_TEST(test_tal_load_error)
+{
+       struct tal tal;
+
+       ck_assert_int_eq(-EINVAL, tal_init(&tal, "resources/tal/4urls-lf-comment-space-1.tal"));
+       ck_assert_int_eq(-EINVAL, tal_init(&tal, "resources/tal/4urls-lf-comment-space-2.tal"));
+       ck_assert_int_eq(-EINVAL, tal_init(&tal, "resources/tal/4urls-lf-comment-space-3.tal"));
+       ck_assert_int_eq(-EINVAL, tal_init(&tal, "resources/tal/4urls-lf-comment-space-4.tal"));
+}
 END_TEST
 
 static Suite *tal_load_suite(void)
@@ -108,7 +155,9 @@ static Suite *tal_load_suite(void)
        TCase *core;
 
        core = tcase_create("Core");
-       tcase_add_test(core, tal_load_normal);
+       tcase_add_test(core, test_tal_load_1url);
+       tcase_add_test(core, test_tal_load_4urls);
+       tcase_add_test(core, test_tal_load_error);
 
        suite = suite_create("tal_load()");
        suite_add_tcase(suite, core);