The data remained the same until an RRDP server had a delta update; in such case the updated files weren't found and the snapshot was processed, so the local cache was built again. In case that the RRDP server didn't had updates, the root manifest wasn't found and the whole validation cycle results were discarded.
Now, when the manifest isn't found and the RRDP has no updates, force the snapshot processing to assure that the error isn't of the RP. Also, update the daemon that cleans up the RRDP visited URIs, so that it deletes the files from its corresponding workspace.
*result = now;
return 0;
}
+
+/*
+ * Maps an absolute @uri that begins with @uri_prefix (either 'rsync://' or
+ * 'https://') to a local URI. If a @workspace is set, append such location
+ * to the local-repository location (this workspace is used at https URIs).
+ *
+ * @result is allocated with the local URI.
+ *
+ * Returns 0 on success, otherwise an error code.
+ */
+int
+map_uri_to_local(char const *uri, char const *uri_prefix, char const *workspace,
+ char **result)
+{
+ char const *repository;
+ char *local;
+ size_t repository_len;
+ size_t uri_prefix_len;
+ size_t uri_len;
+ size_t workspace_len;
+ size_t extra_slash;
+ size_t offset;
+
+ repository = config_get_local_repository();
+ repository_len = strlen(repository);
+ uri_prefix_len = strlen(uri_prefix);
+ uri_len = strlen(uri);
+
+ uri += uri_prefix_len;
+ uri_len -= uri_prefix_len;
+ extra_slash = (repository[repository_len - 1] == '/') ? 0 : 1;
+
+ workspace_len = 0;
+ if (workspace != NULL)
+ workspace_len = strlen(workspace);
+
+ local = malloc(repository_len + extra_slash + workspace_len + uri_len +
+ 1);
+ if (local == NULL)
+ return pr_enomem();
+
+ offset = 0;
+ strcpy(local + offset, repository);
+ offset += repository_len;
+ if (extra_slash) {
+ strcpy(local + offset, "/");
+ offset += extra_slash;
+ }
+ if (workspace_len > 0) {
+ strcpy(local + offset, workspace);
+ offset += workspace_len;
+ }
+ strncpy(local + offset, uri, uri_len);
+ offset += uri_len;
+ local[offset] = '\0';
+
+ *result = local;
+ return 0;
+}
#define SRC_RTR_COMMON_H_
#include <pthread.h>
-#include <semaphore.h>
#include <stdbool.h>
#include <time.h>
#include <netinet/in.h>
int get_current_time(time_t *);
+int map_uri_to_local(char const *, char const*, char const *, char **);
+
#endif /* SRC_RTR_COMMON_H_ */
#include "common.h"
#include "log.h"
#include "random.h"
-#include "uri.h"
#define MAX_FD_ALLOWED 20
* - '= 0' no error
*/
static int
-get_local_path(char const *rcvd, char **result)
+get_local_path(char const *rcvd, char const *workspace, char **result)
{
struct stat attr;
- struct rpki_uri *uri;
char *tmp, *local_path;
size_t tmp_size;
int error;
- error = uri_create_mixed_str(&uri, rcvd, strlen(rcvd));
+ /* Currently, only rsync URIs are utilized */
+ local_path = NULL;
+ error = map_uri_to_local(rcvd, "rsync://", workspace, &local_path);
if (error)
- return error != -ENOMEM ? EINVAL : error;
-
- local_path = strdup(uri_get_local(uri));
- if (local_path == NULL) {
- error = pr_enomem();
- goto release_uri;
- }
+ return error;
error = stat(local_path, &attr);
if (error) {
tmp[tmp_size] = '\0';
free(local_path);
- uri_refput(uri);
*result = tmp;
return 0;
release_local:
free(local_path);
-release_uri:
- uri_refput(uri);
return error;
}
}
static int
-rename_all_roots(struct rem_dirs *rem_dirs, char **src)
+rename_all_roots(struct rem_dirs *rem_dirs, char **src, char const *workspace)
{
char *local_path, *delete_path;
size_t i;
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);
+ workspace, &local_path);
if (error < 0)
return error;
if (error > 0)
}
/*
- * Remove the files listed at @roots array of @roots_len size.
+ * Remove the files listed at @roots array of @roots_len size. The local files
+ * will be searched at the specified HTTP local @workspace.
*
* 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
* considers the relations (parent-child) at dirs.
*/
int
-delete_dir_daemon_start(char **roots, size_t roots_len)
+delete_dir_daemon_start(char **roots, size_t roots_len, char const *workspace)
{
pthread_t thread;
struct rem_dirs *arg;
if (error)
return error;
- error = rename_all_roots(arg, roots);
+ error = rename_all_roots(arg, roots, workspace);
if (error) {
rem_dirs_destroy(arg);
return error;
#include <stddef.h>
-int delete_dir_daemon_start(char **, size_t);
+int delete_dir_daemon_start(char **, size_t, char const *);
#endif /* SRC_DELETE_DIR_DAEMON_H_ */
}
/*
- * Verify the manifest location at the local RRDP workspace. If the manifest
- * exists, then update @mft_uri so that its location be at the workspace.
+ * Verify the manifest location at the local RRDP workspace.
*
* Don't log in case the @mft_uri doesn't exist at the RRDP workspace.
*/
static int
-verify_rrdp_mft_loc(struct rpki_uri **mft_uri)
+verify_rrdp_mft_loc(struct rpki_uri *mft_uri)
{
struct rpki_uri *tmp;
int error;
return -ENOENT;
tmp = NULL;
- error = uri_create_rsync_str_rrdp(&tmp, uri_get_global(*mft_uri),
- uri_get_global_len(*mft_uri));
+ error = uri_create_rsync_str_rrdp(&tmp, uri_get_global(mft_uri),
+ uri_get_global_len(mft_uri));
if (error)
return error;
if (!valid_file_or_dir(uri_get_local(tmp), true, false, NULL)) {
uri_refput(tmp);
- return -EINVAL;
+ return -ENOENT;
}
- uri_refput(*mft_uri);
- *mft_uri = tmp;
+ uri_refput(tmp);
return 0;
}
static int
exec_rrdp_method(struct sia_ca_uris *sia_uris)
{
+ bool data_updated;
int error;
/* Start working on the RRDP local workspace */
if (error)
return error;
- error = rrdp_load(sia_uris->rpkiNotify.uri);
- if (error) {
- db_rrdp_uris_workspace_disable();
- return error;
+ data_updated = false;
+ error = rrdp_load(sia_uris->rpkiNotify.uri, &data_updated);
+ if (error)
+ goto err;
+
+ error = verify_rrdp_mft_loc(sia_uris->mft.uri);
+ switch(error) {
+ case 0:
+ /* MFT exists, great! We're good to go. */
+ break;
+ case -ENOENT:
+ /* Doesn't exist and the RRDP data was updated: error */
+ if (data_updated)
+ goto err;
+
+ /* Otherwise, force the snapshot processing and check again */
+ error = rrdp_reload_snapshot(sia_uris->rpkiNotify.uri);
+ if (error)
+ goto err;
+ error = verify_rrdp_mft_loc(sia_uris->mft.uri);
+ if (error)
+ goto err;
+ break;
+ default:
+ goto err;
}
/* Successfully loaded (or no updates yet), update MFT local URI */
error = replace_rrdp_mft_uri(&sia_uris->mft);
- if (error) {
- db_rrdp_uris_workspace_disable();
- return error;
- }
+ if (error)
+ goto err;
return 0;
+err:
+ db_rrdp_uris_workspace_disable();
+ return error;
}
static int
exec_rsync_method(struct sia_ca_uris *sia_uris)
{
+ int error;
+
/* Stop working on the RRDP local workspace */
db_rrdp_uris_workspace_disable();
- return download_files(sia_uris->caRepository.uri, false, false);
+ error = download_files(sia_uris->caRepository.uri, false, false);
+ if (error)
+ return error;
+
+ return verify_mft_loc(sia_uris->mft.uri);
}
/*
* Currently only two access methods are supported, just consider those two:
* rsync and RRDP. If a new access method is supported, this function must
* change (and probably the sia_ca_uris struct as well).
+ *
+ * Both access method callbacks must verify the manifest existence.
*/
static int
use_access_method(struct sia_ca_uris *sia_uris,
*/
if (!new_level && db_rrdp_uris_workspace_get() != NULL &&
sia_uris->rpkiNotify.uri == NULL &&
- verify_rrdp_mft_loc(&sia_uris->mft.uri) == 0) {
+ verify_rrdp_mft_loc(sia_uris->mft.uri) == 0) {
(*retry_repo_sync) = false;
return replace_rrdp_mft_uri(&sia_uris->mft);
}
if (sia_uris->rpkiNotify.uri == NULL) {
primary_rrdp = false;
error = rsync_cb(sia_uris);
+ if (!error)
+ return 0;
goto verify_mft;
}
error = cb_primary(sia_uris);
if (!error) {
(*retry_repo_sync) = !primary_rrdp;
- goto verify_mft;
+ return 0;
}
if (primary_rrdp) {
working_repo_pop();
verify_mft:
+ /* Reach here on error or when both access methods were utilized */
switch (error) {
case 0:
/* Remove the error'd URI, since we got the repo files */
tal_elem_destroy(struct tal_elem *elem, bool remove_local)
{
if (remove_local)
- db_rrdp_uris_remove_all_local(elem->uris);
+ db_rrdp_uris_remove_all_local(elem->uris, elem->workspace);
db_rrdp_uris_destroy(elem->uris);
free(elem->file_name);
free(elem->workspace);
}
int
-db_rrdp_uris_remove_all_local(struct db_rrdp_uri *uris)
+db_rrdp_uris_remove_all_local(struct db_rrdp_uri *uris, char const *workspace)
{
struct uris_table *uri_node, *uri_tmp;
int error;
/* Remove each 'visited_uris' from all the table */
HASH_ITER(hh, uris->table, uri_node, uri_tmp) {
- error = visited_uris_delete_local(uri_node->visited_uris);
+ error = visited_uris_delete_local(uri_node->visited_uris,
+ workspace);
if (error)
return error;
}
int db_rrdp_uris_get_visited_uris(char const *, struct visited_uris **);
-int db_rrdp_uris_remove_all_local(struct db_rrdp_uri *);
+int db_rrdp_uris_remove_all_local(struct db_rrdp_uri *, char const *);
char const *db_rrdp_uris_workspace_get(void);
int db_rrdp_uris_workspace_enable(void);
remove_rrdp_uri_files(char const *notification_uri)
{
struct visited_uris *tmp;
+ char const *workspace;
int error;
/* Work with the existent visited uris */
if (error)
return error;
- return visited_uris_delete_local(tmp);
+ workspace = db_rrdp_uris_workspace_get();
+
+ return visited_uris_delete_local(tmp, workspace);
}
/* Mark the URI as errored with dummy data, so it won't be requested again */
return 0;
}
-/*
- * Try to get RRDP Update Notification file and process it accordingly.
- *
- * If there's an error that could lead to an inconsistent local repository
- * state, marks the @uri as error'd so that it won't be requested again during
- * the same validation cycle.
- *
- * If there are no errors, updates the local DB and marks the @uri as visited.
- *
- * If the @uri is being visited again, verify its previous visit state. If there
- * were no errors, just return success; otherwise, return error code -EPERM.
- */
-int
-rrdp_load(struct rpki_uri *uri)
+static int
+process_diff_session(struct update_notification *notification,
+ bool log_operation, struct visited_uris **visited)
+{
+ int error;
+
+ error = remove_rrdp_uri_files(notification->uri);
+ if (error)
+ return error;
+
+ return process_snapshot(notification, log_operation, visited);
+}
+
+static int
+__rrdp_load(struct rpki_uri *uri, bool force_snapshot, bool *data_updated)
{
struct update_notification *upd_notification;
struct visited_uris *visited;
bool log_operation;
int error, upd_error;
- if (!config_get_http_enabled())
+ (*data_updated) = false;
+
+ if (!config_get_http_enabled()) {
+ (*data_updated) = true;
return 0;
+ }
/* Avoid multiple requests on the same run */
requested = RRDP_URI_REQ_UNVISITED;
if (error && error != -ENOENT)
return error;
- switch(requested) {
- case RRDP_URI_REQ_VISITED:
- return 0;
- case RRDP_URI_REQ_UNVISITED:
- break;
- case RRDP_URI_REQ_ERROR:
- /* Log has been done before this call */
- return -EPERM;
+ if (!force_snapshot) {
+ switch(requested) {
+ case RRDP_URI_REQ_VISITED:
+ (*data_updated) = true;
+ return 0;
+ case RRDP_URI_REQ_UNVISITED:
+ break;
+ case RRDP_URI_REQ_ERROR:
+ /* Log has been done before this call */
+ return -EPERM;
+ }
+ } else {
+ if (requested != RRDP_URI_REQ_VISITED) {
+ pr_val_info("Skipping RRDP snapshot reload");
+ return -EINVAL;
+ }
}
log_operation = reqs_errors_log_uri(uri_get_global(uri));
- error = rrdp_parse_notification(uri, log_operation, &upd_notification);
+ error = rrdp_parse_notification(uri, log_operation, force_snapshot,
+ &upd_notification);
if (error)
goto upd_end;
goto upd_end;
}
- error = db_rrdp_uris_cmp(uri_get_global(uri),
- upd_notification->global_data.session_id,
- upd_notification->global_data.serial,
- &res);
- if (error)
- goto upd_destroy;
+ do {
+ /* Same flow as a session update */
+ if (force_snapshot) {
+ error = process_diff_session(upd_notification,
+ log_operation, &visited);
+ if (error)
+ goto upd_destroy;
+ (*data_updated) = true;
+ break;
+ }
- 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, log_operation,
- &visited);
+ error = db_rrdp_uris_cmp(uri_get_global(uri),
+ upd_notification->global_data.session_id,
+ upd_notification->global_data.serial,
+ &res);
if (error)
goto upd_destroy;
- break;
- case RRDP_URI_DIFF_SERIAL:
- error = process_diff_serial(upd_notification, log_operation,
- &visited);
- if (!error) {
- visited_uris_refget(visited);
+
+ switch (res) {
+ case RRDP_URI_EQUAL:
+ goto set_update;
+ case RRDP_URI_DIFF_SESSION:
+ /* Delete the old session files */
+ error = process_diff_session(upd_notification,
+ log_operation, &visited);
+ if (error)
+ goto upd_destroy;
+ (*data_updated) = true;
+ break;
+ case RRDP_URI_DIFF_SERIAL:
+ error = process_diff_serial(upd_notification,
+ log_operation, &visited);
+ if (!error) {
+ visited_uris_refget(visited);
+ (*data_updated) = true;
+ break;
+ }
+ /* Something went wrong, use snapshot */
+ pr_val_info("There was an error processing RRDP deltas, using the snapshot instead.");
+ case RRDP_URI_NOTFOUND:
+ error = process_snapshot(upd_notification, log_operation,
+ &visited);
+ if (error)
+ goto upd_destroy;
+ (*data_updated) = true;
break;
+ default:
+ pr_crit("Unexpected RRDP URI comparison result");
}
- /* Something went wrong, use snapshot */
- pr_val_info("There was an error processing RRDP deltas, using the snapshot instead.");
- case RRDP_URI_NOTFOUND:
- error = process_snapshot(upd_notification, log_operation,
- &visited);
- if (error)
- goto upd_destroy;
- break;
- default:
- pr_crit("Unexpected RRDP URI comparison result");
- }
+ } while (0);
/* Any update, and no error during the process, update db as well */
pr_val_debug("Updating local RRDP data of '%s' to:", uri_get_global(uri));
return error;
}
+
+/*
+ * Try to get RRDP Update Notification file and process it accordingly.
+ *
+ * If there's an error that could lead to an inconsistent local repository
+ * state, marks the @uri as error'd so that it won't be requested again during
+ * the same validation cycle.
+ *
+ * If there are no errors, updates the local DB and marks the @uri as visited.
+ *
+ * If the @uri is being visited again, verify its previous visit state. If there
+ * were no errors, just return success; otherwise, return error code -EPERM.
+ *
+ * @data_updated will be true if:
+ * - Delta files were processed
+ * - Snapshot file was processed
+ * - @uri was already visited at this cycle
+ */
+int
+rrdp_load(struct rpki_uri *uri, bool *data_updated)
+{
+ return __rrdp_load(uri, false, data_updated);
+}
+
+/*
+ * Force the processing of the snapshot. The update notification is requested
+ * again, omitting the 'If-Modified-Since' header at the HTTP request.
+ *
+ * Shouldn't be called if @uri had a previous error or hasn't been requested,
+ * still the check is done.
+ */
+int
+rrdp_reload_snapshot(struct rpki_uri *uri)
+{
+ bool tmp;
+
+ tmp = false;
+ return __rrdp_load(uri, true, &tmp);
+}
#ifndef SRC_RRDP_RRDP_LOADER_H_
#define SRC_RRDP_RRDP_LOADER_H_
+#include <stdbool.h>
#include "uri.h"
-int rrdp_load(struct rpki_uri *);
+int rrdp_load(struct rpki_uri *, bool *);
+int rrdp_reload_snapshot(struct rpki_uri *);
#endif /* SRC_RRDP_RRDP_LOADER_H_ */
*
* If the server didn't sent the file, due to the validation of
* 'If-Modified-Since' header, return 0 and set @result to NULL.
+ *
+ * Set @force to true to omit 'If-Modified-Since' header.
*/
int
-rrdp_parse_notification(struct rpki_uri *uri, bool log_operation,
+rrdp_parse_notification(struct rpki_uri *uri, bool log_operation, bool force,
struct update_notification **result)
{
long last_update;
pr_val_debug("Processing notification '%s'.", uri_get_global(uri));
last_update = 0;
- error = db_rrdp_uris_get_last_update(uri_get_global(uri), &last_update);
- if (error && error != -ENOENT)
- return error;
+ if (!force) {
+ error = db_rrdp_uris_get_last_update(uri_get_global(uri), &last_update);
+ if (error && error != -ENOENT)
+ return error;
+ }
error = download_file(uri, last_update, log_operation);
if (error < 0)
#include "uri.h"
#include "visited_uris.h"
-int rrdp_parse_notification(struct rpki_uri *, bool,
+int rrdp_parse_notification(struct rpki_uri *, bool, bool,
struct update_notification **);
int rrdp_parse_snapshot(struct update_notification *, struct visited_uris *,
bool);
URI_HTTPS,
};
+static char const *const PFX_RSYNC = "rsync://";
+static char const *const PFX_HTTPS = "https://";
+
/**
* All rpki_uris are guaranteed to be RSYNC URLs right now.
*
static int
validate_uri_begin(char const *uri_pfx, const size_t uri_pfx_len,
- char const *global, size_t global_len, size_t *size, int error)
+ char const *global, size_t global_len, int error)
{
if (global_len < uri_pfx_len
|| strncasecmp(uri_pfx, global, uri_pfx_len) != 0) {
return error;
}
- (*size) = uri_pfx_len;
return 0;
}
static int
validate_gprefix(char const *global, size_t global_len, uint8_t flags,
- size_t *size, enum rpki_uri_type *type)
+ enum rpki_uri_type *type)
{
- static char const *const PFX_RSYNC = "rsync://";
- static char const *const PFX_HTTPS = "https://";
size_t const PFX_RSYNC_LEN = strlen(PFX_RSYNC);
size_t const PFX_HTTPS_LEN = strlen(PFX_HTTPS);
uint8_t l_flags;
if (l_flags == URI_VALID_RSYNC) {
(*type) = URI_RSYNC;
return validate_uri_begin(PFX_RSYNC, PFX_RSYNC_LEN, global,
- global_len, size, ENOTRSYNC);
+ global_len, ENOTRSYNC);
}
if (l_flags == URI_VALID_HTTPS) {
(*type) = URI_HTTPS;
return validate_uri_begin(PFX_HTTPS, PFX_HTTPS_LEN, global,
- global_len, size, ENOTHTTPS);
+ global_len, ENOTHTTPS);
}
if (l_flags != (URI_VALID_RSYNC | URI_VALID_HTTPS))
pr_crit("Unknown URI flag");
/* It has both flags */
error = validate_uri_begin(PFX_RSYNC, PFX_RSYNC_LEN, global, global_len,
- size, 0);
+ 0);
if (!error) {
(*type) = URI_RSYNC;
return 0;
}
error = validate_uri_begin(PFX_HTTPS, PFX_HTTPS_LEN, global, global_len,
- size, 0);
+ 0);
if (error) {
pr_val_warn("URI '%s' does not begin with '%s' nor '%s'.",
global, PFX_RSYNC, PFX_HTTPS);
}
static int
-get_local_workspace(char **result, size_t *result_len)
+get_local_workspace(char **result)
{
char const *workspace;
char *tmp;
workspace = db_rrdp_uris_workspace_get();
if (workspace == NULL) {
*result = NULL;
- *result_len = 0;
return 0;
}
return pr_enomem();
*result = tmp;
- *result_len = strlen(tmp);
return 0;
}
g2l(char const *global, size_t global_len, uint8_t flags, char **result,
enum rpki_uri_type *result_type)
{
- char const *repository;
char *local;
char *workspace;
- size_t prefix_len;
- size_t repository_len;
- size_t extra_slash;
- size_t offset;
- size_t workspace_len;
enum rpki_uri_type type;
int error;
- repository = config_get_local_repository();
- repository_len = strlen(repository);
-
- error = validate_gprefix(global, global_len, flags, &prefix_len, &type);
+ error = validate_gprefix(global, global_len, flags, &type);
if (error)
return error;
- global += prefix_len;
- global_len -= prefix_len;
- extra_slash = (repository[repository_len - 1] == '/') ? 0 : 1;
-
workspace = NULL;
- workspace_len = 0;
if ((flags & URI_USE_RRDP_WORKSPACE) != 0) {
- error = get_local_workspace(&workspace, &workspace_len);
+ error = get_local_workspace(&workspace);
if (error)
return error;
}
- local = malloc(repository_len + extra_slash + workspace_len +
- global_len + 1);
- if (!local) {
+ error = map_uri_to_local(global,
+ type == URI_RSYNC ? PFX_RSYNC : PFX_HTTPS,
+ workspace,
+ &local);
+ if (error) {
free(workspace);
- return pr_enomem();
- }
-
- offset = 0;
- strcpy(local + offset, repository);
- offset += repository_len;
- if (extra_slash) {
- strcpy(local + offset, "/");
- offset += extra_slash;
- }
- if (workspace_len > 0) {
- strcpy(local + offset, workspace);
- offset += workspace_len;
+ return error;
}
- strncpy(local + offset, global, global_len);
- offset += global_len;
- local[offset] = '\0';
free(workspace);
*result = local;
free(*elem);
}
+/*
+ * Delete all the corresponding local files of @uris located at @workspace
+ */
int
-visited_uris_delete_local(struct visited_uris *uris)
+visited_uris_delete_local(struct visited_uris *uris, char const *workspace)
{
struct uris_roots roots;
int error;
if (roots.len == 0)
goto success;
- error = delete_dir_daemon_start(roots.array, roots.len);
+ error = delete_dir_daemon_start(roots.array, roots.len, workspace);
if (error)
goto err;
success:
#ifndef SRC_VISITED_URIS_H_
#define SRC_VISITED_URIS_H_
-#include <stdbool.h>
-
struct visited_uris;
int visited_uris_create(struct visited_uris **);
int visited_uris_add(struct visited_uris *, char const *);
int visited_uris_remove(struct visited_uris *, char const *);
-int visited_uris_delete_local(struct visited_uris *);
+int visited_uris_delete_local(struct visited_uris *, char const *);
#endif /* SRC_VISITED_URIS_H_ */