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