+Remove unnecessary functions at 'visited_uris.h', rename function that deletes local files.
+Refactor the way the old repository files related to an RRDP URI are deleted, instead of deleting the 'best guess' of the root dir, delete each root dir of the mft uris stored at visited uris struct. The daemon will do its best effort to remove the files.
+Update year 2019 refs by 2020.
+Use 'root-except-ta' rsync strategy as default (and update docs as well), to prevent rsyncs to overwrite repositories fetched via RRDP.
+Remove 'create_snapshot' logic from 'rrdp_parser', wasn't of too much help since the 'If-Modified-Since' impl already avoids to load unnecessary data.
+Remove local repository files related to an RRDP URI only on session ID updates; also, reset RSYNC visited URIs of a TAL if an RRDP repository sync fails, this helps to refresh the repo via rsync (if rsync is the secondary option to fetch it).
+Fix 'tal_test.c' error comparing loaded URIs.
parameters field;
```
-As of 2019-08-12, many signed objects in the global RPKI break this rule.
+As of 2020-01-31, many signed objects in the global RPKI break this rule.
If not `ignore`d, Fort will report this incidence with the following error message:
- **Type:** Enumeration (`off`, `strict`, `root`, `root-except-ta`)
- **Availability:** `argv` and JSON
-- **Default:** `root`
+- **Default:** `root-except-ta`
rsync synchronization strategy. Commands the way rsync URLs are approached during downloads.
"rsync": {
"<a href="#--rsyncenabled">enabled</a>": true,
"<a href="#--rsyncpriority">priority</a>": 50,
- "<a href="#--rsyncstrategy">strategy</a>": "root",
+ "<a href="#--rsyncstrategy">strategy</a>": "root-except-ta",
"retry": {
"<a href="#--rsyncretrycount">count</a>": 2,
"<a href="#--rsyncretryinterval">interval</a>": 5
- **Type:** Enumeration (`strict`, `root`, `root-except-ta`)
- **Availability:** `argv` and JSON
-- **Default:** `root`
+- **Default:** `root-except-ta`
rsync synchronization strategy. Commands the way rsync URLs are approached during downloads.
"rsync": {
"enabled": true,
"priority": 50,
- "strategy": "root",
+ "strategy": "root-except-ta",
"retry": {
"count": 2,
"interval": 5
.br
- \fIstrict\fR: will be the same as \fB--rsync.strategy=strict\fR.
.br
-- \fIroot\fR \fB(default value)\fR: will be the same as
-\fB--rsync.strategy=root\fR.
+- \fIroot\fR: will be the same as \fB--rsync.strategy=root\fR.
.br
-- \fIroot-except-ta\fR: will be the same as
+- \fIroot-except-ta\fR \fB(default value)\fR: will be the same as
\fB--rsync.strategy=root-except-ta\fR.
.RE
.RE
\fIrsync\fR download strategy; states the way rsync URLs are approached during
downloads. It can have one of three values:
.IR strict ", "
-.IB "root" "(default value)" \fR, \fR
-.IR root-except-ta "."
+.IR root ", "
+.IB "root-except-ta" "(default value)" \fR. \fR
.P
.I strict
.RS 4
"rsync": {
"enabled": true,
"priority": 50,
- "strategy": "root",
+ "strategy": "root-except-ta",
"retry": {
"count": 2,
"interval": 5
.P
.\".SH COPYRIGHT
-.\" FORT-validator 2019
+.\" FORT-validator 2020
.\" Licensed under the blah blah...
.SH SEE ALSO
goto revert_port;
}
- rpki_config.sync_strategy = RSYNC_ROOT;
+ rpki_config.sync_strategy = RSYNC_ROOT_EXCEPT_TA;
rpki_config.shuffle_tal_uris = false;
rpki_config.maximum_certificate_depth = 32;
rpki_config.mode = SERVER;
rpki_config.rsync.enabled = true;
rpki_config.rsync.priority = 50;
- rpki_config.rsync.strategy = RSYNC_ROOT;
+ rpki_config.rsync.strategy = RSYNC_ROOT_EXCEPT_TA;
rpki_config.rsync.retry.count = 2;
rpki_config.rsync.retry.interval = 5;
rpki_config.rsync.program = strdup("rsync");
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include "common.h"
#include "log.h"
#include "random.h"
#include "uri.h"
#define MAX_FD_ALLOWED 20
+struct rem_dirs {
+ char **arr;
+ size_t arr_len;
+ size_t arr_set;
+};
+
static int
remove_file(char const *location)
{
+ pr_debug("Trying to remove file '%s'.", location);
if (remove(location))
return pr_errno(errno, "Couldn't delete file '%s'", location);
return 0;
static int
remove_dir(char const *location)
{
+ pr_debug("Trying to remove dir '%s'.", location);
if (rmdir(location))
return pr_errno(errno, "Couldn't delete directory '%s'",
location);
static void *
remove_from_root(void *arg)
{
- char *root_arg = arg;
- char *root;
+ struct rem_dirs *root_arg = arg;
+ char **dirs_arr;
+ size_t len, i;
int error;
- pr_debug("Trying to remove dir '%s'.", root_arg);
- root = strdup(root_arg);
+ dirs_arr = root_arg->arr;
+ len = root_arg->arr_set;
/* Release received arg, and detach thread */
free(root_arg);
pthread_detach(pthread_self());
- if (root == NULL) {
- pr_err("Couldn't allocate memory for a string, the directory '%s' won't be deleted, please delete it manually.",
- root);
- return NULL;
- }
-
- error = nftw(root, traverse, MAX_FD_ALLOWED,
- FTW_DEPTH|FTW_MOUNT|FTW_PHYS);
- if (error) {
- if (errno)
- pr_errno(errno, "Error deleting directory '%s', please delete it manually.",
- root);
- else
- pr_err("Couldn't delete directory '%s', please delete it manually",
- root);
+ for (i = 0; i < len; i++) {
+ error = nftw(dirs_arr[i], traverse, MAX_FD_ALLOWED,
+ FTW_DEPTH|FTW_MOUNT|FTW_PHYS);
+ if (error) {
+ if (errno)
+ pr_debug("Error deleting directory '%s', please delete it manually: %s",
+ dirs_arr[i], strerror(errno));
+ else
+ pr_debug("Couldn't delete directory '%s', please delete it manually",
+ dirs_arr[i]);
+ }
+ /* Release at once, won't be needed anymore */
+ free(dirs_arr[i]);
}
- pr_debug("Done removing dir '%s'.", root);
- free(root);
+ pr_debug("Done removing dirs.");
+ free(dirs_arr);
return NULL;
}
+/*
+ * Soft/hard error logic utilized, beware to prepare caller:
+ * - '> 0' is a soft error
+ * - '< 0' is a hard error
+ * - '= 0' no error
+ */
static int
get_local_path(char const *rcvd, char **result)
{
error = uri_create_mixed_str(&uri, rcvd, strlen(rcvd));
if (error)
- return error;
+ return error != -ENOMEM ? EINVAL : error;
local_path = strdup(uri_get_local(uri));
if (local_path == NULL) {
error = stat(local_path, &attr);
if (error) {
- error = -pr_errno(errno, "Error reading path '%s'", local_path);
+ /* Soft error */
+ pr_debug("Error reading path '%s' (discarding): %s",
+ local_path, strerror(errno));
+ error = errno;
goto release_local;
}
if (!S_ISDIR(attr.st_mode)) {
- error = pr_err("Path '%s' exists but is not a directory.",
+ /* Soft error */
+ pr_debug("Path '%s' exists but is not a directory (discarding).",
local_path);
+ error = ENOTDIR;
goto release_local;
}
return error;
}
+/*
+ * Soft/hard error logic utilized, beware to prepare caller:
+ * - '> 0' is a soft error
+ * - '< 0' is a hard error
+ * - '= 0' no error
+ */
static int
rename_local_path(char const *rcvd, char **result)
{
error = rename(rcvd, tmp);
if (error) {
free(tmp);
- return -pr_errno(errno, "Couldn't rename '%s' to delete it.",
- rcvd);
+ pr_debug("Couldn't rename '%s' to delete it (discarding): %s",
+ rcvd, strerror(errno));
+ return errno; /* Soft error */
}
*result = tmp;
return 0;
}
+static int
+rename_all_roots(struct rem_dirs *rem_dirs, char **src)
+{
+ char *local_path, *delete_path;
+ size_t i;
+ int error;
+
+ for (i = 0; i < rem_dirs->arr_len; i++) {
+ local_path = NULL;
+ error = get_local_path(src[(rem_dirs->arr_len - 1) - i],
+ &local_path);
+ if (error < 0)
+ return error;
+ if (error > 0)
+ continue;
+
+ delete_path = NULL;
+ error = rename_local_path(local_path, &delete_path);
+ free(local_path);
+ if (error < 0)
+ return error;
+ if (error > 0)
+ continue;
+ rem_dirs->arr[rem_dirs->arr_set++] = delete_path;
+ }
+
+ return 0;
+}
+
+static int
+rem_dirs_create(size_t arr_len, struct rem_dirs **result)
+{
+ struct rem_dirs *tmp;
+
+ tmp = malloc(sizeof(struct rem_dirs));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ tmp->arr = calloc(arr_len, sizeof(char *));
+ if (tmp->arr == NULL) {
+ free(tmp);
+ return pr_enomem();
+ }
+
+ tmp->arr_len = arr_len;
+ tmp->arr_set = 0;
+
+ *result = tmp;
+ return 0;
+}
+
+static void
+rem_dirs_destroy(struct rem_dirs *rem_dirs)
+{
+ size_t i;
+
+ for (i = 0; i < rem_dirs->arr_set; i++)
+ free(rem_dirs->arr[i]);
+ free(rem_dirs->arr);
+ free(rem_dirs);
+}
+
/*
- * Start the @thread that will delete every file under @path, @thread must not
- * be joined, since it's detached. @path will be renamed at the file system.
+ * Remove the files listed at @roots array of @roots_len size.
+ *
+ * The daemon will be as quiet as possible, since most of its job is done
+ * asynchronously. Also, it works on the best possible effort; some errors are
+ * treated as "soft" errors, since the directory deletion still doesn't
+ * considers the relations (parent-child) at dirs.
*/
int
-delete_dir_daemon_start(char const *path)
+delete_dir_daemon_start(char **roots, size_t roots_len)
{
pthread_t thread;
- char *local_path, *delete_path;
+ struct rem_dirs *arg;
int error;
- error = get_local_path(path, &local_path);
+ arg = NULL;
+ error = rem_dirs_create(roots_len, &arg);
if (error)
return error;
- delete_path = NULL;
- error = rename_local_path(local_path, &delete_path);
+ error = rename_all_roots(arg, roots);
if (error) {
- free(local_path);
+ rem_dirs_destroy(arg);
return error;
}
/* Thread arg is released at thread before being detached */
- errno = pthread_create(&thread, NULL, remove_from_root,
- (void *) delete_path);
+ errno = pthread_create(&thread, NULL, remove_from_root, (void *) arg);
if (errno) {
- free(delete_path);
- free(local_path);
- return -pr_errno(errno,
+ rem_dirs_destroy(arg);
+ return pr_errno(errno,
"Could not spawn the delete dir daemon thread");
}
- free(local_path);
return 0;
}
#ifndef SRC_DELETE_DIR_DAEMON_H_
#define SRC_DELETE_DIR_DAEMON_H_
-int delete_dir_daemon_start(char const *);
+#include <stddef.h>
+
+int delete_dir_daemon_start(char **, size_t);
#endif /* SRC_DELETE_DIR_DAEMON_H_ */
/* Remove each 'visited_uris' from all the table */
HASH_ITER(hh, uris->table, uri_node, uri_tmp) {
- error = visited_uris_remove_local(uri_node->visited_uris);
+ error = visited_uris_delete_local(uri_node->visited_uris);
if (error)
return error;
}
#include "rrdp/db/db_rrdp_uris.h"
#include "rrdp/rrdp_objects.h"
#include "rrdp/rrdp_parser.h"
+#include "rsync/rsync.h"
#include "config.h"
#include "log.h"
#include "thread_var.h"
if (error)
return error;
- return visited_uris_remove_local(tmp);
+ return visited_uris_delete_local(tmp);
}
/* Mark the URI as errored with dummy data, so it won't be requested again */
switch (res) {
case RRDP_URI_EQUAL:
goto set_update;
+ case RRDP_URI_DIFF_SESSION:
+ /* Delete the old session files */
+ error = remove_rrdp_uri_files(upd_notification->uri);
+ if (error)
+ goto upd_destroy;
+ error = process_snapshot(upd_notification, &visited);
+ if (error)
+ goto upd_destroy;
+ break;
case RRDP_URI_DIFF_SERIAL:
error = process_diff_serial(upd_notification, &visited);
if (!error) {
}
/* Something went wrong, use snapshot */
pr_warn("There was an error processing RRDP deltas, using the snapshot instead.");
- case RRDP_URI_DIFF_SESSION:
- /* Delete the old session files */
- error = remove_rrdp_uri_files(upd_notification->uri);
- if (error)
- break;
case RRDP_URI_NOTFOUND:
error = process_snapshot(upd_notification, &visited);
+ if (error)
+ goto upd_destroy;
break;
default:
pr_crit("Unexpected RRDP URI comparison result");
}
- if (error)
- goto upd_destroy;
-
/* Any update, and no error during the process, update db as well */
pr_debug("Updating local RRDP data of '%s' to:", uri_get_global(uri));
pr_debug("- Session ID: %s", upd_notification->global_data.session_id);
upd_error:
/* Don't fall here on success */
if (error) {
+ /* Reset RSYNC visited URIs, this may force the update */
+ reset_downloaded();
upd_error = mark_rrdp_uri_request_err(uri_get_global(uri));
if (upd_error)
return upd_error;
struct rdr_notification_ctx {
/* Data being parsed */
struct update_notification *notification;
- /* The snapshot must be allocated? */
- bool create_snapshot;
/* Unordered list of deltas */
struct deltas_parsed deltas;
};
static int
rdr_notification_ctx_init(struct rdr_notification_ctx *ctx)
{
- rrdp_uri_cmp_result_t res;
- int error;
-
- ctx->create_snapshot = false;
-
- error = db_rrdp_uris_cmp(ctx->notification->uri,
- ctx->notification->global_data.session_id,
- ctx->notification->global_data.serial,
- &res);
- if (error)
- return error;
-
- switch (res) {
- case RRDP_URI_EQUAL:
- /* Just validate content */
- break;
- case RRDP_URI_DIFF_SERIAL:
- /* Get the deltas to process and the snapshot */
- case RRDP_URI_DIFF_SESSION:
- /* Get only the snapshot */
- case RRDP_URI_NOTFOUND:
- ctx->create_snapshot = true;
- break;
- default:
- pr_crit("Unexpected RRDP URI comparison result");
- }
-
deltas_parsed_init(&ctx->deltas);
return 0;
}
error = parse_notification_delta(reader, ctx);
} else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_SNAPSHOT)) {
error = parse_doc_data(reader, true, true,
- (ctx->create_snapshot ?
- &ctx->notification->snapshot : NULL));
+ &ctx->notification->snapshot);
} else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_NOTIFICATION)) {
/* No need to validate session ID and serial */
error = parse_global_data(reader,
uri_refput(rsync_uri);
return error;
}
+
+void
+reset_downloaded(void)
+{
+ struct validation *state;
+ struct uri_list *list;
+ struct uri *uri;
+
+ state = state_retrieve();
+ if (state == NULL)
+ return;
+
+ list = validation_rsync_visited_uris(state);
+
+ while (!SLIST_EMPTY(list)) {
+ uri = SLIST_FIRST(list);
+ SLIST_REMOVE_HEAD(list, next);
+ uri_refput(uri->uri);
+ free(uri);
+ }
+}
int rsync_create(struct uri_list **);
void rsync_destroy(struct uri_list *);
+void reset_downloaded(void);
#endif /* SRC_RSYNC_RSYNC_H_ */
#include <string.h>
#include "log.h"
#include "delete_dir_daemon.h"
+#include "data_structure/array_list.h"
#include "data_structure/uthash_nonfatal.h"
struct visited_elem {
unsigned int refs;
};
+DEFINE_ARRAY_LIST_STRUCT(uris_roots, char *);
+DEFINE_ARRAY_LIST_FUNCTIONS(uris_roots, char *, static)
+
static int
visited_elem_create(struct visited_elem **elem, char const *uri)
{
return 0;
}
-bool
-visited_uris_exists(struct visited_uris *uris, char const *uri)
-{
- return elem_find(uris, uri) != NULL;
-}
-
-int
-visited_uris_get_root(struct visited_uris *uris, char **result)
+static int
+visited_uris_to_arr(struct visited_uris *uris, struct uris_roots *roots)
{
struct visited_elem *elem;
- char *tmp, *ptr;
+ char *tmp, *last_slash;
size_t size;
- int i;
- elem = uris->table;
- if (elem == NULL) {
- *result = NULL;
- return 0;
+ for (elem = uris->table; elem != NULL; elem = elem->hh.next) {
+ last_slash = strrchr(elem->uri, '/');
+ size = last_slash - elem->uri;
+ tmp = malloc(size + 1);
+ if (tmp == NULL)
+ return pr_enomem();
+ strncpy(tmp, elem->uri, size);
+ tmp[size] = '\0';
+ uris_roots_add(roots, &tmp);
}
- i = 0;
- ptr = strchr(elem->uri, '/');
- while(i < 2) {
- ptr = strchr(ptr + 1, '/');
- i++;
- }
- size = ptr - elem->uri;
- tmp = malloc(size + 1);
- if (tmp == NULL)
- return pr_enomem();
-
- strncpy(tmp, elem->uri, size);
- tmp[size] = '\0';
-
- *result = tmp;
return 0;
}
+static void
+uris_root_destroy(char **elem)
+{
+ free(*elem);
+}
+
int
-visited_uris_remove_local(struct visited_uris *uris)
+visited_uris_delete_local(struct visited_uris *uris)
{
- char *root_path;
+ struct uris_roots roots;
int error;
- error = visited_uris_get_root(uris, &root_path);
- if (error)
- return error;
+ uris_roots_init(&roots);
- if (root_path == NULL)
- return 0;
+ error = visited_uris_to_arr(uris, &roots);
+ if (error)
+ goto err;
- error = delete_dir_daemon_start(root_path);
- if (error) {
- free(root_path);
- return error;
- }
+ if (roots.len == 0)
+ goto success;
- free(root_path);
+ error = delete_dir_daemon_start(roots.array, roots.len);
+ if (error)
+ goto err;
+success:
+ uris_roots_cleanup(&roots, uris_root_destroy);
return 0;
+err:
+ uris_roots_cleanup(&roots, uris_root_destroy);
+ return error;
}
int visited_uris_add(struct visited_uris *, char const *);
int visited_uris_remove(struct visited_uris *, char const *);
-bool visited_uris_exists(struct visited_uris *, char const *);
-int visited_uris_get_root(struct visited_uris *, char **);
-int visited_uris_remove_local(struct visited_uris *);
+int visited_uris_delete_local(struct visited_uris *);
#endif /* SRC_VISITED_URIS_H_ */
ck_assert_int_eq(tal_load("tal/lacnic.tal", &tal), 0);
ck_assert_uint_eq(tal->uris.count, 3);
- ck_assert_str_eq(tal->uris.array[0],
+ 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], "https://potato");
- ck_assert_str_eq(tal->uris.array[2], "rsync://potato");
+ ck_assert_str_eq(tal->uris.array[1]->global, "https://potato");
+ ck_assert_str_eq(tal->uris.array[2]->global, "rsync://potato");
ck_assert_uint_eq(ARRAY_LEN(decoded), tal->spki_len);
for (i = 0; i < ARRAY_LEN(decoded); i++)