From: pcarana Date: Wed, 4 Dec 2019 16:05:55 +0000 (-0600) Subject: Send 'If-Modified-Since' header on update notification requests. X-Git-Tag: v1.2.0~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ec56deed31da6e0f6892fe96159588ebec4bc7fd;p=thirdparty%2FFORT-validator.git Send 'If-Modified-Since' header on update notification requests. +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. --- diff --git a/src/http/http.c b/src/http/http.c index 70865843..3d848025 100644 --- a/src/http/http.c +++ b/src/http/http.c @@ -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; +} diff --git a/src/http/http.h b/src/http/http.h index 25cce94f..073de4ea 100644 --- a/src/http/http.h +++ b/src/http/http.h @@ -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_ */ diff --git a/src/object/tal.c b/src/object/tal.c index d5e9b5ed..9674c40f 100644 --- a/src/object/tal.c +++ b/src/object/tal.c @@ -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); diff --git a/src/rrdp/db_rrdp.c b/src/rrdp/db_rrdp.c index fece933b..379dab57 100644 --- a/src/rrdp/db_rrdp.c +++ b/src/rrdp/db_rrdp.c @@ -2,6 +2,7 @@ #include #include +#include #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) { diff --git a/src/rrdp/db_rrdp.h b/src/rrdp/db_rrdp.h index 3f32ee65..2dff90b6 100644 --- a/src/rrdp/db_rrdp.h +++ b/src/rrdp/db_rrdp.h @@ -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_ */ diff --git a/src/rrdp/rrdp_handler.c b/src/rrdp/rrdp_handler.c index 9b0b9855..b6ce91ec 100644 --- a/src/rrdp/rrdp_handler.c +++ b/src/rrdp/rrdp_handler.c @@ -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)) } diff --git a/src/rrdp/rrdp_handler.h b/src/rrdp/rrdp_handler.h index 3f5db639..aebe582b 100644 --- a/src/rrdp/rrdp_handler.h +++ b/src/rrdp/rrdp_handler.h @@ -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_ */ diff --git a/src/rrdp/rrdp_loader.c b/src/rrdp/rrdp_loader.c index 5c82958e..256f1f34 100644 --- a/src/rrdp/rrdp_loader.c +++ b/src/rrdp/rrdp_loader.c @@ -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 */ diff --git a/src/rrdp/rrdp_parser.c b/src/rrdp/rrdp_parser.c index 143b06e0..7eb2b895 100644 --- a/src/rrdp/rrdp_parser.c +++ b/src/rrdp/rrdp_parser.c @@ -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) { diff --git a/src/rrdp/rrdp_parser.h b/src/rrdp/rrdp_parser.h index e4c26735..21d23404 100644 --- a/src/rrdp/rrdp_parser.h +++ b/src/rrdp/rrdp_parser.h @@ -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); diff --git a/src/rtr/db/vrps.c b/src/rtr/db/vrps.c index 162b9712..62f575dd 100644 --- a/src/rtr/db/vrps.c +++ b/src/rtr/db/vrps.c @@ -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) { diff --git a/src/rtr/db/vrps.h b/src/rtr/db/vrps.h index 60dafe37..3cab65b9 100644 --- a/src/rtr/db/vrps.h +++ b/src/rtr/db/vrps.h @@ -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);