- delay tunnel destruction 1 min to avoid rekeying
[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  * @param emsg error message, NULL on success
375  */
376 static void
377 result_callback (void *cls,
378                  int success,
379                  const char *emsg)
380 {
381   struct TestContext *tc = cls;
382
383   display_test_result (tc, success);
384 }
385
386
387 /**
388  * Function called if NAT failed to confirm success.
389  * Clean up and update GUI (with failure).
390  *
391  * @param cls test context
392  * @param tc scheduler callback
393  */
394 static void
395 fail_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
396 {
397   struct TestContext *tstc = cls;
398
399   tstc->tsk = GNUNET_SCHEDULER_NO_TASK;
400   display_test_result (tstc, GNUNET_NO);
401 }
402
403
404 /**
405  * Test our plugin's configuration (NAT traversal, etc.).
406  *
407  * @param cfg configuration to test
408  */
409 static void
410 do_test_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
411 {
412   char *plugins;
413   char *tok;
414   unsigned long long bnd_port;
415   unsigned long long adv_port;
416   struct TestContext *tc;
417   char *binary;
418
419   if (GNUNET_OK !=
420       GNUNET_CONFIGURATION_get_value_string (cfg, "transport", "plugins",
421                                              &plugins))
422   {
423     FPRINTF (stderr,
424              "%s",
425              _
426              ("No transport plugins configured, peer will never communicate\n"));
427     ret = 4;
428     return;
429   }
430   for (tok = strtok (plugins, " "); tok != NULL; tok = strtok (NULL, " "))
431   {
432     char section[12 + strlen (tok)];
433
434     GNUNET_snprintf (section, sizeof (section), "transport-%s", tok);
435     if (GNUNET_OK !=
436         GNUNET_CONFIGURATION_get_value_number (cfg, section, "PORT", &bnd_port))
437     {
438       FPRINTF (stderr,
439                _("No port configured for plugin `%s', cannot test it\n"), tok);
440       continue;
441     }
442     if (GNUNET_OK !=
443         GNUNET_CONFIGURATION_get_value_number (cfg, section, "ADVERTISED_PORT",
444                                                &adv_port))
445       adv_port = bnd_port;
446     if (NULL == resolver)
447     {
448       binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
449       resolver =
450           GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
451                                    binary,
452                                    "gnunet-service-resolver", NULL);
453       GNUNET_free (binary);
454     }
455     resolver_users++;
456     GNUNET_RESOLVER_connect (cfg);
457     tc = GNUNET_new (struct TestContext);
458     tc->name = GNUNET_strdup (tok);
459     tc->tst =
460         GNUNET_NAT_test_start (cfg,
461                                (0 ==
462                                 strcasecmp (tok,
463                                             "udp")) ? GNUNET_NO : GNUNET_YES,
464                                (uint16_t) bnd_port, (uint16_t) adv_port,
465                                &result_callback, tc);
466     if (NULL == tc->tst)
467     {
468       display_test_result (tc, GNUNET_SYSERR);
469       continue;
470     }
471     tc->tsk = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, tc);
472   }
473   GNUNET_free (plugins);
474 }
475
476
477 /**
478  * Function called to notify a client about the socket
479  * begin ready to queue more data.  @a buf will be
480  * NULL and @a size zero if the socket was closed for
481  * writing in the meantime.
482  *
483  * @param cls closure
484  * @param size number of bytes available in @a buf
485  * @param buf where the callee should write the message
486  * @return number of bytes written to @a buf
487  */
488 static size_t
489 transmit_data (void *cls, size_t size, void *buf)
490 {
491   struct GNUNET_MessageHeader *m = buf;
492
493   if ((NULL == buf) || (0 == size))
494   {
495     th = NULL;
496     return 0;
497   }
498
499   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
500   GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
501   m->size = ntohs (size);
502   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
503   memset (&m[1], 52, size - sizeof (struct GNUNET_MessageHeader));
504   traffic_sent += size;
505   th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, BLOCKSIZE * 1024, 0,
506                                                GNUNET_TIME_UNIT_FOREVER_REL,
507                                                &transmit_data, NULL);
508   if (verbosity > 0)
509     FPRINTF (stdout, _("Transmitting %u bytes to %s\n"), (unsigned int) size,
510              GNUNET_i2s (&pid));
511   return size;
512 }
513
514
515 /**
516  * Function called to notify transport users that another
517  * peer connected to us.
518  *
519  * @param cls closure
520  * @param peer the peer that connected
521  */
522 static void
523 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
524 {
525   if (0 != memcmp (&pid, peer, sizeof (struct GNUNET_PeerIdentity)))
526     return;
527   ret = 0;
528   if (try_connect)
529   {
530       /* all done, terminate instantly */
531       FPRINTF (stdout,
532                _("Successfully connected to `%s'\n"),
533                GNUNET_i2s_full (peer));
534       ret = 0;
535
536       if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
537       {
538         GNUNET_SCHEDULER_cancel (op_timeout);
539         op_timeout = GNUNET_SCHEDULER_NO_TASK;
540       }
541
542       if (GNUNET_SCHEDULER_NO_TASK != end)
543         GNUNET_SCHEDULER_cancel (end);
544       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
545       return;
546   }
547   if (benchmark_send)
548   {
549     if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
550     {
551       GNUNET_SCHEDULER_cancel (op_timeout);
552       op_timeout = GNUNET_SCHEDULER_NO_TASK;
553     }
554     if (verbosity > 0)
555       FPRINTF (stdout, _("Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"),
556           GNUNET_i2s (&pid), BLOCKSIZE);
557     start_time = GNUNET_TIME_absolute_get ();
558     if (NULL == th)
559       th = GNUNET_TRANSPORT_notify_transmit_ready (handle, peer,
560                                                    BLOCKSIZE * 1024, 0,
561                                                    GNUNET_TIME_UNIT_FOREVER_REL,
562                                                    &transmit_data, NULL);
563     else
564       GNUNET_break (0);
565     return;
566   }
567 }
568
569
570 /**
571  * Function called to notify transport users that another
572  * peer disconnected from us.
573  *
574  * @param cls closure
575  * @param peer the peer that disconnected
576  */
577 static void
578 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
579 {
580   if (0 != memcmp (&pid, peer, sizeof (struct GNUNET_PeerIdentity)))
581     return;
582
583   if (NULL != th)
584   {
585     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
586     th = NULL;
587   }
588   if (benchmark_send)
589   {
590       FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"), GNUNET_i2s (&pid));
591       if (GNUNET_SCHEDULER_NO_TASK != end)
592         GNUNET_SCHEDULER_cancel (end);
593       return;
594   }
595 }
596
597 /**
598  * Function called to notify transport users that another
599  * peer connected to us.
600  *
601  * @param cls closure
602  * @param peer the peer that connected
603  */
604 static void
605 monitor_notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
606 {
607   monitor_connect_counter ++;
608   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get();
609   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
610
611   FPRINTF (stdout,
612            _("%24s: %-17s %4s   (%u connections in total)\n"),
613            now_str,
614            _("Connected to"),
615            GNUNET_i2s (peer),
616            monitor_connect_counter);
617 }
618
619
620 /**
621  * Function called to notify transport users that another
622  * peer disconnected from us.
623  *
624  * @param cls closure
625  * @param peer the peer that disconnected
626  */
627 static void
628 monitor_notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
629 {
630   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get();
631   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
632
633   GNUNET_assert (monitor_connect_counter > 0);
634   monitor_connect_counter --;
635
636   FPRINTF (stdout,
637            _("%24s: %-17s %4s   (%u connections in total)\n"),
638            now_str,
639            _("Disconnected from"),
640            GNUNET_i2s (peer),
641            monitor_connect_counter);
642 }
643
644
645
646 /**
647  * Function called by the transport for each received message.
648  *
649  * @param cls closure
650  * @param peer (claimed) identity of the other peer
651  * @param message the message
652  */
653 static void
654 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
655                 const struct GNUNET_MessageHeader *message)
656 {
657   if (benchmark_receive)
658   {
659     if (GNUNET_MESSAGE_TYPE_DUMMY != ntohs (message->type))
660       return;
661     if (verbosity > 0)
662       FPRINTF (stdout,
663                _("Received %u bytes from %s\n"),
664                (unsigned int) ntohs (message->size), GNUNET_i2s (peer));
665
666     if (traffic_received == 0)
667       start_time = GNUNET_TIME_absolute_get ();
668     traffic_received += ntohs (message->size);
669     return;
670   }
671 }
672
673
674 static void
675 resolve_address (const struct GNUNET_HELLO_Address *address,
676                  int numeric);
677
678
679 static void
680 process_string (void *cls, const char *address)
681 {
682   struct ResolutionContext *rc = cls;
683   struct GNUNET_HELLO_Address *addrcp = rc->addrcp;
684
685   if (address != NULL)
686   {
687     FPRINTF (stdout,
688              _("Peer `%s': %s %s\n"),
689              GNUNET_i2s (&addrcp->peer),
690              addrcp->transport_name,
691              address);
692     rc->printed = GNUNET_YES;
693   }
694   else
695   {
696     /* done */
697     GNUNET_assert (address_resolutions > 0);
698     address_resolutions --;
699     if (GNUNET_NO == rc->printed)
700     {
701       if (numeric == GNUNET_NO)
702       {
703         resolve_address (rc->addrcp, GNUNET_YES ); /* Failed to resolve address, try numeric lookup */
704       }
705       else
706         FPRINTF (stdout,
707                  _("Peer `%s': %s <unable to resolve address>\n"),
708                  GNUNET_i2s (&addrcp->peer),
709                  addrcp->transport_name);
710     }
711     GNUNET_free (rc->addrcp);
712     GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, rc);
713     GNUNET_free (rc);
714     if ((0 == address_resolutions) && (iterate_connections))
715     {
716         if (GNUNET_SCHEDULER_NO_TASK != end)
717         {
718           GNUNET_SCHEDULER_cancel (end);
719           end = GNUNET_SCHEDULER_NO_TASK;
720         }
721         if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
722         {
723                 GNUNET_SCHEDULER_cancel (op_timeout);
724                 op_timeout = GNUNET_SCHEDULER_NO_TASK;
725         }
726         ret = 0;
727         end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
728     }
729   }
730 }
731
732
733 static void
734 resolve_address (const struct GNUNET_HELLO_Address *address,
735                  int numeric)
736 {
737   struct ResolutionContext *rc;
738
739   rc = GNUNET_new (struct ResolutionContext);
740   GNUNET_assert (NULL != rc);
741   GNUNET_CONTAINER_DLL_insert (rc_head, rc_tail, rc);
742   address_resolutions ++;
743
744   rc->addrcp = GNUNET_HELLO_address_copy(address);
745   rc->printed = GNUNET_NO;
746   /* Resolve address to string */
747   rc->asc = GNUNET_TRANSPORT_address_to_string (cfg, address, numeric,
748                                       RESOLUTION_TIMEOUT, &process_string,
749                                       rc);
750 }
751
752 /**
753  * Function to call with a binary address
754  *
755  * @param cls closure
756  * @param peer identity of the peer
757  * @param address binary address (NULL on disconnect)
758  */
759 static void
760 process_address (void *cls, const struct GNUNET_PeerIdentity *peer,
761                  const struct GNUNET_HELLO_Address *address)
762 {
763   if (peer == NULL)
764   {
765     /* done */
766     address_resolution_in_progress = GNUNET_NO;
767     pic = NULL;
768     if (GNUNET_SCHEDULER_NO_TASK != end)
769       GNUNET_SCHEDULER_cancel (end);
770     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
771     return;
772   }
773   if (address == NULL)
774   {
775     FPRINTF (stdout, _("Peer `%s' disconnected\n"), GNUNET_i2s (peer));
776     return;
777   }
778
779   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
780         GNUNET_SCHEDULER_cancel (op_timeout);
781   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
782                                              &operation_timeout, NULL);
783
784   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received address for peer `%s': %s\n",
785                 GNUNET_i2s (peer), address->transport_name);
786   resolve_address (address, numeric);
787 }
788
789
790 static void
791 try_connect_cb (void *cls,
792                 const int result)
793 {
794   static int retries = 0;
795   if (GNUNET_OK == result)
796   {
797       tc_handle = NULL;
798       return;
799   }
800   retries ++;
801   if (retries < 10)
802     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb, NULL);
803   else
804   {
805     FPRINTF (stderr, "%s", _("Failed to send connect request to transport service\n"));
806     if (GNUNET_SCHEDULER_NO_TASK != end)
807       GNUNET_SCHEDULER_cancel (end);
808     ret = 1;
809     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
810     return;
811   }
812 }
813
814
815 /**
816  * Function called with the result of the check if the 'transport'
817  * service is running.
818  *
819  * @param cls closure with our configuration
820  * @param result #GNUNET_YES if transport is running
821  */
822 static void
823 testservice_task (void *cls,
824                   int result)
825 {
826   int counter = 0;
827   ret = 1;
828
829   if (GNUNET_YES != result)
830   {
831     FPRINTF (stderr,
832              _("Service `%s' is not running\n"), "transport");
833     return;
834   }
835
836   if ( (NULL != cpid) &&
837        (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (cpid,
838                                                                     strlen (cpid),
839                                                                     &pid.public_key)))
840   {
841     FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
842     return;
843   }
844
845   counter = benchmark_send + benchmark_receive + iterate_connections +
846             monitor_connections + monitor_connects + try_connect;
847
848   if (1 < counter)
849   {
850     FPRINTF (stderr,
851              _("Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s\n"),
852              "connect", "benchmark send", "benchmark receive", "information", "monitor", "events");
853     return;
854   }
855   if (0 == counter)
856   {
857     FPRINTF (stderr,
858              _("No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s\n"),
859              "connect", "benchmark send", "benchmark receive", "information", "monitor", "events");
860     return;
861   }
862
863   if (try_connect) /* -C: Connect to peer */
864   {
865     if (NULL == cpid)
866     {
867       FPRINTF (stderr,
868                _("Option `%s' makes no sense without option `%s'.\n"),
869                "-C", "-p");
870       ret = 1;
871       return;
872     }
873     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL,
874                                        &notify_receive,
875                                        &notify_connect,
876                                        &notify_disconnect);
877     if (NULL == handle)
878     {
879         FPRINTF (stderr,
880                  "%s",
881                  _("Failed to connect to transport service\n"));
882         ret = 1;
883         return;
884     }
885     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb, NULL);
886     if (NULL == tc_handle)
887     {
888       FPRINTF (stderr,
889                "%s",
890                _("Failed to send request to transport service\n"));
891       ret = 1;
892       return;
893     }
894     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
895                                                &operation_timeout, NULL);
896
897   }
898   else if (benchmark_send) /* -s: Benchmark sending */
899   {
900     if (NULL == cpid)
901     {
902       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
903                "-s", "-p");
904       ret = 1;
905       return;
906     }
907     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL,
908                                        &notify_receive,
909                                        &notify_connect,
910                                        &notify_disconnect);
911     if (NULL == handle)
912     {
913         FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
914         ret = 1;
915         return;
916     }
917     tc_handle =  GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb, NULL);
918     if (NULL == tc_handle)
919     {
920         FPRINTF (stderr, "%s", _("Failed to send request to transport service\n"));
921         ret = 1;
922         return;
923     }
924     start_time = GNUNET_TIME_absolute_get ();
925     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
926                                                &operation_timeout, NULL);
927   }
928   else if (benchmark_receive) /* -b: Benchmark receiving */
929   {
930     handle =
931         GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
932                                   NULL, NULL);
933     if (NULL == handle)
934     {
935         FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
936         ret = 1;
937         return;
938     }
939     if (verbosity > 0)
940       FPRINTF (stdout, "%s", _("Starting to receive benchmark data\n"));
941     start_time = GNUNET_TIME_absolute_get ();
942
943   }
944   else if (iterate_connections) /* -i: List all active addresses once */
945   {
946     address_resolution_in_progress = GNUNET_YES;
947     pic = GNUNET_TRANSPORT_peer_get_active_addresses (cfg,
948                                                 (NULL == cpid) ? NULL : &pid,
949                                                 GNUNET_YES,
950                                                 TIMEOUT,
951                                                 &process_address, (void *) cfg);
952     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
953                                                &operation_timeout, NULL);
954   }
955   else if (monitor_connections) /* -m: List all active addresses continously */
956   {
957     address_resolution_in_progress = GNUNET_YES;
958     pic = GNUNET_TRANSPORT_peer_get_active_addresses (cfg,
959                                                 (NULL == cpid) ? NULL : &pid,
960                                                 GNUNET_NO,
961                                                 TIMEOUT,
962                                                 &process_address, (void *) cfg);
963   }
964   else if (monitor_connects) /* -e : Monitor (dis)connect events continously */
965   {
966     monitor_connect_counter = 0;
967     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL,
968                                        &monitor_notify_connect,
969                                        &monitor_notify_disconnect);
970     if (NULL == handle)
971     {
972       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
973       ret = 1;
974       return;
975     }
976     ret = 0;
977   }
978   else
979   {
980     GNUNET_break (0);
981     return;
982   }
983
984   end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
985                                       &shutdown_task,
986                                       NULL);
987
988 }
989
990
991 /**
992  * Main function that will be run by the scheduler.
993  *
994  * @param cls closure
995  * @param args remaining command-line arguments
996  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
997  * @param mycfg configuration
998  */
999 static void
1000 run (void *cls, char *const *args, const char *cfgfile,
1001      const struct GNUNET_CONFIGURATION_Handle *mycfg)
1002 {
1003   cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
1004   if (test_configuration)
1005   {
1006     do_test_configuration (cfg);
1007     return;
1008   }
1009   GNUNET_CLIENT_service_test ("transport", cfg,
1010                               GNUNET_TIME_UNIT_SECONDS,
1011                               &testservice_task,
1012                               (void *) cfg);
1013 }
1014
1015
1016 int
1017 main (int argc, char *const *argv)
1018 {
1019   int res;
1020   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1021     {'b', "benchmark", NULL,
1022      gettext_noop ("measure how fast we are receiving data from all peers (until CTRL-C)"),
1023      0, &GNUNET_GETOPT_set_one, &benchmark_receive},
1024     {'C', "connect", NULL,
1025      gettext_noop ("connect to a peer"),
1026      0, &GNUNET_GETOPT_set_one, &try_connect},
1027     {'i', "information", NULL,
1028      gettext_noop ("provide information about all current connections (once)"),
1029      0, &GNUNET_GETOPT_set_one, &iterate_connections},
1030     {'m', "monitor", NULL,
1031      gettext_noop ("provide information about all current connections (continuously)"),
1032      0, &GNUNET_GETOPT_set_one, &monitor_connections},
1033     {'e', "events", NULL,
1034      gettext_noop ("provide information about all connects and disconnect events (continuously)"),
1035      0, &GNUNET_GETOPT_set_one, &monitor_connects},
1036     {'n', "numeric", NULL,
1037      gettext_noop ("do not resolve hostnames"),
1038      0, &GNUNET_GETOPT_set_one, &numeric},
1039      {'p', "peer", "PEER",
1040       gettext_noop ("peer identity"),
1041       1, &GNUNET_GETOPT_set_string, &cpid},
1042     {'s', "send", NULL,
1043      gettext_noop
1044      ("send data for benchmarking to the other peer (until CTRL-C)"),
1045      0, &GNUNET_GETOPT_set_one, &benchmark_send},
1046     {'t', "test", NULL,
1047      gettext_noop ("test transport configuration (involves external server)"),
1048      0, &GNUNET_GETOPT_set_one, &test_configuration},
1049     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
1050     GNUNET_GETOPT_OPTION_END
1051   };
1052
1053   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1054     return 2;
1055
1056   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-transport",
1057                               gettext_noop
1058                               ("Direct access to transport service."), options,
1059                               &run, NULL);
1060   GNUNET_free ((void *) argv);
1061   if (GNUNET_OK == res)
1062     return ret;
1063   return 1;
1064 }
1065
1066
1067 /* end of gnunet-transport.c */