8bfa6f7aa72507c1a28db5938a44af6954b16aca
[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 /**
53  * Which peer should we connect to?
54  */
55 static char *cpid;
56
57 /**
58  * Handle to transport service.
59  */
60 static struct GNUNET_TRANSPORT_Handle *handle;
61
62 /**
63  * Configuration handle
64  */
65 static struct GNUNET_CONFIGURATION_Handle *cfg;
66
67 /**
68  * Try_connect handle
69  */
70 struct GNUNET_TRANSPORT_TryConnectHandle * tc_handle;
71
72 /**
73  * Option -s.
74  */
75 static int benchmark_send;
76
77 /**
78  * Option -b.
79  */
80 static int benchmark_receive;
81
82 /**
83  * Option -l.
84  */
85 static int benchmark_receive;
86
87 /**
88  * Option -i.
89  */
90 static int iterate_connections;
91
92 /**
93  * Option -t.
94  */
95 static int test_configuration;
96
97 /**
98  * Option -c.
99  */
100 static int monitor_connects;
101
102 /**
103  * Option -m.
104  */
105 static int monitor_connections;
106
107 /**
108  * Option -C.
109  */
110 static int try_connect;
111
112 /**
113  * Option -n.
114  */
115 static int numeric;
116
117 /**
118  * Global return value (0 success).
119  */
120 static int ret;
121
122 /**
123  * Current number of connections in monitor mode
124  */
125 static int monitor_connect_counter;
126
127 /**
128  * Number of bytes of traffic we received so far.
129  */
130 static unsigned long long traffic_received;
131
132 /**
133  * Number of bytes of traffic we sent so far.
134  */
135 static unsigned long long traffic_sent;
136
137 /**
138  * Starting time of transmitting/receiving data.
139  */
140 static struct GNUNET_TIME_Absolute start_time;
141
142 /**
143  * Handle for current transmission request.
144  */
145 static struct GNUNET_TRANSPORT_TransmitHandle *th;
146
147 /**
148  *
149  */
150 struct GNUNET_TRANSPORT_PeerIterateContext *pic;
151
152 /**
153  * Identity of the peer we transmit to / connect to.
154  * (equivalent to 'cpid' string).
155  */
156 static struct GNUNET_PeerIdentity pid;
157
158 /**
159  * Task scheduled for cleanup / termination of the process.
160  */
161 static GNUNET_SCHEDULER_TaskIdentifier end;
162
163 /**
164  * Task for operation timeout
165  */
166 static GNUNET_SCHEDULER_TaskIdentifier op_timeout;
167
168 /**
169  * Selected level of verbosity.
170  */
171 static int verbosity;
172
173 /**
174  * Resolver process handle.
175  */
176 struct GNUNET_OS_Process *resolver;
177
178 /**
179  * Number of tasks running that still need the resolver.
180  */
181 static unsigned int resolver_users;
182
183 /**
184  * Number of address resolutions pending
185  */
186 static unsigned int address_resolutions;
187
188 /**
189  * Address resolutions pending in progress
190  */
191 static unsigned int address_resolution_in_progress;
192
193 /**
194  * Context for a plugin test.
195  */
196 struct TestContext
197 {
198
199   /**
200    * Handle to the active NAT test.
201    */
202   struct GNUNET_NAT_Test *tst;
203
204   /**
205    * Task identifier for the timeout.
206    */
207   GNUNET_SCHEDULER_TaskIdentifier tsk;
208
209   /**
210    * Name of plugin under test.
211    */
212   const char *name;
213
214 };
215
216
217 /**
218  * Task run in monitor mode when the user presses CTRL-C to abort.
219  * Stops monitoring activity.
220  *
221  * @param cls the 'struct GNUNET_TRANSPORT_PeerIterateContext *'
222  * @param tc scheduler context
223  */
224 static void
225 shutdown_task (void *cls,
226                const struct GNUNET_SCHEDULER_TaskContext *tc)
227 {
228   struct GNUNET_TIME_Relative duration;
229   end = GNUNET_SCHEDULER_NO_TASK;
230   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
231   {
232       GNUNET_SCHEDULER_cancel (op_timeout);
233       op_timeout = GNUNET_SCHEDULER_NO_TASK;
234   }
235   if (NULL != tc_handle)
236   {
237       GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
238       tc_handle = NULL;
239   }
240   if (NULL != pic)
241   {
242       GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pic);
243       pic = NULL;
244   }
245   if (NULL != th)
246   {
247     GNUNET_TRANSPORT_notify_transmit_ready_cancel(th);
248     th = NULL;
249   }
250   if (NULL != handle)
251   {
252     GNUNET_TRANSPORT_disconnect(handle);
253     handle = NULL;
254   }
255   if (benchmark_send)
256   {
257     duration = GNUNET_TIME_absolute_get_duration (start_time);
258     FPRINTF (stdout, _("Transmitted %llu bytes/s (%llu bytes in %s)\n"),
259              1000LL * 1000LL * traffic_sent / (1 + duration.rel_value_us), traffic_sent,
260              GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
261   }
262   if (benchmark_receive)
263   {
264     duration = GNUNET_TIME_absolute_get_duration (start_time);
265     FPRINTF (stdout, _("Received %llu bytes/s (%llu bytes in %s)\n"),
266              1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us),
267              traffic_received,
268              GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
269   }
270 }
271
272
273 static struct ResolutionContext *rc_head;
274 static struct ResolutionContext *rc_tail;
275
276 struct ResolutionContext
277 {
278   struct ResolutionContext *next;
279   struct ResolutionContext *prev;
280   struct GNUNET_HELLO_Address *addrcp;
281   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
282   int printed;
283 };
284
285
286 static void
287 operation_timeout (void *cls,
288                    const struct GNUNET_SCHEDULER_TaskContext *tc)
289 {
290   struct ResolutionContext *cur;
291   struct ResolutionContext *next;
292   op_timeout = GNUNET_SCHEDULER_NO_TASK;
293   if ((try_connect) || (benchmark_send) ||
294                 (benchmark_receive))
295   {
296       FPRINTF (stdout, _("Failed to connect to `%s'\n"), GNUNET_i2s_full (&pid));
297       if (GNUNET_SCHEDULER_NO_TASK != end)
298         GNUNET_SCHEDULER_cancel (end);
299       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
300       ret = 1;
301       return;
302   }
303   if (iterate_connections)
304   {
305                 next = rc_head;
306                 while (NULL != (cur = next))
307                 {
308                                 next = cur->next;
309                                 FPRINTF (stdout, _("Failed to resolve address for peer `%s'\n"),
310                                                 GNUNET_i2s (&cur->addrcp->peer));
311
312                                 GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, cur);
313                                 GNUNET_TRANSPORT_address_to_string_cancel (cur->asc);
314                                 GNUNET_free (cur->addrcp);
315                                 GNUNET_free (cur);
316
317                 }
318                 FPRINTF (stdout, "%s", _("Failed to list connections, timeout occured\n"));
319       if (GNUNET_SCHEDULER_NO_TASK != end)
320         GNUNET_SCHEDULER_cancel (end);
321       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
322       ret = 1;
323       return;
324   }
325
326 }
327
328
329
330 /**
331  * Display the result of the test.
332  *
333  * @param tc test context
334  * @param result #GNUNET_YES on success
335  */
336 static void
337 display_test_result (struct TestContext *tc, int result)
338 {
339   if (GNUNET_YES != result)
340   {
341     FPRINTF (stderr, "Configuration for plugin `%s' did not work!\n", tc->name);
342   }
343   else
344   {
345     FPRINTF (stderr, "Configuration for plugin `%s' is working!\n", tc->name);
346   }
347   if (GNUNET_SCHEDULER_NO_TASK != tc->tsk)
348   {
349     GNUNET_SCHEDULER_cancel (tc->tsk);
350     tc->tsk = GNUNET_SCHEDULER_NO_TASK;
351   }
352   if (NULL != tc->tst)
353   {
354     GNUNET_NAT_test_stop (tc->tst);
355     tc->tst = NULL;
356   }
357   GNUNET_free (tc);
358   resolver_users--;
359   if ((0 == resolver_users) && (NULL != resolver))
360   {
361     GNUNET_break (0 == GNUNET_OS_process_kill (resolver, GNUNET_TERM_SIG));
362     GNUNET_OS_process_destroy (resolver);
363     resolver = NULL;
364   }
365 }
366
367
368 /**
369  * Function called by NAT on success.
370  * Clean up and update GUI (with success).
371  *
372  * @param cls test context
373  * @param success currently always #GNUNET_OK
374  */
375 static void
376 result_callback (void *cls, int success)
377 {
378   struct TestContext *tc = cls;
379
380   display_test_result (tc, success);
381 }
382
383
384 /**
385  * Function called if NAT failed to confirm success.
386  * Clean up and update GUI (with failure).
387  *
388  * @param cls test context
389  * @param tc scheduler callback
390  */
391 static void
392 fail_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
393 {
394   struct TestContext *tstc = cls;
395
396   tstc->tsk = GNUNET_SCHEDULER_NO_TASK;
397   display_test_result (tstc, GNUNET_NO);
398 }
399
400
401 /**
402  * Test our plugin's configuration (NAT traversal, etc.).
403  *
404  * @param cfg configuration to test
405  */
406 static void
407 do_test_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
408 {
409   char *plugins;
410   char *tok;
411   unsigned long long bnd_port;
412   unsigned long long adv_port;
413   struct TestContext *tc;
414   char *binary;
415
416   if (GNUNET_OK !=
417       GNUNET_CONFIGURATION_get_value_string (cfg, "transport", "plugins",
418                                              &plugins))
419   {
420     FPRINTF (stderr,
421              "%s",
422              _
423              ("No transport plugins configured, peer will never communicate\n"));
424     ret = 4;
425     return;
426   }
427   for (tok = strtok (plugins, " "); tok != NULL; tok = strtok (NULL, " "))
428   {
429     char section[12 + strlen (tok)];
430
431     GNUNET_snprintf (section, sizeof (section), "transport-%s", tok);
432     if (GNUNET_OK !=
433         GNUNET_CONFIGURATION_get_value_number (cfg, section, "PORT", &bnd_port))
434     {
435       FPRINTF (stderr,
436                _("No port configured for plugin `%s', cannot test it\n"), tok);
437       continue;
438     }
439     if (GNUNET_OK !=
440         GNUNET_CONFIGURATION_get_value_number (cfg, section, "ADVERTISED_PORT",
441                                                &adv_port))
442       adv_port = bnd_port;
443     if (NULL == resolver)
444     {
445       binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
446       resolver =
447           GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
448                                    binary,
449                                    "gnunet-service-resolver", NULL);
450       GNUNET_free (binary);
451     }
452     resolver_users++;
453     GNUNET_RESOLVER_connect (cfg);
454     tc = GNUNET_new (struct TestContext);
455     tc->name = GNUNET_strdup (tok);
456     tc->tst =
457         GNUNET_NAT_test_start (cfg,
458                                (0 ==
459                                 strcasecmp (tok,
460                                             "udp")) ? GNUNET_NO : GNUNET_YES,
461                                (uint16_t) bnd_port, (uint16_t) adv_port,
462                                &result_callback, tc);
463     if (NULL == tc->tst)
464     {
465       display_test_result (tc, GNUNET_SYSERR);
466       continue;
467     }
468     tc->tsk = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, tc);
469   }
470   GNUNET_free (plugins);
471 }
472
473
474 /**
475  * Function called to notify a client about the socket
476  * begin ready to queue more data.  @a buf will be
477  * NULL and @a size zero if the socket was closed for
478  * writing in the meantime.
479  *
480  * @param cls closure
481  * @param size number of bytes available in @a buf
482  * @param buf where the callee should write the message
483  * @return number of bytes written to @a buf
484  */
485 static size_t
486 transmit_data (void *cls, size_t size, void *buf)
487 {
488   struct GNUNET_MessageHeader *m = buf;
489
490   if ((NULL == buf) && (0 == size))
491   {
492     th = NULL;
493     return 0;
494   }
495
496   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
497   GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
498   m->size = ntohs (size);
499   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
500   memset (&m[1], 52, size - sizeof (struct GNUNET_MessageHeader));
501   traffic_sent += size;
502   th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, BLOCKSIZE * 1024, 0,
503                                                GNUNET_TIME_UNIT_FOREVER_REL,
504                                                &transmit_data, NULL);
505   if (verbosity > 0)
506     FPRINTF (stdout, _("Transmitting %u bytes to %s\n"), (unsigned int) size,
507              GNUNET_i2s (&pid));
508   return size;
509 }
510
511
512 /**
513  * Function called to notify transport users that another
514  * peer connected to us.
515  *
516  * @param cls closure
517  * @param peer the peer that connected
518  */
519 static void
520 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
521 {
522   if (0 != memcmp (&pid, peer, sizeof (struct GNUNET_PeerIdentity)))
523     return;
524   ret = 0;
525   if (try_connect)
526   {
527       /* all done, terminate instantly */
528       FPRINTF (stdout,
529                _("Successfully connected to `%s'\n"),
530                GNUNET_i2s_full (peer));
531       ret = 0;
532
533       if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
534       {
535         GNUNET_SCHEDULER_cancel (op_timeout);
536         op_timeout = GNUNET_SCHEDULER_NO_TASK;
537       }
538
539       if (GNUNET_SCHEDULER_NO_TASK != end)
540         GNUNET_SCHEDULER_cancel (end);
541       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
542       return;
543   }
544   if (benchmark_send)
545   {
546     if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
547     {
548       GNUNET_SCHEDULER_cancel (op_timeout);
549       op_timeout = GNUNET_SCHEDULER_NO_TASK;
550     }
551     if (verbosity > 0)
552       FPRINTF (stdout, _("Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"),
553           GNUNET_i2s (&pid), BLOCKSIZE);
554     start_time = GNUNET_TIME_absolute_get ();
555     if (NULL == th)
556       th = GNUNET_TRANSPORT_notify_transmit_ready (handle, peer,
557                                                    BLOCKSIZE * 1024, 0,
558                                                    GNUNET_TIME_UNIT_FOREVER_REL,
559                                                    &transmit_data, NULL);
560     else
561       GNUNET_break (0);
562     return;
563   }
564 }
565
566
567 /**
568  * Function called to notify transport users that another
569  * peer disconnected from us.
570  *
571  * @param cls closure
572  * @param peer the peer that disconnected
573  */
574 static void
575 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
576 {
577   if (0 != memcmp (&pid, peer, sizeof (struct GNUNET_PeerIdentity)))
578     return;
579
580   if (NULL != th)
581   {
582     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
583     th = NULL;
584   }
585   if (benchmark_send)
586   {
587       FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"), GNUNET_i2s (&pid));
588       if (GNUNET_SCHEDULER_NO_TASK != end)
589         GNUNET_SCHEDULER_cancel (end);
590       return;
591   }
592 }
593
594 /**
595  * Function called to notify transport users that another
596  * peer connected to us.
597  *
598  * @param cls closure
599  * @param peer the peer that connected
600  */
601 static void
602 monitor_notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
603 {
604   monitor_connect_counter ++;
605   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get();
606   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
607
608   FPRINTF (stdout,
609            _("%24s: %-17s %4s   (%u connections in total)\n"),
610            now_str,
611            _("Connected to"),
612            GNUNET_i2s (peer),
613            monitor_connect_counter);
614 }
615
616
617 /**
618  * Function called to notify transport users that another
619  * peer disconnected from us.
620  *
621  * @param cls closure
622  * @param peer the peer that disconnected
623  */
624 static void
625 monitor_notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
626 {
627   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get();
628   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
629
630   GNUNET_assert (monitor_connect_counter > 0);
631   monitor_connect_counter --;
632
633   FPRINTF (stdout,
634            _("%24s: %-17s %4s   (%u connections in total)\n"),
635            now_str,
636            _("Disconnected from"),
637            GNUNET_i2s (peer),
638            monitor_connect_counter);
639 }
640
641
642
643 /**
644  * Function called by the transport for each received message.
645  *
646  * @param cls closure
647  * @param peer (claimed) identity of the other peer
648  * @param message the message
649  */
650 static void
651 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
652                 const struct GNUNET_MessageHeader *message)
653 {
654   if (benchmark_receive)
655   {
656     if (GNUNET_MESSAGE_TYPE_DUMMY != ntohs (message->type))
657       return;
658     if (verbosity > 0)
659       FPRINTF (stdout,
660                _("Received %u bytes from %s\n"),
661                (unsigned int) ntohs (message->size), GNUNET_i2s (peer));
662
663     if (traffic_received == 0)
664       start_time = GNUNET_TIME_absolute_get ();
665     traffic_received += ntohs (message->size);
666     return;
667   }
668 }
669
670
671 static void
672 resolve_address (const struct GNUNET_HELLO_Address *address,
673                  int numeric);
674
675
676 static void
677 process_string (void *cls, const char *address)
678 {
679   struct ResolutionContext *rc = cls;
680   struct GNUNET_HELLO_Address *addrcp = rc->addrcp;
681
682   if (address != NULL)
683   {
684     FPRINTF (stdout,
685              _("Peer `%s': %s %s\n"),
686              GNUNET_i2s (&addrcp->peer),
687              addrcp->transport_name,
688              address);
689     rc->printed = GNUNET_YES;
690   }
691   else
692   {
693     /* done */
694     GNUNET_assert (address_resolutions > 0);
695     address_resolutions --;
696     if (GNUNET_NO == rc->printed)
697     {
698       if (numeric == GNUNET_NO)
699       {
700         resolve_address (rc->addrcp, GNUNET_YES ); /* Failed to resolve address, try numeric lookup */
701       }
702       else
703         FPRINTF (stdout,
704                  _("Peer `%s': %s <unable to resolve address>\n"),
705                  GNUNET_i2s (&addrcp->peer),
706                  addrcp->transport_name);
707     }
708     GNUNET_free (rc->addrcp);
709     GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, rc);
710     GNUNET_free (rc);
711     if ((0 == address_resolutions) && (iterate_connections))
712     {
713         if (GNUNET_SCHEDULER_NO_TASK != end)
714         {
715           GNUNET_SCHEDULER_cancel (end);
716           end = GNUNET_SCHEDULER_NO_TASK;
717         }
718         if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
719         {
720                 GNUNET_SCHEDULER_cancel (op_timeout);
721                 op_timeout = GNUNET_SCHEDULER_NO_TASK;
722         }
723         ret = 0;
724         end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
725     }
726   }
727 }
728
729
730 static void
731 resolve_address (const struct GNUNET_HELLO_Address *address,
732                  int numeric)
733 {
734   struct ResolutionContext *rc;
735
736   rc = GNUNET_new (struct ResolutionContext);
737   GNUNET_assert (NULL != rc);
738   GNUNET_CONTAINER_DLL_insert (rc_head, rc_tail, rc);
739   address_resolutions ++;
740
741   rc->addrcp = GNUNET_HELLO_address_copy(address);
742   rc->printed = GNUNET_NO;
743   /* Resolve address to string */
744   rc->asc = GNUNET_TRANSPORT_address_to_string (cfg, address, numeric,
745                                       RESOLUTION_TIMEOUT, &process_string,
746                                       rc);
747 }
748
749 /**
750  * Function to call with a binary address
751  *
752  * @param cls closure
753  * @param peer identity of the peer
754  * @param address binary address (NULL on disconnect)
755  */
756 static void
757 process_address (void *cls, const struct GNUNET_PeerIdentity *peer,
758                  const struct GNUNET_HELLO_Address *address)
759 {
760   if (peer == NULL)
761   {
762     /* done */
763     address_resolution_in_progress = GNUNET_NO;
764     pic = NULL;
765     if (GNUNET_SCHEDULER_NO_TASK != end)
766       GNUNET_SCHEDULER_cancel (end);
767     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
768     return;
769   }
770   if (address == NULL)
771   {
772     FPRINTF (stdout, _("Peer `%s' disconnected\n"), GNUNET_i2s (peer));
773     return;
774   }
775
776   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
777         GNUNET_SCHEDULER_cancel (op_timeout);
778   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
779                                              &operation_timeout, NULL);
780
781   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received address for peer `%s': %s\n",
782                 GNUNET_i2s (peer), address->transport_name);
783   resolve_address (address, numeric);
784 }
785
786
787 static void
788 try_connect_cb (void *cls,
789                 const int result)
790 {
791   static int retries = 0;
792   if (GNUNET_OK == result)
793   {
794       tc_handle = NULL;
795       return;
796   }
797   retries ++;
798   if (retries < 10)
799     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb, NULL);
800   else
801   {
802     FPRINTF (stderr, "%s", _("Failed to send connect request to transport service\n"));
803     if (GNUNET_SCHEDULER_NO_TASK != end)
804       GNUNET_SCHEDULER_cancel (end);
805     ret = 1;
806     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
807     return;
808   }
809 }
810
811
812 /**
813  * Function called with the result of the check if the 'transport'
814  * service is running.
815  *
816  * @param cls closure with our configuration
817  * @param result #GNUNET_YES if transport is running
818  */
819 static void
820 testservice_task (void *cls,
821                   int result)
822 {
823   int counter = 0;
824   ret = 1;
825
826   if (GNUNET_YES != result)
827   {
828     FPRINTF (stderr,
829              _("Service `%s' is not running\n"), "transport");
830     return;
831   }
832
833   if ( (NULL != cpid) &&
834        (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (cpid,
835                                                                     strlen (cpid),
836                                                                     &pid.public_key)))
837   {
838     FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
839     return;
840   }
841
842   counter = benchmark_send + benchmark_receive + iterate_connections +
843             monitor_connections + monitor_connects + try_connect;
844
845   if (1 < counter)
846   {
847     FPRINTF (stderr,
848              _("Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s\n"),
849              "connect", "benchmark send", "benchmark receive", "information", "monitor", "events");
850     return;
851   }
852   if (0 == counter)
853   {
854     FPRINTF (stderr,
855              _("No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s\n"),
856              "connect", "benchmark send", "benchmark receive", "information", "monitor", "events");
857     return;
858   }
859
860   if (try_connect) /* -C: Connect to peer */
861   {
862     if (NULL == cpid)
863     {
864       FPRINTF (stderr,
865                _("Option `%s' makes no sense without option `%s'.\n"),
866                "-C", "-p");
867       ret = 1;
868       return;
869     }
870     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL,
871                                        &notify_receive,
872                                        &notify_connect,
873                                        &notify_disconnect);
874     if (NULL == handle)
875     {
876         FPRINTF (stderr,
877                  "%s",
878                  _("Failed to connect to transport service\n"));
879         ret = 1;
880         return;
881     }
882     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb, NULL);
883     if (NULL == tc_handle)
884     {
885       FPRINTF (stderr,
886                "%s",
887                _("Failed to send request to transport service\n"));
888       ret = 1;
889       return;
890     }
891     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
892                                                &operation_timeout, NULL);
893
894   }
895   else if (benchmark_send) /* -s: Benchmark sending */
896   {
897     if (NULL == cpid)
898     {
899       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
900                "-s", "-p");
901       ret = 1;
902       return;
903     }
904     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL,
905                                        &notify_receive,
906                                        &notify_connect,
907                                        &notify_disconnect);
908     if (NULL == handle)
909     {
910         FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
911         ret = 1;
912         return;
913     }
914     tc_handle =  GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb, NULL);
915     if (NULL == tc_handle)
916     {
917         FPRINTF (stderr, "%s", _("Failed to send request to transport service\n"));
918         ret = 1;
919         return;
920     }
921     start_time = GNUNET_TIME_absolute_get ();
922     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
923                                                &operation_timeout, NULL);
924   }
925   else if (benchmark_receive) /* -b: Benchmark receiving */
926   {
927     handle =
928         GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
929                                   NULL, NULL);
930     if (NULL == handle)
931     {
932         FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
933         ret = 1;
934         return;
935     }
936     if (verbosity > 0)
937       FPRINTF (stdout, "%s", _("Starting to receive benchmark data\n"));
938     start_time = GNUNET_TIME_absolute_get ();
939
940   }
941   else if (iterate_connections) /* -i: List all active addresses once */
942   {
943     address_resolution_in_progress = GNUNET_YES;
944     pic = GNUNET_TRANSPORT_peer_get_active_addresses (cfg,
945                                                 (NULL == cpid) ? NULL : &pid,
946                                                 GNUNET_YES,
947                                                 TIMEOUT,
948                                                 &process_address, (void *) cfg);
949     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
950                                                &operation_timeout, NULL);
951   }
952   else if (monitor_connections) /* -m: List all active addresses continously */
953   {
954     address_resolution_in_progress = GNUNET_YES;
955     pic = GNUNET_TRANSPORT_peer_get_active_addresses (cfg,
956                                                 (NULL == cpid) ? NULL : &pid,
957                                                 GNUNET_NO,
958                                                 TIMEOUT,
959                                                 &process_address, (void *) cfg);
960   }
961   else if (monitor_connects) /* -e : Monitor (dis)connect events continously */
962   {
963     monitor_connect_counter = 0;
964     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL,
965                                        &monitor_notify_connect,
966                                        &monitor_notify_disconnect);
967     if (NULL == handle)
968     {
969       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
970       ret = 1;
971       return;
972     }
973     ret = 0;
974   }
975   else
976   {
977     GNUNET_break (0);
978     return;
979   }
980
981   end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
982                                       &shutdown_task,
983                                       NULL);
984
985 }
986
987
988 /**
989  * Main function that will be run by the scheduler.
990  *
991  * @param cls closure
992  * @param args remaining command-line arguments
993  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
994  * @param mycfg configuration
995  */
996 static void
997 run (void *cls, char *const *args, const char *cfgfile,
998      const struct GNUNET_CONFIGURATION_Handle *mycfg)
999 {
1000   cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
1001   if (test_configuration)
1002   {
1003     do_test_configuration (cfg);
1004     return;
1005   }
1006   GNUNET_CLIENT_service_test ("transport", cfg,
1007                               GNUNET_TIME_UNIT_SECONDS,
1008                               &testservice_task,
1009                               (void *) cfg);
1010 }
1011
1012
1013 int
1014 main (int argc, char *const *argv)
1015 {
1016   int res;
1017   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1018     {'b', "benchmark", NULL,
1019      gettext_noop ("measure how fast we are receiving data from all peers (until CTRL-C)"),
1020      0, &GNUNET_GETOPT_set_one, &benchmark_receive},
1021     {'C', "connect", NULL,
1022      gettext_noop ("connect to a peer"),
1023      0, &GNUNET_GETOPT_set_one, &try_connect},
1024     {'i', "information", NULL,
1025      gettext_noop ("provide information about all current connections (once)"),
1026      0, &GNUNET_GETOPT_set_one, &iterate_connections},
1027     {'m', "monitor", NULL,
1028      gettext_noop ("provide information about all current connections (continuously)"),
1029      0, &GNUNET_GETOPT_set_one, &monitor_connections},
1030     {'e', "events", NULL,
1031      gettext_noop ("provide information about all connects and disconnect events (continuously)"),
1032      0, &GNUNET_GETOPT_set_one, &monitor_connects},
1033     {'n', "numeric", NULL,
1034      gettext_noop ("do not resolve hostnames"),
1035      0, &GNUNET_GETOPT_set_one, &numeric},
1036      {'p', "peer", "PEER",
1037       gettext_noop ("peer identity"),
1038       1, &GNUNET_GETOPT_set_string, &cpid},
1039     {'s', "send", NULL,
1040      gettext_noop
1041      ("send data for benchmarking to the other peer (until CTRL-C)"),
1042      0, &GNUNET_GETOPT_set_one, &benchmark_send},
1043     {'t', "test", NULL,
1044      gettext_noop ("test transport configuration (involves external server)"),
1045      0, &GNUNET_GETOPT_set_one, &test_configuration},
1046     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
1047     GNUNET_GETOPT_OPTION_END
1048   };
1049
1050   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1051     return 2;
1052
1053   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-transport",
1054                               gettext_noop
1055                               ("Direct access to transport service."), options,
1056                               &run, NULL);
1057   GNUNET_free ((void *) argv);
1058   if (GNUNET_OK == res)
1059     return ret;
1060   return 1;
1061 }
1062
1063
1064 /* end of gnunet-transport.c */