auditor_api_get_config.c (7946B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/auditor_api_get_config.c 19 * @brief Implementation of /config for the auditor's HTTP API 20 * @author Sree Harsha Totakura <sreeharsha@totakura.in> 21 * @author Christian Grothoff 22 */ 23 #include <microhttpd.h> 24 #include <gnunet/gnunet_curl_lib.h> 25 #include "taler/taler_json_lib.h" 26 #include "taler/taler_auditor_service.h" 27 #include "taler/taler_signatures.h" 28 #include "auditor_api_curl_defaults.h" 29 30 31 /** 32 * Which revision of the Taler auditor protocol is implemented 33 * by this library? Used to determine compatibility. 34 */ 35 #define TALER_PROTOCOL_CURRENT 1 36 37 /** 38 * How many revisions back are we compatible to? 39 */ 40 #define TALER_PROTOCOL_AGE 0 41 42 43 /** 44 * Log error related to CURL operations. 45 * 46 * @param type log level 47 * @param function which function failed to run 48 * @param code what was the curl error code 49 */ 50 #define CURL_STRERROR(type, function, code) \ 51 GNUNET_log (type, \ 52 "Curl function `%s' has failed at `%s:%d' with error: %s", \ 53 function, __FILE__, __LINE__, curl_easy_strerror (code)); 54 55 56 /** 57 * Handle for the get config request. 58 */ 59 struct TALER_AUDITOR_GetConfigHandle 60 { 61 /** 62 * The context of this handle 63 */ 64 struct GNUNET_CURL_Context *ctx; 65 66 /** 67 * Function to call with the auditor's certification data, 68 * NULL if this has already been done. 69 */ 70 TALER_AUDITOR_ConfigCallback config_cb; 71 72 /** 73 * Closure to pass to @e config_cb. 74 */ 75 void *config_cb_cls; 76 77 /** 78 * Data for the request to get the /config of a auditor, 79 * NULL once we are past stage #MHS_INIT. 80 */ 81 struct GNUNET_CURL_Job *vr; 82 83 /** 84 * The url for the @e vr job. 85 */ 86 char *vr_url; 87 88 }; 89 90 91 /* ***************** Internal /config fetching ************* */ 92 93 /** 94 * Decode the JSON in @a resp_obj from the /config response and store the data 95 * in the @a key_data. 96 * 97 * @param[in] resp_obj JSON object to parse 98 * @param[in,out] vi where to store the results we decoded 99 * @param[out] vc where to store config compatibility data 100 * @return #TALER_EC_NONE on success 101 */ 102 static enum TALER_ErrorCode 103 decode_config_json (const json_t *resp_obj, 104 struct TALER_AUDITOR_ConfigInformation *vi, 105 enum TALER_AUDITOR_VersionCompatibility *vc) 106 { 107 struct TALER_JSON_ProtocolVersion pv; 108 const char *ver; 109 struct GNUNET_JSON_Specification spec[] = { 110 TALER_JSON_spec_version ("version", 111 &pv), 112 GNUNET_JSON_spec_string ("version", 113 &ver), 114 GNUNET_JSON_spec_fixed_auto ("exchange_master_public_key", 115 &vi->exchange_master_public_key), 116 GNUNET_JSON_spec_fixed_auto ("auditor_public_key", 117 &vi->auditor_pub), 118 GNUNET_JSON_spec_end () 119 }; 120 121 if (JSON_OBJECT != json_typeof (resp_obj)) 122 { 123 GNUNET_break_op (0); 124 return TALER_EC_GENERIC_JSON_INVALID; 125 } 126 /* check the config */ 127 if (GNUNET_OK != 128 GNUNET_JSON_parse (resp_obj, 129 spec, 130 NULL, NULL)) 131 { 132 GNUNET_break_op (0); 133 return TALER_EC_GENERIC_JSON_INVALID; 134 } 135 vi->version = ver; 136 *vc = TALER_AUDITOR_VC_MATCH; 137 if (TALER_PROTOCOL_CURRENT < pv.current) 138 { 139 *vc |= TALER_AUDITOR_VC_NEWER; 140 if (TALER_PROTOCOL_CURRENT < pv.current - pv.age) 141 *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; 142 } 143 if (TALER_PROTOCOL_CURRENT > pv.current) 144 { 145 *vc |= TALER_AUDITOR_VC_OLDER; 146 if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > pv.current) 147 *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; 148 } 149 return TALER_EC_NONE; 150 } 151 152 153 /** 154 * Callback used when downloading the reply to a /config request 155 * is complete. 156 * 157 * @param cls the `struct TALER_AUDITOR_GetConfigHandle` 158 * @param response_code HTTP response code, 0 on error 159 * @param gresp_obj parsed JSON result, NULL on error, must be a `const json_t *` 160 */ 161 static void 162 config_completed_cb (void *cls, 163 long response_code, 164 const void *gresp_obj) 165 { 166 struct TALER_AUDITOR_GetConfigHandle *auditor = cls; 167 const json_t *resp_obj = gresp_obj; 168 struct TALER_AUDITOR_ConfigResponse vr = { 169 .hr.reply = resp_obj, 170 .hr.http_status = (unsigned int) response_code 171 }; 172 173 auditor->vr = NULL; 174 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 175 "Received config from URL `%s' with status %ld.\n", 176 auditor->vr_url, 177 response_code); 178 switch (response_code) 179 { 180 case 0: 181 GNUNET_break_op (0); 182 vr.hr.ec = TALER_EC_INVALID; 183 break; 184 case MHD_HTTP_OK: 185 if (NULL == resp_obj) 186 { 187 GNUNET_break_op (0); 188 vr.hr.http_status = 0; 189 vr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 190 break; 191 } 192 vr.hr.ec = decode_config_json (resp_obj, 193 &vr.details.ok.vi, 194 &vr.details.ok.compat); 195 if (TALER_EC_NONE != vr.hr.ec) 196 { 197 GNUNET_break_op (0); 198 vr.hr.http_status = 0; 199 break; 200 } 201 break; 202 case MHD_HTTP_INTERNAL_SERVER_ERROR: 203 vr.hr.ec = TALER_JSON_get_error_code (resp_obj); 204 vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 205 break; 206 default: 207 vr.hr.ec = TALER_JSON_get_error_code (resp_obj); 208 vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 210 "Unexpected response code %u/%d\n", 211 (unsigned int) response_code, 212 (int) vr.hr.ec); 213 break; 214 } 215 auditor->config_cb (auditor->config_cb_cls, 216 &vr); 217 TALER_AUDITOR_get_config_cancel (auditor); 218 } 219 220 221 struct TALER_AUDITOR_GetConfigHandle * 222 TALER_AUDITOR_get_config (struct GNUNET_CURL_Context *ctx, 223 const char *url, 224 TALER_AUDITOR_ConfigCallback config_cb, 225 void *config_cb_cls) 226 { 227 struct TALER_AUDITOR_GetConfigHandle *auditor; 228 CURL *eh; 229 230 auditor = GNUNET_new (struct TALER_AUDITOR_GetConfigHandle); 231 auditor->config_cb = config_cb; 232 auditor->config_cb_cls = config_cb_cls; 233 auditor->ctx = ctx; 234 auditor->vr_url = TALER_url_join (url, 235 "config", 236 NULL); 237 if (NULL == auditor->vr_url) 238 { 239 GNUNET_break (0); 240 GNUNET_free (auditor); 241 return NULL; 242 } 243 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 244 "Requesting auditor config with URL `%s'.\n", 245 auditor->vr_url); 246 eh = TALER_AUDITOR_curl_easy_get_ (auditor->vr_url); 247 if (NULL == eh) 248 { 249 GNUNET_break (0); 250 TALER_AUDITOR_get_config_cancel (auditor); 251 return NULL; 252 } 253 GNUNET_break (CURLE_OK == 254 curl_easy_setopt (eh, 255 CURLOPT_TIMEOUT, 256 (long) 300)); 257 auditor->vr = GNUNET_CURL_job_add (auditor->ctx, 258 eh, 259 &config_completed_cb, 260 auditor); 261 return auditor; 262 } 263 264 265 void 266 TALER_AUDITOR_get_config_cancel (struct TALER_AUDITOR_GetConfigHandle *auditor) 267 { 268 if (NULL != auditor->vr) 269 { 270 GNUNET_CURL_job_cancel (auditor->vr); 271 auditor->vr = NULL; 272 } 273 GNUNET_free (auditor->vr_url); 274 GNUNET_free (auditor); 275 } 276 277 278 /* end of auditor_api_get_config.c */