diff options
Diffstat (limited to 'src/exchange-lib')
| -rw-r--r-- | src/exchange-lib/exchange_api_wire.c | 319 | ||||
| -rw-r--r-- | src/exchange-lib/test_exchange_api.c | 96 | 
2 files changed, 81 insertions, 334 deletions
diff --git a/src/exchange-lib/exchange_api_wire.c b/src/exchange-lib/exchange_api_wire.c index 27ae1dce..0e93a9d2 100644 --- a/src/exchange-lib/exchange_api_wire.c +++ b/src/exchange-lib/exchange_api_wire.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014, 2015 GNUnet e.V. +  Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.    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 @@ -129,257 +129,6 @@ verify_wire_method_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh,  /** - * Perform the next /wire/method request or signal - * the end of the iteration. - * - * @param wh the wire handle - * @return a handle for this request - */ -static void -request_wire_method (struct TALER_EXCHANGE_WireHandle *wh); - - -/** - * Function called when we're done processing the - * HTTP /wire/METHOD request. - * - * @param cls the `struct TALER_EXCHANGE_WireHandle` - * @param eh the curl request handle - */ -static void -handle_wire_method_finished (void *cls, -                             CURL *eh) -{ -  struct TALER_EXCHANGE_WireHandle *wh = cls; -  long response_code; -  json_t *json; - -  wh->job = NULL; -  json = MAC_download_get_result (&wh->db, -                                  eh, -                                  &response_code); -  switch (response_code) -  { -  case 0: -    break; -  case MHD_HTTP_OK: -    { -      const char *method; - -      method = json_string_value (json_array_get (wh->methods, -                                                  wh->methods_off - 1)); -      if (GNUNET_OK != -          verify_wire_method_signature_ok (wh, -                                           method, -                                           json)) -      { -        GNUNET_break_op (0); -        response_code = 0; -        break; -      } -      break; -    } -  case MHD_HTTP_FOUND: -    /* /wire/test returns a 302 redirect, we should just give -       this information back to the callback below */ -    break; -  case MHD_HTTP_BAD_REQUEST: -    /* This should never happen, either us or the exchange is buggy -       (or API version conflict); just pass JSON reply to the application */ -    break; -  case MHD_HTTP_NOT_FOUND: -    /* Nothing really to verify, this should never -       happen, we should pass the JSON reply to the application */ -    break; -  case MHD_HTTP_INTERNAL_SERVER_ERROR: -    /* Server had an internal issue; we should retry, but this API -       leaves this to the application */ -    break; -  default: -    /* unexpected response code */ -    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                "Unexpected response code %u\n", -                response_code); -    GNUNET_break (0); -    response_code = 0; -    break; -  } -  if (0 == response_code) -  { -    /* signal end of iteration */ -    wh->cb (wh->cb_cls, -            0, -            NULL, -            NULL); -    json_decref (json); -    TALER_EXCHANGE_wire_cancel (wh); -    return; -  } -  /* pass on successful reply */ -  wh->cb (wh->cb_cls, -          response_code, -          json_string_value (json_array_get (wh->methods, -                                             wh->methods_off-1)), -          json); -  json_decref (json); -  /* trigger request for the next /wire/method */ -  request_wire_method (wh); -} - - -/** - * Perform the next /wire/method request or signal - * the end of the iteration. - * - * @param wh the wire handle - * @return a handle for this request - */ -static void -request_wire_method (struct TALER_EXCHANGE_WireHandle *wh) -{ -  struct TALER_EXCHANGE_Context *ctx; -  CURL *eh; -  char *path; - -  if (json_array_size (wh->methods) <= wh->methods_off) -  { -    /* we are done, signal end of iteration */ -    wh->cb (wh->cb_cls, -            0, -            NULL, -            NULL); -    TALER_EXCHANGE_wire_cancel (wh); -    return; -  } -  GNUNET_free_non_null (wh->db.buf); -  wh->db.buf = NULL; -  wh->db.buf_size = 0; -  wh->db.eno = 0; -  GNUNET_free_non_null (wh->url); -  GNUNET_asprintf (&path, -                   "/wire/%s", -                   json_string_value (json_array_get (wh->methods, -                                                      wh->methods_off++))); -  wh->url = MAH_path_to_url (wh->exchange, -                             path); -  GNUNET_free (path); - -  eh = curl_easy_init (); -  GNUNET_assert (CURLE_OK == -                 curl_easy_setopt (eh, -                                   CURLOPT_URL, -                                   wh->url)); -  GNUNET_assert (CURLE_OK == -                 curl_easy_setopt (eh, -                                   CURLOPT_WRITEFUNCTION, -                                   &MAC_download_cb)); -  GNUNET_assert (CURLE_OK == -                 curl_easy_setopt (eh, -                                   CURLOPT_WRITEDATA, -                                   &wh->db)); -  /* The default is 'disabled', but let's be sure */ -  GNUNET_assert (CURLE_OK == -                 curl_easy_setopt (eh, -                                   CURLOPT_FOLLOWLOCATION, -                                   (long) 0)); -  ctx = MAH_handle_to_context (wh->exchange); -  wh->job = MAC_job_add (ctx, -                         eh, -                         GNUNET_YES, -                         &handle_wire_method_finished, -                         wh); -  TALER_EXCHANGE_perform (ctx); -} - - -/** - * Verify that the signature on the "200 OK" response - * for /wire from the exchange is valid. - * - * @param wh wire handle - * @param json json reply with the signature - * @return NULL if @a json is invalid, otherwise the - *         "methods" array (with an RC of 1) - */ -static json_t * -verify_wire_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh, -                          json_t *json) -{ -  struct TALER_ExchangeSignatureP exchange_sig; -  struct TALER_ExchangePublicKeyP exchange_pub; -  struct TALER_ExchangeWireSupportMethodsPS mp; -  json_t *methods; -  const struct TALER_EXCHANGE_Keys *key_state; -  struct GNUNET_HashContext *hc; -  struct GNUNET_JSON_Specification spec[] = { -    GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig), -    GNUNET_JSON_spec_fixed_auto ("pub", &exchange_pub), -    GNUNET_JSON_spec_json ("methods", &methods), -    GNUNET_JSON_spec_end() -  }; -  unsigned int i; - -  if (GNUNET_OK != -      GNUNET_JSON_parse (json, -                         spec, -                         NULL, NULL)) -  { -    GNUNET_break_op (0); -    return NULL; -  } -  if (! json_is_array (methods)) -  { -    GNUNET_break_op (0); -    GNUNET_JSON_parse_free (spec); -    return NULL; -  } - -  key_state = TALER_EXCHANGE_get_keys (wh->exchange); -  if (GNUNET_OK != -      TALER_EXCHANGE_test_signing_key (key_state, -                                   &exchange_pub)) -  { -    GNUNET_break_op (0); -    return NULL; -  } -  hc = GNUNET_CRYPTO_hash_context_start (); -  for (i=0;i<json_array_size (methods);i++) -  { -    const json_t *element = json_array_get (methods, i); -    const char *method; - -    if (! json_is_string (element)) -    { -      GNUNET_CRYPTO_hash_context_abort (hc); -      GNUNET_break_op (0); -      GNUNET_JSON_parse_free (spec); -      return NULL; -    } -    method = json_string_value (element); -    GNUNET_CRYPTO_hash_context_read (hc, -                                     method, -                                     strlen (method) + 1); -  } -  mp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_WIRE_TYPES); -  mp.purpose.size = htonl (sizeof (struct TALER_ExchangeWireSupportMethodsPS)); -  GNUNET_CRYPTO_hash_context_finish (hc, -                                     &mp.h_wire_types); - -  if (GNUNET_OK != -      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_WIRE_TYPES, -                                  &mp.purpose, -                                  &exchange_sig.eddsa_signature, -                                  &exchange_pub.eddsa_pub)) -  { -    GNUNET_break_op (0); -    GNUNET_JSON_parse_free (spec); -    return NULL; -  } -  return methods; -} - - -/**   * Function called when we're done processing the   * HTTP /wire request.   * @@ -404,19 +153,47 @@ handle_wire_finished (void *cls,      break;    case MHD_HTTP_OK:      { -      json_t *methods; - -      if (NULL == -          (methods = verify_wire_signature_ok (wh, -                                               json))) +      const char *key; +      json_t *method; +      json_t *keep; +      int ret; + +      /* We 'keep' methods that we support and that are well-formed; +         we fail (by setting response_code=0) if any method that we do +         support fails to verify. */ +      keep = json_object (); +      json_object_foreach (json, key, method) { +        ret = verify_wire_method_signature_ok (wh, +                                               key, +                                               method); +        if (GNUNET_SYSERR == ret) +        { +          /* bogus reply */ +          GNUNET_break_op (0); +          response_code = 0; +        } +        /* GNUNET_NO: not understood by us, simply skip! */ +        if (GNUNET_OK == ret) +        { +          /* supported and valid, keep! */ +          json_object_set (keep, +                           key, +                           method); +        } +      } +      if (0 != response_code)        { -        GNUNET_break_op (0); -        response_code = 0; +        /* all supported methods were valid, use 'keep' for 'json' */ +        json_decref (json); +        json = keep;          break;        } -      wh->methods = methods; -      request_wire_method (wh); -      return; +      else +      { +        /* some supported methods were invalid, release 'keep', preserve +           full 'json' for application-level error handling. */ +        json_decref (keep); +      }      }      break;    case MHD_HTTP_BAD_REQUEST: @@ -440,19 +217,9 @@ handle_wire_finished (void *cls,      response_code = 0;      break;    } -  if (0 != response_code) -  { -    /* pass on successful reply */ -    wh->cb (wh->cb_cls, -            response_code, -            NULL, -            json); -  } -  /* signal end of iteration */    wh->cb (wh->cb_cls, -          0, -          NULL, -          NULL); +          response_code, +          json);    if (NULL != json)      json_decref (json);    TALER_EXCHANGE_wire_cancel (wh); @@ -481,8 +248,8 @@ handle_wire_finished (void *cls,   */  struct TALER_EXCHANGE_WireHandle *  TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange, -                 TALER_EXCHANGE_WireResultCallback wire_cb, -                 void *wire_cb_cls) +                     TALER_EXCHANGE_WireResultCallback wire_cb, +                     void *wire_cb_cls)  {    struct TALER_EXCHANGE_WireHandle *wh;    struct TALER_EXCHANGE_Context *ctx; diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 2a7f670c..6eae08f4 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014, 2015, 2016 GNUnet e.V. +  Copyright (C) 2014, 2015, 2016 GNUnet e.V. and Inria    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 @@ -1213,76 +1213,56 @@ find_pk (const struct TALER_EXCHANGE_Keys *keys,   * Callbacks called with the result(s) of a   * wire format inquiry request to the exchange.   * - * The callback is invoked multiple times, once for each supported @a - * method.  Finally, it is invoked one more time with cls/0/NULL/NULL - * to indicate the end of the iteration.  If any request fails to - * generate a valid response from the exchange, @a http_status will also - * be zero and the iteration will also end.  Thus, the iteration - * always ends with a final call with an @a http_status of 0. If the - * @a http_status is already 0 on the first call, then the response to - * the /wire request was invalid.  Later, clients can tell the - * difference between @a http_status of 0 indicating a failed - * /wire/method request and a regular end of the iteration by @a - * method being non-NULL.  If the exchange simply correctly asserts that - * it does not support any methods, @a method will be NULL but the @a - * http_status will be #MHD_HTTP_OK for the first call (followed by a - * cls/0/NULL/NULL call to signal the end of the iteration). - *   * @param cls closure with the interpreter state   * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request;   *                    0 if the exchange's reply is bogus (fails to follow the protocol) - * @param method wire format method supported, i.e. "test" or "sepa", or NULL - *            if already the /wire request failed.   * @param obj the received JSON reply, if successful this should be the wire - *            format details as provided by /wire/METHOD/, or NULL if the - *            reply was not in JSON format (in this case, the client might - *            want to do an HTTP request to /wire/METHOD/ with a browser to - *            provide more information to the user about the @a method). + *            format details as provided by /wire.   */  static void  wire_cb (void *cls,           unsigned int http_status, -         const char *method,           json_t *obj)  {    struct InterpreterState *is = cls;    struct Command *cmd = &is->commands[is->ip]; -  if (0 == http_status) -  { -    /* 0 always signals the end of the iteration */ -    cmd->details.wire.wh = NULL; -  } -  else if ( (NULL != method) && -            (0 != strcasecmp (method, -                              cmd->details.wire.format)) ) -  { -    /* not the method we care about, skip */ -    return; -  }    if (cmd->expected_response_code != http_status)    {      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                "Unexpected response code %u to command %s/%s\n", +                "Unexpected response code %u to command %s\n",                  http_status, -                cmd->label, -                method); +                cmd->label);      json_dumpf (obj, stderr, 0);      fail (is);      return;    } -  if (0 == http_status) +  switch (http_status)    { -    /* end of iteration, move to next command */ -    is->ip++; -    is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, -                                         is); -    return; +  case MHD_HTTP_OK: +    { +      json_t *method; + +      method = json_object_get (obj, +                                cmd->details.wire.format); +      if (NULL == method) +      { +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    "Expected method `%s' not included in response to command %s\n", +                    cmd->details.wire.format, +                    cmd->label); +        json_dumpf (obj, stderr, 0); +        fail (is); +        return; +      } +    } +    break; +  default: +    break;    } -  /* For now, we only support to be called only once -     with a "positive" result; so we switch to an -     expected value of 0 for the 2nd iteration */ -  cmd->expected_response_code = 0; +  is->ip++; +  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, +                                       is);  } @@ -1899,9 +1879,9 @@ interpreter_run (void *cls,      /* finally, use private key from withdraw sign command */      cmd->details.refresh_link.rlh        = TALER_EXCHANGE_refresh_link (exchange, -                                 &ref->details.reserve_withdraw.coin_priv, -                                 &link_cb, -                                 is); +                                     &ref->details.reserve_withdraw.coin_priv, +                                     &link_cb, +                                     is);      if (NULL == cmd->details.refresh_link.rlh)      {        GNUNET_break (0); @@ -1912,8 +1892,8 @@ interpreter_run (void *cls,      return;    case OC_WIRE:      cmd->details.wire.wh = TALER_EXCHANGE_wire (exchange, -                                            &wire_cb, -                                            is); +                                                &wire_cb, +                                                is);      trigger_context_task ();      return;    case OC_WIRE_DEPOSITS: @@ -1926,9 +1906,9 @@ interpreter_run (void *cls,      }      cmd->details.wire_deposits.wdh        = TALER_EXCHANGE_wire_deposits (exchange, -                                  &cmd->details.wire_deposits.wtid, -                                  &wire_deposits_cb, -                                  is); +                                      &cmd->details.wire_deposits.wtid, +                                      &wire_deposits_cb, +                                      is);      trigger_context_task ();      return;    case OC_DEPOSIT_WTID: @@ -2337,14 +2317,14 @@ run (void *cls,  #if WIRE_TEST      { .oc = OC_WIRE,        .label = "wire-test", -      /* /wire/test replies with a 200 OK */ +      /* expecting 'test' method in response */        .expected_response_code = MHD_HTTP_OK,        .details.wire.format = "test" },  #endif  #if WIRE_SEPA      { .oc = OC_WIRE,        .label = "wire-sepa", -      /* /wire/sepa replies with a 200 redirect */ +      /* expecting 'sepa' method in response */        .expected_response_code = MHD_HTTP_OK,        .details.wire.format = "sepa" },  #endif  | 
