diff options
author | Christian Grothoff <christian@grothoff.org> | 2019-11-15 09:39:31 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2019-11-15 09:39:31 +0100 |
commit | 9badf80eb4228a9c009839a4856710127efe8601 (patch) | |
tree | ec62e28f2ab3b90ca374537a9b2730b1fecdbb93 /src | |
parent | d0e22221b9fd3627d63e484b8957337d2638a871 (diff) | |
download | sync-9badf80eb4228a9c009839a4856710127efe8601.tar.gz sync-9badf80eb4228a9c009839a4856710127efe8601.tar.bz2 sync-9badf80eb4228a9c009839a4856710127efe8601.zip |
implement GET logic
Diffstat (limited to 'src')
-rw-r--r-- | src/include/sync_database_plugin.h | 19 | ||||
-rw-r--r-- | src/include/sync_service.h | 2 | ||||
-rw-r--r-- | src/sync/sync-httpd.c | 27 | ||||
-rw-r--r-- | src/sync/sync-httpd_backup.c | 170 | ||||
-rw-r--r-- | src/sync/sync-httpd_backup.h | 10 | ||||
-rw-r--r-- | src/syncdb/plugin_syncdb_postgres.c | 108 |
6 files changed, 306 insertions, 30 deletions
diff --git a/src/include/sync_database_plugin.h b/src/include/sync_database_plugin.h index cdc65e9..d5759ee 100644 --- a/src/include/sync_database_plugin.h +++ b/src/include/sync_database_plugin.h @@ -38,7 +38,7 @@ enum SYNC_DB_QueryStatus SYNC_DB_OLD_BACKUP_MISSMATCH = -4, /** - * Account is unpaid. + * Account is unpaid / does not exist. */ SYNC_DB_PAYMENT_REQUIRED = -3, @@ -153,12 +153,28 @@ struct SYNC_DatabasePlugin size_t backup_size, const void *backup); + + /** + * Lookup an account and associated backup meta data. + * + * @param cls closure + * @param account_pub account to store @a backup under + * @param backup_hash[OUT] set to hash of @a backup + * @return transaction status + */ + enum SYNC_DB_QueryStatus + (*lookup_account_TR)(void *cls, + const struct SYNC_AccountPublicKeyP *account_pub, + struct GNUNET_HashCode *backup_hash); + + /** * Obtain backup. * * @param cls closure * @param account_pub account to store @a backup under * @param account_sig[OUT] set to signature affirming storage request + * @param prev_hash[OUT] set to hash of the previous @a backup (all zeros if none) * @param backup_hash[OUT] set to hash of @a backup * @param backup_size[OUT] set to number of bytes in @a backup * @param backup[OUT] set to raw data to backup, caller MUST FREE @@ -167,6 +183,7 @@ struct SYNC_DatabasePlugin (*lookup_backup_TR)(void *cls, const struct SYNC_AccountPublicKeyP *account_pub, struct SYNC_AccountSignatureP *account_sig, + struct GNUNET_HashCode *prev_hash, struct GNUNET_HashCode *backup_hash, size_t *backup_size, void **backup); diff --git a/src/include/sync_service.h b/src/include/sync_service.h index e99d0c3..20bbe67 100644 --- a/src/include/sync_service.h +++ b/src/include/sync_service.h @@ -46,7 +46,7 @@ struct SYNC_AccountPublicKeyP /** * We use EdDSA. */ - struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv; + struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub; }; diff --git a/src/sync/sync-httpd.c b/src/sync/sync-httpd.c index d7f6eb5..98a2b24 100644 --- a/src/sync/sync-httpd.c +++ b/src/sync/sync-httpd.c @@ -175,7 +175,10 @@ url_handler (void *cls, struct TM_HandlerContext *hc; struct GNUNET_AsyncScopeId aid; const char *correlation_id = NULL; + struct SYNC_AccountPublicKeyP account_pub; + (void) cls; + (void) version; hc = *con_cls; if (NULL == hc) @@ -197,7 +200,6 @@ url_handler (void *cls, { aid = hc->async_scope_id; } - GNUNET_SCHEDULER_begin_async_scope (&aid); if (NULL != correlation_id) @@ -211,22 +213,25 @@ url_handler (void *cls, "Handling request (%s) for URL '%s'\n", method, url); - if (0 == strncmp (url, - "/backup/", - strlen ("/backup/"))) + + if (GNUNET_OK == + GNUNET_CRYPTO_eddsa_public_key_from_string (url, + strlen (url), + &account_pub.eddsa_pub)) { - // return handle_policy (...); - if (0 == strcmp (method, MHD_HTTP_METHOD_GET)) + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_GET)) { return sync_handler_backup_get (connection, - url, + &account_pub, con_cls); } - if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_POST)) { return sync_handler_backup_post (connection, con_cls, - url, + &account_pub, upload_data, upload_data_size); } @@ -238,8 +243,8 @@ url_handler (void *cls, if ( (0 == strcmp (url, rh->url)) && ( (NULL == rh->method) || - (0 == strcmp (method, - rh->method)) ) ) + (0 == strcasecmp (method, + rh->method)) ) ) { int ret; diff --git a/src/sync/sync-httpd_backup.c b/src/sync/sync-httpd_backup.c index 86ba955..61d9151 100644 --- a/src/sync/sync-httpd_backup.c +++ b/src/sync/sync-httpd_backup.c @@ -21,26 +21,182 @@ #include "platform.h" #include "sync-httpd.h" #include <gnunet/gnunet_util_lib.h> +#include "sync-httpd_backup.h" +#include "sync-httpd_responses.h" /** * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param account public key of the account the request is for + * @param[in,out] con_cls the connection's closure (can be updated) * @return MHD result code */ int sync_handler_backup_get (struct MHD_Connection *connection, - const char *url, + const struct SYNC_AccountPublicKeyP *account, void **con_cls) { - return MHD_NO; + struct SYNC_AccountSignatureP account_sig; + struct GNUNET_HashCode backup_hash; + struct GNUNET_HashCode prev_hash; + size_t backup_size; + void *backup; + enum SYNC_DB_QueryStatus qs; + struct MHD_Response *resp; + const char *inm; + struct GNUNET_HashCode inm_h; + int ret; + + inm = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_IF_NONE_MATCH); + qs = db->lookup_account_TR (db->cls, + account, + &backup_hash); + switch (qs) + { + case SYNC_DB_OLD_BACKUP_MISSMATCH: + GNUNET_break (0); + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_INTERNAL_INVARIANT_FAILURE, + "unexpected return status (backup missmatch)"); + case SYNC_DB_PAYMENT_REQUIRED: + return SH_RESPONSE_reply_not_found (connection, + TALER_EC_SYNC_ACCOUNT_UNKNOWN, + "account"); + case SYNC_DB_HARD_ERROR: + GNUNET_break (0); + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_SYNC_DB_FETCH_ERROR, + "hard database failure"); + case SYNC_DB_SOFT_ERROR: + GNUNET_break (0); + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_SYNC_DB_FETCH_ERROR, + "soft database failure"); + case SYNC_DB_NO_RESULTS: + resp = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, + MHD_HTTP_NO_CONTENT, + resp); + MHD_destroy_response (resp); + return ret; + case SYNC_DB_ONE_RESULT: + if (NULL != inm) + { + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (inm, + strlen (inm), + &inm_h, + sizeof (inm_h))) + { + GNUNET_break_op (0); + return SH_RESPONSE_reply_bad_request (connection, + TALER_EC_SYNC_BAD_ETAG, + "Etag is not a base32-encoded SHA-512 hash"); + } + if (0 == GNUNET_memcmp (&inm_h, + &backup_hash)) + { + resp = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, + MHD_HTTP_NOT_MODIFIED, + resp); + MHD_destroy_response (resp); + return ret; + } + } + /* We have a result, should fetch and return it! */ + break; + } + + qs = db->lookup_backup_TR (db->cls, + account, + &account_sig, + &prev_hash, + &backup_hash, + &backup_size, + &backup); + switch (qs) + { + case SYNC_DB_OLD_BACKUP_MISSMATCH: + GNUNET_break (0); + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_INTERNAL_INVARIANT_FAILURE, + "unexpected return status (backup missmatch)"); + case SYNC_DB_PAYMENT_REQUIRED: + GNUNET_break (0); + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_INTERNAL_INVARIANT_FAILURE, + "unexpected return status (payment required)"); + case SYNC_DB_HARD_ERROR: + GNUNET_break (0); + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_SYNC_DB_FETCH_ERROR, + "hard database failure"); + case SYNC_DB_SOFT_ERROR: + GNUNET_break (0); + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_SYNC_DB_FETCH_ERROR, + "soft database failure"); + case SYNC_DB_NO_RESULTS: + GNUNET_break (0); + /* Note: can theoretically happen due to non-transactional nature if + the backup expired / was gc'ed JUST between the two SQL calls. + But too rare to handle properly, as doing a transaction would be + expensive. Just admit to failure ;-) */ + return SH_RESPONSE_reply_internal_error (connection, + TALER_EC_SYNC_DB_FETCH_ERROR, + "unexpected empty result set (try again?)"); + case SYNC_DB_ONE_RESULT: + /* interesting case below */ + break; + } + resp = MHD_create_response_from_buffer (backup_size, + backup, + MHD_RESPMEM_MUST_FREE); + { + char *sig_s; + char *prev_s; + char *etag; + + sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig, + sizeof (account_sig)); + prev_s = GNUNET_STRINGS_data_to_string_alloc (&prev_hash, + sizeof (prev_hash)); + etag = GNUNET_STRINGS_data_to_string_alloc (&backup_hash, + sizeof (backup_hash)); + GNUNET_break (MHD_YES == + MHD_add_response_header (resp, + "X-Sync-Signature", + sig_s)); + GNUNET_break (MHD_YES == + MHD_add_response_header (resp, + "X-Sync-Previous", + prev_s)); + GNUNET_break (MHD_YES == + MHD_add_response_header (resp, + MHD_HTTP_HEADER_ETAG, + etag)); + GNUNET_free (etag); + GNUNET_free (prev_s); + GNUNET_free (sig_s); + } + ret = MHD_queue_response (connection, + MHD_HTTP_NOT_MODIFIED, + resp); + MHD_destroy_response (resp); + return ret; } /** * @param connection the MHD connection to handle * @param[in,out] connection_cls the connection's closure (can be updated) + * @param account public key of the account the request is for * @param upload_data upload data * @param[in,out] upload_data_size number of bytes (left) in @a upload_data * @return MHD result code @@ -48,9 +204,11 @@ sync_handler_backup_get (struct MHD_Connection *connection, int sync_handler_backup_post (struct MHD_Connection *connection, void **con_cls, - const char *url, + const struct SYNC_AccountPublicKeyP *account, const char *upload_data, size_t *upload_data_size) { + struct SYNC_AccountSignatureP account_sig; + return MHD_NO; } diff --git a/src/sync/sync-httpd_backup.h b/src/sync/sync-httpd_backup.h index 1ba7408..02f31be 100644 --- a/src/sync/sync-httpd_backup.h +++ b/src/sync/sync-httpd_backup.h @@ -24,20 +24,20 @@ /** * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param account public key of the account the request is for + * @param[in,out] con_cls the connection's closure (can be updated) * @return MHD result code */ int sync_handler_backup_get (struct MHD_Connection *connection, - const char *url, + const struct SYNC_AccountPublicKeyP *account, void **con_cls); /** * @param connection the MHD connection to handle * @param[in,out] connection_cls the connection's closure (can be updated) + * @param account public key of the account the request is for * @param upload_data upload data * @param[in,out] upload_data_size number of bytes (left) in @a upload_data * @return MHD result code @@ -45,7 +45,7 @@ sync_handler_backup_get (struct MHD_Connection *connection, int sync_handler_backup_post (struct MHD_Connection *connection, void **con_cls, - const char *url, + const struct SYNC_AccountPublicKeyP *account, const char *upload_data, size_t *upload_data_size); diff --git a/src/syncdb/plugin_syncdb_postgres.c b/src/syncdb/plugin_syncdb_postgres.c index 1d89b5d..aba58d8 100644 --- a/src/syncdb/plugin_syncdb_postgres.c +++ b/src/syncdb/plugin_syncdb_postgres.c @@ -258,6 +258,7 @@ postgres_store_backup (void *cls, struct PostgresClosure *pg = cls; enum GNUNET_DB_QueryStatus qs; struct GNUNET_HashCode bh; + static struct GNUNET_HashCode no_previous_hash; check_connection (pg); postgres_preflight (pg); @@ -265,6 +266,7 @@ postgres_store_backup (void *cls, struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (account_pub), GNUNET_PQ_query_param_auto_from_type (account_sig), + GNUNET_PQ_query_param_auto_from_type (&no_previous_hash), GNUNET_PQ_query_param_auto_from_type (backup_hash), GNUNET_PQ_query_param_fixed_size (backup, backup_size), @@ -404,6 +406,7 @@ postgres_update_backup (void *cls, struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (backup_hash), GNUNET_PQ_query_param_auto_from_type (account_sig), + GNUNET_PQ_query_param_auto_from_type (old_backup_hash), GNUNET_PQ_query_param_fixed_size (backup, backup_size), GNUNET_PQ_query_param_auto_from_type (account_pub), @@ -520,11 +523,96 @@ postgres_update_backup (void *cls, /** + * Lookup an account and associated backup meta data. + * + * @param cls closure + * @param account_pub account to store @a backup under + * @param backup_hash[OUT] set to hash of @a backup + * @return transaction status + */ +static enum SYNC_DB_QueryStatus +postgres_lookup_account (void *cls, + const struct SYNC_AccountPublicKeyP *account_pub, + struct GNUNET_HashCode *backup_hash) +{ + struct PostgresClosure *pg = cls; + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + postgres_preflight (pg); + { + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("backup_hash", + backup_hash), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "backup_select_hash", + params, + rs); + } + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return SYNC_DB_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return SYNC_DB_SOFT_ERROR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + break; /* handle interesting case below */ + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + return SYNC_DB_ONE_RESULT; + default: + GNUNET_break (0); + return SYNC_DB_HARD_ERROR; + } + + /* check if account exists */ + { + struct GNUNET_TIME_Absolute expiration; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("expiration_date", + &expiration), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "account_select", + params, + rs); + } + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return SYNC_DB_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return SYNC_DB_SOFT_ERROR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* indicates: no account */ + return SYNC_DB_PAYMENT_REQUIRED; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* indicates: no backup */ + return SYNC_DB_NO_RESULTS; + default: + GNUNET_break (0); + return SYNC_DB_HARD_ERROR; + } +} + + +/** * Obtain backup. * * @param cls closure * @param account_pub account to store @a backup under * @param account_sig[OUT] set to signature affirming storage request + * @param prev_hash[OUT] set to hash of previous @a backup, all zeros if none * @param backup_hash[OUT] set to hash of @a backup * @param backup_size[OUT] set to number of bytes in @a backup * @param backup[OUT] set to raw data to backup, caller MUST FREE @@ -533,6 +621,7 @@ static enum SYNC_DB_QueryStatus postgres_lookup_backup (void *cls, const struct SYNC_AccountPublicKeyP *account_pub, struct SYNC_AccountSignatureP *account_sig, + struct GNUNET_HashCode *prev_hash, struct GNUNET_HashCode *backup_hash, size_t *backup_size, void **backup) @@ -546,6 +635,8 @@ postgres_lookup_backup (void *cls, struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("account_sig", account_sig), + GNUNET_PQ_result_spec_auto_from_type ("prev_hash", + prev_hash), GNUNET_PQ_result_spec_auto_from_type ("backup_hash", backup_hash), GNUNET_PQ_result_spec_variable_size ("data", @@ -724,6 +815,7 @@ libsync_plugin_db_postgres_init (void *cls) "(" "account_pub BYTEA PRIMARY KEY REFERENCES accounts (account_pub)," "account_sig BYTEA NOT NULL CHECK (length(account_sig)=64)," + "prev_hash BYTEA NOT NULL CHECK (length(prev_hash)=64)," "backup_hash BYTEA NOT NULL CHECK (length(backup_hash)=64)," "data BYTEA NOT NULL" ");"), @@ -766,22 +858,24 @@ libsync_plugin_db_postgres_init (void *cls) "INSERT INTO backups " "(account_pub" ",account_sig" + ",prev_hash" ",backup_hash" ",data" ") VALUES " - "($1,$2,$3,$4);", - 4), + "($1,$2,$3,$4,$5);", + 5), GNUNET_PQ_make_prepare ("backup_update", "UPDATE backups " " SET" " backup_hash=$1" ",account_sig=$2" - ",data=$3" + ",prev_hash=$3" + ",data=$4" " WHERE" - " account_pub=$4" + " account_pub=$5" " AND" - " backup_hash=$5;", - 5), + " backup_hash=$6;", + 6), GNUNET_PQ_make_prepare ("backup_select_hash", "SELECT " " backup_hash " @@ -793,6 +887,7 @@ libsync_plugin_db_postgres_init (void *cls) GNUNET_PQ_make_prepare ("backup_select", "SELECT " " account_sig" + ",prev_hash" ",backup_hash" ",data " "FROM" @@ -822,6 +917,7 @@ libsync_plugin_db_postgres_init (void *cls) plugin->drop_tables = &postgres_drop_tables; plugin->gc = &postgres_gc; plugin->store_backup_TR = &postgres_store_backup; + plugin->lookup_account_TR = &postgres_lookup_account; plugin->lookup_backup_TR = &postgres_lookup_backup; plugin->update_backup_TR = &postgres_update_backup; plugin->increment_lifetime_TR = &postgres_increment_lifetime; |