From c7546a62b87c735ef3921904f1253adf9a74b9b1 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 28 Jun 2022 13:28:11 +0200 Subject: -support poll_providers also during backup --- src/reducer/Makefile.am | 1 + src/reducer/anastasis_api_backup_redux.c | 5 + src/reducer/anastasis_api_providers.c | 300 +++++++++++++++++++++++++++++ src/reducer/anastasis_api_recovery_redux.c | 299 +--------------------------- src/reducer/anastasis_api_redux.c | 71 ++++++- src/reducer/anastasis_api_redux.h | 36 ++++ 6 files changed, 407 insertions(+), 305 deletions(-) create mode 100644 src/reducer/anastasis_api_providers.c (limited to 'src') diff --git a/src/reducer/Makefile.am b/src/reducer/Makefile.am index 6dad659..1536d21 100644 --- a/src/reducer/Makefile.am +++ b/src/reducer/Makefile.am @@ -16,6 +16,7 @@ libanastasisredux_la_LDFLAGS = \ -no-undefined libanastasisredux_la_SOURCES = \ anastasis_api_discovery.c \ + anastasis_api_providers.c \ anastasis_api_redux.c anastasis_api_redux.h \ anastasis_api_recovery_redux.c \ anastasis_api_backup_redux.c \ diff --git a/src/reducer/anastasis_api_backup_redux.c b/src/reducer/anastasis_api_backup_redux.c index 2e73587..abd7c70 100644 --- a/src/reducer/anastasis_api_backup_redux.c +++ b/src/reducer/anastasis_api_backup_redux.c @@ -4716,6 +4716,11 @@ ANASTASIS_backup_action_ (json_t *state, "add_provider", &add_provider }, + { + ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, + "poll_providers", + &ANASTASIS_REDUX_poll_providers_ + }, { ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING, "back", diff --git a/src/reducer/anastasis_api_providers.c b/src/reducer/anastasis_api_providers.c new file mode 100644 index 0000000..82243f5 --- /dev/null +++ b/src/reducer/anastasis_api_providers.c @@ -0,0 +1,300 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + Anastasis; see the file COPYING.GPL. If not, see +*/ +/** + * @file reducer/anastasis_api_providers.c + * @brief anastasis provider synchronization logic + * @author Christian Grothoff + */ + +#include +#include +#include "anastasis_redux.h" +#include "anastasis_error_codes.h" +#include "anastasis_api_redux.h" + + +/** + * Main data structure for sync_providers(). + */ +struct MasterSync; + + +/** + * Data structure for one provider we are syncing /config with. + */ +struct SyncEntry +{ + /** + * Kept in a DLL. + */ + struct SyncEntry *next; + + /** + * Kept in a DLL. + */ + struct SyncEntry *prev; + + /** + * Sync operation we are part of. + */ + struct MasterSync *ms; + + /** + * Redux action for this provider. + */ + struct ANASTASIS_ReduxAction *ra; +}; + + +/** + * Main data structure for sync_providers(). + */ +struct MasterSync +{ + /** + * Our own sync action we expose externally. + */ + struct ANASTASIS_ReduxAction ra; + /** + * Head of DLL with entries per provider. + */ + struct SyncEntry *se_head; + /** + * Tail of DLL with entries per provider. + */ + struct SyncEntry *se_tail; + + /** + * Function to call with the result. + */ + ANASTASIS_ActionCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + +}; + + +/** + * Free @a cls data structure. + * + * @param[in] cls data structure to free, must be a `struct MasterSync *` + */ +static void +clean_sync (void *cls) +{ + struct MasterSync *ms = cls; + struct SyncEntry *se; + + while (NULL != (se = ms->se_head)) + { + GNUNET_CONTAINER_DLL_remove (ms->se_head, + ms->se_tail, + se); + se->ra->cleanup (se->ra->cleanup_cls); + GNUNET_free (se); + } + GNUNET_free (ms); +} + + +/** + * Function called when we have made progress on any of the + * providers we are trying to sync with. + * + * @param cls closure + * @param error error code, #TALER_EC_NONE if @a new_bs is the new successful state + * @param new_state the new state of the operation (client should json_incref() to keep an alias) + */ +static void +sync_progress (void *cls, + enum TALER_ErrorCode error, + json_t *new_state) +{ + struct SyncEntry *se = cls; + struct MasterSync *ms = se->ms; + + GNUNET_CONTAINER_DLL_remove (ms->se_head, + ms->se_tail, + se); + GNUNET_free (se); + ms->cb (ms->cb_cls, + error, + new_state); + clean_sync (ms); +} + + +struct ANASTASIS_ReduxAction * +ANASTASIS_REDUX_sync_providers_ (json_t *state, + const json_t *arguments, + ANASTASIS_ActionCallback cb, + void *cb_cls) +{ + json_t *rd; + json_t *cs_arr; + struct MasterSync *ms; + + rd = json_object_get (state, + "recovery_document"); + if (NULL == rd) + { + GNUNET_break (0); + ANASTASIS_redux_fail_ (cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'recovery_document' missing"); + return NULL; + } + cs_arr = json_object_get (rd, + "challenges"); + if (! json_is_array (cs_arr)) + { + GNUNET_break_op (0); + ANASTASIS_redux_fail_ (cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'recovery_document' must be an array"); + return NULL; + } + ms = GNUNET_new (struct MasterSync); + ms->cb = cb; + ms->cb_cls = cb_cls; + { + json_t *cs; + unsigned int n_index; + + json_array_foreach (cs_arr, n_index, cs) + { + const char *provider_url; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("url", + &provider_url), + GNUNET_JSON_spec_end () + }; + struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; + struct SyncEntry *se; + + if (GNUNET_OK != + GNUNET_JSON_parse (cs, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ANASTASIS_redux_fail_ (cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'recovery_document' missing"); + clean_sync (ms); + return NULL; + } + if (GNUNET_OK == + ANASTASIS_reducer_lookup_salt (state, + provider_url, + &provider_salt)) + continue; /* provider already ready */ + se = GNUNET_new (struct SyncEntry); + se->ms = ms; + GNUNET_CONTAINER_DLL_insert (ms->se_head, + ms->se_tail, + se); + se->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url, + state, + &sync_progress, + se); + } + } + if (NULL == ms->se_head) + { + /* everything already synced */ + clean_sync (ms); + ANASTASIS_redux_fail_ (cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED, + "already in sync"); + return NULL; + } + ms->ra.cleanup = &clean_sync; + ms->ra.cleanup_cls = ms; + return &ms->ra; +} + + +struct ANASTASIS_ReduxAction * +ANASTASIS_REDUX_poll_providers_ (json_t *state, + const json_t *arguments, + ANASTASIS_ActionCallback cb, + void *cb_cls) +{ + json_t *ap; + const char *url; + json_t *obj; + struct MasterSync *ms; + + ap = json_object_get (state, + "authentication_providers"); + if (NULL == ap) + { + GNUNET_break (0); + ANASTASIS_redux_fail_ (cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, + "'authentication_providers' missing"); + return NULL; + } + ms = GNUNET_new (struct MasterSync); + ms->cb = cb; + ms->cb_cls = cb_cls; + json_object_foreach (ap, url, obj) + { + struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; + struct SyncEntry *se; + struct ANASTASIS_ReduxAction *ra; + + if (GNUNET_OK == + ANASTASIS_reducer_lookup_salt (state, + url, + &provider_salt)) + continue; + se = GNUNET_new (struct SyncEntry); + se->ms = ms; + GNUNET_CONTAINER_DLL_insert (ms->se_head, + ms->se_tail, + se); + ra = ANASTASIS_REDUX_add_provider_to_state_ (url, + state, + &sync_progress, + se); + if (NULL == ra) + return NULL; /* sync_progress already called! */ + se->ra = ra; + } + if (NULL == ms->se_head) + { + /* everything already synced */ + clean_sync (ms); + ANASTASIS_redux_fail_ (cb, + cb_cls, + TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, + "already in sync"); + return NULL; + } + ms->ra.cleanup = &clean_sync; + ms->ra.cleanup_cls = ms; + return &ms->ra; +} diff --git a/src/reducer/anastasis_api_recovery_redux.c b/src/reducer/anastasis_api_recovery_redux.c index 22be38d..93884bb 100644 --- a/src/reducer/anastasis_api_recovery_redux.c +++ b/src/reducer/anastasis_api_recovery_redux.c @@ -2012,301 +2012,6 @@ select_challenge (json_t *state, } -/** - * Main data structure for sync_providers(). - */ -struct MasterSync; - - -/** - * Data structure for one provider we are syncing /config with. - */ -struct SyncEntry -{ - /** - * Kept in a DLL. - */ - struct SyncEntry *next; - - /** - * Kept in a DLL. - */ - struct SyncEntry *prev; - - /** - * Sync operation we are part of. - */ - struct MasterSync *ms; - - /** - * Redux action for this provider. - */ - struct ANASTASIS_ReduxAction *ra; -}; - -/** - * Main data structure for sync_providers(). - */ -struct MasterSync -{ - /** - * Our own sync action we expose externally. - */ - struct ANASTASIS_ReduxAction ra; - /** - * Head of DLL with entries per provider. - */ - struct SyncEntry *se_head; - /** - * Tail of DLL with entries per provider. - */ - struct SyncEntry *se_tail; - - /** - * Function to call with the result. - */ - ANASTASIS_ActionCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - -}; - - -/** - * Free @a cls data structure. - * - * @param[in] cls data structure to free, must be a `struct MasterSync *` - */ -static void -clean_sync (void *cls) -{ - struct MasterSync *ms = cls; - struct SyncEntry *se; - - while (NULL != (se = ms->se_head)) - { - GNUNET_CONTAINER_DLL_remove (ms->se_head, - ms->se_tail, - se); - se->ra->cleanup (se->ra->cleanup_cls); - GNUNET_free (se); - } - GNUNET_free (ms); -} - - -/** - * Function called when we have made progress on any of the - * providers we are trying to sync with. - * - * @param cls closure - * @param error error code, #TALER_EC_NONE if @a new_bs is the new successful state - * @param new_state the new state of the operation (client should json_incref() to keep an alias) - */ -static void -sync_progress (void *cls, - enum TALER_ErrorCode error, - json_t *new_state) -{ - struct SyncEntry *se = cls; - struct MasterSync *ms = se->ms; - - GNUNET_CONTAINER_DLL_remove (ms->se_head, - ms->se_tail, - se); - GNUNET_free (se); - ms->cb (ms->cb_cls, - error, - new_state); - clean_sync (ms); -} - - -/** - * Check if we have information on all providers involved in - * a recovery procedure, and if not, try to obtain it. Upon - * success, call @a cb with the updated provider status data. - * - * @param[in] state we are in - * @param arguments our arguments with the solution - * @param cb functiont o call with the new state - * @param cb_cls closure for @a cb - * @return handle to cancel challenge selection step - */ -static struct ANASTASIS_ReduxAction * -sync_providers (json_t *state, - const json_t *arguments, - ANASTASIS_ActionCallback cb, - void *cb_cls) -{ - json_t *rd; - json_t *cs_arr; - struct MasterSync *ms; - - rd = json_object_get (state, - "recovery_document"); - if (NULL == rd) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_document' missing"); - return NULL; - } - cs_arr = json_object_get (rd, - "challenges"); - if (! json_is_array (cs_arr)) - { - GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_document' must be an array"); - return NULL; - } - ms = GNUNET_new (struct MasterSync); - ms->cb = cb; - ms->cb_cls = cb_cls; - { - json_t *cs; - unsigned int n_index; - - json_array_foreach (cs_arr, n_index, cs) - { - const char *provider_url; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("url", - &provider_url), - GNUNET_JSON_spec_end () - }; - struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; - struct SyncEntry *se; - - if (GNUNET_OK != - GNUNET_JSON_parse (cs, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'recovery_document' missing"); - clean_sync (ms); - return NULL; - } - if (GNUNET_OK == - ANASTASIS_reducer_lookup_salt (state, - provider_url, - &provider_salt)) - continue; /* provider already ready */ - se = GNUNET_new (struct SyncEntry); - se->ms = ms; - GNUNET_CONTAINER_DLL_insert (ms->se_head, - ms->se_tail, - se); - se->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url, - state, - &sync_progress, - se); - } - } - if (NULL == ms->se_head) - { - /* everything already synced */ - clean_sync (ms); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED, - "already in sync"); - return NULL; - } - ms->ra.cleanup = &clean_sync; - ms->ra.cleanup_cls = ms; - return &ms->ra; -} - - -/** - * Try to obtain configuration information on all configured - * providers. Upon success, call @a cb with the updated provider - * status data. - * - * @param[in] state we are in - * @param arguments our arguments with the solution - * @param cb functiont o call with the new state - * @param cb_cls closure for @a cb - * @return handle to cancel challenge selection step - */ -static struct ANASTASIS_ReduxAction * -poll_providers (json_t *state, - const json_t *arguments, - ANASTASIS_ActionCallback cb, - void *cb_cls) -{ - json_t *ap; - const char *url; - json_t *obj; - struct MasterSync *ms; - - ap = json_object_get (state, - "authentication_providers"); - if (NULL == ap) - { - GNUNET_break (0); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, - "'authentication_providers' missing"); - return NULL; - } - ms = GNUNET_new (struct MasterSync); - ms->cb = cb; - ms->cb_cls = cb_cls; - json_object_foreach (ap, url, obj) - { - struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; - struct SyncEntry *se; - struct ANASTASIS_ReduxAction *ra; - - if (GNUNET_OK == - ANASTASIS_reducer_lookup_salt (state, - url, - &provider_salt)) - continue; - se = GNUNET_new (struct SyncEntry); - se->ms = ms; - GNUNET_CONTAINER_DLL_insert (ms->se_head, - ms->se_tail, - se); - ra = ANASTASIS_REDUX_add_provider_to_state_ (url, - state, - &sync_progress, - se); - if (NULL == ra) - return NULL; /* sync_progress already called! */ - se->ra = ra; - } - if (NULL == ms->se_head) - { - /* everything already synced */ - clean_sync (ms); - ANASTASIS_redux_fail_ (cb, - cb_cls, - TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, - "already in sync"); - return NULL; - } - ms->ra.cleanup = &clean_sync; - ms->ra.cleanup_cls = ms; - return &ms->ra; -} - - /** * The user pressed "back" during challenge solving. * Transition back to selecting another challenge. @@ -2833,7 +2538,7 @@ ANASTASIS_recovery_action_ (json_t *state, { ANASTASIS_RECOVERY_STATE_SECRET_SELECTING, "poll_providers", - &poll_providers + &ANASTASIS_REDUX_poll_providers_ }, { ANASTASIS_RECOVERY_STATE_SECRET_SELECTING, @@ -2853,7 +2558,7 @@ ANASTASIS_recovery_action_ (json_t *state, { ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING, "sync_providers", - &sync_providers + &ANASTASIS_REDUX_sync_providers_ }, { ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING, diff --git a/src/reducer/anastasis_api_redux.c b/src/reducer/anastasis_api_redux.c index 7554b06..36386de 100644 --- a/src/reducer/anastasis_api_redux.c +++ b/src/reducer/anastasis_api_redux.c @@ -147,6 +147,11 @@ struct ConfigRequest */ struct GNUNET_TIME_Absolute timeout_at; + /** + * How long do we wait before trying again? + */ + struct GNUNET_TIME_Relative backoff; + /** * Obtained status code. */ @@ -580,6 +585,16 @@ notify_waiting_cb (void *cls) } +/** + * Function called when it is time to retry a + * failed /config request. + * + * @param cls the `struct ConfigRequest *` to retry. + */ +static void +retry_config (void *cls); + + /** * Function called with the results of a #ANASTASIS_get_config(). * @@ -595,8 +610,11 @@ config_cb (void *cls, struct ConfigRequest *cr = cls; cr->co = NULL; - GNUNET_SCHEDULER_cancel (cr->tt); - cr->tt = NULL; + if (NULL != cr->tt) + { + GNUNET_SCHEDULER_cancel (cr->tt); + cr->tt = NULL; + } cr->http_status = http_status; if (MHD_HTTP_OK != http_status) { @@ -642,6 +660,13 @@ config_cb (void *cls, } } notify_waiting (cr); + if (MHD_HTTP_OK != http_status) + { + cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); + cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, + &retry_config, + cr); + } } @@ -656,11 +681,41 @@ config_request_timeout (void *cls) struct ConfigRequest *cr = cls; cr->tt = NULL; - ANASTASIS_config_cancel (cr->co); - cr->co = NULL; + if (NULL != cr->co) + { + ANASTASIS_config_cancel (cr->co); + cr->co = NULL; + } cr->http_status = 0; cr->ec = TALER_EC_GENERIC_TIMEOUT; notify_waiting (cr); + cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); + cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, + &retry_config, + cr); +} + + +static void +retry_config (void *cls) +{ + struct ConfigRequest *cr = cls; + + cr->tt = NULL; + if (NULL != cr->co) + { + ANASTASIS_config_cancel (cr->co); + cr->co = NULL; + } + cr->timeout_at = GNUNET_TIME_relative_to_absolute (CONFIG_GENERIC_TIMEOUT); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); + cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, + cr->url, + &config_cb, + cr); + GNUNET_break (NULL != cr->co); } @@ -718,6 +773,10 @@ check_config (struct GNUNET_TIME_Relative timeout, } if (MHD_HTTP_OK == cr->http_status) return cr; + cr->timeout_at = GNUNET_TIME_relative_to_absolute (timeout); + cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, + &config_request_timeout, + cr); cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, cr->url, &config_cb, @@ -727,10 +786,6 @@ check_config (struct GNUNET_TIME_Relative timeout, GNUNET_break (0); return NULL; } - cr->timeout_at = GNUNET_TIME_relative_to_absolute (timeout); - cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, - &config_request_timeout, - cr); return cr; } diff --git a/src/reducer/anastasis_api_redux.h b/src/reducer/anastasis_api_redux.h index cc59713..03eef33 100644 --- a/src/reducer/anastasis_api_redux.h +++ b/src/reducer/anastasis_api_redux.h @@ -107,6 +107,42 @@ json_t * ANASTASIS_REDUX_load_continents_ (void); +/** + * Try to obtain configuration information on all configured + * providers. Upon success, call @a cb with the updated provider + * status data. + * + * @param[in] state we are in + * @param arguments our arguments with the solution + * @param cb functiont o call with the new state + * @param cb_cls closure for @a cb + * @return handle to cancel challenge selection step + */ +struct ANASTASIS_ReduxAction * +ANASTASIS_REDUX_poll_providers_ (json_t *state, + const json_t *arguments, + ANASTASIS_ActionCallback cb, + void *cb_cls); + + +/** + * Check if we have information on all providers involved in + * a recovery procedure, and if not, try to obtain it. Upon + * success, call @a cb with the updated provider status data. + * + * @param[in] state we are in + * @param arguments our arguments with the solution + * @param cb functiont o call with the new state + * @param cb_cls closure for @a cb + * @return handle to cancel challenge selection step + */ +struct ANASTASIS_ReduxAction * +ANASTASIS_REDUX_sync_providers_ (json_t *state, + const json_t *arguments, + ANASTASIS_ActionCallback cb, + void *cb_cls); + + /** * Returns the enum value to a string value of a state. * -- cgit v1.2.3