commit 32e12126c6c7cbc3b329f8611cde593ee150bfc7
parent eb87ea594acc2e2dd8b02cb4bf8cf86802185d1c
Author: Christian Grothoff <christian@grothoff.org>
Date: Tue, 23 Sep 2025 11:31:18 +0200
fix completely cursed POST donau instance logic (see #10442)
Diffstat:
3 files changed, 118 insertions(+), 70 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -4364,6 +4364,7 @@ phase_parse_wallet_data (struct PayContext *pc)
/* Check if the needed data is present for the given donau URL */
{
enum GNUNET_DB_QueryStatus qs;
+
qs = TMH_db->lookup_order_charity (
TMH_db->cls,
pc->hc->instance->settings.id,
@@ -4407,7 +4408,7 @@ phase_parse_wallet_data (struct PayContext *pc)
pc->parse_wallet_data.donau_keys =
DONAU_keys_from_json (donau_keys_json);
json_decref (donau_keys_json);
- if (! pc->parse_wallet_data.donau_keys)
+ if (NULL == pc->parse_wallet_data.donau_keys)
{
GNUNET_break_op (0);
resume_pay_with_error (pc,
diff --git a/src/backend/taler-merchant-httpd_private-post-donau-instance.c b/src/backend/taler-merchant-httpd_private-post-donau-instance.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2024 Taler Systems SA
+ Copyright (C) 2024, 2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -18,8 +18,8 @@
* @brief implementation of POST /donau
* @author Bohdan Potuzhnyi
* @author Vlada Svirsh
+ * @author Christian Grothoff
*/
-
#include "platform.h"
#include <jansson.h>
#include "donau/donau_service.h"
@@ -65,16 +65,30 @@ struct PostDonauCtx
uint64_t charity_id;
/**
- * Data that comes back from Donau
+ * Handle returned by DONAU_charities_get(); needed to cancel on
+ * connection abort, etc.
*/
- struct DONAU_Charity charity;
+ struct DONAU_CharityGetHandle *get_handle;
/**
- * Handle returned by DONAU_charities_get(); needed to cancel on
- * connection abort, etc. */
- struct DONAU_CharityGetHandle *get_handle;
+ * Response to return.
+ */
+ struct MHD_Response *response;
+
+ /**
+ * HTTP status for @e response.
+ */
+ unsigned int http_status;
+
+ /**
+ * #GNUNET_YES if we are suspended,
+ * #GNUNET_NO if not,
+ * #GNUNET_SYSERR on shutdown
+ */
+ enum GNUNET_GenericReturnValue suspended;
};
+
/**
* Head of active pay context DLL.
*/
@@ -86,6 +100,22 @@ static struct PostDonauCtx *pdc_head;
static struct PostDonauCtx *pdc_tail;
+void
+TMH_force_post_donau_resume ()
+{
+ for (struct PostDonauCtx *pdc = pdc_head;
+ NULL != pdc;
+ pdc = pdc->next)
+ {
+ if (GNUNET_YES == pdc->suspended)
+ {
+ pdc->suspended = GNUNET_SYSERR;
+ MHD_resume_connection (pdc->connection);
+ }
+ }
+}
+
+
/**
* Callback for DONAU_charities_get() to handle the response.
*
@@ -100,32 +130,31 @@ donau_charity_get_cb (void *cls,
enum GNUNET_DB_QueryStatus qs;
pdc->get_handle = NULL;
- if (NULL == pdc->connection)
- return;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Processing DONAU charity get response");
/* Anything but 200 => propagate Donau’s response. */
if (MHD_HTTP_OK != gcr->hr.http_status)
{
- TALER_MHD_reply_with_error (pdc->connection,
- gcr->hr.http_status,
- gcr->hr.ec,
- gcr->hr.hint);
+ pdc->http_status = MHD_HTTP_BAD_GATEWAY;
+ pdc->response = TALER_MHD_MAKE_JSON_PACK (
+ TALER_MHD_PACK_EC (gcr->hr.ec),
+ GNUNET_JSON_pack_uint64 ("donau_http_status",
+ gcr->hr.http_status));
+ pdc->suspended = GNUNET_NO;
MHD_resume_connection (cls);
TALER_MHD_daemon_trigger ();
return;
}
- pdc->charity = gcr->details.ok.charity;
-
- if (GNUNET_memcmp (&pdc->charity.charity_pub.eddsa_pub,
+ if (GNUNET_memcmp (&gcr->details.ok.charity.charity_pub.eddsa_pub,
&pdc->hc->instance->merchant_pub.eddsa_pub))
{
- TALER_MHD_reply_with_error (pdc->connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "Merchant public key of this instance and charity public key received "
- "from DONAU for this charity_id do not match");
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Charity key at donau does not match our merchant key\n");
+ pdc->http_status = MHD_HTTP_CONFLICT;
+ pdc->response = TALER_MHD_make_error (
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "charity_pub != merchant_pub");
MHD_resume_connection (pdc->connection);
TALER_MHD_daemon_trigger ();
return;
@@ -133,22 +162,31 @@ donau_charity_get_cb (void *cls,
qs = TMH_db->insert_donau_instance (TMH_db->cls,
pdc->donau_url,
- &pdc->charity,
+ &gcr->details.ok.charity,
pdc->charity_id);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- TALER_MHD_reply_with_error (pdc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "insert_donau_instance: Failed to insert Donau instance");
+ GNUNET_break (0);
+ pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ pdc->response = TALER_MHD_make_error (
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_donau_instance");
break;
case GNUNET_DB_STATUS_SOFT_ERROR:
- TALER_MHD_reply_with_error (pdc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_SOFT_FAILURE,
- "insert_donau_instance: Database operation failed");
+ GNUNET_break (0);
+ pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ pdc->response = TALER_MHD_make_error (
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_donau_instance");
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* presumably idempotent, no need to notify, but still respond */
+ pdc->http_status = MHD_HTTP_NO_CONTENT;
+ pdc->response = MHD_create_response_from_buffer_static (0,
+ NULL);
+ TALER_MHD_add_global_headers (pdc->response,
+ false);
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
{
@@ -160,14 +198,15 @@ donau_charity_get_cb (void *cls,
&es,
pdc->donau_url,
strlen (pdc->donau_url) + 1);
- TALER_MHD_reply_static (pdc->connection,
- MHD_HTTP_NO_CONTENT,
- NULL,
- NULL,
- 0);
+ pdc->http_status = MHD_HTTP_NO_CONTENT;
+ pdc->response = MHD_create_response_from_buffer_static (0,
+ NULL);
+ TALER_MHD_add_global_headers (pdc->response,
+ false);
break;
}
}
+ pdc->suspended = GNUNET_NO;
MHD_resume_connection (pdc->connection);
TALER_MHD_daemon_trigger ();
}
@@ -184,8 +223,10 @@ post_donau_cleanup (void *cls)
struct PostDonauCtx *pdc = cls;
if (pdc->get_handle)
+ {
DONAU_charity_get_cancel (pdc->get_handle);
-
+ pdc->get_handle = NULL;
+ }
GNUNET_CONTAINER_DLL_remove (pdc_head,
pdc_tail,
pdc);
@@ -207,54 +248,42 @@ TMH_private_post_donau_instance (const struct TMH_RequestHandler *rh,
struct TMH_HandlerContext *hc)
{
struct PostDonauCtx *pdc = hc->ctx;
- const char *donau_url = NULL;
- uint64_t charity_id = -1;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("donau_url",
- &donau_url),
- GNUNET_JSON_spec_uint64 ("charity_id",
- &charity_id),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- TALER_MHD_parse_json_data (connection,
- hc->request_body,
- spec))
- {
- return MHD_NO;
- }
- if ( (NULL == donau_url) ||
- (-1 == charity_id) )
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "Missing donau_url or charity_id");
- }
if (NULL == pdc)
{
pdc = GNUNET_new (struct PostDonauCtx);
pdc->connection = connection;
pdc->hc = hc;
- pdc->donau_url = donau_url;
- pdc->charity_id = charity_id;
hc->ctx = pdc;
hc->cc = &post_donau_cleanup;
GNUNET_CONTAINER_DLL_insert (pdc_head,
pdc_tail,
pdc);
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("donau_url",
+ &pdc->donau_url),
+ GNUNET_JSON_spec_uint64 ("charity_id",
+ &pdc->charity_id),
+ GNUNET_JSON_spec_end ()
+ };
+ if (GNUNET_OK !=
+ TALER_MHD_parse_json_data (connection,
+ hc->request_body,
+ spec))
+ {
+ GNUNET_break_op (0);
+ return MHD_NO;
+ }
+ }
pdc->get_handle =
DONAU_charity_get (TMH_curl_ctx,
- donau_url,
- charity_id,
+ pdc->donau_url,
+ pdc->charity_id,
NULL, /* bearer */
&donau_charity_get_cb,
pdc);
-
if (NULL == pdc->get_handle)
{
GNUNET_break (0);
@@ -264,8 +293,18 @@ TMH_private_post_donau_instance (const struct TMH_RequestHandler *rh,
TALER_EC_GENERIC_ALLOCATION_FAILURE,
"Failed to initiate Donau lookup");
}
+ pdc->suspended = GNUNET_YES;
MHD_suspend_connection (connection);
return MHD_YES;
}
- return MHD_YES;
+
+ if (NULL != pdc->response)
+ {
+ GNUNET_break (GNUNET_NO == pdc->suspended);
+ return MHD_queue_response (pdc->connection,
+ pdc->http_status,
+ pdc->response);
+ }
+ GNUNET_break (GNUNET_SYSERR == pdc->suspended);
+ return MHD_NO;
}
diff --git a/src/backend/taler-merchant-httpd_private-post-donau-instance.h b/src/backend/taler-merchant-httpd_private-post-donau-instance.h
@@ -25,6 +25,14 @@
#include "taler-merchant-httpd.h"
+
+/**
+ * Resume all connections suspended on Donau-interaction during shutdown.
+ */
+void
+TMH_force_post_donau_resume (void);
+
+
/**
* Handle a POST "/donau" request.
*