diff --git a/ChangeLog b/ChangeLog index a40af772e..af960b86f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Mon Jun 13 16:35:09 CEST 2016 + Splitting HTTP service to run on different listen + sockets for /admin and /test-APIs and the normal public + APIs (#4569). -CG + Thu Jun 9 19:17:02 CEST 2016 Rename /deposit/wtid to /track/transaction and /wire/deposits to /track/transfer. -CG diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index c47b352b8..93df0ae0d 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -52,12 +52,11 @@ static unsigned int nreserves; /** * How many coins are in `coins` array. This is needed - * as the number of coins is not always nreserves * COINS_PER_RESERVE + * as the number of coins is not always nreserves * COINS_PER_RESERVE * due to refresh operations */ unsigned int ncoins; - /** * Bank details of who creates reserves */ @@ -87,6 +86,11 @@ struct RefreshRevealCls { * Which coin in the list are we melting */ unsigned int coin_index; + + /** + * Array of denominations expected to get from melt + */ + struct TALER_Amount *denoms; }; /** @@ -167,6 +171,12 @@ struct Coin { */ unsigned int refresh; + /** + * If the coin has to be refreshed, this value indicates + * how much is left on this coin + */ + struct TALER_Amount left; + /** * Refresh melt handle */ @@ -179,7 +189,6 @@ struct Coin { }; - /** * Handle to the exchange's process */ @@ -215,16 +224,6 @@ static struct Reserve *reserves; */ static struct Coin *coins; -/** - * Indices of spent coins - */ -static unsigned int *spent_coins; - -/** - * Current number of spent coins - */ -static unsigned int spent_coins_size = 0; - /** * Transaction id counter, used in /deposit's */ @@ -412,29 +411,31 @@ reveal_cb (void *cls, return; } else + { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Coin #%d revealed!\n", rrcls->coin_index); + coins[rrcls->coin_index].left.value = 0; + } + keys = TALER_EXCHANGE_get_keys (exchange); for (i=0; idenoms[i]); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "revealing %s " + "# of coins after refresh: %d\n", + revealed_str, + ncoins); + + GNUNET_free (revealed_str); fresh_coin.reserve_index = coins[rrcls->coin_index].reserve_index; - TALER_string_to_amount (refresh_denom, &amount); - GNUNET_free (refresh_denom); - fresh_coin.pk = find_pk (keys, &amount); + fresh_coin.pk = find_pk (keys, &rrcls->denoms[i]); fresh_coin.sig = sigs[i]; GNUNET_array_append (coins, ncoins, fresh_coin); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "# of coins after refresh: %d\n", - ncoins); } GNUNET_free (rrcls); } @@ -457,8 +458,8 @@ melt_cb (void *cls, const struct TALER_ExchangePublicKeyP *exchange_pub, const json_t *full_response) { + /* free'd in `reveal_cb` */ struct RefreshRevealCls *rrcls = cls; - /* FIXME to be freed */ coins[rrcls->coin_index].rmh = NULL; if (MHD_HTTP_OK != http_status) @@ -467,12 +468,13 @@ melt_cb (void *cls, fail ("Coin not correctly melted!\n"); return; } + coins[rrcls->coin_index].rrh = TALER_EXCHANGE_refresh_reveal (exchange, rrcls->blob_size, rrcls->blob, noreveal_index, - reveal_cb, + &reveal_cb, rrcls); } @@ -519,30 +521,52 @@ deposit_cb (void *cls, fail ("At least one coin has not been deposited, status: %d\n"); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Coin #%d correctly spent!\n", coin_index); - GNUNET_array_append (spent_coins, spent_coins_size, coin_index); - spent_coins_size++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Coin #%d correctly spent!\n", + coin_index); if (GNUNET_YES == coins[coin_index].refresh) { - struct TALER_Amount melt_amount; struct RefreshRevealCls *rrcls; - - TALER_amount_get_zero (currency, &melt_amount); - melt_amount.value = 7; char *blob; size_t blob_size; + const struct TALER_EXCHANGE_Keys *keys; + struct TALER_Amount *denoms = NULL; + struct TALER_EXCHANGE_DenomPublicKey *dpks = NULL; + const struct TALER_EXCHANGE_DenomPublicKey *curr_dpk; + struct TALER_Amount curr; + unsigned int ndenoms = 0; + unsigned int ndenoms2 = 0; + unsigned long long acc_value; + TALER_amount_get_zero (currency, &curr); + curr.value = COIN_VALUE >> 1; + acc_value = 0; + + keys = TALER_EXCHANGE_get_keys (exchange); + for (; curr.value > 0; curr.value = curr.value >> 1) + { + if (acc_value + curr.value <= coins[coin_index].left.value) + { + GNUNET_array_append (denoms, ndenoms, curr); + GNUNET_assert (NULL != (curr_dpk = find_pk (keys, &curr))); + GNUNET_array_append (dpks, ndenoms2, *curr_dpk); + acc_value += curr.value; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "# of coins to get in melt: %d\n", + ndenoms2); blob = TALER_EXCHANGE_refresh_prepare (&coins[coin_index].coin_priv, - &melt_amount, + &coins[coin_index].left, &coins[coin_index].sig, coins[coin_index].pk, GNUNET_YES, - refresh_pk_len, - refresh_pk, + ndenoms2, + dpks, &blob_size); if (NULL == blob) { - fail ("Failed to prepare refresh"); + fail ("Failed to prepare refresh\n"); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -550,14 +574,11 @@ deposit_cb (void *cls, (unsigned int) blob_size); refreshed_once = GNUNET_YES; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "# of coins to get in melt: %d\n", - refresh_pk_len); rrcls = GNUNET_new (struct RefreshRevealCls); rrcls->blob = blob; rrcls->blob_size = blob_size; rrcls->coin_index = coin_index; - + rrcls->denoms = denoms; coins[coin_index].rmh = TALER_EXCHANGE_refresh_melt (exchange, blob_size, blob, @@ -589,7 +610,8 @@ deposit_cb (void *cls, /** * Function called upon completion of our /reserve/withdraw request. - * This is merely the function which spends withdrawn coins + * This is merely the function which spends withdrawn coins. For each + * spent coin, ti either refresh it or re-withdraw it. * * @param cls closure with the interpreter state * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request @@ -613,7 +635,7 @@ reserve_withdraw_cb (void *cls, fail ("At least one coin has not correctly been withdrawn\n"); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%d-th coin withdrawn\n", coin_index); coins[coin_index].sig.rsa_signature = @@ -646,18 +668,20 @@ reserve_withdraw_cb (void *cls, if (GNUNET_YES == eval_probability (REFRESH_PROBABILITY) && GNUNET_NO == refreshed_once) { + /** + * Always spending 1 out of 8 KUDOS. To be improved by randomly + * picking the spent amount + */ struct TALER_Amount one; TALER_amount_get_zero (currency, &one); one.value = 1; - /** - * If the coin is going to be refreshed, only 1 unit - * of currency will be spent, since 4 units are going - * to be refreshed - */ TALER_amount_subtract (&amount, &one, &coins[coin_index].pk->fee_deposit); + TALER_amount_subtract (&coins[coin_index].left, + &coins[coin_index].pk->value, + &one); coins[coin_index].refresh = GNUNET_YES; refreshed_once = GNUNET_YES; } @@ -826,6 +850,7 @@ benchmark_run (void *cls) &reserve_pub.eddsa_pub); reserves[i].aih = TALER_EXCHANGE_admin_add_incoming (exchange, + "http://localhost:18080/", &reserve_pub, &reserve_amount, execution_date, @@ -837,6 +862,9 @@ benchmark_run (void *cls) json_decref (transfer_details); } json_decref (sender_details); + sender_details = NULL; + transfer_details = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "benchmark_run() returns\n"); } @@ -847,7 +875,7 @@ benchmark_run (void *cls) * since that is the only amount refreshed so far by the benchmark * * @param NULL-terminated array of value.fraction pairs - * @return GNUNET_OK if the array is correctly built, GNUNET_SYSERR + * @return #GNUNET_OK if the array is correctly built, #GNUNET_SYSERR * otherwise */ static unsigned int @@ -994,7 +1022,6 @@ do_shutdown (void *cls) GNUNET_free_non_null (reserves); GNUNET_free_non_null (coins); - GNUNET_free_non_null (spent_coins); GNUNET_free_non_null (currency); if (NULL != exchange) @@ -1085,10 +1112,6 @@ run (void *cls) NULL); GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_array_append (spent_coins, - spent_coins_size, - 1); - spent_coins_size++; reserves = NULL; coins = NULL; ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, diff --git a/src/exchange-lib/exchange_api_admin.c b/src/exchange-lib/exchange_api_admin.c index 0452a9546..0d3129186 100644 --- a/src/exchange-lib/exchange_api_admin.c +++ b/src/exchange-lib/exchange_api_admin.c @@ -141,6 +141,7 @@ handle_admin_add_incoming_finished (void *cls, * to the operators of the exchange. * * @param exchange the exchange handle; the exchange must be ready to operate + * @param admin_url URL of the administrative interface of the exchange * @param reserve_pub public key of the reserve * @param amount amount that was deposited * @param execution_date when did we receive the amount @@ -156,6 +157,7 @@ handle_admin_add_incoming_finished (void *cls, */ struct TALER_EXCHANGE_AdminAddIncomingHandle * TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange, + const char *admin_url, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *amount, struct GNUNET_TIME_Absolute execution_date, @@ -188,7 +190,8 @@ TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange, aai->exchange = exchange; aai->cb = res_cb; aai->cb_cls = res_cb_cls; - aai->url = MAH_path_to_url (exchange, "/admin/add/incoming"); + aai->url = MAH_path_to_url2 (admin_url, + "/admin/add/incoming"); eh = curl_easy_init (); GNUNET_assert (NULL != (aai->json_enc = diff --git a/src/exchange-lib/exchange_api_handle.c b/src/exchange-lib/exchange_api_handle.c index b9104869e..318fd9e6b 100644 --- a/src/exchange-lib/exchange_api_handle.c +++ b/src/exchange-lib/exchange_api_handle.c @@ -779,23 +779,39 @@ MAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h) /** * Obtain the URL to use for an API request. * - * @param h the exchange handle to query + * @param base_url base URL of the exchange (i.e. "http://exchange/") * @param path Taler API path (i.e. "/reserve/withdraw") * @return the full URI to use with cURL */ char * -MAH_path_to_url (struct TALER_EXCHANGE_Handle *h, +MAH_path_to_url (struct TALER_EXCHANGE_Handle *exchange, const char *path) +{ + return MAH_path_to_url2 (exchange->url, + path); +} + + +/** + * Obtain the URL to use for an API request. + * + * @param base_url base URL of the exchange (i.e. "http://exchange/") + * @param path Taler API path (i.e. "/reserve/withdraw") + * @return the full URI to use with cURL + */ +char * +MAH_path_to_url2 (const char *base_url, + const char *path) { char *url; if ( ('/' == path[0]) && - (0 < strlen (h->url)) && - ('/' == h->url[strlen (h->url) - 1]) ) + (0 < strlen (base_url)) && + ('/' == base_url[strlen (base_url) - 1]) ) path++; /* avoid generating URL with "//" from concat */ GNUNET_asprintf (&url, "%s%s", - h->url, + base_url, path); return url; } diff --git a/src/exchange-lib/exchange_api_handle.h b/src/exchange-lib/exchange_api_handle.h index 2cf2166d0..6a5661508 100644 --- a/src/exchange-lib/exchange_api_handle.h +++ b/src/exchange-lib/exchange_api_handle.h @@ -55,5 +55,16 @@ char * MAH_path_to_url (struct TALER_EXCHANGE_Handle *h, const char *path); +/** + * Obtain the URL to use for an API request. + * + * @param base_url base URL of the exchange (i.e. "http://exchange/") + * @param path Taler API path (i.e. "/reserve/withdraw") + * @return the full URI to use with cURL + */ +char * +MAH_path_to_url2 (const char *base_url, + const char *path); + /* end of exchange_api_handle.h */ diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 337fd297b..d0e2632e0 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -1753,6 +1753,7 @@ interpreter_run (void *cls) GNUNET_TIME_round_abs (&execution_date); cmd->details.admin_add_incoming.aih = TALER_EXCHANGE_admin_add_incoming (exchange, + "http://localhost:18080/", &reserve_pub, &amount, execution_date, diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index afb5b8fae..333e9a58a 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -11,17 +11,6 @@ KEYDIR = ${TALER_DATA_HOME}/exchange/live-keys/ # the actual coin operations. # WIREFORMAT = test -# serve via tcp socket (on PORT) -SERVE = tcp - -# Unix domain socket to listen on, -# only effective with "SERVE = unix" -UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http -UNIXPATH_MODE = 660 - -# HTTP port the exchange listens to -PORT = 8081 - # Master public key used to sign the exchange's various keys # MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG @@ -34,3 +23,30 @@ DB = postgres # Where do we store the offline master private key of the exchange? MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv + +# Network configuration for the normal API/service HTTP server +# serve via tcp socket (on PORT) +SERVE = tcp + +# Unix domain socket to listen on, +# only effective with "SERVE = unix" +UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http +UNIXPATH_MODE = 660 + +# HTTP port the exchange listens to +PORT = 8081 + + +[exchange-admin] +# Network configuration for the /admin HTTP server + +# serve via tcp socket (on PORT) +SERVE = tcp + +# Unix domain socket to listen on, +# only effective with "SERVE = unix" +UNIXPATH = ${TALER_RUNTIME_DIR}/exchange-admin.http +UNIXPATH_MODE = 660 + +# HTTP port the exchange listens to +PORT = 18080 diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index ff18fb862..4b269a392 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -88,24 +88,50 @@ static unsigned int connection_timeout = 30; /** * The HTTP Daemon. */ -static struct MHD_Daemon *mydaemon; +static struct MHD_Daemon *mhd; + +/** + * The HTTP Daemon for /admin-requests. + */ +static struct MHD_Daemon *mhd_admin; + +/** + * Do not offer /admin API. + */ +static int no_admin; /** * Port to run the daemon on. */ static uint16_t serve_port; +/** + * Port to run the admin daemon on. + */ +static uint16_t serve_admin_port; + /** * Path for the unix domain-socket * to run the daemon on. */ static char *serve_unixpath; +/** + * Path for the unix domain-socket + * to run the admin daemon on. + */ +static char *serve_admin_unixpath; + /** * File mode for unix-domain socket. */ static mode_t unixpath_mode; +/** + * File mode for unix-domain socket. + */ +static mode_t unixpath_admin_mode; + /** * Function called whenever MHD is done with a request. If the @@ -251,14 +277,6 @@ handle_mhd_request (void *cls, "Only GET is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - /* FIXME: maybe conditionally compile these? */ - { "/admin/add/incoming", MHD_HTTP_METHOD_POST, "application/json", - NULL, 0, - &TMH_ADMIN_handler_admin_add_incoming, MHD_HTTP_OK }, - { "/admin/add/incoming", NULL, "text/plain", - "Only POST is allowed", 0, - &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - { "/track/transfer", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, &TMH_TRACKING_handler_track_transfer, MHD_HTTP_OK }, @@ -272,6 +290,74 @@ handle_mhd_request (void *cls, "Only POST is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + { NULL, NULL, NULL, NULL, 0, 0 } + }; + static struct TMH_RequestHandler h404 = + { + "", NULL, "text/html", + "404: not found", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND + }; + struct TMH_RequestHandler *rh; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handling request for URL '%s'\n", + url); + for (i=0;NULL != handlers[i].url;i++) + { + rh = &handlers[i]; + if ( (0 == strcasecmp (url, + rh->url)) && + ( (NULL == rh->method) || + (0 == strcasecmp (method, + rh->method)) ) ) + return rh->handler (rh, + connection, + con_cls, + upload_data, + upload_data_size); + } + return TMH_MHD_handler_static_response (&h404, + connection, + con_cls, + upload_data, + upload_data_size); +} + + +/** + * Handle incoming administrative HTTP request. + * + * @param cls closure for MHD daemon (unused) + * @param connection the connection + * @param url the requested url + * @param method the method (POST, GET, ...) + * @param version HTTP version (ignored) + * @param upload_data request data + * @param upload_data_size size of @a upload_data in bytes + * @param con_cls closure for request (a `struct Buffer *`) + * @return MHD result code + */ +static int +handle_mhd_admin_request (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **con_cls) +{ + static struct TMH_RequestHandler handlers[] = + { + { "/admin/add/incoming", MHD_HTTP_METHOD_POST, "application/json", + NULL, 0, + &TMH_ADMIN_handler_admin_add_incoming, MHD_HTTP_OK }, + { "/admin/add/incoming", NULL, "text/plain", + "Only POST is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + #if HAVE_DEVELOPER /* Client crypto-interoperability test functions */ { "/test", MHD_HTTP_METHOD_POST, "application/json", @@ -374,6 +460,123 @@ handle_mhd_request (void *cls, } +/** + * Parse the configuration to determine on which port + * or UNIX domain path we should run an HTTP service. + * + * @param type section of the configuration to parse ("exchange" or "exchange-admin") + * @param[out] port set to the port number, or 0 for none + * @param[out] unix_path set to the UNIX path, or NULL for none + * @param[out] unix_mode set to the mode to be used for @a unix_path + * @return #GNUNET_OK on success + */ +static int +parse_port_config (const char *section, + uint16_t *rport, + char **unix_path, + mode_t *unix_mode) +{ + const char *choices[] = {"tcp", "unix"}; + const char *serve_type; + unsigned long long port; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_choice (cfg, + section, + "serve", + choices, + &serve_type)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "serve", + "serve type required"); + return GNUNET_SYSERR; + } + + if (0 == strcmp (serve_type, "tcp")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + section, + "port", + &port)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "port", + "port number required"); + return GNUNET_SYSERR; + } + + if ( (0 == port) || + (port > UINT16_MAX) ) + { + fprintf (stderr, + "Invalid configuration (value out of range): %llu is not a valid port\n", + port); + return GNUNET_SYSERR; + } + *rport = (uint16_t) port; + *unix_path = NULL; + return GNUNET_OK; + } + if (0 == strcmp (serve_type, "unix")) + { + struct sockaddr_un s_un; + char *modestring; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + "unixpath", + unix_path)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "unixpath", + "unixpath required"); + return GNUNET_SYSERR; + } + if (strlen (*unix_path) >= sizeof (s_un.sun_path)) + { + fprintf (stderr, + "Invalid configuration: unix path too long\n"); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "unixpath_mode", + &modestring)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "unixpath_mode", + "unixpath_mode required"); + return GNUNET_SYSERR; + } + errno = 0; + *unix_mode = (mode_t) strtoul (modestring, NULL, 8); + if (0 != errno) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "unixpath_mode", + "unixpath_mode required"); + GNUNET_free (modestring); + return GNUNET_SYSERR; + } + GNUNET_free (modestring); + return GNUNET_OK; + } + /* not reached */ + GNUNET_assert (0); + return GNUNET_SYSERR; +} + + /** * Load configuration parameters for the exchange * server into the corresponding global variables. @@ -384,7 +587,6 @@ handle_mhd_request (void *cls, static int exchange_serve_process_config () { - unsigned long long port; char *TMH_master_public_key_str; if (GNUNET_OK != @@ -464,115 +666,24 @@ exchange_serve_process_config () TMH_plugin->create_tables (TMH_plugin->cls); } + if (GNUNET_OK != + parse_port_config ("exchange", + &serve_port, + &serve_unixpath, + &unixpath_mode)) { - const char *choices[] = {"tcp", "unix"}; - const char *serve_type; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_choice (cfg, - "exchange", - "serve", - choices, - &serve_type)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "serve", - "serve type required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if (0 == strcmp (serve_type, "tcp")) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "exchange", - "port", - &port)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "port", - "port number required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if ( (0 == port) || - (port > UINT16_MAX) ) - { - fprintf (stderr, - "Invalid configuration (value out of range): %llu is not a valid port\n", - port); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - serve_port = (uint16_t) port; - } - else if (0 == strcmp (serve_type, "unix")) - { - struct sockaddr_un s_un; - char *modestring; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - "exchange", - "unixpath", - &serve_unixpath)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "unixpath", - "unixpath required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if (strlen (serve_unixpath) >= sizeof (s_un.sun_path)) - { - fprintf (stderr, - "Invalid configuration: unix path too long\n"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "unixpath_mode", - &modestring)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "unixpath_mode", - "unixpath_mode required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - errno = 0; - unixpath_mode = (mode_t) strtoul (modestring, NULL, 8); - if (0 != errno) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "unixpath_mode", - "unixpath_mode required"); - TMH_VALIDATION_done (); - GNUNET_free (modestring); - return GNUNET_SYSERR; - } - GNUNET_free (modestring); - - } - else - { - // not reached - GNUNET_assert (0); - } + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + parse_port_config ("exchange-admin", + &serve_admin_port, + &serve_admin_unixpath, + &unixpath_admin_mode)) + { + TMH_VALIDATION_done (); + return GNUNET_SYSERR; } - - return GNUNET_OK; } @@ -712,6 +823,99 @@ handle_mhd_logs (void *cls, } +/** + * Open UNIX domain socket for listining at @a unix_path with + * permissions @a unix_mode. + * + * @param unix_path where to listen + * @param unix_mode access permissions to set + * @return -1 on error, otherwise the listen socket + */ +static int +open_unix_path (const char *unix_path, + mode_t unix_mode) +{ + struct GNUNET_NETWORK_Handle *nh; + struct sockaddr_un *un; + int fd; + + if (sizeof (un->sun_path) <= strlen (unix_path)) + { + fprintf (stderr, + "unixpath `%s' too long\n", + unix_path); + return -1; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Creating listen socket '%s' with mode %o\n", + unix_path, + unix_mode); + + if (GNUNET_OK != + GNUNET_DISK_directory_create_for_file (unix_path)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "mkdir", + unix_path); + } + + un = GNUNET_new (struct sockaddr_un); + un->sun_family = AF_UNIX; + strncpy (un->sun_path, + unix_path, + sizeof (un->sun_path) - 1); + GNUNET_NETWORK_unix_precheck (un); + + if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, + SOCK_STREAM, + 0))) + { + fprintf (stderr, + "create failed for AF_UNIX\n"); + GNUNET_free (un); + return -1; + } + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (nh, + (void *) un, + sizeof (struct sockaddr_un))) + { + fprintf (stderr, + "bind failed for AF_UNIX\n"); + GNUNET_free (un); + GNUNET_NETWORK_socket_close (nh); + return -1; + } + GNUNET_free (un); + if (GNUNET_OK != + GNUNET_NETWORK_socket_listen (nh, + UNIX_BACKLOG)) + { + fprintf (stderr, + "listen failed for AF_UNIX\n"); + GNUNET_NETWORK_socket_close (nh); + return -1; + } + + if (0 != chmod (serve_unixpath, + unix_mode)) + { + fprintf (stderr, + "chmod failed: %s\n", + strerror (errno)); + GNUNET_NETWORK_socket_close (nh); + return -1; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "set socket '%s' to mode %o\n", + unix_path, + unix_mode); + fd = GNUNET_NETWORK_get_fd (nh); + GNUNET_NETWORK_socket_free_memory_only_ (nh); + return fd; +} + + /** * The main function of the taler-exchange-httpd server ("the exchange"). * @@ -729,6 +933,9 @@ main (int argc, "force HTTP connections to be closed after each request", 0, &GNUNET_GETOPT_set_one, &TMH_exchange_connection_close}, GNUNET_GETOPT_OPTION_CFG_FILE (&cfgfile), + {'D', "disable-admin", NULL, + "do not run the /admin-HTTP server", 0, + &GNUNET_GETOPT_set_one, &no_admin}, {'t', "timeout", "SECONDS", "after how long do connections timeout by default (in seconds)", 1, &GNUNET_GETOPT_set_uint, &connection_timeout}, @@ -745,6 +952,7 @@ main (int argc, const char *listen_pid; const char *listen_fds; int fh = -1; + int fh_admin = -1; GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-exchange-httpd", @@ -778,7 +986,8 @@ main (int argc, if ( (NULL != listen_pid) && (NULL != listen_fds) && (getpid() == strtol (listen_pid, NULL, 10)) && - (1 == strtoul (listen_fds, NULL, 10)) /* we support only 1 socket */) + ( (1 == strtoul (listen_fds, NULL, 10)) || + (2 == strtoul (listen_fds, NULL, 10)) ) ) { int flags; @@ -794,97 +1003,90 @@ main (int argc, if (0 != fcntl (fh, F_SETFD, flags)) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl"); + + if (2 == strtoul (listen_fds, NULL, 10)) + { + fh_admin = 4; + flags = fcntl (fh_admin, F_GETFD); + if ( (-1 == flags) && (EBADF == errno) ) + { + fprintf (stderr, + "Bad listen socket passed, ignored\n"); + fh_admin = -1; + } + flags |= FD_CLOEXEC; + if (0 != fcntl (fh_admin, F_SETFD, flags)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "fcntl"); + } } /* consider unix path */ if ( (-1 == fh) && (NULL != serve_unixpath) ) { - struct GNUNET_NETWORK_Handle *nh; - struct sockaddr_un *un; - - if (sizeof (un->sun_path) <= strlen (serve_unixpath)) + fh = open_unix_path (serve_unixpath, + unixpath_mode); + if (-1 == fh) + return 1; + } + if ( (-1 == fh_admin) && + (0 == no_admin) && + (NULL != serve_admin_unixpath) ) + { + fh_admin = open_unix_path (serve_admin_unixpath, + unixpath_admin_mode); + if (-1 == fh_admin) { - fprintf (stderr, "unixpath too long\n"); + GNUNET_break (0 == close (fh)); return 1; } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Creating listen socket '%s' with mode %o\n", - serve_unixpath, - unixpath_mode); - - if (GNUNET_OK != - GNUNET_DISK_directory_create_for_file (serve_unixpath)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "mkdir", - serve_unixpath); - } - - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - strncpy (un->sun_path, - serve_unixpath, - sizeof (un->sun_path) - 1); - - GNUNET_NETWORK_unix_precheck (un); - - if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0))) - { - fprintf (stderr, - "create failed for AF_UNIX\n"); - return 1; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh, (void *) un, sizeof (struct sockaddr_un))) - { - fprintf (stderr, - "bind failed for AF_UNIX\n"); - return 1; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh, UNIX_BACKLOG)) - { - fprintf (stderr, - "listen failed for AF_UNIX\n"); - return 1; - } - - fh = GNUNET_NETWORK_get_fd (nh); - - if (0 != chmod (serve_unixpath, unixpath_mode)) - { - fprintf (stderr, - "chmod failed: %s\n", - strerror (errno)); - return 1; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "set socket '%s' to mode %o\n", - serve_unixpath, - unixpath_mode); - GNUNET_NETWORK_socket_free_memory_only_ (nh); } - - mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG, - (-1 == fh) ? serve_port : 0, - NULL, NULL, - &handle_mhd_request, NULL, - MHD_OPTION_LISTEN_SOCKET, fh, - MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, + mhd + = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG, + (-1 == fh) ? serve_port : 0, + NULL, NULL, + &handle_mhd_request, NULL, + MHD_OPTION_LISTEN_SOCKET, fh, + MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, #if HAVE_DEVELOPER - MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, + MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, #endif - MHD_OPTION_END); - - if (NULL == mydaemon) + MHD_OPTION_END); + if (NULL == mhd) { fprintf (stderr, "Failed to start HTTP server.\n"); return 1; } + + if (0 == no_admin) + { + mhd_admin + = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG, + (-1 == fh) ? serve_admin_port : 0, + NULL, NULL, + &handle_mhd_admin_request, NULL, + MHD_OPTION_LISTEN_SOCKET, fh_admin, + MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, +#if HAVE_DEVELOPER + MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, +#endif + MHD_OPTION_END); + if (NULL == mhd_admin) + { + fprintf (stderr, + "Failed to start administrative HTTP server.\n"); + MHD_stop_daemon (mhd); + return 1; + } + } + #if HAVE_DEVELOPER if (NULL != input_filename) { @@ -905,15 +1107,20 @@ main (int argc, { case GNUNET_OK: case GNUNET_SYSERR: - MHD_stop_daemon (mydaemon); + MHD_stop_daemon (mhd); + if (NULL != mhd_admin) + MHD_stop_daemon (mhd_admin); break; case GNUNET_NO: { - MHD_socket sock = MHD_quiesce_daemon (mydaemon); + MHD_socket sock = MHD_quiesce_daemon (mhd); + MHD_socket admin_sock; pid_t chld; int flags; /* Set flags to make 'sock' inherited by child */ + if (NULL != mhd_admin) + admin_sock = MHD_quiesce_daemon (mhd_admin); flags = fcntl (sock, F_GETFD); GNUNET_assert (-1 != flags); flags &= ~FD_CLOEXEC; @@ -937,13 +1144,20 @@ main (int argc, "dup2"); _exit (1); } + if ( (NULL != mhd_admin) && + (4 != dup2 (admin_sock, 4)) ) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "dup2"); + _exit (1); + } /* Tell the child that it is the desired recipient for FD #3 */ GNUNET_snprintf (pids, sizeof (pids), "%u", getpid ()); setenv ("LISTEN_PID", pids, 1); - setenv ("LISTEN_FDS", "1", 1); + setenv ("LISTEN_FDS", (NULL != mhd_admin) ? "2" : "1", 1); /* Finally, exec the (presumably) more recent exchange binary */ execvp ("taler-exchange-httpd", argv); @@ -955,16 +1169,25 @@ main (int argc, before exiting; as the listen socket is no longer used, close it here */ GNUNET_break (0 == close (sock)); - while (0 != MHD_get_daemon_info (mydaemon, - MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) + if (NULL != mhd_admin) + GNUNET_break (0 == close (admin_sock)); + while ( (0 != MHD_get_daemon_info (mhd, + MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) || + ( (NULL != mhd_admin) && + (0 != MHD_get_daemon_info (mhd_admin, + MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ) ) sleep (1); /* Now we're really done, practice clean shutdown */ - MHD_stop_daemon (mydaemon); + MHD_stop_daemon (mhd); + if (NULL != mhd_admin) + MHD_stop_daemon (mhd_admin); } break; default: GNUNET_break (0); - MHD_stop_daemon (mydaemon); + MHD_stop_daemon (mhd); + if (NULL != mhd_admin) + MHD_stop_daemon (mhd_admin); break; } TALER_EXCHANGEDB_plugin_unload (TMH_plugin); diff --git a/src/include/Makefile.am b/src/include/Makefile.am index d01852666..dfecf4694 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -15,6 +15,7 @@ talerinclude_HEADERS = \ taler_amount_lib.h \ taler_bank_service.h \ taler_crypto_lib.h \ + taler_error_codes.h \ taler_exchange_service.h \ taler_exchangedb_lib.h \ taler_exchangedb_plugin.h \ diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h new file mode 100644 index 000000000..d58fc2e16 --- /dev/null +++ b/src/include/taler_error_codes.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + Copyright (C) 2016 Inria + + TALER 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. + + TALER 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 + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler_error_codes.h + * @brief error codes returned by GNU Taler + * + * This file should defines constants for error codes returned + * in Taler APIs. We use codes above 1000 to avoid any + * confusing with HTTP status codes. All constants have the + * shared prefix "TALER_EC_" to indicate that they are error + * codes. + */ +#ifndef TALER_ERROR_CODES_H +#define TALER_ERROR_CODES_H + +/** + * Enumeration with all possible Taler error codes. + */ +enum TALER_ErrorCode +{ + + /** + * Special code to indicate no error. + */ + TALER_EC_NONE = 0 + + + +}; + + +#endif diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index ecfbc7253..4e8b0b375 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -1059,6 +1059,7 @@ typedef void * to the operators of the exchange. * * @param exchange the exchange handle; the exchange must be ready to operate + * @param admin_url URL of the administrative interface of the exchange * @param reserve_pub public key of the reserve * @param amount amount that was deposited * @param execution_date when did we receive the amount @@ -1074,6 +1075,7 @@ typedef void */ struct TALER_EXCHANGE_AdminAddIncomingHandle * TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange, + const char *admin_url, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *amount, struct GNUNET_TIME_Absolute execution_date, diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index a58d21738..3fbfc0ab3 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -24,7 +24,6 @@ * to know to implement Taler clients (wallets or merchants or * auditor) that need to produce or verify Taler signatures. */ - #ifndef TALER_SIGNATURES_H #define TALER_SIGNATURES_H