ensure forward-compatibility for auditor C API

This commit is contained in:
Christian Grothoff 2023-06-21 07:53:17 +02:00
parent d6838ed841
commit 17789253e9
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
10 changed files with 200 additions and 138 deletions

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014-2021 Taler Systems SA
Copyright (C) 2014-2023 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
@ -146,21 +146,54 @@ struct TALER_AUDITOR_HttpResponse
};
/**
* Response to /version request.
*/
struct TALER_AUDITOR_VersionResponse
{
/**
* HTTP response.
*/
struct TALER_AUDITOR_HttpResponse hr;
/**
* Details depending on HTTP status.
*/
union
{
/**
* Details for #MHD_HTTP_OK.
*/
struct
{
/**
* Protocol compatibility evaluation.
*/
enum TALER_AUDITOR_VersionCompatibility compat;
/**
* Version data returned by /config.
*/
struct TALER_AUDITOR_VersionInformation vi;
} ok;
} details;
};
/**
* Function called with information about the auditor.
*
* @param cls closure
* @param hr HTTP response data
* @param vi basic information about the auditor
* @param compat protocol compatibility information
* @param vr response data
*/
// FIXME: bad API!
typedef void
(*TALER_AUDITOR_VersionCallback) (
void *cls,
const struct TALER_AUDITOR_HttpResponse *hr,
const struct TALER_AUDITOR_VersionInformation *vi,
enum TALER_AUDITOR_VersionCompatibility compat);
const struct TALER_AUDITOR_VersionResponse *vr);
/**
@ -206,17 +239,29 @@ TALER_AUDITOR_disconnect (struct TALER_AUDITOR_Handle *auditor);
struct TALER_AUDITOR_DepositConfirmationHandle;
/**
* Response to /deposit-confirmation request.
*/
struct TALER_AUDITOR_DepositConfirmationResponse
{
/**
* HTTP response.
*/
struct TALER_AUDITOR_HttpResponse hr;
};
/**
* Signature of functions called with the result from our call to the
* auditor's /deposit-confirmation handler.
*
* @param cls closure
* @param hr HTTP response data
* @param dcr response data
*/
typedef void
(*TALER_AUDITOR_DepositConfirmationResultCallback)(
void *cls,
const struct TALER_AUDITOR_HttpResponse *hr);
const struct TALER_AUDITOR_DepositConfirmationResponse *dcr);
/**
@ -313,20 +358,54 @@ struct TALER_AUDITOR_ExchangeInfo
};
/**
* Response to GET /exchanges request.
*/
struct TALER_AUDITOR_ListExchangesResponse
{
/**
* HTTP response.
*/
struct TALER_AUDITOR_HttpResponse hr;
/**
* Details depending on HTTP status.
*/
union
{
/**
* Details for #MHD_HTTP_OK.
*/
struct
{
/**
* Length of the @e ei array.
*/
unsigned int num_exchanges;
/**
* Array with information about exchanges
* audited by this auditor.
*/
const struct TALER_AUDITOR_ExchangeInfo *ei;
} ok;
} details;
};
/**
* Function called with the result from /exchanges.
*
* @param cls closure
* @param hr HTTP response data
* @param num_exchanges length of array at @a ei
* @param ei information about exchanges returned by the auditor
* @param ler response data
*/
typedef void
(*TALER_AUDITOR_ListExchangesResultCallback)(
void *cls,
const struct TALER_AUDITOR_HttpResponse *hr,
unsigned int num_exchanges,
const struct TALER_AUDITOR_ExchangeInfo *ei);
const struct TALER_AUDITOR_ListExchangesResponse *ler);
/**
* Submit an /exchanges request to the auditor and get the

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014-2021 Taler Systems SA
Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@ -87,64 +87,64 @@ handle_deposit_confirmation_finished (void *cls,
{
const json_t *json = djson;
struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls;
struct TALER_AUDITOR_HttpResponse hr = {
.reply = json,
.http_status = (unsigned int) response_code
struct TALER_AUDITOR_DepositConfirmationResponse dcr = {
.hr.reply = json,
.hr.http_status = (unsigned int) response_code
};
dh->job = NULL;
switch (response_code)
{
case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
dcr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
hr.ec = TALER_EC_NONE;
dcr.hr.ec = TALER_EC_NONE;
break;
case MHD_HTTP_BAD_REQUEST:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
dcr.hr.ec = TALER_JSON_get_error_code (json);
dcr.hr.hint = TALER_JSON_get_error_hint (json);
/* This should never happen, either us or the auditor is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_FORBIDDEN:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
dcr.hr.ec = TALER_JSON_get_error_code (json);
dcr.hr.hint = TALER_JSON_get_error_hint (json);
/* Nothing really to verify, auditor says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
dcr.hr.ec = TALER_JSON_get_error_code (json);
dcr.hr.hint = TALER_JSON_get_error_hint (json);
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
break;
case MHD_HTTP_GONE:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
dcr.hr.ec = TALER_JSON_get_error_code (json);
dcr.hr.hint = TALER_JSON_get_error_hint (json);
/* Nothing really to verify, auditor says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
dcr.hr.ec = TALER_JSON_get_error_code (json);
dcr.hr.hint = TALER_JSON_get_error_hint (json);
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
dcr.hr.ec = TALER_JSON_get_error_code (json);
dcr.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for auditor deposit confirmation\n",
(unsigned int) response_code,
hr.ec);
dcr.hr.ec);
break;
}
dh->cb (dh->cb_cls,
&hr);
&dcr);
TALER_AUDITOR_deposit_confirmation_cancel (dh);
}

View File

@ -89,16 +89,16 @@ handle_exchanges_finished (void *cls,
const json_t *ja;
unsigned int ja_len;
struct TALER_AUDITOR_ListExchangesHandle *leh = cls;
struct TALER_AUDITOR_HttpResponse hr = {
.reply = json,
.http_status = (unsigned int) response_code
struct TALER_AUDITOR_ListExchangesResponse ler = {
.hr.reply = json,
.hr.http_status = (unsigned int) response_code
};
leh->job = NULL;
switch (response_code)
{
case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
ler.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
ja = json_object_get (json,
@ -107,8 +107,8 @@ handle_exchanges_finished (void *cls,
(! json_is_array (ja)) )
{
GNUNET_break (0);
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
hr.http_status = 0;
ler.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
ler.hr.http_status = 0;
break;
}
@ -116,12 +116,12 @@ handle_exchanges_finished (void *cls,
if (ja_len > MAX_EXCHANGES)
{
GNUNET_break (0);
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
hr.http_status = 0;
ler.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
ler.hr.http_status = 0;
break;
}
{
struct TALER_AUDITOR_ExchangeInfo ei[ja_len];
struct TALER_AUDITOR_ExchangeInfo ei[GNUNET_NZL (ja_len)];
bool ok;
ok = true;
@ -141,54 +141,52 @@ handle_exchanges_finished (void *cls,
{
GNUNET_break_op (0);
ok = false;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
hr.http_status = 0;
ler.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
ler.hr.http_status = 0;
break;
}
}
if (! ok)
break;
ler.details.ok.ei = ei;
ler.details.ok.num_exchanges = ja_len;
leh->cb (leh->cb_cls,
&hr,
ja_len,
ei);
&ler);
TALER_AUDITOR_list_exchanges_cancel (leh);
return;
}
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the auditor is buggy
(or API version conflict); just pass JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
ler.hr.ec = TALER_JSON_get_error_code (json);
ler.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
ler.hr.ec = TALER_JSON_get_error_code (json);
ler.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
ler.hr.ec = TALER_JSON_get_error_code (json);
ler.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
ler.hr.ec = TALER_JSON_get_error_code (json);
ler.hr.hint = TALER_JSON_get_error_hint (json);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for auditor list-exchanges request\n",
(unsigned int) response_code,
(int) hr.ec);
(int) ler.hr.ec);
GNUNET_break_op (0);
break;
}
if (NULL != leh->cb)
leh->cb (leh->cb_cls,
&hr,
0,
NULL);
&ler);
TALER_AUDITOR_list_exchanges_cancel (leh);
}

View File

@ -239,10 +239,9 @@ version_completed_cb (void *cls,
{
struct TALER_AUDITOR_Handle *auditor = cls;
const json_t *resp_obj = gresp_obj;
enum TALER_AUDITOR_VersionCompatibility vc;
struct TALER_AUDITOR_HttpResponse hr = {
.reply = resp_obj,
.http_status = (unsigned int) response_code
struct TALER_AUDITOR_VersionResponse vr = {
.hr.reply = resp_obj,
.hr.http_status = (unsigned int) response_code
};
auditor->vr = NULL;
@ -250,7 +249,6 @@ version_completed_cb (void *cls,
"Received version from URL `%s' with status %ld.\n",
auditor->url,
response_code);
vc = TALER_AUDITOR_VC_PROTOCOL_ERROR;
switch (response_code)
{
case 0:
@ -268,28 +266,33 @@ version_completed_cb (void *cls,
{
GNUNET_break_op (0);
TALER_LOG_WARNING ("NULL body for a 200-OK /config\n");
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
vr.hr.http_status = 0;
vr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
hr.ec = decode_version_json (resp_obj,
auditor,
&vc);
if (TALER_EC_NONE != hr.ec)
vr.hr.ec = decode_version_json (resp_obj,
auditor,
&vr.details.ok.compat);
if (TALER_EC_NONE != vr.hr.ec)
{
GNUNET_break_op (0);
hr.http_status = 0;
vr.hr.http_status = 0;
break;
}
auditor->state = MHS_VERSION;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Auditor %s is ready!\n",
auditor->url);
vr.details.ok.vi = auditor->vi;
auditor->retry_delay = GNUNET_TIME_UNIT_ZERO; /* restart quickly */
break;
default:
hr.ec = TALER_JSON_get_error_code (resp_obj);
hr.hint = TALER_JSON_get_error_hint (resp_obj);
vr.hr.ec = TALER_JSON_get_error_code (resp_obj);
vr.hr.hint = TALER_JSON_get_error_hint (resp_obj);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d\n",
(unsigned int) response_code,
(int) hr.ec);
(int) vr.hr.ec);
break;
}
if (MHD_HTTP_OK != response_code)
@ -299,23 +302,9 @@ version_completed_cb (void *cls,
auditor->url,
(unsigned int) response_code);
auditor->state = MHS_FAILED;
/* notify application that we failed */
auditor->version_cb (auditor->version_cb_cls,
&hr,
NULL,
vc);
return;
}
TALER_LOG_DEBUG ("Switching auditor state to 'version'\n");
auditor->state = MHS_VERSION;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Auditor %s is ready!\n",
auditor->url);
/* notify application about the key information */
auditor->version_cb (auditor->version_cb_cls,
&hr,
&auditor->vi,
vc);
&vr);
}

View File

@ -168,19 +168,20 @@ struct KeysRequest
void
TEAH_acc_confirmation_cb (void *cls,
const struct TALER_AUDITOR_HttpResponse *hr)
TEAH_acc_confirmation_cb (
void *cls,
const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
{
struct TEAH_AuditorInteractionEntry *aie = cls;
struct TEAH_AuditorListEntry *ale = aie->ale;
if (MHD_HTTP_OK != hr->http_status)
if (MHD_HTTP_OK != dcr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n",
ale->auditor_url,
hr->http_status,
hr->ec);
dcr->hr.http_status,
dcr->hr.ec);
}
GNUNET_CONTAINER_DLL_remove (ale->ai_head,
ale->ai_tail,
@ -579,21 +580,17 @@ parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf,
* auditor as 'up'.
*
* @param cls closure, a `struct TEAH_AuditorListEntry *`
* @param hr http response from the auditor
* @param vi basic information about the auditor
* @param compat protocol compatibility information
* @param vr response from the auditor
*/
static void
auditor_version_cb (
void *cls,
const struct TALER_AUDITOR_HttpResponse *hr,
const struct TALER_AUDITOR_VersionInformation *vi,
enum TALER_AUDITOR_VersionCompatibility compat)
const struct TALER_AUDITOR_VersionResponse *vr)
{
struct TEAH_AuditorListEntry *ale = cls;
enum TALER_AUDITOR_VersionCompatibility compat;
(void) hr;
if (NULL == vi)
if (MHD_HTTP_OK != vr->hr.http_status)
{
/* In this case, we don't mark the auditor as 'up' */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@ -601,7 +598,7 @@ auditor_version_cb (
ale->auditor_url);
return;
}
compat = vr->details.ok.compat;
if (0 != (TALER_AUDITOR_VC_INCOMPATIBLE & compat))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,

View File

@ -193,11 +193,12 @@ typedef struct TEAH_AuditorInteractionEntry *
* auditor's /deposit-confirmation handler.
*
* @param cls closure of type `struct TEAH_AuditorInteractionEntry *`
* @param hr HTTP response
* @param dcr response
*/
void
TEAH_acc_confirmation_cb (void *cls,
const struct TALER_AUDITOR_HttpResponse *hr);
TEAH_acc_confirmation_cb (
void *cls,
const struct TALER_AUDITOR_DepositConfirmationResponse *dcr);
/**

View File

@ -82,20 +82,15 @@ do_timeout (void *cls)
* Function called with information about the auditor.
*
* @param cls closure
* @param hr http response details
* @param vi basic information about the auditor
* @param compat protocol compatibility information
* @param vr response details
*/
static void
version_cb (void *cls,
const struct TALER_AUDITOR_HttpResponse *hr,
const struct TALER_AUDITOR_VersionInformation *vi,
enum TALER_AUDITOR_VersionCompatibility compat)
const struct TALER_AUDITOR_VersionResponse *vr)
{
(void) cls;
(void) hr;
if ( (NULL != vi) &&
(TALER_AUDITOR_VC_MATCH == compat) )
if ( (MHD_HTTP_OK == vr->hr.http_status) &&
(TALER_AUDITOR_VC_MATCH == vr->details.ok.compat) )
global_ret = 0;
else
global_ret = 2;

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2018, 2021 Taler Systems SA
Copyright (C) 2018-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
@ -132,13 +132,15 @@ do_retry (void *cls)
* to check if the response code is acceptable.
*
* @param cls closure.
* @param hr HTTP response details
* @param dcr response details
*/
static void
deposit_confirmation_cb (void *cls,
const struct TALER_AUDITOR_HttpResponse *hr)
deposit_confirmation_cb (
void *cls,
const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
{
struct DepositConfirmationState *dcs = cls;
const struct TALER_AUDITOR_HttpResponse *hr = &dcr->hr;
dcs->dc = NULL;
if (dcs->expected_response_code != hr->http_status)

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
Copyright (C) 2018-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
@ -127,11 +127,10 @@ do_retry (void *cls)
*/
static void
exchanges_cb (void *cls,
const struct TALER_AUDITOR_HttpResponse *hr,
unsigned int num_exchanges,
const struct TALER_AUDITOR_ExchangeInfo *ei)
const struct TALER_AUDITOR_ListExchangesResponse *ler)
{
struct ExchangesState *es = cls;
const struct TALER_AUDITOR_HttpResponse *hr = &ler->hr;
es->leh = NULL;
if (es->expected_response_code != hr->http_status)
@ -164,24 +163,30 @@ exchanges_cb (void *cls,
hr->http_status);
return;
}
if (MHD_HTTP_OK != hr->http_status)
{
TALER_TESTING_interpreter_next (es->is);
return;
}
if (NULL != es->exchange_url)
{
unsigned int found = GNUNET_NO;
bool found = false;
unsigned int num_exchanges = ler->details.ok.num_exchanges;
const struct TALER_AUDITOR_ExchangeInfo *ei = ler->details.ok.ei;
for (unsigned int i = 0;
i<num_exchanges;
i++)
if (0 == strcmp (es->exchange_url,
ei[i].exchange_url))
found = GNUNET_YES;
if (GNUNET_NO == found)
found = true;
if (! found)
{
TALER_LOG_ERROR ("Exchange '%s' doesn't exist at this auditor\n",
es->exchange_url);
TALER_TESTING_interpreter_fail (es->is);
return;
}
TALER_LOG_DEBUG ("Exchange '%s' exists at this auditor!\n",
es->exchange_url);
}

View File

@ -70,23 +70,19 @@ struct GetAuditorState
* Function called with information about the auditor.
*
* @param cls closure
* @param hr HTTP response data
* @param vi basic information about the auditor
* @param compat protocol compatibility information
* @param vr response data
*/
static void
version_cb (
void *cls,
const struct TALER_AUDITOR_HttpResponse *hr,
const struct TALER_AUDITOR_VersionInformation *vi,
enum TALER_AUDITOR_VersionCompatibility compat)
const struct TALER_AUDITOR_VersionResponse *vr)
{
struct GetAuditorState *gas = cls;
if (MHD_HTTP_OK != hr->http_status)
if (MHD_HTTP_OK != vr->hr.http_status)
{
TALER_TESTING_unexpected_status (gas->is,
hr->http_status);
vr->hr.http_status);
return;
}
TALER_TESTING_interpreter_next (gas->is);