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 */
|