]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Implement RFC 7730 (TALs)
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 4 Sep 2018 21:17:34 +0000 (16:17 -0500)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 4 Sep 2018 21:24:14 +0000 (16:24 -0500)
18 files changed:
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
deconf.sh [new file with mode: 0755]
src/Makefile.am [new file with mode: 0644]
src/common.h [new file with mode: 0644]
src/line_file.c [new file with mode: 0644]
src/line_file.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/tal.c [new file with mode: 0644]
src/tal.h [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/line_file/core.txt [new file with mode: 0644]
test/line_file/empty.txt [new file with mode: 0644]
test/line_file/error.txt [new file with mode: 0644]
test/line_file_test.c [new file with mode: 0644]
test/tal/lacnic.tal [new file with mode: 0644]
test/tal_test.c [new file with mode: 0644]

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..ef5e8ee
--- /dev/null
@@ -0,0 +1,3 @@
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = src test
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..042d19d
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Hello.
+# Run this file to generate the configure script.
+# You'll need Autoconf and Automake installed!
+
+aclocal && automake --add-missing --copy && autoconf
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..d65fdfb
--- /dev/null
@@ -0,0 +1,33 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+# TODO change the bug report address
+AC_INIT([rpki-validator], [0.0.1], [ydahhrk@gmail.com])
+AC_CONFIG_SRCDIR([src/main.c])
+AM_INIT_AUTOMAKE([subdir-objects])
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([netinet/in.h stdlib.h string.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_CHECK_HEADER_STDBOOL
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([memset socket])
+AC_SEARCH_LIBS([pthread_create], [pthread])
+
+# Check dependencies.
+PKG_CHECK_MODULES([CHECK], [check])
+PKG_CHECK_MODULES([GLIB], [glib-2.0])
+
+# Spit out the makefiles.
+AC_OUTPUT(Makefile src/Makefile test/Makefile)
diff --git a/deconf.sh b/deconf.sh
new file mode 100755 (executable)
index 0000000..6b3dba9
--- /dev/null
+++ b/deconf.sh
@@ -0,0 +1,13 @@
+make distclean
+rm -frv \
+       Makefile.in \
+       aclocal.m4 \
+       autom4te.cache \
+       compile \
+       configure \
+       depcomp \
+       install-sh \
+       missing \
+       man/Makefile.in \
+       src/Makefile.in
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..36722ef
--- /dev/null
@@ -0,0 +1,11 @@
+bin_PROGRAMS = rpki_validator
+
+rpki_validator_SOURCES  = main.c
+rpki_validator_SOURCES += common.h
+rpki_validator_SOURCES += line_file.h
+rpki_validator_SOURCES += line_file.c
+rpki_validator_SOURCES += tal.h
+rpki_validator_SOURCES += tal.c
+
+rpki_validator_CFLAGS = -pedantic -Wall -std=gnu11 -O3 ${GLIB_CFLAGS}
+rpki_validator_LDADD = ${GLIB_LIBS}
diff --git a/src/common.h b/src/common.h
new file mode 100644 (file)
index 0000000..9c26620
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef SRC_RTR_COMMON_H_
+#define SRC_RTR_COMMON_H_
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+#endif /* SRC_RTR_COMMON_H_ */
diff --git a/src/line_file.c b/src/line_file.c
new file mode 100644 (file)
index 0000000..59cb666
--- /dev/null
@@ -0,0 +1,152 @@
+#include "line_file.h"
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.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 err;
+
+       lfile = malloc(sizeof(struct line_file));
+       if (lfile == NULL)
+               return -ENOMEM;
+
+       lfile->file = fopen(file_name, "r");
+       if (lfile->file == NULL) {
+               err = errno;
+               free(lfile);
+               return err;
+       }
+       lfile->file_name = file_name;
+       lfile->offset = 0;
+
+       *result = lfile;
+       return 0;
+}
+
+void
+lfile_close(struct line_file *lf)
+{
+       fclose(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 err;
+
+       /*
+        * 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.
+        * - The fact that it returns the newline in the buffer is puzzling,
+        *   because who the fuck wants that nonsense. 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 7730
+        *   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.)
+        */
+
+       string = NULL;
+       alloc_len = 0;
+       len = getline(&string, &alloc_len, lfile->file);
+
+       if (len == -1) {
+               err = errno;
+               free(string);
+               *result = NULL;
+               if (ferror(lfile->file))
+                       return err;
+               if (feof(lfile->file))
+                       return 0;
+               warnx("Supposedly unreachable code reached. ferror:%d feof:%d",
+                               ferror(lfile->file), feof(lfile->file));
+               return -EINVAL;
+       }
+
+       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') {
+                       warnx("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;
+}
+
+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
new file mode 100644 (file)
index 0000000..ef882ac
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef LINE_FILE_H_
+#define LINE_FILE_H_
+
+/*
+ * A "line file" is a text file that you want to read line-by-line.
+ *
+ * As defined by RFC7730 (the only current user of this code), lines are
+ * terminated by either CRLF or LF.
+ * (...which is the same as saying "lines are terminated by LF.")
+ */
+
+#include <stddef.h>
+
+struct line_file;
+
+int lfile_open(const char *, struct line_file **);
+void lfile_close();
+
+int lfile_read(struct line_file *, char **);
+
+const char *lfile_name(struct line_file *);
+size_t lfile_offset(struct line_file *);
+
+#endif /* LINE_FILE_H_ */
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..94f713b
--- /dev/null
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+
+int
+main(void)
+{
+       return EXIT_SUCCESS;
+}
diff --git a/src/tal.c b/src/tal.c
new file mode 100644 (file)
index 0000000..1c751aa
--- /dev/null
+++ b/src/tal.c
@@ -0,0 +1,204 @@
+#include "tal.h"
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <stdio.h>
+
+#include "common.h"
+#include "line_file.h"
+
+struct uri {
+       char *string;
+       SLIST_ENTRY(uri) next;
+};
+
+SLIST_HEAD(uri_list, uri);
+
+struct tal {
+       struct uri_list uris;
+       /* Decoded; not base64. */
+       guchar *spki;
+       size_t spki_size;
+};
+
+static void
+uri_destroy(struct uri *uri)
+{
+       free(uri->string);
+       free(uri);
+}
+
+static void
+uris_destroy(struct uri_list *uris)
+{
+       struct uri *uri;
+
+       while (!SLIST_EMPTY(uris)) {
+               uri = SLIST_FIRST(uris);
+               SLIST_REMOVE_HEAD(uris, next);
+               uri_destroy(uri);
+       }
+}
+
+static int
+read_uri(struct line_file *lfile, struct uri **result)
+{
+       struct uri *uri;
+       int err;
+
+       uri = malloc(sizeof(struct uri));
+       if (uri == NULL) {
+               warnx("Out of memory");
+               return -ENOMEM;
+       }
+
+       err = lfile_read(lfile, &uri->string);
+       if (err) {
+               /* TODO have lfile_read print error msg */
+               free(uri);
+               return err;
+       }
+
+       *result = uri;
+       return 0;
+}
+
+static int
+read_uris(struct line_file *lfile, struct uri_list *uris)
+{
+       struct uri *previous, *uri;
+       int err;
+
+       err = read_uri(lfile, &uri);
+       if (err)
+               return err;
+
+       if (strcmp(uri->string, "") == 0) {
+               uri_destroy(uri);
+               warnx("TAL file %s contains no URIs", lfile_name(lfile));
+               return -EINVAL;
+       }
+
+       SLIST_INIT(uris);
+       SLIST_INSERT_HEAD(uris, uri, next);
+
+       do {
+               previous = uri;
+
+               err = read_uri(lfile, &uri);
+               if (err)
+                       return err;
+
+               if (strcmp(uri->string, "") == 0) {
+                       uri_destroy(uri);
+                       return 0; /* Happy path */
+               }
+
+               SLIST_INSERT_AFTER(previous, uri, next);
+       } while (true);
+}
+
+/*
+ * 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)
+{
+       struct stat st;
+       size_t result;
+
+       stat(lfile_name(lfile), &st);
+       result = st.st_size - lfile_offset(lfile);
+
+       /*
+        * See the documentation for g_base64_decode_step().
+        * I added `+ 1`. It's because `result / 4` truncates, and I'm not sure
+        * if the original equation meant to round up.
+        */
+       return (result / 4 + 1) * 3 + 3;
+}
+
+static int
+read_spki(struct line_file *lfile, struct tal *tal)
+{
+       char *line;
+       gint state = 0;
+       guint save = 0;
+       int err;
+
+       tal->spki = malloc(get_spki_alloc_size(lfile));
+       if (tal->spki == NULL)
+               return -ENOMEM;
+       tal->spki_size = 0;
+
+       do {
+               err = lfile_read(lfile, &line);
+               if (err) {
+                       free(tal->spki);
+                       return err;
+               }
+
+               if (line == NULL)
+                       return 0;
+
+               tal->spki_size += g_base64_decode_step(line, strlen(line),
+                   tal->spki + tal->spki_size, &state, &save);
+               free(line);
+       } while (true);
+}
+
+int
+tal_load(const char *file_name, struct tal **result)
+{
+       struct line_file *lfile;
+       struct tal *tal;
+       int err;
+
+       err = lfile_open(file_name, &lfile);
+       if (err)
+               return err;
+
+       tal = malloc(sizeof(struct tal));
+       if (tal == NULL) {
+               lfile_close(lfile);
+               return -ENOMEM;
+       }
+
+       err = read_uris(lfile, &tal->uris);
+       if (err) {
+               free(tal);
+               lfile_close(lfile);
+               return err;
+       }
+
+       err = read_spki(lfile, tal);
+       if (err) {
+               uris_destroy(&tal->uris);
+               free(tal);
+               lfile_close(lfile);
+               return err;
+       }
+
+       lfile_close(lfile);
+       *result = tal;
+       return 0;
+}
+
+void tal_destroy(struct tal *tal)
+{
+       if (tal == NULL)
+               return;
+
+       uris_destroy(&tal->uris);
+       free(tal->spki);
+       free(tal);
+}
diff --git a/src/tal.h b/src/tal.h
new file mode 100644 (file)
index 0000000..9dfa869
--- /dev/null
+++ b/src/tal.h
@@ -0,0 +1,9 @@
+#ifndef TAL_H_
+#define TAL_H_
+
+struct tal;
+
+int tal_load(const char *, struct tal **);
+void tal_destroy(struct tal *);
+
+#endif /* TAL_H_ */
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644 (file)
index 0000000..adbd3c7
--- /dev/null
@@ -0,0 +1,12 @@
+AM_CFLAGS = -pedantic -Wall -std=gnu11 -I../src ${CHECK_CFLAGS}
+MY_LDADD = ${CHECK_LIBS}
+
+check_PROGRAMS = line_file.test tal.test
+TESTS = ${check_PROGRAMS}
+
+line_file_test_SOURCES = line_file_test.c ../src/line_file.c ../src/line_file.h
+line_file_test_LDADD = ${MY_LDADD}
+
+tal_test_SOURCES = tal_test.c ../src/line_file.c ../src/line_file.h
+tal_test_CFLAGS = ${AM_CFLAGS} ${GLIB_CFLAGS}
+tal_test_LDADD = ${MY_LDADD} ${GLIB_LIBS}
diff --git a/test/line_file/core.txt b/test/line_file/core.txt
new file mode 100644 (file)
index 0000000..211243d
--- /dev/null
@@ -0,0 +1,6 @@
+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/line_file/empty.txt b/test/line_file/empty.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/line_file/error.txt b/test/line_file/error.txt
new file mode 100644 (file)
index 0000000..f657862
Binary files /dev/null and b/test/line_file/error.txt differ
diff --git a/test/line_file_test.c b/test/line_file_test.c
new file mode 100644 (file)
index 0000000..ed8004a
--- /dev/null
@@ -0,0 +1,121 @@
+#include "line_file.h"
+
+#include <check.h>
+#include <errno.h>
+#include <stdlib.h>
+
+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("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("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("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
+
+Suite *lfile_read_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 = lfile_read_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/tal/lacnic.tal b/test/tal/lacnic.tal
new file mode 100644 (file)
index 0000000..62bda32
--- /dev/null
@@ -0,0 +1,11 @@
+rsync://repository.lacnic.net/rpki/lacnic/rta-lacnic-rpki.cer
+http://potato
+rsync://potato
+
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZEzhYK0+PtDOPfub/KR
+c3MeWx3neXx4/wbnJWGbNAtbYqXg3uU5J4HFzPgk/VIppgSKAhlO0H60DRP48by9
+gr5/yDHu2KXhOmnMg46sYsUIpfgtBS9+VtrqWziJfb+pkGtuOWeTnj6zBmBNZKK+
+5AlMCW1WPhrylIcB+XSZx8tk9GS/3SMQ+YfMVwwAyYjsex14Uzto4GjONALE5oh1
+M3+glRQduD6vzSwOD+WahMbc9vCOTED+2McLHRKgNaQf0YJ9a1jG9oJIvDkKXEqd
+fqDRktwyoD74cV57bW3tBAexB7GglITbInyQAsmdngtfg2LUMrcROHHP86QPZINj
+DQIDAQAB
diff --git a/test/tal_test.c b/test/tal_test.c
new file mode 100644 (file)
index 0000000..b6015de
--- /dev/null
@@ -0,0 +1,89 @@
+#include "tal.c"
+
+#include <check.h>
+#include <errno.h>
+#include <stdlib.h>
+
+START_TEST(tal_load_normal)
+{
+       struct tal *tal;
+       struct uri *uri;
+       unsigned int i;
+       /* Got this by feeding the subjectPublicKeyInfo to `base64 -d`. */
+       unsigned char decoded[] = {
+           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,
+           0xA9, 0x91, 0x33, 0x85, 0x82, 0xB4, 0xF8, 0xFB, 0x43, 0x38, 0xF7,
+           0xEE, 0x6F, 0xF2, 0x91, 0x73, 0x73, 0x1E, 0x5B, 0x1D, 0xE7, 0x79,
+           0x7C, 0x78, 0xFF, 0x06, 0xE7, 0x25, 0x61, 0x9B, 0x34, 0x0B, 0x5B,
+           0x62, 0xA5, 0xE0, 0xDE, 0xE5, 0x39, 0x27, 0x81, 0xC5, 0xCC, 0xF8,
+           0x24, 0xFD, 0x52, 0x29, 0xA6, 0x04, 0x8A, 0x02, 0x19, 0x4E, 0xD0,
+           0x7E, 0xB4, 0x0D, 0x13, 0xF8, 0xF1, 0xBC, 0xBD, 0x82, 0xBE, 0x7F,
+           0xC8, 0x31, 0xEE, 0xD8, 0xA5, 0xE1, 0x3A, 0x69, 0xCC, 0x83, 0x8E,
+           0xAC, 0x62, 0xC5, 0x08, 0xA5, 0xF8, 0x2D, 0x05, 0x2F, 0x7E, 0x56,
+           0xDA, 0xEA, 0x5B, 0x38, 0x89, 0x7D, 0xBF, 0xA9, 0x90, 0x6B, 0x6E,
+           0x39, 0x67, 0x93, 0x9E, 0x3E, 0xB3, 0x06, 0x60, 0x4D, 0x64, 0xA2,
+           0xBE, 0xE4, 0x09, 0x4C, 0x09, 0x6D, 0x56, 0x3E, 0x1A, 0xF2, 0x94,
+           0x87, 0x01, 0xF9, 0x74, 0x99, 0xC7, 0xCB, 0x64, 0xF4, 0x64, 0xBF,
+           0xDD, 0x23, 0x10, 0xF9, 0x87, 0xCC, 0x57, 0x0C, 0x00, 0xC9, 0x88,
+           0xEC, 0x7B, 0x1D, 0x78, 0x53, 0x3B, 0x68, 0xE0, 0x68, 0xCE, 0x34,
+           0x02, 0xC4, 0xE6, 0x88, 0x75, 0x33, 0x7F, 0xA0, 0x95, 0x14, 0x1D,
+           0xB8, 0x3E, 0xAF, 0xCD, 0x2C, 0x0E, 0x0F, 0xE5, 0x9A, 0x84, 0xC6,
+           0xDC, 0xF6, 0xF0, 0x8E, 0x4C, 0x40, 0xFE, 0xD8, 0xC7, 0x0B, 0x1D,
+           0x12, 0xA0, 0x35, 0xA4, 0x1F, 0xD1, 0x82, 0x7D, 0x6B, 0x58, 0xC6,
+           0xF6, 0x82, 0x48, 0xBC, 0x39, 0x0A, 0x5C, 0x4A, 0x9D, 0x7E, 0xA0,
+           0xD1, 0x92, 0xDC, 0x32, 0xA0, 0x3E, 0xF8, 0x71, 0x5E, 0x7B, 0x6D,
+           0x6D, 0xED, 0x04, 0x07, 0xB1, 0x07, 0xB1, 0xA0, 0x94, 0x84, 0xDB,
+           0x22, 0x7C, 0x90, 0x02, 0xC9, 0x9D, 0x9E, 0x0B, 0x5F, 0x83, 0x62,
+           0xD4, 0x32, 0xB7, 0x11, 0x38, 0x71, 0xCF, 0xF3, 0xA4, 0x0F, 0x64,
+           0x83, 0x63, 0x0D, 0x02, 0x03, 0x01, 0x00, 0x01
+       };
+
+       ck_assert_int_eq(tal_load("tal/lacnic.tal", &tal), 0);
+
+       uri = SLIST_FIRST(&tal->uris);
+       ck_assert_str_eq(uri->string, "rsync://repository.lacnic.net/rpki/lacnic/rta-lacnic-rpki.cer");
+       uri = SLIST_NEXT(uri, next);
+       ck_assert_str_eq(uri->string, "http://potato");
+       uri = SLIST_NEXT(uri, next);
+       ck_assert_str_eq(uri->string, "rsync://potato");
+       uri = SLIST_NEXT(uri, next);
+       ck_assert(uri == NULL);
+
+       ck_assert_uint_eq(ARRAY_SIZE(decoded), tal->spki_size);
+       for (i = 0; i < ARRAY_SIZE(decoded); i++)
+               ck_assert_uint_eq(tal->spki[i], decoded[i]);
+
+       tal_destroy(tal);
+}
+END_TEST
+
+Suite *tal_load_suite(void)
+{
+       Suite *suite;
+       TCase *core;
+
+       core = tcase_create("Core");
+       tcase_add_test(core, tal_load_normal);
+
+       suite = suite_create("lfile_read()");
+       suite_add_tcase(suite, core);
+       return suite;
+}
+
+int main(void)
+{
+       Suite *suite;
+       SRunner *runner;
+       int tests_failed;
+
+       suite = tal_load_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;
+}