+Each TAL will validate its own repositories without waiting for the others to terminate.
+Remove a TODO on configure.ac
+Update unit tests.
+Fix warning at base64 sanitizer, replace the function 'strchr' with a local one since the read buffer isn't necessarily a string.
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
-# TODO change the bug report address
AC_INIT([fort], [1.0.0], [fort-validator@nic.mx])
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE([subdir-objects])
#include "tal.h"
#include <errno.h>
+#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/queue.h>
#include <sys/stat.h>
#include <openssl/evp.h>
#include "random.h"
#include "state.h"
#include "thread_var.h"
+#include "validation_handler.h"
#include "crypto/base64.h"
#include "object/certificate.h"
#include "rsync/rsync.h"
+#include "rtr/db/vrps.h"
#define TAL_FILE_EXTENSION ".tal"
size_t spki_len;
};
+struct fv_param {
+ char *tal_file;
+ void *arg;
+};
+
+struct thread {
+ pthread_t pid;
+ char *file;
+ SLIST_ENTRY(thread) next;
+};
+
+/* List of threads, one per TAL file */
+SLIST_HEAD(threads_list, thread) threads;
+
static int
uris_init(struct uris *uris)
{
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).
{
#define BUF_SIZE 65
FILE *fd;
- char buf[BUF_SIZE];
- char *result, *eol;
+ char *buf, *result, *eol;
size_t original_size, new_size;
size_t fread_result, offset;
int error;
if (result == NULL)
return pr_enomem();
+ buf = malloc(BUF_SIZE);
+ if (buf == NULL) {
+ free(result);
+ return pr_enomem();
+ }
+
fd = lfile_fd(lfile);
offset = 0;
while ((fread_result = fread(buf, 1,
}
original_size -= fread_result;
- eol = strchr(buf, '\n');
+ 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);
}
result = eol;
}
+ free(buf);
result[offset] = '\0';
*out = result;
return 0;
free_result:
+ free(buf);
free(result);
return error;
#undef BUF_SIZE
* A "hard error" is any other error.
*/
+ struct validation_handler validation_handler;
struct validation *state;
struct cert_stack *certstack;
struct deferred_cert deferred;
int error;
+ validation_handler.handle_roa_v4 = handle_roa_v4;
+ validation_handler.handle_roa_v6 = handle_roa_v6;
+ validation_handler.handle_router_key = handle_router_key;
+ validation_handler.arg = arg;
+
+ error = validation_prepare(&state, tal, &validation_handler);
+ if (error)
+ return ENSURE_NEGATIVE(error);
+
error = download_files(uri, true, false);
if (error) {
pr_warn("TAL '%s' could not be RSYNC'd.",
uri_get_printable(uri));
+ validation_destroy(state);
return ENSURE_NEGATIVE(error);
}
- error = validation_prepare(&state, tal, arg);
- if (error)
- return ENSURE_NEGATIVE(error);
-
pr_debug_add("TAL URI '%s' {", uri_get_printable(uri));
if (!uri_is_certificate(uri)) {
return error;
}
-static int
-do_file_validation(char const *tal_file, void *arg)
+static void *
+do_file_validation(void *thread_arg)
{
+ struct fv_param param;
struct tal *tal;
int error;
- fnstack_push(tal_file);
+ memcpy(¶m, thread_arg, sizeof(param));
+ free(thread_arg);
- error = tal_load(tal_file, &tal);
+ fnstack_init();
+ fnstack_push(param.tal_file);
+
+ error = tal_load(param.tal_file, &tal);
if (error)
goto end;
if (config_get_shuffle_tal_uris())
tal_shuffle_uris(tal);
- error = foreach_uri(tal, handle_tal_uri, arg);
+ error = foreach_uri(tal, handle_tal_uri, param.arg);
if (error > 0)
error = 0;
else if (error == 0)
error = pr_err("None of the URIs of the TAL '%s' yielded a successful traversal.",
- tal_file);
+ param.tal_file);
tal_destroy(tal);
end:
- fnstack_pop();
+ fnstack_cleanup();
+ free(param.tal_file);
+ return NULL;
+}
+
+static void
+thread_destroy(struct thread *thread)
+{
+ free(thread->file);
+ free(thread);
+}
+
+/* Creates a thread for the @tal_file */
+static int
+__do_file_validation(char const *tal_file, void *arg)
+{
+ struct thread *thread;
+ struct fv_param *param;
+ static pthread_t pid;
+ int error;
+
+ param = malloc(sizeof(struct fv_param));
+ if (param == NULL)
+ return pr_enomem();
+
+ param->tal_file = strdup(tal_file);
+ param->arg = arg;
+
+ errno = pthread_create(&pid, NULL, do_file_validation, param);
+ if (errno) {
+ error = -pr_errno(errno,
+ "Could not spawn the file validation thread");
+ goto free_param;
+ }
+
+ thread = malloc(sizeof(struct thread));
+ if (thread == NULL) {
+ close_thread(pid, tal_file);
+ error = -EINVAL;
+ goto free_param;
+ }
+
+ thread->pid = pid;
+ thread->file = strdup(tal_file);
+ SLIST_INSERT_HEAD(&threads, thread, next);
+
+ return 0;
+free_param:
+ free(param->tal_file);
+ free(param);
return error;
}
int
-perform_standalone_validation(struct validation_handler *handler)
+perform_standalone_validation(struct db_table *table)
{
+ struct thread *thread;
int error;
- error = rsync_init();
+ SLIST_INIT(&threads);
+ error = process_file_or_dir(config_get_tal(), TAL_FILE_EXTENSION,
+ __do_file_validation, table);
if (error)
return error;
- fnstack_init();
- error = process_file_or_dir(config_get_tal(), TAL_FILE_EXTENSION,
- do_file_validation, handler);
- fnstack_cleanup();
- rsync_destroy();
+ /* Wait for all */
+ while (!SLIST_EMPTY(&threads)) {
+ thread = threads.slh_first;
+ error = pthread_join(thread->pid, NULL);
+ if (error)
+ pr_crit("pthread_join() threw %d on the '%s' thread.",
+ error, thread->file);
+ SLIST_REMOVE_HEAD(&threads, next);
+ thread_destroy(thread);
+ }
return error;
}
+
+void
+terminate_standalone_validation(void)
+{
+ struct thread *thread;
+
+ /* End all threads */
+ while (!SLIST_EMPTY(&threads)) {
+ thread = threads.slh_first;
+ close_thread(thread->pid, thread->file);
+ SLIST_REMOVE_HEAD(&threads, next);
+ thread_destroy(thread);
+ }
+}
#include <stddef.h>
#include "uri.h"
-#include "validation_handler.h"
+#include "rtr/db/db_table.h"
struct tal;
char const *tal_get_file_name(struct tal *);
void tal_get_spki(struct tal *, unsigned char const **, size_t *);
-int perform_standalone_validation(struct validation_handler *);
+int perform_standalone_validation(struct db_table *);
+void terminate_standalone_validation(void);
#endif /* TAL_OBJECT_H_ */
#include "config.h"
#include "log.h"
#include "str.h"
+#include "thread_var.h"
struct uri {
struct rpki_uri *uri;
};
/** URIs that we have already downloaded. */
-SLIST_HEAD(uri_list, uri) visited_uris;
+SLIST_HEAD(uri_list, uri);
/* static char const *const RSYNC_PREFIX = "rsync://"; */
int
-rsync_init(void)
+rsync_create(struct uri_list **result)
{
- SLIST_INIT(&visited_uris);
+ struct uri_list *visited_uris;
+
+ visited_uris = malloc(sizeof(struct uri_list));
+ if (visited_uris == NULL)
+ return pr_enomem();
+
+ SLIST_INIT(visited_uris);
+
+ *result = visited_uris;
return 0;
}
void
-rsync_destroy(void)
+rsync_destroy(struct uri_list *list)
{
struct uri *uri;
- while (!SLIST_EMPTY(&visited_uris)) {
- uri = SLIST_FIRST(&visited_uris);
- SLIST_REMOVE_HEAD(&visited_uris, next);
+ while (!SLIST_EMPTY(list)) {
+ uri = SLIST_FIRST(list);
+ SLIST_REMOVE_HEAD(list, next);
uri_refput(uri->uri);
free(uri);
}
+ free(list);
}
/*
* run.
*/
static bool
-is_already_downloaded(struct rpki_uri *uri)
+is_already_downloaded(struct rpki_uri *uri, struct uri_list *visited_uris)
{
struct uri *cursor;
/* TODO (next iteration) this is begging for a radix trie. */
- SLIST_FOREACH(cursor, &visited_uris, next)
+ SLIST_FOREACH(cursor, visited_uris, next)
if (is_descendant(cursor->uri, uri))
return true;
}
static int
-mark_as_downloaded(struct rpki_uri *uri)
+mark_as_downloaded(struct rpki_uri *uri, struct uri_list *visited_uris)
{
struct uri *node;
node->uri = uri;
uri_refget(uri);
- SLIST_INSERT_HEAD(&visited_uris, node, next);
+ SLIST_INSERT_HEAD(visited_uris, node, next);
return 0;
}
* @rsync_uri is the URL we're actually going to RSYNC.
* (They can differ, depending on config_get_sync_strategy().)
*/
+ struct validation *state;
+ struct uri_list *visited_uris;
struct rpki_uri *rsync_uri;
int error;
if (config_get_sync_strategy() == SYNC_OFF)
return 0;
- if (!force && is_already_downloaded(requested_uri)) {
+ state = state_retrieve();
+ if (state == NULL)
+ return -EINVAL;
+
+ visited_uris = validation_visited_uris(state);
+
+ if (!force && is_already_downloaded(requested_uri, visited_uris)) {
pr_debug("No need to redownload '%s'.",
uri_get_printable(requested_uri));
return 0;
/* Don't store when "force" and if its already downloaded */
error = do_rsync(rsync_uri, is_ta);
- if (!error && !(force && is_already_downloaded(rsync_uri)))
- error = mark_as_downloaded(rsync_uri);
+ if (!error &&
+ !(force && is_already_downloaded(rsync_uri, visited_uris)))
+ error = mark_as_downloaded(rsync_uri, visited_uris);
uri_refput(rsync_uri);
return error;
#include <stdbool.h>
#include "uri.h"
+struct uri_list;
+
int download_files(struct rpki_uri *, bool, bool);
-int rsync_init(void);
-void rsync_destroy(void);
+int rsync_create(struct uri_list **);
+void rsync_destroy(struct uri_list *);
#endif /* SRC_RSYNC_RSYNC_H_ */
} state;
/** Read/write lock, which protects @state and its inhabitants. */
-static pthread_rwlock_t lock;
+static pthread_rwlock_t state_lock;
+
+/** Lock to protect ROA table during construction. */
+static pthread_rwlock_t table_lock;
void
deltagroup_cleanup(struct delta_group *group)
? (state.v0_session_id - 1)
: (0xFFFFu);
- error = pthread_rwlock_init(&lock, NULL);
+ error = pthread_rwlock_init(&state_lock, NULL);
+ if (error) {
+ deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
+ return pr_errno(error, "state pthread_rwlock_init() errored");
+ }
+
+ error = pthread_rwlock_init(&table_lock, NULL);
if (error) {
+ pthread_rwlock_destroy(&state_lock);
deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
- return pr_errno(error, "pthread_rwlock_init() errored");
+ return pr_errno(error, "table pthread_rwlock_init() errored");
}
return 0;
if (state.base != NULL)
db_table_destroy(state.base);
deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
- pthread_rwlock_destroy(&lock); /* Nothing to do with error code */
+ /* Nothing to do with error codes from now on */
+ pthread_rwlock_destroy(&state_lock);
+ pthread_rwlock_destroy(&table_lock);
}
+#define WLOCK_HANDLER(lock, cb) \
+ int error; \
+ rwlock_write_lock(lock); \
+ error = cb; \
+ rwlock_unlock(lock); \
+ return error;
+
int
-__handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
+handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
uint8_t max_length, void *arg)
{
- return rtrhandler_handle_roa_v4(arg, as, prefix, max_length);
+ WLOCK_HANDLER(&table_lock,
+ rtrhandler_handle_roa_v4(arg, as, prefix, max_length))
}
int
-__handle_roa_v6(uint32_t as, struct ipv6_prefix const * prefix,
+handle_roa_v6(uint32_t as, struct ipv6_prefix const * prefix,
uint8_t max_length, void *arg)
{
- return rtrhandler_handle_roa_v6(arg, as, prefix, max_length);
+ WLOCK_HANDLER(&table_lock,
+ rtrhandler_handle_roa_v6(arg, as, prefix, max_length))
}
int
-__handle_router_key(unsigned char const *ski, uint32_t as,
+handle_router_key(unsigned char const *ski, uint32_t as,
unsigned char const *spk, void *arg)
{
- return rtrhandler_handle_router_key(arg, ski, as, spk);
+ WLOCK_HANDLER(&table_lock,
+ rtrhandler_handle_router_key(arg, ski, as, spk))
}
static int
__perform_standalone_validation(struct db_table **result)
{
struct db_table *db;
- struct validation_handler validation_handler;
int error;
db = db_table_create();
if (db == NULL)
return pr_enomem();
- validation_handler.handle_roa_v4 = __handle_roa_v4;
- validation_handler.handle_roa_v6 = __handle_roa_v6;
- validation_handler.handle_router_key = __handle_router_key;
- validation_handler.arg = db;
-
- error = perform_standalone_validation(&validation_handler);
+ error = perform_standalone_validation(db);
if (error) {
+ terminate_standalone_validation();
db_table_destroy(db);
return error;
}
if (error)
return error;
- rwlock_write_lock(&lock);
+ rwlock_write_lock(&state_lock);
/*
* TODO (next iteration) Remember the last valid SLURM
if (state.base != NULL) {
error = compute_deltas(state.base, new_base, &deltas);
if (error) {
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
goto revert_base;
}
if (deltas_is_empty(deltas)) {
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
goto revert_deltas; /* error == 0 is good */
}
deltas_node.deltas = deltas;
error = deltas_db_add(&state.deltas, &deltas_node);
if (error) {
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
goto revert_deltas;
}
}
/* Remove unnecessary deltas */
error = vrps_purge(&deltas);
if (error) {
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
goto revert_base;
}
} else {
error = create_empty_delta(&deltas);
if (error) {
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
goto revert_base;
}
}
state.base = new_base;
state.next_serial++;
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
if (old_base != NULL)
db_table_destroy(old_base);
{
int error;
- error = rwlock_read_lock(&lock);
+ error = rwlock_read_lock(&state_lock);
if (error)
return error;
error = -EAGAIN;
unlock:
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
return error;
}
from_found = false;
- error = rwlock_read_lock(&lock);
+ error = rwlock_read_lock(&state_lock);
if (error)
return error;
if (state.base == NULL) {
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
return -EAGAIN;
}
error = deltas_db_add(result, group);
if (error) {
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
return error;
}
*to = group->serial;
}
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
return from_found ? 0 : -ESRCH;
}
{
int error;
- error = rwlock_read_lock(&lock);
+ error = rwlock_read_lock(&state_lock);
if (error)
return error;
else
error = -EAGAIN;
- rwlock_unlock(&lock);
+ rwlock_unlock(&state_lock);
return error;
}
int vrps_foreach_filtered_delta(struct deltas_db *, delta_vrp_foreach_cb,
delta_router_key_foreach_cb, void *);
+int handle_roa_v4(uint32_t, struct ipv4_prefix const *, uint8_t, void *);
+int handle_roa_v6(uint32_t, struct ipv6_prefix const *, uint8_t, void *);
+int handle_router_key(unsigned char const *, uint32_t, unsigned char const *,
+ void *);
+
uint16_t get_current_session_id(uint8_t);
#endif /* SRC_VRPS_H_ */
struct cert_stack *certstack;
+ struct uri_list *visited_uris;
+
/* Did the TAL's public key match the root certificate's public key? */
enum pubkey_state pubkey_state;
if (error)
goto abort3;
+ error = rsync_create(&result->visited_uris);
+ if (error)
+ goto abort4;
+
result->pubkey_state = PKS_UNTESTED;
result->validation_handler = *validation_handler;
result->x509_data.params = params; /* Ownership transfered */
*out = result;
return 0;
+abort4:
+ certstack_destroy(result->certstack);
abort3:
X509_VERIFY_PARAM_free(params);
abort2:
X509_VERIFY_PARAM_free(state->x509_data.params);
X509_STORE_free(state->x509_data.store);
certstack_destroy(state->certstack);
+ rsync_destroy(state->visited_uris);
free(state);
}
return state->certstack;
}
+struct uri_list *
+validation_visited_uris(struct validation *state)
+{
+ return state->visited_uris;
+}
+
void
validation_pubkey_valid(struct validation *state)
{
#include "cert_stack.h"
#include "validation_handler.h"
#include "object/tal.h"
+#include "rsync/rsync.h"
struct validation;
struct tal *validation_tal(struct validation *);
X509_STORE *validation_store(struct validation *);
-struct cert_stack *validation_certstack(struct validation *state);
+struct cert_stack *validation_certstack(struct validation *);
+struct uri_list *validation_visited_uris(struct validation *);
enum pubkey_state {
PKS_VALID,
#include "uri.c"
#include "rsync/rsync.c"
+
+struct validation *
+state_retrieve(void)
+{
+ return NULL;
+}
+
START_TEST(rsync_load_normal)
{
END_TEST
static void
-__mark_as_downloaded(char *uri_str)
+__mark_as_downloaded(char *uri_str, struct uri_list *visited_uris)
{
struct rpki_uri *uri;
ck_assert_int_eq(0, uri_create_str(&uri, uri_str, strlen(uri_str)));
- ck_assert_int_eq(mark_as_downloaded(uri), 0);
+ ck_assert_int_eq(mark_as_downloaded(uri, visited_uris), 0);
uri_refput(uri);
}
static void
-assert_downloaded(char *uri_str, bool expected)
+assert_downloaded(char *uri_str, struct uri_list *visited_uris, bool expected)
{
struct rpki_uri *uri;
ck_assert_int_eq(0, uri_create_str(&uri, uri_str, strlen(uri_str)));
- ck_assert_int_eq(is_already_downloaded(uri), expected);
+ ck_assert_int_eq(is_already_downloaded(uri, visited_uris), expected);
uri_refput(uri);
}
START_TEST(rsync_test_list)
{
- struct uri *uri;
-
- ck_assert_int_eq(rsync_init(), 0);
-
- __mark_as_downloaded("rsync://example.foo/repository/");
- __mark_as_downloaded("rsync://example.foo/member_repository/");
- __mark_as_downloaded("rsync://example.foz/repository/");
- __mark_as_downloaded("rsync://example.boo/repo/");
- __mark_as_downloaded("rsync://example.potato/rpki/");
-
- assert_downloaded("rsync://example.foo/repository/", true);
- assert_downloaded("rsync://example.foo/repository/abc/cdfg", true);
- assert_downloaded("rsync://example.foo/member_repository/bca", true);
- assert_downloaded("rsync://example.boo/repository/", false);
- assert_downloaded("rsync://example.potato/repository/", false);
- assert_downloaded("rsync://example.potato/rpki/abc/", true);
-
- /* rsync destroy */
- while (!SLIST_EMPTY(&visited_uris)) {
- uri = SLIST_FIRST(&visited_uris);
- SLIST_REMOVE_HEAD(&visited_uris, next);
- free(uri);
- }
+ struct uri_list *visited_uris;
+
+ ck_assert_int_eq(rsync_create(&visited_uris), 0);
+
+ __mark_as_downloaded("rsync://example.foo/repository/", visited_uris);
+ __mark_as_downloaded("rsync://example.foo/member_repository/",
+ visited_uris);
+ __mark_as_downloaded("rsync://example.foz/repository/", visited_uris);
+ __mark_as_downloaded("rsync://example.boo/repo/", visited_uris);
+ __mark_as_downloaded("rsync://example.potato/rpki/", visited_uris);
+
+ assert_downloaded("rsync://example.foo/repository/", visited_uris,
+ true);
+ assert_downloaded("rsync://example.foo/repository/abc/cdfg",
+ visited_uris, true);
+ assert_downloaded("rsync://example.foo/member_repository/bca",
+ visited_uris, true);
+ assert_downloaded("rsync://example.boo/repository/", visited_uris,
+ false);
+ assert_downloaded("rsync://example.potato/repository/", visited_uris,
+ false);
+ assert_downloaded("rsync://example.potato/rpki/abc/", visited_uris,
+ true);
+
+ rsync_destroy(visited_uris);
}
END_TEST
}
int
-perform_standalone_validation(struct validation_handler *handler)
+__handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
+ uint8_t max_length, void *arg)
{
+ return rtrhandler_handle_roa_v4(arg, as, prefix, max_length);
+}
+
+int
+__handle_roa_v6(uint32_t as, struct ipv6_prefix const * prefix,
+ uint8_t max_length, void *arg)
+{
+ return rtrhandler_handle_roa_v6(arg, as, prefix, max_length);
+}
+
+int
+__handle_router_key(unsigned char const *ski, uint32_t as,
+ unsigned char const *spk, void *arg)
+{
+ return rtrhandler_handle_router_key(arg, ski, as, spk);
+}
+
+int
+perform_standalone_validation(struct db_table *table)
+{
+ struct validation_handler handler;
+
+ handler.handle_roa_v4 = __handle_roa_v4;
+ handler.handle_roa_v6 = __handle_roa_v6;
+ handler.handle_router_key = __handle_router_key;
+ handler.arg = table;
+
switch (iteration) {
case 0:
- add_v4(handler, 0);
- add_v6(handler, 0);
+ add_v4(&handler, 0);
+ add_v6(&handler, 0);
break;
case 1:
- add_v4(handler, 0);
- add_v6(handler, 0);
- add_v4(handler, 1);
- add_v6(handler, 1);
+ add_v4(&handler, 0);
+ add_v6(&handler, 0);
+ add_v4(&handler, 1);
+ add_v6(&handler, 1);
break;
case 2:
- add_v4(handler, 1);
- add_v6(handler, 1);
+ add_v4(&handler, 1);
+ add_v6(&handler, 1);
break;
case 3:
- add_v4(handler, 0);
- add_v6(handler, 0);
+ add_v4(&handler, 0);
+ add_v6(&handler, 0);
break;
default:
ck_abort_msg("perform_standalone_validation() was called too many times (%d).",
iteration++;
return 0;
}
+
+void
+terminate_standalone_validation(void)
+{
+ /* Nothing, no threads to join */
+}
return 0;
}
+int
+rtrhandler_handle_roa_v4(struct db_table *table, uint32_t asn,
+ struct ipv4_prefix const *prefix4, uint8_t max_length)
+{
+ return 0;
+}
+
+int
+rtrhandler_handle_roa_v6(struct db_table *table, uint32_t asn,
+ struct ipv6_prefix const *prefix6, uint8_t max_length)
+{
+ return 0;
+}
+
+int
+rtrhandler_handle_router_key(struct db_table *table,
+ unsigned char const *ski, uint32_t as, unsigned char const *spk)
+{
+ return 0;
+}
+
/* End of impersonator */
static void
return 0;
}
+void
+close_thread(pthread_t thread, char const *what)
+{
+ /* Nothing to close */
+}
+
void
fnstack_init(void)
{
/* Empty */
}
+struct validation *
+state_retrieve(void)
+{
+ return NULL;
+}
+
START_TEST(tal_load_normal)
{
struct tal *tal;