LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 239 430 55.6 %
Date: 2021-08-30 06:43:37 Functions: 13 17 76.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd.c
      18             :  * @brief Serve the HTTP interface of the exchange
      19             :  * @author Florian Dold
      20             :  * @author Benedikt Mueller
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <jansson.h>
      26             : #include <microhttpd.h>
      27             : #include <sched.h>
      28             : #include <pthread.h>
      29             : #include <sys/resource.h>
      30             : #include <limits.h>
      31             : #include "taler_mhd_lib.h"
      32             : #include "taler-exchange-httpd_auditors.h"
      33             : #include "taler-exchange-httpd_deposit.h"
      34             : #include "taler-exchange-httpd_deposits_get.h"
      35             : #include "taler-exchange-httpd_keys.h"
      36             : #include "taler-exchange-httpd_link.h"
      37             : #include "taler-exchange-httpd_management.h"
      38             : #include "taler-exchange-httpd_melt.h"
      39             : #include "taler-exchange-httpd_mhd.h"
      40             : #include "taler-exchange-httpd_recoup.h"
      41             : #include "taler-exchange-httpd_refreshes_reveal.h"
      42             : #include "taler-exchange-httpd_refund.h"
      43             : #include "taler-exchange-httpd_reserves_get.h"
      44             : #include "taler-exchange-httpd_terms.h"
      45             : #include "taler-exchange-httpd_transfers_get.h"
      46             : #include "taler-exchange-httpd_wire.h"
      47             : #include "taler-exchange-httpd_withdraw.h"
      48             : #include "taler_exchangedb_lib.h"
      49             : #include "taler_exchangedb_plugin.h"
      50             : #include <gnunet/gnunet_mhd_compat.h>
      51             : 
      52             : /**
      53             :  * Backlog for listen operation on unix domain sockets.
      54             :  */
      55             : #define UNIX_BACKLOG 50
      56             : 
      57             : 
      58             : /**
      59             :  * Are clients allowed to request /keys for times other than the
      60             :  * current time? Allowing this could be abused in a DoS-attack
      61             :  * as building new /keys responses is expensive. Should only be
      62             :  * enabled for testcases, development and test systems.
      63             :  */
      64             : int TEH_allow_keys_timetravel;
      65             : 
      66             : /**
      67             :  * The exchange's configuration (global)
      68             :  */
      69             : const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
      70             : 
      71             : /**
      72             :  * How long is caching /keys allowed at most? (global)
      73             :  */
      74             : struct GNUNET_TIME_Relative TEH_max_keys_caching;
      75             : 
      76             : /**
      77             :  * How long is the delay before we close reserves?
      78             :  */
      79             : struct GNUNET_TIME_Relative TEH_reserve_closing_delay;
      80             : 
      81             : /**
      82             :  * Master public key (according to the
      83             :  * configuration in the exchange directory).  (global)
      84             :  */
      85             : struct TALER_MasterPublicKeyP TEH_master_public_key;
      86             : 
      87             : /**
      88             :  * Our DB plugin.  (global)
      89             :  */
      90             : struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
      91             : 
      92             : /**
      93             :  * Our currency.
      94             :  */
      95             : char *TEH_currency;
      96             : 
      97             : /**
      98             :  * Default timeout in seconds for HTTP requests.
      99             :  */
     100             : static unsigned int connection_timeout = 30;
     101             : 
     102             : /**
     103             :  * -C command-line flag given?
     104             :  */
     105             : static int connection_close;
     106             : 
     107             : /**
     108             :  * Value to return from main()
     109             :  */
     110             : static int global_ret;
     111             : 
     112             : /**
     113             :  * Port to run the daemon on.
     114             :  */
     115             : static uint16_t serve_port;
     116             : 
     117             : /**
     118             :  * Counter for the number of requests this HTTP has processed so far.
     119             :  */
     120             : static unsigned long long req_count;
     121             : 
     122             : /**
     123             :  * Limit for the number of requests this HTTP may process before restarting.
     124             :  * (This was added as one way of dealing with unavoidable memory fragmentation
     125             :  * happening slowly over time.)
     126             :  */
     127             : static unsigned long long req_max;
     128             : 
     129             : 
     130             : /**
     131             :  * Signature of functions that handle operations on coins.
     132             :  *
     133             :  * @param connection the MHD connection to handle
     134             :  * @param coin_pub the public key of the coin
     135             :  * @param root uploaded JSON data
     136             :  * @return MHD result code
     137             :  */
     138             : typedef MHD_RESULT
     139             : (*CoinOpHandler)(struct MHD_Connection *connection,
     140             :                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
     141             :                  const json_t *root);
     142             : 
     143             : 
     144             : /**
     145             :  * Generate a 404 "not found" reply on @a connection with
     146             :  * the hint @a details.
     147             :  *
     148             :  * @param connection where to send the reply on
     149             :  * @param details details for the error message, can be NULL
     150             :  */
     151             : static MHD_RESULT
     152           0 : r404 (struct MHD_Connection *connection,
     153             :       const char *details)
     154             : {
     155           0 :   return TALER_MHD_reply_with_error (connection,
     156             :                                      MHD_HTTP_NOT_FOUND,
     157             :                                      TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
     158             :                                      details);
     159             : }
     160             : 
     161             : 
     162             : /**
     163             :  * Handle a "/coins/$COIN_PUB/$OP" POST request.  Parses the "coin_pub"
     164             :  * EdDSA key of the coin and demultiplexes based on $OP.
     165             :  *
     166             :  * @param rc request context
     167             :  * @param root uploaded JSON data
     168             :  * @param args array of additional options (first must be the
     169             :  *         reserve public key, the second one should be "withdraw")
     170             :  * @return MHD result code
     171             :  */
     172             : static MHD_RESULT
     173          91 : handle_post_coins (struct TEH_RequestContext *rc,
     174             :                    const json_t *root,
     175             :                    const char *const args[2])
     176             : {
     177             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     178             :   static const struct
     179             :   {
     180             :     /**
     181             :      * Name of the operation (args[1])
     182             :      */
     183             :     const char *op;
     184             : 
     185             :     /**
     186             :      * Function to call to perform the operation.
     187             :      */
     188             :     CoinOpHandler handler;
     189             : 
     190             :   } h[] = {
     191             :     {
     192             :       .op = "deposit",
     193             :       .handler = &TEH_handler_deposit
     194             :     },
     195             :     {
     196             :       .op = "melt",
     197             :       .handler = &TEH_handler_melt
     198             :     },
     199             :     {
     200             :       .op = "recoup",
     201             :       .handler = &TEH_handler_recoup
     202             :     },
     203             :     {
     204             :       .op = "refund",
     205             :       .handler = &TEH_handler_refund
     206             :     },
     207             :     {
     208             :       .op = NULL,
     209             :       .handler = NULL
     210             :     },
     211             :   };
     212             : 
     213          91 :   if (GNUNET_OK !=
     214          91 :       GNUNET_STRINGS_string_to_data (args[0],
     215             :                                      strlen (args[0]),
     216             :                                      &coin_pub,
     217             :                                      sizeof (coin_pub)))
     218             :   {
     219           0 :     GNUNET_break_op (0);
     220           0 :     return TALER_MHD_reply_with_error (rc->connection,
     221             :                                        MHD_HTTP_BAD_REQUEST,
     222             :                                        TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
     223             :                                        args[0]);
     224             :   }
     225         181 :   for (unsigned int i = 0; NULL != h[i].op; i++)
     226         181 :     if (0 == strcmp (h[i].op,
     227         181 :                      args[1]))
     228          91 :       return h[i].handler (rc->connection,
     229             :                            &coin_pub,
     230             :                            root);
     231           0 :   return r404 (rc->connection,
     232           0 :                args[1]);
     233             : }
     234             : 
     235             : 
     236             : /**
     237             :  * Function called whenever MHD is done with a request.  If the
     238             :  * request was a POST, we may have stored a `struct Buffer *` in the
     239             :  * @a con_cls that might still need to be cleaned up.  Call the
     240             :  * respective function to free the memory.
     241             :  *
     242             :  * @param cls client-defined closure
     243             :  * @param connection connection handle
     244             :  * @param con_cls value as set by the last call to
     245             :  *        the #MHD_AccessHandlerCallback
     246             :  * @param toe reason for request termination
     247             :  * @see #MHD_OPTION_NOTIFY_COMPLETED
     248             :  * @ingroup request
     249             :  */
     250             : static void
     251         285 : handle_mhd_completion_callback (void *cls,
     252             :                                 struct MHD_Connection *connection,
     253             :                                 void **con_cls,
     254             :                                 enum MHD_RequestTerminationCode toe)
     255             : {
     256         285 :   struct TEH_RequestContext *rc = *con_cls;
     257             :   struct GNUNET_AsyncScopeSave old_scope;
     258             : 
     259             :   (void) cls;
     260         285 :   if (NULL == rc)
     261           0 :     return;
     262         285 :   GNUNET_async_scope_enter (&rc->async_scope_id,
     263             :                             &old_scope);
     264         285 :   if (NULL != rc->rh_cleaner)
     265           0 :     rc->rh_cleaner (rc);
     266             :   {
     267             : #if MHD_VERSION >= 0x00097304
     268             :     const union MHD_ConnectionInfo *ci;
     269         285 :     unsigned int http_status = 0;
     270             : 
     271         285 :     ci = MHD_get_connection_info (connection,
     272             :                                   MHD_CONNECTION_INFO_HTTP_STATUS);
     273         285 :     if (NULL != ci)
     274         278 :       http_status = ci->http_status;
     275         285 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     276             :                 "Request for `%s' completed with HTTP status %u (%d)\n",
     277             :                 rc->url,
     278             :                 http_status,
     279             :                 toe);
     280             : #else
     281             :     (void) connection;
     282             :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     283             :                 "Request for `%s' completed (%d)\n",
     284             :                 rc->url,
     285             :                 toe);
     286             : #endif
     287             :   }
     288             : 
     289         285 :   TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context);
     290             :   /* Sanity-check that we didn't leave any transactions hanging */
     291         285 :   GNUNET_break (GNUNET_OK ==
     292             :                 TEH_plugin->preflight (TEH_plugin->cls));
     293         285 :   GNUNET_free (rc);
     294         285 :   *con_cls = NULL;
     295         285 :   GNUNET_async_scope_restore (&old_scope);
     296             : }
     297             : 
     298             : 
     299             : /**
     300             :  * We found a request handler responsible for handling a request. Parse the
     301             :  * @a upload_data (if applicable) and the @a url and call the
     302             :  * handler.
     303             :  *
     304             :  * @param rc request context
     305             :  * @param url rest of the URL to parse
     306             :  * @param upload_data upload data to parse (if available)
     307             :  * @param[in,out] upload_data_size number of bytes in @a upload_data
     308             :  * @return MHD result code
     309             :  */
     310             : static MHD_RESULT
     311         689 : proceed_with_handler (struct TEH_RequestContext *rc,
     312             :                       const char *url,
     313             :                       const char *upload_data,
     314             :                       size_t *upload_data_size)
     315         689 : {
     316         689 :   const struct TEH_RequestHandler *rh = rc->rh;
     317         689 :   const char *args[rh->nargs + 1];
     318         689 :   size_t ulen = strlen (url) + 1;
     319         689 :   json_t *root = NULL;
     320             :   MHD_RESULT ret;
     321             : 
     322             :   /* We do check for "ulen" here, because we'll later stack-allocate a buffer
     323             :      of that size and don't want to enable malicious clients to cause us
     324             :      huge stack allocations. */
     325         689 :   if (ulen > 512)
     326             :   {
     327             :     /* 512 is simply "big enough", as it is bigger than "6 * 54",
     328             :        which is the longest URL format we ever get (for
     329             :        /deposits/).  The value should be adjusted if we ever define protocol
     330             :        endpoints with plausibly longer inputs.  */
     331           0 :     GNUNET_break_op (0);
     332           0 :     return TALER_MHD_reply_with_error (rc->connection,
     333             :                                        MHD_HTTP_URI_TOO_LONG,
     334             :                                        TALER_EC_GENERIC_URI_TOO_LONG,
     335             :                                        url);
     336             :   }
     337             : 
     338             :   /* All POST endpoints come with a body in JSON format. So we parse
     339             :      the JSON here. */
     340         689 :   if (0 == strcasecmp (rh->method,
     341             :                        MHD_HTTP_METHOD_POST))
     342             :   {
     343             :     enum GNUNET_GenericReturnValue res;
     344             : 
     345         573 :     res = TALER_MHD_parse_post_json (rc->connection,
     346             :                                      &rc->opaque_post_parsing_context,
     347             :                                      upload_data,
     348             :                                      upload_data_size,
     349             :                                      &root);
     350         573 :     if (GNUNET_SYSERR == res)
     351             :     {
     352           0 :       GNUNET_assert (NULL == root);
     353           0 :       return MHD_NO;  /* bad upload, could not even generate error */
     354             :     }
     355         573 :     if ( (GNUNET_NO == res) ||
     356         573 :          (NULL == root) )
     357             :     {
     358         382 :       GNUNET_assert (NULL == root);
     359         382 :       return MHD_YES; /* so far incomplete upload or parser error */
     360             :     }
     361             :   }
     362             : 
     363         307 :   {
     364         307 :     char d[ulen];
     365             : 
     366             :     /* Parse command-line arguments, if applicable */
     367         307 :     args[0] = NULL;
     368         307 :     if (rh->nargs > 0)
     369             :     {
     370             :       unsigned int i;
     371             :       const char *fin;
     372             :       char *sp;
     373             : 
     374             :       /* make a copy of 'url' because 'strtok_r()' will modify */
     375         216 :       memcpy (d,
     376             :               url,
     377             :               ulen);
     378         216 :       i = 0;
     379         216 :       args[i++] = strtok_r (d, "/", &sp);
     380         444 :       while ( (NULL != args[i - 1]) &&
     381         391 :               (i < rh->nargs) )
     382         228 :         args[i++] = strtok_r (NULL, "/", &sp);
     383             :       /* make sure above loop ran nicely until completion, and also
     384             :          that there is no excess data in 'd' afterwards */
     385         216 :       if ( (! rh->nargs_is_upper_bound) &&
     386         163 :            ( (i != rh->nargs) ||
     387         326 :              (NULL == args[i - 1]) ||
     388         163 :              (NULL != (fin = strtok_r (NULL, "/", &sp))) ) )
     389             :       {
     390             :         char emsg[128 + 512];
     391             : 
     392           0 :         GNUNET_snprintf (emsg,
     393             :                          sizeof (emsg),
     394             :                          "Got %u/%u segments for %s request ('%s')",
     395           0 :                          (NULL == args[i - 1])
     396             :                          ? i - 1
     397           0 :                          : i + ((NULL != fin) ? 1 : 0),
     398             :                          rh->nargs,
     399             :                          rh->url,
     400             :                          url);
     401           0 :         GNUNET_break_op (0);
     402           0 :         json_decref (root);
     403           0 :         return TALER_MHD_reply_with_error (rc->connection,
     404             :                                            MHD_HTTP_NOT_FOUND,
     405             :                                            TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
     406             :                                            emsg);
     407             :       }
     408             : 
     409             :       /* just to be safe(r), we always terminate the array with a NULL
     410             :          (even if handlers requested precise number of arguments) */
     411         216 :       args[i] = NULL;
     412             :     }
     413             : 
     414             :     /* Above logic ensures that 'root' is exactly non-NULL for POST operations,
     415             :        so we test for 'root' to decide which handler to invoke. */
     416         307 :     if (NULL != root)
     417         191 :       ret = rh->handler.post (rc,
     418             :                               root,
     419             :                               args);
     420             :     else /* We also only have "POST" or "GET" in the API for at this point
     421             :             (OPTIONS/HEAD are taken care of earlier) */
     422         116 :       ret = rh->handler.get (rc,
     423             :                              args);
     424             :   }
     425         307 :   json_decref (root);
     426         307 :   return ret;
     427             : }
     428             : 
     429             : 
     430             : /**
     431             :  * Handle a "/seed" request.
     432             :  *
     433             :  * @param rc request context
     434             :  * @param args array of additional options (must be empty for this function)
     435             :  * @return MHD result code
     436             :  */
     437             : static MHD_RESULT
     438          16 : handler_seed (struct TEH_RequestContext *rc,
     439             :               const char *const args[])
     440             : {
     441             : #define SEED_SIZE 32
     442             :   char *body;
     443             :   MHD_RESULT ret;
     444             :   struct MHD_Response *resp;
     445             : 
     446          16 :   body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */
     447          16 :   if (NULL == body)
     448           0 :     return MHD_NO;
     449          16 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
     450             :                               body,
     451             :                               SEED_SIZE);
     452          16 :   resp = MHD_create_response_from_buffer (SEED_SIZE,
     453             :                                           body,
     454             :                                           MHD_RESPMEM_MUST_FREE);
     455          16 :   TALER_MHD_add_global_headers (resp);
     456          16 :   ret = MHD_queue_response (rc->connection,
     457             :                             MHD_HTTP_OK,
     458             :                             resp);
     459          16 :   GNUNET_break (MHD_YES == ret);
     460          16 :   MHD_destroy_response (resp);
     461          16 :   return ret;
     462             : #undef SEED_SIZE
     463             : }
     464             : 
     465             : 
     466             : /**
     467             :  * Handle POST "/management/..." requests.
     468             :  *
     469             :  * @param rc request context
     470             :  * @param root uploaded JSON data
     471             :  * @param args array of additional options
     472             :  * @return MHD result code
     473             :  */
     474             : static MHD_RESULT
     475          53 : handle_post_management (struct TEH_RequestContext *rc,
     476             :                         const json_t *root,
     477             :                         const char *const args[])
     478             : {
     479          53 :   if (NULL == args[0])
     480             :   {
     481           0 :     GNUNET_break_op (0);
     482           0 :     return r404 (rc->connection,
     483             :                  "/management");
     484             :   }
     485          53 :   if (0 == strcmp (args[0],
     486             :                    "auditors"))
     487             :   {
     488             :     struct TALER_AuditorPublicKeyP auditor_pub;
     489             : 
     490          13 :     if (NULL == args[1])
     491           9 :       return TEH_handler_management_auditors (rc->connection,
     492             :                                               root);
     493           4 :     if ( (NULL == args[1]) ||
     494           4 :          (NULL == args[2]) ||
     495           4 :          (0 != strcmp (args[2],
     496           4 :                        "disable")) ||
     497           4 :          (NULL != args[3]) )
     498           0 :       return r404 (rc->connection,
     499             :                    "/management/auditors/$AUDITOR_PUB/disable");
     500           4 :     if (GNUNET_OK !=
     501           4 :         GNUNET_STRINGS_string_to_data (args[1],
     502           4 :                                        strlen (args[1]),
     503             :                                        &auditor_pub,
     504             :                                        sizeof (auditor_pub)))
     505             :     {
     506           0 :       GNUNET_break_op (0);
     507           0 :       return TALER_MHD_reply_with_error (rc->connection,
     508             :                                          MHD_HTTP_BAD_REQUEST,
     509             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     510           0 :                                          args[1]);
     511             :     }
     512           4 :     return TEH_handler_management_auditors_AP_disable (rc->connection,
     513             :                                                        &auditor_pub,
     514             :                                                        root);
     515             :   }
     516          40 :   if (0 == strcmp (args[0],
     517             :                    "denominations"))
     518             :   {
     519             :     struct GNUNET_HashCode h_denom_pub;
     520             : 
     521           8 :     if ( (NULL == args[0]) ||
     522           8 :          (NULL == args[1]) ||
     523           8 :          (NULL == args[2]) ||
     524           8 :          (0 != strcmp (args[2],
     525           8 :                        "revoke")) ||
     526           8 :          (NULL != args[3]) )
     527           0 :       return r404 (rc->connection,
     528             :                    "/management/denominations/$HDP/revoke");
     529           8 :     if (GNUNET_OK !=
     530           8 :         GNUNET_STRINGS_string_to_data (args[1],
     531           8 :                                        strlen (args[1]),
     532             :                                        &h_denom_pub,
     533             :                                        sizeof (h_denom_pub)))
     534             :     {
     535           0 :       GNUNET_break_op (0);
     536           0 :       return TALER_MHD_reply_with_error (rc->connection,
     537             :                                          MHD_HTTP_BAD_REQUEST,
     538             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     539           0 :                                          args[1]);
     540             :     }
     541           8 :     return TEH_handler_management_denominations_HDP_revoke (rc->connection,
     542             :                                                             &h_denom_pub,
     543             :                                                             root);
     544             :   }
     545          32 :   if (0 == strcmp (args[0],
     546             :                    "signkeys"))
     547             :   {
     548             :     struct TALER_ExchangePublicKeyP exchange_pub;
     549             : 
     550           0 :     if ( (NULL == args[0]) ||
     551           0 :          (NULL == args[1]) ||
     552           0 :          (NULL == args[2]) ||
     553           0 :          (0 != strcmp (args[2],
     554           0 :                        "revoke")) ||
     555           0 :          (NULL != args[3]) )
     556           0 :       return r404 (rc->connection,
     557             :                    "/management/signkeys/$HDP/revoke");
     558           0 :     if (GNUNET_OK !=
     559           0 :         GNUNET_STRINGS_string_to_data (args[1],
     560           0 :                                        strlen (args[1]),
     561             :                                        &exchange_pub,
     562             :                                        sizeof (exchange_pub)))
     563             :     {
     564           0 :       GNUNET_break_op (0);
     565           0 :       return TALER_MHD_reply_with_error (rc->connection,
     566             :                                          MHD_HTTP_BAD_REQUEST,
     567             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     568           0 :                                          args[1]);
     569             :     }
     570           0 :     return TEH_handler_management_signkeys_EP_revoke (rc->connection,
     571             :                                                       &exchange_pub,
     572             :                                                       root);
     573             :   }
     574          32 :   if (0 == strcmp (args[0],
     575             :                    "keys"))
     576             :   {
     577           8 :     if (NULL != args[1])
     578             :     {
     579           0 :       GNUNET_break_op (0);
     580           0 :       return r404 (rc->connection,
     581             :                    "/management/keys/*");
     582             :     }
     583           8 :     return TEH_handler_management_post_keys (rc->connection,
     584             :                                              root);
     585             :   }
     586          24 :   if (0 == strcmp (args[0],
     587             :                    "wire"))
     588             :   {
     589          14 :     if (NULL == args[1])
     590          11 :       return TEH_handler_management_post_wire (rc->connection,
     591             :                                                root);
     592           3 :     if ( (0 != strcmp (args[1],
     593           3 :                        "disable")) ||
     594           3 :          (NULL != args[2]) )
     595             :     {
     596           0 :       GNUNET_break_op (0);
     597           0 :       return r404 (rc->connection,
     598             :                    "/management/wire/disable");
     599             :     }
     600           3 :     return TEH_handler_management_post_wire_disable (rc->connection,
     601             :                                                      root);
     602             :   }
     603          10 :   if (0 == strcmp (args[0],
     604             :                    "wire-fee"))
     605             :   {
     606          10 :     if (NULL != args[1])
     607             :     {
     608           0 :       GNUNET_break_op (0);
     609           0 :       return r404 (rc->connection,
     610             :                    "/management/wire-fee/*");
     611             :     }
     612          10 :     return TEH_handler_management_post_wire_fees (rc->connection,
     613             :                                                   root);
     614             :   }
     615           0 :   GNUNET_break_op (0);
     616           0 :   return r404 (rc->connection,
     617             :                "/management/*");
     618             : }
     619             : 
     620             : 
     621             : /**
     622             :  * Handle a get "/management" request.
     623             :  *
     624             :  * @param rc request context
     625             :  * @param args array of additional options (must be empty for this function)
     626             :  * @return MHD result code
     627             :  */
     628             : static MHD_RESULT
     629          10 : handle_get_management (struct TEH_RequestContext *rc,
     630             :                        const char *const args[1])
     631             : {
     632          10 :   if ( (NULL != args[0]) &&
     633          10 :        (0 == strcmp (args[0],
     634          10 :                      "keys")) &&
     635          10 :        (NULL == args[1]) )
     636             :   {
     637          10 :     return TEH_keys_management_get_keys_handler (rc->rh,
     638             :                                                  rc->connection);
     639             :   }
     640           0 :   GNUNET_break_op (0);
     641           0 :   return r404 (rc->connection,
     642             :                "/management/*");
     643             : }
     644             : 
     645             : 
     646             : /**
     647             :  * Handle POST "/auditors/..." requests.
     648             :  *
     649             :  * @param rc request context
     650             :  * @param root uploaded JSON data
     651             :  * @param args array of additional options
     652             :  * @return MHD result code
     653             :  */
     654             : static MHD_RESULT
     655           0 : handle_post_auditors (struct TEH_RequestContext *rc,
     656             :                       const json_t *root,
     657             :                       const char *const args[])
     658             : {
     659             :   struct TALER_AuditorPublicKeyP auditor_pub;
     660             :   struct GNUNET_HashCode h_denom_pub;
     661             : 
     662           0 :   if ( (NULL == args[0]) ||
     663           0 :        (NULL == args[1]) ||
     664           0 :        (NULL != args[2]) )
     665             :   {
     666           0 :     GNUNET_break_op (0);
     667           0 :     return r404 (rc->connection,
     668             :                  "/auditors/$AUDITOR_PUB/$H_DENOM_PUB");
     669             :   }
     670             : 
     671           0 :   if (GNUNET_OK !=
     672           0 :       GNUNET_STRINGS_string_to_data (args[0],
     673             :                                      strlen (args[0]),
     674             :                                      &auditor_pub,
     675             :                                      sizeof (auditor_pub)))
     676             :   {
     677           0 :     GNUNET_break_op (0);
     678           0 :     return TALER_MHD_reply_with_error (rc->connection,
     679             :                                        MHD_HTTP_BAD_REQUEST,
     680             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     681             :                                        args[0]);
     682             :   }
     683           0 :   if (GNUNET_OK !=
     684           0 :       GNUNET_STRINGS_string_to_data (args[1],
     685           0 :                                      strlen (args[1]),
     686             :                                      &h_denom_pub,
     687             :                                      sizeof (h_denom_pub)))
     688             :   {
     689           0 :     GNUNET_break_op (0);
     690           0 :     return TALER_MHD_reply_with_error (rc->connection,
     691             :                                        MHD_HTTP_BAD_REQUEST,
     692             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     693           0 :                                        args[1]);
     694             :   }
     695           0 :   return TEH_handler_auditors (rc->connection,
     696             :                                &auditor_pub,
     697             :                                &h_denom_pub,
     698             :                                root);
     699             : }
     700             : 
     701             : 
     702             : /**
     703             :  * Handle incoming HTTP request.
     704             :  *
     705             :  * @param cls closure for MHD daemon (unused)
     706             :  * @param connection the connection
     707             :  * @param url the requested url
     708             :  * @param method the method (POST, GET, ...)
     709             :  * @param version HTTP version (ignored)
     710             :  * @param upload_data request data
     711             :  * @param upload_data_size size of @a upload_data in bytes
     712             :  * @param con_cls closure for request (a `struct TEH_RequestContext *`)
     713             :  * @return MHD result code
     714             :  */
     715             : static MHD_RESULT
     716         692 : handle_mhd_request (void *cls,
     717             :                     struct MHD_Connection *connection,
     718             :                     const char *url,
     719             :                     const char *method,
     720             :                     const char *version,
     721             :                     const char *upload_data,
     722             :                     size_t *upload_data_size,
     723             :                     void **con_cls)
     724             : {
     725             :   static struct TEH_RequestHandler handlers[] = {
     726             :     /* /robots.txt: disallow everything */
     727             :     {
     728             :       .url = "robots.txt",
     729             :       .method = MHD_HTTP_METHOD_GET,
     730             :       .handler.get = &TEH_handler_static_response,
     731             :       .mime_type = "text/plain",
     732             :       .data = "User-agent: *\nDisallow: /\n",
     733             :       .response_code = MHD_HTTP_OK
     734             :     },
     735             :     /* Landing page, tell humans to go away. */
     736             :     {
     737             :       .url = "",
     738             :       .method = MHD_HTTP_METHOD_GET,
     739             :       .handler.get = TEH_handler_static_response,
     740             :       .mime_type = "text/plain",
     741             :       .data =
     742             :         "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n",
     743             :       .response_code = MHD_HTTP_OK
     744             :     },
     745             :     /* AGPL licensing page, redirect to source. As per the AGPL-license, every
     746             :        deployment is required to offer the user a download of the source of
     747             :        the actual deployment. We make this easy by including a redirect to the
     748             :        source here. */
     749             :     {
     750             :       .url = "agpl",
     751             :       .method = MHD_HTTP_METHOD_GET,
     752             :       .handler.get = &TEH_handler_agpl_redirect
     753             :     },
     754             :     {
     755             :       .url = "seed",
     756             :       .method = MHD_HTTP_METHOD_GET,
     757             :       .handler.get = &handler_seed
     758             :     },
     759             :     /* Terms of service */
     760             :     {
     761             :       .url = "terms",
     762             :       .method = MHD_HTTP_METHOD_GET,
     763             :       .handler.get = &TEH_handler_terms
     764             :     },
     765             :     /* Privacy policy */
     766             :     {
     767             :       .url = "privacy",
     768             :       .method = MHD_HTTP_METHOD_GET,
     769             :       .handler.get = &TEH_handler_privacy
     770             :     },
     771             :     /* Return key material and fundamental properties for this exchange */
     772             :     {
     773             :       .url = "keys",
     774             :       .method = MHD_HTTP_METHOD_GET,
     775             :       .handler.get = &TEH_keys_get_handler,
     776             :     },
     777             :     /* Requests for wiring information */
     778             :     {
     779             :       .url = "wire",
     780             :       .method = MHD_HTTP_METHOD_GET,
     781             :       .handler.get = &TEH_handler_wire
     782             :     },
     783             :     /* Withdrawing coins / interaction with reserves */
     784             :     {
     785             :       .url = "reserves",
     786             :       .method = MHD_HTTP_METHOD_GET,
     787             :       .handler.get = &TEH_handler_reserves_get,
     788             :       .nargs = 1
     789             :     },
     790             :     {
     791             :       .url = "reserves",
     792             :       .method = MHD_HTTP_METHOD_POST,
     793             :       .handler.post = &TEH_handler_withdraw,
     794             :       .nargs = 2
     795             :     },
     796             :     /* coins */
     797             :     {
     798             :       .url = "coins",
     799             :       .method = MHD_HTTP_METHOD_POST,
     800             :       .handler.post = &handle_post_coins,
     801             :       .nargs = 2
     802             :     },
     803             :     {
     804             :       .url = "coins",
     805             :       .method = MHD_HTTP_METHOD_GET,
     806             :       .handler.get = TEH_handler_link,
     807             :       .nargs = 2,
     808             :     },
     809             :     /* refreshes/$RCH/reveal */
     810             :     {
     811             :       .url = "refreshes",
     812             :       .method = MHD_HTTP_METHOD_POST,
     813             :       .handler.post = &TEH_handler_reveal,
     814             :       .nargs = 2
     815             :     },
     816             :     /* tracking transfers */
     817             :     {
     818             :       .url = "transfers",
     819             :       .method = MHD_HTTP_METHOD_GET,
     820             :       .handler.get = &TEH_handler_transfers_get,
     821             :       .nargs = 1
     822             :     },
     823             :     /* tracking deposits */
     824             :     {
     825             :       .url = "deposits",
     826             :       .method = MHD_HTTP_METHOD_GET,
     827             :       .handler.get = &TEH_handler_deposits_get,
     828             :       .nargs = 4
     829             :     },
     830             :     /* POST management endpoints */
     831             :     {
     832             :       .url = "management",
     833             :       .method = MHD_HTTP_METHOD_POST,
     834             :       .handler.post = &handle_post_management,
     835             :       .nargs = 4,
     836             :       .nargs_is_upper_bound = true
     837             :     },
     838             :     /* GET management endpoints (we only really have "/management/keys") */
     839             :     {
     840             :       .url = "management",
     841             :       .method = MHD_HTTP_METHOD_GET,
     842             :       .handler.get = &handle_get_management,
     843             :       .nargs = 1
     844             :     },
     845             :     /* auditor endpoints */
     846             :     {
     847             :       .url = "auditors",
     848             :       .method = MHD_HTTP_METHOD_POST,
     849             :       .handler.post = &handle_post_auditors,
     850             :       .nargs = 4,
     851             :       .nargs_is_upper_bound = true
     852             :     },
     853             :     /* mark end of list */
     854             :     {
     855             :       .url = NULL
     856             :     }
     857             :   };
     858         692 :   struct TEH_RequestContext *rc = *con_cls;
     859             :   struct GNUNET_AsyncScopeSave old_scope;
     860         692 :   const char *correlation_id = NULL;
     861             : 
     862             :   (void) cls;
     863             :   (void) version;
     864         692 :   if (NULL == rc)
     865             :   {
     866             :     unsigned long long cnt;
     867             : 
     868         285 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     869             :                 "Handling new request\n");
     870             :     /* Atomic operation, no need for a lock ;-) */
     871         285 :     cnt = __sync_add_and_fetch (&req_count,
     872             :                                 1LLU);
     873         285 :     if (req_max == cnt)
     874             :     {
     875           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     876             :                   "Restarting exchange service after %llu requests\n",
     877             :                   cnt);
     878           0 :       (void) kill (getpid (),
     879             :                    SIGHUP);
     880             :     }
     881             : 
     882             :     /* We're in a new async scope! */
     883         285 :     rc = *con_cls = GNUNET_new (struct TEH_RequestContext);
     884         285 :     GNUNET_async_scope_fresh (&rc->async_scope_id);
     885         285 :     rc->url = url;
     886         285 :     rc->connection = connection;
     887             :     /* We only read the correlation ID on the first callback for every client */
     888         285 :     correlation_id = MHD_lookup_connection_value (connection,
     889             :                                                   MHD_HEADER_KIND,
     890             :                                                   "Taler-Correlation-Id");
     891         285 :     if ( (NULL != correlation_id) &&
     892             :          (GNUNET_YES !=
     893           0 :           GNUNET_CURL_is_valid_scope_id (correlation_id)) )
     894             :     {
     895           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     896             :                   "illegal incoming correlation ID\n");
     897           0 :       correlation_id = NULL;
     898             :     }
     899             :   }
     900             : 
     901         692 :   GNUNET_async_scope_enter (&rc->async_scope_id,
     902             :                             &old_scope);
     903         692 :   if (NULL != correlation_id)
     904           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     905             :                 "Handling request (%s) for URL '%s', correlation_id=%s\n",
     906             :                 method,
     907             :                 url,
     908             :                 correlation_id);
     909             :   else
     910         692 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     911             :                 "Handling request (%s) for URL '%s'\n",
     912             :                 method,
     913             :                 url);
     914             :   /* on repeated requests, check our cache first */
     915         692 :   if (NULL != rc->rh)
     916             :   {
     917             :     MHD_RESULT ret;
     918             :     const char *start;
     919             : 
     920         407 :     if ('\0' == url[0])
     921             :       /* strange, should start with '/', treat as just "/" */
     922           0 :       url = "/";
     923         407 :     start = strchr (url + 1, '/');
     924         407 :     if (NULL == start)
     925          25 :       start = "";
     926         407 :     ret = proceed_with_handler (rc,
     927             :                                 start,
     928             :                                 upload_data,
     929             :                                 upload_data_size);
     930         407 :     GNUNET_async_scope_restore (&old_scope);
     931         407 :     return ret;
     932             :   }
     933             : 
     934         285 :   if ( (0 == strcasecmp (method,
     935           0 :                          MHD_HTTP_METHOD_OPTIONS)) &&
     936           0 :        (0 == strcmp ("*",
     937             :                      url)) )
     938           0 :     return TALER_MHD_reply_cors_preflight (connection);
     939             : 
     940         285 :   if (0 == strcasecmp (method,
     941             :                        MHD_HTTP_METHOD_HEAD))
     942           0 :     method = MHD_HTTP_METHOD_GET;   /* treat HEAD as GET here, MHD will do the rest */
     943             : 
     944             :   /* parse first part of URL */
     945             :   {
     946         285 :     bool found = false;
     947             :     size_t tok_size;
     948             :     const char *tok;
     949             :     const char *rest;
     950             : 
     951         285 :     if ('\0' == url[0])
     952             :       /* strange, should start with '/', treat as just "/" */
     953           0 :       url = "/";
     954         285 :     tok = url + 1;
     955         285 :     rest = strchr (tok, '/');
     956         285 :     if (NULL == rest)
     957             :     {
     958          67 :       tok_size = strlen (tok);
     959             :     }
     960             :     else
     961             :     {
     962         218 :       tok_size = rest - tok;
     963         218 :       rest++; /* skip over '/' */
     964             :     }
     965        3089 :     for (unsigned int i = 0; NULL != handlers[i].url; i++)
     966             :     {
     967        3086 :       struct TEH_RequestHandler *rh = &handlers[i];
     968             : 
     969        3086 :       if ( (0 != strncmp (tok,
     970             :                           rh->url,
     971         341 :                           tok_size)) ||
     972         341 :            (tok_size != strlen (rh->url) ) )
     973        2754 :         continue;
     974         332 :       found = true;
     975             :       /* The URL is a match!  What we now do depends on the method. */
     976         332 :       if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS))
     977             :       {
     978           0 :         GNUNET_async_scope_restore (&old_scope);
     979           0 :         return TALER_MHD_reply_cors_preflight (connection);
     980             :       }
     981         332 :       GNUNET_assert (NULL != rh->method);
     982         332 :       if (0 == strcasecmp (method,
     983             :                            rh->method))
     984             :       {
     985             :         MHD_RESULT ret;
     986             : 
     987             :         /* cache to avoid the loop next time */
     988         282 :         rc->rh = rh;
     989             :         /* run handler */
     990         282 :         ret = proceed_with_handler (rc,
     991         282 :                                     url + tok_size + 1,
     992             :                                     upload_data,
     993             :                                     upload_data_size);
     994         282 :         GNUNET_async_scope_restore (&old_scope);
     995         282 :         return ret;
     996             :       }
     997             :     }
     998             : 
     999           3 :     if (found)
    1000             :     {
    1001             :       /* we found a matching address, but the method is wrong */
    1002             :       struct MHD_Response *reply;
    1003             :       MHD_RESULT ret;
    1004           0 :       char *allowed = NULL;
    1005             : 
    1006           0 :       GNUNET_break_op (0);
    1007           0 :       for (unsigned int i = 0; NULL != handlers[i].url; i++)
    1008             :       {
    1009           0 :         struct TEH_RequestHandler *rh = &handlers[i];
    1010             : 
    1011           0 :         if ( (0 != strncmp (tok,
    1012             :                             rh->url,
    1013           0 :                             tok_size)) ||
    1014           0 :              (tok_size != strlen (rh->url) ) )
    1015           0 :           continue;
    1016           0 :         if (NULL == allowed)
    1017             :         {
    1018           0 :           allowed = GNUNET_strdup (rh->method);
    1019             :         }
    1020             :         else
    1021             :         {
    1022             :           char *tmp;
    1023             : 
    1024           0 :           GNUNET_asprintf (&tmp,
    1025             :                            "%s, %s",
    1026             :                            allowed,
    1027             :                            rh->method);
    1028           0 :           GNUNET_free (allowed);
    1029           0 :           allowed = tmp;
    1030             :         }
    1031           0 :         if (0 == strcasecmp (rh->method,
    1032             :                              MHD_HTTP_METHOD_GET))
    1033             :         {
    1034             :           char *tmp;
    1035             : 
    1036           0 :           GNUNET_asprintf (&tmp,
    1037             :                            "%s, %s",
    1038             :                            allowed,
    1039             :                            MHD_HTTP_METHOD_HEAD);
    1040           0 :           GNUNET_free (allowed);
    1041           0 :           allowed = tmp;
    1042             :         }
    1043             :       }
    1044           0 :       reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
    1045             :                                     method);
    1046           0 :       GNUNET_break (MHD_YES ==
    1047             :                     MHD_add_response_header (reply,
    1048             :                                              MHD_HTTP_HEADER_ALLOW,
    1049             :                                              allowed));
    1050           0 :       GNUNET_free (allowed);
    1051           0 :       ret = MHD_queue_response (connection,
    1052             :                                 MHD_HTTP_METHOD_NOT_ALLOWED,
    1053             :                                 reply);
    1054           0 :       MHD_destroy_response (reply);
    1055           0 :       return ret;
    1056             :     }
    1057             :   }
    1058             : 
    1059             :   /* No handler matches, generate not found */
    1060             :   {
    1061             :     MHD_RESULT ret;
    1062             : 
    1063           3 :     ret = TALER_MHD_reply_with_error (connection,
    1064             :                                       MHD_HTTP_NOT_FOUND,
    1065             :                                       TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
    1066             :                                       url);
    1067           3 :     GNUNET_async_scope_restore (&old_scope);
    1068           3 :     return ret;
    1069             :   }
    1070             : }
    1071             : 
    1072             : 
    1073             : /**
    1074             :  * Load configuration parameters for the exchange
    1075             :  * server into the corresponding global variables.
    1076             :  *
    1077             :  * @return #GNUNET_OK on success
    1078             :  */
    1079             : static enum GNUNET_GenericReturnValue
    1080          12 : exchange_serve_process_config (void)
    1081             : {
    1082          12 :   if (GNUNET_OK !=
    1083          12 :       GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
    1084             :                                              "exchange",
    1085             :                                              "MAX_REQUESTS",
    1086             :                                              &req_max))
    1087             :   {
    1088          12 :     req_max = ULLONG_MAX;
    1089             :   }
    1090          12 :   if (GNUNET_OK !=
    1091          12 :       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
    1092             :                                            "exchangedb",
    1093             :                                            "IDLE_RESERVE_EXPIRATION_TIME",
    1094             :                                            &TEH_reserve_closing_delay))
    1095             :   {
    1096           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1097             :                                "exchangedb",
    1098             :                                "IDLE_RESERVE_EXPIRATION_TIME");
    1099             :     /* use default */
    1100             :     TEH_reserve_closing_delay
    1101           0 :       = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
    1102             :                                        4);
    1103             :   }
    1104             : 
    1105          12 :   if (GNUNET_OK !=
    1106          12 :       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
    1107             :                                            "exchange",
    1108             :                                            "MAX_KEYS_CACHING",
    1109             :                                            &TEH_max_keys_caching))
    1110             :   {
    1111           0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1112             :                                "exchange",
    1113             :                                "MAX_KEYS_CACHING",
    1114             :                                "valid relative time expected");
    1115           0 :     return GNUNET_SYSERR;
    1116             :   }
    1117          12 :   if (GNUNET_OK !=
    1118          12 :       TALER_config_get_currency (TEH_cfg,
    1119             :                                  &TEH_currency))
    1120             :   {
    1121           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1122             :                                "taler",
    1123             :                                "CURRENCY");
    1124           0 :     return GNUNET_SYSERR;
    1125             :   }
    1126             :   {
    1127             :     char *master_public_key_str;
    1128             : 
    1129          12 :     if (GNUNET_OK !=
    1130          12 :         GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
    1131             :                                                "exchange",
    1132             :                                                "MASTER_PUBLIC_KEY",
    1133             :                                                &master_public_key_str))
    1134             :     {
    1135           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1136             :                                  "exchange",
    1137             :                                  "MASTER_PUBLIC_KEY");
    1138           0 :       return GNUNET_SYSERR;
    1139             :     }
    1140          12 :     if (GNUNET_OK !=
    1141          12 :         GNUNET_CRYPTO_eddsa_public_key_from_string (master_public_key_str,
    1142             :                                                     strlen (
    1143             :                                                       master_public_key_str),
    1144             :                                                     &TEH_master_public_key.
    1145             :                                                     eddsa_pub))
    1146             :     {
    1147           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1148             :                   "Invalid master public key given in exchange configuration.");
    1149           0 :       GNUNET_free (master_public_key_str);
    1150           0 :       return GNUNET_SYSERR;
    1151             :     }
    1152          12 :     GNUNET_free (master_public_key_str);
    1153             :   }
    1154          12 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1155             :               "Launching exchange with public key `%s'...\n",
    1156             :               GNUNET_p2s (&TEH_master_public_key.eddsa_pub));
    1157             : 
    1158          12 :   if (NULL ==
    1159          12 :       (TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg)))
    1160             :   {
    1161           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1162             :                 "Failed to initialize DB subsystem\n");
    1163           0 :     return GNUNET_SYSERR;
    1164             :   }
    1165          12 :   return GNUNET_OK;
    1166             : }
    1167             : 
    1168             : 
    1169             : /**
    1170             :  * Called when the main thread exits, writes out performance
    1171             :  * stats if requested.
    1172             :  */
    1173             : static void
    1174          12 : write_stats (void)
    1175             : {
    1176             :   struct GNUNET_DISK_FileHandle *fh;
    1177          12 :   pid_t pid = getpid ();
    1178             :   char *benchmark_dir;
    1179             :   char *s;
    1180             :   struct rusage usage;
    1181             : 
    1182          12 :   benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
    1183          12 :   if (NULL == benchmark_dir)
    1184          12 :     return;
    1185           0 :   GNUNET_asprintf (&s,
    1186             :                    "%s/taler-exchange-%llu.txt",
    1187             :                    benchmark_dir,
    1188             :                    (unsigned long long) pid);
    1189           0 :   fh = GNUNET_DISK_file_open (s,
    1190             :                               (GNUNET_DISK_OPEN_WRITE
    1191             :                                | GNUNET_DISK_OPEN_TRUNCATE
    1192             :                                | GNUNET_DISK_OPEN_CREATE),
    1193             :                               (GNUNET_DISK_PERM_USER_READ
    1194             :                                | GNUNET_DISK_PERM_USER_WRITE));
    1195           0 :   GNUNET_free (s);
    1196           0 :   if (NULL == fh)
    1197           0 :     return; /* permission denied? */
    1198             : 
    1199             :   /* Collect stats, summed up for all threads */
    1200           0 :   GNUNET_assert (0 ==
    1201             :                  getrusage (RUSAGE_SELF,
    1202             :                             &usage));
    1203           0 :   GNUNET_asprintf (&s,
    1204             :                    "time_exchange sys %llu user %llu\n",
    1205           0 :                    (unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000
    1206           0 :                                          + usage.ru_stime.tv_usec),
    1207           0 :                    (unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000
    1208           0 :                                          + usage.ru_utime.tv_usec));
    1209           0 :   GNUNET_assert (GNUNET_SYSERR !=
    1210             :                  GNUNET_DISK_file_write_blocking (fh,
    1211             :                                                   s,
    1212             :                                                   strlen (s)));
    1213           0 :   GNUNET_free (s);
    1214           0 :   GNUNET_assert (GNUNET_OK ==
    1215             :                  GNUNET_DISK_file_close (fh));
    1216             : }
    1217             : 
    1218             : 
    1219             : /* Developer logic for supporting the `-f' option. */
    1220             : #if HAVE_DEVELOPER
    1221             : 
    1222             : /**
    1223             :  * Option `-f' (specifies an input file to give to the HTTP server).
    1224             :  */
    1225             : static char *input_filename;
    1226             : 
    1227             : 
    1228             : /**
    1229             :  * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename
    1230             :  * as the input for the request.  If launching the client worked,
    1231             :  * run the #TEH_KS_loop() event loop as usual.
    1232             :  *
    1233             :  * @return child pid
    1234             :  */
    1235             : static pid_t
    1236           0 : run_fake_client (void)
    1237             : {
    1238             :   pid_t cld;
    1239             :   char ports[6];
    1240             :   int fd;
    1241             : 
    1242           0 :   if (0 == strcmp (input_filename,
    1243             :                    "-"))
    1244           0 :     fd = STDIN_FILENO;
    1245             :   else
    1246           0 :     fd = open (input_filename,
    1247             :                O_RDONLY);
    1248           0 :   if (-1 == fd)
    1249             :   {
    1250           0 :     fprintf (stderr,
    1251             :              "Failed to open `%s': %s\n",
    1252             :              input_filename,
    1253           0 :              strerror (errno));
    1254           0 :     return -1;
    1255             :   }
    1256             :   /* Fake HTTP client request with #input_filename as input.
    1257             :      We do this using the nc tool. */
    1258           0 :   GNUNET_snprintf (ports,
    1259             :                    sizeof (ports),
    1260             :                    "%u",
    1261             :                    serve_port);
    1262           0 :   if (0 == (cld = fork ()))
    1263             :   {
    1264           0 :     GNUNET_break (0 == close (0));
    1265           0 :     GNUNET_break (0 == dup2 (fd, 0));
    1266           0 :     GNUNET_break (0 == close (fd));
    1267           0 :     if ( (0 != execlp ("nc",
    1268             :                        "nc",
    1269             :                        "localhost",
    1270             :                        ports,
    1271             :                        "-w", "30",
    1272           0 :                        NULL)) &&
    1273           0 :          (0 != execlp ("ncat",
    1274             :                        "ncat",
    1275             :                        "localhost",
    1276             :                        ports,
    1277             :                        "-i", "30",
    1278             :                        NULL)) )
    1279             :     {
    1280           0 :       fprintf (stderr,
    1281             :                "Failed to run both `nc' and `ncat': %s\n",
    1282           0 :                strerror (errno));
    1283             :     }
    1284           0 :     _exit (1);
    1285             :   }
    1286             :   /* parent process */
    1287           0 :   if (0 != strcmp (input_filename,
    1288             :                    "-"))
    1289           0 :     GNUNET_break (0 == close (fd));
    1290           0 :   return cld;
    1291             : }
    1292             : 
    1293             : 
    1294             : /**
    1295             :  * Run the exchange to serve a single request only, without threads.
    1296             :  *
    1297             :  * @return #GNUNET_OK on success
    1298             :  */
    1299             : static void
    1300           0 : run_single_request (void)
    1301             : {
    1302             :   pid_t xfork;
    1303             : 
    1304           0 :   xfork = fork ();
    1305           0 :   if (-1 == xfork)
    1306             :   {
    1307           0 :     global_ret = EXIT_FAILURE;
    1308           0 :     GNUNET_SCHEDULER_shutdown ();
    1309           0 :     return;
    1310             :   }
    1311           0 :   if (0 == xfork)
    1312             :   {
    1313             :     pid_t cld;
    1314             : 
    1315           0 :     cld = run_fake_client ();
    1316           0 :     if (-1 == cld)
    1317           0 :       _exit (EXIT_FAILURE);
    1318           0 :     _exit (EXIT_SUCCESS);
    1319             :   }
    1320             : 
    1321             :   {
    1322             :     int status;
    1323             : 
    1324           0 :     if (xfork != waitpid (xfork,
    1325             :                           &status,
    1326             :                           0))
    1327           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1328             :                   "Waiting for `nc' child failed: %s\n",
    1329             :                   strerror (errno));
    1330             :   }
    1331             : }
    1332             : 
    1333             : 
    1334             : /* end of HAVE_DEVELOPER */
    1335             : #endif
    1336             : 
    1337             : 
    1338             : /**
    1339             :  * Signature of the callback used by MHD to notify the application
    1340             :  * about completed connections.  If we are running in test-mode with
    1341             :  * an input_filename, this function is used to terminate the HTTPD
    1342             :  * after the first request has been processed.
    1343             :  *
    1344             :  * @param cls client-defined closure, NULL
    1345             :  * @param connection connection handle (ignored)
    1346             :  * @param socket_context socket-specific pointer (ignored)
    1347             :  * @param toe reason for connection notification
    1348             :  */
    1349             : static void
    1350         448 : connection_done (void *cls,
    1351             :                  struct MHD_Connection *connection,
    1352             :                  void **socket_context,
    1353             :                  enum MHD_ConnectionNotificationCode toe)
    1354             : {
    1355             :   (void) cls;
    1356             :   (void) connection;
    1357             :   (void) socket_context;
    1358             : 
    1359             : #if HAVE_DEVELOPER
    1360             :   /* We only act if the connection is closed. */
    1361         448 :   if (MHD_CONNECTION_NOTIFY_CLOSED != toe)
    1362         224 :     return;
    1363         224 :   if (NULL != input_filename)
    1364           0 :     GNUNET_SCHEDULER_shutdown ();
    1365             : #endif
    1366             : }
    1367             : 
    1368             : 
    1369             : /**
    1370             :  * Function run on shutdown.
    1371             :  *
    1372             :  * @param cls NULL
    1373             :  */
    1374             : static void
    1375          12 : do_shutdown (void *cls)
    1376             : {
    1377             :   struct MHD_Daemon *mhd;
    1378             :   (void) cls;
    1379             : 
    1380          12 :   mhd = TALER_MHD_daemon_stop ();
    1381          12 :   TEH_resume_keys_requests (true);
    1382          12 :   TEH_reserves_get_cleanup ();
    1383          12 :   if (NULL != mhd)
    1384          12 :     MHD_stop_daemon (mhd);
    1385          12 :   TEH_WIRE_done ();
    1386          12 :   TEH_keys_finished ();
    1387          12 :   TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
    1388          12 : }
    1389             : 
    1390             : 
    1391             : /**
    1392             :  * Main function that will be run by the scheduler.
    1393             :  *
    1394             :  * @param cls closure
    1395             :  * @param args remaining command-line arguments
    1396             :  * @param cfgfile name of the configuration file used (for saving, can be
    1397             :  *        NULL!)
    1398             :  * @param config configuration
    1399             :  */
    1400             : static void
    1401          12 : run (void *cls,
    1402             :      char *const *args,
    1403             :      const char *cfgfile,
    1404             :      const struct GNUNET_CONFIGURATION_Handle *config)
    1405             : {
    1406             :   enum TALER_MHD_GlobalOptions go;
    1407             :   int fh;
    1408             : 
    1409          12 :   go = TALER_MHD_GO_NONE;
    1410          12 :   if (connection_close)
    1411           0 :     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
    1412          12 :   TALER_MHD_setup (go);
    1413          12 :   TEH_cfg = config;
    1414             : 
    1415          12 :   if (GNUNET_OK !=
    1416          12 :       exchange_serve_process_config ())
    1417             :   {
    1418           0 :     global_ret = EXIT_NOTCONFIGURED;
    1419           0 :     GNUNET_SCHEDULER_shutdown ();
    1420           0 :     return;
    1421             :   }
    1422          12 :   if (GNUNET_SYSERR ==
    1423          12 :       TEH_plugin->preflight (TEH_plugin->cls))
    1424             :   {
    1425           0 :     global_ret = EXIT_FAILURE;
    1426           0 :     GNUNET_SCHEDULER_shutdown ();
    1427           0 :     return;
    1428             :   }
    1429          12 :   if (GNUNET_OK !=
    1430          12 :       TEH_keys_init ())
    1431             :   {
    1432           0 :     global_ret = EXIT_FAILURE;
    1433           0 :     GNUNET_SCHEDULER_shutdown ();
    1434           0 :     return;
    1435             :   }
    1436          12 :   if (GNUNET_OK !=
    1437          12 :       TEH_wire_init ())
    1438             :   {
    1439           0 :     global_ret = EXIT_FAILURE;
    1440           0 :     GNUNET_SCHEDULER_shutdown ();
    1441           0 :     return;
    1442             :   }
    1443             : 
    1444          12 :   TEH_load_terms (TEH_cfg);
    1445          12 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1446             :                                  NULL);
    1447          12 :   fh = TALER_MHD_bind (TEH_cfg,
    1448             :                        "exchange",
    1449             :                        &serve_port);
    1450          12 :   if ( (0 == serve_port) &&
    1451             :        (-1 == fh) )
    1452             :   {
    1453           0 :     GNUNET_SCHEDULER_shutdown ();
    1454           0 :     return;
    1455             :   }
    1456             :   {
    1457             :     struct MHD_Daemon *mhd;
    1458             : 
    1459          12 :     mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
    1460             :                             | MHD_USE_PIPE_FOR_SHUTDOWN
    1461             :                             | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
    1462             :                             | MHD_USE_TCP_FASTOPEN,
    1463             :                             (-1 == fh) ? serve_port : 0,
    1464             :                             NULL, NULL,
    1465             :                             &handle_mhd_request, NULL,
    1466             :                             MHD_OPTION_LISTEN_BACKLOG_SIZE,
    1467             :                             (unsigned int) 1024,
    1468             :                             MHD_OPTION_LISTEN_SOCKET,
    1469             :                             fh,
    1470             :                             MHD_OPTION_EXTERNAL_LOGGER,
    1471             :                             &TALER_MHD_handle_logs,
    1472             :                             NULL,
    1473             :                             MHD_OPTION_NOTIFY_COMPLETED,
    1474             :                             &handle_mhd_completion_callback,
    1475             :                             NULL,
    1476             :                             MHD_OPTION_NOTIFY_CONNECTION,
    1477             :                             &connection_done,
    1478             :                             NULL,
    1479             :                             MHD_OPTION_CONNECTION_TIMEOUT,
    1480             :                             connection_timeout,
    1481             :                             MHD_OPTION_END);
    1482          12 :     if (NULL == mhd)
    1483             :     {
    1484           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1485             :                   "Failed to launch HTTP service. Is the port in use?\n");
    1486           0 :       GNUNET_SCHEDULER_shutdown ();
    1487           0 :       return;
    1488             :     }
    1489          12 :     global_ret = EXIT_SUCCESS;
    1490          12 :     TALER_MHD_daemon_start (mhd);
    1491             :   }
    1492          12 :   atexit (&write_stats);
    1493             : 
    1494             : #if HAVE_DEVELOPER
    1495          12 :   if (NULL != input_filename)
    1496           0 :     run_single_request ();
    1497             : #endif
    1498             : }
    1499             : 
    1500             : 
    1501             : /**
    1502             :  * The main function of the taler-exchange-httpd server ("the exchange").
    1503             :  *
    1504             :  * @param argc number of arguments from the command line
    1505             :  * @param argv command line arguments
    1506             :  * @return 0 ok, 1 on error
    1507             :  */
    1508             : int
    1509          12 : main (int argc,
    1510             :       char *const *argv)
    1511             : {
    1512          12 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1513          12 :     GNUNET_GETOPT_option_flag ('a',
    1514             :                                "allow-timetravel",
    1515             :                                "allow clients to request /keys for arbitrary timestamps (for testing and development only)",
    1516             :                                &TEH_allow_keys_timetravel),
    1517          12 :     GNUNET_GETOPT_option_flag ('C',
    1518             :                                "connection-close",
    1519             :                                "force HTTP connections to be closed after each request",
    1520             :                                &connection_close),
    1521          12 :     GNUNET_GETOPT_option_uint ('t',
    1522             :                                "timeout",
    1523             :                                "SECONDS",
    1524             :                                "after how long do connections timeout by default (in seconds)",
    1525             :                                &connection_timeout),
    1526          12 :     GNUNET_GETOPT_option_timetravel ('T',
    1527             :                                      "timetravel"),
    1528             : #if HAVE_DEVELOPER
    1529          12 :     GNUNET_GETOPT_option_filename ('f',
    1530             :                                    "file-input",
    1531             :                                    "FILENAME",
    1532             :                                    "run in test-mode using FILENAME as the HTTP request to process, use '-' to read from stdin",
    1533             :                                    &input_filename),
    1534             : #endif
    1535          12 :     GNUNET_GETOPT_option_help (
    1536             :       "HTTP server providing a RESTful API to access a Taler exchange"),
    1537          12 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
    1538             :     GNUNET_GETOPT_OPTION_END
    1539             :   };
    1540             :   enum GNUNET_GenericReturnValue ret;
    1541             : 
    1542          12 :   TALER_OS_init ();
    1543          12 :   ret = GNUNET_PROGRAM_run (argc, argv,
    1544             :                             "taler-exchange-httpd",
    1545             :                             "Taler exchange HTTP service",
    1546             :                             options,
    1547             :                             &run, NULL);
    1548          12 :   if (GNUNET_SYSERR == ret)
    1549           0 :     return EXIT_INVALIDARGUMENT;
    1550          12 :   if (GNUNET_NO == ret)
    1551           0 :     return EXIT_SUCCESS;
    1552          12 :   return global_ret;
    1553             : }
    1554             : 
    1555             : 
    1556             : /* end of taler-exchange-httpd.c */

Generated by: LCOV version 1.14