LCOV - code coverage report
Current view: top level - auditor - taler-auditor-httpd.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 74 112 66.1 %
Date: 2021-08-30 06:43:37 Functions: 7 7 100.0 %
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             : /**
      18             :  * @file taler-auditor-httpd.c
      19             :  * @brief Serve the HTTP interface of the auditor
      20             :  * @author Florian Dold
      21             :  * @author Benedikt Mueller
      22             :  * @author Christian Grothoff
      23             :  */
      24             : #include "platform.h"
      25             : #include <gnunet/gnunet_util_lib.h>
      26             : #include <jansson.h>
      27             : #include <microhttpd.h>
      28             : #include <pthread.h>
      29             : #include <sys/resource.h>
      30             : #include "taler_mhd_lib.h"
      31             : #include "taler_auditordb_lib.h"
      32             : #include "taler_exchangedb_lib.h"
      33             : #include "taler-auditor-httpd_deposit-confirmation.h"
      34             : #include "taler-auditor-httpd_exchanges.h"
      35             : #include "taler-auditor-httpd_mhd.h"
      36             : #include "taler-auditor-httpd.h"
      37             : 
      38             : /**
      39             :  * Auditor protocol version string.
      40             :  *
      41             :  * Taler protocol version in the format CURRENT:REVISION:AGE
      42             :  * as used by GNU libtool.  See
      43             :  * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
      44             :  *
      45             :  * Please be very careful when updating and follow
      46             :  * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
      47             :  * precisely.  Note that this version has NOTHING to do with the
      48             :  * release version, and the format is NOT the same that semantic
      49             :  * versioning uses either.
      50             :  */
      51             : #define AUDITOR_PROTOCOL_VERSION "0:0:0"
      52             : 
      53             : /**
      54             :  * Backlog for listen operation on unix domain sockets.
      55             :  */
      56             : #define UNIX_BACKLOG 500
      57             : 
      58             : /**
      59             :  * Should we return "Connection: close" in each response?
      60             :  */
      61             : static int auditor_connection_close;
      62             : 
      63             : /**
      64             :  * The auditor's configuration.
      65             :  */
      66             : static const struct GNUNET_CONFIGURATION_Handle *cfg;
      67             : 
      68             : /**
      69             :  * Our DB plugin.
      70             :  */
      71             : struct TALER_AUDITORDB_Plugin *TAH_plugin;
      72             : 
      73             : /**
      74             :  * Our DB plugin to talk to the *exchange* database.
      75             :  */
      76             : struct TALER_EXCHANGEDB_Plugin *TAH_eplugin;
      77             : 
      78             : /**
      79             :  * Public key of this auditor.
      80             :  */
      81             : static struct TALER_AuditorPublicKeyP auditor_pub;
      82             : 
      83             : /**
      84             :  * Default timeout in seconds for HTTP requests.
      85             :  */
      86             : static unsigned int connection_timeout = 30;
      87             : 
      88             : /**
      89             :  * Return value from main()
      90             :  */
      91             : static int global_ret;
      92             : 
      93             : /**
      94             :  * Port to run the daemon on.
      95             :  */
      96             : static uint16_t serve_port;
      97             : 
      98             : /**
      99             :  * Our currency.
     100             :  */
     101             : char *TAH_currency;
     102             : 
     103             : 
     104             : /**
     105             :  * Function called whenever MHD is done with a request.  If the
     106             :  * request was a POST, we may have stored a `struct Buffer *` in the
     107             :  * @a con_cls that might still need to be cleaned up.  Call the
     108             :  * respective function to free the memory.
     109             :  *
     110             :  * @param cls client-defined closure
     111             :  * @param connection connection handle
     112             :  * @param con_cls value as set by the last call to
     113             :  *        the #MHD_AccessHandlerCallback
     114             :  * @param toe reason for request termination
     115             :  * @see #MHD_OPTION_NOTIFY_COMPLETED
     116             :  * @ingroup request
     117             :  */
     118             : static void
     119           9 : handle_mhd_completion_callback (void *cls,
     120             :                                 struct MHD_Connection *connection,
     121             :                                 void **con_cls,
     122             :                                 enum MHD_RequestTerminationCode toe)
     123             : {
     124             :   (void) cls;
     125             :   (void) connection;
     126             :   (void) toe;
     127           9 :   if (NULL == *con_cls)
     128           9 :     return;
     129           0 :   TALER_MHD_parse_post_cleanup_callback (*con_cls);
     130           0 :   *con_cls = NULL;
     131             : }
     132             : 
     133             : 
     134             : /**
     135             :  * Handle a "/version" request.
     136             :  *
     137             :  * @param rh context of the handler
     138             :  * @param connection the MHD connection to handle
     139             :  * @param[in,out] connection_cls the connection's closure (can be updated)
     140             :  * @param upload_data upload data
     141             :  * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
     142             :  * @return MHD result code
     143             :   */
     144             : static MHD_RESULT
     145           5 : handle_version (struct TAH_RequestHandler *rh,
     146             :                 struct MHD_Connection *connection,
     147             :                 void **connection_cls,
     148             :                 const char *upload_data,
     149             :                 size_t *upload_data_size)
     150             : {
     151             :   static json_t *ver; /* we build the response only once, keep around for next query! */
     152             : 
     153             :   (void) rh;
     154             :   (void) upload_data;
     155             :   (void) upload_data_size;
     156             :   (void) connection_cls;
     157           5 :   if (NULL == ver)
     158             :   {
     159           2 :     ver = GNUNET_JSON_PACK (
     160             :       GNUNET_JSON_pack_string ("version",
     161             :                                AUDITOR_PROTOCOL_VERSION),
     162             :       GNUNET_JSON_pack_string ("currency",
     163             :                                TAH_currency),
     164             :       GNUNET_JSON_pack_data_auto ("auditor_public_key",
     165             :                                   &auditor_pub));
     166             :   }
     167           5 :   if (NULL == ver)
     168             :   {
     169           0 :     GNUNET_break (0);
     170           0 :     return MHD_NO;
     171             :   }
     172           5 :   return TALER_MHD_reply_json (connection,
     173             :                                ver,
     174             :                                MHD_HTTP_OK);
     175             : }
     176             : 
     177             : 
     178             : /**
     179             :  * Handle incoming HTTP request.
     180             :  *
     181             :  * @param cls closure for MHD daemon (unused)
     182             :  * @param connection the connection
     183             :  * @param url the requested url
     184             :  * @param method the method (POST, GET, ...)
     185             :  * @param version HTTP version (ignored)
     186             :  * @param upload_data request data
     187             :  * @param upload_data_size size of @a upload_data in bytes
     188             :  * @param con_cls closure for request (a `struct Buffer *`)
     189             :  * @return MHD result code
     190             :  */
     191             : static MHD_RESULT
     192          11 : handle_mhd_request (void *cls,
     193             :                     struct MHD_Connection *connection,
     194             :                     const char *url,
     195             :                     const char *method,
     196             :                     const char *version,
     197             :                     const char *upload_data,
     198             :                     size_t *upload_data_size,
     199             :                     void **con_cls)
     200             : {
     201             :   static struct TAH_RequestHandler handlers[] = {
     202             :     /* Our most popular handler (thus first!), used by merchants to
     203             :        probabilistically report us their deposit confirmations. */
     204             :     { "/deposit-confirmation", MHD_HTTP_METHOD_PUT, "application/json",
     205             :       NULL, 0,
     206             :       &TAH_DEPOSIT_CONFIRMATION_handler, MHD_HTTP_OK },
     207             :     { "/exchanges", MHD_HTTP_METHOD_GET, "application/json",
     208             :       NULL, 0,
     209             :       &TAH_EXCHANGES_handler, MHD_HTTP_OK },
     210             :     { "/version", MHD_HTTP_METHOD_GET, "application/json",
     211             :       NULL, 0,
     212             :       &handle_version, MHD_HTTP_OK },
     213             :     /* Landing page, for now tells humans to go away
     214             :      * (NOTE: ideally, the reverse proxy will respond with a nicer page) */
     215             :     { "/", MHD_HTTP_METHOD_GET, "text/plain",
     216             :       "Hello, I'm the Taler auditor. This HTTP server is not for humans.\n", 0,
     217             :       &TAH_MHD_handler_static_response, MHD_HTTP_OK },
     218             :     /* /robots.txt: disallow everything */
     219             :     { "/robots.txt", MHD_HTTP_METHOD_GET, "text/plain",
     220             :       "User-agent: *\nDisallow: /\n", 0,
     221             :       &TAH_MHD_handler_static_response, MHD_HTTP_OK },
     222             :     /* AGPL licensing page, redirect to source. As per the AGPL-license,
     223             :        every deployment is required to offer the user a download of the
     224             :        source. We make this easy by including a redirect t the source
     225             :        here. */
     226             :     { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
     227             :       NULL, 0,
     228             :       &TAH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
     229             :     { NULL, NULL, NULL, NULL, 0, NULL, 0 }
     230             :   };
     231             : 
     232             :   (void) cls;
     233             :   (void) version;
     234          11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     235             :               "Handling request for URL '%s'\n",
     236             :               url);
     237          11 :   if (0 == strcasecmp (method,
     238             :                        MHD_HTTP_METHOD_HEAD))
     239           0 :     method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
     240          28 :   for (unsigned int i = 0; NULL != handlers[i].url; i++)
     241             :   {
     242          28 :     struct TAH_RequestHandler *rh = &handlers[i];
     243             : 
     244          28 :     if ( (0 == strcasecmp (url,
     245          11 :                            rh->url)) &&
     246          11 :          ( (NULL == rh->method) ||
     247          11 :            (0 == strcasecmp (method,
     248             :                              rh->method)) ) )
     249          11 :       return rh->handler (rh,
     250             :                           connection,
     251             :                           con_cls,
     252             :                           upload_data,
     253             :                           upload_data_size);
     254             :   }
     255             : #define NOT_FOUND "<html><title>404: not found</title></html>"
     256           0 :   return TALER_MHD_reply_static (connection,
     257             :                                  MHD_HTTP_NOT_FOUND,
     258             :                                  "text/html",
     259             :                                  NOT_FOUND,
     260             :                                  strlen (NOT_FOUND));
     261             : #undef NOT_FOUND
     262             : }
     263             : 
     264             : 
     265             : /**
     266             :  * Load configuration parameters for the auditor
     267             :  * server into the corresponding global variables.
     268             :  *
     269             :  * @return #GNUNET_OK on success
     270             :  */
     271             : static enum GNUNET_GenericReturnValue
     272           4 : auditor_serve_process_config (void)
     273             : {
     274           4 :   if (NULL ==
     275           4 :       (TAH_plugin = TALER_AUDITORDB_plugin_load (cfg)))
     276             :   {
     277           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     278             :                 "Failed to initialize DB subsystem to interact with auditor database\n");
     279           0 :     return GNUNET_SYSERR;
     280             :   }
     281           4 :   if (NULL ==
     282           4 :       (TAH_eplugin = TALER_EXCHANGEDB_plugin_load (cfg)))
     283             :   {
     284           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     285             :                 "Failed to initialize DB subsystem to query exchange database\n");
     286           0 :     return GNUNET_SYSERR;
     287             :   }
     288           4 :   if (GNUNET_SYSERR ==
     289           4 :       TAH_eplugin->preflight (TAH_eplugin->cls))
     290             :   {
     291           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     292             :                 "Failed to initialize DB subsystem to query exchange database\n");
     293           0 :     return GNUNET_SYSERR;
     294             :   }
     295           4 :   if (GNUNET_OK !=
     296           4 :       TALER_config_get_currency (cfg,
     297             :                                  &TAH_currency))
     298             :   {
     299           0 :     return GNUNET_SYSERR;
     300             :   }
     301             :   {
     302             :     char *pub;
     303             : 
     304           4 :     if (GNUNET_OK ==
     305           4 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     306             :                                                "AUDITOR",
     307             :                                                "PUBLIC_KEY",
     308             :                                                &pub))
     309             :     {
     310           0 :       if (GNUNET_OK !=
     311           0 :           GNUNET_CRYPTO_eddsa_public_key_from_string (pub,
     312             :                                                       strlen (pub),
     313             :                                                       &auditor_pub.eddsa_pub))
     314             :       {
     315           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     316             :                     "Invalid public key given in auditor configuration.");
     317           0 :         GNUNET_free (pub);
     318           0 :         return GNUNET_SYSERR;
     319             :       }
     320           0 :       GNUNET_free (pub);
     321           0 :       return GNUNET_OK;
     322             :     }
     323             :   }
     324             : 
     325             :   {
     326             :     /* Fall back to trying to read private key */
     327             :     char *auditor_key_file;
     328             :     struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
     329             : 
     330           4 :     if (GNUNET_OK !=
     331           4 :         GNUNET_CONFIGURATION_get_value_filename (cfg,
     332             :                                                  "auditor",
     333             :                                                  "AUDITOR_PRIV_FILE",
     334             :                                                  &auditor_key_file))
     335             :     {
     336           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     337             :                                  "AUDITOR",
     338             :                                  "PUBLIC_KEY");
     339           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     340             :                                  "AUDITOR",
     341             :                                  "AUDITOR_PRIV_FILE");
     342           0 :       return GNUNET_SYSERR;
     343             :     }
     344           4 :     if (GNUNET_OK !=
     345           4 :         GNUNET_CRYPTO_eddsa_key_from_file (auditor_key_file,
     346             :                                            GNUNET_NO,
     347             :                                            &eddsa_priv))
     348             :     {
     349             :       /* Both failed, complain! */
     350           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     351             :                                  "AUDITOR",
     352             :                                  "PUBLIC_KEY");
     353           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     354             :                   "Failed to initialize auditor key from file `%s'\n",
     355             :                   auditor_key_file);
     356           0 :       GNUNET_free (auditor_key_file);
     357           0 :       return 1;
     358             :     }
     359           4 :     GNUNET_free (auditor_key_file);
     360           4 :     GNUNET_CRYPTO_eddsa_key_get_public (&eddsa_priv,
     361             :                                         &auditor_pub.eddsa_pub);
     362             :   }
     363           4 :   return GNUNET_OK;
     364             : }
     365             : 
     366             : 
     367             : /**
     368             :  * Function run on shutdown.
     369             :  *
     370             :  * @param cls NULL
     371             :  */
     372             : static void
     373           4 : do_shutdown (void *cls)
     374             : {
     375             :   struct MHD_Daemon *mhd;
     376             :   (void) cls;
     377             : 
     378           4 :   mhd = TALER_MHD_daemon_stop ();
     379           4 :   TEAH_DEPOSIT_CONFIRMATION_done ();
     380           4 :   if (NULL != mhd)
     381           4 :     MHD_stop_daemon (mhd);
     382           4 :   if (NULL != TAH_plugin)
     383             :   {
     384           4 :     TALER_AUDITORDB_plugin_unload (TAH_plugin);
     385           4 :     TAH_plugin = NULL;
     386             :   }
     387           4 :   if (NULL != TAH_eplugin)
     388             :   {
     389           4 :     TALER_EXCHANGEDB_plugin_unload (TAH_eplugin);
     390           4 :     TAH_eplugin = NULL;
     391             :   }
     392           4 : }
     393             : 
     394             : 
     395             : /**
     396             :  * Main function that will be run by the scheduler.
     397             :  *
     398             :  * @param cls closure
     399             :  * @param args remaining command-line arguments
     400             :  * @param cfgfile name of the configuration file used (for saving, can be
     401             :  *        NULL!)
     402             :  * @param config configuration
     403             :  */
     404             : static void
     405           4 : run (void *cls,
     406             :      char *const *args,
     407             :      const char *cfgfile,
     408             :      const struct GNUNET_CONFIGURATION_Handle *config)
     409             : {
     410             :   enum TALER_MHD_GlobalOptions go;
     411             :   int fh;
     412             : 
     413           4 :   go = TALER_MHD_GO_NONE;
     414           4 :   if (auditor_connection_close)
     415           0 :     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
     416           4 :   TALER_MHD_setup (go);
     417           4 :   cfg = config;
     418             : 
     419           4 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
     420             :                                  NULL);
     421           4 :   if (GNUNET_OK !=
     422           4 :       auditor_serve_process_config ())
     423             :   {
     424           0 :     global_ret = EXIT_NOTCONFIGURED;
     425           0 :     GNUNET_SCHEDULER_shutdown ();
     426           0 :     return;
     427             :   }
     428           4 :   TEAH_DEPOSIT_CONFIRMATION_init ();
     429           4 :   fh = TALER_MHD_bind (cfg,
     430             :                        "auditor",
     431             :                        &serve_port);
     432           4 :   if ( (0 == serve_port) &&
     433             :        (-1 == fh) )
     434             :   {
     435           0 :     GNUNET_SCHEDULER_shutdown ();
     436           0 :     return;
     437             :   }
     438             :   {
     439             :     struct MHD_Daemon *mhd;
     440             : 
     441           4 :     mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
     442             :                             | MHD_USE_PIPE_FOR_SHUTDOWN
     443             :                             | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
     444             :                             | MHD_USE_TCP_FASTOPEN,
     445             :                             (-1 == fh) ? serve_port : 0,
     446             :                             NULL, NULL,
     447             :                             &handle_mhd_request, NULL,
     448             :                             MHD_OPTION_LISTEN_BACKLOG_SIZE,
     449             :                             (unsigned int) 1024,
     450             :                             MHD_OPTION_LISTEN_SOCKET,
     451             :                             fh,
     452             :                             MHD_OPTION_EXTERNAL_LOGGER,
     453             :                             &TALER_MHD_handle_logs,
     454             :                             NULL,
     455             :                             MHD_OPTION_NOTIFY_COMPLETED,
     456             :                             &handle_mhd_completion_callback,
     457             :                             NULL,
     458             :                             MHD_OPTION_CONNECTION_TIMEOUT,
     459             :                             connection_timeout,
     460             :                             MHD_OPTION_END);
     461           4 :     if (NULL == mhd)
     462             :     {
     463           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     464             :                   "Failed to launch HTTP service. Is the port in use?\n");
     465           0 :       GNUNET_SCHEDULER_shutdown ();
     466           0 :       return;
     467             :     }
     468           4 :     global_ret = EXIT_SUCCESS;
     469           4 :     TALER_MHD_daemon_start (mhd);
     470             :   }
     471             : }
     472             : 
     473             : 
     474             : /**
     475             :  * The main function of the taler-auditor-httpd server ("the auditor").
     476             :  *
     477             :  * @param argc number of arguments from the command line
     478             :  * @param argv command line arguments
     479             :  * @return 0 ok, 1 on error
     480             :  */
     481             : int
     482           4 : main (int argc,
     483             :       char *const *argv)
     484             : {
     485           4 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
     486           4 :     GNUNET_GETOPT_option_flag ('C',
     487             :                                "connection-close",
     488             :                                "force HTTP connections to be closed after each request",
     489             :                                &auditor_connection_close),
     490           4 :     GNUNET_GETOPT_option_uint ('t',
     491             :                                "timeout",
     492             :                                "SECONDS",
     493             :                                "after how long do connections timeout by default (in seconds)",
     494             :                                &connection_timeout),
     495           4 :     GNUNET_GETOPT_option_help (
     496             :       "HTTP server providing a RESTful API to access a Taler auditor"),
     497           4 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
     498             :     GNUNET_GETOPT_OPTION_END
     499             :   };
     500             :   int ret;
     501             : 
     502           4 :   TALER_OS_init ();
     503           4 :   ret = GNUNET_PROGRAM_run (argc, argv,
     504             :                             "taler-auditor-httpd",
     505             :                             "Taler auditor HTTP service",
     506             :                             options,
     507             :                             &run, NULL);
     508           4 :   if (GNUNET_SYSERR == ret)
     509           0 :     return EXIT_INVALIDARGUMENT;
     510           4 :   if (GNUNET_NO == ret)
     511           0 :     return EXIT_SUCCESS;
     512           4 :   return global_ret;
     513             : }
     514             : 
     515             : 
     516             : /* end of taler-auditor-httpd.c */

Generated by: LCOV version 1.14