-simplify logic
[oweals/gnunet.git] / src / transport / gnunet-transport.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2014 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 #include "platform.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_resolver_service.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_nat_lib.h"
36
37 /**
38  * How long do we wait for the NAT test to report success?
39  * Should match NAT_SERVER_TIMEOUT in 'nat_test.c'.
40  */
41 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
42
43 /**
44  * Timeout for a name resolution
45  */
46 #define RESOLUTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
47
48 /**
49  * Timeout for an operations
50  */
51 #define OP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
52
53
54 /**
55  * Context to store name resolutions for valiation
56  */
57 struct ValidationResolutionContext
58 {
59   /**
60    * Next in DLL
61    */
62   struct ValidationResolutionContext *next;
63
64   /**
65    * Previous in DLL
66    */
67   struct ValidationResolutionContext *prev;
68
69   /**
70    * Address to resolve
71    */
72   struct GNUNET_HELLO_Address *addrcp;
73
74   /**
75    * Time of last validation
76    */
77   struct GNUNET_TIME_Absolute last_validation;
78
79   /**
80    * Address is valid until
81    */
82   struct GNUNET_TIME_Absolute valid_until;
83
84   /**
85    * Time of next validation
86    */
87   struct GNUNET_TIME_Absolute next_validation;
88
89   /**
90    * state of validation process
91    */
92   enum GNUNET_TRANSPORT_ValidationState state;
93
94   /**
95    * Tranport conversion handle
96    */
97   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
98
99   /**
100    * plugin name
101    */
102   char *transport;
103
104   /**
105    * was the entry printed
106    */
107   int printed;
108 };
109
110 /**
111  * Struct to store information about peers in monitor mode
112  */
113 struct MonitoredPeer
114 {
115   /**
116    * State of the peer
117    */
118   enum GNUNET_TRANSPORT_PeerState state;
119
120   /**
121    * Timeout
122    */
123   struct GNUNET_TIME_Absolute state_timeout;
124
125   /**
126    * The address to convert
127    */
128   struct GNUNET_HELLO_Address *address;
129 };
130
131 /**
132  * Context to store name resolutions for valiation
133  */
134 struct PeerResolutionContext
135 {
136   /**
137    * Next in DLL
138    */
139   struct PeerResolutionContext *next;
140
141   /**
142    * Prev in DLL
143    */
144   struct PeerResolutionContext *prev;
145
146   /**
147    * address to resolve
148    */
149   struct GNUNET_HELLO_Address *addrcp;
150
151   /**
152    * transport conversiion context
153    */
154   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
155
156   /**
157    * peer state
158    */
159   enum GNUNET_TRANSPORT_PeerState state;
160
161   /**
162    * state timeout
163    */
164   struct GNUNET_TIME_Absolute state_timeout;
165
166   /**
167    * transport plugin
168    */
169   char *transport;
170
171   /**
172    * was the entry printed
173    */
174   int printed;
175 };
176
177
178 /**
179  * Context for a plugin test.
180  */
181 struct TestContext
182 {
183   /**
184    * Previous in DLL
185    */
186   struct TestContext *prev;
187
188   /**
189    * Next in DLL
190    */
191   struct TestContext *next;
192
193   /**
194    * Handle to the active NAT test.
195    */
196   struct GNUNET_NAT_Test *tst;
197
198   /**
199    * Task identifier for the timeout.
200    */
201   struct GNUNET_SCHEDULER_Task * tsk;
202
203   /**
204    * Name of plugin under test.
205    */
206   char *name;
207
208   /**
209    * Bound port
210    */
211   unsigned long long bnd_port;
212
213   /**
214    * Advertised ports
215    */
216   unsigned long long adv_port;
217
218 };
219
220
221 /**
222  *
223  */
224 enum TestResult
225 {
226   /**
227    * NAT returned success
228    */
229   NAT_TEST_SUCCESS = GNUNET_OK,
230
231   /**
232    * NAT returned failure
233    */
234   NAT_TEST_FAIL = GNUNET_NO,
235
236   /**
237    * NAT returned failure while running test
238    */
239   NAT_TEST_INTERNAL_FAIL = GNUNET_SYSERR,
240
241   /**
242    * We could not start the test
243    */
244   NAT_TEST_FAILED_TO_START = 2,
245
246   /**
247    * We had a timeout while running the test
248    */
249   NAT_TEST_TIMEOUT = 3
250 };
251
252
253 /**
254  * Benchmarking block size in KB
255  */
256 #define BLOCKSIZE 4
257
258 /**
259  * Which peer should we connect to?
260  */
261 static char *cpid;
262
263 /**
264  * Handle to transport service.
265  */
266 static struct GNUNET_TRANSPORT_Handle *handle;
267
268 /**
269  * Configuration handle
270  */
271 static struct GNUNET_CONFIGURATION_Handle *cfg;
272
273 /**
274  * Try connect handle
275  */
276 struct GNUNET_TRANSPORT_TryConnectHandle *tc_handle;
277
278 /**
279  * Try disconnect handle
280  */
281 struct GNUNET_TRANSPORT_TryDisconnectHandle *td_handle;
282
283 /**
284  * Option -s.
285  */
286 static int benchmark_send;
287
288 /**
289  * Option -b.
290  */
291 static int benchmark_receive;
292
293 /**
294  * Option -l.
295  */
296 static int benchmark_receive;
297
298 /**
299  * Option -i.
300  */
301 static int iterate_connections;
302
303 /**
304  * Option -d.
305  */
306 static int iterate_validation;
307
308 /**
309  * Option -a.
310  */
311 static int iterate_all;
312
313 /**
314  * Option -t.
315  */
316 static int test_configuration;
317
318 /**
319  * Option -c.
320  */
321 static int monitor_connects;
322
323 /**
324  * Option -m.
325  */
326 static int monitor_connections;
327
328 /**
329  * Option -f.
330  */
331 static int monitor_validation;
332
333 /**
334  * Option -P.
335  */
336 static int monitor_plugins;
337
338 /**
339  * Option -C.
340  */
341 static int try_connect;
342
343 /**
344  * Option -D.
345  */
346 static int try_disconnect;
347
348 /**
349  * Option -n.
350  */
351 static int numeric;
352
353 /**
354  * Global return value (0 success).
355  */
356 static int ret;
357
358 /**
359  * Current number of connections in monitor mode
360  */
361 static int monitor_connect_counter;
362
363 /**
364  * Number of bytes of traffic we received so far.
365  */
366 static unsigned long long traffic_received;
367
368 /**
369  * Number of bytes of traffic we sent so far.
370  */
371 static unsigned long long traffic_sent;
372
373 /**
374  * Starting time of transmitting/receiving data.
375  */
376 static struct GNUNET_TIME_Absolute start_time;
377
378 /**
379  * Handle for current transmission request.
380  */
381 static struct GNUNET_TRANSPORT_TransmitHandle *th;
382
383 /**
384  * Map storing information about monitored peers
385  */
386 static struct GNUNET_CONTAINER_MultiPeerMap *monitored_peers;
387
388 /**
389  * Map storing information about monitored plugins's sessions.
390  */
391 static struct GNUNET_CONTAINER_MultiPeerMap *monitored_plugins;
392
393 /**
394  * Handle if we are monitoring peers at the transport level.
395  */
396 static struct GNUNET_TRANSPORT_PeerMonitoringContext *pic;
397
398 /**
399  * Handle if we are monitoring transport validation activity.
400  */
401 static struct GNUNET_TRANSPORT_ValidationMonitoringContext *vic;
402
403 /**
404  * Handle if we are monitoring plugin session activity.
405  */
406 static struct GNUNET_TRANSPORT_PluginMonitor *pm;
407
408 /**
409  * Identity of the peer we transmit to / connect to.
410  * (equivalent to 'cpid' string).
411  */
412 static struct GNUNET_PeerIdentity pid;
413
414 /**
415  * Task scheduled for cleanup / termination of the process.
416  */
417 static struct GNUNET_SCHEDULER_Task * end;
418
419 /**
420  * Task for operation timeout
421  */
422 static struct GNUNET_SCHEDULER_Task * op_timeout;
423
424 /**
425  * Selected level of verbosity.
426  */
427 static int verbosity;
428
429 /**
430  * Resolver process handle.
431  */
432 struct GNUNET_OS_Process *resolver;
433
434 /**
435  * Number of address resolutions pending
436  */
437 static unsigned int address_resolutions;
438
439 /**
440  * Address resolutions pending in progress
441  */
442 static unsigned int address_resolution_in_progress;
443
444 /**
445  * DLL for NAT Test Contexts: head
446  */
447 struct TestContext *head;
448
449 /**
450  * DLL for NAT Test Contexts: tail
451  */
452 struct TestContext *tail;
453
454 /**
455  * DLL: head of validation resolution entries
456  */
457 static struct ValidationResolutionContext *vc_head;
458
459 /**
460  * DLL: tail of validation resolution entries
461  */
462 static struct ValidationResolutionContext *vc_tail;
463
464 /**
465  * DLL: head of resolution entries
466  */
467 static struct PeerResolutionContext *rc_head;
468
469 /**
470  * DLL: head of resolution entries
471  */
472 static struct PeerResolutionContext *rc_tail;
473
474
475 /**
476  * Function called to release data stored in the #monitored_peers map.
477  *
478  * @param cls unused
479  * @param key the peer identity
480  * @param value a `struct MonitoredPeer` to release
481  * @return #GNUNET_OK (continue to iterate)
482  */
483 static int
484 destroy_it (void *cls,
485             const struct GNUNET_PeerIdentity *key,
486             void *value)
487 {
488   struct MonitoredPeer *m = value;
489
490   GNUNET_assert (GNUNET_OK ==
491                  GNUNET_CONTAINER_multipeermap_remove (monitored_peers,
492                                                        key,
493                                                        value));
494   GNUNET_free_non_null (m->address);
495   GNUNET_free (value);
496   return GNUNET_OK;
497 }
498
499
500 /**
501  * Task run in monitor mode when the user presses CTRL-C to abort.
502  * Stops monitoring activity.
503  *
504  * @param cls NULL
505  * @param tc scheduler context
506  */
507 static void
508 shutdown_task (void *cls,
509                const struct GNUNET_SCHEDULER_TaskContext *tc)
510 {
511   struct GNUNET_TIME_Relative duration;
512   struct ValidationResolutionContext *cur;
513   struct ValidationResolutionContext *next;
514   struct PeerResolutionContext *rc;
515
516   end = NULL;
517   if (NULL != op_timeout)
518   {
519     GNUNET_SCHEDULER_cancel (op_timeout);
520     op_timeout = NULL;
521   }
522   if (NULL != tc_handle)
523   {
524     GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
525     tc_handle = NULL;
526   }
527   if (NULL != pic)
528   {
529     GNUNET_TRANSPORT_monitor_peers_cancel (pic);
530     pic = NULL;
531   }
532   if (NULL != vic)
533   {
534     GNUNET_TRANSPORT_monitor_validation_entries_cancel (vic);
535     vic = NULL;
536   }
537   if (NULL != pm)
538   {
539     GNUNET_TRANSPORT_monitor_plugins_cancel (pm);
540     pm = NULL;
541   }
542
543   next = vc_head;
544   for (cur = next; NULL != cur; cur = next)
545   {
546     next = cur->next;
547
548     GNUNET_TRANSPORT_address_to_string_cancel (cur->asc);
549     GNUNET_CONTAINER_DLL_remove (vc_head,
550                                  vc_tail,
551                                  cur);
552     GNUNET_free (cur->transport);
553     GNUNET_HELLO_address_free (cur->addrcp);
554     GNUNET_free (cur);
555   }
556   while (NULL != (rc = rc_head))
557   {
558     GNUNET_CONTAINER_DLL_remove (rc_head,
559                                  rc_tail,
560                                  rc);
561     GNUNET_TRANSPORT_address_to_string_cancel (rc->asc);
562     GNUNET_free (rc->transport);
563     GNUNET_free (rc->addrcp);
564     GNUNET_free (rc);
565   }
566   if (NULL != th)
567   {
568     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
569     th = NULL;
570   }
571   if (NULL != handle)
572   {
573     GNUNET_TRANSPORT_disconnect (handle);
574     handle = NULL;
575   }
576   if (benchmark_send)
577   {
578     duration = GNUNET_TIME_absolute_get_duration (start_time);
579     FPRINTF (stdout,
580              _("Transmitted %llu bytes/s (%llu bytes in %s)\n"),
581              1000LL * 1000LL * traffic_sent / (1 + duration.rel_value_us),
582              traffic_sent,
583              GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
584   }
585   if (benchmark_receive)
586   {
587     duration = GNUNET_TIME_absolute_get_duration (start_time);
588     FPRINTF (stdout,
589              _("Received %llu bytes/s (%llu bytes in %s)\n"),
590              1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us),
591              traffic_received,
592              GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
593   }
594
595   if (NULL != monitored_peers)
596   {
597     GNUNET_CONTAINER_multipeermap_iterate (monitored_peers, &destroy_it, NULL);
598     GNUNET_CONTAINER_multipeermap_destroy (monitored_peers);
599     monitored_peers = NULL;
600   }
601   if (NULL != monitored_plugins)
602   {
603     GNUNET_break (0 ==
604                   GNUNET_CONTAINER_multipeermap_size (monitored_plugins));
605     GNUNET_CONTAINER_multipeermap_destroy (monitored_plugins);
606     monitored_plugins = NULL;
607   }
608 }
609
610
611 static void
612 operation_timeout (void *cls,
613                    const struct GNUNET_SCHEDULER_TaskContext *tc)
614 {
615   struct PeerResolutionContext *cur;
616   struct PeerResolutionContext *next;
617   op_timeout = NULL;
618   if ((try_connect) || (benchmark_send) || (benchmark_receive))
619   {
620     FPRINTF (stdout,
621              _("Failed to connect to `%s'\n"),
622              GNUNET_i2s_full (&pid));
623     if (NULL != end)
624       GNUNET_SCHEDULER_cancel (end);
625     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
626     ret = 1;
627     return;
628   }
629   if (iterate_connections)
630   {
631     next = rc_head;
632     while (NULL != (cur = next))
633     {
634       next = cur->next;
635       FPRINTF (stdout,
636                _("Failed to resolve address for peer `%s'\n"),
637                GNUNET_i2s (&cur->addrcp->peer));
638
639       GNUNET_CONTAINER_DLL_remove(rc_head, rc_tail, cur);
640       GNUNET_TRANSPORT_address_to_string_cancel (cur->asc);
641       GNUNET_free(cur->transport);
642       GNUNET_free(cur->addrcp);
643       GNUNET_free(cur);
644
645     }
646     FPRINTF (stdout,
647              "%s",
648              _("Failed to list connections, timeout occured\n"));
649     if (NULL != end)
650       GNUNET_SCHEDULER_cancel (end);
651     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
652     ret = 1;
653     return;
654   }
655 }
656
657
658 static void
659 run_nat_test (void);
660
661
662 /**
663  * Display the result of the test.
664  *
665  * @param tc test context
666  * @param result #GNUNET_YES on success
667  */
668 static void
669 display_test_result (struct TestContext *tc,
670                      enum TestResult result)
671 {
672   switch (result) {
673     case NAT_TEST_FAIL:
674       FPRINTF (stderr,
675                _("Configuration for plugin `%s' did not work!\n"),
676                tc->name);
677       break;
678     case NAT_TEST_SUCCESS:
679       FPRINTF (stderr,
680                _("Configuration for plugin `%s' did work!\n"),
681                tc->name);
682       break;
683     case NAT_TEST_INTERNAL_FAIL:
684       FPRINTF (stderr,
685                _("Internal NAT error while running test for plugin `%s'\n"),
686                tc->name);
687       break;
688     case NAT_TEST_FAILED_TO_START:
689       FPRINTF (stderr,
690                _("Failed to start NAT test for plugin `%s'\n"),
691                tc->name);
692       break;
693     case NAT_TEST_TIMEOUT:
694       FPRINTF (stderr,
695                _("Timeout while waiting for result of NAT test for plugin `%s'\n"),
696                tc->name);
697       break;
698     default:
699       FPRINTF (stderr,
700                _("Configuration for plugin `%s' did not work!\n"),
701                tc->name);
702       break;
703   }
704   if (NULL != tc->tsk)
705   {
706     GNUNET_SCHEDULER_cancel (tc->tsk);
707     tc->tsk = NULL;
708   }
709   if (NULL != tc->tst)
710   {
711     GNUNET_NAT_test_stop (tc->tst);
712     tc->tst = NULL;
713   }
714
715   GNUNET_CONTAINER_DLL_remove (head, tail, tc);
716   GNUNET_free (tc->name);
717   GNUNET_free (tc);
718
719   if ((NULL == head) && (NULL != resolver))
720   {
721     GNUNET_break (0 == GNUNET_OS_process_kill (resolver,
722                                                GNUNET_TERM_SIG));
723     GNUNET_OS_process_destroy (resolver);
724     resolver = NULL;
725   }
726   if (NULL != head)
727     run_nat_test ();
728 }
729
730
731 /**
732  * Function called by NAT to report the outcome of the nat-test.
733  * Clean up and update GUI.
734  *
735  * @param cls test context
736  * @param result status code
737  */
738 static void
739 result_callback (void *cls,
740                  enum GNUNET_NAT_StatusCode result)
741 {
742   struct TestContext *tc = cls;
743
744   display_test_result (tc,
745                        result);
746 }
747
748
749 /**
750  * Resolve address we got a validation state for to a string.
751  *
752  * @param address the address itself
753  * @param numeric #GNUNET_YES to disable DNS, #GNUNET_NO to try reverse lookup
754  * @param last_validation when was the address validated last
755  * @param valid_until until when is the address valid
756  * @param next_validation when will we try to revalidate the address next
757  * @param state where are we in the validation state machine
758  */
759 static void
760 resolve_validation_address (const struct GNUNET_HELLO_Address *address,
761                             int numeric,
762                             struct GNUNET_TIME_Absolute last_validation,
763                             struct GNUNET_TIME_Absolute valid_until,
764                             struct GNUNET_TIME_Absolute next_validation,
765                             enum GNUNET_TRANSPORT_ValidationState state);
766
767
768 /**
769  * Function to call with a textual representation of an address.  This
770  * function will be called several times with different possible
771  * textual representations, and a last time with @a address being NULL
772  * to signal the end of the iteration.  Note that @a address NULL
773  * always is the last call, regardless of the value in @a res.
774  *
775  * @param cls closure
776  * @param address NULL on end of iteration,
777  *        otherwise 0-terminated printable UTF-8 string,
778  *        in particular an empty string if @a res is #GNUNET_NO
779  * @param res result of the address to string conversion:
780  *        if #GNUNET_OK: conversion successful
781  *        if #GNUNET_NO: address was invalid (or not supported)
782  *        if #GNUNET_SYSERR: communication error (IPC error)
783  */
784 static void
785 process_validation_string (void *cls,
786                            const char *address,
787                            int res)
788 {
789   struct ValidationResolutionContext *vc = cls;
790   char *s_valid;
791   char *s_last;
792   char *s_next;
793
794   if (NULL != address)
795   {
796     if (GNUNET_SYSERR == res)
797     {
798       FPRINTF (stderr,
799                "Failed to convert address for peer `%s' plugin `%s' length %u to string \n",
800                GNUNET_i2s (&vc->addrcp->peer),
801                vc->addrcp->transport_name,
802                (unsigned int) vc->addrcp->address_length);
803     }
804     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->valid_until.abs_value_us)
805       s_valid = GNUNET_strdup ("never");
806     else
807       s_valid = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (vc->valid_until));
808
809     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->last_validation.abs_value_us)
810       s_last = GNUNET_strdup ("never");
811     else
812       s_last = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (vc->last_validation));
813
814     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->next_validation.abs_value_us)
815       s_next = GNUNET_strdup ("never");
816     else
817       s_next = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (vc->next_validation));
818
819     FPRINTF (stdout,
820              _("Peer `%s' %s %s\n\t%s%s\n\t%s%s\n\t%s%s\n"),
821              GNUNET_i2s (&vc->addrcp->peer),
822              (GNUNET_OK == res) ? address : "<invalid address>",
823              (monitor_validation) ? GNUNET_TRANSPORT_vs2s (vc->state) : "",
824              "Valid until    : ", s_valid,
825              "Last validation: ",s_last,
826              "Next validation: ", s_next);
827     GNUNET_free (s_valid);
828     GNUNET_free (s_last);
829     GNUNET_free (s_next);
830     vc->printed = GNUNET_YES;
831     return;
832   }
833   /* last call, we are done */
834   GNUNET_assert (address_resolutions > 0);
835   address_resolutions--;
836   if ( (GNUNET_SYSERR == res) &&
837        (GNUNET_NO == vc->printed))
838   {
839     if (numeric == GNUNET_NO)
840     {
841       /* Failed to resolve address, try numeric lookup
842          (note: this should be unnecessary, as
843          transport should fallback to numeric lookup
844          internally if DNS takes too long anyway) */
845       resolve_validation_address (vc->addrcp,
846                                   GNUNET_NO,
847                                   vc->last_validation,
848                                   vc->valid_until,
849                                   vc->next_validation,
850                                   vc->state);
851     }
852     else
853     {
854       FPRINTF (stdout,
855                _("Peer `%s' %s `%s' \n"),
856                GNUNET_i2s (&vc->addrcp->peer),
857                "<unable to resolve address>",
858                GNUNET_TRANSPORT_vs2s (vc->state));
859     }
860   }
861   GNUNET_free (vc->transport);
862   GNUNET_free (vc->addrcp);
863   GNUNET_CONTAINER_DLL_remove (vc_head, vc_tail, vc);
864   GNUNET_free (vc);
865   if ((0 == address_resolutions) && (iterate_validation))
866   {
867     if (NULL != end)
868     {
869       GNUNET_SCHEDULER_cancel (end);
870       end = NULL;
871     }
872     if (NULL != op_timeout)
873     {
874       GNUNET_SCHEDULER_cancel (op_timeout);
875       op_timeout = NULL;
876     }
877     ret = 0;
878     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
879   }
880 }
881
882
883 /**
884  * Resolve address we got a validation state for to a string.
885  *
886  * @param address the address itself
887  * @param numeric #GNUNET_YES to disable DNS, #GNUNET_NO to try reverse lookup
888  * @param last_validation when was the address validated last
889  * @param valid_until until when is the address valid
890  * @param next_validation when will we try to revalidate the address next
891  * @param state where are we in the validation state machine
892  */
893 static void
894 resolve_validation_address (const struct GNUNET_HELLO_Address *address,
895                             int numeric,
896                             struct GNUNET_TIME_Absolute last_validation,
897                             struct GNUNET_TIME_Absolute valid_until,
898                             struct GNUNET_TIME_Absolute next_validation,
899                             enum GNUNET_TRANSPORT_ValidationState state)
900 {
901   struct ValidationResolutionContext *vc;
902
903   vc = GNUNET_new (struct ValidationResolutionContext);
904   GNUNET_assert(NULL != vc);
905   GNUNET_CONTAINER_DLL_insert(vc_head, vc_tail, vc);
906   address_resolutions++;
907
908   vc->transport = GNUNET_strdup(address->transport_name);
909   vc->addrcp = GNUNET_HELLO_address_copy (address);
910   vc->printed = GNUNET_NO;
911   vc->state = state;
912   vc->last_validation = last_validation;
913   vc->valid_until = valid_until;
914   vc->next_validation = next_validation;
915
916   /* Resolve address to string */
917   vc->asc = GNUNET_TRANSPORT_address_to_string (cfg,
918                                                 address,
919                                                 numeric,
920                                                 RESOLUTION_TIMEOUT,
921                                                 &process_validation_string, vc);
922 }
923
924
925 /**
926  * Resolve address we got a validation state for to a string.
927  *
928  * @param cls NULL
929  * @param address the address itself
930  * @param last_validation when was the address validated last
931  * @param valid_until until when is the address valid
932  * @param next_validation when will we try to revalidate the address next
933  * @param state where are we in the validation state machine
934  */
935 static void
936 process_validation_cb (void *cls,
937                        const struct GNUNET_HELLO_Address *address,
938                        struct GNUNET_TIME_Absolute last_validation,
939                        struct GNUNET_TIME_Absolute valid_until,
940                        struct GNUNET_TIME_Absolute next_validation,
941                        enum GNUNET_TRANSPORT_ValidationState state)
942 {
943   if (NULL == address)
944   {
945     if (monitor_validation)
946     {
947       FPRINTF (stdout,
948                "%s",
949                _("Monitor disconnected from transport service. Reconnecting.\n"));
950       return;
951     }
952     vic = NULL;
953     if (NULL != end)
954       GNUNET_SCHEDULER_cancel (end);
955     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
956     return;
957   }
958   resolve_validation_address (address,
959                               numeric,
960                               last_validation,
961                               valid_until,
962                               next_validation,
963                               state);
964 }
965
966
967 static void
968 run_nat_test ()
969 {
970   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
971               "Running test for plugin `%s' using bind port %u and advertised port %u \n",
972               head->name,
973               (uint16_t) head->bnd_port,
974               (uint16_t) head->adv_port);
975
976   head->tst = GNUNET_NAT_test_start (cfg,
977                                      (0 == strcasecmp (head->name, "udp"))
978                                      ? GNUNET_NO : GNUNET_YES,
979                                      (uint16_t) head->bnd_port,
980                                      (uint16_t) head->adv_port,
981                                      TIMEOUT,
982                                      &result_callback, head);
983 }
984
985
986 /**
987  * Test our plugin's configuration (NAT traversal, etc.).
988  *
989  * @param cfg configuration to test
990  */
991 static void
992 do_test_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
993 {
994   char *plugins;
995   char *tok;
996   unsigned long long bnd_port;
997   unsigned long long adv_port;
998   struct TestContext *tc;
999   char *binary;
1000
1001   if (GNUNET_OK
1002       != GNUNET_CONFIGURATION_get_value_string (cfg, "transport", "plugins",
1003           &plugins))
1004   {
1005     FPRINTF (stderr, "%s", _
1006     ("No transport plugins configured, peer will never communicate\n"));
1007     ret = 4;
1008     return;
1009   }
1010
1011   for (tok = strtok (plugins, " "); tok != NULL ; tok = strtok (NULL, " "))
1012   {
1013     char section[12 + strlen (tok)];
1014     GNUNET_snprintf (section, sizeof(section), "transport-%s", tok);
1015     if (GNUNET_OK
1016         != GNUNET_CONFIGURATION_get_value_number (cfg, section, "PORT",
1017             &bnd_port))
1018     {
1019       FPRINTF (stderr,
1020           _("No port configured for plugin `%s', cannot test it\n"), tok);
1021       continue;
1022     }
1023     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, section,
1024             "ADVERTISED_PORT", &adv_port))
1025       adv_port = bnd_port;
1026
1027     tc = GNUNET_new (struct TestContext);
1028     tc->name = GNUNET_strdup (tok);
1029     tc->adv_port = adv_port;
1030     tc->bnd_port = bnd_port;
1031     GNUNET_CONTAINER_DLL_insert_tail (head, tail, tc);
1032   }
1033   GNUNET_free(plugins);
1034
1035   if ((NULL != head) && (NULL == resolver))
1036   {
1037     binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
1038     resolver = GNUNET_OS_start_process (GNUNET_YES,
1039                                         GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
1040                                         NULL, NULL, NULL,
1041                                         binary,
1042                                         "gnunet-service-resolver", NULL);
1043     if (NULL == resolver)
1044     {
1045       FPRINTF (stderr, _("Failed to start resolver!\n"));
1046       return;
1047     }
1048
1049     GNUNET_free(binary);
1050     GNUNET_RESOLVER_connect (cfg);
1051     run_nat_test ();
1052   }
1053 }
1054
1055
1056 /**
1057  * Function called to notify a client about the socket
1058  * begin ready to queue more data.  @a buf will be
1059  * NULL and @a size zero if the socket was closed for
1060  * writing in the meantime.
1061  *
1062  * @param cls closure
1063  * @param size number of bytes available in @a buf
1064  * @param buf where the callee should write the message
1065  * @return number of bytes written to @a buf
1066  */
1067 static size_t
1068 transmit_data (void *cls,
1069                size_t size,
1070                void *buf)
1071 {
1072   struct GNUNET_MessageHeader *m = buf;
1073
1074   if ((NULL == buf) || (0 == size))
1075   {
1076     th = NULL;
1077     return 0;
1078   }
1079
1080   GNUNET_assert(size >= sizeof(struct GNUNET_MessageHeader));
1081   GNUNET_assert(size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
1082   m->size = ntohs (size);
1083   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
1084   memset (&m[1], 52, size - sizeof(struct GNUNET_MessageHeader));
1085   traffic_sent += size;
1086   th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid,
1087                                                BLOCKSIZE * 1024,
1088                                                GNUNET_TIME_UNIT_FOREVER_REL,
1089                                                &transmit_data, NULL);
1090   if (verbosity > 0)
1091     FPRINTF (stdout, _("Transmitting %u bytes to %s\n"), (unsigned int) size,
1092         GNUNET_i2s (&pid));
1093   return size;
1094 }
1095
1096
1097 /**
1098  * Function called to notify transport users that another
1099  * peer connected to us.
1100  *
1101  * @param cls closure
1102  * @param peer the peer that connected
1103  */
1104 static void
1105 notify_connect (void *cls,
1106                 const struct GNUNET_PeerIdentity *peer)
1107 {
1108   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
1109     return;
1110   ret = 0;
1111   if (try_connect)
1112   {
1113     /* all done, terminate instantly */
1114     FPRINTF (stdout, _("Successfully connected to `%s'\n"),
1115         GNUNET_i2s_full (peer));
1116     ret = 0;
1117
1118     if (NULL != op_timeout)
1119     {
1120       GNUNET_SCHEDULER_cancel (op_timeout);
1121       op_timeout = NULL;
1122     }
1123
1124     if (NULL != end)
1125       GNUNET_SCHEDULER_cancel (end);
1126     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1127     return;
1128   }
1129   if (benchmark_send)
1130   {
1131     if (NULL != op_timeout)
1132     {
1133       GNUNET_SCHEDULER_cancel (op_timeout);
1134       op_timeout = NULL;
1135     }
1136     if (verbosity > 0)
1137       FPRINTF (stdout,
1138           _("Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"),
1139           GNUNET_i2s (&pid), BLOCKSIZE);
1140     start_time = GNUNET_TIME_absolute_get ();
1141     if (NULL == th)
1142       th = GNUNET_TRANSPORT_notify_transmit_ready (handle, peer,
1143                                                    BLOCKSIZE * 1024,
1144                                                    GNUNET_TIME_UNIT_FOREVER_REL,
1145                                                    &transmit_data,
1146                                                    NULL);
1147     else
1148       GNUNET_break(0);
1149     return;
1150   }
1151 }
1152
1153
1154 /**
1155  * Function called to notify transport users that another
1156  * peer disconnected from us.
1157  *
1158  * @param cls closure
1159  * @param peer the peer that disconnected
1160  */
1161 static void
1162 notify_disconnect (void *cls,
1163                    const struct GNUNET_PeerIdentity *peer)
1164 {
1165   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
1166     return;
1167
1168   if (try_disconnect)
1169   {
1170     /* all done, terminate instantly */
1171     FPRINTF (stdout, _("Successfully disconnected from `%s'\n"),
1172         GNUNET_i2s_full (peer));
1173     ret = 0;
1174
1175     if (NULL != op_timeout)
1176     {
1177       GNUNET_SCHEDULER_cancel (op_timeout);
1178       op_timeout = NULL;
1179     }
1180
1181     if (NULL != end)
1182       GNUNET_SCHEDULER_cancel (end);
1183     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1184     return;
1185   }
1186
1187   if (NULL != th)
1188   {
1189     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
1190     th = NULL;
1191   }
1192   if (benchmark_send)
1193   {
1194     FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"),
1195         GNUNET_i2s (&pid));
1196     if (NULL != end)
1197       GNUNET_SCHEDULER_cancel (end);
1198     return;
1199   }
1200 }
1201
1202
1203 /**
1204  * Function called to notify transport users that another
1205  * peer connected to us.
1206  *
1207  * @param cls closure
1208  * @param peer the peer that connected
1209  */
1210 static void
1211 monitor_notify_connect (void *cls,
1212                         const struct GNUNET_PeerIdentity *peer)
1213 {
1214   monitor_connect_counter++;
1215   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1216   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
1217
1218   FPRINTF (stdout,
1219            _("%24s: %-17s %4s   (%u connections in total)\n"),
1220            now_str,
1221            _("Connected to"),
1222            GNUNET_i2s (peer),
1223            monitor_connect_counter);
1224 }
1225
1226
1227 /**
1228  * Function called to notify transport users that another
1229  * peer disconnected from us.
1230  *
1231  * @param cls closure
1232  * @param peer the peer that disconnected
1233  */
1234 static void
1235 monitor_notify_disconnect (void *cls,
1236                            const struct GNUNET_PeerIdentity *peer)
1237 {
1238   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1239   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
1240
1241   GNUNET_assert(monitor_connect_counter > 0);
1242   monitor_connect_counter--;
1243
1244   FPRINTF (stdout,
1245            _("%24s: %-17s %4s   (%u connections in total)\n"),
1246            now_str,
1247            _("Disconnected from"),
1248            GNUNET_i2s (peer),
1249            monitor_connect_counter);
1250 }
1251
1252
1253 /**
1254  * Function called by the transport for each received message.
1255  *
1256  * @param cls closure
1257  * @param peer (claimed) identity of the other peer
1258  * @param message the message
1259  */
1260 static void
1261 notify_receive (void *cls,
1262                 const struct GNUNET_PeerIdentity *peer,
1263                 const struct GNUNET_MessageHeader *message)
1264 {
1265   if (benchmark_receive)
1266   {
1267     if (GNUNET_MESSAGE_TYPE_DUMMY != ntohs (message->type))
1268       return;
1269     if (verbosity > 0)
1270       FPRINTF (stdout,
1271                _("Received %u bytes from %s\n"),
1272                (unsigned int) ntohs (message->size),
1273                GNUNET_i2s (peer));
1274
1275     if (traffic_received == 0)
1276       start_time = GNUNET_TIME_absolute_get ();
1277     traffic_received += ntohs (message->size);
1278     return;
1279   }
1280 }
1281
1282
1283 /**
1284  * Convert address to a printable format.
1285  *
1286  * @param address the address
1287  * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO
1288  *                to try to use reverse DNS
1289  * @param state state the peer is in
1290  * @param state_timeout when will the peer's state expire
1291  */
1292 static void
1293 resolve_peer_address (const struct GNUNET_HELLO_Address *address,
1294                       int numeric,
1295                       enum GNUNET_TRANSPORT_PeerState state,
1296                       struct GNUNET_TIME_Absolute state_timeout);
1297
1298
1299 static void
1300 print_info (const struct GNUNET_PeerIdentity *id,
1301             const char *transport,
1302             const char *addr,
1303             enum GNUNET_TRANSPORT_PeerState state,
1304             struct GNUNET_TIME_Absolute state_timeout)
1305 {
1306
1307   if ( ((GNUNET_YES == iterate_connections) && (GNUNET_YES == iterate_all)) ||
1308        (GNUNET_YES == monitor_connections))
1309   {
1310     FPRINTF (stdout,
1311              _("Peer `%s': %s %s in state `%s' until %s\n"),
1312              GNUNET_i2s (id),
1313              (NULL == transport) ? "<none>" : transport,
1314              (NULL == transport) ? "<none>" : addr,
1315              GNUNET_TRANSPORT_ps2s (state),
1316              GNUNET_STRINGS_absolute_time_to_string (state_timeout));
1317   }
1318   else if ( (GNUNET_YES == iterate_connections) &&
1319              (GNUNET_TRANSPORT_is_connected(state)))
1320   {
1321     /* Only connected peers, skip state */
1322     FPRINTF (stdout,
1323              _("Peer `%s': %s %s\n"),
1324              GNUNET_i2s (id),
1325              transport,
1326              addr);
1327   }
1328 }
1329
1330
1331 /**
1332  * Function called with a textual representation of an address.  This
1333  * function will be called several times with different possible
1334  * textual representations, and a last time with @a address being NULL
1335  * to signal the end of the iteration.  Note that @a address NULL
1336  * always is the last call, regardless of the value in @a res.
1337  *
1338  * @param cls closure
1339  * @param address NULL on end of iteration,
1340  *        otherwise 0-terminated printable UTF-8 string,
1341  *        in particular an empty string if @a res is #GNUNET_NO
1342  * @param res result of the address to string conversion:
1343  *        if #GNUNET_OK: conversion successful
1344  *        if #GNUNET_NO: address was invalid (or not supported)
1345  *        if #GNUNET_SYSERR: communication error (IPC error)
1346  */
1347 static void
1348 process_peer_string (void *cls,
1349                      const char *address,
1350                      int res)
1351 {
1352   struct PeerResolutionContext *rc = cls;
1353
1354   if (NULL != address)
1355   {
1356     if (GNUNET_SYSERR == res)
1357     {
1358       FPRINTF (stderr,
1359                "Failed to convert address for peer `%s' plugin `%s' length %u to string \n",
1360                GNUNET_i2s (&rc->addrcp->peer),
1361                rc->addrcp->transport_name,
1362                (unsigned int) rc->addrcp->address_length);
1363       print_info (&rc->addrcp->peer,
1364                   rc->transport,
1365                   NULL,
1366                   rc->state,
1367                   rc->state_timeout);
1368       rc->printed = GNUNET_YES;
1369       return;
1370     }
1371     if (GNUNET_OK == res)
1372     {
1373       print_info (&rc->addrcp->peer,
1374                   rc->transport,
1375                   address,
1376                   rc->state,
1377                   rc->state_timeout);
1378       rc->printed = GNUNET_YES;
1379       return; /* Wait for done call */
1380     }
1381     /* GNUNET_NO == res: ignore, was simply not supported */
1382     return;
1383   }
1384   /* NULL == address, last call, we are done */
1385
1386   rc->asc = NULL;
1387   GNUNET_assert (address_resolutions > 0);
1388   address_resolutions--;
1389   if (GNUNET_NO == rc->printed)
1390   {
1391     if (numeric == GNUNET_NO)
1392     {
1393       /* Failed to resolve address, try numeric lookup
1394          (note: this should not be needed, as transport
1395          should fallback to numeric conversion if DNS takes
1396          too long) */
1397       resolve_peer_address (rc->addrcp,
1398                             GNUNET_YES,
1399                             rc->state,
1400                             rc->state_timeout);
1401     }
1402     else
1403     {
1404       print_info (&rc->addrcp->peer,
1405                   rc->transport,
1406                   NULL,
1407                   rc->state,
1408                   rc->state_timeout);
1409     }
1410   }
1411   GNUNET_free (rc->transport);
1412   GNUNET_free (rc->addrcp);
1413   GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, rc);
1414   GNUNET_free (rc);
1415   if ((0 == address_resolutions) && (iterate_connections))
1416   {
1417     if (NULL != end)
1418     {
1419       GNUNET_SCHEDULER_cancel (end);
1420       end = NULL;
1421     }
1422     if (NULL != op_timeout)
1423     {
1424       GNUNET_SCHEDULER_cancel (op_timeout);
1425       op_timeout = NULL;
1426     }
1427     ret = 0;
1428     end = GNUNET_SCHEDULER_add_now (&shutdown_task, 
1429                                     NULL);
1430   }
1431 }
1432
1433
1434 /**
1435  * Convert address to a printable format and print it
1436  * together with the given state data.
1437  *
1438  * @param address the address
1439  * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO
1440  *                to try to use reverse DNS
1441  * @param state state the peer is in
1442  * @param state_timeout when will the peer's state expire
1443  */
1444 static void
1445 resolve_peer_address (const struct GNUNET_HELLO_Address *address,
1446                       int numeric,
1447                       enum GNUNET_TRANSPORT_PeerState state,
1448                       struct GNUNET_TIME_Absolute state_timeout)
1449 {
1450   struct PeerResolutionContext *rc;
1451
1452   rc = GNUNET_new (struct PeerResolutionContext);
1453   GNUNET_CONTAINER_DLL_insert (rc_head, 
1454                                rc_tail,
1455                                rc);
1456   address_resolutions++;
1457   rc->transport = GNUNET_strdup (address->transport_name);
1458   rc->addrcp = GNUNET_HELLO_address_copy (address);
1459   rc->printed = GNUNET_NO;
1460   rc->state = state;
1461   rc->state_timeout = state_timeout;
1462   /* Resolve address to string */
1463   rc->asc = GNUNET_TRANSPORT_address_to_string (cfg,
1464                                                 address,
1465                                                 numeric,
1466                                                 RESOLUTION_TIMEOUT,
1467                                                 &process_peer_string, rc);
1468 }
1469
1470
1471 /**
1472  * Function called with information about a peers during a one shot iteration
1473  *
1474  * @param cls closure
1475  * @param peer identity of the peer, NULL for final callback when operation done
1476  * @param address binary address used to communicate with this peer,
1477  *  NULL on disconnect or when done
1478  * @param state current state this peer is in
1479  * @param state_timeout time out for the current state
1480  */
1481 static void
1482 process_peer_iteration_cb (void *cls,
1483                            const struct GNUNET_PeerIdentity *peer,
1484                            const struct GNUNET_HELLO_Address *address,
1485                            enum GNUNET_TRANSPORT_PeerState state,
1486                            struct GNUNET_TIME_Absolute state_timeout)
1487 {
1488   if (NULL == peer)
1489   {
1490     /* done */
1491     address_resolution_in_progress = GNUNET_NO;
1492     pic = NULL;
1493     if (NULL != end)
1494       GNUNET_SCHEDULER_cancel (end);
1495     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1496     return;
1497   }
1498
1499   if ( (GNUNET_NO == iterate_all) &&
1500        (GNUNET_NO == GNUNET_TRANSPORT_is_connected(state)))
1501       return; /* Display only connected peers */
1502
1503   if (NULL != op_timeout)
1504     GNUNET_SCHEDULER_cancel (op_timeout);
1505   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
1506                                              &operation_timeout,
1507                                              NULL);
1508
1509   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1510               "Received address for peer `%s': %s\n",
1511               GNUNET_i2s (peer),
1512               address->transport_name);
1513
1514   if (NULL != address)
1515     resolve_peer_address (address, 
1516                           numeric, 
1517                           state,
1518                           state_timeout);
1519   else
1520     print_info (peer, 
1521                 NULL,
1522                 NULL,
1523                 state,
1524                 state_timeout);
1525 }
1526
1527
1528 /**
1529  * Context for address resolution by #plugin_monitoring_cb().
1530  */
1531 struct PluginMonitorAddress
1532 {
1533
1534   /**
1535    * Ongoing resolution request.
1536    */
1537   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
1538
1539   /**
1540    * Resolved address as string.
1541    */
1542   char *str;
1543 };
1544
1545
1546 /**
1547  * Function called with a textual representation of an address.  This
1548  * function will be called several times with different possible
1549  * textual representations, and a last time with @a address being NULL
1550  * to signal the end of the iteration.  Note that @a address NULL
1551  * always is the last call, regardless of the value in @a res.
1552  *
1553  * @param cls closure
1554  * @param address NULL on end of iteration,
1555  *        otherwise 0-terminated printable UTF-8 string,
1556  *        in particular an empty string if @a res is #GNUNET_NO
1557  * @param res result of the address to string conversion:
1558  *        if #GNUNET_OK: conversion successful
1559  *        if #GNUNET_NO: address was invalid (or not supported)
1560  *        if #GNUNET_SYSERR: communication error (IPC error)
1561  */
1562 static void
1563 address_cb (void *cls,
1564             const char *address,
1565             int res)
1566 {
1567   struct PluginMonitorAddress *addr = cls;
1568
1569   if (NULL == address)
1570   {
1571     addr->asc = NULL;
1572     return;
1573   }
1574   if (NULL != addr->str)
1575     return;
1576   addr->str = GNUNET_strdup (address);
1577 }
1578
1579
1580 /**
1581  * Function called by the plugin with information about the
1582  * current sessions managed by the plugin (for monitoring).
1583  *
1584  * @param cls closure (NULL)
1585  * @param session session handle this information is about,
1586  *        NULL to indicate that we are "in sync" (initial
1587  *        iteration complete)
1588  * @param session_ctx storage location where the application
1589  *        can store data; will point to NULL on #GNUNET_TRANSPORT_SS_INIT,
1590  *        and must be reset to NULL on #GNUNET_TRANSPORT_SS_DONE
1591  * @param info information about the state of the session,
1592  *        NULL if @a session is also NULL and we are
1593  *        merely signalling that the initial iteration is over;
1594  *        NULL with @a session being non-NULL if the monitor
1595  *        was being cancelled while sessions were active
1596  */
1597 static void
1598 plugin_monitoring_cb (void *cls,
1599                       struct GNUNET_TRANSPORT_PluginSession *session,
1600                       void **session_ctx,
1601                       const struct GNUNET_TRANSPORT_SessionInfo *info)
1602 {
1603   const char *state;
1604   struct PluginMonitorAddress *addr;
1605
1606   if ( (NULL == info) &&
1607        (NULL == session) )
1608     return; /* in sync with transport service */
1609   addr = *session_ctx;
1610   if (NULL == info)
1611   {
1612     if (NULL != addr)
1613     {
1614       if (NULL != addr->asc)
1615       {
1616         GNUNET_TRANSPORT_address_to_string_cancel (addr->asc);
1617         addr->asc = NULL;
1618       }
1619       GNUNET_free_non_null (addr->str);
1620       GNUNET_free (addr);
1621       *session_ctx = NULL;
1622     }
1623     return; /* shutdown */
1624   }
1625   if ( (NULL != cpid) &&
1626        (0 != memcmp (&info->address->peer,
1627                      cpid,
1628                      sizeof (struct GNUNET_PeerIdentity))) )
1629     return; /* filtered */
1630   if (NULL == addr)
1631   {
1632     addr = GNUNET_new (struct PluginMonitorAddress);
1633     addr->asc = GNUNET_TRANSPORT_address_to_string (cfg,
1634                                                     info->address,
1635                                                     GNUNET_NO,
1636                                                     GNUNET_TIME_UNIT_FOREVER_REL,
1637                                                     &address_cb,
1638                                                     addr);
1639     *session_ctx = addr;
1640   }
1641   switch (info->state)
1642   {
1643   case GNUNET_TRANSPORT_SS_INIT:
1644     state = "INIT";
1645     break;
1646   case GNUNET_TRANSPORT_SS_HANDSHAKE:
1647     state = "HANDSHAKE";
1648     break;
1649   case GNUNET_TRANSPORT_SS_UP:
1650     state = "UP";
1651     break;
1652   case GNUNET_TRANSPORT_SS_UPDATE:
1653     state = "UPDATE";
1654     break;
1655   case GNUNET_TRANSPORT_SS_DONE:
1656     state = "DONE";
1657     break;
1658   default:
1659     state = "UNKNOWN";
1660     break;
1661   }
1662   fprintf (stdout,
1663            "%s: %s %s (# %u/%u b) blocked until %s timeout in %s [%s]\n",
1664            GNUNET_i2s (&info->address->peer),
1665            addr->str,
1666            (info->is_inbound == GNUNET_YES) ? "<-" : ((info->is_inbound == GNUNET_NO) ? "->" : "<>"),
1667            info->num_msg_pending,
1668            info->num_bytes_pending,
1669            GNUNET_STRINGS_absolute_time_to_string (info->receive_delay),
1670            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (info->session_timeout),
1671                                                 GNUNET_YES),
1672            state);
1673   if (GNUNET_TRANSPORT_SS_DONE == info->state)
1674   {
1675     if (NULL != addr->asc)
1676     {
1677       GNUNET_TRANSPORT_address_to_string_cancel (addr->asc);
1678       addr->asc = NULL;
1679     }
1680     GNUNET_free_non_null (addr->str);
1681     GNUNET_free (addr);
1682     *session_ctx = NULL;
1683   }
1684 }
1685
1686
1687 /**
1688  * Function called with information about a peers
1689  *
1690  * @param cls closure, NULL
1691  * @param peer identity of the peer, NULL for final callback when operation done
1692  * @param address binary address used to communicate with this peer,
1693  *  NULL on disconnect or when done
1694  * @param state current state this peer is in
1695  * @param state_timeout time out for the current state
1696  */
1697 static void
1698 process_peer_monitoring_cb (void *cls,
1699                             const struct GNUNET_PeerIdentity *peer,
1700                             const struct GNUNET_HELLO_Address *address,
1701                             enum GNUNET_TRANSPORT_PeerState state,
1702                             struct GNUNET_TIME_Absolute state_timeout)
1703 {
1704   struct MonitoredPeer *m;
1705
1706   if (NULL == peer)
1707   {
1708     FPRINTF (stdout,
1709              "%s",
1710              _("Monitor disconnected from transport service. Reconnecting.\n"));
1711     return;
1712   }
1713
1714   if (NULL != op_timeout)
1715     GNUNET_SCHEDULER_cancel (op_timeout);
1716   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
1717                                              &operation_timeout,
1718                                              NULL);
1719
1720   if (NULL == (m = GNUNET_CONTAINER_multipeermap_get (monitored_peers, 
1721                                                       peer)))
1722   {
1723     m = GNUNET_new (struct MonitoredPeer);
1724     GNUNET_CONTAINER_multipeermap_put (monitored_peers,
1725                                        peer,
1726                                        m,
1727                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1728   }
1729   else
1730   {
1731     if ( (m->state == state) &&
1732          (m->state_timeout.abs_value_us == state_timeout.abs_value_us) &&
1733          (NULL == address) &&
1734          (NULL == m->address) )
1735     {
1736       return; /* No real change */
1737     }
1738     if ( (m->state == state) &&
1739          (NULL != address) && 
1740          (NULL != m->address) &&
1741          (0 == GNUNET_HELLO_address_cmp(m->address, address)) )
1742       return; /* No real change */
1743   }
1744
1745   if (NULL != m->address)
1746   {
1747     GNUNET_free (m->address);
1748     m->address = NULL;
1749   }
1750   if (NULL != address)
1751     m->address = GNUNET_HELLO_address_copy (address);
1752   m->state = state;
1753   m->state_timeout = state_timeout;
1754
1755   if (NULL != address)
1756     resolve_peer_address (m->address,
1757                           numeric,
1758                           m->state,
1759                           m->state_timeout);
1760   else
1761     print_info (peer,
1762                 NULL,
1763                 NULL,
1764                 m->state,
1765                 m->state_timeout);
1766 }
1767
1768
1769 /**
1770  * Function called with our result of trying to connect to a peer
1771  * using the transport service. Will retry 10 times, and if we still
1772  * fail to connect terminate with an error message.
1773  *
1774  * @param cls NULL
1775  * @param result #GNUNET_OK if we connected to the service
1776  */
1777 static void
1778 try_connect_cb (void *cls,
1779                 const int result)
1780 {
1781   static int retries = 0;
1782
1783   tc_handle = NULL;
1784   if (GNUNET_OK == result)
1785     return;
1786   retries++;
1787   if (retries < 10)
1788   {
1789     tc_handle = GNUNET_TRANSPORT_try_connect (handle,
1790                                               &pid,
1791                                               &try_connect_cb,
1792                                               NULL);
1793     return;
1794   }
1795   FPRINTF (stderr,
1796            "%s",
1797            _("Failed to send connect request to transport service\n"));
1798   if (NULL != end)
1799     GNUNET_SCHEDULER_cancel (end);
1800   ret = 1;
1801   end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1802 }
1803
1804
1805 /**
1806  * Function called with our result of trying to disconnect a peer
1807  * using the transport service. Will retry 10 times, and if we still
1808  * fail to disconnect, terminate with an error message.
1809  *
1810  * @param cls NULL
1811  * @param result #GNUNET_OK if we connected to the service
1812  */
1813 static void
1814 try_disconnect_cb (void *cls,
1815                    const int result)
1816 {
1817   static int retries = 0;
1818
1819   td_handle = NULL;
1820   if (GNUNET_OK == result)
1821     return;
1822   retries++;
1823   if (retries < 10)
1824   {
1825     td_handle = GNUNET_TRANSPORT_try_disconnect (handle,
1826                                                  &pid,
1827                                                  &try_disconnect_cb,
1828                                                  NULL);
1829     return;
1830   }
1831   FPRINTF (stderr, "%s",
1832            _("Failed to send disconnect request to transport service\n"));
1833   if (NULL != end)
1834     GNUNET_SCHEDULER_cancel (end);
1835   ret = 1;
1836   end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1837 }
1838
1839
1840 /**
1841  * Function called with the result of the check if the 'transport'
1842  * service is running.
1843  *
1844  * @param cls closure with our configuration
1845  * @param result #GNUNET_YES if transport is running
1846  */
1847 static void
1848 testservice_task (void *cls,
1849                   int result)
1850 {
1851   int counter = 0;
1852   ret = 1;
1853
1854   if (GNUNET_YES != result)
1855   {
1856     FPRINTF (stderr, _("Service `%s' is not running\n"), "transport");
1857     return;
1858   }
1859
1860   if ((NULL != cpid)
1861       && (GNUNET_OK
1862           != GNUNET_CRYPTO_eddsa_public_key_from_string (cpid, strlen (cpid),
1863               &pid.public_key)))
1864   {
1865     FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
1866     return;
1867   }
1868
1869   counter = benchmark_send + benchmark_receive + iterate_connections
1870       + monitor_connections + monitor_connects + try_connect + try_disconnect +
1871       + iterate_validation + monitor_validation + monitor_plugins;
1872
1873   if (1 < counter)
1874   {
1875     FPRINTF (stderr,
1876         _("Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s %s\n"),
1877         "connect", "benchmark send", "benchmark receive", "information",
1878              "monitor", "events", "plugins");
1879     return;
1880   }
1881   if (0 == counter)
1882   {
1883     FPRINTF (stderr,
1884         _("No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s, %s\n"),
1885         "connect", "benchmark send", "benchmark receive", "information",
1886              "monitor", "events", "plugins");
1887     return;
1888   }
1889
1890   if (try_connect) /* -C: Connect to peer */
1891   {
1892     if (NULL == cpid)
1893     {
1894       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
1895           "-C", "-p");
1896       ret = 1;
1897       return;
1898     }
1899     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
1900         &notify_connect, &notify_disconnect);
1901     if (NULL == handle)
1902     {
1903       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
1904       ret = 1;
1905       return;
1906     }
1907     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
1908         NULL);
1909     if (NULL == tc_handle)
1910     {
1911       FPRINTF (stderr, "%s",
1912           _("Failed to send request to transport service\n"));
1913       ret = 1;
1914       return;
1915     }
1916     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1917         NULL);
1918
1919   }
1920   else if (try_disconnect) /* -D: Disconnect from peer */
1921   {
1922     if (NULL == cpid)
1923     {
1924       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
1925           "-D", "-p");
1926       ret = 1;
1927       return;
1928     }
1929     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
1930         &notify_connect, &notify_disconnect);
1931     if (NULL == handle)
1932     {
1933       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
1934       ret = 1;
1935       return;
1936     }
1937     td_handle = GNUNET_TRANSPORT_try_disconnect (handle, &pid,
1938                                                  &try_disconnect_cb,
1939                                                  NULL);
1940     if (NULL == td_handle)
1941     {
1942       FPRINTF (stderr, "%s",
1943           _("Failed to send request to transport service\n"));
1944       ret = 1;
1945       return;
1946     }
1947     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1948         NULL);
1949
1950   }
1951   else if (benchmark_send) /* -s: Benchmark sending */
1952   {
1953     if (NULL == cpid)
1954     {
1955       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
1956           "-s", "-p");
1957       ret = 1;
1958       return;
1959     }
1960     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL,
1961                                        &notify_receive,
1962                                        &notify_connect,
1963                                        &notify_disconnect);
1964     if (NULL == handle)
1965     {
1966       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
1967       ret = 1;
1968       return;
1969     }
1970     tc_handle = GNUNET_TRANSPORT_try_connect (handle,
1971                                               &pid,
1972                                               &try_connect_cb,
1973                                               NULL);
1974     if (NULL == tc_handle)
1975     {
1976       FPRINTF (stderr, "%s",
1977           _("Failed to send request to transport service\n"));
1978       ret = 1;
1979       return;
1980     }
1981     start_time = GNUNET_TIME_absolute_get ();
1982     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
1983                                                &operation_timeout,
1984                                                NULL);
1985   }
1986   else if (benchmark_receive) /* -b: Benchmark receiving */
1987   {
1988     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive, NULL,
1989         NULL);
1990     if (NULL == handle)
1991     {
1992       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
1993       ret = 1;
1994       return;
1995     }
1996     if (verbosity > 0)
1997       FPRINTF (stdout, "%s", _("Starting to receive benchmark data\n"));
1998     start_time = GNUNET_TIME_absolute_get ();
1999
2000   }
2001   else if (iterate_connections) /* -i: List information about peers once */
2002   {
2003     address_resolution_in_progress = GNUNET_YES;
2004     pic = GNUNET_TRANSPORT_monitor_peers (cfg, (NULL == cpid) ? NULL : &pid,
2005         GNUNET_YES, TIMEOUT, &process_peer_iteration_cb, (void *) cfg);
2006     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
2007         NULL);
2008   }
2009   else if (monitor_connections) /* -m: List information about peers continuously */
2010   {
2011     monitored_peers = GNUNET_CONTAINER_multipeermap_create (10, 
2012                                                             GNUNET_NO);
2013     address_resolution_in_progress = GNUNET_YES;
2014     pic = GNUNET_TRANSPORT_monitor_peers (cfg, 
2015                                           (NULL == cpid) ? NULL : &pid,
2016                                           GNUNET_NO, 
2017                                           TIMEOUT,
2018                                           &process_peer_monitoring_cb, NULL);
2019   }
2020   else if (monitor_plugins) /* -P: List information about plugins continuously */
2021   {
2022     monitored_plugins = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
2023     pm = GNUNET_TRANSPORT_monitor_plugins (cfg,
2024                                            &plugin_monitoring_cb,
2025                                            NULL);
2026   }
2027   else if (iterate_validation) /* -d: Print information about validations */
2028   {
2029     vic = GNUNET_TRANSPORT_monitor_validation_entries (cfg,
2030                                                        (NULL == cpid) ? NULL : &pid,
2031                                                        GNUNET_YES, TIMEOUT,
2032                                                        &process_validation_cb, (void *) cfg);
2033   }
2034   else if (monitor_validation) /* -f: Print information about validations continuously */
2035   {
2036     vic = GNUNET_TRANSPORT_monitor_validation_entries (cfg,
2037                                                        (NULL == cpid) ? NULL : &pid,
2038                                                        GNUNET_NO, TIMEOUT,
2039                                                        &process_validation_cb, (void *) cfg);
2040   }
2041   else if (monitor_connects) /* -e : Monitor (dis)connect events continuously */
2042   {
2043     monitor_connect_counter = 0;
2044     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL,
2045                                        &monitor_notify_connect,
2046                                        &monitor_notify_disconnect);
2047     if (NULL == handle)
2048     {
2049       FPRINTF (stderr,
2050                "%s",
2051                _("Failed to connect to transport service\n"));
2052       ret = 1;
2053       return;
2054     }
2055     ret = 0;
2056   }
2057   else
2058   {
2059     GNUNET_break(0);
2060     return;
2061   }
2062
2063   end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
2064                                       &shutdown_task,
2065                                       NULL);
2066 }
2067
2068
2069 /**
2070  * Main function that will be run by the scheduler.
2071  *
2072  * @param cls closure
2073  * @param args remaining command-line arguments
2074  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2075  * @param mycfg configuration
2076  */
2077 static void
2078 run (void *cls,
2079      char * const *args,
2080      const char *cfgfile,
2081      const struct GNUNET_CONFIGURATION_Handle *mycfg)
2082 {
2083   cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
2084   if (test_configuration)
2085   {
2086     do_test_configuration (cfg);
2087     return;
2088   }
2089   GNUNET_CLIENT_service_test ("transport", cfg, GNUNET_TIME_UNIT_SECONDS,
2090       &testservice_task, (void *) cfg);
2091 }
2092
2093 int
2094 main (int argc, char * const *argv)
2095 {
2096   int res;
2097   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2098     { 'a', "all", NULL,
2099       gettext_noop ("print information for all peers (instead of only connected peers)"),
2100       0, &GNUNET_GETOPT_set_one, &iterate_all },
2101     { 'b', "benchmark", NULL,
2102       gettext_noop ("measure how fast we are receiving data from all peers (until CTRL-C)"),
2103       0, &GNUNET_GETOPT_set_one, &benchmark_receive },
2104     { 'C', "connect",
2105       NULL, gettext_noop ("connect to a peer"), 0,
2106       &GNUNET_GETOPT_set_one, &try_connect },
2107     { 'D', "disconnect",
2108       NULL, gettext_noop ("disconnect to a peer"), 0,
2109       &GNUNET_GETOPT_set_one, &try_disconnect },
2110     { 'd', "validation", NULL,
2111       gettext_noop ("print information for all pending validations "),
2112       0, &GNUNET_GETOPT_set_one, &iterate_validation },
2113     { 'f', "monitorvalidation", NULL,
2114       gettext_noop ("print information for all pending validations continuously"),
2115       0, &GNUNET_GETOPT_set_one, &monitor_validation },
2116     { 'i', "information", NULL,
2117       gettext_noop ("provide information about all current connections (once)"),
2118       0, &GNUNET_GETOPT_set_one, &iterate_connections },
2119     { 'm', "monitor", NULL,
2120       gettext_noop ("provide information about all current connections (continuously)"),
2121       0, &GNUNET_GETOPT_set_one, &monitor_connections },
2122     { 'e', "events", NULL,
2123       gettext_noop ("provide information about all connects and disconnect events (continuously)"),
2124       0, &GNUNET_GETOPT_set_one, &monitor_connects },
2125     { 'n', "numeric",
2126       NULL, gettext_noop ("do not resolve hostnames"), 0,
2127       &GNUNET_GETOPT_set_one, &numeric },
2128     { 'p', "peer", "PEER",
2129       gettext_noop ("peer identity"), 1, &GNUNET_GETOPT_set_string,
2130       &cpid },
2131     { 'P', "plugins", NULL,
2132       gettext_noop ("monitor plugin sessions"), 0, &GNUNET_GETOPT_set_one,
2133       &monitor_plugins },
2134     { 's', "send", NULL, gettext_noop
2135       ("send data for benchmarking to the other peer (until CTRL-C)"), 0,
2136       &GNUNET_GETOPT_set_one, &benchmark_send },
2137     { 't', "test", NULL,
2138       gettext_noop ("test transport configuration (involves external server)"),
2139       0, &GNUNET_GETOPT_set_one, &test_configuration },
2140     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
2141     GNUNET_GETOPT_OPTION_END
2142   };
2143
2144   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2145     return 2;
2146
2147   res = GNUNET_PROGRAM_run (argc, argv,
2148                             "gnunet-transport",
2149                             gettext_noop ("Direct access to transport service."),
2150                             options,
2151                             &run, NULL);
2152   GNUNET_free((void *) argv);
2153   if (GNUNET_OK == res)
2154     return ret;
2155   return 1;
2156 }
2157
2158 /* end of gnunet-transport.c */