made the service more resilient against out of order and simply incorrect messages
[oweals/gnunet.git] / src / scalarproduct / gnunet-scalarproduct.c
index 449085593f02e9d9d385622df33127749bcb7a94..32ed952685c92b84c2d0a28e0d33056be36e6417 100644 (file)
@@ -19,8 +19,8 @@
  */
 
 /**
- * @file vectorproduct/gnunet-vectorproduct.c
- * @brief vectorproduct client
+ * @file scalarproduct/gnunet-scalarproduct.c
+ * @brief scalarproduct client
  * @author Christian M. Fuchs
  */
 #define GCRYPT_NO_DEPRECATED
 
 #include "platform.h"
 #include "gnunet_util_lib.h"
-#include "gnunet_vectorproduct_service.h"
+#include "gnunet_scalarproduct_service.h"
 #include "gnunet_protocols.h"
+#include "scalarproduct.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "gnunet-scalarproduct",__VA_ARGS__)
+#define INPUTSTRINGLENGTH       1024
+
+struct ScalarProductCallbackClosure
+{
+  /**
+   * the session key identifying this computation
+   */
+  struct GNUNET_HashCode key;
+
+  /**
+   * PeerID we want to compute a scalar product with
+   */
+  struct GNUNET_PeerIdentity peer;
+};
 
-#define LOG(kind,...) GNUNET_log_from (kind, "gnunet-vectorproduct",__VA_ARGS__)
 /**
  * Option -p: destination peer identity for checking message-ids with
  */
@@ -44,174 +60,111 @@ static char *input_peer_id = NULL;
 static char *input_key = NULL;
 
 /**
- * Option -e: vector to calculate a vectorproduct with
+ * Option -e: vector to calculate a scalarproduct with
  */
 static char *input_elements = NULL;
 
 /**
- * Option -m: message-ids to calculate a vectorproduct with
+ * Option -m: message-ids to calculate a scalarproduct with
  */
 static char *input_mask = NULL;
 
-/**
- * the count of the messages sent to the service for processing
- */
-static unsigned short element_count;
-
-/**
- * the count of the mask bytes
- */
-unsigned short mask_length = 0;
-
-/**
- * the count of the number of mask bytes
- */
-unsigned short mask_bytes;
-
-/**
- * the array of converted message IDs to send to our service
- */
-static int32_t * elements = NULL;
-
-/**
- * the array of converted message IDs to send to our service
- */
-static unsigned char * mask = NULL;
-
-/**
- * information about the peer we are comparing with
- */
-struct GNUNET_PeerIdentity peer;
-
-/**
- * information about the peer we are comparing with
- */
-struct GNUNET_HashCode key;
-
-/**
- * Pointer to the GNUNET_VECTORPRODUCT_Handle
- */
-struct GNUNET_VECTORPRODUCT_Handle *handle;
-
 /**
  * Global return value
  */
-static int ret;
-
-struct GNUNET_VECTORPRODUCT_TestCls
-{
-  struct GNUNET_VECTORPRODUCT_Handle * h;
-};
-
-struct GNUNET_VECTORPRODUCT_TestCls test_cls;
+static int ret = -1;
 
 
 /**
  * Callback called if we are initiating a new computation session
- * 
+ *
  * @param cls unused
- * @param status if our job was successfully processed 
+ * @param status if our job was successfully processed
  */
 static void
 responder_callback (void *cls,
-                    const struct GNUNET_HashCode * key,
-                    enum GNUNET_VECTORPRODUCT_ResponseStatus status)
+                    enum GNUNET_SCALARPRODUCT_ResponseStatus status)
 {
-  ret = -1;
+  struct ScalarProductCallbackClosure * closure = cls;
 
   switch (status)
-    {
-    case GNUNET_VECTORPRODUCT_Status_Success:
-      ret = 0;
-      LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (key));
-      break;
-    case GNUNET_VECTORPRODUCT_Status_InvalidResponse:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (key));
-      break;
-    case GNUNET_VECTORPRODUCT_Status_Timeout:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: timeout\n", GNUNET_h2s (key));
-      break;
-    case GNUNET_VECTORPRODUCT_Status_Failure:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (key));
-    case GNUNET_VECTORPRODUCT_Status_ServiceDisconnected:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service disconnect!!\n", GNUNET_h2s (key));
-      break;
-    default:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (key), (int) status);
-    }
-
-  GNUNET_VECTORPRODUCT_disconnect (handle);
-  GNUNET_SCHEDULER_shutdown ();
+  {
+  case GNUNET_SCALARPRODUCT_Status_Success:
+    ret = 0;
+    LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (&closure->key));
+    break;
+  case GNUNET_SCALARPRODUCT_Status_InvalidResponse:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (&closure->key));
+    break;
+  case GNUNET_SCALARPRODUCT_Status_Failure:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (&closure->key));
+  case GNUNET_SCALARPRODUCT_Status_ServiceDisconnected:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service disconnect!!\n", GNUNET_h2s (&closure->key));
+    break;
+  default:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (&closure->key), status);
+  }
+  GNUNET_SCHEDULER_shutdown();
 }
 
 
 /**
  * Callback called if we are initiating a new computation session
- * 
+ *
  * @param cls unused
  * @param key unused
  * @param peer unused
- * @param status if our job was successfully processed 
+ * @param status if our job was successfully processed
  * @param size size of the msg returned
  * @param msg the response we got.
- * @param type of the message received 
+ * @param type of the message received
  */
 static void
 requester_callback (void *cls,
-        const struct GNUNET_HashCode * key,
-        const struct GNUNET_PeerIdentity * peer,
-        enum GNUNET_VECTORPRODUCT_ResponseStatus status,
-        const struct GNUNET_VECTORPRODUCT_client_response *msg)
+                    enum GNUNET_SCALARPRODUCT_ResponseStatus status,
+                    gcry_mpi_t result)
 {
-  uint32_t product_len;
-  ret = -1;
+  struct ScalarProductCallbackClosure * closure = cls;
+  unsigned char * buf;
+  gcry_error_t rc;
 
   switch (status)
+  {
+  case GNUNET_SCALARPRODUCT_Status_Success:
+    if (0 == (rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result)))
     {
-    case GNUNET_VECTORPRODUCT_Status_Success:
-      product_len = ntohl (msg->product_length);
-
-      LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (key));
-
-      if (0 < product_len && NULL != &msg[1])
-        {
-          gcry_mpi_t result;
-          size_t read = 0;
-
-          if (0 != gcry_mpi_scan (&result, GCRYMPI_FMT_USG, &msg[1], product_len, &read))
-            LOG (GNUNET_ERROR_TYPE_ERROR, "Could not convert to mpi to value!\n");
-          else
-            {
-              unsigned char * buf;
-              gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result);
-
-              printf ("Successfully computed result for session %s: %s\n", GNUNET_h2s (key), buf);
-              ret = 0;
-            }
-        }
-      else
-        { //currently not used, but if we get more info due to MESH we will need this
-          LOG (GNUNET_ERROR_TYPE_ERROR, "Service-side error in session %s, return code: %d\n", GNUNET_h2s (key), product_len);
-        }
-      break;
-    case GNUNET_VECTORPRODUCT_Status_InvalidResponse:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (key));
-      break;
-    case GNUNET_VECTORPRODUCT_Status_Timeout:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: timeout\n", GNUNET_h2s (key));
-      break;
-    case GNUNET_VECTORPRODUCT_Status_Failure:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (key));
-    case GNUNET_VECTORPRODUCT_Status_ServiceDisconnected:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Disconnected from service.\n", GNUNET_h2s (key));
-      break;
-    default:
-      LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (key), (int) status);
+      ret = 0;
+      printf ("%s", buf);
     }
-  GNUNET_VECTORPRODUCT_disconnect (handle);
-  GNUNET_SCHEDULER_shutdown ();
+    else
+      LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_aprint", rc);
+    break;
+  case GNUNET_SCALARPRODUCT_Status_InvalidResponse:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s failed: invalid response received\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer));
+    break;
+  case GNUNET_SCALARPRODUCT_Status_Failure:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s failed: API failure\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer));
+  case GNUNET_SCALARPRODUCT_Status_ServiceDisconnected:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s was disconnected from service.\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer));
+    break;
+  default:
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s failed: return code %d\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer), status);
+  }
+  GNUNET_SCHEDULER_shutdown();
 }
 
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ * @param tc unused
+ */
+static void
+shutdown_task (void *cls,
+               const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  GNUNET_SCALARPRODUCT_disconnect ();
+}
 
 /**
  * Main function that will be run by the scheduler.
@@ -231,151 +184,157 @@ run (void *cls,
   char * end;
   int32_t element;
   int i;
-  ret = -1;
+  int32_t * elements;
+  unsigned char * mask;
+  uint32_t mask_bytes;
+  uint32_t element_count = 0;
+  struct ScalarProductCallbackClosure * closure;
 
   if (NULL == input_elements)
-    {
-      FPRINTF (stderr, "%s", _ ("You must specify at least one message ID to check!\n"));
-      return;
-    }
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, _ ("You must specify at least one message ID to check!\n"));
+    return;
+  }
 
   if (NULL == input_key)
-    {
-      FPRINTF (stderr, "%s", _ ("This program needs a session identifier for comparing vectors.\n"));
-      return;
-    }
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, _ ("This program needs a session identifier for comparing vectors.\n"));
+    return;
+  }
 
   if (1 > strnlen (input_key, sizeof (struct GNUNET_HashCode)))
-    {
-      FPRINTF (stderr, _ ("Please give a session key for --input_key!\n"));
-      return;
-    }
-  GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key);
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Please give a session key for --input_key!\n"));
+    return;
+  }
+  closure = GNUNET_new (struct ScalarProductCallbackClosure);
+  GNUNET_CRYPTO_hash (input_key, strlen (input_key), &closure->key);
 
   if (input_peer_id && GNUNET_OK != GNUNET_CRYPTO_hash_from_string (input_peer_id,
-                                                                    (struct GNUNET_HashCode *) &peer))
+                                                                    (struct GNUNET_HashCode *) &closure->peer))
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Tried to set initiator mode, as peer ID was given. "
+                                     "However, `%s' is not a valid peer identifier.\n"),
+         input_peer_id);
+    return;
+  }
+
+  /* Count input_elements_peer1, and put in elements_peer1 array */
+  do
+  {
+    // get the length of the current element and replace , with null
+    for (end = begin; *end && *end != ','; end++);
+
+    if (1 == sscanf (begin, "%" SCNd32 ",", &element))
+    {
+      //element in the middle
+      element_count++;
+      begin = end + 1;
+    }
+    else if (0 == *begin)
+    {
+      break;
+    }
+    else
     {
-      FPRINTF (stderr, _ ("Tried to set initiator mode, as peer ID was given. "
-                          "However, `%s' is not a valid peer identifier.\n"),
-               input_peer_id);
+      LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Could not convert `%s' to int32_t.\n"), begin);
       return;
     }
+  }
+  while (1);
+  if (0 == element_count)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Need elements to compute the vectorproduct, got none.\n"));
+    return;
+  }
 
-  int exit_loop = 0;
+  begin = input_elements;
+  elements = GNUNET_malloc (sizeof (int32_t) * element_count);
+  element_count = 0;
   /* Read input_elements_peer1, and put in elements_peer1 array */
   do
-    {
-      unsigned int mcount = element_count;
-      //ignore empty rows of ,,,,,,
-      while (*begin == ',')
-        begin++;
-      // get the length of the current element and replace , with null
-      for (end = begin; *end && *end != ','; end++);
-
-      if (*end == '\0')
-        exit_loop = 1;
-
-      if (*end == ',')
-        *end = '\0';
+  {
+    // get the length of the current element and replace , with null
+    for (end = begin; *end && *end != ','; end++);
 
-      if (1 != sscanf (begin, "%" SCNd32, &element))
-        {
-          FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
-          return;
-        }
-
-      GNUNET_array_append (elements, mcount, element);
+    if (1 == sscanf (begin, "%" SCNd32 ",", &elements[element_count]))
+    {
+      //element in the middle
       element_count++;
-
-      begin = ++end;
+      begin = end + 1;
+    }
+    else if (0 == *begin)
+    {
+      break;
     }
-  while (!exit_loop);
+  }
+  while (1);
 
-  GNUNET_assert (elements != NULL);
-  GNUNET_assert (element_count > 1);
-  mask_length = element_count / 8 + (element_count % 8 ? 1 : 0);
-  mask = GNUNET_malloc ((element_count / 8) + 2);
+  mask_bytes = element_count / 8 + (element_count % 8 ? 1 : 0);
+  mask = GNUNET_malloc ((element_count / 8) + 1);
 
   /* Read input_mask_peer1 and read in mask_peer1 array */
-  if (NULL != input_mask)
-    {
-      begin = input_mask;
-      unsigned short mask_count = 0;
-      int exit_loop = 0;
-
-      do
-        {
-          //ignore empty rows of ,,,,,,
-          while (* begin == ',')
-            begin++;
-          // get the length of the current element and replace , with null
-          // gnunet_ascii-armor uses base32, thus we can use , as separator!
-          for (end = begin; *end && *end != ','; end++);
-
-          if (*end == '\0')
-            exit_loop = 1;
-
-          if (*end == ',')
-            *end = '\0';
-
-          if (1 != sscanf (begin, "%" SCNd32, &element))
-            {
-              FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
-              return;
-            }
-
-          GNUNET_assert (mask_count <= element_count);
-
-          if (element)
-            mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8);
-
-          mask_count++;
-          begin = ++end;
-        }
-      while (!exit_loop);
-      // +1 to see if we would have more data, which would indicate malformed/superficial input
-      GNUNET_assert (mask_count == element_count);
-    }
-  else if (input_peer_id)
-    {
-      for (i = 0; i <= mask_length; i++)
-        mask[i] = UCHAR_MAX; // all 1's
-    }
+  if ((NULL != input_peer_id) && (NULL != input_mask))
+  {
+    begin = input_mask;
+    unsigned short mask_count = 0;
 
-  handle = GNUNET_VECTORPRODUCT_connect (cfg);
-  if (handle == NULL)
+    do
     {
-      FPRINTF (stderr, _ ("Could not connect to the GNUNET Vector Product Service\n"));
-      return;
-    }
+      // get the length of the current element and replace , with null
+      for (end = begin; *end && *end != ','; end++);
 
-  test_cls.h = handle;
-
-  if (input_peer_id && !GNUNET_VECTORPRODUCT_request (handle,
-                                                      &key,
-                                                      &peer,
-                                                      element_count,
-                                                      mask_length,
-                                                      elements, mask,
-                                                      GNUNET_TIME_UNIT_MINUTES,
-                                                      &requester_callback,
-                                                      (void *) &test_cls))
+      if (1 == sscanf (begin, "%" SCNd32 ",", &element))
+      {
+        //element in the middle
+        begin = end + 1;
+      }
+      else if (*begin == 0)
+      {
+        break;
+      }
+      else
+      {
+        LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Could not convert `%s' to int32_t.\n"), begin);
+        return;
+      }
+
+      if (element)
+        mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8);
+      mask_count++;
+    }
+    while (mask_count < element_count);
+  }
+  else if (NULL != input_peer_id)
+    for (i = 0; i <= mask_bytes; i++)
+      mask[i] = UCHAR_MAX; // all 1's
+
+  if (input_peer_id && (NULL == GNUNET_SCALARPRODUCT_request (cfg,
+                                                              &closure->key,
+                                                              &closure->peer,
+                                                              elements, element_count,
+                                                              mask, mask_bytes,
+                                                              &requester_callback,
+                                                              (void *) &closure)))
     return;
-  if ( !input_peer_id && !GNUNET_VECTORPRODUCT_prepare_response (handle,
-                                                   &key,
-                                                   element_count,
-                                                   elements,
-                                                   GNUNET_TIME_UNIT_MINUTES,
-                                                   &responder_callback,
-                                                   (void *) &test_cls))
+
+  if ((NULL == input_peer_id) && (NULL == GNUNET_SCALARPRODUCT_response (cfg,
+                                                                         &closure->key,
+                                                                         elements, element_count,
+                                                                         &responder_callback,
+                                                                         (void *) &closure)))
     return;
 
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+                                &shutdown_task,
+                                NULL);
+
   ret = 0;
 }
 
 
 /**
- * The main function to the vectorproduct client.
+ * The main function to the scalarproduct client.
  *
  * @param argc number of arguments from the command line
  * @param argv command line arguments
@@ -392,7 +351,7 @@ main (int argc, char *const *argv)
       gettext_noop ("A comma separated mask to select which elements should actually be compared."),
       1, &GNUNET_GETOPT_set_string, &input_mask},
     {'p', "peer", "PEERID",
-      gettext_noop ("[Optional] peer to calculate our vectorproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."),
+      gettext_noop ("[Optional] peer to calculate our scalarproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."),
       1, &GNUNET_GETOPT_set_string, &input_peer_id},
     {'k', "key", "TRANSACTION_ID",
       gettext_noop ("Transaction ID shared with peer."),
@@ -403,7 +362,7 @@ main (int argc, char *const *argv)
   return (GNUNET_OK ==
           GNUNET_PROGRAM_run (argc,
                               argv,
-                              "gnunet-vectorproduct",
+                              "gnunet-scalarproduct",
                               gettext_noop ("Calculate the Vectorproduct with a GNUnet peer."),
                               options, &run, NULL)) ? ret : 1;
 }