8311c1d72d66a00338f5c42ee19aad415ea7b861
[oweals/gnunet.git] / src / transport / gnunet-transport.c
1 /*
2  This file is part of GNUnet.
3  (C) 2011 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 src/transport/gnunet-transport.c
23  * @brief Tool to help configure, measure and control the transport subsystem.
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  *
27  * This utility can be used to test if a transport mechanism for
28  * GNUnet is properly configured.
29  */
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_resolver_service.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_transport_service.h"
36 #include "gnunet_nat_lib.h"
37
38 /**
39  * How long do we wait for the NAT test to report success?
40  * Should match NAT_SERVER_TIMEOUT in 'nat_test.c'.
41  */
42 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
43 #define RESOLUTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
44 #define OP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
45
46 /**
47  * Benchmarking block size in KB
48  */
49 #define BLOCKSIZE 4
50
51 /**
52  * Which peer should we connect to?
53  */
54 static char *cpid;
55
56 /**
57  * Handle to transport service.
58  */
59 static struct GNUNET_TRANSPORT_Handle *handle;
60
61 /**
62  * Configuration handle
63  */
64 static struct GNUNET_CONFIGURATION_Handle *cfg;
65
66 /**
67  * Try_connect handle
68  */
69 struct GNUNET_TRANSPORT_TryConnectHandle * tc_handle;
70
71 /**
72  * Option -s.
73  */
74 static int benchmark_send;
75
76 /**
77  * Option -b.
78  */
79 static int benchmark_receive;
80
81 /**
82  * Option -l.
83  */
84 static int benchmark_receive;
85
86 /**
87  * Option -i.
88  */
89 static int iterate_connections;
90
91 /**
92  * Option -d.
93  */
94 static int iterate_validation;
95
96 /**
97  * Option -a.
98  */
99 static int iterate_all;
100
101 /**
102  * Option -t.
103  */
104 static int test_configuration;
105
106 /**
107  * Option -c.
108  */
109 static int monitor_connects;
110
111 /**
112  * Option -m.
113  */
114 static int monitor_connections;
115
116 /**
117  * Option -C.
118  */
119 static int try_connect;
120
121 /**
122  * Option -n.
123  */
124 static int numeric;
125
126 /**
127  * Global return value (0 success).
128  */
129 static int ret;
130
131 /**
132  * Current number of connections in monitor mode
133  */
134 static int monitor_connect_counter;
135
136 /**
137  * Number of bytes of traffic we received so far.
138  */
139 static unsigned long long traffic_received;
140
141 /**
142  * Number of bytes of traffic we sent so far.
143  */
144 static unsigned long long traffic_sent;
145
146 /**
147  * Starting time of transmitting/receiving data.
148  */
149 static struct GNUNET_TIME_Absolute start_time;
150
151 /**
152  * Handle for current transmission request.
153  */
154 static struct GNUNET_TRANSPORT_TransmitHandle *th;
155
156 /**
157  * Map storing information about monitored peers
158  */
159 static struct GNUNET_CONTAINER_MultiPeerMap *monitored_peers;
160
161 /**
162  *
163  */
164 static struct GNUNET_TRANSPORT_PeerMonitoringContext *pic;
165
166 static struct GNUNET_TRANSPORT_ValidationMonitoringContext *vic;
167
168 /**
169  * Identity of the peer we transmit to / connect to.
170  * (equivalent to 'cpid' string).
171  */
172 static struct GNUNET_PeerIdentity pid;
173
174 /**
175  * Task scheduled for cleanup / termination of the process.
176  */
177 static GNUNET_SCHEDULER_TaskIdentifier end;
178
179 /**
180  * Task for operation timeout
181  */
182 static GNUNET_SCHEDULER_TaskIdentifier op_timeout;
183
184 /**
185  * Selected level of verbosity.
186  */
187 static int verbosity;
188
189 /**
190  * Resolver process handle.
191  */
192 struct GNUNET_OS_Process *resolver;
193
194 /**
195  * Number of tasks running that still need the resolver.
196  */
197 static unsigned int resolver_users;
198
199 /**
200  * Number of address resolutions pending
201  */
202 static unsigned int address_resolutions;
203
204 /**
205  * Address resolutions pending in progress
206  */
207 static unsigned int address_resolution_in_progress;
208
209
210 /**
211  * Context for a plugin test.
212  */
213 struct TestContext
214 {
215
216   /**
217    * Handle to the active NAT test.
218    */
219   struct GNUNET_NAT_Test *tst;
220
221   /**
222    * Task identifier for the timeout.
223    */
224   GNUNET_SCHEDULER_TaskIdentifier tsk;
225
226   /**
227    * Name of plugin under test.
228    */
229   const char *name;
230
231 };
232
233 struct MonitoredPeer
234 {
235   enum GNUNET_TRANSPORT_PeerState state;
236   struct GNUNET_TIME_Absolute state_timeout;
237   struct GNUNET_HELLO_Address *address;
238 };
239
240
241  int destroy_it (void *cls,
242     const struct GNUNET_PeerIdentity *key,
243     void *value)
244 {
245    struct MonitoredPeer *m = value;
246    GNUNET_CONTAINER_multipeermap_remove (monitored_peers, key, value);
247    GNUNET_free_non_null (m->address);
248    GNUNET_free (value);
249    return GNUNET_OK;
250 }
251
252 /**
253  * Task run in monitor mode when the user presses CTRL-C to abort.
254  * Stops monitoring activity.
255  *
256  * @param cls the 'struct GNUNET_TRANSPORT_PeerIterateContext *'
257  * @param tc scheduler context
258  */
259 static void
260 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
261 {
262   struct GNUNET_TIME_Relative duration;
263   end = GNUNET_SCHEDULER_NO_TASK;
264   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
265   {
266     GNUNET_SCHEDULER_cancel (op_timeout);
267     op_timeout = GNUNET_SCHEDULER_NO_TASK;
268   }
269   if (NULL != tc_handle)
270   {
271     GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
272     tc_handle = NULL;
273   }
274   if (NULL != pic)
275   {
276     GNUNET_TRANSPORT_monitor_peers_cancel (pic);
277     pic = NULL;
278   }
279   if (NULL != vic)
280   {
281     GNUNET_TRANSPORT_monitor_validation_entries_cancel (vic);
282     vic = NULL;
283   }
284   if (NULL != th)
285   {
286     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
287     th = NULL;
288   }
289   if (NULL != handle)
290   {
291     GNUNET_TRANSPORT_disconnect (handle);
292     handle = NULL;
293   }
294   if (benchmark_send)
295   {
296     duration = GNUNET_TIME_absolute_get_duration (start_time);
297     FPRINTF (stdout, _("Transmitted %llu bytes/s (%llu bytes in %s)\n"),
298         1000LL * 1000LL * traffic_sent / (1 + duration.rel_value_us),
299         traffic_sent,
300         GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
301   }
302   if (benchmark_receive)
303   {
304     duration = GNUNET_TIME_absolute_get_duration (start_time);
305     FPRINTF (stdout, _("Received %llu bytes/s (%llu bytes in %s)\n"),
306         1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us),
307         traffic_received,
308         GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
309   }
310
311   if (NULL != monitored_peers)
312   {
313     GNUNET_CONTAINER_multipeermap_iterate (monitored_peers, &destroy_it, NULL);
314     GNUNET_CONTAINER_multipeermap_destroy (monitored_peers);
315     monitored_peers = NULL;
316   }
317 }
318
319 static struct PeerResolutionContext *rc_head;
320 static struct PeerResolutionContext *rc_tail;
321
322 struct PeerResolutionContext
323 {
324   struct PeerResolutionContext *next;
325   struct PeerResolutionContext *prev;
326   struct GNUNET_PeerIdentity id;
327   struct GNUNET_HELLO_Address *addrcp;
328   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
329   enum GNUNET_TRANSPORT_PeerState state;
330   struct GNUNET_TIME_Absolute state_timeout;
331   char *transport;
332   int printed;
333 };
334
335 static struct ValidationResolutionContext *vc_head;
336 static struct ValidationResolutionContext *vc_tail;
337
338 struct ValidationResolutionContext
339 {
340   struct ValidationResolutionContext *next;
341   struct ValidationResolutionContext *prev;
342
343   struct GNUNET_PeerIdentity id;
344   struct GNUNET_HELLO_Address *addrcp;
345   struct GNUNET_TIME_Absolute last_validation;
346   struct GNUNET_TIME_Absolute valid_until;
347   struct GNUNET_TIME_Absolute next_validation;
348   enum GNUNET_TRANSPORT_ValidationState state;
349
350   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
351
352   char *transport;
353   int printed;
354 };
355
356 static void
357 operation_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
358 {
359   struct PeerResolutionContext *cur;
360   struct PeerResolutionContext *next;
361   op_timeout = GNUNET_SCHEDULER_NO_TASK;
362   if ((try_connect) || (benchmark_send) || (benchmark_receive))
363   {
364     FPRINTF (stdout, _("Failed to connect to `%s'\n"), GNUNET_i2s_full (&pid));
365     if (GNUNET_SCHEDULER_NO_TASK != end)
366       GNUNET_SCHEDULER_cancel (end);
367     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
368     ret = 1;
369     return;
370   }
371   if (iterate_connections)
372   {
373     next = rc_head;
374     while (NULL != (cur = next))
375     {
376       next = cur->next;
377       FPRINTF (stdout, _("Failed to resolve address for peer `%s'\n"),
378           GNUNET_i2s (&cur->addrcp->peer));
379
380       GNUNET_CONTAINER_DLL_remove(rc_head, rc_tail, cur);
381       GNUNET_TRANSPORT_address_to_string_cancel (cur->asc);
382       GNUNET_free(cur->transport);
383       GNUNET_free(cur->addrcp);
384       GNUNET_free(cur);
385
386     }
387     FPRINTF (stdout, "%s", _("Failed to list connections, timeout occured\n") );
388     if (GNUNET_SCHEDULER_NO_TASK != end)
389       GNUNET_SCHEDULER_cancel (end);
390     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
391     ret = 1;
392     return;
393   }
394
395 }
396
397 /**
398  * Display the result of the test.
399  *
400  * @param tc test context
401  * @param result #GNUNET_YES on success
402  */
403 static void
404 display_test_result (struct TestContext *tc, int result)
405 {
406   if (GNUNET_YES != result)
407   {
408     FPRINTF (stderr, "Configuration for plugin `%s' did not work!\n", tc->name);
409   }
410   else
411   {
412     FPRINTF (stderr, "Configuration for plugin `%s' is working!\n", tc->name);
413   }
414   if (GNUNET_SCHEDULER_NO_TASK != tc->tsk)
415   {
416     GNUNET_SCHEDULER_cancel (tc->tsk);
417     tc->tsk = GNUNET_SCHEDULER_NO_TASK;
418   }
419   if (NULL != tc->tst)
420   {
421     GNUNET_NAT_test_stop (tc->tst);
422     tc->tst = NULL;
423   }
424   GNUNET_free(tc);
425   resolver_users--;
426   if ((0 == resolver_users) && (NULL != resolver))
427   {
428     GNUNET_break(0 == GNUNET_OS_process_kill (resolver, GNUNET_TERM_SIG));
429     GNUNET_OS_process_destroy (resolver);
430     resolver = NULL;
431   }
432 }
433
434 /**
435  * Function called by NAT on success.
436  * Clean up and update GUI (with success).
437  *
438  * @param cls test context
439  * @param success currently always #GNUNET_OK
440  * @param emsg error message, NULL on success
441  */
442 static void
443 result_callback (void *cls, int success, const char *emsg)
444 {
445   struct TestContext *tc = cls;
446
447   display_test_result (tc, success);
448 }
449
450 /**
451  * Function called if NAT failed to confirm success.
452  * Clean up and update GUI (with failure).
453  *
454  * @param cls test context
455  * @param tc scheduler callback
456  */
457 static void
458 fail_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
459 {
460   struct TestContext *tstc = cls;
461
462   tstc->tsk = GNUNET_SCHEDULER_NO_TASK;
463   display_test_result (tstc, GNUNET_NO);
464 }
465
466
467 static void
468 resolve_validation_address (const struct GNUNET_PeerIdentity *id,
469     const struct GNUNET_HELLO_Address *address, int numeric,
470     struct GNUNET_TIME_Absolute last_validation,
471     struct GNUNET_TIME_Absolute valid_until,
472     struct GNUNET_TIME_Absolute next_validation,
473     enum GNUNET_TRANSPORT_ValidationState state);
474
475 static void
476 process_validation_string (void *cls, const char *address)
477 {
478   struct ValidationResolutionContext *vc = cls;
479   char *s_valid;
480   char *s_last;
481   char *s_next;
482
483   if (address != NULL )
484   {
485     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->valid_until.abs_value_us)
486       s_valid = GNUNET_strdup("never");
487     else
488       s_valid = GNUNET_strdup(GNUNET_STRINGS_absolute_time_to_string (vc->valid_until));
489
490     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->last_validation.abs_value_us)
491       s_last = GNUNET_strdup("never");
492     else
493       s_last = GNUNET_strdup(GNUNET_STRINGS_absolute_time_to_string (vc->last_validation));
494
495     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->next_validation.abs_value_us)
496       s_next = GNUNET_strdup("never");
497     else
498       s_next = GNUNET_strdup(GNUNET_STRINGS_absolute_time_to_string (vc->next_validation));
499
500     FPRINTF (stdout,
501         _("Peer `%s' %s `%s'\n\t%s%s\n\t%s%s\n\t%s%s\n"),
502         GNUNET_i2s (&vc->id), address, GNUNET_TRANSPORT_vs2s (vc->state),
503         "Valid until    : ", s_valid,
504         "Last validation: ",s_last,
505         "Next validation: ", s_next);
506     GNUNET_free (s_valid);
507     GNUNET_free (s_last);
508     GNUNET_free (s_next);
509     vc->printed = GNUNET_YES;
510   }
511   else
512   {
513     /* done */
514     GNUNET_assert(address_resolutions > 0);
515     address_resolutions--;
516     if (GNUNET_NO == vc->printed)
517     {
518       if (numeric == GNUNET_NO)
519       {
520         /* Failed to resolve address, try numeric lookup */
521         resolve_validation_address (&vc->id, vc->addrcp, GNUNET_NO,
522            vc->last_validation, vc->valid_until, vc->next_validation,
523            vc->state);
524       }
525       else
526       {
527         FPRINTF (stdout, _("Peer `%s' %s `%s' \n"),
528             GNUNET_i2s (&vc->id), "<unable to resolve address>",
529             GNUNET_TRANSPORT_vs2s (vc->state));
530       }
531     }
532     GNUNET_free (vc->transport);
533     GNUNET_free (vc->addrcp);
534     GNUNET_CONTAINER_DLL_remove(vc_head, vc_tail, vc);
535     GNUNET_free(vc);
536     if ((0 == address_resolutions) && (iterate_validation))
537     {
538       if (GNUNET_SCHEDULER_NO_TASK != end)
539       {
540         GNUNET_SCHEDULER_cancel (end);
541         end = GNUNET_SCHEDULER_NO_TASK;
542       }
543       if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
544       {
545         GNUNET_SCHEDULER_cancel (op_timeout);
546         op_timeout = GNUNET_SCHEDULER_NO_TASK;
547       }
548       ret = 0;
549       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
550     }
551   }
552 }
553
554
555
556 static void
557 resolve_validation_address (const struct GNUNET_PeerIdentity *id,
558     const struct GNUNET_HELLO_Address *address, int numeric,
559     struct GNUNET_TIME_Absolute last_validation,
560     struct GNUNET_TIME_Absolute valid_until,
561     struct GNUNET_TIME_Absolute next_validation,
562     enum GNUNET_TRANSPORT_ValidationState state)
563 {
564   struct ValidationResolutionContext *vc;
565
566   vc = GNUNET_new (struct ValidationResolutionContext);
567   GNUNET_assert(NULL != vc);
568   GNUNET_CONTAINER_DLL_insert(vc_head, vc_tail, vc);
569   address_resolutions++;
570
571   vc->id = (*id);
572   vc->transport = GNUNET_strdup(address->transport_name);
573   vc->addrcp = GNUNET_HELLO_address_copy (address);
574   vc->printed = GNUNET_NO;
575   vc->state = state;
576   vc->last_validation = last_validation;
577   vc->valid_until = valid_until;
578   vc->next_validation = next_validation;
579
580   /* Resolve address to string */
581   vc->asc = GNUNET_TRANSPORT_address_to_string (cfg, address, numeric,
582       RESOLUTION_TIMEOUT, &process_validation_string, vc);
583 }
584
585
586 void process_validation_cb (void *cls,
587     const struct GNUNET_PeerIdentity *peer,
588     const struct GNUNET_HELLO_Address *address,
589     struct GNUNET_TIME_Absolute last_validation,
590     struct GNUNET_TIME_Absolute valid_until,
591     struct GNUNET_TIME_Absolute next_validation,
592     enum GNUNET_TRANSPORT_ValidationState state)
593 {
594   if ((NULL == peer) && (NULL == address))
595   {
596     /* done */
597     vic = NULL;
598     if (GNUNET_SCHEDULER_NO_TASK != end)
599       GNUNET_SCHEDULER_cancel (end);
600     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
601     return;
602   }
603   if ((NULL == peer) || (NULL == address))
604   {
605     /* invalid response */
606     vic = NULL;
607     if (GNUNET_SCHEDULER_NO_TASK != end)
608       GNUNET_SCHEDULER_cancel (end);
609     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
610     return;
611   }
612   resolve_validation_address (peer, address,
613      numeric, last_validation,
614      valid_until, next_validation, state);
615 }
616
617 /**
618  * Test our plugin's configuration (NAT traversal, etc.).
619  *
620  * @param cfg configuration to test
621  */
622 static void
623 do_test_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
624 {
625   char *plugins;
626   char *tok;
627   unsigned long long bnd_port;
628   unsigned long long adv_port;
629   struct TestContext *tc;
630   char *binary;
631
632   if (GNUNET_OK
633       != GNUNET_CONFIGURATION_get_value_string (cfg, "transport", "plugins",
634           &plugins))
635   {
636     FPRINTF (stderr, "%s", _
637     ("No transport plugins configured, peer will never communicate\n") );
638     ret = 4;
639     return;
640   }
641   for (tok = strtok (plugins, " "); tok != NULL ; tok = strtok (NULL, " "))
642   {
643     char section[12 + strlen (tok)];
644
645     GNUNET_snprintf (section, sizeof(section), "transport-%s", tok);
646     if (GNUNET_OK
647         != GNUNET_CONFIGURATION_get_value_number (cfg, section, "PORT",
648             &bnd_port))
649     {
650       FPRINTF (stderr,
651           _("No port configured for plugin `%s', cannot test it\n"), tok);
652       continue;
653     }
654     if (GNUNET_OK
655         != GNUNET_CONFIGURATION_get_value_number (cfg, section,
656             "ADVERTISED_PORT", &adv_port))
657       adv_port = bnd_port;
658     if (NULL == resolver)
659     {
660       binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
661       resolver = GNUNET_OS_start_process (GNUNET_YES,
662           GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, binary,
663           "gnunet-service-resolver", NULL );
664       GNUNET_free(binary);
665     }
666     resolver_users++;
667     GNUNET_RESOLVER_connect (cfg);
668     tc = GNUNET_new (struct TestContext);
669     tc->name = GNUNET_strdup (tok);
670     tc->tst = GNUNET_NAT_test_start (cfg,
671         (0 == strcasecmp (tok, "udp")) ? GNUNET_NO : GNUNET_YES,
672         (uint16_t) bnd_port, (uint16_t) adv_port, &result_callback, tc);
673     if (NULL == tc->tst)
674     {
675       display_test_result (tc, GNUNET_SYSERR);
676       continue;
677     }
678     tc->tsk = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, tc);
679   }
680   GNUNET_free(plugins);
681 }
682
683 /**
684  * Function called to notify a client about the socket
685  * begin ready to queue more data.  @a buf will be
686  * NULL and @a size zero if the socket was closed for
687  * writing in the meantime.
688  *
689  * @param cls closure
690  * @param size number of bytes available in @a buf
691  * @param buf where the callee should write the message
692  * @return number of bytes written to @a buf
693  */
694 static size_t
695 transmit_data (void *cls, size_t size, void *buf)
696 {
697   struct GNUNET_MessageHeader *m = buf;
698
699   if ((NULL == buf) || (0 == size))
700   {
701     th = NULL;
702     return 0;
703   }
704
705   GNUNET_assert(size >= sizeof(struct GNUNET_MessageHeader));
706   GNUNET_assert(size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
707   m->size = ntohs (size);
708   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
709   memset (&m[1], 52, size - sizeof(struct GNUNET_MessageHeader));
710   traffic_sent += size;
711   th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, BLOCKSIZE * 1024,
712       0, GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data, NULL );
713   if (verbosity > 0)
714     FPRINTF (stdout, _("Transmitting %u bytes to %s\n"), (unsigned int) size,
715         GNUNET_i2s (&pid));
716   return size;
717 }
718
719 /**
720  * Function called to notify transport users that another
721  * peer connected to us.
722  *
723  * @param cls closure
724  * @param peer the peer that connected
725  */
726 static void
727 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
728 {
729   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
730     return;
731   ret = 0;
732   if (try_connect)
733   {
734     /* all done, terminate instantly */
735     FPRINTF (stdout, _("Successfully connected to `%s'\n"),
736         GNUNET_i2s_full (peer));
737     ret = 0;
738
739     if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
740     {
741       GNUNET_SCHEDULER_cancel (op_timeout);
742       op_timeout = GNUNET_SCHEDULER_NO_TASK;
743     }
744
745     if (GNUNET_SCHEDULER_NO_TASK != end)
746       GNUNET_SCHEDULER_cancel (end);
747     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
748     return;
749   }
750   if (benchmark_send)
751   {
752     if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
753     {
754       GNUNET_SCHEDULER_cancel (op_timeout);
755       op_timeout = GNUNET_SCHEDULER_NO_TASK;
756     }
757     if (verbosity > 0)
758       FPRINTF (stdout,
759           _("Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"),
760           GNUNET_i2s (&pid), BLOCKSIZE);
761     start_time = GNUNET_TIME_absolute_get ();
762     if (NULL == th)
763       th = GNUNET_TRANSPORT_notify_transmit_ready (handle, peer,
764           BLOCKSIZE * 1024, 0, GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data,
765           NULL );
766     else
767       GNUNET_break(0);
768     return;
769   }
770 }
771
772 /**
773  * Function called to notify transport users that another
774  * peer disconnected from us.
775  *
776  * @param cls closure
777  * @param peer the peer that disconnected
778  */
779 static void
780 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
781 {
782   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
783     return;
784
785   if (NULL != th)
786   {
787     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
788     th = NULL;
789   }
790   if (benchmark_send)
791   {
792     FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"),
793         GNUNET_i2s (&pid));
794     if (GNUNET_SCHEDULER_NO_TASK != end)
795       GNUNET_SCHEDULER_cancel (end);
796     return;
797   }
798 }
799
800 /**
801  * Function called to notify transport users that another
802  * peer connected to us.
803  *
804  * @param cls closure
805  * @param peer the peer that connected
806  */
807 static void
808 monitor_notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
809 {
810   monitor_connect_counter++;
811   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
812   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
813
814   FPRINTF (stdout, _("%24s: %-17s %4s   (%u connections in total)\n"), now_str,
815       _("Connected to"), GNUNET_i2s (peer), monitor_connect_counter);
816 }
817
818 /**
819  * Function called to notify transport users that another
820  * peer disconnected from us.
821  *
822  * @param cls closure
823  * @param peer the peer that disconnected
824  */
825 static void
826 monitor_notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
827 {
828   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
829   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
830
831   GNUNET_assert(monitor_connect_counter > 0);
832   monitor_connect_counter--;
833
834   FPRINTF (stdout, _("%24s: %-17s %4s   (%u connections in total)\n"), now_str,
835       _("Disconnected from"), GNUNET_i2s (peer), monitor_connect_counter);
836 }
837
838 /**
839  * Function called by the transport for each received message.
840  *
841  * @param cls closure
842  * @param peer (claimed) identity of the other peer
843  * @param message the message
844  */
845 static void
846 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
847     const struct GNUNET_MessageHeader *message)
848 {
849   if (benchmark_receive)
850   {
851     if (GNUNET_MESSAGE_TYPE_DUMMY != ntohs (message->type))
852       return;
853     if (verbosity > 0)
854       FPRINTF (stdout, _("Received %u bytes from %s\n"),
855           (unsigned int) ntohs (message->size), GNUNET_i2s (peer));
856
857     if (traffic_received == 0)
858       start_time = GNUNET_TIME_absolute_get ();
859     traffic_received += ntohs (message->size);
860     return;
861   }
862 }
863
864 static void
865 resolve_peer_address (const struct GNUNET_PeerIdentity *id,
866     const struct GNUNET_HELLO_Address *address, int numeric,
867     enum GNUNET_TRANSPORT_PeerState state,
868     struct GNUNET_TIME_Absolute state_timeout);
869
870 static void
871 print_info (const struct GNUNET_PeerIdentity *id, const char *transport,
872     const char *addr, enum GNUNET_TRANSPORT_PeerState state,
873     struct GNUNET_TIME_Absolute state_timeout)
874 {
875   if ((GNUNET_YES == iterate_all) || (GNUNET_YES == monitor_connections) )
876   {
877     FPRINTF (stdout, _("Peer `%s': %s %s in state `%s' until %s\n"),
878         GNUNET_i2s (id),
879         (NULL == transport) ? "<none>" : transport,
880         (NULL == transport) ? "<none>" : addr,
881         GNUNET_TRANSPORT_ps2s (state),
882         GNUNET_STRINGS_absolute_time_to_string (state_timeout));
883   }
884   else
885   {
886     /* Only connected peers, skip state */
887     FPRINTF (stdout, _("Peer `%s': %s %s\n"), GNUNET_i2s (id), transport, addr);
888   }
889
890 }
891
892 static void
893 process_peer_string (void *cls, const char *address)
894 {
895   struct PeerResolutionContext *rc = cls;
896
897   if (address != NULL )
898   {
899     print_info (&rc->id, rc->transport, address, rc->state, rc->state_timeout);
900     rc->printed = GNUNET_YES;
901   }
902   else
903   {
904     /* done */
905     GNUNET_assert(address_resolutions > 0);
906     address_resolutions--;
907     if (GNUNET_NO == rc->printed)
908     {
909       if (numeric == GNUNET_NO)
910       {
911         /* Failed to resolve address, try numeric lookup */
912         resolve_peer_address (&rc->id, rc->addrcp, GNUNET_YES,
913             rc->state, rc->state_timeout);
914       }
915       else
916       {
917         print_info (&rc->id, rc->transport, "<unable to resolve address>",
918             rc->state, rc->state_timeout);
919       }
920     }
921     GNUNET_free (rc->transport);
922     GNUNET_free (rc->addrcp);
923     GNUNET_CONTAINER_DLL_remove(rc_head, rc_tail, rc);
924     GNUNET_free(rc);
925     if ((0 == address_resolutions) && (iterate_connections))
926     {
927       if (GNUNET_SCHEDULER_NO_TASK != end)
928       {
929         GNUNET_SCHEDULER_cancel (end);
930         end = GNUNET_SCHEDULER_NO_TASK;
931       }
932       if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
933       {
934         GNUNET_SCHEDULER_cancel (op_timeout);
935         op_timeout = GNUNET_SCHEDULER_NO_TASK;
936       }
937       ret = 0;
938       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
939     }
940   }
941 }
942
943 static void
944 resolve_peer_address (const struct GNUNET_PeerIdentity *id,
945     const struct GNUNET_HELLO_Address *address, int numeric,
946     enum GNUNET_TRANSPORT_PeerState state,
947     struct GNUNET_TIME_Absolute state_timeout)
948 {
949   struct PeerResolutionContext *rc;
950
951   rc = GNUNET_new (struct PeerResolutionContext);
952   GNUNET_assert(NULL != rc);
953   GNUNET_CONTAINER_DLL_insert(rc_head, rc_tail, rc);
954   address_resolutions++;
955
956   rc->id = (*id);
957   rc->transport = GNUNET_strdup(address->transport_name);
958   rc->addrcp = GNUNET_HELLO_address_copy (address);
959   rc->printed = GNUNET_NO;
960   rc->state = state;
961   rc->state_timeout = state_timeout;
962   /* Resolve address to string */
963   rc->asc = GNUNET_TRANSPORT_address_to_string (cfg, address, numeric,
964       RESOLUTION_TIMEOUT, &process_peer_string, rc);
965 }
966
967 /**
968  * Function called with information about a peers during a one shot iteration
969  *
970  * @param cls closure
971  * @param peer identity of the peer, NULL for final callback when operation done
972  * @param address binary address used to communicate with this peer,
973  *  NULL on disconnect or when done
974  * @param state current state this peer is in
975  * @param state_timeout time out for the current state
976  *
977  */
978 static void
979 process_peer_iteration_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
980     const struct GNUNET_HELLO_Address *address,
981     enum GNUNET_TRANSPORT_PeerState state,
982     struct GNUNET_TIME_Absolute state_timeout)
983 {
984   if (peer == NULL )
985   {
986     /* done */
987     address_resolution_in_progress = GNUNET_NO;
988     pic = NULL;
989     if (GNUNET_SCHEDULER_NO_TASK != end)
990       GNUNET_SCHEDULER_cancel (end);
991     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
992     return;
993   }
994
995   if ((GNUNET_NO == iterate_all) && (GNUNET_NO == GNUNET_TRANSPORT_is_connected(state)) )
996       return; /* Display only connected peers */
997
998   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
999     GNUNET_SCHEDULER_cancel (op_timeout);
1000   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1001       NULL );
1002
1003   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Received address for peer `%s': %s\n",
1004       GNUNET_i2s (peer), address->transport_name);
1005
1006   if (NULL != address)
1007     resolve_peer_address (peer, address, numeric, state, state_timeout);
1008   else
1009     print_info (peer, NULL, NULL, state, state_timeout);
1010 }
1011
1012
1013 /**
1014  * Function called with information about a peers
1015  *
1016  * @param cls closure
1017  * @param peer identity of the peer, NULL for final callback when operation done
1018  * @param address binary address used to communicate with this peer,
1019  *  NULL on disconnect or when done
1020  * @param state current state this peer is in
1021  * @param state_timeout time out for the current state
1022  *
1023  */
1024 static void
1025 process_peer_monitoring_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
1026     const struct GNUNET_HELLO_Address *address,
1027     enum GNUNET_TRANSPORT_PeerState state,
1028     struct GNUNET_TIME_Absolute state_timeout)
1029 {
1030   struct MonitoredPeer *m;
1031
1032   if (peer == NULL )
1033   {
1034     /* done */
1035     address_resolution_in_progress = GNUNET_NO;
1036     pic = NULL;
1037     if (GNUNET_SCHEDULER_NO_TASK != end)
1038       GNUNET_SCHEDULER_cancel (end);
1039     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
1040     return;
1041   }
1042
1043   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
1044     GNUNET_SCHEDULER_cancel (op_timeout);
1045   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1046       NULL );
1047
1048   if (NULL == (m = GNUNET_CONTAINER_multipeermap_get (monitored_peers, peer)))
1049   {
1050     m = GNUNET_new (struct MonitoredPeer);
1051     GNUNET_CONTAINER_multipeermap_put (monitored_peers, peer,
1052         m, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1053   }
1054   else
1055   {
1056     if ( (m->state == state) &&
1057       (m->state_timeout.abs_value_us == state_timeout.abs_value_us) &&
1058       ((NULL == address) && (NULL == m->address)) )
1059     {
1060       return; /* No real change */
1061     }
1062     if ( ((NULL != address) && (NULL != m->address)) &&
1063         (0 == GNUNET_HELLO_address_cmp(m->address, address)) )
1064       return; /* No real change */
1065   }
1066
1067
1068   if (NULL != m->address)
1069   {
1070     GNUNET_free (m->address);
1071     m->address = NULL;
1072   }
1073   if (NULL != address)
1074     m->address = GNUNET_HELLO_address_copy (address);
1075   m->state = state;
1076   m->state_timeout = state_timeout;
1077
1078   if (NULL != address)
1079     resolve_peer_address (peer, m->address, numeric, m->state, m->state_timeout);
1080   else
1081     print_info (peer, NULL, NULL, m->state, m->state_timeout);
1082 }
1083
1084 static void
1085 try_connect_cb (void *cls, const int result)
1086 {
1087   static int retries = 0;
1088   if (GNUNET_OK == result)
1089   {
1090     tc_handle = NULL;
1091     return;
1092   }
1093   retries++;
1094   if (retries < 10)
1095     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
1096         NULL );
1097   else
1098   {
1099     FPRINTF (stderr, "%s",
1100         _("Failed to send connect request to transport service\n") );
1101     if (GNUNET_SCHEDULER_NO_TASK != end)
1102       GNUNET_SCHEDULER_cancel (end);
1103     ret = 1;
1104     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
1105     return;
1106   }
1107 }
1108
1109 /**
1110  * Function called with the result of the check if the 'transport'
1111  * service is running.
1112  *
1113  * @param cls closure with our configuration
1114  * @param result #GNUNET_YES if transport is running
1115  */
1116 static void
1117 testservice_task (void *cls, int result)
1118 {
1119   int counter = 0;
1120   ret = 1;
1121
1122   if (GNUNET_YES != result)
1123   {
1124     FPRINTF (stderr, _("Service `%s' is not running\n"), "transport");
1125     return;
1126   }
1127
1128   if ((NULL != cpid)
1129       && (GNUNET_OK
1130           != GNUNET_CRYPTO_eddsa_public_key_from_string (cpid, strlen (cpid),
1131               &pid.public_key)))
1132   {
1133     FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
1134     return;
1135   }
1136
1137   counter = benchmark_send + benchmark_receive + iterate_connections
1138       + monitor_connections + monitor_connects + try_connect + iterate_validation;
1139
1140   if (1 < counter)
1141   {
1142     FPRINTF (stderr,
1143         _("Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s\n"),
1144         "connect", "benchmark send", "benchmark receive", "information",
1145         "monitor", "events");
1146     return;
1147   }
1148   if (0 == counter)
1149   {
1150     FPRINTF (stderr,
1151         _("No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s\n"),
1152         "connect", "benchmark send", "benchmark receive", "information",
1153         "monitor", "events");
1154     return;
1155   }
1156
1157   if (try_connect) /* -C: Connect to peer */
1158   {
1159     if (NULL == cpid)
1160     {
1161       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
1162           "-C", "-p");
1163       ret = 1;
1164       return;
1165     }
1166     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
1167         &notify_connect, &notify_disconnect);
1168     if (NULL == handle)
1169     {
1170       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1171       ret = 1;
1172       return;
1173     }
1174     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
1175         NULL );
1176     if (NULL == tc_handle)
1177     {
1178       FPRINTF (stderr, "%s",
1179           _("Failed to send request to transport service\n") );
1180       ret = 1;
1181       return;
1182     }
1183     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1184         NULL );
1185
1186   }
1187   else if (benchmark_send) /* -s: Benchmark sending */
1188   {
1189     if (NULL == cpid)
1190     {
1191       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
1192           "-s", "-p");
1193       ret = 1;
1194       return;
1195     }
1196     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
1197         &notify_connect, &notify_disconnect);
1198     if (NULL == handle)
1199     {
1200       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1201       ret = 1;
1202       return;
1203     }
1204     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
1205         NULL );
1206     if (NULL == tc_handle)
1207     {
1208       FPRINTF (stderr, "%s",
1209           _("Failed to send request to transport service\n") );
1210       ret = 1;
1211       return;
1212     }
1213     start_time = GNUNET_TIME_absolute_get ();
1214     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1215         NULL );
1216   }
1217   else if (benchmark_receive) /* -b: Benchmark receiving */
1218   {
1219     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive, NULL,
1220         NULL );
1221     if (NULL == handle)
1222     {
1223       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1224       ret = 1;
1225       return;
1226     }
1227     if (verbosity > 0)
1228       FPRINTF (stdout, "%s", _("Starting to receive benchmark data\n") );
1229     start_time = GNUNET_TIME_absolute_get ();
1230
1231   }
1232   else if (iterate_connections) /* -i: List information about peers once */
1233   {
1234     address_resolution_in_progress = GNUNET_YES;
1235     pic = GNUNET_TRANSPORT_monitor_peers (cfg, (NULL == cpid) ? NULL : &pid,
1236         GNUNET_YES, TIMEOUT, &process_peer_iteration_cb, (void *) cfg);
1237     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1238         NULL );
1239   }
1240   else if (monitor_connections) /* -m: List information about peers continuously */
1241   {
1242     monitored_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
1243     address_resolution_in_progress = GNUNET_YES;
1244     pic = GNUNET_TRANSPORT_monitor_peers (cfg, (NULL == cpid) ? NULL : &pid,
1245         GNUNET_NO, TIMEOUT, &process_peer_monitoring_cb, (void *) cfg);
1246   }
1247   else if (iterate_validation) /* -d: Print information about validations */
1248   {
1249     vic = GNUNET_TRANSPORT_monitor_validation_entries (cfg, (NULL == cpid) ? NULL : &pid,
1250         GNUNET_YES, TIMEOUT, &process_validation_cb, (void *) cfg);
1251   }
1252   else if (monitor_connects) /* -e : Monitor (dis)connect events continuously */
1253   {
1254     monitor_connect_counter = 0;
1255     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL,
1256         &monitor_notify_connect, &monitor_notify_disconnect);
1257     if (NULL == handle)
1258     {
1259       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1260       ret = 1;
1261       return;
1262     }
1263     ret = 0;
1264   }
1265   else
1266   {
1267     GNUNET_break(0);
1268     return;
1269   }
1270
1271   end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1272       &shutdown_task, NULL );
1273
1274 }
1275
1276 /**
1277  * Main function that will be run by the scheduler.
1278  *
1279  * @param cls closure
1280  * @param args remaining command-line arguments
1281  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1282  * @param mycfg configuration
1283  */
1284 static void
1285 run (void *cls, char * const *args, const char *cfgfile,
1286     const struct GNUNET_CONFIGURATION_Handle *mycfg)
1287 {
1288   cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
1289   if (test_configuration)
1290   {
1291     do_test_configuration (cfg);
1292     return;
1293   }
1294   GNUNET_CLIENT_service_test ("transport", cfg, GNUNET_TIME_UNIT_SECONDS,
1295       &testservice_task, (void *) cfg);
1296 }
1297
1298 int
1299 main (int argc, char * const *argv)
1300 {
1301   int res;
1302   static const struct GNUNET_GETOPT_CommandLineOption options[] =
1303       {
1304           { 'a', "all", NULL,
1305               gettext_noop ("print information for all peers (instead of only connected peers )"),
1306               0, &GNUNET_GETOPT_set_one, &iterate_all },
1307           { 'b', "benchmark", NULL,
1308               gettext_noop ("measure how fast we are receiving data from all peers (until CTRL-C)"),
1309               0, &GNUNET_GETOPT_set_one, &benchmark_receive }, { 'C', "connect",
1310               NULL, gettext_noop ("connect to a peer"), 0,
1311               &GNUNET_GETOPT_set_one, &try_connect },
1312           { 'd', "validation", NULL,
1313               gettext_noop ("print information for all pending validations "),
1314               0, &GNUNET_GETOPT_set_one, &iterate_validation },
1315           { 'i', "information", NULL,
1316               gettext_noop ("provide information about all current connections (once)"),
1317               0, &GNUNET_GETOPT_set_one, &iterate_connections },
1318           { 'm', "monitor", NULL,
1319               gettext_noop ("provide information about all current connections (continuously)"),
1320               0, &GNUNET_GETOPT_set_one, &monitor_connections },
1321           { 'e', "events", NULL,
1322               gettext_noop ("provide information about all connects and disconnect events (continuously)"),
1323               0, &GNUNET_GETOPT_set_one, &monitor_connects }, { 'n', "numeric",
1324               NULL, gettext_noop ("do not resolve hostnames"), 0,
1325               &GNUNET_GETOPT_set_one, &numeric }, { 'p', "peer", "PEER",
1326               gettext_noop ("peer identity"), 1, &GNUNET_GETOPT_set_string,
1327               &cpid }, { 's', "send", NULL, gettext_noop
1328           ("send data for benchmarking to the other peer (until CTRL-C)"), 0,
1329               &GNUNET_GETOPT_set_one, &benchmark_send },
1330           { 't', "test", NULL,
1331               gettext_noop ("test transport configuration (involves external server)"),
1332               0, &GNUNET_GETOPT_set_one, &test_configuration },
1333               GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
1334               GNUNET_GETOPT_OPTION_END };
1335
1336   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1337     return 2;
1338
1339   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-transport", gettext_noop
1340   ("Direct access to transport service."), options, &run, NULL );
1341   GNUNET_free((void * ) argv);
1342   if (GNUNET_OK == res)
1343     return ret;
1344   return 1;
1345 }
1346
1347 /* end of gnunet-transport.c */