]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Process delta files, create rrdp_loader to centralize rrdp processing.
authorpcarana <pc.moreno2099@gmail.com>
Fri, 29 Nov 2019 21:54:33 +0000 (15:54 -0600)
committerpcarana <pc.moreno2099@gmail.com>
Fri, 29 Nov 2019 21:54:33 +0000 (15:54 -0600)
+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.

15 files changed:
src/Makefile.am
src/object/certificate.c
src/object/tal.c
src/rrdp/db_rrdp.c
src/rrdp/db_rrdp.h
src/rrdp/rrdp_handler.c
src/rrdp/rrdp_handler.h
src/rrdp/rrdp_loader.c [new file with mode: 0644]
src/rrdp/rrdp_loader.h [new file with mode: 0644]
src/rrdp/rrdp_objects.c
src/rrdp/rrdp_objects.h
src/rrdp/rrdp_parser.c
src/rrdp/rrdp_parser.h
src/rtr/db/vrps.c
src/rtr/db/vrps.h

index bca8af9f69d60527ebba779d572319739250a836..c1e09b47acce1e2f8b8fffea348bb4a561a3f131 100644 (file)
@@ -81,6 +81,7 @@ fort_SOURCES += resource/asn.h resource/asn.c
 
 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
 
index ca5d98dcf05a631a45df2e327c15ff74107c531b..8140c0b7a546b68677895842b1f164e9b8344857 100644 (file)
@@ -19,8 +19,7 @@
 #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. */
@@ -1880,62 +1879,7 @@ certificate_validate_aia(struct rpki_uri *caIssuers, X509 *cert)
 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
index 3512a4114091a850d754c96b99cf1a2fadb72adc..d5e9b5eda36a7d97d6b6eb47be023d68d2dbc3e2 100644 (file)
@@ -482,6 +482,7 @@ handle_tal_uri(struct tal *tal, struct rpki_uri *uri, void *arg)
 
        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);
index 862d10654b510049628c373f5d3fb3c4d43be9b3..fece933b39db83086d6b450f88d92602eb0326c6 100644 (file)
@@ -122,6 +122,20 @@ db_rrdp_add_uri(struct db_rrdp *db, char const *uri, char const *session_id,
        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)
 {
index 48ca94d7de26d89cdcdfbcfcf886101a837f689b..3f32ee65cbb38ab738647f1dd3bf064aa9ca1252 100644 (file)
@@ -16,5 +16,6 @@ enum rrdp_uri_cmp_result db_rrdp_cmp_uri(struct db_rrdp *, char const *,
     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_ */
index a09765ba36fb89c4f37c5cbf8828f5391a3b5fe0..9b0b985516816dcc1379c9b6a7d755a368420cb8 100644 (file)
@@ -50,3 +50,18 @@ rhandler_uri_update(char const *uri, char const *session_id,
            ? 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;
+}
index bcf5da7425d920b400b934413f75dbf797b6c766..3f5db639f732146257d575f0eefb6dbeaaac5bae 100644 (file)
@@ -22,9 +22,12 @@ struct rrdp_handler {
            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_ */
diff --git a/src/rrdp/rrdp_loader.c b/src/rrdp/rrdp_loader.c
new file mode 100644 (file)
index 0000000..5c82958
--- /dev/null
@@ -0,0 +1,80 @@
+#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;
+}
diff --git a/src/rrdp/rrdp_loader.h b/src/rrdp/rrdp_loader.h
new file mode 100644 (file)
index 0000000..3b8025a
--- /dev/null
@@ -0,0 +1,8 @@
+#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_ */
index e5146dc50de102b1c8ed7792ea411a4ee60af992..7a90c5e0600fc64adc9f5e33139e34defebdfea6 100644 (file)
@@ -1,11 +1,23 @@
 #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)
 {
@@ -69,20 +81,97 @@ xml_source_set(struct xml_source *src, xmlDoc *orig)
        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;
 }
@@ -90,42 +179,60 @@ update_notification_create(struct update_notification **file)
 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)
 {
@@ -136,7 +243,6 @@ snapshot_create(struct snapshot **file)
                return pr_enomem();
 
        global_data_init(&tmp->global_data);
-       SLIST_INIT(&tmp->publish_list);
        tmp->source = NULL;
 
        *file = tmp;
@@ -146,20 +252,32 @@ snapshot_create(struct snapshot **file)
 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);
 }
 
@@ -189,8 +307,23 @@ publish_destroy(struct publish *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);
+}
index a9fb90a73da8a9640e2db0aa2e14cc7d03512922..bf2430b81fc2d2c85536cd1e05a4e6bb6979fd5a 100644 (file)
@@ -2,14 +2,10 @@
 #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,
 
@@ -18,6 +14,9 @@ enum rrdp_uri_cmp_result {
 
        /* 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) */
@@ -36,54 +35,45 @@ struct doc_data {
        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 *);
@@ -99,16 +89,27 @@ int xml_source_set(struct xml_source *, xmlDoc *);
 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_ */
index 9610cdc94b1607eb9cfffac85d4c40922f1f25c6..4e3d7c301222a0a7127bb24174d66d2510077884 100644 (file)
@@ -6,8 +6,10 @@
 #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)
@@ -243,7 +250,7 @@ parse_long(xmlNode *root, char const *attr, unsigned long *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;
@@ -261,8 +268,8 @@ parse_long(xmlNode *root, char const *attr, unsigned long *result)
 }
 
 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;
@@ -272,13 +279,14 @@ parse_hex_string(xmlNode *root, char const *attr, unsigned char **result,
 
        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);
        }
 
@@ -308,88 +316,116 @@ parse_hex_string(xmlNode *root, char const *attr, unsigned char **result,
 /* @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)
 {
@@ -399,10 +435,11 @@ 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;
@@ -412,7 +449,8 @@ parse_notification_data(xmlNode *root, struct update_notification *file)
 }
 
 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;
@@ -422,7 +460,7 @@ parse_publish(xmlNode *root, bool parse_hash, struct publish **publish)
        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;
 
@@ -434,6 +472,8 @@ parse_publish(xmlNode *root, bool parse_hash, struct publish **publish)
        if (error)
                goto release_base64;
 
+       /* FIXME (now) validate hash of base64str vs doc_data->hash */
+
        free(base64_str);
        *publish = tmp;
        return 0;
@@ -444,6 +484,27 @@ release_tmp:
        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)
 {
@@ -483,7 +544,71 @@ 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;
@@ -493,7 +618,7 @@ parse_snapshot_publish_list(xmlNode *root, struct snapshot *file)
        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;
 
@@ -502,6 +627,44 @@ parse_snapshot_publish_list(xmlNode *root, struct snapshot *file)
                        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;
                }
        }
 
@@ -531,7 +694,7 @@ parse_notification(const char *path, struct update_notification **file)
                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;
 
@@ -551,12 +714,11 @@ release_doc:
 }
 
 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;
 
@@ -566,7 +728,7 @@ parse_snapshot(const char *path, struct update_notification *parent,
        if (error)
                return error;
 
-       error = snapshot_create(&tmp);
+       error = snapshot_create(&snapshot);
        if (error)
                goto release_doc;
 
@@ -579,30 +741,137 @@ parse_snapshot(const char *path, struct update_notification *parent,
        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)
@@ -630,11 +899,9 @@ rrdp_parse_notification(struct rpki_uri *uri,
 }
 
 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,
@@ -647,16 +914,62 @@ rrdp_parse_snapshot(struct update_notification *parent,
                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;
+}
index 3cd2f3a2d38d8a3eaf2dc85b3b5ac978edd0f548..e4c267353d4a43b9bda321b1aefbffbeafe7255f 100644 (file)
@@ -5,6 +5,8 @@
 #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_ */
index 3a9fa8bea0760dd7f71f6f3d6dd0bbafcff0ee25..162b9712cd50eb6640fb679df4bf245a44141ec0 100644 (file)
@@ -195,6 +195,13 @@ rrdp_uri_update(char const *uri, char const *session_id, unsigned long serial)
            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)
 {
index 0ed662d65c454c8ce724bec0864af887942f2c18..60dafe3709d1b5c0a2d520e73ebd5d7d52cafdd2 100644 (file)
@@ -44,6 +44,7 @@ int handle_router_key(unsigned char const *, uint32_t, unsigned char const *,
 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);