- converted peeridentity from hashcode to eddsa
[oweals/gnunet.git] / src / scalarproduct / gnunet-scalarproduct.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file scalarproduct/gnunet-scalarproduct.c
23  * @brief scalarproduct client
24  * @author Christian M. Fuchs
25  */
26 #define GCRYPT_NO_DEPRECATED
27 #include <gcrypt.h>
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_scalarproduct_service.h"
33 #include "gnunet_protocols.h"
34 #include "scalarproduct.h"
35
36 #define LOG(kind,...) GNUNET_log_from (kind, "gnunet-scalarproduct",__VA_ARGS__)
37 #define INPUTSTRINGLENGTH       1024
38
39 /**
40  * A primitive closure structure holding information about our session
41  */
42 struct ScalarProductCallbackClosure
43 {
44   /**
45    * the session key identifying this computation
46    */
47   struct GNUNET_HashCode key;
48
49   /**
50    * PeerID we want to compute a scalar product with
51    */
52   struct GNUNET_PeerIdentity peer;
53 };
54
55 /**
56  * Option -p: destination peer identity for checking message-ids with
57  */
58 static char *input_peer_id;
59
60 /**
61  * Option -p: destination peer identity for checking message-ids with
62  */
63 static char *input_key;
64
65 /**
66  * Option -e: vector to calculate a scalarproduct with
67  */
68 static char *input_elements;
69
70 /**
71  * Option -m: message-ids to calculate a scalarproduct with
72  */
73 static char *input_mask;
74
75 /**
76  * Global return value
77  */
78 static int ret = -1;
79
80
81 /**
82  * Callback called if we are initiating a new computation session
83  *
84  * @param cls unused
85  * @param status if our job was successfully processed
86  */
87 static void
88 responder_callback (void *cls,
89                     enum GNUNET_SCALARPRODUCT_ResponseStatus status)
90 {
91   struct ScalarProductCallbackClosure * closure = cls;
92
93   switch (status)
94   {
95   case GNUNET_SCALARPRODUCT_Status_Success:
96     ret = 0;
97     LOG (GNUNET_ERROR_TYPE_INFO,
98          "Session %s concluded.\n",
99          GNUNET_h2s (&closure->key));
100     break;
101   case GNUNET_SCALARPRODUCT_Status_InvalidResponse:
102     LOG (GNUNET_ERROR_TYPE_ERROR,
103          "Session %s failed: invalid response\n",
104          GNUNET_h2s (&closure->key));
105     break;
106   case GNUNET_SCALARPRODUCT_Status_Failure:
107     LOG (GNUNET_ERROR_TYPE_ERROR,
108          "Session %s failed: service failure\n",
109          GNUNET_h2s (&closure->key));
110     break;
111   case GNUNET_SCALARPRODUCT_Status_ServiceDisconnected:
112     LOG (GNUNET_ERROR_TYPE_ERROR,
113          "Session %s failed: service disconnect!\n",
114          GNUNET_h2s (&closure->key));
115     break;
116   default:
117     LOG (GNUNET_ERROR_TYPE_ERROR,
118          "Session %s failed: return code %d\n",
119          GNUNET_h2s (&closure->key),
120          status);
121   }
122   GNUNET_SCHEDULER_shutdown();
123 }
124
125
126 /**
127  * Callback called if we are initiating a new computation session
128  *
129  * @param cls unused
130  * @param status if our job was successfully processed
131  * @param result the result in gnu/gcry MPI format
132  */
133 static void
134 requester_callback (void *cls,
135                     enum GNUNET_SCALARPRODUCT_ResponseStatus status,
136                     gcry_mpi_t result)
137 {
138   struct ScalarProductCallbackClosure * closure = cls;
139   unsigned char * buf;
140   gcry_error_t rc;
141
142   switch (status)
143   {
144   case GNUNET_SCALARPRODUCT_Status_Success:
145     if (0 == (rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result)))
146     {
147       ret = 0;
148       printf ("%s", buf);
149     }
150     else
151       LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
152                 "gcry_mpi_aprint",
153                 rc);
154     break;
155   case GNUNET_SCALARPRODUCT_Status_InvalidResponse:
156     LOG (GNUNET_ERROR_TYPE_ERROR,
157          "Session %s with peer %s failed: invalid response received\n",
158          GNUNET_h2s (&closure->key),
159          GNUNET_i2s (&closure->peer));
160     break;
161   case GNUNET_SCALARPRODUCT_Status_Failure:
162     LOG (GNUNET_ERROR_TYPE_ERROR,
163          "Session %s with peer %s failed: API failure\n",
164          GNUNET_h2s (&closure->key),
165          GNUNET_i2s (&closure->peer));
166     break;
167   case GNUNET_SCALARPRODUCT_Status_ServiceDisconnected:
168     LOG (GNUNET_ERROR_TYPE_ERROR,
169          "Session %s with peer %s was disconnected from service.\n",
170          GNUNET_h2s (&closure->key),
171          GNUNET_i2s (&closure->peer));
172     break;
173   default:
174     LOG (GNUNET_ERROR_TYPE_ERROR,
175          "Session %s with peer %s failed: return code %d\n",
176          GNUNET_h2s (&closure->key),
177          GNUNET_i2s (&closure->peer),
178          status);
179   }
180   GNUNET_SCHEDULER_shutdown();
181 }
182
183
184 /**
185  * Task run during shutdown.
186  *
187  * @param cls unused
188  * @param tc unused
189  */
190 static void
191 shutdown_task (void *cls,
192                const struct GNUNET_SCHEDULER_TaskContext *tc)
193 {
194   GNUNET_SCALARPRODUCT_disconnect ();
195 }
196
197
198 /**
199  * Main function that will be run by the scheduler.
200  *
201  * @param cls closure
202  * @param args remaining command-line arguments
203  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
204  * @param cfg configuration
205  */
206 static void
207 run (void *cls,
208      char *const *args,
209      const char *cfgfile,
210      const struct GNUNET_CONFIGURATION_Handle *cfg)
211 {
212   char *begin = input_elements;
213   char *end;
214   int32_t element;
215   int i;
216   int32_t *elements;
217   unsigned char *mask;
218   uint32_t mask_bytes;
219   uint32_t element_count = 0;
220   struct ScalarProductCallbackClosure * closure;
221
222   if (NULL == input_elements)
223   {
224     LOG (GNUNET_ERROR_TYPE_ERROR,
225          _ ("You must specify at least one message ID to check!\n"));
226     return;
227   }
228
229   if (NULL == input_key)
230   {
231     LOG (GNUNET_ERROR_TYPE_ERROR,
232          _ ("This program needs a session identifier for comparing vectors.\n"));
233     return;
234   }
235
236   if (1 > strnlen (input_key, sizeof (struct GNUNET_HashCode)))
237   {
238     LOG (GNUNET_ERROR_TYPE_ERROR,
239          _ ("Please give a session key for --input_key!\n"));
240     return;
241   }
242   closure = GNUNET_new (struct ScalarProductCallbackClosure);
243   GNUNET_CRYPTO_hash (input_key, strlen (input_key), &closure->key);
244
245   if (input_peer_id &&
246       (GNUNET_OK !=
247        GNUNET_CRYPTO_eddsa_public_key_from_string (input_peer_id,
248                                        (struct GNUNET_CRYPTO_EddsaPublicKey *) &closure->peer)))
249   {
250     LOG (GNUNET_ERROR_TYPE_ERROR,
251          _ ("Tried to set initiator mode, as peer ID was given. "
252             "However, `%s' is not a valid peer identifier.\n"),
253          input_peer_id);
254     return;
255   }
256
257   /* Count input_elements_peer1, and put in elements_peer1 array */
258   do
259   {
260     // get the length of the current element and replace , with null
261     for (end = begin; *end && *end != ','; end++);
262
263     if (1 == sscanf (begin, "%" SCNd32 ",", &element))
264     {
265       //element in the middle
266       element_count++;
267       begin = end + 1;
268     }
269     else if (0 == *begin)
270     {
271       break;
272     }
273     else
274     {
275       LOG (GNUNET_ERROR_TYPE_ERROR,
276            _ ("Could not convert `%s' to int32_t.\n"), begin);
277       return;
278     }
279   }
280   while (1);
281   if (0 == element_count)
282   {
283     LOG (GNUNET_ERROR_TYPE_ERROR,
284          _ ("Need elements to compute the vectorproduct, got none.\n"));
285     return;
286   }
287
288   begin = input_elements;
289   elements = GNUNET_malloc (sizeof (int32_t) * element_count);
290   element_count = 0;
291   /* Read input_elements_peer1, and put in elements_peer1 array */
292   do
293   {
294     // get the length of the current element and replace , with null
295     for (end = begin; *end && *end != ','; end++);
296
297     if (1 == sscanf (begin, "%" SCNd32 ",", &elements[element_count]))
298     {
299       //element in the middle
300       element_count++;
301       begin = end + 1;
302     }
303     else if (0 == *begin)
304     {
305       break;
306     }
307   }
308   while (1);
309
310   mask_bytes = element_count / 8 + ( (element_count % 8) ? 1 : 0);
311   mask = GNUNET_malloc ((element_count / 8) + 1);
312
313   /* Read input_mask_peer1 and read in mask_peer1 array */
314   if ((NULL != input_peer_id) && (NULL != input_mask))
315   {
316     begin = input_mask;
317     unsigned short mask_count = 0;
318
319     do
320     {
321       // get the length of the current element and replace , with null
322       for (end = begin; *end && *end != ','; end++);
323
324       if (1 == sscanf (begin, "%" SCNd32 ",", &element))
325       {
326         //element in the middle
327         begin = end + 1;
328       }
329       else if (*begin == 0)
330       {
331         break;
332       }
333       else
334       {
335         LOG (GNUNET_ERROR_TYPE_ERROR,
336              _ ("Could not convert `%s' to integer.\n"), begin);
337         return;
338       }
339
340       if (element)
341         mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8);
342       mask_count++;
343     }
344     while (mask_count < element_count);
345   }
346   else if (NULL != input_peer_id)
347     for (i = 0; i <= mask_bytes; i++)
348       mask[i] = UCHAR_MAX; // all 1's
349
350   if (input_peer_id &&
351       (NULL == GNUNET_SCALARPRODUCT_request (cfg,
352                                              &closure->key,
353                                              &closure->peer,
354                                              elements, element_count,
355                                              mask, mask_bytes,
356                                              &requester_callback,
357                                              (void *) &closure)))
358   {
359     GNUNET_free (elements);
360     GNUNET_free (mask);
361     return;
362   }
363
364   if ((NULL == input_peer_id) &&
365       (NULL == GNUNET_SCALARPRODUCT_response (cfg,
366                                               &closure->key,
367                                               elements, element_count,
368                                               &responder_callback,
369                                               (void *) &closure)))
370   {
371     GNUNET_free (elements);
372     GNUNET_free (mask);
373     return;
374   }
375   GNUNET_free (elements);
376   GNUNET_free (mask);
377   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
378                                 &shutdown_task,
379                                 NULL);
380
381   ret = 0;
382 }
383
384
385 /**
386  * The main function to the scalarproduct client.
387  *
388  * @param argc number of arguments from the command line
389  * @param argv command line arguments
390  * @return 0 ok, 1 on error
391  */
392 int
393 main (int argc, char *const *argv)
394 {
395   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
396     {'e', "elements", "\"val1,val2,...,valn\"",
397       gettext_noop ("A comma separated list of elements to compare as vector with our remote peer."),
398       1, &GNUNET_GETOPT_set_string, &input_elements},
399     {'m', "mask", "\"0,1,...,maskn\"",
400       gettext_noop ("A comma separated mask to select which elements should actually be compared."),
401       1, &GNUNET_GETOPT_set_string, &input_mask},
402     {'p', "peer", "PEERID",
403       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."),
404       1, &GNUNET_GETOPT_set_string, &input_peer_id},
405     {'k', "key", "TRANSACTION_ID",
406       gettext_noop ("Transaction ID shared with peer."),
407       1, &GNUNET_GETOPT_set_string, &input_key},
408     GNUNET_GETOPT_OPTION_END
409   };
410
411   return (GNUNET_OK ==
412           GNUNET_PROGRAM_run (argc,
413                               argv,
414                               "gnunet-scalarproduct",
415                               gettext_noop ("Calculate the Vectorproduct with a GNUnet peer."),
416                               options, &run, NULL)) ? ret : 1;
417 }
418