+Parse and process delta files, includes file deleting due to a withdraw as well as the parent dir deletion if the dir is empty.
+Consider that 'publish' elements have an optional 'hash' in some cases.
+Calculate the deltas necessary to process from a notification file, based on the current loaded serial and the last downloaded serial.
+Add handler function to get the last downloaded serial.
+The RRDP loader gets the notification file and takes the decission to process such file (no changes, serial update, session update, or new uri). Its code was at certificate.c, but was rellocated here.
+Remove SLIST from rrdp_objects, as well as some other properties that aren't necessary (lists at delta and at snapshot structs).
+Prepare 'deltas_head' to be referenced from distinct parts.
+Fix serial validation when parsing a 'son' object (e.g. validating a delta that was listed at the update notification file).
+No need to return parsed snapshot and delta, since they are processed asap and no further actions are required with the allocated structs.
fort_SOURCES += rrdp/db_rrdp.h rrdp/db_rrdp.c
fort_SOURCES += rrdp/rrdp_handler.h rrdp/rrdp_handler.c
+fort_SOURCES += rrdp/rrdp_loader.h rrdp/rrdp_loader.c
fort_SOURCES += rrdp/rrdp_objects.h rrdp/rrdp_objects.c
fort_SOURCES += rrdp/rrdp_parser.h rrdp/rrdp_parser.c
#include "object/name.h"
#include "object/manifest.h"
#include "object/signed_object.h"
-#include "rrdp/rrdp_objects.h"
-#include "rrdp/rrdp_parser.h"
+#include "rrdp/rrdp_loader.h"
#include "rsync/rsync.h"
/* Just to prevent some line breaking. */
static int
exec_rrdp_method(struct sia_ca_uris *sia_uris)
{
- struct update_notification *upd_notification;
- struct snapshot *snapshot;
- enum rrdp_uri_cmp_result res;
- int error;
-
- error = rrdp_parse_notification(sia_uris->rpkiNotify.uri,
- &upd_notification);
- if (error)
- return error;
-
- res = rhandler_uri_cmp(uri_get_global(sia_uris->rpkiNotify.uri),
- upd_notification->global_data.session_id,
- upd_notification->global_data.serial);
- switch(res) {
- case RRDP_URI_EQUAL:
- update_notification_destroy(upd_notification);
- fnstack_pop();
- return 0;
- case RRDP_URI_DIFF_SERIAL:
- /* FIXME (now) Fetch and process the deltas */
- /* Store the new value */
- error = rhandler_uri_update(
- uri_get_global(sia_uris->rpkiNotify.uri),
- upd_notification->global_data.session_id,
- upd_notification->global_data.serial);
- break;
- case RRDP_URI_DIFF_SESSION:
- /* FIXME (now) delete the old session files */
- case RRDP_URI_NOTFOUND:
- /* Process the snapshot */
- error = rrdp_parse_snapshot(upd_notification, &snapshot);
- if (error) {
- break;
- }
- snapshot_destroy(snapshot);
- fnstack_pop(); /* FIXME (now) Snapshot pop, shouldn't be here */
- /* Store the new value */
- error = rhandler_uri_update(
- uri_get_global(sia_uris->rpkiNotify.uri),
- upd_notification->global_data.session_id,
- upd_notification->global_data.serial);
-
- break;
- default:
- pr_crit("Unknown RRDP URI comparison result");
- }
-
- /*
- * FIXME (now) Now do the validation, start by the root manifest
- */
- update_notification_destroy(upd_notification);
- fnstack_pop();
- if (error) {
- return error;
- }
- return 0;
+ return rrdp_load(sia_uris->rpkiNotify.uri);
}
static int
rrdp_handler.uri_cmp = rrdp_uri_cmp;
rrdp_handler.uri_update = rrdp_uri_update;
+ rrdp_handler.uri_get_serial = rrdp_uri_get_serial;
error = validation_prepare(&state, tal, &validation_handler,
&rrdp_handler);
return 0;
}
+int
+db_rrdp_get_serial(struct db_rrdp *db, char const *uri, unsigned long *serial)
+{
+ struct db_rrdp_uri *found;
+
+ HASH_FIND_STR(db->uris, uri, found);
+ if (found == NULL)
+ return -ENOENT;
+
+ *serial = found->data.serial;
+
+ return 0;
+}
+
int
db_rrdp_create(struct db_rrdp **result)
{
char const *, unsigned long);
int db_rrdp_add_uri(struct db_rrdp *, char const *, char const *,
unsigned long);
+int db_rrdp_get_serial(struct db_rrdp *, char const *, unsigned long *);
#endif /* SRC_RRDP_DB_RRDP_H_ */
? handler->uri_update(uri, session_id, serial)
: 0;
}
+
+int
+rhandler_uri_get_serial(char const *uri, unsigned long *serial)
+{
+ struct rrdp_handler const *handler;
+ int error;
+
+ error = get_current_threads_handler(&handler);
+ if (error)
+ return error;
+
+ return (handler->uri_get_serial != NULL)
+ ? handler->uri_get_serial(uri, serial)
+ : 0;
+}
unsigned long);
/* Add or update an RRDP URI */
int (*uri_update)(char const *, char const *, unsigned long);
+ /* Get the data related to an URI */
+ int (*uri_get_serial)(char const *, unsigned long *);
};
enum rrdp_uri_cmp_result rhandler_uri_cmp(char const *, char const *, unsigned long);
int rhandler_uri_update(char const *, char const *, unsigned long);
+int rhandler_uri_get_serial(char const *, unsigned long *);
#endif /* SRC_RRDP_RRDP_HANDLER_H_ */
--- /dev/null
+#include "rrdp_loader.h"
+
+#include "rrdp/rrdp_handler.h"
+#include "rrdp/rrdp_objects.h"
+#include "rrdp/rrdp_parser.h"
+#include "log.h"
+#include "thread_var.h"
+
+/* Fetch and process the deltas from the @notification located at @uri */
+static int
+process_diff_serial(struct update_notification *notification, char const *uri)
+{
+ unsigned long serial;
+ int error;
+
+ error = rhandler_uri_get_serial(uri, &serial);
+ if (error)
+ return error;
+
+ error = rrdp_process_deltas(notification, serial);
+ if (error)
+ return error;
+
+ /* Store the new value */
+ return rhandler_uri_update(uri, notification->global_data.session_id,
+ notification->global_data.serial);
+}
+
+/* Fetch and process the snapshot from the @notification located at @uri */
+static int
+process_snapshot(struct update_notification *notification, char const *uri)
+{
+ int error;
+
+ error = rrdp_parse_snapshot(notification);
+ if (error)
+ return error;
+
+ return rhandler_uri_update(uri, notification->global_data.session_id,
+ notification->global_data.serial);
+}
+
+int
+rrdp_load(struct rpki_uri *uri)
+{
+ struct update_notification *upd_notification;
+ enum rrdp_uri_cmp_result res;
+ int error;
+
+ error = rrdp_parse_notification(uri, &upd_notification);
+ if (error)
+ return error;
+
+ res = rhandler_uri_cmp(uri_get_global(uri),
+ upd_notification->global_data.session_id,
+ upd_notification->global_data.serial);
+ switch(res) {
+ case RRDP_URI_EQUAL:
+ break; /* Error 0 its ok */
+ case RRDP_URI_DIFF_SERIAL:
+ error = process_diff_serial(upd_notification,
+ uri_get_global(uri));
+ break;
+ case RRDP_URI_DIFF_SESSION:
+ /* FIXME (now) delete the old session files */
+ case RRDP_URI_NOTFOUND:
+ error = process_snapshot(upd_notification, uri_get_global(uri));
+ break;
+ default:
+ pr_crit("Unexpected RRDP URI comparison result");
+ }
+
+ /*
+ * FIXME (now) Now do the validation, start by the root manifest
+ */
+ update_notification_destroy(upd_notification);
+ fnstack_pop(); /* Pop from rrdp_parse_notification */
+
+ return error;
+}
--- /dev/null
+#ifndef SRC_RRDP_RRDP_LOADER_H_
+#define SRC_RRDP_RRDP_LOADER_H_
+
+#include "uri.h"
+
+int rrdp_load(struct rpki_uri *);
+
+#endif /* SRC_RRDP_RRDP_LOADER_H_ */
#include "rrdp_objects.h"
+#include <sys/queue.h>
+#include <string.h>
#include "log.h"
struct xml_source {
xmlDoc *doc;
};
+struct delta_head {
+ unsigned long serial;
+ struct doc_data doc_data;
+ unsigned int references;
+ SLIST_ENTRY(delta_head) next;
+};
+
+/* List of deltas inside an update notification file */
+SLIST_HEAD(deltas_head, delta_head);
+
int
global_data_init(struct global_data *data)
{
return 0;
}
+static int
+delta_head_create(struct delta_head **result)
+{
+ struct delta_head *tmp;
+
+ tmp = malloc(sizeof(struct delta_head));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ doc_data_init(&tmp->doc_data);
+ tmp->references = 1;
+
+ *result = tmp;
+ return 0;
+}
+
+unsigned long
+delta_head_get_serial(struct delta_head *delta_head)
+{
+ return delta_head->serial;
+}
+
+struct doc_data *
+delta_head_get_doc_data(struct delta_head *delta_head)
+{
+ return &delta_head->doc_data;
+}
+
+void
+delta_head_refget(struct delta_head *delta_head)
+{
+ delta_head->references++;
+}
+
+void
+delta_head_refput(struct delta_head *delta_head)
+{
+ delta_head->references--;
+ if (delta_head->references == 0) {
+ doc_data_cleanup(&delta_head->doc_data);
+ free(delta_head);
+ }
+}
+
+static int
+deltas_head_create(struct deltas_head **deltas)
+{
+ struct deltas_head *tmp;
+
+ tmp = malloc(sizeof(struct deltas_head));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ SLIST_INIT(tmp);
+
+ *deltas = tmp;
+ return 0;
+}
+
+static void
+deltas_head_destroy(struct deltas_head *deltas)
+{
+ struct delta_head *head;
+
+ while (!SLIST_EMPTY(deltas)) {
+ head = deltas->slh_first;
+ SLIST_REMOVE_HEAD(deltas, next);
+ delta_head_refput(head);
+ }
+ free(deltas);
+}
+
int
update_notification_create(struct update_notification **file)
{
struct update_notification *tmp;
+ int error;
tmp = malloc(sizeof(struct update_notification));
if (tmp == NULL)
return pr_enomem();
+ error = deltas_head_create(&tmp->deltas_list);
+ if (error) {
+ free(tmp);
+ return error;
+ }
+
global_data_init(&tmp->global_data);
doc_data_init(&tmp->snapshot);
- SLIST_INIT(&tmp->deltas_list);
-
*file = tmp;
return 0;
}
void
update_notification_destroy(struct update_notification *file)
{
- struct deltas_head *list;
- struct delta_head *head;
-
- list = &file->deltas_list;
- while (!SLIST_EMPTY(list)) {
- head = list->slh_first;
- SLIST_REMOVE_HEAD(list, next);
- doc_data_cleanup(&head->doc_data);
- free(head);
- }
doc_data_cleanup(&file->snapshot);
global_data_cleanup(&file->global_data);
-
+ deltas_head_destroy(file->deltas_list);
free(file);
}
-/* URI and HASH must already be allocated */
+/* A new delta_head will be allocated, as well as its URI and HASH */
int
-update_notification_deltas_add(struct deltas_head *deltas, unsigned long serial,
- char **uri, unsigned char **hash, size_t hash_len)
+deltas_head_add(struct deltas_head *deltas, unsigned long serial,
+ char *uri, unsigned char *hash, size_t hash_len)
{
struct delta_head *elem;
+ int error;
- elem = malloc(sizeof(struct delta_head));
- if (elem == NULL)
- return pr_enomem();
+ elem = NULL;
+ error = delta_head_create(&elem);
+ if (error)
+ return error;
elem->serial = serial;
- elem->doc_data.uri = *uri;
- elem->doc_data.hash = *hash;
+
+ elem->doc_data.uri = strdup(uri);
+ if (elem->doc_data.uri == NULL) {
+ free(elem);
+ return pr_enomem();
+ }
+
elem->doc_data.hash_len = hash_len;
+ elem->doc_data.hash = malloc(hash_len);
+ if (elem->doc_data.hash == NULL) {
+ free(elem->doc_data.uri);
+ free(elem);
+ return pr_enomem();
+ }
SLIST_INSERT_HEAD(deltas, elem, next);
return 0;
}
+int
+deltas_head_for_each(struct deltas_head *deltas, delta_head_cb cb, void *arg)
+{
+ struct delta_head *cursor;
+ int error;
+
+ SLIST_FOREACH(cursor, deltas, next) {
+ error = cb(cursor, arg);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
int
snapshot_create(struct snapshot **file)
{
return pr_enomem();
global_data_init(&tmp->global_data);
- SLIST_INIT(&tmp->publish_list);
tmp->source = NULL;
*file = tmp;
void
snapshot_destroy(struct snapshot *file)
{
- struct publish_list *list;
- struct publish *head;
-
- list = &file->publish_list;
- while (!SLIST_EMPTY(list)) {
- head = list->slh_first;
- SLIST_REMOVE_HEAD(list, next);
- doc_data_cleanup(&head->doc_data);
- free(head->content);
- free(head);
- }
global_data_cleanup(&file->global_data);
xml_source_destroy(file->source);
+ free(file);
+}
+
+int
+delta_create(struct delta **file)
+{
+ struct delta *tmp;
+
+ tmp = malloc(sizeof(struct delta));
+ if (tmp == NULL)
+ return pr_enomem();
+ global_data_init(&tmp->global_data);
+ tmp->source = NULL;
+
+ *file = tmp;
+ return 0;
+}
+
+void
+delta_destroy(struct delta *file)
+{
+ global_data_cleanup(&file->global_data);
+ xml_source_destroy(file->source);
free(file);
}
}
int
-publish_list_add(struct publish_list *list, struct publish *elem)
+withdraw_create(struct withdraw **file)
{
- SLIST_INSERT_HEAD(list, elem, next);
+ struct withdraw *tmp;
+
+ tmp = malloc(sizeof(struct withdraw));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ doc_data_init(&tmp->doc_data);
+
+ *file = tmp;
return 0;
}
+
+void
+withdraw_destroy(struct withdraw *file)
+{
+ doc_data_cleanup(&file->doc_data);
+ free(file);
+}
#define SRC_RRDP_RRDP_OBJECTS_H_
#include <libxml/tree.h>
-#include <sys/queue.h>
#include <stddef.h>
/* Possible results for an RRDP URI comparison */
enum rrdp_uri_cmp_result {
- /* The URI doesn't exists */
- RRDP_URI_NOTFOUND,
-
/* The URI exists and has the same session ID and serial */
RRDP_URI_EQUAL,
/* The URI exists but has distinct session ID */
RRDP_URI_DIFF_SESSION,
+
+ /* The URI doesn't exists */
+ RRDP_URI_NOTFOUND,
};
/* Structure to remember the XML source file (useful for hash validations) */
size_t hash_len;
};
-/* Represents a <publish> element to be utilized inside a list */
+/* Represents a <publish> element */
struct publish {
struct doc_data doc_data;
unsigned char *content;
size_t content_len;
- SLIST_ENTRY(publish) next;
};
-/* Represents a <withdraw> element to be utilized inside a list */
+/* Represents a <withdraw> element */
struct withdraw {
struct doc_data doc_data;
- SLIST_ENTRY(withdraw) next;
};
-/* List of <publish> elements (either in a delta or a snapshot file) */
-SLIST_HEAD(publish_list, publish);
-/* List of <withdraw> elements */
-SLIST_HEAD(withdrawn_list, withdraw);
-
-/* Delta file content */
+/*
+ * Delta file content.
+ * Publish/withdraw list aren't remember, they are processed ASAP.
+ */
struct delta {
struct global_data global_data;
- struct publish_list publish_list;
- struct withdrawn_list withdraw_list;
struct xml_source *source;
};
-/* Snapshot file content */
+/* Snapshot file content
+ * Publish list isn't remember, is processed ASAP.
+ */
struct snapshot {
struct global_data global_data;
- struct publish_list publish_list;
struct xml_source *source;
};
/* Delta element located at an update notification file */
-struct delta_head {
- unsigned long serial;
- struct doc_data doc_data;
- SLIST_ENTRY(delta_head) next;
-};
+struct delta_head;
/* List of deltas inside an update notification file */
-SLIST_HEAD(deltas_head, delta_head);
+struct deltas_head;
struct update_notification {
struct global_data global_data;
struct doc_data snapshot;
- struct deltas_head deltas_list;
+ struct deltas_head *deltas_list;
};
int global_data_init(struct global_data *);
int update_notification_create(struct update_notification **);
void update_notification_destroy(struct update_notification *);
-int update_notification_deltas_add(struct deltas_head *, unsigned long, char **,
- unsigned char **, size_t);
+unsigned long delta_head_get_serial(struct delta_head *);
+struct doc_data *delta_head_get_doc_data(struct delta_head *);
+
+void delta_head_refget(struct delta_head *);
+void delta_head_refput(struct delta_head *);
+
+typedef int (*delta_head_cb)(struct delta_head *, void *);
+int deltas_head_for_each(struct deltas_head *, delta_head_cb, void *);
+int deltas_head_add(struct deltas_head *, unsigned long, char *,
+ unsigned char *, size_t);
int snapshot_create(struct snapshot **);
void snapshot_destroy(struct snapshot *);
+int delta_create(struct delta **);
+void delta_destroy(struct delta *);
+
int publish_create(struct publish **);
void publish_destroy(struct publish *);
-int publish_list_add(struct publish_list *, struct publish *);
-
+int withdraw_create(struct withdraw **);
+void withdraw_destroy(struct withdraw *);
#endif /* SRC_RRDP_RRDP_OBJECTS_H_ */
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include "crypto/base64.h"
#include "http/http.h"
#define RRDP_ATTR_URI "uri"
#define RRDP_ATTR_HASH "hash"
+struct deltas_proc_args {
+ struct delta_head **deltas;
+ unsigned long serial;
+ size_t deltas_set;
+};
static int
get_root_element(xmlDoc *doc, xmlNode **result)
xml_value = xmlGetProp(root, BAD_CAST attr);
if (xml_value == NULL)
- return pr_err("RRDP file: Couldn't find xml attribute %s",
+ return pr_err("RRDP file: Couldn't find xml attribute '%s'",
attr);
errno = 0;
}
static int
-parse_hex_string(xmlNode *root, char const *attr, unsigned char **result,
- size_t *result_len)
+parse_hex_string(xmlNode *root, bool required, char const *attr,
+ unsigned char **result, size_t *result_len)
{
xmlChar *xml_value;
unsigned char *tmp, *ptr;
xml_value = xmlGetProp(root, BAD_CAST attr);
if (xml_value == NULL)
- return pr_err("RRDP file: Couldn't find xml attribute %s",
- attr);
+ return required ?
+ pr_err("RRDP file: Couldn't find xml attribute '%s'", attr)
+ : 0;
/* The rest of the checks are done at the schema */
if (xmlStrlen(xml_value) % 2 != 0) {
xmlFree(xml_value);
- return pr_err("RRDP file: Attribute %s isn't a valid hash",
+ return pr_err("RRDP file: Attribute %s isn't a valid hex string",
attr);
}
/* @gdata elements are allocated */
static int
parse_global_data(xmlNode *root, struct global_data *gdata,
- struct global_data *parent_data)
+ struct global_data *parent_data, bool validate_serial)
{
+ char *session_id;
+ unsigned long serial;
int error;
- error = parse_string(root, RRDP_ATTR_SESSION_ID, &gdata->session_id);
+ error = parse_string(root, RRDP_ATTR_SESSION_ID, &session_id);
if (error)
return error;
- error = parse_long(root, RRDP_ATTR_SERIAL, &gdata->serial);
+ error = parse_long(root, RRDP_ATTR_SERIAL, &serial);
if (error) {
- free(gdata->session_id);
+ free(session_id);
return error;
}
if (parent_data == NULL)
- return 0;
+ goto return_val; /* Error O is OK */
/*
* FIXME (now) Prepare the callers to receive positive error values,
* which means the file was successfully parsed but is has a logic error
* (in this case, session ID or serial don't match parent's).
*/
- if (strcmp(parent_data->session_id, gdata->session_id) != 0) {
+ if (strcmp(parent_data->session_id, session_id) != 0) {
pr_info("Object session id doesn't match parent's session id");
- return EINVAL;
+ error = EINVAL;
+ goto return_val;
}
- if (parent_data->serial != gdata->serial) {
+ if (!validate_serial)
+ goto return_val;
+
+ if (parent_data->serial != serial) {
pr_info("Object serial doesn't match parent's serial");
- return EINVAL;
+ error = EINVAL;
}
- return 0;
+return_val:
+ gdata->session_id = session_id;
+ gdata->serial = serial;
+ return error;
}
/* @data elements are allocated */
static int
-parse_doc_data(xmlNode *root, bool parse_hash, struct doc_data *data)
+parse_doc_data(xmlNode *root, bool parse_hash, bool hash_req,
+ struct doc_data *data)
{
+ char *uri;
+ unsigned char *hash;
+ size_t hash_len;
int error;
- error = parse_string(root, RRDP_ATTR_URI, &data->uri);
+ uri = NULL;
+ hash = NULL;
+ hash_len = 0;
+
+ error = parse_string(root, RRDP_ATTR_URI, &uri);
if (error)
return error;
- if (!parse_hash)
+ if (!parse_hash) {
+ data->uri = uri;
return 0;
+ }
- error = parse_hex_string(root, RRDP_ATTR_HASH, &data->hash,
- &data->hash_len);
+ error = parse_hex_string(root, hash_req, RRDP_ATTR_HASH, &hash,
+ &hash_len);
if (error) {
- free(data->uri);
+ free(uri);
return error;
}
+ data->uri = uri;
+ data->hash = hash;
+ data->hash_len = hash_len;
return 0;
}
static int
parse_notification_deltas(xmlNode *root, struct deltas_head *deltas)
{
- struct delta_head delta;
+ struct doc_data doc_data;
+ unsigned long serial;
int error;
- error = parse_long(root, RRDP_ATTR_SERIAL, &delta.serial);
- if (error)
- return error;
-
- error = parse_doc_data(root, true, &delta.doc_data);
+ error = parse_long(root, RRDP_ATTR_SERIAL, &serial);
if (error)
return error;
- error = update_notification_deltas_add(deltas, delta.serial,
- &delta.doc_data.uri, &delta.doc_data.hash, delta.doc_data.hash_len);
+ doc_data_init(&doc_data);
+ error = parse_doc_data(root, true, true, &doc_data);
if (error) {
- doc_data_cleanup(&delta.doc_data);
+ doc_data_cleanup(&doc_data);
return error;
}
+ error = deltas_head_add(deltas, serial, doc_data.uri, doc_data.hash,
+ doc_data.hash_len);
+
+ /* Always release data */
+ doc_data_cleanup(&doc_data);
+ if (error)
+ return error;
+
return 0;
}
+/* Get the notification data. In case of error, the caller must cleanup @file */
static int
parse_notification_data(xmlNode *root, struct update_notification *file)
{
for (cur_node = root->children; cur_node; cur_node = cur_node->next) {
if (xmlStrEqual(cur_node->name, BAD_CAST RRDP_ELEM_DELTA))
error = parse_notification_deltas(cur_node,
- &file->deltas_list);
+ file->deltas_list);
else if (xmlStrEqual(cur_node->name,
BAD_CAST RRDP_ELEM_SNAPSHOT))
- error = parse_doc_data(cur_node, true, &file->snapshot);
+ error = parse_doc_data(cur_node, true, true,
+ &file->snapshot);
if (error)
return error;
}
static int
-parse_publish(xmlNode *root, bool parse_hash, struct publish **publish)
+parse_publish(xmlNode *root, bool parse_hash, bool hash_required,
+ struct publish **publish)
{
struct publish *tmp;
char *base64_str;
if (error)
return error;
- error = parse_doc_data(root, parse_hash, &tmp->doc_data);
+ error = parse_doc_data(root, parse_hash, hash_required, &tmp->doc_data);
if (error)
goto release_tmp;
if (error)
goto release_base64;
+ /* FIXME (now) validate hash of base64str vs doc_data->hash */
+
free(base64_str);
*publish = tmp;
return 0;
return error;
}
+static int
+parse_withdraw(xmlNode *root, struct withdraw **withdraw)
+{
+ struct withdraw *tmp;
+ int error;
+
+ error = withdraw_create(&tmp);
+ if (error)
+ return error;
+
+ error = parse_doc_data(root, true, true, &tmp->doc_data);
+ if (error)
+ goto release_tmp;
+
+ *withdraw = tmp;
+ return 0;
+release_tmp:
+ withdraw_destroy(tmp);
+ return error;
+}
+
static int
write_from_uri(char const *location, unsigned char *content, size_t content_len)
{
}
static int
-parse_snapshot_publish_list(xmlNode *root, struct snapshot *file)
+delete_from_uri(char const *location)
+{
+ struct rpki_uri *uri;
+ char *local_uri, *work_loc, *tmp;
+ int error;
+
+ error = uri_create_mixed_str(&uri, location, strlen(location));
+ if (error)
+ return error;
+
+ local_uri = strdup(uri_get_local(uri));
+ if (local_uri == NULL) {
+ error = pr_enomem();
+ goto release_uri;
+ }
+
+ /* FIXME (now) validate hash, must come from withdraw element */
+ errno = 0;
+ error = remove(local_uri);
+ if (error) {
+ error = pr_errno(errno, "Couldn't delete %s", local_uri);
+ goto release_str;
+ }
+
+ /*
+ * Delete parent dirs only if empty.
+ *
+ * The algorithm is a bit aggressive, but rmdir() won't delete
+ * something unless is empty, so in case the dir still has something in
+ * it the cycle is finished.
+ */
+ work_loc = local_uri;
+ do {
+ tmp = strrchr(work_loc, '/');
+ if (tmp == NULL)
+ break;
+ *tmp = '\0';
+
+ /* FIXME (now) use a lock, what if the root dir is reached? */
+
+ errno = 0;
+ error = rmdir(work_loc);
+ if (!error)
+ continue; /* Keep deleting up */
+
+ /* Stop if there's content in the dir */
+ if (errno == ENOTEMPTY || errno == EEXIST)
+ break;
+
+ error = pr_errno(errno, "Couldn't delete dir %s", work_loc);
+ goto release_str;
+ } while (true);
+
+ uri_refput(uri);
+ free(local_uri);
+ return 0;
+release_str:
+ free(local_uri);
+release_uri:
+ uri_refput(uri);
+ return error;
+}
+
+static int
+parse_publish_list(xmlNode *root)
{
struct publish *tmp;
xmlNode *cur_node;
for (cur_node = root->children; cur_node; cur_node = cur_node->next) {
if (xmlStrEqual(cur_node->name, BAD_CAST RRDP_ELEM_PUBLISH)) {
tmp = NULL;
- error = parse_publish(cur_node, false, &tmp);
+ error = parse_publish(cur_node, false, false, &tmp);
if (error)
return error;
publish_destroy(tmp);
if (error)
return error;
+ }
+ }
+
+ return 0;
+}
+
+static int
+parse_delta_element_list(xmlNode *root)
+{
+ struct publish *pub;
+ struct withdraw *wit;
+ xmlNode *cur_node;
+ int error;
+
+ /* Elements already validated by syntax */
+ for (cur_node = root->children; cur_node; cur_node = cur_node->next) {
+ if (xmlStrEqual(cur_node->name, BAD_CAST RRDP_ELEM_PUBLISH)) {
+ pub = NULL;
+ error = parse_publish(cur_node, true, false, &pub);
+ if (error)
+ return error;
+
+ error = write_from_uri(pub->doc_data.uri, pub->content,
+ pub->content_len);
+ publish_destroy(pub);
+ if (error)
+ return error;
+ } else if (xmlStrEqual(cur_node->name,
+ BAD_CAST RRDP_ELEM_WITHDRAW)) {
+ wit = NULL;
+ error = parse_withdraw(cur_node, &wit);
+ if (error)
+ return error;
+
+ error = delete_from_uri(wit->doc_data.uri);
+ withdraw_destroy(wit);
+ if (error)
+ return error;
}
}
goto release_update;
/* FIXME (now) validate version, namespace, etc. */
- error = parse_global_data(root, &tmp->global_data, NULL);
+ error = parse_global_data(root, &tmp->global_data, NULL, false);
if (error)
goto release_update;
}
static int
-parse_snapshot(const char *path, struct update_notification *parent,
- struct snapshot **file)
+parse_snapshot(const char *path, struct update_notification *parent)
{
xmlDoc *doc;
xmlNode *root;
- struct snapshot *tmp;
+ struct snapshot *snapshot;
struct xml_source *source;
int error;
if (error)
return error;
- error = snapshot_create(&tmp);
+ error = snapshot_create(&snapshot);
if (error)
goto release_doc;
if (error)
goto release_snapshot;
- tmp->source = source;
+ snapshot->source = source;
error = xml_source_set(source, doc);
if (error)
goto release_snapshot;
- error = parse_global_data(root, &tmp->global_data,
- &parent->global_data);
+ error = parse_global_data(root, &snapshot->global_data,
+ &parent->global_data, true);
if (error)
goto release_snapshot;
- error = parse_snapshot_publish_list(root, tmp);
- if (error)
- goto release_snapshot;
+ error = parse_publish_list(root);
- *file = tmp;
/* Error 0 is ok */
- goto release_doc;
release_snapshot:
- snapshot_destroy(tmp);
+ snapshot_destroy(snapshot);
+release_doc:
+ xmlFreeDoc(doc);
+ return error;
+}
+
+static int
+parse_delta(const char *path, struct update_notification *parent)
+{
+ xmlDoc *doc;
+ xmlNode *root;
+ struct delta *delta;
+ struct xml_source *source;
+ int error;
+
+ root = NULL;
+
+ error = relax_ng_validate(path, &doc);
+ if (error)
+ return error;
+
+ error = delta_create(&delta);
+ if (error)
+ goto release_doc;
+
+ error = get_root_element(doc, &root);
+ if (error)
+ goto release_delta;
+
+ /* FIXME (now) validate hash, version, namespace, etc. */
+ error = xml_source_create(&source);
+ if (error)
+ goto release_delta;
+
+ delta->source = source;
+ error = xml_source_set(source, doc);
+ if (error)
+ goto release_delta;
+
+ error = parse_global_data(root, &delta->global_data,
+ &parent->global_data, false);
+ if (error)
+ goto release_delta;
+
+ error = parse_delta_element_list(root);
+ /* Error 0 is ok */
+release_delta:
+ delta_destroy(delta);
release_doc:
xmlFreeDoc(doc);
return error;
}
+static int
+get_pending_delta(struct delta_head **delta_head, unsigned long pos,
+ struct deltas_proc_args *args)
+{
+ /* Ref to the delta element */
+ args->deltas[pos] = *delta_head;
+ args->deltas_set++;
+ delta_head_refget(*delta_head);
+
+ return 0;
+}
+
+static int
+__get_pending_delta(struct delta_head *delta_head, void *arg)
+{
+ struct deltas_proc_args *args = arg;
+ unsigned long serial;
+
+ serial = delta_head_get_serial(delta_head);
+ if (serial <= args->serial)
+ return 0;
+
+ return get_pending_delta(&delta_head, serial - args->serial - 1, args);
+}
+
+static int process_delta(struct delta_head *delta_head,
+ struct update_notification *parent)
+{
+ struct rpki_uri *uri;
+ struct doc_data *head_data;
+ int error;
+
+ head_data = delta_head_get_doc_data(delta_head);
+
+ error = uri_create_https_str(&uri, head_data->uri,
+ strlen(head_data->uri));
+ if (error)
+ return error;
+
+ error = http_download_file(uri, write_local);
+ if (error)
+ goto release_uri;
+
+ fnstack_push_uri(uri);
+ error = parse_delta(uri_get_local(uri), parent);
+ if (error) {
+ fnstack_pop();
+ goto release_uri;
+ }
+
+ fnstack_pop();
+ uri_refput(uri);
+
+ return 0;
+release_uri:
+ uri_refput(uri);
+ return error;
+}
+
+/*
+ * Download from @uri and set result file contents to @result, the file name
+ * is pushed into fnstack, so don't forget to do the pop when done working
+ * with the file.
+ */
int
rrdp_parse_notification(struct rpki_uri *uri,
struct update_notification **result)
}
int
-rrdp_parse_snapshot(struct update_notification *parent,
- struct snapshot **result)
+rrdp_parse_snapshot(struct update_notification *parent)
{
struct rpki_uri *uri;
- struct snapshot *tmp;
int error;
error = uri_create_https_str(&uri, parent->snapshot.uri,
goto release_uri;
fnstack_push_uri(uri);
- error = parse_snapshot(uri_get_local(uri), parent, &tmp);
- if (error)
+ error = parse_snapshot(uri_get_local(uri), parent);
+ if (error) {
+ fnstack_pop();
goto release_uri;
+ }
- uri_refput(uri);
- *result = tmp;
fnstack_pop();
+ uri_refput(uri);
+
return 0;
release_uri:
- fnstack_pop();
uri_refput(uri);
return error;
}
+
+int
+rrdp_process_deltas(struct update_notification *parent, unsigned long serial)
+{
+ struct delta_head *deltas[parent->global_data.serial - serial];
+ struct deltas_proc_args args;
+ size_t deltas_len;
+ size_t index;
+ int error;
+
+ deltas_len = parent->global_data.serial - serial;
+ for (index = 0; index < deltas_len; index++)
+ deltas[index] = NULL;
+
+ args.deltas = deltas;
+ args.serial = serial;
+ args.deltas_set = 0;
+
+ error = deltas_head_for_each(parent->deltas_list, __get_pending_delta,
+ &args);
+ if (error)
+ goto release_deltas;
+
+ /* Check that all expected deltas are set */
+ if (args.deltas_set != deltas_len) {
+ error = pr_err("Less deltas than expected: should be from serial %lu to %lu (%lu), but got only %lu",
+ serial, parent->global_data.serial, deltas_len,
+ args.deltas_set);
+ goto release_deltas;
+ }
+
+ /* Now process each delta in order */
+ for (index = 0; index < deltas_len; index++) {
+ error = process_delta(deltas[index], parent);
+ if (error)
+ break;
+ }
+ /* Error 0 it's ok */
+release_deltas:
+ for (index = 0; index < deltas_len; index++)
+ if (deltas[index] != NULL)
+ delta_head_refput(deltas[index]);
+
+ return error;
+}
#include "uri.h"
int rrdp_parse_notification(struct rpki_uri *, struct update_notification **);
-int rrdp_parse_snapshot(struct update_notification *, struct snapshot **);
+int rrdp_parse_snapshot(struct update_notification *);
+
+int rrdp_process_deltas(struct update_notification *, unsigned long serial);
#endif /* SRC_RRDP_RRDP_PARSER_H_ */
db_rrdp_add_uri(state.rrdp_uris, uri, session_id, serial))
}
+int
+rrdp_uri_get_serial(char const *uri, unsigned long *serial)
+{
+ RLOCK_HANDLER(&state_lock,
+ db_rrdp_get_serial(state.rrdp_uris, uri, serial))
+}
+
static int
__perform_standalone_validation(struct db_table **result)
{
enum rrdp_uri_cmp_result rrdp_uri_cmp(char const *, char const *,
unsigned long);
int rrdp_uri_update(char const *, char const *, unsigned long);
+int rrdp_uri_get_serial(char const *, unsigned long *);
uint16_t get_current_session_id(uint8_t);