LCOV - code coverage report
Current view: top level - bank-lib - fakebank.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 77 110 70.0 %
Date: 2025-06-22 12:09:43 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2016-2024 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or
       6             :   modify it under the terms of the GNU General Public License
       7             :   as published by the Free Software Foundation; either version 3,
       8             :   or (at your option) any later version.
       9             : 
      10             :   TALER is distributed in the hope that it will be useful,
      11             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :   GNU General Public License for more details.
      14             : 
      15             :   You should have received a copy of the GNU General Public
      16             :   License along with TALER; see the file COPYING.  If not,
      17             :   see <http://www.gnu.org/licenses/>
      18             : */
      19             : /**
      20             :  * @file bank-lib/fakebank.c
      21             :  * @brief library that fakes being a Taler bank for testcases
      22             :  * @author Christian Grothoff <christian@grothoff.org>
      23             :  * @defgroup request Request handling routines
      24             :  */
      25             : #include "taler/platform.h"
      26             : #include <pthread.h>
      27             : #include <poll.h>
      28             : #ifdef __linux__
      29             : #include <sys/eventfd.h>
      30             : #endif
      31             : #include "taler/taler_fakebank_lib.h"
      32             : #include "taler/taler_bank_service.h"
      33             : #include "taler/taler_mhd_lib.h"
      34             : #include <gnunet/gnunet_mhd_compat.h>
      35             : #include "fakebank.h"
      36             : #include "fakebank_bank.h"
      37             : #include "fakebank_common_lp.h"
      38             : #include "fakebank_tbi.h"
      39             : 
      40             : 
      41             : /**
      42             :  * Function called whenever MHD is done with a request.  If the
      43             :  * request was a POST, we may have stored a `struct Buffer *` in the
      44             :  * @a con_cls that might still need to be cleaned up.  Call the
      45             :  * respective function to free the memory.
      46             :  *
      47             :  * @param cls a `struct TALER_FAKEBANK_Handle *`
      48             :  * @param connection connection handle
      49             :  * @param con_cls a `struct ConnectionContext *`
      50             :  *        the #MHD_AccessHandlerCallback
      51             :  * @param toe reason for request termination
      52             :  * @see #MHD_OPTION_NOTIFY_COMPLETED
      53             :  * @ingroup request
      54             :  */
      55             : static void
      56         310 : handle_mhd_completion_callback (void *cls,
      57             :                                 struct MHD_Connection *connection,
      58             :                                 void **con_cls,
      59             :                                 enum MHD_RequestTerminationCode toe)
      60             : {
      61         310 :   struct TALER_FAKEBANK_Handle *h = cls;
      62         310 :   struct ConnectionContext *cc = *con_cls;
      63             : 
      64             :   (void) h;
      65             :   (void) connection;
      66             :   (void) toe;
      67         310 :   if (NULL == cc)
      68           2 :     return;
      69         308 :   cc->ctx_cleaner (cc->ctx);
      70         308 :   GNUNET_free (cc);
      71             : }
      72             : 
      73             : 
      74             : /**
      75             :  * Handle incoming HTTP request.
      76             :  *
      77             :  * @param cls a `struct TALER_FAKEBANK_Handle`
      78             :  * @param connection the connection
      79             :  * @param url the requested url
      80             :  * @param method the method (POST, GET, ...)
      81             :  * @param version HTTP version (ignored)
      82             :  * @param upload_data request data
      83             :  * @param upload_data_size size of @a upload_data in bytes
      84             :  * @param con_cls closure for request
      85             :  * @return MHD result code
      86             :  */
      87             : static MHD_RESULT
      88         580 : handle_mhd_request (void *cls,
      89             :                     struct MHD_Connection *connection,
      90             :                     const char *url,
      91             :                     const char *method,
      92             :                     const char *version,
      93             :                     const char *upload_data,
      94             :                     size_t *upload_data_size,
      95             :                     void **con_cls)
      96             : {
      97         580 :   struct TALER_FAKEBANK_Handle *h = cls;
      98             : 
      99             :   (void) version;
     100         580 :   if (0 == strncmp (url,
     101             :                     "/taler-integration/",
     102             :                     strlen ("/taler-integration/")))
     103             :   {
     104           0 :     url += strlen ("/taler-integration");
     105           0 :     return TALER_FAKEBANK_tbi_main_ (h,
     106             :                                      connection,
     107             :                                      url,
     108             :                                      method,
     109             :                                      upload_data,
     110             :                                      upload_data_size,
     111             :                                      con_cls);
     112             :   }
     113         580 :   return TALER_FAKEBANK_bank_main_ (h,
     114             :                                     connection,
     115             :                                     url,
     116             :                                     method,
     117             :                                     upload_data,
     118             :                                     upload_data_size,
     119             :                                     con_cls);
     120             : }
     121             : 
     122             : 
     123             : #if EPOLL_SUPPORT
     124             : /**
     125             :  * Schedule MHD.  This function should be called initially when an
     126             :  * MHD is first getting its client socket, and will then automatically
     127             :  * always be called later whenever there is work to be done.
     128             :  *
     129             :  * @param h fakebank handle to schedule MHD for
     130             :  */
     131             : static void
     132          13 : schedule_httpd (struct TALER_FAKEBANK_Handle *h)
     133             : {
     134             :   int haveto;
     135             :   MHD_UNSIGNED_LONG_LONG timeout;
     136             :   struct GNUNET_TIME_Relative tv;
     137             : 
     138          13 :   GNUNET_assert (-1 != h->mhd_fd);
     139          13 :   haveto = MHD_get_timeout (h->mhd_bank,
     140             :                             &timeout);
     141          13 :   if (MHD_YES == haveto)
     142           0 :     tv.rel_value_us = (uint64_t) timeout * 1000LL;
     143             :   else
     144          13 :     tv = GNUNET_TIME_UNIT_FOREVER_REL;
     145          13 :   if (NULL != h->mhd_task)
     146           0 :     GNUNET_SCHEDULER_cancel (h->mhd_task);
     147          13 :   h->mhd_task =
     148          13 :     GNUNET_SCHEDULER_add_read_net (tv,
     149             :                                    h->mhd_rfd,
     150             :                                    &TALER_FAKEBANK_run_mhd_,
     151             :                                    h);
     152          13 : }
     153             : 
     154             : 
     155             : #else
     156             : /**
     157             :  * Schedule MHD.  This function should be called initially when an
     158             :  * MHD is first getting its client socket, and will then automatically
     159             :  * always be called later whenever there is work to be done.
     160             :  *
     161             :  * @param h fakebank handle to schedule MHD for
     162             :  */
     163             : static void
     164             : schedule_httpd (struct TALER_FAKEBANK_Handle *h)
     165             : {
     166             :   fd_set rs;
     167             :   fd_set ws;
     168             :   fd_set es;
     169             :   struct GNUNET_NETWORK_FDSet *wrs;
     170             :   struct GNUNET_NETWORK_FDSet *wws;
     171             :   int max;
     172             :   int haveto;
     173             :   MHD_UNSIGNED_LONG_LONG timeout;
     174             :   struct GNUNET_TIME_Relative tv;
     175             : 
     176             : #ifdef __linux__
     177             :   GNUNET_assert (-1 == h->lp_event);
     178             : #else
     179             :   GNUNET_assert (-1 == h->lp_event_in);
     180             :   GNUNET_assert (-1 == h->lp_event_out);
     181             : #endif
     182             :   FD_ZERO (&rs);
     183             :   FD_ZERO (&ws);
     184             :   FD_ZERO (&es);
     185             :   max = -1;
     186             :   if (MHD_YES != MHD_get_fdset (h->mhd_bank,
     187             :                                 &rs,
     188             :                                 &ws,
     189             :                                 &es,
     190             :                                 &max))
     191             :   {
     192             :     GNUNET_assert (0);
     193             :     return;
     194             :   }
     195             :   haveto = MHD_get_timeout (h->mhd_bank,
     196             :                             &timeout);
     197             :   if (MHD_YES == haveto)
     198             :     tv.rel_value_us = (uint64_t) timeout * 1000LL;
     199             :   else
     200             :     tv = GNUNET_TIME_UNIT_FOREVER_REL;
     201             :   if (-1 != max)
     202             :   {
     203             :     wrs = GNUNET_NETWORK_fdset_create ();
     204             :     wws = GNUNET_NETWORK_fdset_create ();
     205             :     GNUNET_NETWORK_fdset_copy_native (wrs,
     206             :                                       &rs,
     207             :                                       max + 1);
     208             :     GNUNET_NETWORK_fdset_copy_native (wws,
     209             :                                       &ws,
     210             :                                       max + 1);
     211             :   }
     212             :   else
     213             :   {
     214             :     wrs = NULL;
     215             :     wws = NULL;
     216             :   }
     217             :   if (NULL != h->mhd_task)
     218             :     GNUNET_SCHEDULER_cancel (h->mhd_task);
     219             :   h->mhd_task =
     220             :     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
     221             :                                  tv,
     222             :                                  wrs,
     223             :                                  wws,
     224             :                                  &TALER_FAKEBANK_run_mhd_,
     225             :                                  h);
     226             :   if (NULL != wrs)
     227             :     GNUNET_NETWORK_fdset_destroy (wrs);
     228             :   if (NULL != wws)
     229             :     GNUNET_NETWORK_fdset_destroy (wws);
     230             : }
     231             : 
     232             : 
     233             : #endif
     234             : 
     235             : 
     236             : /**
     237             :  * Task run whenever HTTP server operations are pending.
     238             :  *
     239             :  * @param cls the `struct TALER_FAKEBANK_Handle`
     240             :  */
     241             : void
     242          12 : TALER_FAKEBANK_run_mhd_ (void *cls)
     243             : {
     244          12 :   struct TALER_FAKEBANK_Handle *h = cls;
     245             : 
     246          12 :   h->mhd_task = NULL;
     247          12 :   h->mhd_again = true;
     248          24 :   while (h->mhd_again)
     249             :   {
     250          12 :     h->mhd_again = false;
     251          12 :     GNUNET_assert (MHD_YES ==
     252             :                    MHD_run (h->mhd_bank));
     253             :   }
     254             : #ifdef __linux__
     255          12 :   GNUNET_assert (-1 == h->lp_event);
     256             : #else
     257             :   GNUNET_assert (-1 == h->lp_event_in);
     258             :   GNUNET_assert (-1 == h->lp_event_out);
     259             : #endif
     260          12 :   schedule_httpd (h);
     261          12 : }
     262             : 
     263             : 
     264             : struct TALER_FAKEBANK_Handle *
     265          13 : TALER_FAKEBANK_start (uint16_t port,
     266             :                       const char *currency)
     267             : {
     268          13 :   return TALER_FAKEBANK_start2 (port,
     269             :                                 currency,
     270             :                                 65536, /* RAM limit */
     271             :                                 1);
     272             : }
     273             : 
     274             : 
     275             : struct TALER_FAKEBANK_Handle *
     276          13 : TALER_FAKEBANK_start2 (uint16_t port,
     277             :                        const char *currency,
     278             :                        uint64_t ram_limit,
     279             :                        unsigned int num_threads)
     280             : {
     281             :   struct TALER_Amount zero;
     282             : 
     283          13 :   if (GNUNET_OK !=
     284          13 :       TALER_amount_set_zero (currency,
     285             :                              &zero))
     286             :   {
     287           0 :     GNUNET_break (0);
     288           0 :     return NULL;
     289             :   }
     290          13 :   return TALER_FAKEBANK_start3 ("localhost",
     291             :                                 port,
     292             :                                 NULL,
     293             :                                 currency,
     294             :                                 ram_limit,
     295             :                                 num_threads,
     296             :                                 &zero);
     297             : }
     298             : 
     299             : 
     300             : struct TALER_FAKEBANK_Handle *
     301          15 : TALER_FAKEBANK_start3 (const char *hostname,
     302             :                        uint16_t port,
     303             :                        const char *exchange_url,
     304             :                        const char *currency,
     305             :                        uint64_t ram_limit,
     306             :                        unsigned int num_threads,
     307             :                        const struct TALER_Amount *signup_bonus)
     308             : {
     309             :   struct TALER_FAKEBANK_Handle *h;
     310             : 
     311          15 :   if (SIZE_MAX / sizeof (struct Transaction *) < ram_limit)
     312             :   {
     313           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     314             :                 "This CPU architecture does not support keeping %llu transactions in RAM\n",
     315             :                 (unsigned long long) ram_limit);
     316           0 :     return NULL;
     317             :   }
     318          15 :   GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
     319          15 :   if (0 != strcmp (signup_bonus->currency,
     320             :                    currency))
     321             :   {
     322           0 :     GNUNET_break (0);
     323           0 :     return NULL;
     324             :   }
     325          15 :   h = GNUNET_new (struct TALER_FAKEBANK_Handle);
     326          15 :   h->signup_bonus = *signup_bonus;
     327          15 :   if (NULL != exchange_url)
     328           0 :     h->exchange_url = GNUNET_strdup (exchange_url);
     329             : #ifdef __linux__
     330          15 :   h->lp_event = -1;
     331             : #else
     332             :   h->lp_event_in = -1;
     333             :   h->lp_event_out = -1;
     334             : #endif
     335             : #if EPOLL_SUPPORT
     336          15 :   h->mhd_fd = -1;
     337             : #endif
     338          15 :   h->port = port;
     339          15 :   h->ram_limit = ram_limit;
     340          15 :   h->serial_counter = 0;
     341          15 :   GNUNET_assert (0 ==
     342             :                  pthread_mutex_init (&h->accounts_lock,
     343             :                                      NULL));
     344          15 :   GNUNET_assert (0 ==
     345             :                  pthread_mutex_init (&h->rpubs_lock,
     346             :                                      NULL));
     347          15 :   GNUNET_assert (0 ==
     348             :                  pthread_mutex_init (&h->uuid_map_lock,
     349             :                                      NULL));
     350          15 :   GNUNET_assert (0 ==
     351             :                  pthread_mutex_init (&h->big_lock,
     352             :                                      NULL));
     353             :   h->transactions
     354          15 :     = GNUNET_malloc_large (sizeof (struct Transaction *)
     355             :                            * ram_limit);
     356          15 :   if (NULL == h->transactions)
     357             :   {
     358           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     359             :                          "malloc");
     360           0 :     TALER_FAKEBANK_stop (h);
     361           0 :     return NULL;
     362             :   }
     363          15 :   h->accounts = GNUNET_CONTAINER_multihashmap_create (128,
     364             :                                                       GNUNET_NO);
     365          15 :   h->uuid_map = GNUNET_CONTAINER_multihashmap_create (ram_limit * 4 / 3,
     366             :                                                       GNUNET_YES);
     367          15 :   if (NULL == h->uuid_map)
     368             :   {
     369           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     370             :                          "malloc");
     371           0 :     TALER_FAKEBANK_stop (h);
     372           0 :     return NULL;
     373             :   }
     374          15 :   h->rpubs = GNUNET_CONTAINER_multipeermap_create (ram_limit * 4 / 3,
     375             :                                                    GNUNET_NO);
     376          15 :   if (NULL == h->rpubs)
     377             :   {
     378           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     379             :                          "malloc");
     380           0 :     TALER_FAKEBANK_stop (h);
     381           0 :     return NULL;
     382             :   }
     383          15 :   h->lp_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
     384          15 :   h->currency = GNUNET_strdup (currency);
     385          15 :   h->hostname = GNUNET_strdup (hostname);
     386          15 :   GNUNET_asprintf (&h->my_baseurl,
     387             :                    "http://%s:%u/",
     388             :                    h->hostname,
     389             :                    (unsigned int) port);
     390          15 :   if (0 == num_threads)
     391             :   {
     392           1 :     h->mhd_bank = MHD_start_daemon (
     393             :       MHD_USE_DEBUG
     394             : #if EPOLL_SUPPORT
     395             :       | MHD_USE_EPOLL
     396             : #endif
     397             :       | MHD_USE_DUAL_STACK
     398             :       | MHD_ALLOW_SUSPEND_RESUME,
     399             :       port,
     400             :       NULL, NULL,
     401             :       &handle_mhd_request, h,
     402             :       MHD_OPTION_NOTIFY_COMPLETED,
     403             :       &handle_mhd_completion_callback, h,
     404             :       MHD_OPTION_LISTEN_BACKLOG_SIZE,
     405             :       (unsigned int) 1024,
     406             :       MHD_OPTION_CONNECTION_LIMIT,
     407             :       (unsigned int) 65536,
     408             :       MHD_OPTION_END);
     409           1 :     if (NULL == h->mhd_bank)
     410             :     {
     411           0 :       TALER_FAKEBANK_stop (h);
     412           0 :       return NULL;
     413             :     }
     414             : #if EPOLL_SUPPORT
     415           1 :     h->mhd_fd = MHD_get_daemon_info (h->mhd_bank,
     416           1 :                                      MHD_DAEMON_INFO_EPOLL_FD)->epoll_fd;
     417           1 :     h->mhd_rfd = GNUNET_NETWORK_socket_box_native (h->mhd_fd);
     418             : #endif
     419           1 :     schedule_httpd (h);
     420             :   }
     421             :   else
     422             :   {
     423             : #ifdef __linux__
     424          14 :     h->lp_event = eventfd (0,
     425             :                            EFD_CLOEXEC);
     426          14 :     if (-1 == h->lp_event)
     427             :     {
     428           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     429             :                            "eventfd");
     430           0 :       TALER_FAKEBANK_stop (h);
     431           0 :       return NULL;
     432             :     }
     433             : #else
     434             :     {
     435             :       int pipefd[2];
     436             : 
     437             :       if (0 != pipe (pipefd))
     438             :       {
     439             :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     440             :                              "pipe");
     441             :         TALER_FAKEBANK_stop (h);
     442             :         return NULL;
     443             :       }
     444             :       h->lp_event_out = pipefd[0];
     445             :       h->lp_event_in = pipefd[1];
     446             :     }
     447             : #endif
     448          14 :     if (0 !=
     449          14 :         pthread_create (&h->lp_thread,
     450             :                         NULL,
     451             :                         &TALER_FAKEBANK_lp_expiration_thread_,
     452             :                         h))
     453             :     {
     454           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     455             :                            "pthread_create");
     456             : #ifdef __linux__
     457           0 :       GNUNET_break (0 == close (h->lp_event));
     458           0 :       h->lp_event = -1;
     459             : #else
     460             :       GNUNET_break (0 == close (h->lp_event_in));
     461             :       GNUNET_break (0 == close (h->lp_event_out));
     462             :       h->lp_event_in = -1;
     463             :       h->lp_event_out = -1;
     464             : #endif
     465           0 :       TALER_FAKEBANK_stop (h);
     466           0 :       return NULL;
     467             :     }
     468          14 :     h->mhd_bank = MHD_start_daemon (
     469             :       MHD_USE_DEBUG
     470             :       | MHD_USE_AUTO_INTERNAL_THREAD
     471             :       | MHD_ALLOW_SUSPEND_RESUME
     472             :       | MHD_USE_TURBO
     473             :       | MHD_USE_TCP_FASTOPEN
     474             :       | MHD_USE_DUAL_STACK,
     475             :       port,
     476             :       NULL, NULL,
     477             :       &handle_mhd_request, h,
     478             :       MHD_OPTION_NOTIFY_COMPLETED,
     479             :       &handle_mhd_completion_callback, h,
     480             :       MHD_OPTION_LISTEN_BACKLOG_SIZE,
     481             :       (unsigned int) 1024,
     482             :       MHD_OPTION_CONNECTION_LIMIT,
     483             :       (unsigned int) 65536,
     484             :       MHD_OPTION_THREAD_POOL_SIZE,
     485             :       num_threads,
     486             :       MHD_OPTION_END);
     487          14 :     if (NULL == h->mhd_bank)
     488             :     {
     489           0 :       GNUNET_break (0);
     490           0 :       TALER_FAKEBANK_stop (h);
     491           0 :       return NULL;
     492             :     }
     493             :   }
     494          15 :   return h;
     495             : }
     496             : 
     497             : 
     498             : /* end of fakebank.c */

Generated by: LCOV version 1.16