diff --git a/debian/taler-exchange.taler-exchange-aggregator.service b/debian/taler-exchange.taler-exchange-aggregator.service index d0fcbf60c..683c1a81e 100644 --- a/debian/taler-exchange.taler-exchange-aggregator.service +++ b/debian/taler-exchange.taler-exchange-aggregator.service @@ -5,7 +5,8 @@ PartOf=taler-exchange.target [Service] User=taler-exchange-aggregator Type=simple -Restart=on-failure +Restart=always +RestartSec=100ms ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf StandardOutput=journal StandardError=journal diff --git a/debian/taler-exchange.taler-exchange-closer.service b/debian/taler-exchange.taler-exchange-closer.service index b2a820d35..01c52b2d4 100644 --- a/debian/taler-exchange.taler-exchange-closer.service +++ b/debian/taler-exchange.taler-exchange-closer.service @@ -5,7 +5,8 @@ PartOf=taler-exchange.target [Service] User=taler-exchange-closer Type=simple -Restart=on-failure +Restart=always +RestartSec=100ms ExecStart=/usr/bin/taler-exchange-closer -c /etc/taler/taler.conf StandardOutput=journal StandardError=journal diff --git a/debian/taler-exchange.taler-exchange-httpd.service b/debian/taler-exchange.taler-exchange-httpd.service index de65e17b6..7db2b1194 100644 --- a/debian/taler-exchange.taler-exchange-httpd.service +++ b/debian/taler-exchange.taler-exchange-httpd.service @@ -8,7 +8,11 @@ PartOf=taler-exchange.target [Service] User=taler-exchange-httpd Type=simple -Restart=on-failure +# Depending on the configuration, the service suicides and then +# needs to be restarted. +Restart=always +# Do not dally on restarts. +RestartSec=1ms ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf StandardOutput=journal StandardError=journal diff --git a/debian/taler-exchange.taler-exchange-httpd@.service b/debian/taler-exchange.taler-exchange-httpd@.service new file mode 100644 index 000000000..4235f72e9 --- /dev/null +++ b/debian/taler-exchange.taler-exchange-httpd@.service @@ -0,0 +1,26 @@ +% This is a systemd service template. +[Unit] +Description=GNU Taler payment system exchange REST API at %I +AssertPathExists=/run/taler/exchange-httpd +Requires=taler-exchange-httpd@%i.socket taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service +After=postgres.service network.target taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-httpd +Type=simple +# Depending on the configuration, the service suicides and then +# needs to be restarted. +Restart=always +# Do not dally on restarts. +RestartSec=1ms +EnvironmentFile=/etc/environment +ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=no +PrivateDevices=yes +ProtectSystem=full + +[Install] +WantedBy=multi-user.target diff --git a/debian/taler-exchange.taler-exchange-httpd@.socket b/debian/taler-exchange.taler-exchange-httpd@.socket new file mode 100644 index 000000000..e1d6b6bd4 --- /dev/null +++ b/debian/taler-exchange.taler-exchange-httpd@.socket @@ -0,0 +1,14 @@ +[Unit] +Description=Taler Exchange Socket at %I +PartOf=taler-exchange-httpd@%i.service + +[Socket] +ListenStream=80 +Accept=no +Service=taler-exchange-httpd@%i.service +SocketUser=taler-exchange-httpd +SocketGroup=www-data +SocketMode=0660 + +[Install] +WantedBy=sockets.target diff --git a/debian/taler-exchange.taler-exchange-secmod-eddsa.service b/debian/taler-exchange.taler-exchange-secmod-eddsa.service index 7406e3c76..e4898581c 100644 --- a/debian/taler-exchange.taler-exchange-secmod-eddsa.service +++ b/debian/taler-exchange.taler-exchange-secmod-eddsa.service @@ -6,7 +6,8 @@ PartOf=taler-exchange.target [Service] User=taler-exchange-secmod-eddsa Type=simple -Restart=on-failure +Restart=always +RestartSec=100ms ExecStart=/usr/bin/taler-exchange-secmod-eddsa -c /etc/taler/taler.conf StandardOutput=journal StandardError=journal diff --git a/debian/taler-exchange.taler-exchange-secmod-rsa.service b/debian/taler-exchange.taler-exchange-secmod-rsa.service index b4fedd1cc..6c5a3d613 100644 --- a/debian/taler-exchange.taler-exchange-secmod-rsa.service +++ b/debian/taler-exchange.taler-exchange-secmod-rsa.service @@ -6,7 +6,8 @@ PartOf=taler-exchange.target [Service] User=taler-exchange-secmod-rsa Type=simple -Restart=on-failure +Restart=always +RestartSec=100ms ExecStart=/usr/bin/taler-exchange-secmod-rsa -c /etc/taler/taler.conf StandardOutput=journal StandardError=journal diff --git a/debian/taler-exchange.taler-exchange-transfer.service b/debian/taler-exchange.taler-exchange-transfer.service index 274b64624..b2615b7c9 100644 --- a/debian/taler-exchange.taler-exchange-transfer.service +++ b/debian/taler-exchange.taler-exchange-transfer.service @@ -6,7 +6,8 @@ PartOf=taler-exchange.target [Service] User=taler-exchange-wire Type=simple -Restart=on-failure +Restart=always +RestartSec=100ms ExecStart=/usr/bin/taler-exchange-transfer -c /etc/taler/taler.conf StandardOutput=journal StandardError=journal diff --git a/debian/taler-exchange.taler-exchange-wirewatch.service b/debian/taler-exchange.taler-exchange-wirewatch.service index 2705ca2b5..54704cb80 100644 --- a/debian/taler-exchange.taler-exchange-wirewatch.service +++ b/debian/taler-exchange.taler-exchange-wirewatch.service @@ -6,7 +6,8 @@ PartOf=taler-exchange.target [Service] User=taler-exchange-wire Type=simple -Restart=on-failure +Restart=always +RestartSec=100ms ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf StandardOutput=journal StandardError=journal diff --git a/debian/taler-exchange.taler-exchange-wirewatch@.service b/debian/taler-exchange.taler-exchange-wirewatch@.service new file mode 100644 index 000000000..54704cb80 --- /dev/null +++ b/debian/taler-exchange.taler-exchange-wirewatch@.service @@ -0,0 +1,16 @@ +[Unit] +Description=GNU Taler payment system exchange wirewatch service +After=network.target +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-wire +Type=simple +Restart=always +RestartSec=100ms +ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index 4d353acec..92de5e31c 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -40,6 +40,9 @@ PORT = 8081 # transfers to enable tracking. BASE_URL = http://localhost:8081/ +# Maximum number of requests this process should handle before +# committing suicide. +# MAX_REQUESTS = # How long should the aggregator sleep if it has nothing to do? AGGREGATOR_IDLE_SLEEP_INTERVAL = 60 s diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 64304e9c2..b948c315c 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -76,6 +76,11 @@ static int allow_address_reuse; */ const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; +/** + * Handle to the HTTP server. + */ +static struct MHD_Daemon *mhd; + /** * Our KYC configuration. */ @@ -122,6 +127,17 @@ static unsigned int connection_timeout = 30; */ static int connection_close; +/** + * -I command-line flag given? + */ +int TEH_check_invariants_flag; + +/** + * True if we should commit suicide once all active + * connections are finished. + */ +bool TEH_suicide; + /** * Value to return from main() */ @@ -137,6 +153,11 @@ static uint16_t serve_port; */ static unsigned long long req_count; +/** + * Counter for the number of open connections. + */ +static unsigned long long active_connections; + /** * Limit for the number of requests this HTTP may process before restarting. * (This was added as one way of dealing with unavoidable memory fragmentation @@ -262,6 +283,45 @@ handle_post_coins (struct TEH_RequestContext *rc, } +/** + * Increments our request counter and checks if this + * process should commit suicide. + */ +static void +check_suicide (void) +{ + int fd; + pid_t chld; + unsigned long long cnt; + + cnt = req_count++; + if (req_max != cnt) + return; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Restarting exchange service after %llu requests\n", + cnt); + /* Stop accepting new connections */ + fd = MHD_quiesce_daemon (mhd); + GNUNET_break (0 == close (fd)); + /* Continue handling existing connections in child, + so that this process can die and be replaced by + systemd with a fresh one */ + chld = fork (); + if (-1 == chld) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "fork"); + _exit (1); + } + if (0 != chld) + { + /* We are the parent, instant-suicide! */ + _exit (0); + } + TEH_suicide = true; +} + + /** * Function called whenever MHD is done with a request. If the * request was a POST, we may have stored a `struct Buffer *` in the @@ -290,6 +350,7 @@ handle_mhd_completion_callback (void *cls, return; GNUNET_async_scope_enter (&rc->async_scope_id, &old_scope); + check_suicide (); TEH_check_invariants (); if (NULL != rc->rh_cleaner) rc->rh_cleaner (rc); @@ -1642,8 +1703,19 @@ connection_done (void *cls, (void) cls; (void) connection; (void) socket_context; - unsigned long long cnt; + switch (toe) + { + case MHD_CONNECTION_NOTIFY_STARTED: + active_connections++; + break; + case MHD_CONNECTION_NOTIFY_CLOSED: + active_connections--; + if (TEH_suicide && + (0 == active_connections) ) + GNUNET_SCHEDULER_shutdown (); + break; + } #if HAVE_DEVELOPER /* We only act if the connection is closed. */ if (MHD_CONNECTION_NOTIFY_CLOSED != toe) @@ -1651,15 +1723,6 @@ connection_done (void *cls, if (NULL != input_filename) GNUNET_SCHEDULER_shutdown (); #endif - cnt = req_count++; - if (req_max == cnt) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Restarting exchange service after %llu requests\n", - cnt); - (void) kill (getpid (), - SIGTERM); - } } @@ -1780,46 +1843,42 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - { - struct MHD_Daemon *mhd; - - mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME - | MHD_USE_PIPE_FOR_SHUTDOWN - | MHD_USE_DEBUG | MHD_USE_DUAL_STACK - | MHD_USE_TCP_FASTOPEN, - (-1 == fh) ? serve_port : 0, - NULL, NULL, - &handle_mhd_request, NULL, - MHD_OPTION_LISTEN_BACKLOG_SIZE, - (unsigned int) 1024, - MHD_OPTION_LISTEN_SOCKET, - fh, - MHD_OPTION_EXTERNAL_LOGGER, - &TALER_MHD_handle_logs, - NULL, - MHD_OPTION_NOTIFY_COMPLETED, - &handle_mhd_completion_callback, - NULL, - MHD_OPTION_NOTIFY_CONNECTION, - &connection_done, - NULL, - MHD_OPTION_CONNECTION_TIMEOUT, - connection_timeout, - (0 == allow_address_reuse) + mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME + | MHD_USE_PIPE_FOR_SHUTDOWN + | MHD_USE_DEBUG | MHD_USE_DUAL_STACK + | MHD_USE_TCP_FASTOPEN, + (-1 == fh) ? serve_port : 0, + NULL, NULL, + &handle_mhd_request, NULL, + MHD_OPTION_LISTEN_BACKLOG_SIZE, + (unsigned int) 1024, + MHD_OPTION_LISTEN_SOCKET, + fh, + MHD_OPTION_EXTERNAL_LOGGER, + &TALER_MHD_handle_logs, + NULL, + MHD_OPTION_NOTIFY_COMPLETED, + &handle_mhd_completion_callback, + NULL, + MHD_OPTION_NOTIFY_CONNECTION, + &connection_done, + NULL, + MHD_OPTION_CONNECTION_TIMEOUT, + connection_timeout, + (0 == allow_address_reuse) ? MHD_OPTION_END : MHD_OPTION_LISTENING_ADDRESS_REUSE, - (unsigned int) allow_address_reuse, - MHD_OPTION_END); - if (NULL == mhd) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to launch HTTP service. Is the port in use?\n"); - GNUNET_SCHEDULER_shutdown (); - return; - } - global_ret = EXIT_SUCCESS; - TALER_MHD_daemon_start (mhd); + (unsigned int) allow_address_reuse, + MHD_OPTION_END); + if (NULL == mhd) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to launch HTTP service. Is the port in use?\n"); + GNUNET_SCHEDULER_shutdown (); + return; } + global_ret = EXIT_SUCCESS; + TALER_MHD_daemon_start (mhd); atexit (&write_stats); #if HAVE_DEVELOPER @@ -1849,6 +1908,10 @@ main (int argc, "connection-close", "force HTTP connections to be closed after each request", &connection_close), + GNUNET_GETOPT_option_flag ('I', + "check-invariants", + "enable expensive invariant checks", + &TEH_check_invariants_flag), GNUNET_GETOPT_option_flag ('r', "allow-reuse-address", "allow multiple HTTPDs to listen to the same port", diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index d52ce1a0f..07f6b0231 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -144,6 +144,11 @@ extern const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; */ extern char *TEH_exchange_directory; +/** + * -I command-line flag given? + */ +extern int TEH_check_invariants_flag; + /** * Are clients allowed to request /keys for times other than the * current time? Allowing this could be abused in a DoS-attack @@ -157,6 +162,13 @@ extern int TEH_allow_keys_timetravel; */ extern char *TEH_revocation_directory; +/** + * True if we should commit suicide once all active + * connections are finished. Also forces /keys requests + * to terminate if they are long-polling. + */ +extern bool TEH_suicide; + /** * Master public key (according to the * configuration in the exchange directory). diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 6ac39aa80..86366eaf6 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -36,6 +36,12 @@ #define SKR_LIMIT 32 +/** + * When do we forcefully timeout a /keys request? + */ +#define KEYS_TIMEOUT GNUNET_TIME_UNIT_MINUTES + + /** * Taler protocol version in the format CURRENT:REVISION:AGE * as used by GNU libtool. See @@ -355,6 +361,11 @@ struct SuspendedKeysRequests * The suspended connection. */ struct MHD_Connection *connection; + + /** + * When does this request timeout? + */ + struct GNUNET_TIME_Absolute timeout; }; @@ -398,6 +409,11 @@ static unsigned int skr_size; */ static struct MHD_Connection *skr_connection; +/** + * Task to force timeouts on /keys requests. + */ +static struct GNUNET_SCHEDULER_Task *keys_tt; + /** * For how long should a signing key be legally retained? * Configuration value. @@ -419,6 +435,40 @@ static struct TALER_SecurityModulePublicKeyP esign_sm_pub; */ static bool terminating; + +/** + * Function called to forcefully resume suspended keys requests. + * + * @param cls unused, NULL + */ +static void +keys_timeout_cb (void *cls) +{ + struct SuspendedKeysRequests *skr; + + (void) cls; + keys_tt = NULL; + while (NULL != (skr = skr_head)) + { + if (GNUNET_TIME_absolute_is_future (skr->timeout)) + break; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Resuming /keys request due to timeout\n"); + GNUNET_CONTAINER_DLL_remove (skr_head, + skr_tail, + skr); + MHD_resume_connection (skr->connection); + TALER_MHD_daemon_trigger (); + GNUNET_free (skr); + } + if (NULL == skr) + return; + keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout, + &keys_timeout_cb, + NULL); +} + + /** * Suspend /keys request while we (hopefully) are waiting to be * provisioned with key material. @@ -445,6 +495,13 @@ suspend_request (struct MHD_Connection *connection) GNUNET_CONTAINER_DLL_insert (skr_head, skr_tail, skr); + skr->timeout = GNUNET_TIME_relative_to_absolute (KEYS_TIMEOUT); + if (NULL == keys_tt) + { + keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout, + &keys_timeout_cb, + NULL); + } skr_size++; if (skr_size > SKR_LIMIT) { @@ -477,9 +534,8 @@ check_dk (void *cls, { struct TEH_DenominationKey *dk = value; - + (void) cls; (void) hc; - (void) value; GNUNET_assert (TALER_DENOMINATION_INVALID != dk->denom_pub.cipher); if (TALER_DENOMINATION_RSA == dk->denom_pub.cipher) GNUNET_assert (GNUNET_CRYPTO_rsa_public_key_check ( @@ -493,6 +549,8 @@ TEH_check_invariants () { struct TEH_KeyStateHandle *ksh; + if (0 == TEH_check_invariants_flag) + return; ksh = TEH_keys_get_state (); if (NULL == ksh) return; @@ -1073,6 +1131,11 @@ TEH_keys_init () void TEH_keys_finished () { + if (NULL != keys_tt) + { + GNUNET_SCHEDULER_cancel (keys_tt); + keys_tt = NULL; + } if (NULL != key_state) destroy_key_state (key_state, true); @@ -2282,13 +2345,17 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc, ksh = TEH_keys_get_state (); if (NULL == ksh) { - if ( (SKR_LIMIT == skr_size) && - (rc->connection == skr_connection) ) + if ( ( (SKR_LIMIT == skr_size) && + (rc->connection == skr_connection) ) || + TEH_suicide) { - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - "too many connections suspended on /keys"); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_SERVICE_UNAVAILABLE, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + TEH_suicide + ? "server terminating" + : "too many connections suspended waiting on /keys"); } return suspend_request (rc->connection); } @@ -2688,7 +2755,7 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, if (NULL == ksh) { return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, + MHD_HTTP_SERVICE_UNAVAILABLE, TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, "no key state"); } diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index 4ec703136..4631a2b92 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -271,6 +271,7 @@ refreshes_reveal_transaction (void *cls, /* Obtain basic information about the refresh operation and what gamma we committed to. */ + // FIXME: why do we do 'get_melt' twice? qs = TEH_plugin->get_melt (TEH_plugin->cls, &rctx->rc, &melt); @@ -611,6 +612,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, { enum GNUNET_DB_QueryStatus qs; + // FIXME: why do we do 'get_melt' twice? if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != (qs = TEH_plugin->get_melt (TEH_plugin->cls, &rctx->rc, @@ -682,9 +684,13 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, rctx->dks = dks; rctx->link_sigs = link_sigs; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Optimistically creating %u signatures\n", + (unsigned int) rctx->num_fresh_coins); /* sign _early_ (optimistic!) to keep out of transaction scope! */ rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins, struct TALER_BlindedDenominationSignature); + // FIXME: this is sequential, modify logic to enable parallel signing! for (unsigned int i = 0; inum_fresh_coins; i++) { enum TALER_ErrorCode ec = TALER_EC_NONE; @@ -705,6 +711,8 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, } } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signatures ready, starting DB interaction\n"); /* We try the three transactions a few times, as theoretically the pre-check might be satisfied by a concurrent transaction voiding our final commit due to uniqueness violation; naturally, diff --git a/src/testing/testing_api_cmd_change_auth.c b/src/testing/testing_api_cmd_change_auth.c index 2f0f95b79..588eaa730 100644 --- a/src/testing/testing_api_cmd_change_auth.c +++ b/src/testing/testing_api_cmd_change_auth.c @@ -37,6 +37,11 @@ struct AuthchangeState * What is the new authorization token to send? */ const char *auth_token; + + /** + * Old context, clean up on termination. + */ + struct GNUNET_CURL_Context *old_ctx; }; @@ -55,11 +60,7 @@ authchange_run (void *cls, struct AuthchangeState *ss = cls; (void) cmd; - if (NULL != is->ctx) - { - GNUNET_CURL_fini (is->ctx); - is->ctx = NULL; - } + ss->old_ctx = is->ctx; if (NULL != is->rc) { GNUNET_CURL_gnunet_rc_destroy (is->rc); @@ -101,6 +102,11 @@ authchange_cleanup (void *cls, struct AuthchangeState *ss = cls; (void) cmd; + if (NULL != ss->old_ctx) + { + GNUNET_CURL_fini (ss->old_ctx); + ss->old_ctx = NULL; + } GNUNET_free (ss); } diff --git a/src/util/mhd.c b/src/util/mhd.c index 2f262340f..6f6b38d35 100644 --- a/src/util/mhd.c +++ b/src/util/mhd.c @@ -42,11 +42,11 @@ TALER_mhd_is_https (struct MHD_Connection *connection) if (NULL != forwarded_proto) { - if (0 == strcmp (forwarded_proto, - "https")) + if (0 == strcasecmp (forwarded_proto, + "https")) return GNUNET_YES; - if (0 == strcmp (forwarded_proto, - "http")) + if (0 == strcasecmp (forwarded_proto, + "http")) return GNUNET_NO; GNUNET_break (0); return GNUNET_SYSERR; diff --git a/src/util/secmod_common.c b/src/util/secmod_common.c index 33b880d2c..0a83bfb6c 100644 --- a/src/util/secmod_common.c +++ b/src/util/secmod_common.c @@ -74,17 +74,15 @@ static volatile bool in_shutdown; enum GNUNET_GenericReturnValue -TES_transmit (int sock, - const struct GNUNET_MessageHeader *hdr) +TES_transmit_raw (int sock, + size_t end, + const void *pos) { ssize_t off = 0; - const void *pos = hdr; - uint16_t end = ntohs (hdr->size); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending message of type %u and length %u\n", - (unsigned int) ntohs (hdr->type), - (unsigned int) ntohs (hdr->size)); + "Sending message of length %u\n", + (unsigned int) end); while (off < end) { ssize_t ret = send (sock, @@ -118,6 +116,20 @@ TES_transmit (int sock, } +enum GNUNET_GenericReturnValue +TES_transmit (int sock, + const struct GNUNET_MessageHeader *hdr) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending message of type %u and length %u\n", + (unsigned int) ntohs (hdr->type), + (unsigned int) ntohs (hdr->size)); + return TES_transmit_raw (sock, + ntohs (hdr->size), + hdr); +} + + struct GNUNET_NETWORK_Handle * TES_open_socket (const char *unixpath) { diff --git a/src/util/secmod_common.h b/src/util/secmod_common.h index a212c9d49..b24e91cb2 100644 --- a/src/util/secmod_common.h +++ b/src/util/secmod_common.h @@ -51,6 +51,19 @@ TES_transmit (int sock, const struct GNUNET_MessageHeader *hdr); +/** + * Transmit @a end bytes from @a pos on @a sock. + * + * @param sock where to send the data + * @param end how many bytes to send + * @param pos first address with data + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TES_transmit_raw (int sock, + size_t end, + const void *pos); + /** * Information we keep for a client connected to us. */ diff --git a/src/util/taler-exchange-secmod-rsa.c b/src/util/taler-exchange-secmod-rsa.c index 5fd6049a7..43109b5a4 100644 --- a/src/util/taler-exchange-secmod-rsa.c +++ b/src/util/taler-exchange-secmod-rsa.c @@ -85,6 +85,11 @@ struct DenominationKey */ struct GNUNET_CRYPTO_RsaPublicKey *denom_pub; + /** + * Message to transmit to clients to introduce this public key. + */ + struct TALER_CRYPTO_RsaKeyAvailableNotification *an; + /** * Hash of this denomination's public key. */ @@ -214,7 +219,6 @@ static struct GNUNET_CONTAINER_MultiHashMap *keys; */ static struct GNUNET_SCHEDULER_Task *keygen_task; - /** * Lock for the keys queue. */ @@ -227,15 +231,12 @@ static uint64_t key_gen; /** - * Notify @a client about @a dk becoming available. + * Generate the announcement message for @a dk. * - * @param[in,out] client the client to notify; possible freed if transmission fails - * @param dk the key to notify @a client about - * @return #GNUNET_OK on success + * @param[in,out] denomination key to generate the announcement for */ -static enum GNUNET_GenericReturnValue -notify_client_dk_add (struct TES_Client *client, - const struct DenominationKey *dk) +static void +generate_response (struct DenominationKey *dk) { struct Denomination *denom = dk->denom; size_t nlen = strlen (denom->section) + 1; @@ -273,55 +274,7 @@ notify_client_dk_add (struct TES_Client *client, memcpy (p + buf_len, denom->section, nlen); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA denomination key %s (%s)\n", - GNUNET_h2s (&dk->h_rsa.hash), - denom->section); - if (GNUNET_OK != - TES_transmit (client->csock, - &an->header)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %p must have disconnected\n", - client); - GNUNET_free (an); - return GNUNET_SYSERR; - } - GNUNET_free (an); - return GNUNET_OK; -} - - -/** - * Notify @a client about @a dk being purged. - * - * @param[in,out] client the client to notify; possible freed if transmission fails - * @param dk the key to notify @a client about - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -notify_client_dk_del (struct TES_Client *client, - const struct DenominationKey *dk) -{ - struct TALER_CRYPTO_RsaKeyPurgeNotification pn = { - .header.type = htons (TALER_HELPER_RSA_MT_PURGE), - .header.size = htons (sizeof (pn)), - .h_rsa = dk->h_rsa - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA denomination expiration %s\n", - GNUNET_h2s (&dk->h_rsa.hash)); - if (GNUNET_OK != - TES_transmit (client->csock, - &pn.header)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %p must have disconnected\n", - client); - return GNUNET_SYSERR; - } - return GNUNET_OK; + dk->an = an; } @@ -342,6 +295,7 @@ handle_sign_request (struct TES_Client *client, const void *blinded_msg = &sr[1]; size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr); struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); dk = GNUNET_CONTAINER_multihashmap_get (keys, @@ -407,6 +361,7 @@ handle_sign_request (struct TES_Client *client, return TES_transmit (client->csock, &sf.header); } + { struct TALER_CRYPTO_SignResponse *sr; void *buf; @@ -427,7 +382,10 @@ handle_sign_request (struct TES_Client *client, buf_size); GNUNET_free (buf); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending RSA signature\n"); + "Sending RSA signature after %s\n", + GNUNET_STRINGS_relative_time_to_string ( + GNUNET_TIME_absolute_get_duration (now), + GNUNET_YES)); ret = TES_transmit (client->csock, &sr->header); GNUNET_free (sr); @@ -502,6 +460,7 @@ setup_key (struct DenominationKey *dk, dk->denom_priv = priv; dk->denom_pub = pub; dk->key_gen = key_gen; + generate_response (dk); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( keys, @@ -514,6 +473,7 @@ setup_key (struct DenominationKey *dk, GNUNET_CRYPTO_rsa_private_key_free (dk->denom_priv); GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub); GNUNET_free (dk->filename); + GNUNET_free (dk->an); GNUNET_free (dk); return GNUNET_SYSERR; } @@ -660,6 +620,12 @@ rsa_work_dispatch (struct TES_Client *client, static enum GNUNET_GenericReturnValue rsa_client_init (struct TES_Client *client) { + size_t obs = 0; + char *buf; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Initializing new client %p\n", + client); GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); for (struct Denomination *denom = denom_head; NULL != denom; @@ -669,20 +635,39 @@ rsa_client_init (struct TES_Client *client) NULL != dk; dk = dk->next) { - if (GNUNET_OK != - notify_client_dk_add (client, - dk)) - { - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client %p must have disconnected\n", - client); - return GNUNET_SYSERR; - } + obs += ntohs (dk->an->header.size); + } + } + buf = GNUNET_malloc (obs); + obs = 0; + for (struct Denomination *denom = denom_head; + NULL != denom; + denom = denom->next) + { + for (struct DenominationKey *dk = denom->keys_head; + NULL != dk; + dk = dk->next) + { + memcpy (&buf[obs], + dk->an, + ntohs (dk->an->header.size)); + obs += ntohs (dk->an->header.size); } } client->key_gen = key_gen; GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + if (GNUNET_OK != + TES_transmit_raw (client->csock, + obs, + buf)) + { + GNUNET_free (buf); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client %p must have disconnected\n", + client); + return GNUNET_SYSERR; + } + GNUNET_free (buf); { struct GNUNET_MessageHeader synced = { .type = htons (TALER_HELPER_RSA_SYNCED), @@ -714,7 +699,36 @@ rsa_client_init (struct TES_Client *client) static enum GNUNET_GenericReturnValue rsa_update_client_keys (struct TES_Client *client) { + size_t obs = 0; + char *buf; + enum GNUNET_GenericReturnValue ret; + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + for (struct Denomination *denom = denom_head; + NULL != denom; + denom = denom->next) + { + for (struct DenominationKey *key = denom->keys_head; + NULL != key; + key = key->next) + { + if (key->key_gen <= client->key_gen) + continue; + if (key->purge) + obs += sizeof (struct TALER_CRYPTO_RsaKeyPurgeNotification); + else + obs += ntohs (key->an->header.size); + } + } + if (0 == obs) + { + /* nothing to do */ + client->key_gen = key_gen; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return GNUNET_OK; + } + buf = GNUNET_malloc (obs); + obs = 0; for (struct Denomination *denom = denom_head; NULL != denom; denom = denom->next) @@ -727,29 +741,33 @@ rsa_update_client_keys (struct TES_Client *client) continue; if (key->purge) { - if (GNUNET_OK != - notify_client_dk_del (client, - key)) - { - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - return GNUNET_SYSERR; - } + struct TALER_CRYPTO_RsaKeyPurgeNotification pn = { + .header.type = htons (TALER_HELPER_RSA_MT_PURGE), + .header.size = htons (sizeof (pn)), + .h_rsa = key->h_rsa + }; + + memcpy (&buf[obs], + &pn, + sizeof (pn)); + obs += sizeof (pn); } else { - if (GNUNET_OK != - notify_client_dk_add (client, - key)) - { - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - return GNUNET_SYSERR; - } + memcpy (&buf[obs], + key->an, + ntohs (key->an->header.size)); + obs += ntohs (key->an->header.size); } } } client->key_gen = key_gen; GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - return GNUNET_OK; + ret = TES_transmit_raw (client->csock, + obs, + buf); + GNUNET_free (buf); + return ret; } @@ -896,6 +914,7 @@ update_keys (struct Denomination *denom, GNUNET_free (key->filename); GNUNET_CRYPTO_rsa_private_key_free (key->denom_priv); GNUNET_CRYPTO_rsa_public_key_free (key->denom_pub); + GNUNET_free (key->an); GNUNET_free (key); key = nxt; } @@ -943,6 +962,8 @@ update_denominations (void *cls) keygen_task = NULL; now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Updating denominations ...\n"); GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); do { denom = denom_head; @@ -953,6 +974,8 @@ update_denominations (void *cls) return; } while (denom != denom_head); GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Updating denominations finished ...\n"); if (wake) TES_wake_clients (); keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom), @@ -1041,6 +1064,7 @@ parse_key (struct Denomination *denom, TALER_rsa_pub_hash (pub, &dk->h_rsa); dk->denom_pub = pub; + generate_response (dk); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( keys, @@ -1054,6 +1078,7 @@ parse_key (struct Denomination *denom, filename); GNUNET_CRYPTO_rsa_private_key_free (priv); GNUNET_CRYPTO_rsa_public_key_free (pub); + GNUNET_free (dk->an); GNUNET_free (dk); return; }