fee8eea81b95beb34e07c2ba87f2b852969b6f90
[oweals/gnunet.git] / src / dv / test_transport_api_dv.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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  * @file dv/test_transport_api_dv.c
22  * @brief base testcase for testing distance vector transport
23  */
24 #include "platform.h"
25 #include "gnunet_testing_lib.h"
26 #include "gnunet_core_service.h"
27
28 #define VERBOSE GNUNET_NO
29
30 #define TEST_ALL GNUNET_NO
31
32 /**
33  * How long until we fail the whole testcase?
34  */
35 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
36
37 /**
38  * How long until we give up on starting the peers?
39  */
40 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 500)
41
42 #define DEFAULT_NUM_PEERS 4
43
44 #define DEFAULT_ADDITIONAL_MESSAGES 2
45
46 #define MAX_OUTSTANDING_CONNECTIONS 100
47
48 static float fail_percentage = 0.00;
49
50 static int ok;
51
52 static unsigned long long num_additional_messages;
53
54 static unsigned long long num_peers;
55
56 static unsigned int total_connections;
57
58 static unsigned int failed_connections;
59
60 static unsigned int total_server_connections;
61
62 static unsigned int total_messages_received;
63
64 static unsigned int total_other_expected_messages;
65
66 static unsigned int temp_total_other_messages;
67
68 static unsigned int total_other_messages;
69
70 static unsigned int expected_messages;
71
72 static unsigned int expected_connections;
73
74 static unsigned long long peers_left;
75
76 static struct GNUNET_TESTING_PeerGroup *pg;
77
78 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
79
80 GNUNET_SCHEDULER_TaskIdentifier die_task;
81
82 static char *dotOutFileName = "topology.dot";
83
84 static FILE *dotOutFile;
85
86 static char *blacklist_transports;
87
88 static int transmit_ready_scheduled;
89
90 static int transmit_ready_failed;
91
92 static int transmit_ready_called;
93
94 static enum GNUNET_TESTING_Topology topology;
95
96 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_NONE;  /* Don't do any blacklisting */
97
98 static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
99
100 static enum GNUNET_TESTING_TopologyOption connect_topology_option =
101     GNUNET_TESTING_TOPOLOGY_OPTION_ALL;
102
103 static double connect_topology_option_modifier = 0.0;
104
105 static char *test_directory;
106
107 struct GNUNET_CONTAINER_MultiHashMap *peer_daemon_hash;
108
109 #define MTYPE 12345
110
111 struct GNUNET_TestMessage
112 {
113   /**
114    * Header of the message
115    */
116   struct GNUNET_MessageHeader header;
117
118   /**
119    * Unique identifier for this message.
120    */
121   uint32_t uid;
122 };
123
124 struct PeerContext
125 {
126   /* This is a linked list */
127   struct PeerContext *next;
128
129   /**
130    * Handle to the daemon
131    */
132   struct GNUNET_TESTING_Daemon *daemon;
133
134   /* Handle to the peer core */
135   struct GNUNET_CORE_Handle *peer_handle;
136 };
137
138 static struct PeerContext *all_peers;
139
140 struct TestMessageContext
141 {
142   /* This is a linked list */
143   struct TestMessageContext *next;
144
145   /* Handle to the sending peer core */
146   struct GNUNET_CORE_Handle *peer1handle;
147
148   /* Handle to the receiving peer core */
149   struct GNUNET_CORE_Handle *peer2handle;
150
151   /* Handle to the sending peer daemon */
152   struct GNUNET_TESTING_Daemon *peer1;
153
154   /* Handle to the receiving peer daemon */
155   struct GNUNET_TESTING_Daemon *peer2;
156
157   /* Identifier for this message, so we don't disconnect other peers! */
158   uint32_t uid;
159
160   /* Task for disconnecting cores, allow task to be cancelled on shutdown */
161   GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
162 };
163
164 static struct TestMessageContext *test_messages;
165
166 static struct TestMessageContext *other_test_messages;
167
168 /**
169  * Check whether peers successfully shut down.
170  */
171 void
172 shutdown_callback (void *cls, const char *emsg)
173 {
174   if (emsg != NULL)
175   {
176 #if VERBOSE
177     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown of peers failed!\n");
178 #endif
179     if (ok == 0)
180       ok = 666;
181   }
182   else
183   {
184 #if VERBOSE
185     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All peers successfully shut down!\n");
186 #endif
187   }
188 }
189
190 static void
191 finish_testing ()
192 {
193   GNUNET_assert (pg != NULL);
194   struct PeerContext *peer_pos;
195   struct PeerContext *free_peer_pos;
196   struct TestMessageContext *pos;
197   struct TestMessageContext *free_pos;
198
199 #if VERBOSE
200   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
201               "Called finish testing, stopping daemons.\n");
202 #endif
203   peer_pos = all_peers;
204   while (peer_pos != NULL)
205   {
206     if (peer_pos->peer_handle != NULL)
207       GNUNET_CORE_disconnect (peer_pos->peer_handle);
208     free_peer_pos = peer_pos;
209     peer_pos = peer_pos->next;
210     GNUNET_free (free_peer_pos);
211   }
212   all_peers = NULL;
213
214   pos = test_messages;
215   while (pos != NULL)
216   {
217     if (pos->peer1handle != NULL)
218     {
219       GNUNET_CORE_disconnect (pos->peer1handle);
220       pos->peer1handle = NULL;
221     }
222     if (pos->peer2handle != NULL)
223     {
224       GNUNET_CORE_disconnect (pos->peer2handle);
225       pos->peer2handle = NULL;
226     }
227     free_pos = pos;
228     pos = pos->next;
229     if (free_pos->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
230     {
231       GNUNET_SCHEDULER_cancel (free_pos->disconnect_task);
232     }
233     GNUNET_free (free_pos);
234   }
235
236   pos = other_test_messages;
237   while (pos != NULL)
238   {
239     if (pos->peer1handle != NULL)
240     {
241       GNUNET_CORE_disconnect (pos->peer1handle);
242       pos->peer1handle = NULL;
243     }
244     if (pos->peer2handle != NULL)
245     {
246       GNUNET_CORE_disconnect (pos->peer2handle);
247       pos->peer2handle = NULL;
248     }
249     free_pos = pos;
250     pos = pos->next;
251     if (free_pos->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
252     {
253       GNUNET_SCHEDULER_cancel (free_pos->disconnect_task);
254     }
255     GNUNET_free (free_pos);
256   }
257 #if VERBOSE
258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
259               "transmit_ready's scheduled %d, failed %d, transmit_ready's called %d\n",
260               transmit_ready_scheduled, transmit_ready_failed,
261               transmit_ready_called);
262 #endif
263
264 #if VERBOSE
265   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Calling daemons_stop\n");
266 #endif
267   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
268 #if VERBOSE
269   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "daemons_stop finished\n");
270 #endif
271   if (dotOutFile != NULL)
272   {
273     fprintf (dotOutFile, "}");
274     fclose (dotOutFile);
275   }
276
277   ok = 0;
278 }
279
280
281 static void
282 disconnect_cores (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
283 {
284   struct TestMessageContext *pos = cls;
285
286   /* Disconnect from the respective cores */
287 #if VERBOSE
288   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from peer 1 `%4s'\n",
289               GNUNET_i2s (&pos->peer1->id));
290 #endif
291   if (pos->peer1handle != NULL)
292     GNUNET_CORE_disconnect (pos->peer1handle);
293 #if VERBOSE
294   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from peer 2 `%4s'\n",
295               GNUNET_i2s (&pos->peer2->id));
296 #endif
297   if (pos->peer2handle != NULL)
298     GNUNET_CORE_disconnect (pos->peer2handle);
299   /* Set handles to NULL so test case can be ended properly */
300   pos->peer1handle = NULL;
301   pos->peer2handle = NULL;
302   pos->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
303   /* Decrement total connections so new can be established */
304   total_server_connections -= 2;
305 }
306
307 static void
308 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
309 {
310   char *msg = cls;
311
312   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
313               "End badly was called (%s)... stopping daemons.\n", msg);
314   struct TestMessageContext *pos;
315   struct TestMessageContext *free_pos;
316   struct PeerContext *peer_pos;
317   struct PeerContext *free_peer_pos;
318
319   peer_pos = all_peers;
320   while (peer_pos != NULL)
321   {
322     if (peer_pos->peer_handle != NULL)
323       GNUNET_CORE_disconnect (peer_pos->peer_handle);
324     free_peer_pos = peer_pos;
325     peer_pos = peer_pos->next;
326     GNUNET_free (free_peer_pos);
327   }
328   all_peers = NULL;
329
330   pos = test_messages;
331   while (pos != NULL)
332   {
333     if (pos->peer1handle != NULL)
334     {
335       GNUNET_CORE_disconnect (pos->peer1handle);
336       pos->peer1handle = NULL;
337     }
338     if (pos->peer2handle != NULL)
339     {
340       GNUNET_CORE_disconnect (pos->peer2handle);
341       pos->peer2handle = NULL;
342     }
343     free_pos = pos;
344     pos = pos->next;
345     GNUNET_free (free_pos);
346   }
347
348   pos = other_test_messages;
349   while (pos != NULL)
350   {
351     if (pos->peer1handle != NULL)
352     {
353       GNUNET_CORE_disconnect (pos->peer1handle);
354       pos->peer1handle = NULL;
355     }
356     if (pos->peer2handle != NULL)
357     {
358       GNUNET_CORE_disconnect (pos->peer2handle);
359       pos->peer2handle = NULL;
360     }
361     free_pos = pos;
362     pos = pos->next;
363     if (free_pos->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
364     {
365       GNUNET_SCHEDULER_cancel (free_pos->disconnect_task);
366     }
367     GNUNET_free (free_pos);
368   }
369
370   if (pg != NULL)
371   {
372     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
373     ok = 7331;                  /* Opposite of leet */
374   }
375   else
376     ok = 401;                   /* Never got peers started */
377
378   if (dotOutFile != NULL)
379   {
380     fprintf (dotOutFile, "}");
381     fclose (dotOutFile);
382   }
383 }
384
385 static void send_other_messages (void *cls,
386                                  const struct GNUNET_SCHEDULER_TaskContext *tc);
387
388 /**
389  * Get distance information from 'atsi'.
390  *
391  * @param atsi performance data
392  * @return connected transport distance
393  */
394 static uint32_t
395 get_atsi_distance (const struct GNUNET_TRANSPORT_ATS_Information *atsi)
396 {
397   while ((ntohl (atsi->type) != GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR) &&
398          (ntohl (atsi->type) != GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE))
399     atsi++;
400   if (ntohl (atsi->type) == GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR)
401   {
402     GNUNET_break (0);
403     /* FIXME: we do not have distance data? Assume direct neighbor. */
404     return 1;
405   }
406   return ntohl (atsi->value);
407 }
408
409 static int
410 process_mtype (void *cls, const struct GNUNET_PeerIdentity *peer,
411                const struct GNUNET_MessageHeader *message,
412                const struct GNUNET_TRANSPORT_ATS_Information *atsi)
413 {
414   struct TestMessageContext *pos = cls;
415   struct GNUNET_TestMessage *msg = (struct GNUNET_TestMessage *) message;
416
417 #if VERBOSE
418   uint32_t distance;
419 #endif
420   if (pos->uid != ntohl (msg->uid))
421     return GNUNET_OK;
422
423 #if VERBOSE
424   distance = get_atsi_distance (atsi);
425 #endif
426   GNUNET_assert (0 ==
427                  memcmp (peer, &pos->peer1->id,
428                          sizeof (struct GNUNET_PeerIdentity)));
429   if (total_other_expected_messages == 0)
430   {
431     total_messages_received++;
432 #if VERBOSE
433     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434                 "Received message from `%4s', type %d, uid %u, distance %u.\n",
435                 GNUNET_i2s (peer), ntohs (message->type), ntohl (msg->uid),
436                 distance);
437     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
438                 "Total messages received %d, expected %d.\n",
439                 total_messages_received, expected_messages);
440 #endif
441   }
442   else
443   {
444     total_other_messages++;
445 #if VERBOSE
446     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
447                 "Received message from `%4s', type %d, uid %u, distance %u.\n",
448                 GNUNET_i2s (peer), ntohs (message->type), ntohl (msg->uid),
449                 distance);
450     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
451                 "Total messages received %d, expected %d.\n",
452                 total_other_messages, total_other_expected_messages);
453 #endif
454   }
455
456   if ((total_messages_received == expected_messages) &&
457       (total_other_messages == 0))
458   {
459     GNUNET_SCHEDULER_cancel (die_task);
460     die_task =
461         GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &end_badly,
462                                       "waiting for DV peers to connect!");
463     /*
464      * if ((num_peers == 3) && (total_other_expected_messages == 2))
465      * {
466      * GNUNET_SCHEDULER_add_now (&send_other_messages, NULL);
467      * }
468      * else
469      * {
470      * GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20), &send_other_messages, NULL);
471      * } */
472   }
473   else if ((total_other_expected_messages > 0) &&
474            (total_other_messages == total_other_expected_messages))
475   {
476     GNUNET_SCHEDULER_cancel (die_task);
477     GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
478   }
479   else
480   {
481     pos->disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cores, pos);
482   }
483
484   return GNUNET_OK;
485 }
486
487 static size_t
488 transmit_ready (void *cls, size_t size, void *buf)
489 {
490   struct GNUNET_TestMessage *m;
491   struct TestMessageContext *pos = cls;
492
493   GNUNET_assert (buf != NULL);
494   m = (struct GNUNET_TestMessage *) buf;
495   m->header.type = htons (MTYPE);
496   m->header.size = htons (sizeof (struct GNUNET_TestMessage));
497   m->uid = htonl (pos->uid);
498   transmit_ready_called++;
499 #if VERBOSE
500   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501               "transmit ready for peer %s\ntransmit_ready's scheduled %d, transmit_ready's called %d\n",
502               GNUNET_i2s (&pos->peer1->id), transmit_ready_scheduled,
503               transmit_ready_called);
504 #endif
505   return sizeof (struct GNUNET_TestMessage);
506 }
507
508
509 static struct GNUNET_CORE_MessageHandler no_handlers[] = {
510   {NULL, 0, 0}
511 };
512
513 static struct GNUNET_CORE_MessageHandler handlers[] = {
514   {&process_mtype, MTYPE, sizeof (struct GNUNET_TestMessage)},
515   {NULL, 0, 0}
516 };
517
518 /**
519  * Notify of all peer1's peers, once peer 2 is found, schedule connect
520  * to peer two for message send.
521  *
522  * @param cls closure
523  * @param peer peer identity this notification is about
524  * @param atsi performance data for the connection
525  */
526 static void
527 connect_notify_peer2 (void *cls, const struct GNUNET_PeerIdentity *peer,
528                       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
529 {
530   struct TestMessageContext *pos = cls;
531
532   if (0 == memcmp (&pos->peer1->id, peer, sizeof (struct GNUNET_PeerIdentity)))
533   {
534 #if VERBOSE
535     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
536                 "Core connection from `%s' to `%4s' verfied, sending message!\n",
537                 GNUNET_i2s (&pos->peer2->id), GNUNET_h2s (&peer->hashPubKey));
538 #endif
539     if (NULL ==
540         GNUNET_CORE_notify_transmit_ready (pos->peer1handle, GNUNET_YES, 0,
541                                            TIMEOUT, &pos->peer2->id,
542                                            sizeof (struct GNUNET_TestMessage),
543                                            &transmit_ready, pos))
544     {
545       /* This probably shouldn't happen, but it does (timing issue?) */
546       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
547                   "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
548                   GNUNET_i2s (&pos->peer2->id));
549       transmit_ready_failed++;
550       total_other_expected_messages--;
551     }
552     else
553     {
554       transmit_ready_scheduled++;
555     }
556   }
557 }
558
559 static void
560 init_notify_peer2 (void *cls, struct GNUNET_CORE_Handle *server,
561                    const struct GNUNET_PeerIdentity *my_identity,
562                    const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
563                    *publicKey)
564 {
565 #if VERBOSE
566   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
567               "Core connection to `%4s' established, awaiting connections.\n",
568               GNUNET_i2s (my_identity));
569 #endif
570   total_server_connections++;
571 }
572
573 /**
574  * Notify of all peer1's peers, once peer 2 is found, schedule connect
575  * to peer two for message send.
576  *
577  * @param cls closure
578  * @param peer peer identity this notification is about
579  * @param atsi performance data for the connection
580  */
581 static void
582 connect_notify_peer1 (void *cls, const struct GNUNET_PeerIdentity *peer,
583                       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
584 {
585   struct TestMessageContext *pos = cls;
586
587   if (0 == memcmp (&pos->peer2->id, peer, sizeof (struct GNUNET_PeerIdentity)))
588   {
589 #if VERBOSE
590     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591                 "Core connection from `%s' to `%4s' verified.\n",
592                 GNUNET_i2s (&pos->peer1->id), GNUNET_h2s (&peer->hashPubKey));
593 #endif
594     /*
595      * Connect to the receiving peer
596      */
597     pos->peer2handle =
598         GNUNET_CORE_connect (pos->peer2->cfg, 1, pos, &init_notify_peer2,
599                              &connect_notify_peer2, NULL, NULL, NULL,
600                              GNUNET_YES, NULL, GNUNET_YES, handlers);
601   }
602 }
603
604 static void
605 init_notify_peer1 (void *cls, struct GNUNET_CORE_Handle *server,
606                    const struct GNUNET_PeerIdentity *my_identity,
607                    const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
608                    *publicKey)
609 {
610   total_server_connections++;
611 #if VERBOSE
612   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613               "Core connection to `%4s' established, awaiting connections...\n",
614               GNUNET_i2s (my_identity));
615 #endif
616 }
617
618
619 static void
620 send_test_messages (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
621 {
622   struct TestMessageContext *pos = cls;
623
624   if (((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) || (cls == NULL))
625     return;
626
627   if (die_task == GNUNET_SCHEDULER_NO_TASK)
628   {
629     die_task =
630         GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &end_badly,
631                                       "from create topology (timeout)");
632   }
633
634   if (total_server_connections >= MAX_OUTSTANDING_CONNECTIONS)
635   {
636     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
637                                   (GNUNET_TIME_UNIT_SECONDS, 1),
638                                   &send_test_messages, pos);
639     return;                     /* Otherwise we'll double schedule messages here! */
640   }
641 #if VERBOSE
642   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
643               "Attempting to send test message from %s to %s\n",
644               pos->peer1->shortname, pos->peer2->shortname);
645 #endif
646   /*
647    * Connect to the sending peer
648    */
649   pos->peer1handle =
650       GNUNET_CORE_connect (pos->peer1->cfg, 1, pos, &init_notify_peer1,
651                            &connect_notify_peer1, NULL, NULL, NULL, GNUNET_NO,
652                            NULL, GNUNET_NO, no_handlers);
653
654   GNUNET_assert (pos->peer1handle != NULL);
655
656   if (total_server_connections < MAX_OUTSTANDING_CONNECTIONS)
657   {
658     GNUNET_SCHEDULER_add_now (&send_test_messages, pos->next);
659   }
660   else
661   {
662     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
663                                   (GNUNET_TIME_UNIT_SECONDS, 1),
664                                   &send_test_messages, pos->next);
665   }
666 }
667
668 static void
669 send_other_messages (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
670 {
671   struct TestMessageContext *pos;
672   struct TestMessageContext *free_pos;
673   struct PeerContext *peer_pos;
674
675 #if TEST_ALL
676   struct PeerContext *inner_peer_pos;
677   struct TestMessageContext *temp_context;
678 #endif
679   peer_pos = all_peers;
680   while (peer_pos != NULL)
681   {
682     if (peer_pos->peer_handle != NULL)
683     {
684       GNUNET_CORE_disconnect (peer_pos->peer_handle);
685       peer_pos->peer_handle = NULL;
686     }
687 #if TEST_ALL
688     inner_peer_pos = all_peers;
689     while (inner_peer_pos != NULL)
690     {
691       if (inner_peer_pos != peer_pos)
692       {
693         temp_total_other_messages++;
694         temp_context = GNUNET_malloc (sizeof (struct TestMessageContext));
695         temp_context->peer1 = peer_pos->daemon;
696         temp_context->peer2 = inner_peer_pos->daemon;
697         temp_context->next = other_test_messages;
698         temp_context->uid = total_connections + temp_total_other_messages;
699         temp_context->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
700         other_test_messages = temp_context;
701       }
702       inner_peer_pos = inner_peer_pos->next;
703     }
704 #endif
705     peer_pos = peer_pos->next;
706   }
707   all_peers = NULL;
708
709   pos = test_messages;
710   while (pos != NULL)
711   {
712     if (pos->peer1handle != NULL)
713     {
714       GNUNET_CORE_disconnect (pos->peer1handle);
715       pos->peer1handle = NULL;
716     }
717     if (pos->peer2handle != NULL)
718     {
719       GNUNET_CORE_disconnect (pos->peer2handle);
720       pos->peer2handle = NULL;
721     }
722     free_pos = pos;
723     pos = pos->next;
724     if (free_pos->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
725     {
726       GNUNET_SCHEDULER_cancel (free_pos->disconnect_task);
727     }
728     GNUNET_free (free_pos);
729   }
730   test_messages = NULL;
731
732   total_other_expected_messages = temp_total_other_messages;
733   if (total_other_expected_messages == 0)
734   {
735     GNUNET_SCHEDULER_add_now (&end_badly,
736                               "send_other_messages had 0 messages to send, no DV connections made!");
737   }
738 #if VERBOSE
739   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
740               "Preparing to send %d other test messages\n",
741               total_other_expected_messages);
742 #endif
743
744   GNUNET_SCHEDULER_add_now (&send_test_messages, other_test_messages);
745   GNUNET_SCHEDULER_cancel (die_task);
746   die_task =
747       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
748                                     (GNUNET_TIME_UNIT_SECONDS, 250), &end_badly,
749                                     "from send_other_messages");
750 }
751
752 static void
753 topology_callback (void *cls, const struct GNUNET_PeerIdentity *first,
754                    const struct GNUNET_PeerIdentity *second, uint32_t distance,
755                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
756                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
757                    struct GNUNET_TESTING_Daemon *first_daemon,
758                    struct GNUNET_TESTING_Daemon *second_daemon,
759                    const char *emsg)
760 {
761   struct TestMessageContext *temp_context;
762
763   if (emsg == NULL)
764   {
765     total_connections++;
766 #if VERBOSE
767     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768                 "connected peer %s to peer %s, distance %u\n",
769                 first_daemon->shortname, second_daemon->shortname, distance);
770 #endif
771     temp_context = GNUNET_malloc (sizeof (struct TestMessageContext));
772     temp_context->peer1 = first_daemon;
773     temp_context->peer2 = second_daemon;
774     temp_context->next = test_messages;
775     temp_context->uid = total_connections;
776     temp_context->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
777     test_messages = temp_context;
778     expected_messages++;
779   }
780 #if VERBOSE
781   else
782   {
783     failed_connections++;
784     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
785                 "Failed to connect peer %s to peer %s with error :\n%s\n",
786                 first_daemon->shortname, second_daemon->shortname, emsg);
787   }
788 #endif
789
790   if (total_connections == expected_connections)
791   {
792 #if VERBOSE
793     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
794                 "Created %u total connections, which is our target number!  Calling send messages.\n",
795                 total_connections);
796 #endif
797
798     GNUNET_SCHEDULER_cancel (die_task);
799     die_task = GNUNET_SCHEDULER_NO_TASK;
800     GNUNET_SCHEDULER_add_now (&send_test_messages, test_messages);
801   }
802   else if (total_connections + failed_connections == expected_connections)
803   {
804     if (failed_connections <
805         (unsigned int) (fail_percentage * total_connections))
806     {
807       GNUNET_SCHEDULER_cancel (die_task);
808       die_task = GNUNET_SCHEDULER_NO_TASK;
809       GNUNET_SCHEDULER_add_now (&send_test_messages, test_messages);
810     }
811     else
812     {
813       GNUNET_SCHEDULER_cancel (die_task);
814       die_task =
815           GNUNET_SCHEDULER_add_now (&end_badly,
816                                     "from topology_callback (too many failed connections)");
817     }
818   }
819   else
820   {
821 #if VERBOSE
822     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
823                 "Have %d total connections, %d failed connections, Want %d (at least %d)\n",
824                 total_connections, failed_connections, expected_connections,
825                 expected_connections -
826                 (unsigned int) (fail_percentage * expected_connections));
827 #endif
828   }
829 }
830
831 static void
832 connect_topology ()
833 {
834   expected_connections = -1;
835   if ((pg != NULL) && (peers_left == 0))
836   {
837     expected_connections =
838         GNUNET_TESTING_connect_topology (pg, connection_topology,
839                                          connect_topology_option,
840                                          connect_topology_option_modifier,
841                                          TIMEOUT, 12, NULL, NULL);
842 #if VERBOSE
843     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %d expected connections\n",
844                 expected_connections);
845 #endif
846   }
847
848   GNUNET_SCHEDULER_cancel (die_task);
849   if (expected_connections == GNUNET_SYSERR)
850   {
851     die_task =
852         GNUNET_SCHEDULER_add_now (&end_badly,
853                                   "from connect topology (bad return)");
854   }
855
856   die_task =
857       GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &end_badly,
858                                     "from connect topology (timeout)");
859 }
860
861 static void
862 create_topology ()
863 {
864   peers_left = num_peers;       /* Reset counter */
865   if (GNUNET_TESTING_create_topology
866       (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
867   {
868 #if VERBOSE
869     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
870                 "Topology set up, now starting peers!\n");
871 #endif
872     GNUNET_TESTING_daemons_continue_startup (pg);
873   }
874   else
875   {
876     GNUNET_SCHEDULER_cancel (die_task);
877     die_task =
878         GNUNET_SCHEDULER_add_now (&end_badly,
879                                   "from create topology (bad return)");
880   }
881   GNUNET_SCHEDULER_cancel (die_task);
882   die_task =
883       GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &end_badly,
884                                     "from continue startup (timeout)");
885 }
886
887 /**
888  * Method called whenever a given peer connects.
889  *
890  * @param cls closure
891  * @param peer peer identity this notification is about
892  * @param latency reported latency of the connection with 'other'
893  * @param distance reported distance (DV) to 'other'
894  */
895 static void
896 all_connect_handler (void *cls, const struct GNUNET_PeerIdentity *peer,
897                      const struct GNUNET_TRANSPORT_ATS_Information *atsi)
898 {
899   struct GNUNET_TESTING_Daemon *d = cls;
900   struct GNUNET_TESTING_Daemon *second_daemon;
901   char *second_shortname;
902
903 #if !TEST_ALL
904   struct TestMessageContext *temp_context;
905 #endif
906   uint32_t distance;
907
908   if (0 == memcmp (&d->id, peer, sizeof (struct GNUNET_PeerIdentity)))
909     return;
910   second_shortname = GNUNET_strdup (GNUNET_i2s (peer));
911   distance = get_atsi_distance (atsi);
912
913 #if VERBOSE
914   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
915               "connected peer %s to peer %s, distance %u\n", d->shortname,
916               second_shortname, distance);
917 #endif
918
919   second_daemon =
920       GNUNET_CONTAINER_multihashmap_get (peer_daemon_hash, &peer->hashPubKey);
921
922   if (second_daemon == NULL)
923   {
924     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Couldn't find second peer!\n");
925     GNUNET_free (second_shortname);
926     return;
927   }
928 #if !TEST_ALL
929   if (distance > 1)
930   {
931     temp_total_other_messages++;
932     temp_context = GNUNET_malloc (sizeof (struct TestMessageContext));
933     temp_context->peer1 = d;
934     temp_context->peer2 = second_daemon;
935     temp_context->next = other_test_messages;
936     temp_context->uid = total_connections + temp_total_other_messages;
937     temp_context->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
938     other_test_messages = temp_context;
939   }
940 #endif
941
942   if (dotOutFile != NULL)
943   {
944     if (distance == 1)
945       fprintf (dotOutFile, "\tn%s -- n%s;\n", d->shortname, second_shortname);
946     else if (distance == 2)
947       fprintf (dotOutFile, "\tn%s -- n%s [color=blue];\n", d->shortname,
948                second_shortname);
949     else if (distance == 3)
950       fprintf (dotOutFile, "\tn%s -- n%s [color=red];\n", d->shortname,
951                second_shortname);
952     else if (distance == 4)
953       fprintf (dotOutFile, "\tn%s -- n%s [color=green];\n", d->shortname,
954                second_shortname);
955     else
956       fprintf (dotOutFile, "\tn%s -- n%s [color=brown];\n", d->shortname,
957                second_shortname);
958   }
959   GNUNET_free (second_shortname);
960
961   if (temp_total_other_messages == num_additional_messages)
962   {
963     GNUNET_SCHEDULER_add_now (&send_other_messages, NULL);
964   }
965 }
966
967 static void
968 peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id,
969                         const struct GNUNET_CONFIGURATION_Handle *cfg,
970                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
971 {
972   struct PeerContext *new_peer;
973
974   if (emsg != NULL)
975   {
976     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
977                 "Failed to start daemon with error: `%s'\n", emsg);
978     return;
979   }
980   GNUNET_assert (id != NULL);
981 #if VERBOSE
982   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
983               (num_peers - peers_left) + 1, num_peers);
984 #endif
985   GNUNET_assert (GNUNET_SYSERR !=
986                  GNUNET_CONTAINER_multihashmap_put (peer_daemon_hash,
987                                                     &id->hashPubKey, d,
988                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
989
990   new_peer = GNUNET_malloc (sizeof (struct PeerContext));
991   new_peer->peer_handle =
992       GNUNET_CORE_connect (cfg, 1, d, NULL, &all_connect_handler, NULL, NULL,
993                            NULL, GNUNET_NO, NULL, GNUNET_NO, no_handlers);
994   new_peer->daemon = d;
995   new_peer->next = all_peers;
996   all_peers = new_peer;
997   peers_left--;
998
999   if (peers_left == 0)
1000   {
1001 #if VERBOSE
1002     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1003                 "All %d daemons started, now creating topology!\n", num_peers);
1004 #endif
1005     GNUNET_SCHEDULER_cancel (die_task);
1006     /* Set up task in case topology creation doesn't finish
1007      * within a reasonable amount of time */
1008     die_task =
1009         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1010                                       (GNUNET_TIME_UNIT_MINUTES, 5), &end_badly,
1011                                       "from peers_started_callback");
1012
1013     connect_topology ();
1014     ok = 0;
1015   }
1016 }
1017
1018 /**
1019  * Callback indicating that the hostkey was created for a peer.
1020  *
1021  * @param cls NULL
1022  * @param id the peer identity
1023  * @param d the daemon handle (pretty useless at this point, remove?)
1024  * @param emsg non-null on failure
1025  */
1026 static void
1027 hostkey_callback (void *cls, const struct GNUNET_PeerIdentity *id,
1028                   struct GNUNET_TESTING_Daemon *d, const char *emsg)
1029 {
1030   if (emsg != NULL)
1031   {
1032     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1033                 "Hostkey callback received error: %s\n", emsg);
1034   }
1035
1036 #if VERBOSE
1037   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostkey created for peer `%s'\n",
1038               GNUNET_i2s (id));
1039 #endif
1040   peers_left--;
1041   if (peers_left == 0)
1042   {
1043 #if VERBOSE
1044     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1045                 "All %d hostkeys created, now creating topology!\n", num_peers);
1046 #endif
1047     GNUNET_SCHEDULER_cancel (die_task);
1048     /* Set up task in case topology creation doesn't finish
1049      * within a reasonable amount of time */
1050     die_task =
1051         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1052                                       (GNUNET_TIME_UNIT_MINUTES, 5), &end_badly,
1053                                       "from hostkey_callback");
1054     GNUNET_SCHEDULER_add_now (&create_topology, NULL);
1055     ok = 0;
1056   }
1057 }
1058
1059 static void
1060 run (void *cls, char *const *args, const char *cfgfile,
1061      const struct GNUNET_CONFIGURATION_Handle *cfg)
1062 {
1063   char *topology_str;
1064   char *connect_topology_str;
1065   char *blacklist_topology_str;
1066   char *connect_topology_option_str;
1067   char *connect_topology_option_modifier_string;
1068
1069   ok = 1;
1070
1071   dotOutFile = fopen (dotOutFileName, "w");
1072   if (dotOutFile != NULL)
1073   {
1074     fprintf (dotOutFile, "strict graph G {\n");
1075   }
1076
1077 #if VERBOSE
1078   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1079               "Starting daemons based on config file %s\n", cfgfile);
1080 #endif
1081
1082   if (GNUNET_YES !=
1083       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
1084                                              &test_directory))
1085   {
1086     ok = 404;
1087     return;
1088   }
1089
1090   if ((GNUNET_YES ==
1091        GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "topology",
1092                                               &topology_str)) &&
1093       (GNUNET_NO == GNUNET_TESTING_topology_get (&topology, topology_str)))
1094   {
1095     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1096                 "Invalid topology `%s' given for section %s option %s\n",
1097                 topology_str, "TESTING", "TOPOLOGY");
1098     topology = GNUNET_TESTING_TOPOLOGY_CLIQUE;  /* Defaults to NONE, so set better default here */
1099   }
1100
1101   if ((GNUNET_YES ==
1102        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1103                                               "connect_topology",
1104                                               &connect_topology_str)) &&
1105       (GNUNET_NO ==
1106        GNUNET_TESTING_topology_get (&connection_topology,
1107                                     connect_topology_str)))
1108   {
1109     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1110                 "Invalid connect topology `%s' given for section %s option %s\n",
1111                 connect_topology_str, "TESTING", "CONNECT_TOPOLOGY");
1112   }
1113   GNUNET_free_non_null (connect_topology_str);
1114   if ((GNUNET_YES ==
1115        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1116                                               "connect_topology_option",
1117                                               &connect_topology_option_str)) &&
1118       (GNUNET_NO ==
1119        GNUNET_TESTING_topology_option_get (&connect_topology_option,
1120                                            connect_topology_option_str)))
1121   {
1122     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1123                 "Invalid connect topology option `%s' given for section %s option %s\n",
1124                 connect_topology_option_str, "TESTING",
1125                 "CONNECT_TOPOLOGY_OPTION");
1126     connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL;       /* Defaults to NONE, set to ALL */
1127   }
1128   GNUNET_free_non_null (connect_topology_option_str);
1129   if (GNUNET_YES ==
1130       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1131                                              "connect_topology_option_modifier",
1132                                              &connect_topology_option_modifier_string))
1133   {
1134     if (sscanf
1135         (connect_topology_option_modifier_string, "%lf",
1136          &connect_topology_option_modifier) != 1)
1137     {
1138       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1139                   _
1140                   ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1141                   connect_topology_option_modifier_string,
1142                   "connect_topology_option_modifier", "TESTING");
1143     }
1144     GNUNET_free (connect_topology_option_modifier_string);
1145   }
1146
1147   if (GNUNET_YES !=
1148       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1149                                              "blacklist_transports",
1150                                              &blacklist_transports))
1151     blacklist_transports = NULL;
1152
1153   if ((GNUNET_YES ==
1154        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
1155                                               "blacklist_topology",
1156                                               &blacklist_topology_str)) &&
1157       (GNUNET_NO ==
1158        GNUNET_TESTING_topology_get (&blacklist_topology,
1159                                     blacklist_topology_str)))
1160   {
1161     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1162                 "Invalid topology `%s' given for section %s option %s\n",
1163                 topology_str, "TESTING", "BLACKLIST_TOPOLOGY");
1164   }
1165   GNUNET_free_non_null (topology_str);
1166   GNUNET_free_non_null (blacklist_topology_str);
1167   if (GNUNET_SYSERR ==
1168       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
1169                                              &num_peers))
1170     num_peers = DEFAULT_NUM_PEERS;
1171
1172   if (GNUNET_SYSERR ==
1173       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
1174                                              "additional_messages",
1175                                              &num_additional_messages))
1176     num_additional_messages = DEFAULT_ADDITIONAL_MESSAGES;
1177
1178   main_cfg = cfg;
1179
1180   GNUNET_assert (num_peers > 0 && num_peers < (unsigned int) -1);
1181   peers_left = num_peers;
1182
1183   /* Set up a task to end testing if peer start fails */
1184   die_task =
1185       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1186                                     (GNUNET_TIME_UNIT_MINUTES, 5), &end_badly,
1187                                     "didn't start all daemons in reasonable amount of time!!!");
1188
1189   peer_daemon_hash = GNUNET_CONTAINER_multihashmap_create (peers_left);
1190   pg = GNUNET_TESTING_daemons_start (cfg, peers_left,   /* Total number of peers */
1191                                      peers_left,        /* Number of outstanding connections */
1192                                      peers_left,        /* Number of parallel ssh connections, or peers being started at once */
1193                                      TIMEOUT, &hostkey_callback, NULL,
1194                                      &peers_started_callback, NULL,
1195                                      &topology_callback, NULL, NULL);
1196
1197 }
1198
1199 static int
1200 check ()
1201 {
1202   int ret;
1203
1204   char *const argv[] = { "test-transport-dv",
1205     "-c",
1206     "test_transport_dv_data.conf",
1207 #if VERBOSE
1208     "-L", "DEBUG",
1209 #endif
1210     NULL
1211   };
1212   struct GNUNET_GETOPT_CommandLineOption options[] = {
1213     GNUNET_GETOPT_OPTION_END
1214   };
1215   ret =
1216       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
1217                           "test-transport-dv", "nohelp", options, &run, &ok);
1218   if (ret != GNUNET_OK)
1219   {
1220     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1221                 "`test-transport-dv': Failed with error code %d\n", ret);
1222   }
1223   return ok;
1224 }
1225
1226 int
1227 main (int argc, char *argv[])
1228 {
1229   int ret;
1230
1231   GNUNET_log_setup ("test-transport-dv",
1232 #if VERBOSE
1233                     "DEBUG",
1234 #else
1235                     "WARNING",
1236 #endif
1237                     NULL);
1238   ret = check ();
1239   /**
1240    * Need to remove base directory, subdirectories taken care
1241    * of by the testing framework.
1242    */
1243   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
1244   {
1245     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1246                 "Failed to remove testing directory %s\n", test_directory);
1247   }
1248   return ret;
1249 }
1250
1251 /* end of test_transport_api_dv.c */