commit 721c98aad016cd1227ceab395c346793f44e9fee
parent 7137acc7578790f79d81d6f85c72a537e03d3abf
Author: Christian Grothoff <christian@grothoff.org>
Date: Tue, 11 Aug 2026 13:51:36 +0200
fix #11107: return ECs that are more specific about what went wrong during transmission
Diffstat:
1 file changed, 86 insertions(+), 5 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_post-challenge-ID.c b/src/backend/taler-merchant-httpd_post-challenge-ID.c
@@ -155,6 +155,19 @@ struct MfaState
* Set to true if sending worked.
*/
bool send_ok;
+
+ /**
+ * Exit status of the transmission helper, valid if it exited normally.
+ * Retained (and not just collapsed into @e send_ok) because the helpers
+ * classify *why* transmission failed; see classify_helper_status().
+ */
+ unsigned long int exit_code;
+
+ /**
+ * True if the transmission helper exited normally, so that @e exit_code
+ * is meaningful.
+ */
+ bool exited;
};
@@ -278,6 +291,49 @@ respond_with_error (struct MfaState *mfa,
/**
+ * Map the exit status of a challenge transmission helper onto an HTTP status
+ * and error code.
+ *
+ * The helpers shipped with Challenger use a banded exit-code scheme documented
+ * in challenger-send-sms(1): anything below 10 means the challenge was
+ * transmitted, 10-19 blames the address configured for this step, 20-29 is a
+ * recipient that is temporarily unreachable, 30-39 is the transmission
+ * provider and 40-49 is our own configuration.
+ *
+ * Codes we do not recognise are reported as an upstream failure rather than
+ * blamed on the client, so that a helper predating this scheme -- which used
+ * small ad-hoc exit codes -- never yields a client error.
+ *
+ * @param exit_code exit status of the helper, which must have exited normally
+ * @param[out] http_status set to the HTTP status to return
+ * @return error code to return
+ */
+static enum TALER_ErrorCode
+classify_helper_status (unsigned long int exit_code,
+ unsigned int *http_status)
+{
+ if (exit_code < 20)
+ {
+ *http_status = MHD_HTTP_BAD_REQUEST;
+ return TALER_EC_MERCHANT_TAN_ADDRESS_UNUSABLE;
+ }
+ if (exit_code < 30)
+ {
+ *http_status = MHD_HTTP_SERVICE_UNAVAILABLE;
+ return TALER_EC_MERCHANT_TAN_ADDRESS_UNREACHABLE;
+ }
+ if ( (exit_code >= 40) &&
+ (exit_code < 50) )
+ {
+ *http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return TALER_EC_MERCHANT_TAN_HELPER_MISCONFIGURED;
+ }
+ *http_status = MHD_HTTP_BAD_GATEWAY;
+ return TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED;
+}
+
+
+/**
* Challenge code transmission complete. Continue based on the result.
*
* @param[in,out] mfa process to send the challenge for
@@ -289,10 +345,30 @@ phase_sent (struct MfaState *mfa)
if (! mfa->send_ok)
{
+ char es[32];
+ unsigned int http_status;
+ enum TALER_ErrorCode ec;
+
+ if (! mfa->exited)
+ {
+ /* Killed by a signal or otherwise abnormal: there is no exit code to
+ classify. */
+ http_status = MHD_HTTP_BAD_GATEWAY;
+ ec = TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED;
+ }
+ else
+ {
+ ec = classify_helper_status (mfa->exit_code,
+ &http_status);
+ }
+ GNUNET_snprintf (es,
+ sizeof (es),
+ "process exited with %u",
+ (unsigned int) mfa->exit_code);
respond_with_error (mfa,
- MHD_HTTP_BAD_GATEWAY,
- TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED,
- "process exited with error");
+ http_status,
+ ec,
+ es);
return;
}
qs = TALER_MERCHANTDB_update_mfa_challenge (TMH_db,
@@ -369,8 +445,13 @@ transmission_done_cb (void *cls,
GNUNET_process_destroy (mfa->child);
mfa->child = NULL;
}
- mfa->send_ok = ( (GNUNET_OS_PROCESS_EXITED == type) &&
- (0 == exit_code) );
+ mfa->exited = (GNUNET_OS_PROCESS_EXITED == type);
+ mfa->exit_code = exit_code;
+ /* Exit codes below 10 all mean the challenge was transmitted: 0 confirmed
+ on the handset, 1 accepted by the provider, 2 suppressed as a duplicate
+ of a message already in flight. See challenger-send-sms(1). */
+ mfa->send_ok = (mfa->exited &&
+ (exit_code < 10) );
if (! mfa->send_ok)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"MFA helper failed with status %d/%u\n",