+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.
#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];
/* 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);
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;
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
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;
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);
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;
+}
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_ */
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);
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include "data_structure/uthash_nonfatal.h"
#include "rrdp/rrdp_objects.h"
#include "log.h"
/* Key */
char *uri;
struct global_data data;
+ long last_update;
UT_hash_handle hh;
};
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)
}
tmp->data.serial = serial;
+ tmp->last_update = 0;
*result = tmp;
return 0;
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)
{
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_ */
#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)
{
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))
}
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_ */
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));
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 */
#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"
* 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;
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) {
#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);
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)
{
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);