fakebank.c (14163B)
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 handle_mhd_completion_callback (void *cls, 57 struct MHD_Connection *connection, 58 void **con_cls, 59 enum MHD_RequestTerminationCode toe) 60 { 61 struct TALER_FAKEBANK_Handle *h = cls; 62 struct ConnectionContext *cc = *con_cls; 63 64 (void) h; 65 (void) connection; 66 (void) toe; 67 if (NULL == cc) 68 return; 69 cc->ctx_cleaner (cc->ctx); 70 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 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 struct TALER_FAKEBANK_Handle *h = cls; 98 99 (void) version; 100 if (0 == strncmp (url, 101 "/taler-integration/", 102 strlen ("/taler-integration/"))) 103 { 104 url += strlen ("/taler-integration"); 105 return TALER_FAKEBANK_tbi_main_ (h, 106 connection, 107 url, 108 method, 109 upload_data, 110 upload_data_size, 111 con_cls); 112 } 113 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 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 GNUNET_assert (-1 != h->mhd_fd); 139 haveto = MHD_get_timeout (h->mhd_bank, 140 &timeout); 141 if (MHD_YES == haveto) 142 tv.rel_value_us = (uint64_t) timeout * 1000LL; 143 else 144 tv = GNUNET_TIME_UNIT_FOREVER_REL; 145 if (NULL != h->mhd_task) 146 GNUNET_SCHEDULER_cancel (h->mhd_task); 147 h->mhd_task = 148 GNUNET_SCHEDULER_add_read_net (tv, 149 h->mhd_rfd, 150 &TALER_FAKEBANK_run_mhd_, 151 h); 152 } 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 TALER_FAKEBANK_run_mhd_ (void *cls) 243 { 244 struct TALER_FAKEBANK_Handle *h = cls; 245 246 h->mhd_task = NULL; 247 h->mhd_again = true; 248 while (h->mhd_again) 249 { 250 h->mhd_again = false; 251 GNUNET_assert (MHD_YES == 252 MHD_run (h->mhd_bank)); 253 } 254 #ifdef __linux__ 255 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 schedule_httpd (h); 261 } 262 263 264 struct TALER_FAKEBANK_Handle * 265 TALER_FAKEBANK_start (uint16_t port, 266 const char *currency) 267 { 268 return TALER_FAKEBANK_start2 (port, 269 currency, 270 65536, /* RAM limit */ 271 1); 272 } 273 274 275 struct TALER_FAKEBANK_Handle * 276 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 if (GNUNET_OK != 284 TALER_amount_set_zero (currency, 285 &zero)) 286 { 287 GNUNET_break (0); 288 return NULL; 289 } 290 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 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 if (SIZE_MAX / sizeof (struct Transaction *) < ram_limit) 312 { 313 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 return NULL; 317 } 318 GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN); 319 if (0 != strcmp (signup_bonus->currency, 320 currency)) 321 { 322 GNUNET_break (0); 323 return NULL; 324 } 325 h = GNUNET_new (struct TALER_FAKEBANK_Handle); 326 h->signup_bonus = *signup_bonus; 327 if (NULL != exchange_url) 328 h->exchange_url = GNUNET_strdup (exchange_url); 329 #ifdef __linux__ 330 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 h->mhd_fd = -1; 337 #endif 338 h->port = port; 339 h->ram_limit = ram_limit; 340 h->serial_counter = 0; 341 GNUNET_assert (0 == 342 pthread_mutex_init (&h->accounts_lock, 343 NULL)); 344 GNUNET_assert (0 == 345 pthread_mutex_init (&h->rpubs_lock, 346 NULL)); 347 GNUNET_assert (0 == 348 pthread_mutex_init (&h->uuid_map_lock, 349 NULL)); 350 GNUNET_assert (0 == 351 pthread_mutex_init (&h->big_lock, 352 NULL)); 353 h->transactions 354 = GNUNET_malloc_large (sizeof (struct Transaction *) 355 * ram_limit); 356 if (NULL == h->transactions) 357 { 358 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 359 "malloc"); 360 TALER_FAKEBANK_stop (h); 361 return NULL; 362 } 363 h->accounts = GNUNET_CONTAINER_multihashmap_create (128, 364 GNUNET_NO); 365 h->uuid_map = GNUNET_CONTAINER_multihashmap_create (ram_limit * 4 / 3, 366 GNUNET_YES); 367 if (NULL == h->uuid_map) 368 { 369 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 370 "malloc"); 371 TALER_FAKEBANK_stop (h); 372 return NULL; 373 } 374 h->rpubs = GNUNET_CONTAINER_multipeermap_create (ram_limit * 4 / 3, 375 GNUNET_NO); 376 if (NULL == h->rpubs) 377 { 378 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 379 "malloc"); 380 TALER_FAKEBANK_stop (h); 381 return NULL; 382 } 383 h->lp_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); 384 h->currency = GNUNET_strdup (currency); 385 h->hostname = GNUNET_strdup (hostname); 386 GNUNET_asprintf (&h->my_baseurl, 387 "http://%s:%u/", 388 h->hostname, 389 (unsigned int) port); 390 if (0 == num_threads) 391 { 392 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 if (NULL == h->mhd_bank) 410 { 411 TALER_FAKEBANK_stop (h); 412 return NULL; 413 } 414 #if EPOLL_SUPPORT 415 h->mhd_fd = MHD_get_daemon_info (h->mhd_bank, 416 MHD_DAEMON_INFO_EPOLL_FD)->epoll_fd; 417 h->mhd_rfd = GNUNET_NETWORK_socket_box_native (h->mhd_fd); 418 #endif 419 schedule_httpd (h); 420 } 421 else 422 { 423 #ifdef __linux__ 424 h->lp_event = eventfd (0, 425 EFD_CLOEXEC); 426 if (-1 == h->lp_event) 427 { 428 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 429 "eventfd"); 430 TALER_FAKEBANK_stop (h); 431 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 if (0 != 449 pthread_create (&h->lp_thread, 450 NULL, 451 &TALER_FAKEBANK_lp_expiration_thread_, 452 h)) 453 { 454 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 455 "pthread_create"); 456 #ifdef __linux__ 457 GNUNET_break (0 == close (h->lp_event)); 458 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 TALER_FAKEBANK_stop (h); 466 return NULL; 467 } 468 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 if (NULL == h->mhd_bank) 488 { 489 GNUNET_break (0); 490 TALER_FAKEBANK_stop (h); 491 return NULL; 492 } 493 } 494 return h; 495 } 496 497 498 /* end of fakebank.c */