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

            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          326 : handle_mhd_completion_callback (void *cls,
      57              :                                 struct MHD_Connection *connection,
      58              :                                 void **con_cls,
      59              :                                 enum MHD_RequestTerminationCode toe)
      60              : {
      61          326 :   struct TALER_FAKEBANK_Handle *h = cls;
      62          326 :   struct ConnectionContext *cc = *con_cls;
      63              : 
      64              :   (void) h;
      65              :   (void) connection;
      66              :   (void) toe;
      67          326 :   if (NULL == cc)
      68            3 :     return;
      69          323 :   cc->ctx_cleaner (cc->ctx);
      70          323 :   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          618 : 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          618 :   struct TALER_FAKEBANK_Handle *h = cls;
      98              : 
      99              :   (void) version;
     100          618 :   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          618 :   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           14 : 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           14 :   GNUNET_assert (-1 != h->mhd_fd);
     139           14 :   haveto = MHD_get_timeout (h->mhd_bank,
     140              :                             &timeout);
     141           14 :   if (MHD_YES == haveto)
     142            0 :     tv.rel_value_us = (uint64_t) timeout * 1000LL;
     143              :   else
     144           14 :     tv = GNUNET_TIME_UNIT_FOREVER_REL;
     145           14 :   if (NULL != h->mhd_task)
     146            0 :     GNUNET_SCHEDULER_cancel (h->mhd_task);
     147           14 :   h->mhd_task =
     148           14 :     GNUNET_SCHEDULER_add_read_net (tv,
     149              :                                    h->mhd_rfd,
     150              :                                    &TALER_FAKEBANK_run_mhd_,
     151              :                                    h);
     152           14 : }
     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           13 : TALER_FAKEBANK_run_mhd_ (void *cls)
     243              : {
     244           13 :   struct TALER_FAKEBANK_Handle *h = cls;
     245              : 
     246           13 :   h->mhd_task = NULL;
     247           13 :   h->mhd_again = true;
     248           26 :   while (h->mhd_again)
     249              :   {
     250           13 :     h->mhd_again = false;
     251           13 :     GNUNET_assert (MHD_YES ==
     252              :                    MHD_run (h->mhd_bank));
     253              :   }
     254              : #ifdef __linux__
     255           13 :   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           13 :   schedule_httpd (h);
     261           13 : }
     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           16 : 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           16 :   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           16 :   GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
     319           16 :   if (0 != strcmp (signup_bonus->currency,
     320              :                    currency))
     321              :   {
     322            0 :     GNUNET_break (0);
     323            0 :     return NULL;
     324              :   }
     325           16 :   h = GNUNET_new (struct TALER_FAKEBANK_Handle);
     326           16 :   h->signup_bonus = *signup_bonus;
     327           16 :   if (NULL != exchange_url)
     328            0 :     h->exchange_url = GNUNET_strdup (exchange_url);
     329              : #ifdef __linux__
     330           16 :   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           16 :   h->mhd_fd = -1;
     337              : #endif
     338           16 :   h->port = port;
     339           16 :   h->ram_limit = ram_limit;
     340           16 :   h->serial_counter = 0;
     341           16 :   GNUNET_assert (0 ==
     342              :                  pthread_mutex_init (&h->accounts_lock,
     343              :                                      NULL));
     344           16 :   GNUNET_assert (0 ==
     345              :                  pthread_mutex_init (&h->rpubs_lock,
     346              :                                      NULL));
     347           16 :   GNUNET_assert (0 ==
     348              :                  pthread_mutex_init (&h->uuid_map_lock,
     349              :                                      NULL));
     350           16 :   GNUNET_assert (0 ==
     351              :                  pthread_mutex_init (&h->big_lock,
     352              :                                      NULL));
     353              :   h->transactions
     354           16 :     = GNUNET_malloc_large (sizeof (struct Transaction *)
     355              :                            * ram_limit);
     356           16 :   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           16 :   h->accounts = GNUNET_CONTAINER_multihashmap_create (128,
     364              :                                                       GNUNET_NO);
     365           16 :   h->uuid_map = GNUNET_CONTAINER_multihashmap_create (ram_limit * 4 / 3,
     366              :                                                       GNUNET_YES);
     367           16 :   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           16 :   h->rpubs = GNUNET_CONTAINER_multipeermap_create (ram_limit * 4 / 3,
     375              :                                                    GNUNET_NO);
     376           16 :   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           16 :   h->lp_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
     384           16 :   h->currency = GNUNET_strdup (currency);
     385           16 :   h->hostname = GNUNET_strdup (hostname);
     386           16 :   GNUNET_asprintf (&h->my_baseurl,
     387              :                    "http://%s:%u/",
     388              :                    h->hostname,
     389              :                    (unsigned int) port);
     390           16 :   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           15 :     h->lp_event = eventfd (0,
     425              :                            EFD_CLOEXEC);
     426           15 :     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           15 :     if (0 !=
     449           15 :         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           15 :     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           15 :     if (NULL == h->mhd_bank)
     488              :     {
     489            0 :       GNUNET_break (0);
     490            0 :       TALER_FAKEBANK_stop (h);
     491            0 :       return NULL;
     492              :     }
     493              :   }
     494           16 :   return h;
     495              : }
     496              : 
     497              : 
     498              : /* end of fakebank.c */
        

Generated by: LCOV version 2.0-1