Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2022 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 : * @file taler-exchange-httpd.c
18 : * @brief Serve the HTTP interface of the exchange
19 : * @author Florian Dold
20 : * @author Benedikt Mueller
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <jansson.h>
26 : #include <microhttpd.h>
27 : #include <sched.h>
28 : #include <sys/resource.h>
29 : #include <limits.h>
30 : #include "taler_kyclogic_lib.h"
31 : #include "taler_templating_lib.h"
32 : #include "taler_mhd_lib.h"
33 : #include "taler-exchange-httpd_auditors.h"
34 : #include "taler-exchange-httpd_batch-deposit.h"
35 : #include "taler-exchange-httpd_batch-withdraw.h"
36 : #include "taler-exchange-httpd_contract.h"
37 : #include "taler-exchange-httpd_csr.h"
38 : #include "taler-exchange-httpd_deposit.h"
39 : #include "taler-exchange-httpd_deposits_get.h"
40 : #include "taler-exchange-httpd_extensions.h"
41 : #include "taler-exchange-httpd_keys.h"
42 : #include "taler-exchange-httpd_kyc-check.h"
43 : #include "taler-exchange-httpd_kyc-proof.h"
44 : #include "taler-exchange-httpd_kyc-wallet.h"
45 : #include "taler-exchange-httpd_link.h"
46 : #include "taler-exchange-httpd_management.h"
47 : #include "taler-exchange-httpd_melt.h"
48 : #include "taler-exchange-httpd_metrics.h"
49 : #include "taler-exchange-httpd_mhd.h"
50 : #include "taler-exchange-httpd_purses_create.h"
51 : #include "taler-exchange-httpd_purses_deposit.h"
52 : #include "taler-exchange-httpd_purses_get.h"
53 : #include "taler-exchange-httpd_purses_merge.h"
54 : #include "taler-exchange-httpd_recoup.h"
55 : #include "taler-exchange-httpd_recoup-refresh.h"
56 : #include "taler-exchange-httpd_refreshes_reveal.h"
57 : #include "taler-exchange-httpd_refund.h"
58 : #include "taler-exchange-httpd_reserves_get.h"
59 : #include "taler-exchange-httpd_reserves_history.h"
60 : #include "taler-exchange-httpd_reserves_purse.h"
61 : #include "taler-exchange-httpd_reserves_status.h"
62 : #include "taler-exchange-httpd_terms.h"
63 : #include "taler-exchange-httpd_transfers_get.h"
64 : #include "taler-exchange-httpd_wire.h"
65 : #include "taler-exchange-httpd_withdraw.h"
66 : #include "taler_exchangedb_lib.h"
67 : #include "taler_exchangedb_plugin.h"
68 : #include "taler_extensions.h"
69 : #include <gnunet/gnunet_mhd_compat.h>
70 :
71 : /**
72 : * Backlog for listen operation on unix domain sockets.
73 : */
74 : #define UNIX_BACKLOG 50
75 :
76 : /**
77 : * Above what request latency do we start to log?
78 : */
79 : #define WARN_LATENCY GNUNET_TIME_relative_multiply ( \
80 : GNUNET_TIME_UNIT_MILLISECONDS, 500)
81 :
82 : /**
83 : * Are clients allowed to request /keys for times other than the
84 : * current time? Allowing this could be abused in a DoS-attack
85 : * as building new /keys responses is expensive. Should only be
86 : * enabled for testcases, development and test systems.
87 : */
88 : int TEH_allow_keys_timetravel;
89 :
90 : /**
91 : * Should we allow two HTTPDs to bind to the same port?
92 : */
93 : static int allow_address_reuse;
94 :
95 : /**
96 : * The exchange's configuration (global)
97 : */
98 : const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
99 :
100 : /**
101 : * Handle to the HTTP server.
102 : */
103 : static struct MHD_Daemon *mhd;
104 :
105 : /**
106 : * Our KYC configuration.
107 : */
108 : struct TEH_KycOptions TEH_kyc_config;
109 :
110 : /**
111 : * How long is caching /keys allowed at most? (global)
112 : */
113 : struct GNUNET_TIME_Relative TEH_max_keys_caching;
114 :
115 : /**
116 : * How long is the delay before we close reserves?
117 : */
118 : struct GNUNET_TIME_Relative TEH_reserve_closing_delay;
119 :
120 : /**
121 : * Master public key (according to the
122 : * configuration in the exchange directory). (global)
123 : */
124 : struct TALER_MasterPublicKeyP TEH_master_public_key;
125 :
126 : /**
127 : * Our DB plugin. (global)
128 : */
129 : struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
130 :
131 : /**
132 : * Our currency.
133 : */
134 : char *TEH_currency;
135 :
136 : /**
137 : * Our base URL.
138 : */
139 : char *TEH_base_url;
140 :
141 : /**
142 : * Age restriction flags and mask
143 : */
144 : bool TEH_age_restriction_enabled = true;
145 :
146 : /**
147 : * Default timeout in seconds for HTTP requests.
148 : */
149 : static unsigned int connection_timeout = 30;
150 :
151 : /**
152 : * -C command-line flag given?
153 : */
154 : static int connection_close;
155 :
156 : /**
157 : * -I command-line flag given?
158 : */
159 : int TEH_check_invariants_flag;
160 :
161 : /**
162 : * True if we should commit suicide once all active
163 : * connections are finished.
164 : */
165 : bool TEH_suicide;
166 :
167 : /**
168 : * Signature of the configuration of all enabled extensions,
169 : * signed by the exchange's offline master key with purpose
170 : * TALER_SIGNATURE_MASTER_EXTENSION.
171 : */
172 : struct TALER_MasterSignatureP TEH_extensions_sig;
173 :
174 : /**
175 : * Value to return from main()
176 : */
177 : static int global_ret;
178 :
179 : /**
180 : * Port to run the daemon on.
181 : */
182 : static uint16_t serve_port;
183 :
184 : /**
185 : * Counter for the number of requests this HTTP has processed so far.
186 : */
187 : static unsigned long long req_count;
188 :
189 : /**
190 : * Counter for the number of open connections.
191 : */
192 : static unsigned long long active_connections;
193 :
194 : /**
195 : * Limit for the number of requests this HTTP may process before restarting.
196 : * (This was added as one way of dealing with unavoidable memory fragmentation
197 : * happening slowly over time.)
198 : */
199 : static unsigned long long req_max;
200 :
201 : /**
202 : * Context for all CURL operations (useful to the event loop)
203 : */
204 : struct GNUNET_CURL_Context *TEH_curl_ctx;
205 :
206 : /**
207 : * Context for integrating #TEH_curl_ctx with the
208 : * GNUnet event loop.
209 : */
210 : static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
211 :
212 : /**
213 : * Signature of functions that handle operations on coins.
214 : *
215 : * @param connection the MHD connection to handle
216 : * @param coin_pub the public key of the coin
217 : * @param root uploaded JSON data
218 : * @return MHD result code
219 : */
220 : typedef MHD_RESULT
221 : (*CoinOpHandler)(struct MHD_Connection *connection,
222 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
223 : const json_t *root);
224 :
225 :
226 : /**
227 : * Generate a 404 "not found" reply on @a connection with
228 : * the hint @a details.
229 : *
230 : * @param connection where to send the reply on
231 : * @param details details for the error message, can be NULL
232 : */
233 : static MHD_RESULT
234 0 : r404 (struct MHD_Connection *connection,
235 : const char *details)
236 : {
237 0 : return TALER_MHD_reply_with_error (connection,
238 : MHD_HTTP_NOT_FOUND,
239 : TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
240 : details);
241 : }
242 :
243 :
244 : /**
245 : * Handle a "/coins/$COIN_PUB/$OP" POST request. Parses the "coin_pub"
246 : * EdDSA key of the coin and demultiplexes based on $OP.
247 : *
248 : * @param rc request context
249 : * @param root uploaded JSON data
250 : * @param args array of additional options
251 : * @return MHD result code
252 : */
253 : static MHD_RESULT
254 0 : handle_post_coins (struct TEH_RequestContext *rc,
255 : const json_t *root,
256 : const char *const args[2])
257 : {
258 : struct TALER_CoinSpendPublicKeyP coin_pub;
259 : static const struct
260 : {
261 : /**
262 : * Name of the operation (args[1])
263 : */
264 : const char *op;
265 :
266 : /**
267 : * Function to call to perform the operation.
268 : */
269 : CoinOpHandler handler;
270 :
271 : } h[] = {
272 : {
273 : .op = "deposit",
274 : .handler = &TEH_handler_deposit
275 : },
276 : {
277 : .op = "melt",
278 : .handler = &TEH_handler_melt
279 : },
280 : {
281 : .op = "recoup",
282 : .handler = &TEH_handler_recoup
283 : },
284 : {
285 : .op = "recoup-refresh",
286 : .handler = &TEH_handler_recoup_refresh
287 : },
288 : {
289 : .op = "refund",
290 : .handler = &TEH_handler_refund
291 : },
292 : {
293 : .op = NULL,
294 : .handler = NULL
295 : },
296 : };
297 :
298 0 : if (GNUNET_OK !=
299 0 : GNUNET_STRINGS_string_to_data (args[0],
300 : strlen (args[0]),
301 : &coin_pub,
302 : sizeof (coin_pub)))
303 : {
304 0 : GNUNET_break_op (0);
305 0 : return TALER_MHD_reply_with_error (rc->connection,
306 : MHD_HTTP_BAD_REQUEST,
307 : TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
308 : args[0]);
309 : }
310 0 : for (unsigned int i = 0; NULL != h[i].op; i++)
311 0 : if (0 == strcmp (h[i].op,
312 0 : args[1]))
313 0 : return h[i].handler (rc->connection,
314 : &coin_pub,
315 : root);
316 0 : return r404 (rc->connection,
317 0 : args[1]);
318 : }
319 :
320 :
321 : /**
322 : * Signature of functions that handle operations on reserves.
323 : *
324 : * @param rc request context
325 : * @param reserve_pub the public key of the reserve
326 : * @param root uploaded JSON data
327 : * @return MHD result code
328 : */
329 : typedef MHD_RESULT
330 : (*ReserveOpHandler)(struct TEH_RequestContext *rc,
331 : const struct TALER_ReservePublicKeyP *reserve_pub,
332 : const json_t *root);
333 :
334 :
335 : /**
336 : * Handle a "/reserves/$RESERVE_PUB/$OP" POST request. Parses the "reserve_pub"
337 : * EdDSA key of the reserve and demultiplexes based on $OP.
338 : *
339 : * @param rc request context
340 : * @param root uploaded JSON data
341 : * @param args array of additional options
342 : * @return MHD result code
343 : */
344 : static MHD_RESULT
345 0 : handle_post_reserves (struct TEH_RequestContext *rc,
346 : const json_t *root,
347 : const char *const args[2])
348 : {
349 : struct TALER_ReservePublicKeyP reserve_pub;
350 : static const struct
351 : {
352 : /**
353 : * Name of the operation (args[1])
354 : */
355 : const char *op;
356 :
357 : /**
358 : * Function to call to perform the operation.
359 : */
360 : ReserveOpHandler handler;
361 :
362 : } h[] = {
363 : {
364 : .op = "withdraw",
365 : .handler = &TEH_handler_withdraw
366 : },
367 : {
368 : .op = "batch-withdraw",
369 : .handler = &TEH_handler_batch_withdraw
370 : },
371 : {
372 : .op = "status",
373 : .handler = &TEH_handler_reserves_status
374 : },
375 : {
376 : .op = "history",
377 : .handler = &TEH_handler_reserves_history
378 : },
379 : {
380 : .op = "purse",
381 : .handler = &TEH_handler_reserves_purse
382 : },
383 : {
384 : .op = NULL,
385 : .handler = NULL
386 : },
387 : };
388 :
389 0 : if (GNUNET_OK !=
390 0 : GNUNET_STRINGS_string_to_data (args[0],
391 : strlen (args[0]),
392 : &reserve_pub,
393 : sizeof (reserve_pub)))
394 : {
395 0 : GNUNET_break_op (0);
396 0 : return TALER_MHD_reply_with_error (rc->connection,
397 : MHD_HTTP_BAD_REQUEST,
398 : TALER_EC_EXCHANGE_GENERIC_RESERVE_PUB_MALFORMED,
399 : args[0]);
400 : }
401 0 : for (unsigned int i = 0; NULL != h[i].op; i++)
402 0 : if (0 == strcmp (h[i].op,
403 0 : args[1]))
404 0 : return h[i].handler (rc,
405 : &reserve_pub,
406 : root);
407 0 : return r404 (rc->connection,
408 0 : args[1]);
409 : }
410 :
411 :
412 : /**
413 : * Signature of functions that handle operations on purses.
414 : *
415 : * @param connection HTTP request handle
416 : * @param purse_pub the public key of the purse
417 : * @param root uploaded JSON data
418 : * @return MHD result code
419 : */
420 : typedef MHD_RESULT
421 : (*PurseOpHandler)(struct MHD_Connection *connection,
422 : const struct TALER_PurseContractPublicKeyP *purse_pub,
423 : const json_t *root);
424 :
425 :
426 : /**
427 : * Handle a "/purses/$RESERVE_PUB/$OP" POST request. Parses the "purse_pub"
428 : * EdDSA key of the purse and demultiplexes based on $OP.
429 : *
430 : * @param rc request context
431 : * @param root uploaded JSON data
432 : * @param args array of additional options
433 : * @return MHD result code
434 : */
435 : static MHD_RESULT
436 0 : handle_post_purses (struct TEH_RequestContext *rc,
437 : const json_t *root,
438 : const char *const args[2])
439 : {
440 : struct TALER_PurseContractPublicKeyP purse_pub;
441 : static const struct
442 : {
443 : /**
444 : * Name of the operation (args[1])
445 : */
446 : const char *op;
447 :
448 : /**
449 : * Function to call to perform the operation.
450 : */
451 : PurseOpHandler handler;
452 :
453 : } h[] = {
454 : {
455 : .op = "create",
456 : .handler = &TEH_handler_purses_create
457 : },
458 : {
459 : .op = "deposit",
460 : .handler = &TEH_handler_purses_deposit
461 : },
462 : {
463 : .op = "merge",
464 : .handler = &TEH_handler_purses_merge
465 : },
466 : {
467 : .op = NULL,
468 : .handler = NULL
469 : },
470 : };
471 :
472 0 : if (GNUNET_OK !=
473 0 : GNUNET_STRINGS_string_to_data (args[0],
474 : strlen (args[0]),
475 : &purse_pub,
476 : sizeof (purse_pub)))
477 : {
478 0 : GNUNET_break_op (0);
479 0 : return TALER_MHD_reply_with_error (rc->connection,
480 : MHD_HTTP_BAD_REQUEST,
481 : TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
482 : args[0]);
483 : }
484 0 : for (unsigned int i = 0; NULL != h[i].op; i++)
485 0 : if (0 == strcmp (h[i].op,
486 0 : args[1]))
487 0 : return h[i].handler (rc->connection,
488 : &purse_pub,
489 : root);
490 0 : return r404 (rc->connection,
491 0 : args[1]);
492 : }
493 :
494 :
495 : /**
496 : * Increments our request counter and checks if this
497 : * process should commit suicide.
498 : */
499 : static void
500 0 : check_suicide (void)
501 : {
502 : int fd;
503 : pid_t chld;
504 : unsigned long long cnt;
505 :
506 0 : cnt = req_count++;
507 0 : if (req_max != cnt)
508 0 : return;
509 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
510 : "Restarting exchange service after %llu requests\n",
511 : cnt);
512 : /* Stop accepting new connections */
513 0 : fd = MHD_quiesce_daemon (mhd);
514 0 : GNUNET_break (0 == close (fd));
515 : /* Continue handling existing connections in child,
516 : so that this process can die and be replaced by
517 : systemd with a fresh one */
518 0 : chld = fork ();
519 0 : if (-1 == chld)
520 : {
521 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
522 : "fork");
523 0 : _exit (1);
524 : }
525 0 : if (0 != chld)
526 : {
527 : /* We are the parent, instant-suicide! */
528 0 : _exit (0);
529 : }
530 0 : TEH_suicide = true;
531 : }
532 :
533 :
534 : /**
535 : * Function called whenever MHD is done with a request. If the
536 : * request was a POST, we may have stored a `struct Buffer *` in the
537 : * @a con_cls that might still need to be cleaned up. Call the
538 : * respective function to free the memory.
539 : *
540 : * @param cls client-defined closure
541 : * @param connection connection handle
542 : * @param con_cls value as set by the last call to
543 : * the #MHD_AccessHandlerCallback
544 : * @param toe reason for request termination
545 : * @see #MHD_OPTION_NOTIFY_COMPLETED
546 : * @ingroup request
547 : */
548 : static void
549 0 : handle_mhd_completion_callback (void *cls,
550 : struct MHD_Connection *connection,
551 : void **con_cls,
552 : enum MHD_RequestTerminationCode toe)
553 : {
554 0 : struct TEH_RequestContext *rc = *con_cls;
555 : struct GNUNET_AsyncScopeSave old_scope;
556 :
557 : (void) cls;
558 0 : if (NULL == rc)
559 0 : return;
560 0 : GNUNET_async_scope_enter (&rc->async_scope_id,
561 : &old_scope);
562 0 : check_suicide ();
563 0 : TEH_check_invariants ();
564 0 : if (NULL != rc->rh_cleaner)
565 0 : rc->rh_cleaner (rc);
566 0 : TEH_check_invariants ();
567 : {
568 : #if MHD_VERSION >= 0x00097304
569 : const union MHD_ConnectionInfo *ci;
570 0 : unsigned int http_status = 0;
571 :
572 0 : ci = MHD_get_connection_info (connection,
573 : MHD_CONNECTION_INFO_HTTP_STATUS);
574 0 : if (NULL != ci)
575 0 : http_status = ci->http_status;
576 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
577 : "Request for `%s' completed with HTTP status %u (%d)\n",
578 : rc->url,
579 : http_status,
580 : toe);
581 : #else
582 : (void) connection;
583 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
584 : "Request for `%s' completed (%d)\n",
585 : rc->url,
586 : toe);
587 : #endif
588 : }
589 :
590 0 : TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context);
591 : /* Sanity-check that we didn't leave any transactions hanging */
592 0 : GNUNET_break (GNUNET_OK ==
593 : TEH_plugin->preflight (TEH_plugin->cls));
594 : {
595 : struct GNUNET_TIME_Relative latency;
596 :
597 0 : latency = GNUNET_TIME_absolute_get_duration (rc->start_time);
598 0 : if (latency.rel_value_us >
599 0 : WARN_LATENCY.rel_value_us)
600 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
601 : "Request for `%s' took %s\n",
602 : rc->url,
603 : GNUNET_STRINGS_relative_time_to_string (latency,
604 : GNUNET_YES));
605 : }
606 0 : GNUNET_free (rc);
607 0 : *con_cls = NULL;
608 0 : GNUNET_async_scope_restore (&old_scope);
609 : }
610 :
611 :
612 : /**
613 : * We found a request handler responsible for handling a request. Parse the
614 : * @a upload_data (if applicable) and the @a url and call the
615 : * handler.
616 : *
617 : * @param rc request context
618 : * @param url rest of the URL to parse
619 : * @param upload_data upload data to parse (if available)
620 : * @param[in,out] upload_data_size number of bytes in @a upload_data
621 : * @return MHD result code
622 : */
623 : static MHD_RESULT
624 0 : proceed_with_handler (struct TEH_RequestContext *rc,
625 : const char *url,
626 : const char *upload_data,
627 : size_t *upload_data_size)
628 0 : {
629 0 : const struct TEH_RequestHandler *rh = rc->rh;
630 0 : const char *args[rh->nargs + 2];
631 0 : size_t ulen = strlen (url) + 1;
632 0 : json_t *root = NULL;
633 : MHD_RESULT ret;
634 :
635 : /* We do check for "ulen" here, because we'll later stack-allocate a buffer
636 : of that size and don't want to enable malicious clients to cause us
637 : huge stack allocations. */
638 0 : if (ulen > 512)
639 : {
640 : /* 512 is simply "big enough", as it is bigger than "6 * 54",
641 : which is the longest URL format we ever get (for
642 : /deposits/). The value should be adjusted if we ever define protocol
643 : endpoints with plausibly longer inputs. */
644 0 : GNUNET_break_op (0);
645 0 : return TALER_MHD_reply_with_error (rc->connection,
646 : MHD_HTTP_URI_TOO_LONG,
647 : TALER_EC_GENERIC_URI_TOO_LONG,
648 : url);
649 : }
650 :
651 : /* All POST endpoints come with a body in JSON format. So we parse
652 : the JSON here. */
653 0 : if (0 == strcasecmp (rh->method,
654 : MHD_HTTP_METHOD_POST))
655 : {
656 : enum GNUNET_GenericReturnValue res;
657 :
658 0 : res = TALER_MHD_parse_post_json (rc->connection,
659 : &rc->opaque_post_parsing_context,
660 : upload_data,
661 : upload_data_size,
662 : &root);
663 0 : if (GNUNET_SYSERR == res)
664 : {
665 0 : GNUNET_assert (NULL == root);
666 0 : return MHD_NO; /* bad upload, could not even generate error */
667 : }
668 0 : if ( (GNUNET_NO == res) ||
669 0 : (NULL == root) )
670 : {
671 0 : GNUNET_assert (NULL == root);
672 0 : return MHD_YES; /* so far incomplete upload or parser error */
673 : }
674 : }
675 :
676 0 : {
677 0 : char d[ulen];
678 : unsigned int i;
679 : char *sp;
680 :
681 : /* Parse command-line arguments */
682 : /* make a copy of 'url' because 'strtok_r()' will modify */
683 0 : memcpy (d,
684 : url,
685 : ulen);
686 0 : i = 0;
687 0 : args[i++] = strtok_r (d, "/", &sp);
688 0 : while ( (NULL != args[i - 1]) &&
689 0 : (i <= rh->nargs + 1) )
690 0 : args[i++] = strtok_r (NULL, "/", &sp);
691 : /* make sure above loop ran nicely until completion, and also
692 : that there is no excess data in 'd' afterwards */
693 0 : if ( ( (rh->nargs_is_upper_bound) &&
694 0 : (i - 1 > rh->nargs) ) ||
695 0 : ( (! rh->nargs_is_upper_bound) &&
696 0 : (i - 1 != rh->nargs) ) )
697 : {
698 : char emsg[128 + 512];
699 :
700 0 : GNUNET_snprintf (emsg,
701 : sizeof (emsg),
702 : "Got %u+/%u segments for `%s' request (`%s')",
703 : i - 1,
704 : rh->nargs,
705 : rh->url,
706 : url);
707 0 : GNUNET_break_op (0);
708 0 : json_decref (root);
709 0 : return TALER_MHD_reply_with_error (rc->connection,
710 : MHD_HTTP_NOT_FOUND,
711 : TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
712 : emsg);
713 : }
714 0 : GNUNET_assert (NULL == args[i - 1]);
715 :
716 : /* Above logic ensures that 'root' is exactly non-NULL for POST operations,
717 : so we test for 'root' to decide which handler to invoke. */
718 0 : if (NULL != root)
719 0 : ret = rh->handler.post (rc,
720 : root,
721 : args);
722 : else /* We also only have "POST" or "GET" in the API for at this point
723 : (OPTIONS/HEAD are taken care of earlier) */
724 0 : ret = rh->handler.get (rc,
725 : args);
726 : }
727 0 : json_decref (root);
728 0 : return ret;
729 : }
730 :
731 :
732 : /**
733 : * Handle a "/seed" request.
734 : *
735 : * @param rc request context
736 : * @param args array of additional options (must be empty for this function)
737 : * @return MHD result code
738 : */
739 : static MHD_RESULT
740 0 : handler_seed (struct TEH_RequestContext *rc,
741 : const char *const args[])
742 : {
743 : #define SEED_SIZE 32
744 : char *body;
745 : MHD_RESULT ret;
746 : struct MHD_Response *resp;
747 :
748 : (void) args;
749 0 : body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */
750 0 : if (NULL == body)
751 0 : return MHD_NO;
752 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
753 : body,
754 : SEED_SIZE);
755 0 : resp = MHD_create_response_from_buffer (SEED_SIZE,
756 : body,
757 : MHD_RESPMEM_MUST_FREE);
758 0 : TALER_MHD_add_global_headers (resp);
759 0 : ret = MHD_queue_response (rc->connection,
760 : MHD_HTTP_OK,
761 : resp);
762 0 : GNUNET_break (MHD_YES == ret);
763 0 : MHD_destroy_response (resp);
764 0 : return ret;
765 : #undef SEED_SIZE
766 : }
767 :
768 :
769 : /**
770 : * Handle POST "/management/..." requests.
771 : *
772 : * @param rc request context
773 : * @param root uploaded JSON data
774 : * @param args array of additional options
775 : * @return MHD result code
776 : */
777 : static MHD_RESULT
778 0 : handle_post_management (struct TEH_RequestContext *rc,
779 : const json_t *root,
780 : const char *const args[])
781 : {
782 0 : if (NULL == args[0])
783 : {
784 0 : GNUNET_break_op (0);
785 0 : return r404 (rc->connection,
786 : "/management");
787 : }
788 0 : if (0 == strcmp (args[0],
789 : "auditors"))
790 : {
791 : struct TALER_AuditorPublicKeyP auditor_pub;
792 :
793 0 : if (NULL == args[1])
794 0 : return TEH_handler_management_auditors (rc->connection,
795 : root);
796 0 : if ( (NULL == args[1]) ||
797 0 : (NULL == args[2]) ||
798 0 : (0 != strcmp (args[2],
799 0 : "disable")) ||
800 0 : (NULL != args[3]) )
801 0 : return r404 (rc->connection,
802 : "/management/auditors/$AUDITOR_PUB/disable");
803 0 : if (GNUNET_OK !=
804 0 : GNUNET_STRINGS_string_to_data (args[1],
805 0 : strlen (args[1]),
806 : &auditor_pub,
807 : sizeof (auditor_pub)))
808 : {
809 0 : GNUNET_break_op (0);
810 0 : return TALER_MHD_reply_with_error (rc->connection,
811 : MHD_HTTP_BAD_REQUEST,
812 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
813 0 : args[1]);
814 : }
815 0 : return TEH_handler_management_auditors_AP_disable (rc->connection,
816 : &auditor_pub,
817 : root);
818 : }
819 0 : if (0 == strcmp (args[0],
820 : "denominations"))
821 : {
822 : struct TALER_DenominationHashP h_denom_pub;
823 :
824 0 : if ( (NULL == args[0]) ||
825 0 : (NULL == args[1]) ||
826 0 : (NULL == args[2]) ||
827 0 : (0 != strcmp (args[2],
828 0 : "revoke")) ||
829 0 : (NULL != args[3]) )
830 0 : return r404 (rc->connection,
831 : "/management/denominations/$HDP/revoke");
832 0 : if (GNUNET_OK !=
833 0 : GNUNET_STRINGS_string_to_data (args[1],
834 0 : strlen (args[1]),
835 : &h_denom_pub,
836 : sizeof (h_denom_pub)))
837 : {
838 0 : GNUNET_break_op (0);
839 0 : return TALER_MHD_reply_with_error (rc->connection,
840 : MHD_HTTP_BAD_REQUEST,
841 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
842 0 : args[1]);
843 : }
844 0 : return TEH_handler_management_denominations_HDP_revoke (rc->connection,
845 : &h_denom_pub,
846 : root);
847 : }
848 0 : if (0 == strcmp (args[0],
849 : "signkeys"))
850 : {
851 : struct TALER_ExchangePublicKeyP exchange_pub;
852 :
853 0 : if ( (NULL == args[0]) ||
854 0 : (NULL == args[1]) ||
855 0 : (NULL == args[2]) ||
856 0 : (0 != strcmp (args[2],
857 0 : "revoke")) ||
858 0 : (NULL != args[3]) )
859 0 : return r404 (rc->connection,
860 : "/management/signkeys/$HDP/revoke");
861 0 : if (GNUNET_OK !=
862 0 : GNUNET_STRINGS_string_to_data (args[1],
863 0 : strlen (args[1]),
864 : &exchange_pub,
865 : sizeof (exchange_pub)))
866 : {
867 0 : GNUNET_break_op (0);
868 0 : return TALER_MHD_reply_with_error (rc->connection,
869 : MHD_HTTP_BAD_REQUEST,
870 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
871 0 : args[1]);
872 : }
873 0 : return TEH_handler_management_signkeys_EP_revoke (rc->connection,
874 : &exchange_pub,
875 : root);
876 : }
877 0 : if (0 == strcmp (args[0],
878 : "keys"))
879 : {
880 0 : if (NULL != args[1])
881 : {
882 0 : GNUNET_break_op (0);
883 0 : return r404 (rc->connection,
884 : "/management/keys/*");
885 : }
886 0 : return TEH_handler_management_post_keys (rc->connection,
887 : root);
888 : }
889 0 : if (0 == strcmp (args[0],
890 : "wire"))
891 : {
892 0 : if (NULL == args[1])
893 0 : return TEH_handler_management_post_wire (rc->connection,
894 : root);
895 0 : if ( (0 != strcmp (args[1],
896 0 : "disable")) ||
897 0 : (NULL != args[2]) )
898 : {
899 0 : GNUNET_break_op (0);
900 0 : return r404 (rc->connection,
901 : "/management/wire/disable");
902 : }
903 0 : return TEH_handler_management_post_wire_disable (rc->connection,
904 : root);
905 : }
906 0 : if (0 == strcmp (args[0],
907 : "wire-fee"))
908 : {
909 0 : if (NULL != args[1])
910 : {
911 0 : GNUNET_break_op (0);
912 0 : return r404 (rc->connection,
913 : "/management/wire-fee/*");
914 : }
915 0 : return TEH_handler_management_post_wire_fees (rc->connection,
916 : root);
917 : }
918 0 : if (0 == strcmp (args[0],
919 : "global-fee"))
920 : {
921 0 : if (NULL != args[1])
922 : {
923 0 : GNUNET_break_op (0);
924 0 : return r404 (rc->connection,
925 : "/management/global-fee/*");
926 : }
927 0 : return TEH_handler_management_post_global_fees (rc->connection,
928 : root);
929 : }
930 0 : if (0 == strcmp (args[0],
931 : "extensions"))
932 : {
933 0 : if (NULL != args[1])
934 : {
935 0 : GNUNET_break_op (0);
936 0 : return r404 (rc->connection,
937 : "/management/extensions/*");
938 : }
939 0 : return TEH_handler_management_post_extensions (rc->connection,
940 : root);
941 : }
942 0 : if (0 == strcmp (args[0],
943 : "drain"))
944 : {
945 0 : if (NULL != args[1])
946 : {
947 0 : GNUNET_break_op (0);
948 0 : return r404 (rc->connection,
949 : "/management/drain/*");
950 : }
951 0 : return TEH_handler_management_post_drain (rc->connection,
952 : root);
953 : }
954 0 : GNUNET_break_op (0);
955 0 : return r404 (rc->connection,
956 : "/management/*");
957 : }
958 :
959 :
960 : /**
961 : * Handle a get "/management" request.
962 : *
963 : * @param rc request context
964 : * @param args array of additional options (must be [0] == "keys")
965 : * @return MHD result code
966 : */
967 : static MHD_RESULT
968 0 : handle_get_management (struct TEH_RequestContext *rc,
969 : const char *const args[2])
970 : {
971 0 : if ( (NULL != args[0]) &&
972 0 : (0 == strcmp (args[0],
973 0 : "keys")) &&
974 0 : (NULL == args[1]) )
975 : {
976 0 : return TEH_keys_management_get_keys_handler (rc->rh,
977 : rc->connection);
978 : }
979 0 : GNUNET_break_op (0);
980 0 : return r404 (rc->connection,
981 : "/management/*");
982 : }
983 :
984 :
985 : /**
986 : * Handle POST "/auditors/..." requests.
987 : *
988 : * @param rc request context
989 : * @param root uploaded JSON data
990 : * @param args array of additional options
991 : * @return MHD result code
992 : */
993 : static MHD_RESULT
994 0 : handle_post_auditors (struct TEH_RequestContext *rc,
995 : const json_t *root,
996 : const char *const args[])
997 : {
998 : struct TALER_AuditorPublicKeyP auditor_pub;
999 : struct TALER_DenominationHashP h_denom_pub;
1000 :
1001 0 : if ( (NULL == args[0]) ||
1002 0 : (NULL == args[1]) ||
1003 0 : (NULL != args[2]) )
1004 : {
1005 0 : GNUNET_break_op (0);
1006 0 : return r404 (rc->connection,
1007 : "/auditors/$AUDITOR_PUB/$H_DENOM_PUB");
1008 : }
1009 :
1010 0 : if (GNUNET_OK !=
1011 0 : GNUNET_STRINGS_string_to_data (args[0],
1012 : strlen (args[0]),
1013 : &auditor_pub,
1014 : sizeof (auditor_pub)))
1015 : {
1016 0 : GNUNET_break_op (0);
1017 0 : return TALER_MHD_reply_with_error (rc->connection,
1018 : MHD_HTTP_BAD_REQUEST,
1019 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1020 : args[0]);
1021 : }
1022 0 : if (GNUNET_OK !=
1023 0 : GNUNET_STRINGS_string_to_data (args[1],
1024 0 : strlen (args[1]),
1025 : &h_denom_pub,
1026 : sizeof (h_denom_pub)))
1027 : {
1028 0 : GNUNET_break_op (0);
1029 0 : return TALER_MHD_reply_with_error (rc->connection,
1030 : MHD_HTTP_BAD_REQUEST,
1031 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1032 0 : args[1]);
1033 : }
1034 0 : return TEH_handler_auditors (rc->connection,
1035 : &auditor_pub,
1036 : &h_denom_pub,
1037 : root);
1038 : }
1039 :
1040 :
1041 : /**
1042 : * Handle incoming HTTP request.
1043 : *
1044 : * @param cls closure for MHD daemon (unused)
1045 : * @param connection the connection
1046 : * @param url the requested url
1047 : * @param method the method (POST, GET, ...)
1048 : * @param version HTTP version (ignored)
1049 : * @param upload_data request data
1050 : * @param upload_data_size size of @a upload_data in bytes
1051 : * @param con_cls closure for request (a `struct TEH_RequestContext *`)
1052 : * @return MHD result code
1053 : */
1054 : static MHD_RESULT
1055 0 : handle_mhd_request (void *cls,
1056 : struct MHD_Connection *connection,
1057 : const char *url,
1058 : const char *method,
1059 : const char *version,
1060 : const char *upload_data,
1061 : size_t *upload_data_size,
1062 : void **con_cls)
1063 : {
1064 : static struct TEH_RequestHandler handlers[] = {
1065 : /* /robots.txt: disallow everything */
1066 : {
1067 : .url = "robots.txt",
1068 : .method = MHD_HTTP_METHOD_GET,
1069 : .handler.get = &TEH_handler_static_response,
1070 : .mime_type = "text/plain",
1071 : .data = "User-agent: *\nDisallow: /\n",
1072 : .response_code = MHD_HTTP_OK
1073 : },
1074 : /* Landing page, tell humans to go away. */
1075 : {
1076 : .url = "",
1077 : .method = MHD_HTTP_METHOD_GET,
1078 : .handler.get = TEH_handler_static_response,
1079 : .mime_type = "text/plain",
1080 : .data =
1081 : "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n",
1082 : .response_code = MHD_HTTP_OK
1083 : },
1084 : /* AGPL licensing page, redirect to source. As per the AGPL-license, every
1085 : deployment is required to offer the user a download of the source of
1086 : the actual deployment. We make this easy by including a redirect to the
1087 : source here. */
1088 : {
1089 : .url = "agpl",
1090 : .method = MHD_HTTP_METHOD_GET,
1091 : .handler.get = &TEH_handler_agpl_redirect
1092 : },
1093 : {
1094 : .url = "seed",
1095 : .method = MHD_HTTP_METHOD_GET,
1096 : .handler.get = &handler_seed
1097 : },
1098 : /* Performance metrics */
1099 : {
1100 : .url = "metrics",
1101 : .method = MHD_HTTP_METHOD_GET,
1102 : .handler.get = &TEH_handler_metrics
1103 : },
1104 : /* Terms of service */
1105 : {
1106 : .url = "terms",
1107 : .method = MHD_HTTP_METHOD_GET,
1108 : .handler.get = &TEH_handler_terms
1109 : },
1110 : /* Privacy policy */
1111 : {
1112 : .url = "privacy",
1113 : .method = MHD_HTTP_METHOD_GET,
1114 : .handler.get = &TEH_handler_privacy
1115 : },
1116 : /* Return key material and fundamental properties for this exchange */
1117 : {
1118 : .url = "keys",
1119 : .method = MHD_HTTP_METHOD_GET,
1120 : .handler.get = &TEH_keys_get_handler,
1121 : },
1122 : /* Requests for wiring information */
1123 : {
1124 : .url = "wire",
1125 : .method = MHD_HTTP_METHOD_GET,
1126 : .handler.get = &TEH_handler_wire
1127 : },
1128 : {
1129 : .url = "batch-deposit",
1130 : .method = MHD_HTTP_METHOD_POST,
1131 : .handler.post = &TEH_handler_batch_deposit,
1132 : .nargs = 0
1133 : },
1134 : /* request R, used in clause schnorr withdraw and refresh */
1135 : {
1136 : .url = "csr-melt",
1137 : .method = MHD_HTTP_METHOD_POST,
1138 : .handler.post = &TEH_handler_csr_melt,
1139 : .nargs = 0
1140 : },
1141 : {
1142 : .url = "csr-withdraw",
1143 : .method = MHD_HTTP_METHOD_POST,
1144 : .handler.post = &TEH_handler_csr_withdraw,
1145 : .nargs = 0
1146 : },
1147 : /* Withdrawing coins / interaction with reserves */
1148 : {
1149 : .url = "reserves",
1150 : .method = MHD_HTTP_METHOD_GET,
1151 : .handler.get = &TEH_handler_reserves_get,
1152 : .nargs = 1
1153 : },
1154 : {
1155 : .url = "reserves",
1156 : .method = MHD_HTTP_METHOD_POST,
1157 : .handler.post = &handle_post_reserves,
1158 : .nargs = 2
1159 : },
1160 : /* coins */
1161 : {
1162 : .url = "coins",
1163 : .method = MHD_HTTP_METHOD_POST,
1164 : .handler.post = &handle_post_coins,
1165 : .nargs = 2
1166 : },
1167 : {
1168 : .url = "coins",
1169 : .method = MHD_HTTP_METHOD_GET,
1170 : .handler.get = TEH_handler_link,
1171 : .nargs = 2,
1172 : },
1173 : /* refreshes/$RCH/reveal */
1174 : {
1175 : .url = "refreshes",
1176 : .method = MHD_HTTP_METHOD_POST,
1177 : .handler.post = &TEH_handler_reveal,
1178 : .nargs = 2
1179 : },
1180 : /* tracking transfers */
1181 : {
1182 : .url = "transfers",
1183 : .method = MHD_HTTP_METHOD_GET,
1184 : .handler.get = &TEH_handler_transfers_get,
1185 : .nargs = 1
1186 : },
1187 : /* tracking deposits */
1188 : {
1189 : .url = "deposits",
1190 : .method = MHD_HTTP_METHOD_GET,
1191 : .handler.get = &TEH_handler_deposits_get,
1192 : .nargs = 4
1193 : },
1194 : /* Operating on purses */
1195 : {
1196 : .url = "purses",
1197 : .method = MHD_HTTP_METHOD_POST,
1198 : .handler.post = &handle_post_purses,
1199 : .nargs = 2 // ??
1200 : },
1201 : /* Getting purse status */
1202 : {
1203 : .url = "purses",
1204 : .method = MHD_HTTP_METHOD_GET,
1205 : .handler.get = &TEH_handler_purses_get,
1206 : .nargs = 2
1207 : },
1208 : /* Getting contracts */
1209 : {
1210 : .url = "contracts",
1211 : .method = MHD_HTTP_METHOD_GET,
1212 : .handler.get = &TEH_handler_contracts_get,
1213 : .nargs = 1
1214 : },
1215 : /* KYC endpoints */
1216 : {
1217 : .url = "kyc-check",
1218 : .method = MHD_HTTP_METHOD_GET,
1219 : .handler.get = &TEH_handler_kyc_check,
1220 : .nargs = 3
1221 : },
1222 : {
1223 : .url = "kyc-proof",
1224 : .method = MHD_HTTP_METHOD_GET,
1225 : .handler.get = &TEH_handler_kyc_proof,
1226 : .nargs = 128,
1227 : .nargs_is_upper_bound = true
1228 : },
1229 : {
1230 : .url = "kyc-wallet",
1231 : .method = MHD_HTTP_METHOD_POST,
1232 : .handler.post = &TEH_handler_kyc_wallet,
1233 : .nargs = 0
1234 : },
1235 : /* POST management endpoints */
1236 : {
1237 : .url = "management",
1238 : .method = MHD_HTTP_METHOD_POST,
1239 : .handler.post = &handle_post_management,
1240 : .nargs = 4,
1241 : .nargs_is_upper_bound = true
1242 : },
1243 : /* GET management endpoints (we only really have "/management/keys") */
1244 : {
1245 : .url = "management",
1246 : .method = MHD_HTTP_METHOD_GET,
1247 : .handler.get = &handle_get_management,
1248 : .nargs = 1
1249 : },
1250 : /* auditor endpoints */
1251 : {
1252 : .url = "auditors",
1253 : .method = MHD_HTTP_METHOD_POST,
1254 : .handler.post = &handle_post_auditors,
1255 : .nargs = 4,
1256 : .nargs_is_upper_bound = true
1257 : },
1258 : /* mark end of list */
1259 : {
1260 : .url = NULL
1261 : }
1262 : };
1263 0 : struct TEH_RequestContext *rc = *con_cls;
1264 : struct GNUNET_AsyncScopeSave old_scope;
1265 0 : const char *correlation_id = NULL;
1266 :
1267 : (void) cls;
1268 : (void) version;
1269 0 : if (NULL == rc)
1270 : {
1271 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1272 : "Handling new request\n");
1273 :
1274 : /* We're in a new async scope! */
1275 0 : rc = *con_cls = GNUNET_new (struct TEH_RequestContext);
1276 0 : rc->start_time = GNUNET_TIME_absolute_get ();
1277 0 : GNUNET_async_scope_fresh (&rc->async_scope_id);
1278 0 : TEH_check_invariants ();
1279 0 : rc->url = url;
1280 0 : rc->connection = connection;
1281 : /* We only read the correlation ID on the first callback for every client */
1282 0 : correlation_id = MHD_lookup_connection_value (connection,
1283 : MHD_HEADER_KIND,
1284 : "Taler-Correlation-Id");
1285 0 : if ( (NULL != correlation_id) &&
1286 : (GNUNET_YES !=
1287 0 : GNUNET_CURL_is_valid_scope_id (correlation_id)) )
1288 : {
1289 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1290 : "illegal incoming correlation ID\n");
1291 0 : correlation_id = NULL;
1292 : }
1293 :
1294 : /* Check if upload is in bounds */
1295 0 : if (0 == strcasecmp (method,
1296 : MHD_HTTP_METHOD_POST))
1297 : {
1298 : const char *cl;
1299 :
1300 : /* Maybe check for maximum upload size
1301 : and refuse requests if they are just too big. */
1302 0 : cl = MHD_lookup_connection_value (connection,
1303 : MHD_HEADER_KIND,
1304 : MHD_HTTP_HEADER_CONTENT_LENGTH);
1305 0 : if (NULL != cl)
1306 : {
1307 : unsigned long long cv;
1308 : char dummy;
1309 :
1310 0 : if (1 != sscanf (cl,
1311 : "%llu%c",
1312 : &cv,
1313 : &dummy))
1314 : {
1315 : /* Not valid HTTP request, just close connection. */
1316 0 : GNUNET_break_op (0);
1317 0 : return MHD_NO;
1318 : }
1319 0 : if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
1320 0 : return TALER_MHD_reply_request_too_large (connection);
1321 : }
1322 : }
1323 : }
1324 :
1325 0 : GNUNET_async_scope_enter (&rc->async_scope_id,
1326 : &old_scope);
1327 0 : TEH_check_invariants ();
1328 0 : if (NULL != correlation_id)
1329 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1330 : "Handling request (%s) for URL '%s', correlation_id=%s\n",
1331 : method,
1332 : url,
1333 : correlation_id);
1334 : else
1335 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1336 : "Handling request (%s) for URL '%s'\n",
1337 : method,
1338 : url);
1339 : /* on repeated requests, check our cache first */
1340 0 : if (NULL != rc->rh)
1341 : {
1342 : MHD_RESULT ret;
1343 : const char *start;
1344 :
1345 0 : if ('\0' == url[0])
1346 : /* strange, should start with '/', treat as just "/" */
1347 0 : url = "/";
1348 0 : start = strchr (url + 1, '/');
1349 0 : if (NULL == start)
1350 0 : start = "";
1351 0 : ret = proceed_with_handler (rc,
1352 : start,
1353 : upload_data,
1354 : upload_data_size);
1355 0 : GNUNET_async_scope_restore (&old_scope);
1356 0 : return ret;
1357 : }
1358 :
1359 0 : if ( (0 == strcasecmp (method,
1360 0 : MHD_HTTP_METHOD_OPTIONS)) &&
1361 0 : (0 == strcmp ("*",
1362 : url)) )
1363 0 : return TALER_MHD_reply_cors_preflight (connection);
1364 :
1365 0 : if (0 == strcasecmp (method,
1366 : MHD_HTTP_METHOD_HEAD))
1367 0 : method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
1368 :
1369 : /* parse first part of URL */
1370 : {
1371 0 : bool found = false;
1372 : size_t tok_size;
1373 : const char *tok;
1374 : const char *rest;
1375 :
1376 0 : if ('\0' == url[0])
1377 : /* strange, should start with '/', treat as just "/" */
1378 0 : url = "/";
1379 0 : tok = url + 1;
1380 0 : rest = strchr (tok, '/');
1381 0 : if (NULL == rest)
1382 : {
1383 0 : tok_size = strlen (tok);
1384 : }
1385 : else
1386 : {
1387 0 : tok_size = rest - tok;
1388 0 : rest++; /* skip over '/' */
1389 : }
1390 0 : for (unsigned int i = 0; NULL != handlers[i].url; i++)
1391 : {
1392 0 : struct TEH_RequestHandler *rh = &handlers[i];
1393 :
1394 0 : if ( (0 != strncmp (tok,
1395 : rh->url,
1396 0 : tok_size)) ||
1397 0 : (tok_size != strlen (rh->url) ) )
1398 0 : continue;
1399 0 : found = true;
1400 : /* The URL is a match! What we now do depends on the method. */
1401 0 : if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS))
1402 : {
1403 0 : GNUNET_async_scope_restore (&old_scope);
1404 0 : return TALER_MHD_reply_cors_preflight (connection);
1405 : }
1406 0 : GNUNET_assert (NULL != rh->method);
1407 0 : if (0 == strcasecmp (method,
1408 : rh->method))
1409 : {
1410 : MHD_RESULT ret;
1411 :
1412 : /* cache to avoid the loop next time */
1413 0 : rc->rh = rh;
1414 : /* run handler */
1415 0 : ret = proceed_with_handler (rc,
1416 0 : url + tok_size + 1,
1417 : upload_data,
1418 : upload_data_size);
1419 0 : GNUNET_async_scope_restore (&old_scope);
1420 0 : return ret;
1421 : }
1422 : }
1423 :
1424 0 : if (found)
1425 : {
1426 : /* we found a matching address, but the method is wrong */
1427 : struct MHD_Response *reply;
1428 : MHD_RESULT ret;
1429 0 : char *allowed = NULL;
1430 :
1431 0 : GNUNET_break_op (0);
1432 0 : for (unsigned int i = 0; NULL != handlers[i].url; i++)
1433 : {
1434 0 : struct TEH_RequestHandler *rh = &handlers[i];
1435 :
1436 0 : if ( (0 != strncmp (tok,
1437 : rh->url,
1438 0 : tok_size)) ||
1439 0 : (tok_size != strlen (rh->url) ) )
1440 0 : continue;
1441 0 : if (NULL == allowed)
1442 : {
1443 0 : allowed = GNUNET_strdup (rh->method);
1444 : }
1445 : else
1446 : {
1447 : char *tmp;
1448 :
1449 0 : GNUNET_asprintf (&tmp,
1450 : "%s, %s",
1451 : allowed,
1452 : rh->method);
1453 0 : GNUNET_free (allowed);
1454 0 : allowed = tmp;
1455 : }
1456 0 : if (0 == strcasecmp (rh->method,
1457 : MHD_HTTP_METHOD_GET))
1458 : {
1459 : char *tmp;
1460 :
1461 0 : GNUNET_asprintf (&tmp,
1462 : "%s, %s",
1463 : allowed,
1464 : MHD_HTTP_METHOD_HEAD);
1465 0 : GNUNET_free (allowed);
1466 0 : allowed = tmp;
1467 : }
1468 : }
1469 0 : reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
1470 : method);
1471 0 : GNUNET_break (MHD_YES ==
1472 : MHD_add_response_header (reply,
1473 : MHD_HTTP_HEADER_ALLOW,
1474 : allowed));
1475 0 : GNUNET_free (allowed);
1476 0 : ret = MHD_queue_response (connection,
1477 : MHD_HTTP_METHOD_NOT_ALLOWED,
1478 : reply);
1479 0 : MHD_destroy_response (reply);
1480 0 : return ret;
1481 : }
1482 : }
1483 :
1484 : /* No handler matches, generate not found */
1485 : {
1486 : MHD_RESULT ret;
1487 :
1488 0 : ret = TALER_MHD_reply_with_error (connection,
1489 : MHD_HTTP_NOT_FOUND,
1490 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
1491 : url);
1492 0 : GNUNET_async_scope_restore (&old_scope);
1493 0 : return ret;
1494 : }
1495 : }
1496 :
1497 :
1498 : /**
1499 : * Load general KYC configuration parameters for the exchange server into the
1500 : * #TEH_kyc_config variable.
1501 : *
1502 : * @return #GNUNET_OK on success
1503 : */
1504 : static enum GNUNET_GenericReturnValue
1505 0 : parse_kyc_settings (void)
1506 : {
1507 0 : if (GNUNET_OK !=
1508 0 : GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
1509 : "exchange",
1510 : "KYC_WITHDRAW_PERIOD",
1511 : &TEH_kyc_config.withdraw_period))
1512 : {
1513 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1514 : "exchange",
1515 : "KYC_WITHDRAW_PERIOD",
1516 : "valid relative time expected");
1517 0 : return GNUNET_SYSERR;
1518 : }
1519 0 : if (GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period))
1520 0 : return GNUNET_OK;
1521 0 : if (GNUNET_OK !=
1522 0 : TALER_config_get_amount (TEH_cfg,
1523 : "exchange",
1524 : "KYC_WITHDRAW_LIMIT",
1525 : &TEH_kyc_config.withdraw_limit))
1526 0 : return GNUNET_SYSERR;
1527 0 : if (0 != strcasecmp (TEH_kyc_config.withdraw_limit.currency,
1528 : TEH_currency))
1529 : {
1530 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1531 : "exchange",
1532 : "KYC_WITHDRAW_LIMIT",
1533 : "currency mismatch");
1534 0 : return GNUNET_SYSERR;
1535 : }
1536 0 : return GNUNET_OK;
1537 : }
1538 :
1539 :
1540 : /**
1541 : * Load OAuth2.0 configuration parameters for the exchange server into the
1542 : * #TEH_kyc_config variable.
1543 : *
1544 : * @return #GNUNET_OK on success
1545 : */
1546 : static enum GNUNET_GenericReturnValue
1547 0 : parse_kyc_oauth_cfg (void)
1548 : {
1549 : char *s;
1550 :
1551 0 : if (GNUNET_OK !=
1552 0 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1553 : "exchange-kyc-oauth2",
1554 : "KYC_OAUTH2_AUTH_URL",
1555 : &s))
1556 : {
1557 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1558 : "exchange-kyc-oauth2",
1559 : "KYC_OAUTH2_AUTH_URL");
1560 0 : return GNUNET_SYSERR;
1561 : }
1562 0 : if ( (! TALER_url_valid_charset (s)) ||
1563 0 : ( (0 != strncasecmp (s,
1564 : "http://",
1565 0 : strlen ("http://"))) &&
1566 0 : (0 != strncasecmp (s,
1567 : "https://",
1568 : strlen ("https://"))) ) )
1569 : {
1570 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1571 : "exchange-kyc-oauth2",
1572 : "KYC_OAUTH2_AUTH_URL",
1573 : "not a valid URL");
1574 0 : GNUNET_free (s);
1575 0 : return GNUNET_SYSERR;
1576 : }
1577 0 : TEH_kyc_config.details.oauth2.auth_url = s;
1578 :
1579 0 : if (GNUNET_OK !=
1580 0 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1581 : "exchange-kyc-oauth2",
1582 : "KYC_OAUTH2_LOGIN_URL",
1583 : &s))
1584 : {
1585 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1586 : "exchange-kyc-oauth2",
1587 : "KYC_OAUTH2_LOGIN_URL");
1588 0 : return GNUNET_SYSERR;
1589 : }
1590 0 : if ( (! TALER_url_valid_charset (s)) ||
1591 0 : ( (0 != strncasecmp (s,
1592 : "http://",
1593 0 : strlen ("http://"))) &&
1594 0 : (0 != strncasecmp (s,
1595 : "https://",
1596 : strlen ("https://"))) ) )
1597 : {
1598 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1599 : "exchange-kyc-oauth2",
1600 : "KYC_OAUTH2_LOGIN_URL",
1601 : "not a valid URL");
1602 0 : GNUNET_free (s);
1603 0 : return GNUNET_SYSERR;
1604 : }
1605 0 : TEH_kyc_config.details.oauth2.login_url = s;
1606 :
1607 0 : if (GNUNET_OK !=
1608 0 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1609 : "exchange-kyc-oauth2",
1610 : "KYC_INFO_URL",
1611 : &s))
1612 : {
1613 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1614 : "exchange-kyc-oauth2",
1615 : "KYC_INFO_URL");
1616 0 : return GNUNET_SYSERR;
1617 : }
1618 0 : if ( (! TALER_url_valid_charset (s)) ||
1619 0 : ( (0 != strncasecmp (s,
1620 : "http://",
1621 0 : strlen ("http://"))) &&
1622 0 : (0 != strncasecmp (s,
1623 : "https://",
1624 : strlen ("https://"))) ) )
1625 : {
1626 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1627 : "exchange-kyc-oauth2",
1628 : "KYC_INFO_URL",
1629 : "not a valid URL");
1630 0 : GNUNET_free (s);
1631 0 : return GNUNET_SYSERR;
1632 : }
1633 0 : TEH_kyc_config.details.oauth2.info_url = s;
1634 :
1635 0 : if (GNUNET_OK !=
1636 0 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1637 : "exchange-kyc-oauth2",
1638 : "KYC_OAUTH2_CLIENT_ID",
1639 : &s))
1640 : {
1641 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1642 : "exchange-kyc-oauth2",
1643 : "KYC_OAUTH2_CLIENT_ID");
1644 0 : return GNUNET_SYSERR;
1645 : }
1646 0 : TEH_kyc_config.details.oauth2.client_id = s;
1647 :
1648 0 : if (GNUNET_OK !=
1649 0 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1650 : "exchange-kyc-oauth2",
1651 : "KYC_OAUTH2_CLIENT_SECRET",
1652 : &s))
1653 : {
1654 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1655 : "exchange-kyc-oauth2",
1656 : "KYC_OAUTH2_CLIENT_SECRET");
1657 0 : return GNUNET_SYSERR;
1658 : }
1659 0 : TEH_kyc_config.details.oauth2.client_secret = s;
1660 :
1661 0 : if (GNUNET_OK !=
1662 0 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1663 : "exchange-kyc-oauth2",
1664 : "KYC_OAUTH2_POST_URL",
1665 : &s))
1666 : {
1667 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1668 : "exchange-kyc-oauth2",
1669 : "KYC_OAUTH2_POST_URL");
1670 0 : return GNUNET_SYSERR;
1671 : }
1672 0 : TEH_kyc_config.details.oauth2.post_kyc_redirect_url = s;
1673 0 : return GNUNET_OK;
1674 : }
1675 :
1676 :
1677 : /**
1678 : * Load configuration parameters for the exchange
1679 : * server into the corresponding global variables.
1680 : *
1681 : * @return #GNUNET_OK on success
1682 : */
1683 : static enum GNUNET_GenericReturnValue
1684 1 : exchange_serve_process_config (void)
1685 : {
1686 1 : if (GNUNET_OK !=
1687 1 : TALER_KYCLOGIC_kyc_init (TEH_cfg))
1688 : {
1689 0 : return GNUNET_SYSERR;
1690 : }
1691 : {
1692 : char *kyc_mode;
1693 :
1694 1 : if (GNUNET_OK !=
1695 1 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1696 : "exchange",
1697 : "KYC_MODE",
1698 : &kyc_mode))
1699 : {
1700 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1701 : "exchange",
1702 : "KYC_MODE");
1703 0 : return GNUNET_SYSERR;
1704 : }
1705 1 : if (0 == strcasecmp (kyc_mode,
1706 : "NONE"))
1707 : {
1708 1 : TEH_kyc_config.mode = TEH_KYC_NONE;
1709 : }
1710 0 : else if (0 == strcasecmp (kyc_mode,
1711 : "OAUTH2"))
1712 : {
1713 0 : TEH_kyc_config.mode = TEH_KYC_OAUTH2;
1714 0 : if (GNUNET_OK !=
1715 0 : parse_kyc_oauth_cfg ())
1716 : {
1717 0 : GNUNET_free (kyc_mode);
1718 0 : return GNUNET_SYSERR;
1719 : }
1720 : }
1721 : else
1722 : {
1723 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1724 : "exchange",
1725 : "KYC_MODE",
1726 : "Must be 'NONE' or 'OAUTH2'");
1727 0 : GNUNET_free (kyc_mode);
1728 0 : return GNUNET_SYSERR;
1729 : }
1730 1 : GNUNET_free (kyc_mode);
1731 : }
1732 1 : if (GNUNET_OK !=
1733 1 : GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
1734 : "exchange",
1735 : "MAX_REQUESTS",
1736 : &req_max))
1737 : {
1738 1 : req_max = ULLONG_MAX;
1739 : }
1740 1 : if (GNUNET_OK !=
1741 1 : GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
1742 : "exchangedb",
1743 : "IDLE_RESERVE_EXPIRATION_TIME",
1744 : &TEH_reserve_closing_delay))
1745 : {
1746 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1747 : "exchangedb",
1748 : "IDLE_RESERVE_EXPIRATION_TIME");
1749 : /* use default */
1750 : TEH_reserve_closing_delay
1751 0 : = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
1752 : 4);
1753 : }
1754 :
1755 1 : if (GNUNET_OK !=
1756 1 : GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
1757 : "exchange",
1758 : "MAX_KEYS_CACHING",
1759 : &TEH_max_keys_caching))
1760 : {
1761 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1762 : "exchange",
1763 : "MAX_KEYS_CACHING",
1764 : "valid relative time expected");
1765 0 : return GNUNET_SYSERR;
1766 : }
1767 1 : if (GNUNET_OK !=
1768 1 : TALER_config_get_currency (TEH_cfg,
1769 : &TEH_currency))
1770 : {
1771 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1772 : "taler",
1773 : "CURRENCY");
1774 0 : return GNUNET_SYSERR;
1775 : }
1776 1 : if (GNUNET_OK !=
1777 1 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1778 : "exchange",
1779 : "BASE_URL",
1780 : &TEH_base_url))
1781 : {
1782 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1783 : "exchange",
1784 : "BASE_URL");
1785 0 : return GNUNET_SYSERR;
1786 : }
1787 1 : if (! TALER_url_valid_charset (TEH_base_url))
1788 : {
1789 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1790 : "exchange",
1791 : "BASE_URL",
1792 : "invalid URL");
1793 0 : return GNUNET_SYSERR;
1794 : }
1795 :
1796 1 : if (TEH_KYC_NONE != TEH_kyc_config.mode)
1797 : {
1798 0 : if (GNUNET_YES ==
1799 0 : GNUNET_CONFIGURATION_have_value (TEH_cfg,
1800 : "exchange",
1801 : "KYC_WALLET_BALANCE_LIMIT"))
1802 : {
1803 0 : if ( (GNUNET_OK !=
1804 0 : TALER_config_get_amount (TEH_cfg,
1805 : "exchange",
1806 : "KYC_WALLET_BALANCE_LIMIT",
1807 0 : &TEH_kyc_config.wallet_balance_limit)) ||
1808 0 : (0 != strcasecmp (TEH_currency,
1809 : TEH_kyc_config.wallet_balance_limit.currency)) )
1810 : {
1811 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1812 : "exchange",
1813 : "KYC_WALLET_BALANCE_LIMIT",
1814 : "valid amount expected");
1815 0 : return GNUNET_SYSERR;
1816 : }
1817 : }
1818 : else
1819 : {
1820 0 : memset (&TEH_kyc_config.wallet_balance_limit,
1821 : 0,
1822 : sizeof (TEH_kyc_config.wallet_balance_limit));
1823 : }
1824 : }
1825 : {
1826 : char *master_public_key_str;
1827 :
1828 1 : if (GNUNET_OK !=
1829 1 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
1830 : "exchange",
1831 : "MASTER_PUBLIC_KEY",
1832 : &master_public_key_str))
1833 : {
1834 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1835 : "exchange",
1836 : "MASTER_PUBLIC_KEY");
1837 0 : return GNUNET_SYSERR;
1838 : }
1839 1 : if (GNUNET_OK !=
1840 1 : GNUNET_CRYPTO_eddsa_public_key_from_string (master_public_key_str,
1841 : strlen (
1842 : master_public_key_str),
1843 : &TEH_master_public_key.
1844 : eddsa_pub))
1845 : {
1846 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1847 : "exchange",
1848 : "MASTER_PUBLIC_KEY",
1849 : "invalid base32 encoding for a master public key");
1850 0 : GNUNET_free (master_public_key_str);
1851 0 : return GNUNET_SYSERR;
1852 : }
1853 1 : GNUNET_free (master_public_key_str);
1854 : }
1855 1 : if (TEH_KYC_NONE != TEH_kyc_config.mode)
1856 : {
1857 0 : if (GNUNET_OK !=
1858 0 : parse_kyc_settings ())
1859 0 : return GNUNET_SYSERR;
1860 : }
1861 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1862 : "Launching exchange with public key `%s'...\n",
1863 : GNUNET_p2s (&TEH_master_public_key.eddsa_pub));
1864 :
1865 1 : if (NULL ==
1866 1 : (TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg)))
1867 : {
1868 1 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1869 : "Failed to initialize DB subsystem\n");
1870 1 : return GNUNET_SYSERR;
1871 : }
1872 0 : return GNUNET_OK;
1873 : }
1874 :
1875 :
1876 : /**
1877 : * Called when the main thread exits, writes out performance
1878 : * stats if requested.
1879 : */
1880 : static void
1881 0 : write_stats (void)
1882 : {
1883 : struct GNUNET_DISK_FileHandle *fh;
1884 0 : pid_t pid = getpid ();
1885 : char *benchmark_dir;
1886 : char *s;
1887 : struct rusage usage;
1888 :
1889 0 : benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
1890 0 : if (NULL == benchmark_dir)
1891 0 : return;
1892 0 : GNUNET_asprintf (&s,
1893 : "%s/taler-exchange-%llu.txt",
1894 : benchmark_dir,
1895 : (unsigned long long) pid);
1896 0 : fh = GNUNET_DISK_file_open (s,
1897 : (GNUNET_DISK_OPEN_WRITE
1898 : | GNUNET_DISK_OPEN_TRUNCATE
1899 : | GNUNET_DISK_OPEN_CREATE),
1900 : (GNUNET_DISK_PERM_USER_READ
1901 : | GNUNET_DISK_PERM_USER_WRITE));
1902 0 : GNUNET_free (s);
1903 0 : if (NULL == fh)
1904 0 : return; /* permission denied? */
1905 :
1906 : /* Collect stats, summed up for all threads */
1907 0 : GNUNET_assert (0 ==
1908 : getrusage (RUSAGE_SELF,
1909 : &usage));
1910 0 : GNUNET_asprintf (&s,
1911 : "time_exchange sys %llu user %llu\n",
1912 0 : (unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000
1913 0 : + usage.ru_stime.tv_usec),
1914 0 : (unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000
1915 0 : + usage.ru_utime.tv_usec));
1916 0 : GNUNET_assert (GNUNET_SYSERR !=
1917 : GNUNET_DISK_file_write_blocking (fh,
1918 : s,
1919 : strlen (s)));
1920 0 : GNUNET_free (s);
1921 0 : GNUNET_assert (GNUNET_OK ==
1922 : GNUNET_DISK_file_close (fh));
1923 : }
1924 :
1925 :
1926 : /* Developer logic for supporting the `-f' option. */
1927 : #if HAVE_DEVELOPER
1928 :
1929 : /**
1930 : * Option `-f' (specifies an input file to give to the HTTP server).
1931 : */
1932 : static char *input_filename;
1933 :
1934 :
1935 : /**
1936 : * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename
1937 : * as the input for the request. If launching the client worked,
1938 : * run the #TEH_KS_loop() event loop as usual.
1939 : *
1940 : * @return child pid
1941 : */
1942 : static pid_t
1943 0 : run_fake_client (void)
1944 : {
1945 : pid_t cld;
1946 : char ports[6];
1947 : int fd;
1948 :
1949 0 : if (0 == strcmp (input_filename,
1950 : "-"))
1951 0 : fd = STDIN_FILENO;
1952 : else
1953 0 : fd = open (input_filename,
1954 : O_RDONLY);
1955 0 : if (-1 == fd)
1956 : {
1957 0 : fprintf (stderr,
1958 : "Failed to open `%s': %s\n",
1959 : input_filename,
1960 0 : strerror (errno));
1961 0 : return -1;
1962 : }
1963 : /* Fake HTTP client request with #input_filename as input.
1964 : We do this using the nc tool. */
1965 0 : GNUNET_snprintf (ports,
1966 : sizeof (ports),
1967 : "%u",
1968 : serve_port);
1969 0 : if (0 == (cld = fork ()))
1970 : {
1971 0 : GNUNET_break (0 == close (0));
1972 0 : GNUNET_break (0 == dup2 (fd, 0));
1973 0 : GNUNET_break (0 == close (fd));
1974 0 : if ( (0 != execlp ("nc",
1975 : "nc",
1976 : "localhost",
1977 : ports,
1978 : "-w", "30",
1979 0 : NULL)) &&
1980 0 : (0 != execlp ("ncat",
1981 : "ncat",
1982 : "localhost",
1983 : ports,
1984 : "-i", "30",
1985 : NULL)) )
1986 : {
1987 0 : fprintf (stderr,
1988 : "Failed to run both `nc' and `ncat': %s\n",
1989 0 : strerror (errno));
1990 : }
1991 0 : _exit (1);
1992 : }
1993 : /* parent process */
1994 0 : if (0 != strcmp (input_filename,
1995 : "-"))
1996 0 : GNUNET_break (0 == close (fd));
1997 0 : return cld;
1998 : }
1999 :
2000 :
2001 : /**
2002 : * Run the exchange to serve a single request only, without threads.
2003 : *
2004 : * @return #GNUNET_OK on success
2005 : */
2006 : static void
2007 0 : run_single_request (void)
2008 : {
2009 : pid_t xfork;
2010 :
2011 0 : xfork = fork ();
2012 0 : if (-1 == xfork)
2013 : {
2014 0 : global_ret = EXIT_FAILURE;
2015 0 : GNUNET_SCHEDULER_shutdown ();
2016 0 : return;
2017 : }
2018 0 : if (0 == xfork)
2019 : {
2020 : pid_t cld;
2021 :
2022 0 : cld = run_fake_client ();
2023 0 : if (-1 == cld)
2024 0 : _exit (EXIT_FAILURE);
2025 0 : _exit (EXIT_SUCCESS);
2026 : }
2027 :
2028 : {
2029 : int status;
2030 :
2031 0 : if (xfork != waitpid (xfork,
2032 : &status,
2033 : 0))
2034 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2035 : "Waiting for `nc' child failed: %s\n",
2036 : strerror (errno));
2037 : }
2038 : }
2039 :
2040 :
2041 : /* end of HAVE_DEVELOPER */
2042 : #endif
2043 :
2044 :
2045 : /**
2046 : * Signature of the callback used by MHD to notify the application
2047 : * about completed connections. If we are running in test-mode with
2048 : * an input_filename, this function is used to terminate the HTTPD
2049 : * after the first request has been processed.
2050 : *
2051 : * @param cls client-defined closure, NULL
2052 : * @param connection connection handle (ignored)
2053 : * @param socket_context socket-specific pointer (ignored)
2054 : * @param toe reason for connection notification
2055 : */
2056 : static void
2057 0 : connection_done (void *cls,
2058 : struct MHD_Connection *connection,
2059 : void **socket_context,
2060 : enum MHD_ConnectionNotificationCode toe)
2061 : {
2062 : (void) cls;
2063 : (void) connection;
2064 : (void) socket_context;
2065 :
2066 0 : switch (toe)
2067 : {
2068 0 : case MHD_CONNECTION_NOTIFY_STARTED:
2069 0 : active_connections++;
2070 0 : break;
2071 0 : case MHD_CONNECTION_NOTIFY_CLOSED:
2072 0 : active_connections--;
2073 0 : if (TEH_suicide &&
2074 0 : (0 == active_connections) )
2075 0 : GNUNET_SCHEDULER_shutdown ();
2076 0 : break;
2077 : }
2078 : #if HAVE_DEVELOPER
2079 : /* We only act if the connection is closed. */
2080 0 : if (MHD_CONNECTION_NOTIFY_CLOSED != toe)
2081 0 : return;
2082 0 : if (NULL != input_filename)
2083 0 : GNUNET_SCHEDULER_shutdown ();
2084 : #endif
2085 : }
2086 :
2087 :
2088 : /**
2089 : * Function run on shutdown.
2090 : *
2091 : * @param cls NULL
2092 : */
2093 : static void
2094 0 : do_shutdown (void *cls)
2095 : {
2096 : struct MHD_Daemon *mhd;
2097 : (void) cls;
2098 :
2099 0 : mhd = TALER_MHD_daemon_stop ();
2100 0 : TEH_resume_keys_requests (true);
2101 0 : TEH_reserves_get_cleanup ();
2102 0 : TEH_purses_get_cleanup ();
2103 0 : TEH_kyc_check_cleanup ();
2104 0 : TEH_kyc_proof_cleanup ();
2105 0 : TALER_KYCLOGIC_kyc_done ();
2106 0 : if (NULL != mhd)
2107 : {
2108 0 : MHD_stop_daemon (mhd);
2109 0 : mhd = NULL;
2110 : }
2111 0 : TEH_wire_done ();
2112 0 : TEH_extensions_done ();
2113 0 : TEH_keys_finished ();
2114 0 : if (NULL != TEH_plugin)
2115 : {
2116 0 : TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
2117 0 : TEH_plugin = NULL;
2118 : }
2119 0 : if (NULL != TEH_curl_ctx)
2120 : {
2121 0 : GNUNET_CURL_fini (TEH_curl_ctx);
2122 0 : TEH_curl_ctx = NULL;
2123 : }
2124 0 : if (NULL != exchange_curl_rc)
2125 : {
2126 0 : GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc);
2127 0 : exchange_curl_rc = NULL;
2128 : }
2129 0 : TALER_TEMPLATING_done ();
2130 0 : }
2131 :
2132 :
2133 : /**
2134 : * Main function that will be run by the scheduler.
2135 : *
2136 : * @param cls closure
2137 : * @param args remaining command-line arguments
2138 : * @param cfgfile name of the configuration file used (for saving, can be
2139 : * NULL!)
2140 : * @param config configuration
2141 : */
2142 : static void
2143 1 : run (void *cls,
2144 : char *const *args,
2145 : const char *cfgfile,
2146 : const struct GNUNET_CONFIGURATION_Handle *config)
2147 : {
2148 : enum TALER_MHD_GlobalOptions go;
2149 : int fh;
2150 :
2151 : (void) cls;
2152 : (void) args;
2153 : (void ) cfgfile;
2154 1 : go = TALER_MHD_GO_NONE;
2155 1 : if (connection_close)
2156 0 : go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
2157 1 : TALER_MHD_setup (go);
2158 1 : TEH_cfg = config;
2159 :
2160 1 : if (GNUNET_OK !=
2161 1 : exchange_serve_process_config ())
2162 : {
2163 1 : global_ret = EXIT_NOTCONFIGURED;
2164 1 : GNUNET_SCHEDULER_shutdown ();
2165 1 : return;
2166 : }
2167 0 : if (GNUNET_OK !=
2168 0 : TALER_TEMPLATING_init ("exchange"))
2169 : {
2170 0 : global_ret = EXIT_FAILURE;
2171 0 : GNUNET_SCHEDULER_shutdown ();
2172 0 : return;
2173 : }
2174 0 : if (GNUNET_SYSERR ==
2175 0 : TEH_plugin->preflight (TEH_plugin->cls))
2176 : {
2177 0 : global_ret = EXIT_FAILURE;
2178 0 : GNUNET_SCHEDULER_shutdown ();
2179 0 : return;
2180 : }
2181 0 : if (GNUNET_OK !=
2182 0 : TEH_extensions_init ())
2183 : {
2184 0 : global_ret = EXIT_FAILURE;
2185 0 : GNUNET_SCHEDULER_shutdown ();
2186 0 : return;
2187 : }
2188 0 : if (GNUNET_OK !=
2189 0 : TEH_keys_init ())
2190 : {
2191 0 : global_ret = EXIT_FAILURE;
2192 0 : GNUNET_SCHEDULER_shutdown ();
2193 0 : return;
2194 : }
2195 0 : if (GNUNET_OK !=
2196 0 : TEH_wire_init ())
2197 : {
2198 0 : global_ret = EXIT_FAILURE;
2199 0 : GNUNET_SCHEDULER_shutdown ();
2200 0 : return;
2201 : }
2202 :
2203 0 : TEH_load_terms (TEH_cfg);
2204 : TEH_curl_ctx
2205 0 : = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
2206 : &exchange_curl_rc);
2207 0 : if (NULL == TEH_curl_ctx)
2208 : {
2209 0 : GNUNET_break (0);
2210 0 : global_ret = EXIT_FAILURE;
2211 0 : GNUNET_SCHEDULER_shutdown ();
2212 0 : return;
2213 : }
2214 0 : exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx);
2215 0 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2216 : NULL);
2217 0 : fh = TALER_MHD_bind (TEH_cfg,
2218 : "exchange",
2219 : &serve_port);
2220 0 : if ( (0 == serve_port) &&
2221 : (-1 == fh) )
2222 : {
2223 0 : GNUNET_SCHEDULER_shutdown ();
2224 0 : return;
2225 : }
2226 0 : mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
2227 : | MHD_USE_PIPE_FOR_SHUTDOWN
2228 : | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
2229 : | MHD_USE_TCP_FASTOPEN,
2230 : (-1 == fh) ? serve_port : 0,
2231 : NULL, NULL,
2232 : &handle_mhd_request, NULL,
2233 : MHD_OPTION_LISTEN_BACKLOG_SIZE,
2234 : (unsigned int) 1024,
2235 : MHD_OPTION_LISTEN_SOCKET,
2236 : fh,
2237 : MHD_OPTION_EXTERNAL_LOGGER,
2238 : &TALER_MHD_handle_logs,
2239 : NULL,
2240 : MHD_OPTION_NOTIFY_COMPLETED,
2241 : &handle_mhd_completion_callback,
2242 : NULL,
2243 : MHD_OPTION_NOTIFY_CONNECTION,
2244 : &connection_done,
2245 : NULL,
2246 : MHD_OPTION_CONNECTION_TIMEOUT,
2247 : connection_timeout,
2248 0 : (0 == allow_address_reuse)
2249 : ? MHD_OPTION_END
2250 : : MHD_OPTION_LISTENING_ADDRESS_REUSE,
2251 : (unsigned int) allow_address_reuse,
2252 : MHD_OPTION_END);
2253 0 : if (NULL == mhd)
2254 : {
2255 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2256 : "Failed to launch HTTP service. Is the port in use?\n");
2257 0 : GNUNET_SCHEDULER_shutdown ();
2258 0 : return;
2259 : }
2260 0 : global_ret = EXIT_SUCCESS;
2261 0 : TALER_MHD_daemon_start (mhd);
2262 0 : atexit (&write_stats);
2263 :
2264 : #if HAVE_DEVELOPER
2265 0 : if (NULL != input_filename)
2266 0 : run_single_request ();
2267 : #endif
2268 : }
2269 :
2270 :
2271 : /**
2272 : * The main function of the taler-exchange-httpd server ("the exchange").
2273 : *
2274 : * @param argc number of arguments from the command line
2275 : * @param argv command line arguments
2276 : * @return 0 ok, 1 on error
2277 : */
2278 : int
2279 1 : main (int argc,
2280 : char *const *argv)
2281 : {
2282 1 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
2283 1 : GNUNET_GETOPT_option_flag ('a',
2284 : "allow-timetravel",
2285 : "allow clients to request /keys for arbitrary timestamps (for testing and development only)",
2286 : &TEH_allow_keys_timetravel),
2287 1 : GNUNET_GETOPT_option_flag ('C',
2288 : "connection-close",
2289 : "force HTTP connections to be closed after each request",
2290 : &connection_close),
2291 1 : GNUNET_GETOPT_option_flag ('I',
2292 : "check-invariants",
2293 : "enable expensive invariant checks",
2294 : &TEH_check_invariants_flag),
2295 1 : GNUNET_GETOPT_option_flag ('r',
2296 : "allow-reuse-address",
2297 : "allow multiple HTTPDs to listen to the same port",
2298 : &allow_address_reuse),
2299 1 : GNUNET_GETOPT_option_uint ('t',
2300 : "timeout",
2301 : "SECONDS",
2302 : "after how long do connections timeout by default (in seconds)",
2303 : &connection_timeout),
2304 1 : GNUNET_GETOPT_option_timetravel ('T',
2305 : "timetravel"),
2306 : #if HAVE_DEVELOPER
2307 1 : GNUNET_GETOPT_option_filename ('f',
2308 : "file-input",
2309 : "FILENAME",
2310 : "run in test-mode using FILENAME as the HTTP request to process, use '-' to read from stdin",
2311 : &input_filename),
2312 : #endif
2313 1 : GNUNET_GETOPT_option_help (
2314 : "HTTP server providing a RESTful API to access a Taler exchange"),
2315 : GNUNET_GETOPT_OPTION_END
2316 : };
2317 : enum GNUNET_GenericReturnValue ret;
2318 :
2319 1 : TALER_OS_init ();
2320 1 : ret = GNUNET_PROGRAM_run (argc, argv,
2321 : "taler-exchange-httpd",
2322 : "Taler exchange HTTP service",
2323 : options,
2324 : &run, NULL);
2325 1 : if (GNUNET_SYSERR == ret)
2326 0 : return EXIT_INVALIDARGUMENT;
2327 1 : if (GNUNET_NO == ret)
2328 0 : return EXIT_SUCCESS;
2329 1 : return global_ret;
2330 : }
2331 :
2332 :
2333 : /* end of taler-exchange-httpd.c */
|