0077bc9b77d65d403d83ea50f98bdc35e206a771
[oweals/gnunet.git] / src / psyc / test_psyc.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 psyc/test_psyc.c
23  * @brief Tests for the PSYC API.
24  * @author Gabor X Toth
25  * @author Christian Grothoff
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_crypto_lib.h"
32 #include "gnunet_common.h"
33 #include "gnunet_util_lib.h"
34 #include "gnunet_testing_lib.h"
35 #include "gnunet_env_lib.h"
36 #include "gnunet_psyc_util_lib.h"
37 #include "gnunet_psyc_service.h"
38
39 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
40
41 #define DEBUG_SERVICE 0
42
43
44 /**
45  * Return value from 'main'.
46  */
47 static int res;
48
49 static const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51 /**
52  * Handle for task for timeout termination.
53  */
54 static GNUNET_SCHEDULER_TaskIdentifier end_badly_task;
55
56 static struct GNUNET_PSYC_Master *mst;
57 static struct GNUNET_PSYC_Slave *slv;
58
59 static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
60 static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
61
62 static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
63 static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
64
65 struct GNUNET_PSYC_MasterTransmitHandle *mth;
66
67 struct TransmitClosure
68 {
69   struct GNUNET_PSYC_MasterTransmitHandle *mst_tmit;
70   struct GNUNET_PSYC_SlaveTransmitHandle *slv_tmit;
71   struct GNUNET_ENV_Environment *env;
72   struct GNUNET_ENV_Modifier *mod;
73   char *data[16];
74   const char *mod_value;
75   size_t mod_value_size;
76   uint8_t data_delay[16];
77   uint8_t data_count;
78   uint8_t paused;
79   uint8_t n;
80 };
81
82 struct TransmitClosure *tmit;
83
84 static uint8_t join_req_count;
85
86 enum
87 {
88   TEST_NONE,
89   TEST_SLAVE_TRANSMIT,
90   TEST_MASTER_TRANSMIT,
91 } test;
92
93
94 static void
95 master_transmit ();
96
97
98 /**
99  * Clean up all resources used.
100  */
101 static void
102 cleanup ()
103 {
104   if (NULL != slv)
105   {
106     GNUNET_PSYC_slave_part (slv);
107     slv = NULL;
108   }
109   if (NULL != mst)
110   {
111     GNUNET_PSYC_master_stop (mst);
112     mst = NULL;
113   }
114   if (NULL != tmit)
115   {
116     GNUNET_ENV_environment_destroy (tmit->env);
117     GNUNET_free (tmit);
118     tmit = NULL;
119   }
120   GNUNET_SCHEDULER_shutdown ();
121 }
122
123
124 /**
125  * Terminate the test case (failure).
126  *
127  * @param cls NULL
128  * @param tc scheduler context
129  */
130 static void
131 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
132 {
133   res = 1;
134   cleanup ();
135   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test FAILED.\n");
136 }
137
138
139 /**
140  * Terminate the test case (success).
141  *
142  * @param cls NULL
143  * @param tc scheduler context
144  */
145 static void
146 end_normally (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
147 {
148   res = 0;
149   cleanup ();
150   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Test PASSED.\n");
151 }
152
153
154 /**
155  * Finish the test case (successfully).
156  */
157 static void
158 end ()
159 {
160   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending tests.\n");
161
162   if (end_badly_task != GNUNET_SCHEDULER_NO_TASK)
163   {
164     GNUNET_SCHEDULER_cancel (end_badly_task);
165     end_badly_task = GNUNET_SCHEDULER_NO_TASK;
166   }
167   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
168                                 &end_normally, NULL);
169 }
170
171
172 static void
173 master_message_cb (void *cls, uint64_t message_id, uint32_t flags,
174                    const struct GNUNET_MessageHeader *msg)
175 {
176   if (NULL == msg)
177   {
178     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
179                 "Error while receiving message %llu\n", message_id);
180     return;
181   }
182
183   uint16_t type = ntohs (msg->type);
184   uint16_t size = ntohs (msg->size);
185
186   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
187               "Master got message part of type %u and size %u "
188               "belonging to message ID %llu with flags %x\n",
189               type, size, message_id, flags);
190
191   switch (test)
192   {
193   case TEST_SLAVE_TRANSMIT:
194     if (GNUNET_PSYC_MESSAGE_REQUEST != flags)
195     {
196       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197                   "Unexpected request flags: %x" PRIu32 "\n", flags);
198       GNUNET_assert (0);
199       return;
200     }
201     // FIXME: check rest of message
202
203     if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END == type)
204       master_transmit ();
205     break;
206
207   case TEST_MASTER_TRANSMIT:
208     break;
209
210   default:
211     GNUNET_assert (0);
212   }
213 }
214
215
216 static void
217 slave_message_cb (void *cls, uint64_t message_id, uint32_t flags,
218                   const struct GNUNET_MessageHeader *msg)
219 {
220   if (NULL == msg)
221   {
222     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
223                 "Error while receiving message %llu\n", message_id);
224     return;
225   }
226
227   uint16_t type = ntohs (msg->type);
228   uint16_t size = ntohs (msg->size);
229
230   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
231               "Slave got message part of type %u and size %u "
232               "belonging to message ID %llu with flags %x\n",
233               type, size, message_id, flags);
234
235   switch (test)
236   {
237   case TEST_MASTER_TRANSMIT:
238     if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END == type)
239       end ();
240     break;
241
242   default:
243     GNUNET_assert (0);
244   }
245 }
246
247
248 static void
249 transmit_resume (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
250 {
251   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission resumed.\n");
252   struct TransmitClosure *tmit = cls;
253   if (NULL != tmit->mst_tmit)
254     GNUNET_PSYC_master_transmit_resume (tmit->mst_tmit);
255   else
256     GNUNET_PSYC_slave_transmit_resume (tmit->slv_tmit);
257 }
258
259
260 static int
261 tmit_notify_data (void *cls, uint16_t *data_size, void *data)
262 {
263   struct TransmitClosure *tmit = cls;
264   if (0 == tmit->data_count)
265   {
266     *data_size = 0;
267     return GNUNET_YES;
268   }
269
270   uint16_t size = strlen (tmit->data[tmit->n]);
271   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272               "Transmit notify data: %u bytes available, "
273               "processing fragment %u/%u (size %u).\n",
274               *data_size, tmit->n + 1, tmit->data_count, size);
275   if (*data_size < size)
276   {
277     *data_size = 0;
278     GNUNET_assert (0);
279     return GNUNET_SYSERR;
280   }
281
282   if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
283   {
284     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission paused.\n");
285     tmit->paused = GNUNET_YES;
286     GNUNET_SCHEDULER_add_delayed (
287       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
288                                      tmit->data_delay[tmit->n]),
289       &transmit_resume, tmit);
290     *data_size = 0;
291     return GNUNET_NO;
292   }
293   tmit->paused = GNUNET_NO;
294
295   *data_size = size;
296   memcpy (data, tmit->data[tmit->n], size);
297
298   return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
299 }
300
301
302 static int
303 tmit_notify_mod (void *cls, uint16_t *data_size, void *data, uint8_t *oper,
304                  uint32_t *full_value_size)
305 {
306   struct TransmitClosure *tmit = cls;
307   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308               "Transmit notify modifier: %lu bytes available, "
309               "%u modifiers left to process.\n",
310               *data_size, GNUNET_ENV_environment_get_count (tmit->env));
311
312   uint16_t name_size = 0;
313   size_t value_size = 0;
314   const char *value = NULL;
315
316   if (NULL != oper && NULL != tmit->mod)
317   { /* New modifier */
318     tmit->mod = tmit->mod->next;
319     if (NULL == tmit->mod)
320     { /* No more modifiers, continue with data */
321       *data_size = 0;
322       return GNUNET_YES;
323     }
324
325     GNUNET_assert (tmit->mod->value_size < UINT32_MAX);
326     *full_value_size = tmit->mod->value_size;
327     *oper = tmit->mod->oper;
328     name_size = strlen (tmit->mod->name);
329
330     if (name_size + 1 + tmit->mod->value_size <= *data_size)
331     {
332       *data_size = name_size + 1 + tmit->mod->value_size;
333     }
334     else
335     {
336       tmit->mod_value_size = tmit->mod->value_size;
337       value_size = *data_size - name_size - 1;
338       tmit->mod_value_size -= value_size;
339       tmit->mod_value = tmit->mod->value + value_size;
340     }
341
342     memcpy (data, tmit->mod->name, name_size);
343     ((char *)data)[name_size] = '\0';
344     memcpy ((char *)data + name_size + 1, tmit->mod->value, value_size);
345   }
346   else if (NULL != tmit->mod_value && 0 < tmit->mod_value_size)
347   { /* Modifier continuation */
348     value = tmit->mod_value;
349     if (tmit->mod_value_size <= *data_size)
350     {
351       value_size = tmit->mod_value_size;
352       tmit->mod_value = NULL;
353     }
354     else
355     {
356       value_size = *data_size;
357       tmit->mod_value += value_size;
358     }
359     tmit->mod_value_size -= value_size;
360
361     if (*data_size < value_size)
362     {
363       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364                   "value larger than buffer: %u < %zu\n",
365                   *data_size, value_size);
366       *data_size = 0;
367       return GNUNET_NO;
368     }
369
370     *data_size = value_size;
371     memcpy (data, value, value_size);
372   }
373
374   return 0 == tmit->mod_value_size ? GNUNET_YES : GNUNET_NO;
375 }
376
377
378 static void
379 slave_join ();
380
381
382 static void
383 join_decision_cb (void *cls, int is_admitted,
384                   const struct GNUNET_PSYC_MessageHeader *join_msg)
385 {
386   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
387               "Slave got join decision: %d\n", is_admitted);
388
389   if (GNUNET_YES != is_admitted)
390   { /* First join request is refused, retry. */
391     GNUNET_assert (1 == join_req_count);
392     slave_join ();
393     return;
394   }
395
396   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Slave sending request to master.\n");
397
398   test = TEST_SLAVE_TRANSMIT;
399
400   tmit = GNUNET_new (struct TransmitClosure);
401   tmit->env = GNUNET_ENV_environment_create ();
402   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
403                               "_abc", "abc def", 7);
404   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
405                               "_abc_def", "abc def ghi", 11);
406   tmit->mod = GNUNET_ENV_environment_head (tmit->env);
407   tmit->n = 0;
408   tmit->data[0] = "slave test";
409   tmit->data_count = 1;
410   tmit->slv_tmit
411     = GNUNET_PSYC_slave_transmit (slv, "_request_test", tmit_notify_mod,
412                                   tmit_notify_data, tmit,
413                                   GNUNET_PSYC_SLAVE_TRANSMIT_NONE);
414 }
415
416
417 static void
418 join_request_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
419                  const struct GNUNET_PSYC_MessageHeader *msg,
420                  struct GNUNET_PSYC_JoinHandle *jh)
421 {
422   struct GNUNET_HashCode slave_key_hash;
423   GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
424   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
425               "Got join request #%u from %s.\n",
426               join_req_count, GNUNET_h2s (&slave_key_hash));
427
428   /* Reject first request */
429   int is_admitted = (0 < join_req_count++) ? GNUNET_YES : GNUNET_NO;
430   GNUNET_PSYC_join_decision (jh, is_admitted, 0, NULL, NULL);
431 }
432
433
434 static void
435 slave_connect_cb (void *cls, uint64_t max_message_id)
436 {
437   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
438               "Slave connected: %lu\n", max_message_id);
439 }
440
441
442 static void
443 slave_join ()
444 {
445   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Joining slave.\n");
446
447   struct GNUNET_PeerIdentity origin; // FIXME: this peer
448   struct GNUNET_ENV_Environment *env = GNUNET_ENV_environment_create ();
449   GNUNET_ENV_environment_add (env, GNUNET_ENV_OP_ASSIGN,
450                               "_foo", "bar baz", 7);
451   GNUNET_ENV_environment_add (env, GNUNET_ENV_OP_ASSIGN,
452                               "_foo_bar", "foo bar baz", 11);
453   struct GNUNET_MessageHeader *
454     join_msg = GNUNET_PSYC_message_create ("_request_join", env, "some data", 9);
455
456   slv = GNUNET_PSYC_slave_join (cfg, &channel_pub_key, slave_key, &origin,
457                                 0, NULL, &slave_message_cb,
458                                 &slave_connect_cb, &join_decision_cb, NULL,
459                                 join_msg);
460   GNUNET_ENV_environment_destroy (env);
461 }
462
463
464 static void
465 master_transmit ()
466 {
467   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Master sending message to all.\n");
468   test = TEST_MASTER_TRANSMIT;
469   uint32_t i, j;
470
471   char *name_max = "_test_max";
472   uint8_t name_max_size = sizeof ("_test_max");
473   char *val_max = GNUNET_malloc (GNUNET_PSYC_MODIFIER_MAX_PAYLOAD);
474   for (i = 0; i < GNUNET_PSYC_MODIFIER_MAX_PAYLOAD; i++)
475     val_max[i] = (0 == i % 10000) ? '0' + i / 10000 : '.';
476
477   char *name_cont = "_test_cont";
478   uint8_t name_cont_size = sizeof ("_test_cont");
479   char *val_cont = GNUNET_malloc (GNUNET_PSYC_MODIFIER_MAX_PAYLOAD
480                                   + GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD);
481   for (i = 0; i < GNUNET_PSYC_MODIFIER_MAX_PAYLOAD - name_cont_size; i++)
482     val_cont[i] = (0 == i % 10000) ? '0' + i / 10000 : ':';
483   for (j = 0; j < GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD; j++, i++)
484     val_cont[i] = (0 == j % 10000) ? '0' + j / 10000 : '!';
485
486   tmit = GNUNET_new (struct TransmitClosure);
487   tmit->env = GNUNET_ENV_environment_create ();
488   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
489                               "_foo", "bar baz", 7);
490   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
491                               name_max, val_max,
492                               GNUNET_PSYC_MODIFIER_MAX_PAYLOAD
493                               - name_max_size);
494   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
495                               "_foo_bar", "foo bar baz", 11);
496   GNUNET_ENV_environment_add (tmit->env, GNUNET_ENV_OP_ASSIGN,
497                               name_cont, val_cont,
498                               GNUNET_PSYC_MODIFIER_MAX_PAYLOAD - name_cont_size
499                               + GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD);
500   tmit->mod = GNUNET_ENV_environment_head (tmit->env);
501   tmit->data[0] = "foo";
502   tmit->data[1] =  GNUNET_malloc (GNUNET_PSYC_DATA_MAX_PAYLOAD + 1);
503   for (i = 0; i < GNUNET_PSYC_DATA_MAX_PAYLOAD; i++)
504     tmit->data[1][i] = (0 == i % 10000) ? '0' + i / 10000 : '_';
505   tmit->data[2] = "foo bar";
506   tmit->data[3] = "foo bar baz";
507   tmit->data_delay[1] = 3;
508   tmit->data_count = 4;
509   tmit->mst_tmit
510     = GNUNET_PSYC_master_transmit (mst, "_notice_test", tmit_notify_mod,
511                                    tmit_notify_data, tmit,
512                                    GNUNET_PSYC_MASTER_TRANSMIT_INC_GROUP_GEN);
513 }
514
515
516 static void
517 master_start_cb (void *cls, uint64_t max_message_id)
518 {
519   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
520               "Master started: %" PRIu64 "\n", max_message_id);
521   slave_join ();
522 }
523
524
525 /**
526  * Main function of the test, run from scheduler.
527  *
528  * @param cls NULL
529  * @param cfg configuration we use (also to connect to PSYC service)
530  * @param peer handle to access more of the peer (not used)
531  */
532 static void
533 #if DEBUG_SERVICE
534 run (void *cls, char *const *args, const char *cfgfile,
535      const struct GNUNET_CONFIGURATION_Handle *c)
536 #else
537 run (void *cls,
538      const struct GNUNET_CONFIGURATION_Handle *c,
539      struct GNUNET_TESTING_Peer *peer)
540 #endif
541 {
542   cfg = c;
543   end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
544
545   channel_key = GNUNET_CRYPTO_eddsa_key_create ();
546   slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
547
548   GNUNET_CRYPTO_eddsa_key_get_public (channel_key, &channel_pub_key);
549   GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
550
551   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting master.\n");
552   mst = GNUNET_PSYC_master_start (cfg, channel_key, GNUNET_PSYC_CHANNEL_PRIVATE,
553                                   &master_start_cb, &join_request_cb,
554                                   &master_message_cb, NULL);
555 }
556
557
558 int
559 main (int argc, char *argv[])
560 {
561   res = 1;
562 #if DEBUG_SERVICE
563   const struct GNUNET_GETOPT_CommandLineOption opts[] = {
564     GNUNET_GETOPT_OPTION_END
565   };
566   if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-psyc",
567                                        "test-psyc [options]",
568                                        opts, &run, NULL))
569     return 1;
570 #else
571   if (0 != GNUNET_TESTING_peer_run ("test-psyc", "test_psyc.conf", &run, NULL))
572     return 1;
573 #endif
574   return res;
575 }
576
577 /* end of test_psyc.c */