Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2024 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 : * @defgroup request Request handling routines
20 : * @author Florian Dold
21 : * @author Benedikt Mueller
22 : * @author Christian Grothoff
23 : */
24 : #include "taler/platform.h"
25 : #include <gnunet/gnunet_util_lib.h>
26 : #include <jansson.h>
27 : #include <microhttpd.h>
28 : #include <sched.h>
29 : #include <sys/resource.h>
30 : #include <limits.h>
31 : #include "taler/taler_kyclogic_lib.h"
32 : #include "taler/taler_templating_lib.h"
33 : #include "taler/taler_mhd_lib.h"
34 : #include "taler-exchange-httpd_withdraw.h"
35 : #include "taler-exchange-httpd_aml-attributes-get.h"
36 : #include "taler-exchange-httpd_aml-decision.h"
37 : #include "taler-exchange-httpd_aml-legitimization-measures-get.h"
38 : #include "taler-exchange-httpd_aml-statistics-get.h"
39 : #include "taler-exchange-httpd_aml-transfer-get.h"
40 : #include "taler-exchange-httpd_aml-measures-get.h"
41 : #include "taler-exchange-httpd_auditors.h"
42 : #include "taler-exchange-httpd_batch-deposit.h"
43 : #include "taler-exchange-httpd_blinding-prepare.h"
44 : #include "taler-exchange-httpd_coins_get.h"
45 : #include "taler-exchange-httpd_config.h"
46 : #include "taler-exchange-httpd_contract.h"
47 : #include "taler-exchange-httpd_deposits_get.h"
48 : #include "taler-exchange-httpd_extensions.h"
49 : #include "taler-exchange-httpd_keys.h"
50 : #include "taler-exchange-httpd_kyc-check.h"
51 : #include "taler-exchange-httpd_kyc-info.h"
52 : #include "taler-exchange-httpd_kyc-proof.h"
53 : #include "taler-exchange-httpd_kyc-start.h"
54 : #include "taler-exchange-httpd_kyc-upload.h"
55 : #include "taler-exchange-httpd_kyc-wallet.h"
56 : #include "taler-exchange-httpd_kyc-webhook.h"
57 : #include "taler-exchange-httpd_aml-decision.h"
58 : #include "taler-exchange-httpd_management.h"
59 : #include "taler-exchange-httpd_melt_v27.h"
60 : #include "taler-exchange-httpd_metrics.h"
61 : #include "taler-exchange-httpd_mhd.h"
62 : #include "taler-exchange-httpd_purses_create.h"
63 : #include "taler-exchange-httpd_purses_deposit.h"
64 : #include "taler-exchange-httpd_purses_get.h"
65 : #include "taler-exchange-httpd_purses_delete.h"
66 : #include "taler-exchange-httpd_purses_merge.h"
67 : #include "taler-exchange-httpd_recoup.h"
68 : #include "taler-exchange-httpd_recoup-refresh.h"
69 : #include "taler-exchange-httpd_refund.h"
70 : #include "taler-exchange-httpd_reserves_attest.h"
71 : #include "taler-exchange-httpd_reserves_close.h"
72 : #include "taler-exchange-httpd_reserves_get.h"
73 : #include "taler-exchange-httpd_reserves_get_attest.h"
74 : #include "taler-exchange-httpd_reserves_history.h"
75 : #include "taler-exchange-httpd_reserves_open.h"
76 : #include "taler-exchange-httpd_reserves_purse.h"
77 : #include "taler-exchange-httpd_reveal-withdraw.h"
78 : #include "taler-exchange-httpd_reveal-melt.h"
79 : #include "taler-exchange-httpd_spa.h"
80 : #include "taler-exchange-httpd_terms.h"
81 : #include "taler-exchange-httpd_transfers_get.h"
82 : #include "taler/taler_exchangedb_lib.h"
83 : #include "taler/taler_exchangedb_plugin.h"
84 : #include "taler/taler_extensions.h"
85 : #include <gnunet/gnunet_mhd_compat.h>
86 :
87 : /**
88 : * Backlog for listen operation on unix domain sockets.
89 : */
90 : #define UNIX_BACKLOG 50
91 :
92 : /**
93 : * How often will we try to connect to the database before giving up?
94 : */
95 : #define MAX_DB_RETRIES 5
96 :
97 : /**
98 : * Above what request latency do we start to log?
99 : */
100 : #define WARN_LATENCY GNUNET_TIME_relative_multiply ( \
101 : GNUNET_TIME_UNIT_MILLISECONDS, 500)
102 :
103 : /**
104 : * Are clients allowed to request /keys for times other than the
105 : * current time? Allowing this could be abused in a DoS-attack
106 : * as building new /keys responses is expensive. Should only be
107 : * enabled for testcases, development and test systems.
108 : */
109 : int TEH_allow_keys_timetravel;
110 :
111 : /**
112 : * Should we allow two HTTPDs to bind to the same port?
113 : */
114 : static int allow_address_reuse;
115 :
116 : /**
117 : * The exchange's configuration (global)
118 : */
119 : const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
120 :
121 : /**
122 : * Configuration of age restriction
123 : *
124 : * Set after loading the library, enabled in database event handler.
125 : */
126 : bool TEH_age_restriction_enabled = false;
127 : struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0};
128 :
129 : /**
130 : * Set to true if we started *any* HTTP daemons.
131 : */
132 : static bool have_daemons;
133 :
134 : /**
135 : * How long is caching /keys allowed at most? (global)
136 : */
137 : struct GNUNET_TIME_Relative TEH_max_keys_caching;
138 :
139 : /**
140 : * How long is the delay before we close reserves?
141 : */
142 : struct GNUNET_TIME_Relative TEH_reserve_closing_delay;
143 :
144 : /**
145 : * How long do we allow AML programs to run?
146 : */
147 : struct GNUNET_TIME_Relative TEH_aml_program_timeout;
148 :
149 : /**
150 : * Master public key (according to the
151 : * configuration in the exchange directory). (global)
152 : */
153 : struct TALER_MasterPublicKeyP TEH_master_public_key;
154 :
155 : /**
156 : * Key used to encrypt KYC attribute data in our database.
157 : */
158 : struct TALER_AttributeEncryptionKeyP TEH_attribute_key;
159 :
160 : /**
161 : * Our DB plugin. (global)
162 : */
163 : struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
164 :
165 : /**
166 : * Absolute STEFAN parameter.
167 : */
168 : struct TALER_Amount TEH_stefan_abs;
169 :
170 : /**
171 : * Logarithmic STEFAN parameter.
172 : */
173 : struct TALER_Amount TEH_stefan_log;
174 :
175 : /**
176 : * Smallest amount that can be transferred. Used for the
177 : * KYC auth transfers by default.
178 : */
179 : struct TALER_Amount TEH_tiny_amount;
180 :
181 : /**
182 : * Linear STEFAN parameter.
183 : */
184 : float TEH_stefan_lin;
185 :
186 : /**
187 : * JSON array with hard limits for /keys response.
188 : */
189 : json_t *TEH_hard_limits;
190 :
191 : /**
192 : * JSON array with zero limits for /keys response.
193 : */
194 : json_t *TEH_zero_limits;
195 :
196 : /**
197 : * URL where users can discover shops accepting digital cash
198 : * issued by this exchange. Can be NULL.
199 : */
200 : char *TEH_shopping_url;
201 :
202 : /**
203 : * Where to redirect users from "/"?
204 : */
205 : static char *toplevel_redirect_url;
206 :
207 : /**
208 : * Our currency.
209 : */
210 : char *TEH_currency;
211 :
212 : /**
213 : * Our base URL.
214 : */
215 : char *TEH_base_url;
216 :
217 : /**
218 : * Default timeout in seconds for HTTP requests.
219 : */
220 : static unsigned int connection_timeout = 30;
221 :
222 : /**
223 : * -C command-line flag given?
224 : */
225 : static int connection_close;
226 :
227 : /**
228 : * Allows banks to select a custom UI/UX for certain bank-specific
229 : * wallet functions, such as specific texts for buttons based on
230 : * legal requirements.
231 : */
232 : char *TEH_bank_compliance_language;
233 :
234 : /**
235 : * Determines the set of AML forms (and other bank-specific settings)
236 : * to be selected for the AML SPA.
237 : * legal requirements.
238 : */
239 : char *TEH_aml_spa_dialect;
240 :
241 : /**
242 : * Option set to #GNUNET_YES if KYC/AML are enabled.
243 : */
244 : int TEH_enable_kyc;
245 :
246 : /**
247 : * -I command-line flag given?
248 : */
249 : int TEH_check_invariants_flag;
250 :
251 : /**
252 : * True if we should commit suicide once all active
253 : * connections are finished.
254 : */
255 : bool TEH_suicide;
256 :
257 : /**
258 : * Signature of the configuration of all enabled extensions,
259 : * signed by the exchange's offline master key with purpose
260 : * TALER_SIGNATURE_MASTER_EXTENSION.
261 : */
262 : struct TALER_MasterSignatureP TEH_extensions_sig;
263 : bool TEH_extensions_signed = false;
264 :
265 : /**
266 : * Value to return from main()
267 : */
268 : static int global_ret;
269 :
270 : /**
271 : * Counter for the number of requests this HTTP has processed so far.
272 : */
273 : static unsigned long long req_count;
274 :
275 : /**
276 : * Counter for the number of open connections.
277 : */
278 : static unsigned long long active_connections;
279 :
280 : /**
281 : * Limit for the number of requests this HTTP may process before restarting.
282 : * (This was added as one way of dealing with unavoidable memory fragmentation
283 : * happening slowly over time.)
284 : */
285 : static unsigned long long req_max;
286 :
287 : /**
288 : * Length of the cspecs array.
289 : */
290 : static unsigned int num_cspecs;
291 :
292 : /**
293 : * Rendering specs for currencies.
294 : */
295 : static struct TALER_CurrencySpecification *cspecs;
296 :
297 : /**
298 : * Rendering spec for our currency.
299 : */
300 : const struct TALER_CurrencySpecification *TEH_cspec;
301 :
302 :
303 : /**
304 : * Context for all CURL operations (useful to the event loop)
305 : */
306 : struct GNUNET_CURL_Context *TEH_curl_ctx;
307 :
308 : /**
309 : * Context for integrating #TEH_curl_ctx with the
310 : * GNUnet event loop.
311 : */
312 : static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
313 :
314 : /**
315 : * Signature of functions that handle operations on coins.
316 : *
317 : * @param connection the MHD connection to handle
318 : * @param coin_pub the public key of the coin
319 : * @param root uploaded JSON data
320 : * @return MHD result code
321 : */
322 : typedef MHD_RESULT
323 : (*CoinOpHandler)(struct MHD_Connection *connection,
324 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
325 : const json_t *root);
326 :
327 :
328 : /**
329 : * Generate a 404 "not found" reply on @a connection with
330 : * the hint @a details.
331 : *
332 : * @param connection where to send the reply on
333 : * @param details details for the error message, can be NULL
334 : */
335 : static MHD_RESULT
336 0 : r404 (struct MHD_Connection *connection,
337 : const char *details)
338 : {
339 0 : return TALER_MHD_reply_with_error (connection,
340 : MHD_HTTP_NOT_FOUND,
341 : TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
342 : details);
343 : }
344 :
345 :
346 : /**
347 : * Handle a "/coins/$COIN_PUB/$OP" POST request. Parses the "coin_pub"
348 : * EdDSA key of the coin and demultiplexes based on $OP.
349 : *
350 : * @param rc request context
351 : * @param root uploaded JSON data
352 : * @param args array of additional options
353 : * @return MHD result code
354 : */
355 : static MHD_RESULT
356 16 : handle_post_coins (struct TEH_RequestContext *rc,
357 : const json_t *root,
358 : const char *const args[2])
359 : {
360 : struct TALER_CoinSpendPublicKeyP coin_pub;
361 : static const struct
362 : {
363 : /**
364 : * Name of the operation (args[1])
365 : */
366 : const char *op;
367 :
368 : /**
369 : * Function to call to perform the operation.
370 : */
371 : CoinOpHandler handler;
372 :
373 : } h[] = {
374 : #if FIXME_9828
375 : {
376 : .op = "recoup",
377 : .handler = &TEH_handler_recoup
378 : },
379 : {
380 : .op = "recoup-refresh",
381 : .handler = &TEH_handler_recoup_refresh
382 : },
383 : #endif
384 : {
385 : .op = "refund",
386 : .handler = &TEH_handler_refund
387 : },
388 : {
389 : .op = NULL,
390 : .handler = NULL
391 : },
392 : };
393 :
394 16 : if (GNUNET_OK !=
395 16 : GNUNET_STRINGS_string_to_data (args[0],
396 : strlen (args[0]),
397 : &coin_pub,
398 : sizeof (coin_pub)))
399 : {
400 0 : GNUNET_break_op (0);
401 0 : return TALER_MHD_reply_with_error (rc->connection,
402 : MHD_HTTP_BAD_REQUEST,
403 : TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
404 : args[0]);
405 : }
406 16 : for (unsigned int i = 0; NULL != h[i].op; i++)
407 16 : if (0 == strcmp (h[i].op,
408 16 : args[1]))
409 16 : return h[i].handler (rc->connection,
410 : &coin_pub,
411 : root);
412 0 : return r404 (rc->connection,
413 0 : args[1]);
414 : }
415 :
416 :
417 : /**
418 : * Handle a GET "/coins/$COIN_PUB[/$OP]" request. Parses the "coin_pub"
419 : * EdDSA key of the coin and demultiplexes based on $OP.
420 : *
421 : * @param rc request context
422 : * @param args array of additional options
423 : * @return MHD result code
424 : */
425 : static MHD_RESULT
426 4 : handle_get_coins (struct TEH_RequestContext *rc,
427 : const char *const args[2])
428 : {
429 : struct TALER_CoinSpendPublicKeyP coin_pub;
430 :
431 4 : if (NULL == args[0])
432 : {
433 0 : return TALER_MHD_reply_with_error (rc->connection,
434 : MHD_HTTP_NOT_FOUND,
435 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
436 : rc->url);
437 : }
438 4 : if (GNUNET_OK !=
439 4 : GNUNET_STRINGS_string_to_data (args[0],
440 : strlen (args[0]),
441 : &coin_pub,
442 : sizeof (coin_pub)))
443 : {
444 0 : GNUNET_break_op (0);
445 0 : return TALER_MHD_reply_with_error (rc->connection,
446 : MHD_HTTP_BAD_REQUEST,
447 : TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
448 : args[0]);
449 : }
450 4 : if (NULL != args[1])
451 : {
452 4 : if (0 == strcmp (args[1],
453 : "history"))
454 4 : return TEH_handler_coins_get (rc,
455 : &coin_pub);
456 : }
457 0 : return TALER_MHD_reply_with_error (rc->connection,
458 : MHD_HTTP_NOT_FOUND,
459 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
460 : rc->url);
461 : }
462 :
463 :
464 : /**
465 : * Signature of functions that handle operations
466 : * authorized by AML officers.
467 : *
468 : * @param rc request context
469 : * @param officer_pub the public key of the AML officer
470 : * @param root uploaded JSON data
471 : * @return MHD result code
472 : */
473 : typedef MHD_RESULT
474 : (*AmlOpPostHandler)(struct TEH_RequestContext *rc,
475 : const struct TALER_AmlOfficerPublicKeyP *officer_pub,
476 : const json_t *root);
477 :
478 :
479 : /**
480 : * Handle a "/aml/$OFFICER_PUB/$OP" POST request. Parses the "officer_pub"
481 : * EdDSA key of the officer and demultiplexes based on $OP.
482 : *
483 : * @param rc request context
484 : * @param root uploaded JSON data
485 : * @param args array of additional options
486 : * @return MHD result code
487 : */
488 : static MHD_RESULT
489 3 : handle_post_aml (struct TEH_RequestContext *rc,
490 : const json_t *root,
491 : const char *const args[2])
492 : {
493 : struct TALER_AmlOfficerPublicKeyP officer_pub;
494 : static const struct
495 : {
496 : /**
497 : * Name of the operation (args[1])
498 : */
499 : const char *op;
500 :
501 : /**
502 : * Function to call to perform the operation.
503 : */
504 : AmlOpPostHandler handler;
505 :
506 : } h[] = {
507 : {
508 : .op = "decision",
509 : .handler = &TEH_handler_post_aml_decision
510 : },
511 : {
512 : .op = NULL,
513 : .handler = NULL
514 : },
515 : };
516 :
517 3 : if (GNUNET_OK !=
518 3 : GNUNET_STRINGS_string_to_data (args[0],
519 : strlen (args[0]),
520 : &officer_pub,
521 : sizeof (officer_pub)))
522 : {
523 0 : GNUNET_break_op (0);
524 0 : return TALER_MHD_reply_with_error (rc->connection,
525 : MHD_HTTP_BAD_REQUEST,
526 : TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
527 : args[0]);
528 : }
529 3 : for (unsigned int i = 0; NULL != h[i].op; i++)
530 3 : if (0 == strcmp (h[i].op,
531 3 : args[1]))
532 3 : return h[i].handler (rc,
533 : &officer_pub,
534 : root);
535 0 : return r404 (rc->connection,
536 0 : args[1]);
537 : }
538 :
539 :
540 : /**
541 : * Signature of functions that handle operations
542 : * authorized by AML officers.
543 : *
544 : * @param rc request context
545 : * @param officer_pub the public key of the AML officer
546 : * @param args remaining arguments
547 : * @return MHD result code
548 : */
549 : typedef MHD_RESULT
550 : (*AmlOpGetHandler)(struct TEH_RequestContext *rc,
551 : const struct TALER_AmlOfficerPublicKeyP *officer_pub,
552 : const char *const args[]);
553 :
554 :
555 : /**
556 : * Handle a "/aml/$OFFICER_PUB/$OP" GET request. Parses the "officer_pub"
557 : * EdDSA key of the officer, checks the authentication signature, and
558 : * demultiplexes based on $OP.
559 : *
560 : * @param rc request context
561 : * @param args array of additional options
562 : * @return MHD result code
563 : */
564 : static MHD_RESULT
565 5 : handle_get_aml (struct TEH_RequestContext *rc,
566 : const char *const args[])
567 : {
568 : struct TALER_AmlOfficerPublicKeyP officer_pub;
569 : static const struct
570 : {
571 : /**
572 : * Name of the operation (args[1])
573 : */
574 : const char *op;
575 :
576 : /**
577 : * Function to call to perform the operation.
578 : */
579 : AmlOpGetHandler handler;
580 :
581 : } h[] = {
582 : {
583 : .op = "attributes",
584 : .handler = &TEH_handler_aml_attributes_get
585 : },
586 : {
587 : .op = "decisions",
588 : .handler = &TEH_handler_aml_decisions_get
589 : },
590 : {
591 : .op = "legitimizations",
592 : .handler = &TEH_handler_aml_legitimization_measures_get
593 : },
594 : {
595 : .op = "kyc-statistics",
596 : .handler = &TEH_handler_aml_kyc_statistics_get
597 : },
598 : {
599 : .op = "measures",
600 : .handler = &TEH_handler_aml_measures_get
601 : },
602 : {
603 : .op = "transfers-credit",
604 : .handler = &TEH_handler_aml_transfer_credit_get
605 : },
606 : {
607 : .op = "transfers-kycauth",
608 : .handler = &TEH_handler_aml_transfer_kycauth_get
609 : },
610 : {
611 : .op = "transfers-debit",
612 : .handler = &TEH_handler_aml_transfer_debit_get
613 : },
614 : {
615 : .op = NULL,
616 : .handler = NULL
617 : },
618 : };
619 :
620 5 : if (NULL == args[0])
621 : {
622 0 : GNUNET_break_op (0);
623 0 : return TALER_MHD_reply_with_error (
624 : rc->connection,
625 : MHD_HTTP_BAD_REQUEST,
626 : TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
627 : "argument missing");
628 : }
629 5 : if (GNUNET_OK !=
630 5 : GNUNET_STRINGS_string_to_data (args[0],
631 : strlen (args[0]),
632 : &officer_pub,
633 : sizeof (officer_pub)))
634 : {
635 0 : GNUNET_break_op (0);
636 0 : return TALER_MHD_reply_with_error (
637 : rc->connection,
638 : MHD_HTTP_BAD_REQUEST,
639 : TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
640 : args[0]);
641 : }
642 5 : if (NULL == args[1])
643 : {
644 0 : GNUNET_break_op (0);
645 0 : return TALER_MHD_reply_with_error (
646 : rc->connection,
647 : MHD_HTTP_NOT_FOUND,
648 : TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
649 : "AML GET operations must specify an operation identifier");
650 : }
651 : {
652 : const char *sig_hdr;
653 : struct TALER_AmlOfficerSignatureP officer_sig;
654 :
655 5 : sig_hdr = MHD_lookup_connection_value (
656 : rc->connection,
657 : MHD_HEADER_KIND,
658 : TALER_AML_OFFICER_SIGNATURE_HEADER);
659 10 : if ( (NULL == sig_hdr) ||
660 : (GNUNET_OK !=
661 5 : GNUNET_STRINGS_string_to_data (sig_hdr,
662 : strlen (sig_hdr),
663 : &officer_sig,
664 5 : sizeof (officer_sig))) ||
665 : (GNUNET_OK !=
666 5 : TALER_officer_aml_query_verify (&officer_pub,
667 : &officer_sig)) )
668 : {
669 0 : GNUNET_break_op (0);
670 0 : return TALER_MHD_reply_with_error (
671 : rc->connection,
672 : MHD_HTTP_BAD_REQUEST,
673 : TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID,
674 : sig_hdr);
675 : }
676 5 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
677 : }
678 :
679 : {
680 : enum GNUNET_DB_QueryStatus qs;
681 :
682 5 : qs = TEH_plugin->test_aml_officer (TEH_plugin->cls,
683 : &officer_pub);
684 5 : switch (qs)
685 : {
686 0 : case GNUNET_DB_STATUS_HARD_ERROR:
687 : case GNUNET_DB_STATUS_SOFT_ERROR:
688 0 : GNUNET_break (0);
689 0 : return TALER_MHD_reply_with_error (rc->connection,
690 : MHD_HTTP_INTERNAL_SERVER_ERROR,
691 : TALER_EC_GENERIC_DB_FETCH_FAILED,
692 : NULL);
693 1 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
694 1 : return TALER_MHD_reply_with_error (rc->connection,
695 : MHD_HTTP_FORBIDDEN,
696 : TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
697 : NULL);
698 4 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
699 4 : break;
700 : }
701 : }
702 9 : for (unsigned int i = 0; NULL != h[i].op; i++)
703 9 : if (0 == strcmp (h[i].op,
704 9 : args[1]))
705 4 : return h[i].handler (rc,
706 : &officer_pub,
707 : &args[2]);
708 0 : return r404 (rc->connection,
709 0 : args[1]);
710 : }
711 :
712 :
713 : /**
714 : * Signature of functions that handle operations on reserves.
715 : *
716 : * @param rc request context
717 : * @param reserve_pub the public key of the reserve
718 : * @param root uploaded JSON data
719 : * @return MHD result code
720 : */
721 : typedef MHD_RESULT
722 : (*ReserveOpHandler)(struct TEH_RequestContext *rc,
723 : const struct TALER_ReservePublicKeyP *reserve_pub,
724 : const json_t *root);
725 :
726 :
727 : /**
728 : * Handle a "/reserves/$RESERVE_PUB/$OP" POST request. Parses the "reserve_pub"
729 : * EdDSA key of the reserve and demultiplexes based on $OP.
730 : *
731 : * @param rc request context
732 : * @param root uploaded JSON data
733 : * @param args array of additional options
734 : * @return MHD result code
735 : */
736 : static MHD_RESULT
737 42 : handle_post_reserves (struct TEH_RequestContext *rc,
738 : const json_t *root,
739 : const char *const args[2])
740 : {
741 : struct TALER_ReservePublicKeyP reserve_pub;
742 : static const struct
743 : {
744 : /**
745 : * Name of the operation (args[1])
746 : */
747 : const char *op;
748 :
749 : /**
750 : * Function to call to perform the operation.
751 : */
752 : ReserveOpHandler handler;
753 :
754 : } h[] = {
755 : {
756 : .op = "purse",
757 : .handler = &TEH_handler_reserves_purse
758 : },
759 : {
760 : .op = "open",
761 : .handler = &TEH_handler_reserves_open
762 : },
763 : {
764 : .op = "close",
765 : .handler = &TEH_handler_reserves_close
766 : },
767 : {
768 : .op = NULL,
769 : .handler = NULL
770 : },
771 : };
772 :
773 42 : if (GNUNET_OK !=
774 42 : GNUNET_STRINGS_string_to_data (args[0],
775 : strlen (args[0]),
776 : &reserve_pub,
777 : sizeof (reserve_pub)))
778 : {
779 0 : GNUNET_break_op (0);
780 0 : return TALER_MHD_reply_with_error (rc->connection,
781 : MHD_HTTP_BAD_REQUEST,
782 : TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
783 : args[0]);
784 : }
785 64 : for (unsigned int i = 0; NULL != h[i].op; i++)
786 64 : if (0 == strcmp (h[i].op,
787 64 : args[1]))
788 42 : return h[i].handler (rc,
789 : &reserve_pub,
790 : root);
791 0 : return r404 (rc->connection,
792 0 : args[1]);
793 : }
794 :
795 :
796 : /**
797 : * Signature of functions that handle GET operations on reserves.
798 : *
799 : * @param rc request context
800 : * @param reserve_pub the public key of the reserve
801 : * @return MHD result code
802 : */
803 : typedef MHD_RESULT
804 : (*ReserveGetOpHandler)(struct TEH_RequestContext *rc,
805 : const struct TALER_ReservePublicKeyP *reserve_pub);
806 :
807 :
808 : /**
809 : * Handle a "GET /reserves/$RESERVE_PUB[/$OP]" request. Parses the "reserve_pub"
810 : * EdDSA key of the reserve and demultiplexes based on $OP.
811 : *
812 : * @param rc request context
813 : * @param args NULL-terminated array of additional options, zero, one or two
814 : * @return MHD result code
815 : */
816 : static MHD_RESULT
817 51 : handle_get_reserves (struct TEH_RequestContext *rc,
818 : const char *const args[])
819 : {
820 : struct TALER_ReservePublicKeyP reserve_pub;
821 : static const struct
822 : {
823 : /**
824 : * Name of the operation (args[1]), optional
825 : */
826 : const char *op;
827 :
828 : /**
829 : * Function to call to perform the operation.
830 : */
831 : ReserveGetOpHandler handler;
832 :
833 : } h[] = {
834 : {
835 : .op = NULL,
836 : .handler = &TEH_handler_reserves_get
837 : },
838 : {
839 : .op = "history",
840 : .handler = &TEH_handler_reserves_history
841 : },
842 : {
843 : .op = NULL,
844 : .handler = NULL
845 : },
846 : };
847 :
848 102 : if ( (NULL == args[0]) ||
849 : (GNUNET_OK !=
850 51 : GNUNET_STRINGS_string_to_data (args[0],
851 : strlen (args[0]),
852 : &reserve_pub,
853 : sizeof (reserve_pub))) )
854 : {
855 0 : GNUNET_break_op (0);
856 0 : return TALER_MHD_reply_with_error (rc->connection,
857 : MHD_HTTP_BAD_REQUEST,
858 : TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
859 : args[0]);
860 : }
861 59 : for (unsigned int i = 0; NULL != h[i].handler; i++)
862 : {
863 59 : if ( ( (NULL == args[1]) &&
864 43 : (NULL == h[i].op) ) ||
865 16 : ( (NULL != args[1]) &&
866 16 : (NULL != h[i].op) &&
867 8 : (0 == strcmp (h[i].op,
868 8 : args[1])) ) )
869 51 : return h[i].handler (rc,
870 : &reserve_pub);
871 : }
872 0 : return r404 (rc->connection,
873 0 : args[1]);
874 : }
875 :
876 :
877 : /**
878 : * Signature of functions that handle operations on purses.
879 : *
880 : * @param rc request handle
881 : * @param purse_pub the public key of the purse
882 : * @param root uploaded JSON data
883 : * @return MHD result code
884 : */
885 : typedef MHD_RESULT
886 : (*PurseOpHandler)(struct TEH_RequestContext *rc,
887 : const struct TALER_PurseContractPublicKeyP *purse_pub,
888 : const json_t *root);
889 :
890 :
891 : /**
892 : * Handle a "/purses/$RESERVE_PUB/$OP" POST request. Parses the "purse_pub"
893 : * EdDSA key of the purse and demultiplexes based on $OP.
894 : *
895 : * @param rc request context
896 : * @param root uploaded JSON data
897 : * @param args array of additional options
898 : * @return MHD result code
899 : */
900 : static MHD_RESULT
901 30 : handle_post_purses (struct TEH_RequestContext *rc,
902 : const json_t *root,
903 : const char *const args[2])
904 : {
905 : struct TALER_PurseContractPublicKeyP purse_pub;
906 : static const struct
907 : {
908 : /**
909 : * Name of the operation (args[1])
910 : */
911 : const char *op;
912 :
913 : /**
914 : * Function to call to perform the operation.
915 : */
916 : PurseOpHandler handler;
917 :
918 : } h[] = {
919 : {
920 : .op = "create",
921 : .handler = &TEH_handler_purses_create
922 : },
923 : {
924 : .op = "deposit",
925 : .handler = &TEH_handler_purses_deposit
926 : },
927 : {
928 : .op = "merge",
929 : .handler = &TEH_handler_purses_merge
930 : },
931 : {
932 : .op = NULL,
933 : .handler = NULL
934 : },
935 : };
936 :
937 30 : if (GNUNET_OK !=
938 30 : GNUNET_STRINGS_string_to_data (args[0],
939 : strlen (args[0]),
940 : &purse_pub,
941 : sizeof (purse_pub)))
942 : {
943 0 : GNUNET_break_op (0);
944 0 : return TALER_MHD_reply_with_error (rc->connection,
945 : MHD_HTTP_BAD_REQUEST,
946 : TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
947 : args[0]);
948 : }
949 61 : for (unsigned int i = 0; NULL != h[i].op; i++)
950 61 : if (0 == strcmp (h[i].op,
951 61 : args[1]))
952 30 : return h[i].handler (rc,
953 : &purse_pub,
954 : root);
955 0 : return r404 (rc->connection,
956 0 : args[1]);
957 : }
958 :
959 :
960 : /**
961 : * Increments our request counter and checks if this
962 : * process should commit suicide.
963 : */
964 : static void
965 4823 : check_suicide (void)
966 : {
967 : pid_t chld;
968 : unsigned long long cnt;
969 :
970 4823 : cnt = req_count++;
971 4823 : if (req_max != cnt)
972 4823 : return;
973 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
974 : "Restarting exchange service after %llu requests\n",
975 : cnt);
976 : /* Stop accepting new connections */
977 0 : TALER_MHD_daemons_quiesce ();
978 : /* Continue handling existing connections in child,
979 : so that this process can die and be replaced by
980 : systemd with a fresh one */
981 0 : chld = fork ();
982 0 : if (-1 == chld)
983 : {
984 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
985 : "fork");
986 0 : _exit (1);
987 : }
988 0 : if (0 != chld)
989 : {
990 : /* We are the parent, instant-suicide! */
991 0 : _exit (0);
992 : }
993 0 : TEH_suicide = true;
994 : }
995 :
996 :
997 : /**
998 : * Function called whenever MHD is done with a request. If the
999 : * request was a POST, we may have stored a `struct Buffer *` in the
1000 : * @a con_cls that might still need to be cleaned up. Call the
1001 : * respective function to free the memory.
1002 : *
1003 : * @param cls client-defined closure
1004 : * @param connection connection handle
1005 : * @param con_cls value as set by the last call to
1006 : * the #MHD_AccessHandlerCallback
1007 : * @param toe reason for request termination
1008 : * @see #MHD_OPTION_NOTIFY_COMPLETED
1009 : * @ingroup request
1010 : */
1011 : static void
1012 4823 : handle_mhd_completion_callback (void *cls,
1013 : struct MHD_Connection *connection,
1014 : void **con_cls,
1015 : enum MHD_RequestTerminationCode toe)
1016 : {
1017 4823 : struct TEH_RequestContext *rc = *con_cls;
1018 : struct GNUNET_AsyncScopeSave old_scope;
1019 :
1020 : (void) cls;
1021 4823 : if (NULL == rc)
1022 0 : return;
1023 4823 : GNUNET_async_scope_enter (&rc->async_scope_id,
1024 : &old_scope);
1025 4823 : check_suicide ();
1026 4823 : TEH_check_invariants ();
1027 4823 : if (NULL != rc->rh_cleaner)
1028 356 : rc->rh_cleaner (rc);
1029 4823 : if (NULL != rc->root)
1030 : {
1031 4537 : json_decref (rc->root);
1032 4537 : rc->root = NULL;
1033 : }
1034 4823 : TEH_check_invariants ();
1035 : {
1036 : #if MHD_VERSION >= 0x00097304
1037 : const union MHD_ConnectionInfo *ci;
1038 4823 : unsigned int http_status = 0;
1039 :
1040 4823 : ci = MHD_get_connection_info (connection,
1041 : MHD_CONNECTION_INFO_HTTP_STATUS);
1042 4823 : if (NULL != ci)
1043 4823 : http_status = ci->http_status;
1044 4823 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1045 : "Request for `%s' completed with HTTP status %u (%d)\n",
1046 : rc->url,
1047 : http_status,
1048 : toe);
1049 : #else
1050 : (void) connection;
1051 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1052 : "Request for `%s' completed (%d)\n",
1053 : rc->url,
1054 : toe);
1055 : #endif
1056 : }
1057 :
1058 4823 : TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context);
1059 : /* Sanity-check that we didn't leave any transactions hanging */
1060 4823 : GNUNET_break (GNUNET_OK ==
1061 : TEH_plugin->preflight (TEH_plugin->cls));
1062 : {
1063 : struct GNUNET_TIME_Relative latency;
1064 :
1065 4823 : latency = GNUNET_TIME_absolute_get_duration (rc->start_time);
1066 9646 : if (latency.rel_value_us >
1067 4823 : WARN_LATENCY.rel_value_us)
1068 42 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1069 : "Request for `%s' took %s\n",
1070 : rc->url,
1071 : GNUNET_STRINGS_relative_time_to_string (latency,
1072 : GNUNET_YES));
1073 : }
1074 4823 : GNUNET_free (rc);
1075 4823 : *con_cls = NULL;
1076 4823 : GNUNET_async_scope_restore (&old_scope);
1077 : }
1078 :
1079 :
1080 : /**
1081 : * We found a request handler responsible for handling a request. Parse the
1082 : * @a upload_data (if applicable) and the @a url and call the
1083 : * handler.
1084 : *
1085 : * @param rc request context
1086 : * @param url rest of the URL to parse
1087 : * @param upload_data upload data to parse (if available)
1088 : * @param[in,out] upload_data_size number of bytes in @a upload_data
1089 : * @return MHD result code
1090 : */
1091 : static MHD_RESULT
1092 14174 : proceed_with_handler (struct TEH_RequestContext *rc,
1093 : const char *url,
1094 : const char *upload_data,
1095 : size_t *upload_data_size)
1096 14174 : {
1097 14174 : const struct TEH_RequestHandler *rh = rc->rh;
1098 14174 : const char *args[rh->nargs + 2];
1099 14174 : size_t ulen = strlen (url) + 1;
1100 : MHD_RESULT ret;
1101 :
1102 : /* We do check for "ulen" here, because we'll later stack-allocate a buffer
1103 : of that size and don't want to enable malicious clients to cause us
1104 : huge stack allocations. */
1105 14174 : if (ulen > 512)
1106 : {
1107 : /* 512 is simply "big enough", as it is bigger than "6 * 54",
1108 : which is the longest URL format we ever get (for
1109 : /deposits/). The value should be adjusted if we ever define protocol
1110 : endpoints with plausibly longer inputs. */
1111 0 : GNUNET_break_op (0);
1112 0 : return TALER_MHD_reply_with_error (rc->connection,
1113 : MHD_HTTP_URI_TOO_LONG,
1114 : TALER_EC_GENERIC_URI_TOO_LONG,
1115 : url);
1116 : }
1117 :
1118 : /* All POST endpoints come with a body in JSON format. So we parse
1119 : the JSON here. */
1120 14174 : if ( (0 == strcasecmp (rh->method,
1121 13844 : MHD_HTTP_METHOD_POST)) &&
1122 13844 : (NULL == rc->root) )
1123 : {
1124 : enum GNUNET_GenericReturnValue res;
1125 :
1126 13657 : res = TALER_MHD_parse_post_json (rc->connection,
1127 : &rc->opaque_post_parsing_context,
1128 : upload_data,
1129 : upload_data_size,
1130 : &rc->root);
1131 13657 : if (GNUNET_SYSERR == res)
1132 : {
1133 0 : GNUNET_assert (NULL == rc->root);
1134 0 : return MHD_NO; /* bad upload, could not even generate error */
1135 : }
1136 13657 : if ( (GNUNET_NO == res) ||
1137 13657 : (NULL == rc->root) )
1138 : {
1139 9120 : GNUNET_assert (NULL == rc->root);
1140 9120 : return MHD_YES; /* so far incomplete upload or parser error */
1141 : }
1142 : }
1143 :
1144 5054 : {
1145 5054 : char d[ulen];
1146 : unsigned int i;
1147 : char *sp;
1148 :
1149 : /* Parse command-line arguments */
1150 : /* make a copy of 'url' because 'strtok_r()' will modify */
1151 5054 : GNUNET_memcpy (d,
1152 : url,
1153 : ulen);
1154 5054 : i = 0;
1155 5054 : args[i++] = strtok_r (d, "/", &sp);
1156 13733 : while ( (NULL != args[i - 1]) &&
1157 8679 : (i <= rh->nargs + 1) )
1158 8679 : args[i++] = strtok_r (NULL, "/", &sp);
1159 : /* make sure above loop ran nicely until completion, and also
1160 : that there is no excess data in 'd' afterwards */
1161 5054 : if ( ( (rh->nargs_is_upper_bound) &&
1162 4217 : (i - 1 > rh->nargs) ) ||
1163 5054 : ( (! rh->nargs_is_upper_bound) &&
1164 837 : (i - 1 != rh->nargs) ) )
1165 : {
1166 : char emsg[128 + 512];
1167 :
1168 0 : GNUNET_snprintf (emsg,
1169 : sizeof (emsg),
1170 : "Got %u+/%u segments for `%s' request (`%s')",
1171 : i - 1,
1172 0 : rh->nargs,
1173 0 : rh->url,
1174 : url);
1175 0 : GNUNET_break_op (0);
1176 0 : return TALER_MHD_reply_with_error (rc->connection,
1177 : MHD_HTTP_NOT_FOUND,
1178 : TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
1179 : emsg);
1180 : }
1181 5054 : GNUNET_assert (NULL == args[i - 1]);
1182 :
1183 : /* Above logic ensures that 'root' is exactly non-NULL for POST operations,
1184 : so we test for 'root' to decide which handler to invoke. */
1185 5054 : if (0 == strcasecmp (rh->method,
1186 : MHD_HTTP_METHOD_POST))
1187 4724 : ret = rh->handler.post (rc,
1188 4724 : rc->root,
1189 : args);
1190 330 : else if (0 == strcasecmp (rh->method,
1191 : MHD_HTTP_METHOD_DELETE))
1192 2 : ret = rh->handler.delete (rc,
1193 : args);
1194 : else /* Only GET left */
1195 328 : ret = rh->handler.get (rc,
1196 : args);
1197 : }
1198 5054 : return ret;
1199 : }
1200 :
1201 :
1202 : /**
1203 : * Handle a "/seed" request.
1204 : *
1205 : * @param rc request context
1206 : * @param args array of additional options (must be empty for this function)
1207 : * @return MHD result code
1208 : */
1209 : static MHD_RESULT
1210 5 : handler_seed (struct TEH_RequestContext *rc,
1211 : const char *const args[])
1212 : {
1213 : #define SEED_SIZE 32
1214 : char *body;
1215 : MHD_RESULT ret;
1216 : struct MHD_Response *resp;
1217 :
1218 : (void) args;
1219 5 : body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */
1220 5 : if (NULL == body)
1221 0 : return MHD_NO;
1222 5 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
1223 : body,
1224 : SEED_SIZE);
1225 5 : resp = MHD_create_response_from_buffer (SEED_SIZE,
1226 : body,
1227 : MHD_RESPMEM_MUST_FREE);
1228 5 : TALER_MHD_add_global_headers (resp,
1229 : false);
1230 5 : ret = MHD_queue_response (rc->connection,
1231 : MHD_HTTP_OK,
1232 : resp);
1233 5 : GNUNET_break (MHD_YES == ret);
1234 5 : MHD_destroy_response (resp);
1235 5 : return ret;
1236 : #undef SEED_SIZE
1237 : }
1238 :
1239 :
1240 : /**
1241 : * Signature of functions that handle simple
1242 : * POST operations for the management API.
1243 : *
1244 : * @param connection the MHD connection to handle
1245 : * @param root uploaded JSON data
1246 : * @return MHD result code
1247 : */
1248 : typedef MHD_RESULT
1249 : (*ManagementPostHandler)(
1250 : struct MHD_Connection *connection,
1251 : const json_t *root);
1252 :
1253 :
1254 : /**
1255 : * Handle POST "/management/..." requests.
1256 : *
1257 : * @param rc request context
1258 : * @param root uploaded JSON data
1259 : * @param args array of additional options
1260 : * @return MHD result code
1261 : */
1262 : static MHD_RESULT
1263 127 : handle_post_management (struct TEH_RequestContext *rc,
1264 : const json_t *root,
1265 : const char *const args[])
1266 : {
1267 : static const struct
1268 : {
1269 : const char *arg0;
1270 : const char *arg1;
1271 : ManagementPostHandler handler;
1272 : } plain_posts[] = {
1273 : {
1274 : .arg0 = "keys",
1275 : .handler = &TEH_handler_management_post_keys
1276 : },
1277 : {
1278 : .arg0 = "wire",
1279 : .handler = &TEH_handler_management_post_wire
1280 : },
1281 : {
1282 : .arg0 = "wire",
1283 : .arg1 = "disable",
1284 : .handler = &TEH_handler_management_post_wire_disable
1285 : },
1286 : {
1287 : .arg0 = "wire-fee",
1288 : .handler = &TEH_handler_management_post_wire_fees
1289 : },
1290 : {
1291 : .arg0 = "global-fee",
1292 : .handler = &TEH_handler_management_post_global_fees
1293 : },
1294 : {
1295 : .arg0 = "extensions",
1296 : .handler = &TEH_handler_management_post_extensions
1297 : },
1298 : {
1299 : .arg0 = "drain",
1300 : .handler = &TEH_handler_management_post_drain
1301 : },
1302 : {
1303 : .arg0 = "aml-officers",
1304 : .handler = &TEH_handler_management_aml_officers
1305 : },
1306 : {
1307 : .arg0 = "partners",
1308 : .handler = &TEH_handler_management_partners
1309 : },
1310 : {
1311 : NULL,
1312 : NULL,
1313 : NULL
1314 : }
1315 : };
1316 127 : if (NULL == args[0])
1317 : {
1318 0 : GNUNET_break_op (0);
1319 0 : return r404 (rc->connection,
1320 : "/management");
1321 : }
1322 127 : if (0 == strcmp (args[0],
1323 : "auditors"))
1324 : {
1325 : struct TALER_AuditorPublicKeyP auditor_pub;
1326 :
1327 20 : if (NULL == args[1])
1328 12 : return TEH_handler_management_auditors (rc->connection,
1329 : root);
1330 8 : if ( (NULL == args[1]) ||
1331 8 : (NULL == args[2]) ||
1332 8 : (0 != strcmp (args[2],
1333 8 : "disable")) ||
1334 8 : (NULL != args[3]) )
1335 0 : return r404 (rc->connection,
1336 : "/management/auditors/$AUDITOR_PUB/disable");
1337 8 : if (GNUNET_OK !=
1338 8 : GNUNET_STRINGS_string_to_data (args[1],
1339 8 : strlen (args[1]),
1340 : &auditor_pub,
1341 : sizeof (auditor_pub)))
1342 : {
1343 0 : GNUNET_break_op (0);
1344 0 : return TALER_MHD_reply_with_error (rc->connection,
1345 : MHD_HTTP_BAD_REQUEST,
1346 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1347 0 : args[1]);
1348 : }
1349 8 : return TEH_handler_management_auditors_AP_disable (rc->connection,
1350 : &auditor_pub,
1351 : root);
1352 : }
1353 107 : if (0 == strcmp (args[0],
1354 : "denominations"))
1355 : {
1356 : struct TALER_DenominationHashP h_denom_pub;
1357 :
1358 0 : if ( (NULL == args[0]) ||
1359 0 : (NULL == args[1]) ||
1360 0 : (NULL == args[2]) ||
1361 0 : (0 != strcmp (args[2],
1362 0 : "revoke")) ||
1363 0 : (NULL != args[3]) )
1364 0 : return r404 (rc->connection,
1365 : "/management/denominations/$HDP/revoke");
1366 0 : if (GNUNET_OK !=
1367 0 : GNUNET_STRINGS_string_to_data (args[1],
1368 0 : strlen (args[1]),
1369 : &h_denom_pub,
1370 : sizeof (h_denom_pub)))
1371 : {
1372 0 : GNUNET_break_op (0);
1373 0 : return TALER_MHD_reply_with_error (rc->connection,
1374 : MHD_HTTP_BAD_REQUEST,
1375 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1376 0 : args[1]);
1377 : }
1378 0 : return TEH_handler_management_denominations_HDP_revoke (rc->connection,
1379 : &h_denom_pub,
1380 : root);
1381 : }
1382 107 : if (0 == strcmp (args[0],
1383 : "signkeys"))
1384 : {
1385 : struct TALER_ExchangePublicKeyP exchange_pub;
1386 :
1387 0 : if ( (NULL == args[0]) ||
1388 0 : (NULL == args[1]) ||
1389 0 : (NULL == args[2]) ||
1390 0 : (0 != strcmp (args[2],
1391 0 : "revoke")) ||
1392 0 : (NULL != args[3]) )
1393 0 : return r404 (rc->connection,
1394 : "/management/signkeys/$HDP/revoke");
1395 0 : if (GNUNET_OK !=
1396 0 : GNUNET_STRINGS_string_to_data (args[1],
1397 0 : strlen (args[1]),
1398 : &exchange_pub,
1399 : sizeof (exchange_pub)))
1400 : {
1401 0 : GNUNET_break_op (0);
1402 0 : return TALER_MHD_reply_with_error (rc->connection,
1403 : MHD_HTTP_BAD_REQUEST,
1404 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1405 0 : args[1]);
1406 : }
1407 0 : return TEH_handler_management_signkeys_EP_revoke (rc->connection,
1408 : &exchange_pub,
1409 : root);
1410 : }
1411 107 : for (unsigned int i = 0;
1412 343 : NULL != plain_posts[i].handler;
1413 236 : i++)
1414 : {
1415 343 : if (0 == strcmp (args[0],
1416 343 : plain_posts[i].arg0))
1417 : {
1418 113 : if ( ( (NULL == args[1]) &&
1419 101 : (NULL == plain_posts[i].arg1) ) ||
1420 12 : ( (NULL != args[1]) &&
1421 12 : (NULL != plain_posts[i].arg1) &&
1422 6 : (0 == strcmp (args[1],
1423 6 : plain_posts[i].arg1)) ) )
1424 107 : return plain_posts[i].handler (rc->connection,
1425 : root);
1426 : }
1427 : }
1428 0 : GNUNET_break_op (0);
1429 0 : return r404 (rc->connection,
1430 : "/management/*");
1431 : }
1432 :
1433 :
1434 : /**
1435 : * Handle a GET "/management" request.
1436 : *
1437 : * @param rc request context
1438 : * @param args array of additional options (must be [0] == "keys")
1439 : * @return MHD result code
1440 : */
1441 : static MHD_RESULT
1442 40 : handle_get_management (struct TEH_RequestContext *rc,
1443 : const char *const args[2])
1444 : {
1445 40 : if ( (NULL != args[0]) &&
1446 40 : (0 == strcmp (args[0],
1447 40 : "keys")) &&
1448 40 : (NULL == args[1]) )
1449 : {
1450 40 : return TEH_keys_management_get_keys_handler (rc->rh,
1451 : rc->connection);
1452 : }
1453 0 : GNUNET_break_op (0);
1454 0 : return r404 (rc->connection,
1455 : "/management/*");
1456 : }
1457 :
1458 :
1459 : /**
1460 : * Handle POST "/auditors/..." requests.
1461 : *
1462 : * @param rc request context
1463 : * @param root uploaded JSON data
1464 : * @param args array of additional options
1465 : * @return MHD result code
1466 : */
1467 : static MHD_RESULT
1468 4028 : handle_post_auditors (struct TEH_RequestContext *rc,
1469 : const json_t *root,
1470 : const char *const args[])
1471 : {
1472 : struct TALER_AuditorPublicKeyP auditor_pub;
1473 : struct TALER_DenominationHashP h_denom_pub;
1474 :
1475 4028 : if ( (NULL == args[0]) ||
1476 4028 : (NULL == args[1]) ||
1477 4028 : (NULL != args[2]) )
1478 : {
1479 0 : GNUNET_break_op (0);
1480 0 : return r404 (rc->connection,
1481 : "/auditors/$AUDITOR_PUB/$H_DENOM_PUB");
1482 : }
1483 :
1484 4028 : if (GNUNET_OK !=
1485 4028 : GNUNET_STRINGS_string_to_data (args[0],
1486 : strlen (args[0]),
1487 : &auditor_pub,
1488 : sizeof (auditor_pub)))
1489 : {
1490 0 : GNUNET_break_op (0);
1491 0 : return TALER_MHD_reply_with_error (rc->connection,
1492 : MHD_HTTP_BAD_REQUEST,
1493 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1494 : args[0]);
1495 : }
1496 4028 : if (GNUNET_OK !=
1497 4028 : GNUNET_STRINGS_string_to_data (args[1],
1498 4028 : strlen (args[1]),
1499 : &h_denom_pub,
1500 : sizeof (h_denom_pub)))
1501 : {
1502 0 : GNUNET_break_op (0);
1503 0 : return TALER_MHD_reply_with_error (rc->connection,
1504 : MHD_HTTP_BAD_REQUEST,
1505 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1506 0 : args[1]);
1507 : }
1508 4028 : return TEH_handler_auditors (rc->connection,
1509 : &auditor_pub,
1510 : &h_denom_pub,
1511 : root);
1512 : }
1513 :
1514 :
1515 : /**
1516 : * Generates the response for "/", redirecting the
1517 : * client to the ``toplevel_redirect_url``.
1518 : *
1519 : * @param rc request context
1520 : * @param args remaining arguments (should be empty)
1521 : * @return MHD result code
1522 : */
1523 : static MHD_RESULT
1524 4 : toplevel_redirect (struct TEH_RequestContext *rc,
1525 : const char *const args[])
1526 : {
1527 4 : const char *text = "Redirecting to /webui/";
1528 : struct MHD_Response *response;
1529 :
1530 4 : response = MHD_create_response_from_buffer (strlen (text),
1531 : (void *) text,
1532 : MHD_RESPMEM_PERSISTENT);
1533 4 : if (NULL == response)
1534 : {
1535 0 : GNUNET_break (0);
1536 0 : return MHD_NO;
1537 : }
1538 4 : TALER_MHD_add_global_headers (response,
1539 : true);
1540 4 : GNUNET_break (MHD_YES ==
1541 : MHD_add_response_header (response,
1542 : MHD_HTTP_HEADER_CONTENT_TYPE,
1543 : "text/plain"));
1544 4 : if (MHD_NO ==
1545 4 : MHD_add_response_header (response,
1546 : MHD_HTTP_HEADER_LOCATION,
1547 : toplevel_redirect_url))
1548 : {
1549 0 : GNUNET_break (0);
1550 0 : MHD_destroy_response (response);
1551 0 : return MHD_NO;
1552 : }
1553 :
1554 : {
1555 : MHD_RESULT ret;
1556 :
1557 4 : ret = MHD_queue_response (rc->connection,
1558 : MHD_HTTP_FOUND,
1559 : response);
1560 4 : MHD_destroy_response (response);
1561 4 : return ret;
1562 : }
1563 : }
1564 :
1565 :
1566 : /**
1567 : * Handle incoming HTTP request.
1568 : *
1569 : * @param cls closure for MHD daemon (unused)
1570 : * @param connection the connection
1571 : * @param url the requested url
1572 : * @param method the method (POST, GET, ...)
1573 : * @param version HTTP version (ignored)
1574 : * @param upload_data request data
1575 : * @param upload_data_size size of @a upload_data in bytes
1576 : * @param con_cls closure for request (a `struct TEH_RequestContext *`)
1577 : * @return MHD result code
1578 : */
1579 : static MHD_RESULT
1580 14181 : handle_mhd_request (void *cls,
1581 : struct MHD_Connection *connection,
1582 : const char *url,
1583 : const char *method,
1584 : const char *version,
1585 : const char *upload_data,
1586 : size_t *upload_data_size,
1587 : void **con_cls)
1588 : {
1589 : static struct TEH_RequestHandler handlers[] = {
1590 : /* /robots.txt: disallow everything */
1591 : {
1592 : .url = "robots.txt",
1593 : .method = MHD_HTTP_METHOD_GET,
1594 : .handler.get = &TEH_handler_static_response,
1595 : .mime_type = "text/plain",
1596 : .data = "User-agent: *\nDisallow: /\n",
1597 : .response_code = MHD_HTTP_OK
1598 : },
1599 : /* Landing page, redirect to toplevel_redirect_url */
1600 : {
1601 : .url = "",
1602 : .method = MHD_HTTP_METHOD_GET,
1603 : .handler.get = &toplevel_redirect
1604 : },
1605 : /* AGPL licensing page, redirect to source. As per the AGPL-license, every
1606 : deployment is required to offer the user a download of the source of
1607 : the actual deployment. We make this easy by including a redirect to the
1608 : source here. */
1609 : {
1610 : .url = "agpl",
1611 : .method = MHD_HTTP_METHOD_GET,
1612 : .handler.get = &TEH_handler_agpl_redirect
1613 : },
1614 : {
1615 : .url = "seed",
1616 : .method = MHD_HTTP_METHOD_GET,
1617 : .handler.get = &handler_seed
1618 : },
1619 : /* Configuration */
1620 : {
1621 : .url = "config",
1622 : .method = MHD_HTTP_METHOD_GET,
1623 : .handler.get = &TEH_handler_config
1624 : },
1625 : /* Performance metrics */
1626 : {
1627 : .url = "metrics",
1628 : .method = MHD_HTTP_METHOD_GET,
1629 : .handler.get = &TEH_handler_metrics
1630 : },
1631 : /* Terms of service */
1632 : {
1633 : .url = "terms",
1634 : .method = MHD_HTTP_METHOD_GET,
1635 : .handler.get = &TEH_handler_terms
1636 : },
1637 : /* Privacy policy */
1638 : {
1639 : .url = "privacy",
1640 : .method = MHD_HTTP_METHOD_GET,
1641 : .handler.get = &TEH_handler_privacy
1642 : },
1643 : /* Return key material and fundamental properties for this exchange */
1644 : {
1645 : .url = "keys",
1646 : .method = MHD_HTTP_METHOD_GET,
1647 : .handler.get = &TEH_keys_get_handler,
1648 : },
1649 : {
1650 : .url = "batch-deposit",
1651 : .method = MHD_HTTP_METHOD_POST,
1652 : .handler.post = &TEH_handler_batch_deposit,
1653 : .nargs = 0
1654 : },
1655 : /* request R's input for Clause-Schnorr signatures in batches */
1656 : {
1657 : .url = "blinding-prepare",
1658 : .method = MHD_HTTP_METHOD_POST,
1659 : .handler.post = &TEH_handler_blinding_prepare,
1660 : .nargs = 0
1661 : },
1662 : /* withdraw request, available since v26 of the API */
1663 : {
1664 : .url = "withdraw",
1665 : .method = MHD_HTTP_METHOD_POST,
1666 : .handler.post = &TEH_handler_withdraw,
1667 : .nargs = 0
1668 : },
1669 : {
1670 : .url = "reserves",
1671 : .method = MHD_HTTP_METHOD_GET,
1672 : .handler.get = &handle_get_reserves,
1673 : .nargs = 2,
1674 : .nargs_is_upper_bound = true
1675 : },
1676 : {
1677 : .url = "reserves",
1678 : .method = MHD_HTTP_METHOD_POST,
1679 : .handler.post = &handle_post_reserves,
1680 : .nargs = 2
1681 : },
1682 : /* reveal-withdraw operation, introduced with v26 */
1683 : {
1684 : .url = "reveal-withdraw",
1685 : .method = MHD_HTTP_METHOD_POST,
1686 : .handler.post = &TEH_handler_reveal_withdraw,
1687 : .nargs = 0
1688 : },
1689 : /* reveal-melt operation, introduced with v27 */
1690 : {
1691 : .url = "reveal-melt",
1692 : .method = MHD_HTTP_METHOD_POST,
1693 : .handler.post = &TEH_handler_reveal_melt,
1694 : .nargs = 0
1695 : },
1696 : {
1697 : .url = "reserves-attest",
1698 : .method = MHD_HTTP_METHOD_GET,
1699 : .handler.get = &TEH_handler_reserves_get_attest,
1700 : .nargs = 1
1701 : },
1702 : {
1703 : .url = "reserves-attest",
1704 : .method = MHD_HTTP_METHOD_POST,
1705 : .handler.post = &TEH_handler_reserves_attest,
1706 : .nargs = 1
1707 : },
1708 : /* coins */
1709 : {
1710 : .url = "coins",
1711 : .method = MHD_HTTP_METHOD_POST,
1712 : .handler.post = &handle_post_coins,
1713 : .nargs = 2
1714 : },
1715 : {
1716 : .url = "coins",
1717 : .method = MHD_HTTP_METHOD_GET,
1718 : .handler.get = &handle_get_coins,
1719 : .nargs = 2,
1720 : .nargs_is_upper_bound = true
1721 : },
1722 : /* melting operation, introduced with v26 */
1723 : {
1724 : .url = "melt",
1725 : .method = MHD_HTTP_METHOD_POST,
1726 : .handler.post = &TEH_handler_melt_v27,
1727 : .nargs = 0
1728 : },
1729 : /* tracking transfers */
1730 : {
1731 : .url = "transfers",
1732 : .method = MHD_HTTP_METHOD_GET,
1733 : .handler.get = &TEH_handler_transfers_get,
1734 : .nargs = 1
1735 : },
1736 : /* tracking deposits */
1737 : {
1738 : .url = "deposits",
1739 : .method = MHD_HTTP_METHOD_GET,
1740 : .handler.get = &TEH_handler_deposits_get,
1741 : .nargs = 4
1742 : },
1743 : /* Operating on purses */
1744 : {
1745 : .url = "purses",
1746 : .method = MHD_HTTP_METHOD_POST,
1747 : .handler.post = &handle_post_purses,
1748 : .nargs = 2
1749 : },
1750 : /* Getting purse status */
1751 : {
1752 : .url = "purses",
1753 : .method = MHD_HTTP_METHOD_GET,
1754 : .handler.get = &TEH_handler_purses_get,
1755 : .nargs = 2
1756 : },
1757 : /* Deleting purse */
1758 : {
1759 : .url = "purses",
1760 : .method = MHD_HTTP_METHOD_DELETE,
1761 : .handler.delete = &TEH_handler_purses_delete,
1762 : .nargs = 1
1763 : },
1764 : /* Getting contracts */
1765 : {
1766 : .url = "contracts",
1767 : .method = MHD_HTTP_METHOD_GET,
1768 : .handler.get = &TEH_handler_contracts_get,
1769 : .nargs = 1
1770 : },
1771 : /* KYC endpoints */
1772 : {
1773 : .url = "kyc-check",
1774 : .method = MHD_HTTP_METHOD_GET,
1775 : .handler.get = &TEH_handler_kyc_check,
1776 : .nargs = 1
1777 : },
1778 : {
1779 : .url = "kyc-proof",
1780 : .method = MHD_HTTP_METHOD_GET,
1781 : .handler.get = &TEH_handler_kyc_proof,
1782 : .nargs = 1
1783 : },
1784 : {
1785 : .url = "kyc-start",
1786 : .method = MHD_HTTP_METHOD_POST,
1787 : .handler.post = &TEH_handler_kyc_start,
1788 : .nargs = 1
1789 : },
1790 : {
1791 : .url = "kyc-wallet",
1792 : .method = MHD_HTTP_METHOD_POST,
1793 : .handler.post = &TEH_handler_kyc_wallet,
1794 : .nargs = 0
1795 : },
1796 : {
1797 : .url = "kyc-webhook",
1798 : .method = MHD_HTTP_METHOD_GET,
1799 : .handler.get = &TEH_handler_kyc_webhook_get,
1800 : .nargs = 16, /* more is not plausible */
1801 : .nargs_is_upper_bound = true
1802 : },
1803 : {
1804 : .url = "kyc-info",
1805 : .method = MHD_HTTP_METHOD_GET,
1806 : .handler.get = &TEH_handler_kyc_info,
1807 : .nargs = 1
1808 : },
1809 : {
1810 : .url = "kyc-upload",
1811 : .method = MHD_HTTP_METHOD_POST,
1812 : .handler.post = &TEH_handler_kyc_upload,
1813 : .nargs = 1,
1814 : .nargs_is_upper_bound = true
1815 : },
1816 : {
1817 : .url = "kyc-webhook",
1818 : .method = MHD_HTTP_METHOD_POST,
1819 : .handler.post = &TEH_handler_kyc_webhook_post,
1820 : .nargs = 16, /* more is not plausible */
1821 : .nargs_is_upper_bound = true
1822 : },
1823 : /* POST management endpoints */
1824 : {
1825 : .url = "management",
1826 : .method = MHD_HTTP_METHOD_POST,
1827 : .handler.post = &handle_post_management,
1828 : .nargs = 4,
1829 : .nargs_is_upper_bound = true
1830 : },
1831 : /* GET management endpoints (we only really have "/management/keys") */
1832 : {
1833 : .url = "management",
1834 : .method = MHD_HTTP_METHOD_GET,
1835 : .handler.get = &handle_get_management,
1836 : .nargs = 1
1837 : },
1838 : /* auditor endpoints */
1839 : {
1840 : .url = "auditors",
1841 : .method = MHD_HTTP_METHOD_POST,
1842 : .handler.post = &handle_post_auditors,
1843 : .nargs = 4,
1844 : .nargs_is_upper_bound = true
1845 : },
1846 : /* AML endpoints */
1847 : {
1848 : .url = "aml",
1849 : .method = MHD_HTTP_METHOD_GET,
1850 : .handler.get = &handle_get_aml,
1851 : .nargs = 4,
1852 : .nargs_is_upper_bound = true
1853 : },
1854 : {
1855 : .url = "aml",
1856 : .method = MHD_HTTP_METHOD_POST,
1857 : .handler.post = &handle_post_aml,
1858 : .nargs = 2
1859 : },
1860 : {
1861 : .url = "aml-spa",
1862 : .method = MHD_HTTP_METHOD_GET,
1863 : .handler.get = &TEH_handler_aml_spa,
1864 : .nargs = 1,
1865 : .nargs_is_upper_bound = true
1866 : },
1867 : {
1868 : .url = "kyc-spa",
1869 : .method = MHD_HTTP_METHOD_GET,
1870 : .handler.get = &TEH_handler_kyc_spa,
1871 : .nargs = 1,
1872 : .nargs_is_upper_bound = true
1873 : },
1874 :
1875 : /* mark end of list */
1876 : {
1877 : .url = NULL
1878 : }
1879 : };
1880 14181 : struct TEH_RequestContext *rc = *con_cls;
1881 : struct GNUNET_AsyncScopeSave old_scope;
1882 14181 : const char *correlation_id = NULL;
1883 :
1884 : (void) cls;
1885 : (void) version;
1886 14181 : if (NULL == rc)
1887 : {
1888 4823 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1889 : "Handling new request\n");
1890 :
1891 : /* We're in a new async scope! */
1892 4823 : rc = *con_cls = GNUNET_new (struct TEH_RequestContext);
1893 4823 : rc->start_time = GNUNET_TIME_absolute_get ();
1894 4823 : GNUNET_async_scope_fresh (&rc->async_scope_id);
1895 4823 : TEH_check_invariants ();
1896 4823 : rc->url = url;
1897 4823 : rc->connection = connection;
1898 : /* We only read the correlation ID on the first callback for every client */
1899 4823 : correlation_id = MHD_lookup_connection_value (connection,
1900 : MHD_HEADER_KIND,
1901 : "Taler-Correlation-Id");
1902 4823 : if ( (NULL != correlation_id) &&
1903 : (GNUNET_YES !=
1904 0 : GNUNET_CURL_is_valid_scope_id (correlation_id)) )
1905 : {
1906 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1907 : "illegal incoming correlation ID\n");
1908 0 : correlation_id = NULL;
1909 : }
1910 :
1911 : /* Check if upload is in bounds */
1912 4823 : if ( (0 == strcasecmp (method,
1913 283 : MHD_HTTP_METHOD_POST)) ||
1914 283 : (0 == strcasecmp (method,
1915 : MHD_HTTP_METHOD_PATCH)) )
1916 : {
1917 4540 : TALER_MHD_check_content_length (connection,
1918 : TALER_MHD_REQUEST_BUFFER_MAX);
1919 : }
1920 : }
1921 :
1922 14181 : GNUNET_async_scope_enter (&rc->async_scope_id,
1923 : &old_scope);
1924 14181 : TEH_check_invariants ();
1925 14181 : if (NULL != correlation_id)
1926 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1927 : "Handling request (%s) for URL '%s', correlation_id=%s\n",
1928 : method,
1929 : url,
1930 : correlation_id);
1931 : else
1932 14181 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1933 : "Handling request (%s) for URL '%s'\n",
1934 : method,
1935 : url);
1936 : /* on repeated requests, check our cache first */
1937 14181 : if (NULL != rc->rh)
1938 : {
1939 : MHD_RESULT ret;
1940 : const char *start;
1941 :
1942 9358 : if ('\0' == url[0])
1943 : /* strange, should start with '/', treat as just "/" */
1944 0 : url = "/";
1945 9358 : start = strchr (url + 1, '/');
1946 9358 : if (NULL == start)
1947 768 : start = "";
1948 9358 : ret = proceed_with_handler (rc,
1949 : start,
1950 : upload_data,
1951 : upload_data_size);
1952 9358 : if (GNUNET_OK !=
1953 9358 : TEH_plugin->preflight (TEH_plugin->cls))
1954 : {
1955 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1956 : "Handler %s left open database transaction behind!\n",
1957 : url);
1958 0 : GNUNET_assert (0);
1959 : }
1960 9358 : GNUNET_async_scope_restore (&old_scope);
1961 9358 : return ret;
1962 : }
1963 :
1964 4823 : if ( (0 == strcasecmp (method,
1965 0 : MHD_HTTP_METHOD_OPTIONS)) &&
1966 0 : (0 == strcmp ("*",
1967 : url)) )
1968 0 : return TALER_MHD_reply_cors_preflight (connection);
1969 :
1970 4823 : if (0 == strcasecmp (method,
1971 : MHD_HTTP_METHOD_HEAD))
1972 0 : method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
1973 :
1974 : /* parse first part of URL */
1975 : {
1976 4823 : bool found = false;
1977 : size_t tok_size;
1978 : const char *tok;
1979 : const char *rest;
1980 :
1981 4823 : if ('\0' == url[0])
1982 : /* strange, should start with '/', treat as just "/" */
1983 0 : url = "/";
1984 4823 : tok = url + 1;
1985 4823 : rest = strchr (tok, '/');
1986 4823 : if (NULL == rest)
1987 : {
1988 420 : tok_size = strlen (tok);
1989 : }
1990 : else
1991 : {
1992 4403 : tok_size = rest - tok;
1993 4403 : rest++; /* skip over '/' */
1994 : }
1995 168667 : for (unsigned int i = 0; NULL != handlers[i].url; i++)
1996 : {
1997 168660 : struct TEH_RequestHandler *rh = &handlers[i];
1998 :
1999 168660 : if ( (0 != strncmp (tok,
2000 : rh->url,
2001 4915 : tok_size)) ||
2002 4915 : (tok_size != strlen (rh->url) ) )
2003 163758 : continue;
2004 4902 : found = true;
2005 : /* The URL is a match! What we now do depends on the method. */
2006 4902 : if (0 == strcasecmp (method,
2007 : MHD_HTTP_METHOD_OPTIONS))
2008 : {
2009 0 : GNUNET_async_scope_restore (&old_scope);
2010 0 : return TALER_MHD_reply_cors_preflight (connection);
2011 : }
2012 4902 : GNUNET_assert (NULL != rh->method);
2013 4902 : if (0 == strcasecmp (method,
2014 : rh->method))
2015 : {
2016 : MHD_RESULT ret;
2017 :
2018 : /* cache to avoid the loop next time */
2019 4816 : rc->rh = rh;
2020 : /* run handler */
2021 4816 : ret = proceed_with_handler (rc,
2022 4816 : url + tok_size + 1,
2023 : upload_data,
2024 : upload_data_size);
2025 4816 : if (GNUNET_OK !=
2026 4816 : TEH_plugin->preflight (TEH_plugin->cls))
2027 : {
2028 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2029 : "Handler %s left open database transaction behind!\n",
2030 : url);
2031 0 : GNUNET_assert (0);
2032 : }
2033 4816 : GNUNET_async_scope_restore (&old_scope);
2034 4816 : return ret;
2035 : }
2036 : }
2037 :
2038 7 : if (found)
2039 : {
2040 : /* we found a matching address, but the method is wrong */
2041 : struct MHD_Response *reply;
2042 : MHD_RESULT ret;
2043 0 : char *allowed = NULL;
2044 :
2045 0 : GNUNET_break_op (0);
2046 0 : for (unsigned int i = 0; NULL != handlers[i].url; i++)
2047 : {
2048 0 : struct TEH_RequestHandler *rh = &handlers[i];
2049 :
2050 0 : if ( (0 != strncmp (tok,
2051 : rh->url,
2052 0 : tok_size)) ||
2053 0 : (tok_size != strlen (rh->url) ) )
2054 0 : continue;
2055 0 : if (NULL == allowed)
2056 : {
2057 0 : allowed = GNUNET_strdup (rh->method);
2058 : }
2059 : else
2060 : {
2061 : char *tmp;
2062 :
2063 0 : GNUNET_asprintf (&tmp,
2064 : "%s, %s",
2065 : allowed,
2066 : rh->method);
2067 0 : GNUNET_free (allowed);
2068 0 : allowed = tmp;
2069 : }
2070 0 : if (0 == strcasecmp (rh->method,
2071 : MHD_HTTP_METHOD_GET))
2072 : {
2073 : char *tmp;
2074 :
2075 0 : GNUNET_asprintf (&tmp,
2076 : "%s, %s",
2077 : allowed,
2078 : MHD_HTTP_METHOD_HEAD);
2079 0 : GNUNET_free (allowed);
2080 0 : allowed = tmp;
2081 : }
2082 : }
2083 0 : reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
2084 : method);
2085 0 : GNUNET_break (MHD_YES ==
2086 : MHD_add_response_header (reply,
2087 : MHD_HTTP_HEADER_ALLOW,
2088 : allowed));
2089 0 : GNUNET_free (allowed);
2090 0 : ret = MHD_queue_response (connection,
2091 : MHD_HTTP_METHOD_NOT_ALLOWED,
2092 : reply);
2093 0 : MHD_destroy_response (reply);
2094 0 : GNUNET_async_scope_restore (&old_scope);
2095 0 : return ret;
2096 : }
2097 : }
2098 :
2099 : /* No handler matches, generate not found */
2100 : {
2101 : MHD_RESULT ret;
2102 :
2103 7 : ret = TALER_MHD_reply_with_error (connection,
2104 : MHD_HTTP_NOT_FOUND,
2105 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
2106 : url);
2107 7 : GNUNET_async_scope_restore (&old_scope);
2108 7 : return ret;
2109 : }
2110 : }
2111 :
2112 :
2113 : /**
2114 : * Load configuration parameters for the exchange
2115 : * server into the corresponding global variables.
2116 : *
2117 : * @param cfg_fn name of our configuration file
2118 : * @return #GNUNET_OK on success
2119 : */
2120 : static enum GNUNET_GenericReturnValue
2121 21 : exchange_serve_process_config (const char *cfg_fn)
2122 : {
2123 : TEH_enable_kyc
2124 21 : = GNUNET_CONFIGURATION_get_value_yesno (
2125 : TEH_cfg,
2126 : "exchange",
2127 : "ENABLE_KYC");
2128 21 : if (GNUNET_SYSERR == TEH_enable_kyc)
2129 : {
2130 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2131 : "Need YES or NO in section `exchange' under `ENABLE_KYC'\n");
2132 0 : return GNUNET_SYSERR;
2133 : }
2134 21 : if (GNUNET_OK !=
2135 21 : TALER_KYCLOGIC_kyc_init (TEH_cfg,
2136 : cfg_fn))
2137 : {
2138 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2139 : "Failed to load configuration `%s'. Exiting.\n",
2140 : cfg_fn);
2141 0 : return GNUNET_SYSERR;
2142 : }
2143 : TEH_hard_limits
2144 21 : = TALER_KYCLOGIC_get_hard_limits ();
2145 : TEH_zero_limits
2146 21 : = TALER_KYCLOGIC_get_zero_limits ();
2147 21 : if (GNUNET_OK !=
2148 21 : GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
2149 : "exchange",
2150 : "MAX_REQUESTS",
2151 : &req_max))
2152 : {
2153 21 : req_max = ULLONG_MAX;
2154 : }
2155 21 : if (GNUNET_OK !=
2156 21 : GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
2157 : "exchangedb",
2158 : "IDLE_RESERVE_EXPIRATION_TIME",
2159 : &TEH_reserve_closing_delay))
2160 : {
2161 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2162 : "exchangedb",
2163 : "IDLE_RESERVE_EXPIRATION_TIME");
2164 : /* use default */
2165 : TEH_reserve_closing_delay
2166 0 : = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
2167 : 4);
2168 : }
2169 21 : if (GNUNET_OK !=
2170 21 : GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
2171 : "exchangedb",
2172 : "MAX_AML_PROGRAM_RUNTIME",
2173 : &TEH_aml_program_timeout))
2174 : {
2175 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2176 : "exchangedb",
2177 : "MAX_AML_PROGRAM_RUNTIME");
2178 : /* use default */
2179 : TEH_aml_program_timeout
2180 0 : = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
2181 : 1);
2182 : }
2183 :
2184 21 : if (GNUNET_OK !=
2185 21 : GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
2186 : "exchange",
2187 : "MAX_KEYS_CACHING",
2188 : &TEH_max_keys_caching))
2189 : {
2190 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2191 : "exchange",
2192 : "MAX_KEYS_CACHING",
2193 : "valid relative time expected");
2194 0 : return GNUNET_SYSERR;
2195 : }
2196 21 : if (GNUNET_OK !=
2197 21 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
2198 : "exchange",
2199 : "TOPLEVEL_REDIRECT_URL",
2200 : &toplevel_redirect_url))
2201 : {
2202 21 : toplevel_redirect_url = GNUNET_strdup ("/terms");
2203 : }
2204 21 : (void)
2205 21 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
2206 : "exchange",
2207 : "BANK_COMPLIANCE_LANGUAGE",
2208 : &TEH_bank_compliance_language);
2209 21 : (void)
2210 21 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
2211 : "exchange",
2212 : "AML_SPA_DIALECT",
2213 : &TEH_aml_spa_dialect);
2214 21 : if (GNUNET_OK !=
2215 21 : TALER_config_get_currency (TEH_cfg,
2216 : "exchange",
2217 : &TEH_currency))
2218 : {
2219 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2220 : "exchange",
2221 : "CURRENCY");
2222 0 : return GNUNET_SYSERR;
2223 : }
2224 :
2225 21 : if (GNUNET_OK !=
2226 21 : TALER_CONFIG_parse_currencies (TEH_cfg,
2227 : TEH_currency,
2228 : &num_cspecs,
2229 : &cspecs))
2230 0 : return GNUNET_SYSERR;
2231 94 : for (unsigned int i = 0; i<num_cspecs; i++)
2232 : {
2233 : struct TALER_CurrencySpecification *cspec;
2234 :
2235 94 : cspec = &cspecs[i];
2236 94 : if (0 == strcmp (TEH_currency,
2237 94 : cspec->currency))
2238 : {
2239 21 : TEH_cspec = cspec;
2240 21 : break;
2241 : }
2242 : }
2243 : /* currency parser must provide default spec for main currency */
2244 21 : GNUNET_assert (NULL != TEH_cspec);
2245 21 : GNUNET_assert (GNUNET_OK ==
2246 : TALER_amount_set_zero (TEH_currency,
2247 : &TEH_stefan_abs));
2248 21 : if (GNUNET_SYSERR ==
2249 21 : TALER_config_get_amount (TEH_cfg,
2250 : "exchange",
2251 : "STEFAN_ABS",
2252 : &TEH_stefan_abs))
2253 : {
2254 0 : GNUNET_break (0);
2255 0 : return GNUNET_SYSERR;
2256 : }
2257 21 : GNUNET_assert (GNUNET_OK ==
2258 : TALER_amount_set_zero (TEH_currency,
2259 : &TEH_stefan_log));
2260 21 : if (GNUNET_SYSERR ==
2261 21 : TALER_config_get_amount (TEH_cfg,
2262 : "exchange",
2263 : "STEFAN_LOG",
2264 : &TEH_stefan_log))
2265 : {
2266 0 : GNUNET_break (0);
2267 0 : return GNUNET_SYSERR;
2268 : }
2269 21 : GNUNET_assert (GNUNET_OK ==
2270 : TALER_amount_set_zero (TEH_currency,
2271 : &TEH_tiny_amount));
2272 21 : if ( (GNUNET_OK !=
2273 21 : TALER_config_get_amount (TEH_cfg,
2274 : "exchange",
2275 : "TINY_AMOUNT",
2276 21 : &TEH_tiny_amount)) &&
2277 : (GNUNET_OK !=
2278 21 : TALER_config_get_amount (TEH_cfg,
2279 : "auditor",
2280 : "TINY_AMOUNT",
2281 : &TEH_tiny_amount)) )
2282 : {
2283 6 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
2284 : "exchange",
2285 : "TINY_AMOUNT");
2286 : }
2287 21 : TEH_stefan_lin = 0.0f;
2288 21 : if (GNUNET_SYSERR ==
2289 21 : GNUNET_CONFIGURATION_get_value_float (TEH_cfg,
2290 : "exchange",
2291 : "STEFAN_LIN",
2292 : &TEH_stefan_lin))
2293 : {
2294 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2295 : "exchange",
2296 : "STEFAN_LIN",
2297 : "must be a floating point");
2298 0 : return GNUNET_SYSERR;
2299 : }
2300 :
2301 21 : if (GNUNET_OK !=
2302 21 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
2303 : "exchange",
2304 : "BASE_URL",
2305 : &TEH_base_url))
2306 : {
2307 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2308 : "exchange",
2309 : "BASE_URL");
2310 0 : return GNUNET_SYSERR;
2311 : }
2312 21 : if ( (! TALER_url_valid_charset (TEH_base_url)) ||
2313 21 : (! TALER_is_web_url (TEH_base_url) ) )
2314 : {
2315 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2316 : "exchange",
2317 : "BASE_URL",
2318 : "invalid URL");
2319 0 : return GNUNET_SYSERR;
2320 : }
2321 21 : if (GNUNET_OK !=
2322 21 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
2323 : "exchange",
2324 : "SHOPPING_URL",
2325 : &TEH_shopping_url))
2326 : {
2327 21 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
2328 : "exchange",
2329 : "SHOPPING_URL");
2330 : }
2331 21 : if ( (NULL != TEH_shopping_url) &&
2332 0 : (! TALER_is_web_url (TEH_shopping_url)) )
2333 : {
2334 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2335 : "exchange",
2336 : "SHOPPING_URL",
2337 : "invalid URL");
2338 0 : return GNUNET_SYSERR;
2339 : }
2340 :
2341 : {
2342 : char *master_public_key_str;
2343 :
2344 21 : if (GNUNET_OK !=
2345 21 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
2346 : "exchange",
2347 : "MASTER_PUBLIC_KEY",
2348 : &master_public_key_str))
2349 : {
2350 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2351 : "exchange",
2352 : "MASTER_PUBLIC_KEY");
2353 0 : return GNUNET_SYSERR;
2354 : }
2355 21 : if (GNUNET_OK !=
2356 21 : GNUNET_CRYPTO_eddsa_public_key_from_string (
2357 : master_public_key_str,
2358 : strlen (master_public_key_str),
2359 : &TEH_master_public_key.eddsa_pub))
2360 : {
2361 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2362 : "exchange",
2363 : "MASTER_PUBLIC_KEY",
2364 : "invalid base32 encoding for a master public key");
2365 0 : GNUNET_free (master_public_key_str);
2366 0 : return GNUNET_SYSERR;
2367 : }
2368 21 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2369 : "Launching exchange with public key `%s'...\n",
2370 : master_public_key_str);
2371 21 : GNUNET_free (master_public_key_str);
2372 : }
2373 :
2374 : {
2375 : char *attr_enc_key_str;
2376 :
2377 21 : if (GNUNET_OK !=
2378 21 : GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
2379 : "exchange",
2380 : "ATTRIBUTE_ENCRYPTION_KEY",
2381 : &attr_enc_key_str))
2382 : {
2383 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2384 : "exchange",
2385 : "ATTRIBUTE_ENCRYPTION_KEY");
2386 0 : return GNUNET_SYSERR;
2387 : }
2388 21 : GNUNET_CRYPTO_hash (attr_enc_key_str,
2389 : strlen (attr_enc_key_str),
2390 : &TEH_attribute_key.hash);
2391 21 : GNUNET_free (attr_enc_key_str);
2392 : }
2393 :
2394 21 : for (unsigned int i = 0; i<MAX_DB_RETRIES; i++)
2395 : {
2396 21 : TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg,
2397 : false);
2398 21 : if (NULL != TEH_plugin)
2399 21 : break;
2400 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2401 : "Failed to connect to DB, will try again %u times\n",
2402 : MAX_DB_RETRIES - i);
2403 0 : sleep (1);
2404 : }
2405 21 : if (NULL == TEH_plugin)
2406 : {
2407 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2408 : "Failed to initialize DB subsystem. Giving up.\n");
2409 0 : return GNUNET_SYSERR;
2410 : }
2411 21 : return GNUNET_OK;
2412 : }
2413 :
2414 :
2415 : /**
2416 : * Called when the main thread exits, writes out performance
2417 : * stats if requested.
2418 : */
2419 : static void
2420 21 : write_stats (void)
2421 : {
2422 : struct GNUNET_DISK_FileHandle *fh;
2423 21 : pid_t pid = getpid ();
2424 : char *benchmark_dir;
2425 : char *s;
2426 : struct rusage usage;
2427 :
2428 21 : benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
2429 21 : if (NULL == benchmark_dir)
2430 21 : return;
2431 0 : GNUNET_asprintf (&s,
2432 : "%s/taler-exchange-%llu.txt",
2433 : benchmark_dir,
2434 : (unsigned long long) pid);
2435 0 : fh = GNUNET_DISK_file_open (s,
2436 : (GNUNET_DISK_OPEN_WRITE
2437 : | GNUNET_DISK_OPEN_TRUNCATE
2438 : | GNUNET_DISK_OPEN_CREATE),
2439 : (GNUNET_DISK_PERM_USER_READ
2440 : | GNUNET_DISK_PERM_USER_WRITE));
2441 0 : GNUNET_free (s);
2442 0 : if (NULL == fh)
2443 0 : return; /* permission denied? */
2444 :
2445 : /* Collect stats, summed up for all threads */
2446 0 : GNUNET_assert (0 ==
2447 : getrusage (RUSAGE_SELF,
2448 : &usage));
2449 0 : GNUNET_asprintf (&s,
2450 : "time_exchange sys %llu user %llu\n",
2451 0 : (unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000
2452 0 : + usage.ru_stime.tv_usec),
2453 0 : (unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000
2454 0 : + usage.ru_utime.tv_usec));
2455 0 : GNUNET_assert (GNUNET_SYSERR !=
2456 : GNUNET_DISK_file_write_blocking (fh,
2457 : s,
2458 : strlen (s)));
2459 0 : GNUNET_free (s);
2460 0 : GNUNET_assert (GNUNET_OK ==
2461 : GNUNET_DISK_file_close (fh));
2462 : }
2463 :
2464 :
2465 : /* Developer logic for supporting the `-f' option. */
2466 : #if HAVE_DEVELOPER
2467 :
2468 : /**
2469 : * Option `-f' (specifies an input file to give to the HTTP server).
2470 : */
2471 : static char *input_filename;
2472 :
2473 :
2474 : /**
2475 : * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename
2476 : * as the input for the request. If launching the client worked,
2477 : * run the #TEH_KS_loop() event loop as usual.
2478 : *
2479 : * @return child pid
2480 : */
2481 : static pid_t
2482 0 : run_fake_client (void)
2483 : {
2484 : unsigned long long serve_port;
2485 : pid_t cld;
2486 : char ports[6];
2487 : int fd;
2488 : bool use_stdin;
2489 :
2490 0 : if (GNUNET_OK !=
2491 0 : GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
2492 : "exchange",
2493 : "PORT",
2494 : &serve_port))
2495 : {
2496 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
2497 : "exchange",
2498 : "PORT",
2499 : "port number required (for nc)");
2500 0 : return -1;
2501 : }
2502 : /* Duping to STDIN and fork() mess up gcc's analysis
2503 : badly, disable diagnostics. */
2504 : #pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
2505 : #pragma GCC diagnostic ignored "-Wanalyzer-fd-use-without-check"
2506 : #pragma GCC diagnostic ignored "-Wanalyzer-fd-double-close"
2507 : #pragma GCC diagnostic ignored "-Wanalyzer-fd-use-after-close"
2508 0 : use_stdin = (0 == strcmp (input_filename,
2509 : "-"));
2510 0 : if (use_stdin)
2511 0 : fd = STDIN_FILENO;
2512 : else
2513 0 : fd = open (input_filename,
2514 : O_RDONLY);
2515 0 : if (-1 == fd)
2516 : {
2517 0 : fprintf (stderr,
2518 : "Failed to open `%s': %s\n",
2519 : input_filename,
2520 0 : strerror (errno));
2521 0 : return -1;
2522 : }
2523 : /* Fake HTTP client request with #input_filename as input.
2524 : We do this using the nc tool. */
2525 0 : GNUNET_snprintf (ports,
2526 : sizeof (ports),
2527 : "%u",
2528 : (unsigned int) serve_port);
2529 0 : if (0 == (cld = fork ()))
2530 : {
2531 0 : if (! use_stdin)
2532 : {
2533 0 : GNUNET_break (0 == close (STDIN_FILENO));
2534 0 : GNUNET_break (STDIN_FILENO ==
2535 : dup2 (fd,
2536 : STDIN_FILENO));
2537 0 : GNUNET_break (0 == close (fd));
2538 : }
2539 0 : if ( (0 != execlp ("nc",
2540 : "nc",
2541 : "localhost",
2542 : ports,
2543 : "-w", "30",
2544 0 : NULL)) &&
2545 0 : (0 != execlp ("ncat",
2546 : "ncat",
2547 : "localhost",
2548 : ports,
2549 : "-i", "30",
2550 : NULL)) )
2551 : {
2552 0 : fprintf (stderr,
2553 : "Failed to run both `nc' and `ncat': %s\n",
2554 0 : strerror (errno));
2555 : }
2556 0 : _exit (1);
2557 : }
2558 : /* parent process */
2559 0 : if (! use_stdin)
2560 0 : GNUNET_break (0 == close (fd));
2561 0 : return cld;
2562 : #pragma GCC diagnostic pop
2563 : #pragma GCC diagnostic pop
2564 : #pragma GCC diagnostic pop
2565 : #pragma GCC diagnostic pop
2566 : }
2567 :
2568 :
2569 : /**
2570 : * Run the exchange to serve a single request only, without threads.
2571 : *
2572 : * @return #GNUNET_OK on success
2573 : */
2574 : static void
2575 0 : run_single_request (void)
2576 : {
2577 : pid_t xfork;
2578 :
2579 0 : xfork = fork ();
2580 0 : if (-1 == xfork)
2581 : {
2582 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
2583 : "fork");
2584 0 : global_ret = EXIT_NO_RESTART;
2585 0 : GNUNET_SCHEDULER_shutdown ();
2586 0 : return;
2587 : }
2588 0 : if (0 == xfork)
2589 : {
2590 : pid_t cld;
2591 :
2592 0 : cld = run_fake_client ();
2593 0 : if (-1 == cld)
2594 0 : _exit (EXIT_FAILURE);
2595 0 : _exit (EXIT_SUCCESS);
2596 : }
2597 :
2598 : {
2599 : int status;
2600 :
2601 0 : if (xfork != waitpid (xfork,
2602 : &status,
2603 : 0))
2604 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2605 : "Waiting for `nc' child failed: %s\n",
2606 : strerror (errno));
2607 : }
2608 : }
2609 :
2610 :
2611 : /* end of HAVE_DEVELOPER */
2612 : #endif
2613 :
2614 :
2615 : /**
2616 : * Signature of the callback used by MHD to notify the application
2617 : * about completed connections. If we are running in test-mode with
2618 : * an input_filename, this function is used to terminate the HTTPD
2619 : * after the first request has been processed.
2620 : *
2621 : * @param cls client-defined closure, NULL
2622 : * @param connection connection handle (ignored)
2623 : * @param socket_context socket-specific pointer (ignored)
2624 : * @param toe reason for connection notification
2625 : */
2626 : static void
2627 8852 : connection_done (void *cls,
2628 : struct MHD_Connection *connection,
2629 : void **socket_context,
2630 : enum MHD_ConnectionNotificationCode toe)
2631 : {
2632 : (void) cls;
2633 : (void) connection;
2634 : (void) socket_context;
2635 :
2636 8852 : switch (toe)
2637 : {
2638 4426 : case MHD_CONNECTION_NOTIFY_STARTED:
2639 4426 : active_connections++;
2640 4426 : break;
2641 4426 : case MHD_CONNECTION_NOTIFY_CLOSED:
2642 4426 : active_connections--;
2643 4426 : if (TEH_suicide &&
2644 0 : (0 == active_connections) )
2645 0 : GNUNET_SCHEDULER_shutdown ();
2646 4426 : break;
2647 : }
2648 : #if HAVE_DEVELOPER
2649 : /* We only act if the connection is closed. */
2650 8852 : if (MHD_CONNECTION_NOTIFY_CLOSED != toe)
2651 4426 : return;
2652 4426 : if (NULL != input_filename)
2653 0 : GNUNET_SCHEDULER_shutdown ();
2654 : #endif
2655 : }
2656 :
2657 :
2658 : /**
2659 : * Function run on shutdown.
2660 : *
2661 : * @param cls NULL
2662 : */
2663 : static void
2664 21 : do_shutdown (void *cls)
2665 : {
2666 : (void) cls;
2667 21 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2668 : "Shutdown of taler-exchange-httpd\n");
2669 21 : TALER_MHD_daemons_halt ();
2670 21 : TEH_resume_keys_requests (true);
2671 21 : TEH_batch_deposit_cleanup ();
2672 21 : TEH_withdraw_cleanup ();
2673 21 : TEH_melt_v27_cleanup ();
2674 21 : TEH_reserves_close_cleanup ();
2675 21 : TEH_reserves_purse_cleanup ();
2676 21 : TEH_purses_merge_cleanup ();
2677 21 : TEH_kyc_wallet_cleanup ();
2678 21 : TEH_kyc_upload_cleanup ();
2679 21 : TEH_deposits_get_cleanup ();
2680 21 : TEH_reserves_get_cleanup ();
2681 21 : TEH_purses_get_cleanup ();
2682 21 : TEH_kyc_check_cleanup ();
2683 21 : TEH_kyc_info_cleanup ();
2684 21 : TEH_kyc_proof_cleanup ();
2685 21 : TEH_kyc_start_cleanup ();
2686 21 : TEH_aml_decision_cleanup ();
2687 21 : TALER_KYCLOGIC_kyc_done ();
2688 21 : TALER_MHD_daemons_destroy ();
2689 21 : TEH_wire_done ();
2690 21 : TEH_extensions_done ();
2691 21 : TEH_keys_finished ();
2692 21 : if (NULL != TEH_plugin)
2693 : {
2694 21 : TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
2695 21 : TEH_plugin = NULL;
2696 : }
2697 21 : if (NULL != TEH_curl_ctx)
2698 : {
2699 21 : GNUNET_CURL_fini (TEH_curl_ctx);
2700 21 : TEH_curl_ctx = NULL;
2701 : }
2702 21 : if (NULL != exchange_curl_rc)
2703 : {
2704 21 : GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc);
2705 21 : exchange_curl_rc = NULL;
2706 : }
2707 21 : TALER_TEMPLATING_done ();
2708 21 : TEH_cspec = NULL;
2709 21 : TALER_CONFIG_free_currencies (num_cspecs,
2710 : cspecs);
2711 21 : num_cspecs = 0;
2712 21 : cspecs = NULL;
2713 21 : }
2714 :
2715 :
2716 : /**
2717 : * Callback invoked on every listen socket to start the
2718 : * respective MHD HTTP daemon.
2719 : *
2720 : * @param cls unused
2721 : * @param lsock the listen socket
2722 : */
2723 : static void
2724 42 : start_daemon (void *cls,
2725 : int lsock)
2726 : {
2727 : struct MHD_Daemon *mhd;
2728 :
2729 : (void) cls;
2730 42 : GNUNET_assert (-1 != lsock);
2731 42 : mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
2732 : | MHD_USE_PIPE_FOR_SHUTDOWN
2733 : | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
2734 : | MHD_USE_TCP_FASTOPEN,
2735 : 0, /* already bound */
2736 : NULL, NULL,
2737 : &handle_mhd_request, NULL,
2738 : MHD_OPTION_LISTEN_SOCKET,
2739 : lsock,
2740 : MHD_OPTION_EXTERNAL_LOGGER,
2741 : &TALER_MHD_handle_logs,
2742 : NULL,
2743 : MHD_OPTION_NOTIFY_COMPLETED,
2744 : &handle_mhd_completion_callback,
2745 : NULL,
2746 : MHD_OPTION_NOTIFY_CONNECTION,
2747 : &connection_done,
2748 : NULL,
2749 : MHD_OPTION_CONNECTION_TIMEOUT,
2750 : connection_timeout,
2751 42 : (0 == allow_address_reuse)
2752 : ? MHD_OPTION_END
2753 : : MHD_OPTION_LISTENING_ADDRESS_REUSE,
2754 : (unsigned int) allow_address_reuse,
2755 : MHD_OPTION_END);
2756 42 : if (NULL == mhd)
2757 : {
2758 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2759 : "Failed to launch HTTP service!\n");
2760 0 : GNUNET_SCHEDULER_shutdown ();
2761 0 : return;
2762 : }
2763 42 : have_daemons = true;
2764 42 : TALER_MHD_daemon_start (mhd);
2765 : }
2766 :
2767 :
2768 : /**
2769 : * Main function that will be run by the scheduler.
2770 : *
2771 : * @param cls closure
2772 : * @param args remaining command-line arguments
2773 : * @param cfgfile name of the configuration file used (for saving, can be
2774 : * NULL!)
2775 : * @param config configuration
2776 : */
2777 : static void
2778 21 : run (void *cls,
2779 : char *const *args,
2780 : const char *cfgfile,
2781 : const struct GNUNET_CONFIGURATION_Handle *config)
2782 : {
2783 : enum TALER_MHD_GlobalOptions go;
2784 : enum GNUNET_GenericReturnValue ret;
2785 :
2786 : (void) cls;
2787 : (void) args;
2788 : (void ) cfgfile;
2789 21 : go = TALER_MHD_GO_NONE;
2790 21 : if (connection_close)
2791 0 : go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
2792 21 : TALER_MHD_setup (go);
2793 21 : TEH_cfg = config;
2794 :
2795 21 : if (GNUNET_OK !=
2796 21 : exchange_serve_process_config (cfgfile))
2797 : {
2798 0 : global_ret = EXIT_NOTCONFIGURED;
2799 0 : GNUNET_SCHEDULER_shutdown ();
2800 0 : return;
2801 : }
2802 21 : if (GNUNET_OK !=
2803 21 : TEH_spa_init ())
2804 : {
2805 0 : global_ret = EXIT_NOTCONFIGURED;
2806 0 : GNUNET_SCHEDULER_shutdown ();
2807 0 : return;
2808 : }
2809 21 : if (GNUNET_OK !=
2810 21 : TALER_TEMPLATING_init (TALER_EXCHANGE_project_data ()))
2811 : {
2812 0 : global_ret = EXIT_NOTINSTALLED;
2813 0 : GNUNET_SCHEDULER_shutdown ();
2814 0 : return;
2815 : }
2816 21 : if (GNUNET_SYSERR ==
2817 21 : TEH_plugin->preflight (TEH_plugin->cls))
2818 : {
2819 0 : GNUNET_break (0);
2820 0 : global_ret = EXIT_NO_RESTART;
2821 0 : GNUNET_SCHEDULER_shutdown ();
2822 0 : return;
2823 : }
2824 21 : if (GNUNET_OK !=
2825 21 : TEH_extensions_init ())
2826 : {
2827 0 : global_ret = EXIT_NOTINSTALLED;
2828 0 : GNUNET_SCHEDULER_shutdown ();
2829 0 : return;
2830 : }
2831 21 : if (GNUNET_OK !=
2832 21 : TEH_keys_init ())
2833 : {
2834 0 : GNUNET_break (0);
2835 0 : global_ret = EXIT_NO_RESTART;
2836 0 : GNUNET_SCHEDULER_shutdown ();
2837 0 : return;
2838 : }
2839 21 : if (GNUNET_OK !=
2840 21 : TEH_wire_init ())
2841 : {
2842 0 : GNUNET_break (0);
2843 0 : global_ret = EXIT_NO_RESTART;
2844 0 : GNUNET_SCHEDULER_shutdown ();
2845 0 : return;
2846 : }
2847 :
2848 21 : TEH_load_terms (TEH_cfg);
2849 : TEH_curl_ctx
2850 21 : = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
2851 : &exchange_curl_rc);
2852 21 : if (NULL == TEH_curl_ctx)
2853 : {
2854 0 : GNUNET_break (0);
2855 0 : global_ret = EXIT_NO_RESTART;
2856 0 : GNUNET_SCHEDULER_shutdown ();
2857 0 : return;
2858 : }
2859 21 : exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx);
2860 21 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2861 : NULL);
2862 21 : ret = TALER_MHD_listen_bind (TEH_cfg,
2863 : "exchange",
2864 : &start_daemon,
2865 : NULL);
2866 21 : switch (ret)
2867 : {
2868 0 : case GNUNET_SYSERR:
2869 0 : global_ret = EXIT_NOTCONFIGURED;
2870 0 : GNUNET_SCHEDULER_shutdown ();
2871 0 : return;
2872 0 : case GNUNET_NO:
2873 0 : if (! have_daemons)
2874 : {
2875 0 : global_ret = EXIT_NOTCONFIGURED;
2876 0 : GNUNET_SCHEDULER_shutdown ();
2877 0 : return;
2878 : }
2879 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2880 : "Could not open all configured listen sockets\n");
2881 0 : break;
2882 21 : case GNUNET_OK:
2883 21 : break;
2884 : }
2885 21 : global_ret = EXIT_SUCCESS;
2886 21 : atexit (&write_stats);
2887 : #if HAVE_DEVELOPER
2888 21 : if (NULL != input_filename)
2889 0 : run_single_request ();
2890 : #endif
2891 : }
2892 :
2893 :
2894 : /**
2895 : * The main function of the taler-exchange-httpd server ("the exchange").
2896 : *
2897 : * @param argc number of arguments from the command line
2898 : * @param argv command line arguments
2899 : * @return 0 ok, 1 on error
2900 : */
2901 : int
2902 40 : main (int argc,
2903 : char *const *argv)
2904 : {
2905 40 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
2906 40 : GNUNET_GETOPT_option_flag ('a',
2907 : "allow-timetravel",
2908 : "allow clients to request /keys for arbitrary timestamps (for testing and development only)",
2909 : &TEH_allow_keys_timetravel),
2910 40 : GNUNET_GETOPT_option_flag ('C',
2911 : "connection-close",
2912 : "force HTTP connections to be closed after each request",
2913 : &connection_close),
2914 40 : GNUNET_GETOPT_option_flag ('I',
2915 : "check-invariants",
2916 : "enable expensive invariant checks",
2917 : &TEH_check_invariants_flag),
2918 40 : GNUNET_GETOPT_option_flag ('r',
2919 : "allow-reuse-address",
2920 : "allow multiple HTTPDs to listen to the same port",
2921 : &allow_address_reuse),
2922 40 : GNUNET_GETOPT_option_uint ('t',
2923 : "timeout",
2924 : "SECONDS",
2925 : "after how long do connections timeout by default (in seconds)",
2926 : &connection_timeout),
2927 40 : GNUNET_GETOPT_option_timetravel ('T',
2928 : "timetravel"),
2929 : #if HAVE_DEVELOPER
2930 40 : GNUNET_GETOPT_option_filename ('f',
2931 : "file-input",
2932 : "FILENAME",
2933 : "run in test-mode using FILENAME as the HTTP request to process, use '-' to read from stdin",
2934 : &input_filename),
2935 : #endif
2936 40 : GNUNET_GETOPT_option_help (
2937 : TALER_EXCHANGE_project_data (),
2938 : "HTTP server providing a RESTful API to access a Taler exchange"),
2939 : GNUNET_GETOPT_OPTION_END
2940 : };
2941 : enum GNUNET_GenericReturnValue ret;
2942 :
2943 40 : ret = GNUNET_PROGRAM_run (
2944 : TALER_EXCHANGE_project_data (),
2945 : argc, argv,
2946 : "taler-exchange-httpd",
2947 : "Taler exchange HTTP service",
2948 : options,
2949 : &run, NULL);
2950 40 : if (GNUNET_SYSERR == ret)
2951 0 : return EXIT_INVALIDARGUMENT;
2952 40 : if (GNUNET_NO == ret)
2953 19 : return EXIT_SUCCESS;
2954 21 : return global_ret;
2955 : }
2956 :
2957 :
2958 : /* end of taler-exchange-httpd.c */
|