]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Send 'If-Modified-Since' header on update notification requests.
authorpcarana <pc.moreno2099@gmail.com>
Wed, 4 Dec 2019 16:05:55 +0000 (10:05 -0600)
committerpcarana <pc.moreno2099@gmail.com>
Wed, 4 Dec 2019 16:05:55 +0000 (10:05 -0600)
+The last update is stored along with the RRDP URIs DB, this date is updated once the file processing (snapshot or deltas) is successfully terminated.
+Be ready in case the server responds an HTTP 304 status code.
+Use CURL option 'CURLOPT_FAILONERROR' to treat HTTP status code > 400 as errors.

12 files changed:
src/http/http.c
src/http/http.h
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
src/rrdp/rrdp_parser.c
src/rrdp/rrdp_parser.h
src/rtr/db/vrps.c
src/rtr/db/vrps.h

index 70865843ff182ecb5604556dab02cc1b99699518..3d8480259cb1a0a9150c44c01ced488d363e601a 100644 (file)
@@ -7,6 +7,11 @@
 #include "file.h"
 #include "log.h"
 
+/* HTTP Response Code 304 (Not Modified) */
+#define HTTP_NOT_MODIFIED      304
+/* HTTP Response Code 400 (Bad Request) */
+#define HTTP_BAD_REQUEST       400
+
 struct http_handler {
        CURL *curl;
        char errbuf[CURL_ERROR_SIZE];
@@ -61,6 +66,17 @@ http_easy_init(struct http_handler *handler)
        /* Currently all requests use GET */
        curl_easy_setopt(tmp, CURLOPT_HTTPGET, 1);
 
+       /*
+        * Response codes >= 400 will be treated as errors
+        *
+        * "This method is not fail-safe and there are occasions where
+        * non-successful response codes will slip through, especially when
+        * authentication is involved (response codes 401 and 407)."
+        *
+        * Well, be ready for those scenarios when performing the requests.
+        */
+       curl_easy_setopt(tmp, CURLOPT_FAILONERROR, 1L);
+
        /* Refer to its error buffer */
        curl_easy_setopt(tmp, CURLOPT_ERRORBUFFER, handler->errbuf);
 
@@ -69,12 +85,19 @@ http_easy_init(struct http_handler *handler)
        return 0;
 }
 
+static char const *
+curl_err_string(struct http_handler *handler, CURLcode res)
+{
+       return strlen(handler->errbuf) > 0 ?
+           handler->errbuf : curl_easy_strerror(res);
+}
+
 /*
  * Fetch data from @uri and write result using @cb (which will receive @arg).
  */
 static int
-http_fetch(struct http_handler *handler, char const *uri, http_write_cb cb,
-    void *arg)
+http_fetch(struct http_handler *handler, char const *uri, long *response_code,
+    http_write_cb cb, void *arg)
 {
        CURLcode res;
 
@@ -85,12 +108,16 @@ http_fetch(struct http_handler *handler, char const *uri, http_write_cb cb,
 
        pr_debug("HTTP GET from '%s'.", uri);
        res = curl_easy_perform(handler->curl);
-       if (res != CURLE_OK)
-               return pr_err("Error requesting URL %s: %s", uri,
-                   strlen(handler->errbuf) > 0 ?
-                   handler->errbuf : curl_easy_strerror(res));
+       curl_easy_getinfo(handler->curl, CURLINFO_RESPONSE_CODE, response_code);
+       if (res == CURLE_OK)
+               return 0;
 
-       return 0;
+       if (*response_code >= HTTP_BAD_REQUEST)
+               return pr_err("Error requesting URL %s (received HTTP code %ld): %s",
+                   uri, *response_code, curl_err_string(handler, res));
+
+       return pr_err("Error requesting URL %s: %s", uri,
+           curl_err_string(handler, res));
 }
 
 static void
@@ -99,13 +126,9 @@ http_easy_cleanup(struct http_handler *handler)
        curl_easy_cleanup(handler->curl);
 }
 
-/*
- * Try to download from global @uri into a local directory structure created
- * from local @uri. The @cb should be utilized to write into a file; the file
- * will be sent to @cb as the last argument (its a FILE reference).
- */
-int
-http_download_file(struct rpki_uri *uri, http_write_cb cb)
+static int
+__http_download_file(struct rpki_uri *uri, http_write_cb cb,
+    long *response_code, long ims_value)
 {
        struct http_handler handler;
        struct stat stat;
@@ -124,7 +147,15 @@ http_download_file(struct rpki_uri *uri, http_write_cb cb)
        if (error)
                goto close_file;
 
-       error = http_fetch(&handler, uri_get_global(uri), cb, out);
+       /* Set "If-Modified-Since" header only if a value is specified */
+       if (ims_value > 0) {
+               curl_easy_setopt(handler.curl, CURLOPT_TIMEVALUE, ims_value);
+               curl_easy_setopt(handler.curl, CURLOPT_TIMECONDITION,
+                   CURL_TIMECOND_IFMODSINCE);
+       }
+
+       error = http_fetch(&handler, uri_get_global(uri), response_code, cb,
+           out);
        http_easy_cleanup(&handler);
        file_close(out);
 
@@ -134,3 +165,45 @@ close_file:
        file_close(out);
        return error;
 }
+
+/*
+ * Try to download from global @uri into a local directory structure created
+ * from local @uri. The @cb should be utilized to write into a file; the file
+ * will be sent to @cb as the last argument (its a FILE reference).
+ *
+ * Regular return value: 0 on success, any other value is an error.
+ */
+int
+http_download_file(struct rpki_uri *uri, http_write_cb cb)
+{
+       long response = 0;
+       return __http_download_file(uri, cb, &response, 0);
+}
+
+/*
+ * Fetch the file from @uri, write it using the @cb.
+ *
+ * The HTTP request is made using the header 'If-Modified-Since' with a value
+ * of @value.
+ *
+ * Returns:
+ *   > 0 file was requested but wasn't downloaded since the server didn't sent
+ *       a response due to its policy using the header 'If-Modified-Since'.
+ *   = 0 file successfully downloaded.
+ *   < 0 an actual error happened.
+ */
+int
+http_download_file_with_ims(struct rpki_uri *uri, http_write_cb cb, long value)
+{
+       long response = 0;
+       int error;
+
+       error = __http_download_file(uri, cb, &response, value);
+       if (error)
+               return error;
+
+       /* rfc7232#section-3.3:
+        * "the origin server SHOULD generate a 304 (Not Modified) response"
+        */
+       return response == HTTP_NOT_MODIFIED;
+}
index 25cce94f6dcd219ba1d0cafd534903765f6da630..073de4eac0d5f44e79ac0a8b9eef4c3fe96dc8e2 100644 (file)
@@ -10,5 +10,6 @@ void http_cleanup(void);
 
 typedef size_t (http_write_cb)(unsigned char *, size_t, size_t, void *);
 int http_download_file(struct rpki_uri *, http_write_cb);
+int http_download_file_with_ims(struct rpki_uri *, http_write_cb, long);
 
 #endif /* SRC_HTTP_HTTP_H_ */
index d5e9b5eda36a7d97d6b6eb47be023d68d2dbc3e2..9674c40fe0d14bcffb575a7b16af11479b0d054e 100644 (file)
@@ -483,6 +483,8 @@ 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;
+       rrdp_handler.uri_get_last_update = rrdp_uri_get_last_update;
+       rrdp_handler.uri_set_last_update = rrdp_uri_set_last_update;
 
        error = validation_prepare(&state, tal, &validation_handler,
            &rrdp_handler);
index fece933b39db83086d6b450f88d92602eb0326c6..379dab57fbc3cd1551d64f5fc01173d8b5f13b8a 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include "data_structure/uthash_nonfatal.h"
 #include "rrdp/rrdp_objects.h"
 #include "log.h"
@@ -10,6 +11,7 @@ struct db_rrdp_uri {
        /* Key */
        char *uri;
        struct global_data data;
+       long last_update;
        UT_hash_handle hh;
 };
 
@@ -17,6 +19,19 @@ struct db_rrdp {
        struct db_rrdp_uri *uris;
 };
 
+static int
+get_current_time(long *result)
+{
+       time_t now;
+
+       now = time(NULL);
+       if (now == ((time_t) -1))
+               return pr_errno(errno, "Error getting the current time");
+
+       *result = now;
+       return 0;
+}
+
 static int
 db_rrdp_uri_create(char const *uri, char const *session_id,
     unsigned long serial, struct db_rrdp_uri **result)
@@ -43,6 +58,7 @@ db_rrdp_uri_create(char const *uri, char const *session_id,
        }
 
        tmp->data.serial = serial;
+       tmp->last_update = 0;
 
        *result = tmp;
        return 0;
@@ -136,6 +152,33 @@ db_rrdp_get_serial(struct db_rrdp *db, char const *uri, unsigned long *serial)
        return 0;
 }
 
+int
+db_rrdp_get_last_update(struct db_rrdp *db, char const *uri, long *date)
+{
+       struct db_rrdp_uri *found;
+
+       HASH_FIND_STR(db->uris, uri, found);
+       if (found == NULL)
+               return -ENOENT;
+
+       *date = found->last_update;
+
+       return 0;
+}
+
+/* Set the last update to now */
+int
+db_rrdp_set_last_update(struct db_rrdp *db, char const *uri)
+{
+       struct db_rrdp_uri *found;
+
+       HASH_FIND_STR(db->uris, uri, found);
+       if (found == NULL)
+               return -ENOENT;
+
+       return get_current_time(&found->last_update);
+}
+
 int
 db_rrdp_create(struct db_rrdp **result)
 {
index 3f32ee65cbb38ab738647f1dd3bf064aa9ca1252..2dff90b6837d809182d69eebe810bf9a57b850a5 100644 (file)
@@ -17,5 +17,8 @@ enum rrdp_uri_cmp_result db_rrdp_cmp_uri(struct db_rrdp *, char const *,
 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 *);
+int db_rrdp_get_last_update(struct db_rrdp *, char const *, long *);
+
+int db_rrdp_set_last_update(struct db_rrdp *, char const *);
 
 #endif /* SRC_RRDP_DB_RRDP_H_ */
index 9b0b985516816dcc1379c9b6a7d755a368420cb8..b6ce91ecf99e60b93c65be65c69733a5176abc25 100644 (file)
@@ -2,6 +2,18 @@
 
 #include "thread_var.h"
 
+#define CALL_HANDLER_FUNC(func_name, func_call)                                \
+       struct rrdp_handler const *handler;                             \
+       int error;                                                      \
+                                                                       \
+       error = get_current_threads_handler(&handler);                  \
+       if (error)                                                      \
+               return error;                                           \
+                                                                       \
+       return (handler->func_name != NULL)                             \
+           ? handler->func_call                                        \
+           : 0;
+
 static int
 get_current_threads_handler(struct rrdp_handler const **result)
 {
@@ -23,45 +35,30 @@ get_current_threads_handler(struct rrdp_handler const **result)
 enum rrdp_uri_cmp_result
 rhandler_uri_cmp(char const *uri, char const *session_id, unsigned long serial)
 {
-       struct rrdp_handler const *handler;
-       int error;
-
-       error = get_current_threads_handler(&handler);
-       if (error)
-               return error;
-
-       return (handler->uri_cmp != NULL)
-           ? handler->uri_cmp(uri, session_id, serial)
-           : RRDP_URI_NOTFOUND;
+       CALL_HANDLER_FUNC(uri_cmp, uri_cmp(uri, session_id, serial))
 }
 
 int
 rhandler_uri_update(char const *uri, char const *session_id,
     unsigned long serial)
 {
-       struct rrdp_handler const *handler;
-       int error;
-
-       error = get_current_threads_handler(&handler);
-       if (error)
-               return error;
-
-       return (handler->uri_update != NULL)
-           ? handler->uri_update(uri, session_id, serial)
-           : 0;
+       CALL_HANDLER_FUNC(uri_update, uri_update(uri, session_id, serial))
 }
 
 int
 rhandler_uri_get_serial(char const *uri, unsigned long *serial)
 {
-       struct rrdp_handler const *handler;
-       int error;
+       CALL_HANDLER_FUNC(uri_get_serial, uri_get_serial(uri, serial))
+}
 
-       error = get_current_threads_handler(&handler);
-       if (error)
-               return error;
+int
+rhandler_uri_get_last_update(char const *uri, long *serial)
+{
+       CALL_HANDLER_FUNC(uri_get_last_update, uri_get_last_update(uri, serial))
+}
 
-       return (handler->uri_get_serial != NULL)
-           ? handler->uri_get_serial(uri, serial)
-           : 0;
+int
+rhandler_uri_set_last_update(char const *uri)
+{
+       CALL_HANDLER_FUNC(uri_set_last_update, uri_set_last_update(uri))
 }
index 3f5db639f732146257d575f0eefb6dbeaaac5bae..aebe582bcf74fcd3c296c8963676effd7b55d911 100644 (file)
@@ -22,12 +22,25 @@ 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 */
+       /*
+        * Get the serial related to an URI, returns -ENOENT if the URI doesn't
+        * exists, any other error means that something went wrong.
+        */
        int (*uri_get_serial)(char const *, unsigned long *);
+       /*
+        * Get the last update that an URI was requested, returns -ENOENT if
+        * the URI doesn't exists, any other error means that something went
+        * wrong.
+        */
+       int (*uri_get_last_update)(char const *, long *);
+       /* Set the last update to now */
+       int (*uri_set_last_update)(char const *);
 };
 
 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 *);
+int rhandler_uri_get_last_update(char const *, long *);
+int rhandler_uri_set_last_update(char const *);
 
 #endif /* SRC_RRDP_RRDP_HANDLER_H_ */
index 5c82958ee71e21b19e8c37c1b6c2fa978fb04233..256f1f3488ae138532a51226373ee9f9a8744c01 100644 (file)
@@ -17,46 +17,43 @@ process_diff_serial(struct update_notification *notification, char const *uri)
        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);
+       return rrdp_process_deltas(notification, 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);
+       return rrdp_parse_snapshot(notification);
 }
 
 int
 rrdp_load(struct rpki_uri *uri)
 {
        struct update_notification *upd_notification;
+       long last_update;
        enum rrdp_uri_cmp_result res;
        int error;
 
-       error = rrdp_parse_notification(uri, &upd_notification);
+       last_update = 0;
+       error = rhandler_uri_get_last_update(uri_get_global(uri), &last_update);
+       if (error && error != -ENOENT)
+               return error;
+
+       error = rrdp_parse_notification(uri, last_update, &upd_notification);
        if (error)
                return error;
 
+       /* No updates at the file (yet) */
+       if (upd_notification == NULL)
+               return 0;
+
        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 */
+               goto set_update;
        case RRDP_URI_DIFF_SERIAL:
                error = process_diff_serial(upd_notification,
                    uri_get_global(uri));
@@ -70,9 +67,19 @@ rrdp_load(struct rpki_uri *uri)
                pr_crit("Unexpected RRDP URI comparison result");
        }
 
-       /*
-        * FIXME (now) Now do the validation, start by the root manifest
-        */
+       /* Any change, and no error during the process, update db */
+       if (!error) {
+               error = rhandler_uri_update(uri_get_global(uri),
+                   upd_notification->global_data.session_id,
+                   upd_notification->global_data.serial);
+               if (error)
+                       goto end;
+       }
+
+set_update:
+       /* Set the last update to now */
+       error = rhandler_uri_set_last_update(uri_get_global(uri));
+end:
        update_notification_destroy(upd_notification);
        fnstack_pop(); /* Pop from rrdp_parse_notification */
 
index 143b06e0287b9b9ff3ff5ab5aed0e31af8eabbd5..7eb2b895e919d6ce0715bc977fcf01eaf8759745 100644 (file)
@@ -14,6 +14,7 @@
 #include "crypto/base64.h"
 #include "crypto/hash.h"
 #include "http/http.h"
+#include "rrdp/rrdp_handler.h"
 #include "xml/relax_ng.h"
 #include "common.h"
 #include "file.h"
@@ -965,9 +966,12 @@ release_uri:
  * 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.
+ *
+ * If the server didn't sent the file, due to the validation of
+ * 'If-Modified-Since' header, return 0 and set @result to NULL.
  */
 int
-rrdp_parse_notification(struct rpki_uri *uri,
+rrdp_parse_notification(struct rpki_uri *uri, long last_update,
     struct update_notification **result)
 {
        int error;
@@ -975,13 +979,16 @@ rrdp_parse_notification(struct rpki_uri *uri,
        if (uri == NULL || uri_is_rsync(uri))
                pr_crit("Wrong call, trying to parse a non HTTPS URI");
 
-       /*
-        * FIXME (now) Add "If-Modified-Since" header (see rfc8182#section-4.2)
-        */
-       error = http_download_file(uri, write_local);
-       if (error)
+       error = http_download_file_with_ims(uri, write_local, last_update);
+       if (error < 0)
                return error;
 
+       /* No updates yet */
+       if (error > 0) {
+               *result = NULL;
+               return 0;
+       }
+
        fnstack_push_uri(uri);
        error = parse_notification(uri, result);
        if (error) {
index e4c267353d4a43b9bda321b1aefbffbeafe7255f..21d23404253bab5ce68345800db0fdc47fc9be83 100644 (file)
@@ -4,7 +4,8 @@
 #include "rrdp/rrdp_objects.h"
 #include "uri.h"
 
-int rrdp_parse_notification(struct rpki_uri *, struct update_notification **);
+int rrdp_parse_notification(struct rpki_uri *, long,
+    struct update_notification **);
 int rrdp_parse_snapshot(struct update_notification *);
 
 int rrdp_process_deltas(struct update_notification *, unsigned long serial);
index 162b9712cd50eb6640fb679df4bf245a44141ec0..62f575dd2951610fd3f2736307e515d51a6c4cc2 100644 (file)
@@ -202,6 +202,20 @@ rrdp_uri_get_serial(char const *uri, unsigned long *serial)
            db_rrdp_get_serial(state.rrdp_uris, uri, serial))
 }
 
+int
+rrdp_uri_get_last_update(char const *uri, long *last_update)
+{
+       RLOCK_HANDLER(&state_lock,
+           db_rrdp_get_last_update(state.rrdp_uris, uri, last_update))
+}
+
+int
+rrdp_uri_set_last_update(char const *uri)
+{
+       WLOCK_HANDLER(&state_lock,
+           db_rrdp_set_last_update(state.rrdp_uris, uri))
+}
+
 static int
 __perform_standalone_validation(struct db_table **result)
 {
index 60dafe3709d1b5c0a2d520e73ebd5d7d52cafdd2..3cab65b9b7f85229e9a2e637d05f4cf31cafdbea 100644 (file)
@@ -45,6 +45,8 @@ 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 *);
+int rrdp_uri_get_last_update(char const *, long *);
+int rrdp_uri_set_last_update(char const *);
 
 uint16_t get_current_session_id(uint8_t);