last step to new cadet api
[oweals/gnunet.git] / src / transport / gnunet-transport.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2014, 2016, 2017 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, 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 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_resolver_service.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_transport_service.h"
32 #include "gnunet_transport_core_service.h"
33
34 /**
35  * Timeout for a name resolution
36  */
37 #define RESOLUTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
38
39 /**
40  * Timeout for an operation
41  */
42 #define OP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
43
44
45 /**
46  * Context to store name resolutions for valiation
47  */
48 struct ValidationResolutionContext
49 {
50   /**
51    * Next in DLL
52    */
53   struct ValidationResolutionContext *next;
54
55   /**
56    * Previous in DLL
57    */
58   struct ValidationResolutionContext *prev;
59
60   /**
61    * Address to resolve
62    */
63   struct GNUNET_HELLO_Address *addrcp;
64
65   /**
66    * Time of last validation
67    */
68   struct GNUNET_TIME_Absolute last_validation;
69
70   /**
71    * Address is valid until
72    */
73   struct GNUNET_TIME_Absolute valid_until;
74
75   /**
76    * Time of next validation
77    */
78   struct GNUNET_TIME_Absolute next_validation;
79
80   /**
81    * Tranport conversion handle
82    */
83   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
84
85   /**
86    * plugin name
87    */
88   char *transport;
89
90   /**
91    * was the entry printed
92    */
93   int printed;
94 };
95
96 /**
97  * Struct to store information about peers in monitor mode
98  */
99 struct MonitoredPeer
100 {
101   /**
102    * State of the peer
103    */
104   enum GNUNET_TRANSPORT_PeerState state;
105
106   /**
107    * Timeout
108    */
109   struct GNUNET_TIME_Absolute state_timeout;
110
111   /**
112    * The address to convert
113    */
114   struct GNUNET_HELLO_Address *address;
115 };
116
117 /**
118  * Context to store name resolutions for valiation
119  */
120 struct PeerResolutionContext
121 {
122   /**
123    * Next in DLL
124    */
125   struct PeerResolutionContext *next;
126
127   /**
128    * Prev in DLL
129    */
130   struct PeerResolutionContext *prev;
131
132   /**
133    * address to resolve
134    */
135   struct GNUNET_HELLO_Address *addrcp;
136
137   /**
138    * transport conversiion context
139    */
140   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
141
142   /**
143    * peer state
144    */
145   enum GNUNET_TRANSPORT_PeerState state;
146
147   /**
148    * state timeout
149    */
150   struct GNUNET_TIME_Absolute state_timeout;
151
152   /**
153    * transport plugin
154    */
155   char *transport;
156
157   /**
158    * was the entry printed
159    */
160   int printed;
161 };
162
163
164 /**
165  * Benchmarking block size in KB
166  */
167 #define BLOCKSIZE 4
168
169 /**
170  * Which peer should we connect to?
171  */
172 static char *cpid;
173
174 /**
175  * Handle to transport service.
176  */
177 static struct GNUNET_TRANSPORT_CoreHandle *handle;
178
179 /**
180  * Configuration handle
181  */
182 static struct GNUNET_CONFIGURATION_Handle *cfg;
183
184 /**
185  * Blacklisting handle
186  */
187 struct GNUNET_TRANSPORT_Blacklist *blacklist;
188
189 /**
190  * Option -s.
191  */
192 static int benchmark_send;
193
194 /**
195  * Option -b.
196  */
197 static int benchmark_receive;
198
199 /**
200  * Option -l.
201  */
202 static int benchmark_receive;
203
204 /**
205  * Option -i.
206  */
207 static int iterate_connections;
208
209 /**
210  * Option -a.
211  */
212 static int iterate_all;
213
214 /**
215  * Option -c.
216  */
217 static int monitor_connects;
218
219 /**
220  * Option -m.
221  */
222 static int monitor_connections;
223
224 /**
225  * Option -P.
226  */
227 static int monitor_plugins;
228
229 /**
230  * Option -D.
231  */
232 static int do_disconnect;
233
234 /**
235  * Option -n.
236  */
237 static int numeric;
238
239 /**
240  * Global return value (0 success).
241  */
242 static int ret;
243
244 /**
245  * Current number of connections in monitor mode
246  */
247 static int monitor_connect_counter;
248
249 /**
250  * Number of bytes of traffic we received so far.
251  */
252 static unsigned long long traffic_received;
253
254 /**
255  * Number of bytes of traffic we sent so far.
256  */
257 static unsigned long long traffic_sent;
258
259 /**
260  * Starting time of transmitting/receiving data.
261  */
262 static struct GNUNET_TIME_Absolute start_time;
263
264 /**
265  * Map storing information about monitored peers
266  */
267 static struct GNUNET_CONTAINER_MultiPeerMap *monitored_peers;
268
269 /**
270  * Map storing information about monitored plugins's sessions.
271  */
272 static struct GNUNET_CONTAINER_MultiPeerMap *monitored_plugins;
273
274 /**
275  * Handle if we are monitoring peers at the transport level.
276  */
277 static struct GNUNET_TRANSPORT_PeerMonitoringContext *pic;
278
279 /**
280  * Handle if we are monitoring plugin session activity.
281  */
282 static struct GNUNET_TRANSPORT_PluginMonitor *pm;
283
284 /**
285  * Identity of the peer we transmit to / connect to.
286  * (equivalent to 'cpid' string).
287  */
288 static struct GNUNET_PeerIdentity pid;
289
290 /**
291  * Task for operation timeout
292  */
293 static struct GNUNET_SCHEDULER_Task *op_timeout;
294
295 /**
296  * Selected level of verbosity.
297  */
298 static int verbosity;
299
300 /**
301  * Resolver process handle.
302  */
303 struct GNUNET_OS_Process *resolver;
304
305 /**
306  * Number of address resolutions pending
307  */
308 static unsigned int address_resolutions;
309
310 /**
311  * DLL: head of validation resolution entries
312  */
313 static struct ValidationResolutionContext *vc_head;
314
315 /**
316  * DLL: tail of validation resolution entries
317  */
318 static struct ValidationResolutionContext *vc_tail;
319
320 /**
321  * DLL: head of resolution entries
322  */
323 static struct PeerResolutionContext *rc_head;
324
325 /**
326  * DLL: head of resolution entries
327  */
328 static struct PeerResolutionContext *rc_tail;
329
330
331 /**
332  * Function called to release data stored in the #monitored_peers map.
333  *
334  * @param cls unused
335  * @param key the peer identity
336  * @param value a `struct MonitoredPeer` to release
337  * @return #GNUNET_OK (continue to iterate)
338  */
339 static int
340 destroy_it (void *cls,
341             const struct GNUNET_PeerIdentity *key,
342             void *value)
343 {
344   struct MonitoredPeer *m = value;
345
346   GNUNET_assert (GNUNET_OK ==
347                  GNUNET_CONTAINER_multipeermap_remove (monitored_peers,
348                                                        key,
349                                                        value));
350   GNUNET_free_non_null (m->address);
351   GNUNET_free (value);
352   return GNUNET_OK;
353 }
354
355
356 /**
357  * Task run in monitor mode when the user presses CTRL-C to abort.
358  * Stops monitoring activity.
359  *
360  * @param cls NULL
361  */
362 static void
363 shutdown_task (void *cls)
364 {
365   struct GNUNET_TIME_Relative duration;
366   struct ValidationResolutionContext *cur;
367   struct ValidationResolutionContext *next;
368   struct PeerResolutionContext *rc;
369
370   if (NULL != op_timeout)
371   {
372     GNUNET_SCHEDULER_cancel (op_timeout);
373     op_timeout = NULL;
374   }
375   if (NULL != pic)
376   {
377     GNUNET_TRANSPORT_monitor_peers_cancel (pic);
378     pic = NULL;
379   }
380   if (NULL != pm)
381   {
382     GNUNET_TRANSPORT_monitor_plugins_cancel (pm);
383     pm = NULL;
384   }
385
386   next = vc_head;
387   for (cur = next; NULL != cur; cur = next)
388   {
389     next = cur->next;
390
391     GNUNET_TRANSPORT_address_to_string_cancel (cur->asc);
392     GNUNET_CONTAINER_DLL_remove (vc_head,
393                                  vc_tail,
394                                  cur);
395     GNUNET_free (cur->transport);
396     GNUNET_HELLO_address_free (cur->addrcp);
397     GNUNET_free (cur);
398   }
399   while (NULL != (rc = rc_head))
400   {
401     GNUNET_CONTAINER_DLL_remove (rc_head,
402                                  rc_tail,
403                                  rc);
404     GNUNET_TRANSPORT_address_to_string_cancel (rc->asc);
405     GNUNET_free (rc->transport);
406     GNUNET_free (rc->addrcp);
407     GNUNET_free (rc);
408   }
409   if (NULL != handle)
410   {
411     GNUNET_TRANSPORT_core_disconnect (handle);
412     handle = NULL;
413   }
414   if (benchmark_send)
415   {
416     duration = GNUNET_TIME_absolute_get_duration (start_time);
417     FPRINTF (stdout,
418              _("Transmitted %llu bytes/s (%llu bytes in %s)\n"),
419              1000LL * 1000LL * traffic_sent / (1 + duration.rel_value_us),
420              traffic_sent,
421              GNUNET_STRINGS_relative_time_to_string (duration,
422                                                      GNUNET_YES));
423   }
424   if (benchmark_receive)
425   {
426     duration = GNUNET_TIME_absolute_get_duration (start_time);
427     FPRINTF (stdout,
428              _("Received %llu bytes/s (%llu bytes in %s)\n"),
429              1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us),
430              traffic_received,
431              GNUNET_STRINGS_relative_time_to_string (duration,
432                                                      GNUNET_YES));
433   }
434
435   if (NULL != monitored_peers)
436   {
437     GNUNET_CONTAINER_multipeermap_iterate (monitored_peers,
438                                            &destroy_it,
439                                            NULL);
440     GNUNET_CONTAINER_multipeermap_destroy (monitored_peers);
441     monitored_peers = NULL;
442   }
443   if (NULL != monitored_plugins)
444   {
445     GNUNET_break (0 ==
446                   GNUNET_CONTAINER_multipeermap_size (monitored_plugins));
447     GNUNET_CONTAINER_multipeermap_destroy (monitored_plugins);
448     monitored_plugins = NULL;
449   }
450   if (NULL != blacklist)
451   {
452     GNUNET_TRANSPORT_blacklist_cancel (blacklist);
453     blacklist = NULL;
454     ret = 0;
455   }
456 }
457
458
459 /**
460  * We are done, shut down.
461  */
462 static void
463 operation_timeout (void *cls)
464 {
465   struct PeerResolutionContext *cur;
466   struct PeerResolutionContext *next;
467
468   op_timeout = NULL;
469   if ((benchmark_send) || (benchmark_receive))
470   {
471     FPRINTF (stdout,
472              _("Failed to connect to `%s'\n"),
473              GNUNET_i2s_full (&pid));
474     GNUNET_SCHEDULER_shutdown ();
475     ret = 1;
476     return;
477   }
478   if (iterate_connections)
479   {
480     next = rc_head;
481     while (NULL != (cur = next))
482     {
483       next = cur->next;
484       FPRINTF (stdout,
485                _("Failed to resolve address for peer `%s'\n"),
486                GNUNET_i2s (&cur->addrcp->peer));
487
488       GNUNET_CONTAINER_DLL_remove(rc_head, 
489                                   rc_tail,
490                                   cur);
491       GNUNET_TRANSPORT_address_to_string_cancel (cur->asc);
492       GNUNET_free (cur->transport);
493       GNUNET_free (cur->addrcp);
494       GNUNET_free (cur);
495
496     }
497     FPRINTF (stdout,
498              "%s",
499              _("Failed to list connections, timeout occured\n"));
500     GNUNET_SCHEDULER_shutdown ();
501     ret = 1;
502     return;
503   }
504 }
505
506
507 /**
508  * Function called to notify a client about the socket
509  * begin ready to queue more data.  Sends another message.
510  *
511  * @param cls closure with the message queue
512  */
513 static void
514 do_send (void *cls)
515 {
516   struct GNUNET_MQ_Handle *mq = cls;
517   struct GNUNET_MessageHeader *m;
518   struct GNUNET_MQ_Envelope *env;
519
520   env = GNUNET_MQ_msg_extra (m,
521                              BLOCKSIZE * 1024,
522                               GNUNET_MESSAGE_TYPE_DUMMY);
523   memset (&m[1],
524           52,
525           BLOCKSIZE * 1024 - sizeof(struct GNUNET_MessageHeader));
526   traffic_sent += BLOCKSIZE * 1024;
527   GNUNET_MQ_notify_sent (env,
528                          &do_send,
529                          mq);
530   if (verbosity > 0)
531     FPRINTF (stdout,
532              _("Transmitting %u bytes\n"),
533              (unsigned int) BLOCKSIZE * 1024);
534   GNUNET_MQ_send (mq,
535                   env);
536 }
537
538
539 /**
540  * Function called to notify transport users that another
541  * peer connected to us.
542  *
543  * @param cls closure
544  * @param peer the peer that connected
545  * @param mq message queue for sending to @a peer
546  */
547 static void *
548 notify_connect (void *cls,
549                 const struct GNUNET_PeerIdentity *peer,
550                 struct GNUNET_MQ_Handle *mq)
551 {
552   if (0 != memcmp (&pid,
553                    peer,
554                    sizeof(struct GNUNET_PeerIdentity)))
555     return NULL;
556   ret = 0;
557   if (! benchmark_send)
558     return NULL;
559   if (NULL != op_timeout)
560   {
561     GNUNET_SCHEDULER_cancel (op_timeout);
562     op_timeout = NULL;
563   }
564   if (verbosity > 0)
565     FPRINTF (stdout,
566              _("Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"),
567              GNUNET_i2s (peer),
568              BLOCKSIZE);
569   start_time = GNUNET_TIME_absolute_get ();
570   do_send (mq);
571   return mq;
572 }
573
574
575 /**
576  * Function called to notify transport users that another
577  * peer disconnected from us.
578  *
579  * @param cls closure
580  * @param peer the peer that disconnected
581  * @param internal_cls what we returned from #notify_connect()
582  */
583 static void
584 notify_disconnect (void *cls,
585                    const struct GNUNET_PeerIdentity *peer,
586                    void *internal_cls)
587 {
588   if (0 != memcmp (&pid,
589                    peer,
590                    sizeof(struct GNUNET_PeerIdentity)))
591     return;
592   if (NULL == internal_cls)
593     return; /* not about target peer */
594   if (! benchmark_send)
595     return; /* not transmitting */
596   FPRINTF (stdout,
597            _("Disconnected from peer `%s' while benchmarking\n"),
598            GNUNET_i2s (&pid));
599 }
600
601
602 /**
603  * Function called to notify transport users that another
604  * peer connected to us.
605  *
606  * @param cls closure
607  * @param peer the peer that connected
608  * @param mq for sending messages to @a peer
609  * @return NULL
610  */
611 static void *
612 monitor_notify_connect (void *cls,
613                         const struct GNUNET_PeerIdentity *peer,
614                         struct GNUNET_MQ_Handle *mq)
615 {
616   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
617   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
618
619   monitor_connect_counter++;
620   FPRINTF (stdout,
621            _("%24s: %-17s %4s   (%u connections in total)\n"),
622            now_str,
623            _("Connected to"),
624            GNUNET_i2s (peer),
625            monitor_connect_counter);
626   return NULL;
627 }
628
629
630 /**
631  * Function called to notify transport users that another
632  * peer disconnected from us.
633  *
634  * @param cls closure
635  * @param peer the peer that disconnected
636  * @param internal_cls what we returned from #monitor_notify_connect()
637  */
638 static void
639 monitor_notify_disconnect (void *cls,
640                            const struct GNUNET_PeerIdentity *peer,
641                            void *internal_cls)
642 {
643   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
644   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
645
646   GNUNET_assert(monitor_connect_counter > 0);
647   monitor_connect_counter--;
648
649   FPRINTF (stdout,
650            _("%24s: %-17s %4s   (%u connections in total)\n"),
651            now_str,
652            _("Disconnected from"),
653            GNUNET_i2s (peer),
654            monitor_connect_counter);
655 }
656
657
658 /**
659  * Function called by the transport for each received message.
660  *
661  * @param cls closure
662  * @param message the message
663  * @return #GNUNET_OK
664  */
665 static int
666 check_dummy (void *cls,
667              const struct GNUNET_MessageHeader *message)
668 {
669   return GNUNET_OK; /* all messages are fine */
670 }
671
672
673 /**
674  * Function called by the transport for each received message.
675  *
676  * @param cls closure
677  * @param message the message
678  */
679 static void
680 handle_dummy (void *cls,
681               const struct GNUNET_MessageHeader *message)
682 {
683   if (! benchmark_receive)
684     return;
685   if (verbosity > 0)
686     FPRINTF (stdout,
687              _("Received %u bytes\n"),
688              (unsigned int) ntohs (message->size));
689   if (0 == traffic_received)
690     start_time = GNUNET_TIME_absolute_get ();
691   traffic_received += ntohs (message->size);
692 }
693
694
695 /**
696  * Convert address to a printable format.
697  *
698  * @param address the address
699  * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO
700  *                to try to use reverse DNS
701  * @param state state the peer is in
702  * @param state_timeout when will the peer's state expire
703  */
704 static void
705 resolve_peer_address (const struct GNUNET_HELLO_Address *address,
706                       int numeric,
707                       enum GNUNET_TRANSPORT_PeerState state,
708                       struct GNUNET_TIME_Absolute state_timeout);
709
710
711 static void
712 print_info (const struct GNUNET_PeerIdentity *id,
713             const char *transport,
714             const char *addr,
715             enum GNUNET_TRANSPORT_PeerState state,
716             struct GNUNET_TIME_Absolute state_timeout)
717 {
718
719   if ( ((GNUNET_YES == iterate_connections) &&
720         (GNUNET_YES == iterate_all)) ||
721        (GNUNET_YES == monitor_connections))
722   {
723     FPRINTF (stdout,
724              _("Peer `%s': %s %s in state `%s' until %s\n"),
725              GNUNET_i2s (id),
726              (NULL == transport) ? "<none>" : transport,
727              (NULL == transport) ? "<none>" : addr,
728              GNUNET_TRANSPORT_ps2s (state),
729              GNUNET_STRINGS_absolute_time_to_string (state_timeout));
730   }
731   else if ( (GNUNET_YES == iterate_connections) &&
732             (GNUNET_TRANSPORT_is_connected(state)) ) 
733   {
734     /* Only connected peers, skip state */
735     FPRINTF (stdout,
736              _("Peer `%s': %s %s\n"),
737              GNUNET_i2s (id),
738              transport,
739              addr);
740   }
741 }
742
743
744 /**
745  * Function called with a textual representation of an address.  This
746  * function will be called several times with different possible
747  * textual representations, and a last time with @a address being NULL
748  * to signal the end of the iteration.  Note that @a address NULL
749  * always is the last call, regardless of the value in @a res.
750  *
751  * @param cls closure
752  * @param address NULL on end of iteration,
753  *        otherwise 0-terminated printable UTF-8 string,
754  *        in particular an empty string if @a res is #GNUNET_NO
755  * @param res result of the address to string conversion:
756  *        if #GNUNET_OK: conversion successful
757  *        if #GNUNET_NO: address was invalid (or not supported)
758  *        if #GNUNET_SYSERR: communication error (IPC error)
759  */
760 static void
761 process_peer_string (void *cls,
762                      const char *address,
763                      int res)
764 {
765   struct PeerResolutionContext *rc = cls;
766
767   if (NULL != address)
768   {
769     if (GNUNET_SYSERR == res)
770     {
771       FPRINTF (stderr,
772                "Failed to convert address for peer `%s' plugin `%s' length %u to string \n",
773                GNUNET_i2s (&rc->addrcp->peer),
774                rc->addrcp->transport_name,
775                (unsigned int) rc->addrcp->address_length);
776       print_info (&rc->addrcp->peer,
777                   rc->transport,
778                   NULL,
779                   rc->state,
780                   rc->state_timeout);
781       rc->printed = GNUNET_YES;
782       return;
783     }
784     if (GNUNET_OK == res)
785     {
786       print_info (&rc->addrcp->peer,
787                   rc->transport,
788                   address,
789                   rc->state,
790                   rc->state_timeout);
791       rc->printed = GNUNET_YES;
792       return; /* Wait for done call */
793     }
794     /* GNUNET_NO == res: ignore, was simply not supported */
795     return;
796   }
797   /* NULL == address, last call, we are done */
798
799   rc->asc = NULL;
800   GNUNET_assert (address_resolutions > 0);
801   address_resolutions--;
802   if (GNUNET_NO == rc->printed)
803   {
804     if (numeric == GNUNET_NO)
805     {
806       /* Failed to resolve address, try numeric lookup
807          (note: this should not be needed, as transport
808          should fallback to numeric conversion if DNS takes
809          too long) */
810       resolve_peer_address (rc->addrcp,
811                             GNUNET_YES,
812                             rc->state,
813                             rc->state_timeout);
814     }
815     else
816     {
817       print_info (&rc->addrcp->peer,
818                   rc->transport,
819                   NULL,
820                   rc->state,
821                   rc->state_timeout);
822     }
823   }
824   GNUNET_free (rc->transport);
825   GNUNET_free (rc->addrcp);
826   GNUNET_CONTAINER_DLL_remove (rc_head,
827                                rc_tail,
828                                rc);
829   GNUNET_free (rc);
830   if ((0 == address_resolutions) && (iterate_connections))
831   {
832     if (NULL != op_timeout)
833     {
834       GNUNET_SCHEDULER_cancel (op_timeout);
835       op_timeout = NULL;
836     }
837     ret = 0;
838     GNUNET_SCHEDULER_shutdown ();
839   }
840 }
841
842
843 /**
844  * Convert address to a printable format and print it
845  * together with the given state data.
846  *
847  * @param address the address
848  * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO
849  *                to try to use reverse DNS
850  * @param state state the peer is in
851  * @param state_timeout when will the peer's state expire
852  */
853 static void
854 resolve_peer_address (const struct GNUNET_HELLO_Address *address,
855                       int numeric,
856                       enum GNUNET_TRANSPORT_PeerState state,
857                       struct GNUNET_TIME_Absolute state_timeout)
858 {
859   struct PeerResolutionContext *rc;
860
861   rc = GNUNET_new (struct PeerResolutionContext);
862   GNUNET_CONTAINER_DLL_insert (rc_head,
863                                rc_tail,
864                                rc);
865   address_resolutions++;
866   rc->transport = GNUNET_strdup (address->transport_name);
867   rc->addrcp = GNUNET_HELLO_address_copy (address);
868   rc->printed = GNUNET_NO;
869   rc->state = state;
870   rc->state_timeout = state_timeout;
871   /* Resolve address to string */
872   rc->asc = GNUNET_TRANSPORT_address_to_string (cfg,
873                                                 address,
874                                                 numeric,
875                                                 RESOLUTION_TIMEOUT,
876                                                 &process_peer_string,
877                                                 rc);
878 }
879
880
881 /**
882  * Function called with information about a peers during a one shot iteration
883  *
884  * @param cls closure
885  * @param peer identity of the peer, NULL for final callback when operation done
886  * @param address binary address used to communicate with this peer,
887  *  NULL on disconnect or when done
888  * @param state current state this peer is in
889  * @param state_timeout time out for the current state
890  */
891 static void
892 process_peer_iteration_cb (void *cls,
893                            const struct GNUNET_PeerIdentity *peer,
894                            const struct GNUNET_HELLO_Address *address,
895                            enum GNUNET_TRANSPORT_PeerState state,
896                            struct GNUNET_TIME_Absolute state_timeout)
897 {
898   if (NULL == peer)
899   {
900     /* done */
901     pic = NULL;
902     return;
903   }
904
905   if ( (GNUNET_NO == iterate_all) &&
906        (GNUNET_NO == GNUNET_TRANSPORT_is_connected(state)))
907       return; /* Display only connected peers */
908
909   if (NULL != op_timeout)
910     GNUNET_SCHEDULER_cancel (op_timeout);
911   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
912                                              &operation_timeout,
913                                              NULL);
914
915   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
916               "Received address for peer `%s': %s\n",
917               GNUNET_i2s (peer),
918               address ? address->transport_name : "");
919
920   if (NULL != address)
921     resolve_peer_address (address,
922                           numeric,
923                           state,
924                           state_timeout);
925   else
926     print_info (peer,
927                 NULL,
928                 NULL,
929                 state,
930                 state_timeout);
931 }
932
933
934 /**
935  * Context for address resolution by #plugin_monitoring_cb().
936  */
937 struct PluginMonitorAddress
938 {
939
940   /**
941    * Ongoing resolution request.
942    */
943   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
944
945   /**
946    * Resolved address as string.
947    */
948   char *str;
949
950   /**
951    * Last event we got and did not yet print because
952    * @e str was NULL (address not yet resolved).
953    */
954   struct GNUNET_TRANSPORT_SessionInfo si;
955 };
956
957
958 /**
959  * Print information about a plugin monitoring event.
960  *
961  * @param addr out internal context
962  * @param info the monitoring information
963  */
964 static void
965 print_plugin_event_info (struct PluginMonitorAddress *addr,
966                          const struct GNUNET_TRANSPORT_SessionInfo *info)
967 {
968   const char *state;
969
970   switch (info->state)
971   {
972   case GNUNET_TRANSPORT_SS_INIT:
973     state = "INIT";
974     break;
975   case GNUNET_TRANSPORT_SS_HANDSHAKE:
976     state = "HANDSHAKE";
977     break;
978   case GNUNET_TRANSPORT_SS_UP:
979     state = "UP";
980     break;
981   case GNUNET_TRANSPORT_SS_UPDATE:
982     state = "UPDATE";
983     break;
984   case GNUNET_TRANSPORT_SS_DONE:
985     state = "DONE";
986     break;
987   default:
988     state = "UNKNOWN";
989     break;
990   }
991   fprintf (stdout,
992            "%s: state %s timeout in %s @ %s%s\n",
993            GNUNET_i2s (&info->address->peer),
994            state,
995            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (info->session_timeout),
996                                                    GNUNET_YES),
997            addr->str,
998            (info->is_inbound == GNUNET_YES) ? " (INBOUND)" : "");
999   fprintf (stdout,
1000            "%s: queue has %3u messages and %6u bytes\n",
1001            GNUNET_i2s (&info->address->peer),
1002            info->num_msg_pending,
1003            info->num_bytes_pending);
1004   if (0 != GNUNET_TIME_absolute_get_remaining (info->receive_delay).rel_value_us)
1005     fprintf (stdout,
1006              "%s: receiving blocked until %s\n",
1007              GNUNET_i2s (&info->address->peer),
1008              GNUNET_STRINGS_absolute_time_to_string (info->receive_delay));
1009 }
1010
1011
1012 /**
1013  * Function called with a textual representation of an address.  This
1014  * function will be called several times with different possible
1015  * textual representations, and a last time with @a address being NULL
1016  * to signal the end of the iteration.  Note that @a address NULL
1017  * always is the last call, regardless of the value in @a res.
1018  *
1019  * @param cls closure
1020  * @param address NULL on end of iteration,
1021  *        otherwise 0-terminated printable UTF-8 string,
1022  *        in particular an empty string if @a res is #GNUNET_NO
1023  * @param res result of the address to string conversion:
1024  *        if #GNUNET_OK: conversion successful
1025  *        if #GNUNET_NO: address was invalid (or not supported)
1026  *        if #GNUNET_SYSERR: communication error (IPC error)
1027  */
1028 static void
1029 address_cb (void *cls,
1030             const char *address,
1031             int res)
1032 {
1033   struct PluginMonitorAddress *addr = cls;
1034
1035   if (NULL == address)
1036   {
1037     addr->asc = NULL;
1038     return;
1039   }
1040   if (NULL != addr->str)
1041     return;
1042   addr->str = GNUNET_strdup (address);
1043   print_plugin_event_info (addr,
1044                            &addr->si);
1045 }
1046
1047
1048 /**
1049  * Function called by the plugin with information about the
1050  * current sessions managed by the plugin (for monitoring).
1051  *
1052  * @param cls closure (NULL)
1053  * @param session session handle this information is about,
1054  *        NULL to indicate that we are "in sync" (initial
1055  *        iteration complete)
1056  * @param session_ctx storage location where the application
1057  *        can store data; will point to NULL on #GNUNET_TRANSPORT_SS_INIT,
1058  *        and must be reset to NULL on #GNUNET_TRANSPORT_SS_DONE
1059  * @param info information about the state of the session,
1060  *        NULL if @a session is also NULL and we are
1061  *        merely signalling that the initial iteration is over;
1062  *        NULL with @a session being non-NULL if the monitor
1063  *        was being cancelled while sessions were active
1064  */
1065 static void
1066 plugin_monitoring_cb (void *cls,
1067                       struct GNUNET_TRANSPORT_PluginSession *session,
1068                       void **session_ctx,
1069                       const struct GNUNET_TRANSPORT_SessionInfo *info)
1070 {
1071   struct PluginMonitorAddress *addr;
1072
1073   if ( (NULL == info) &&
1074        (NULL == session) )
1075     return; /* in sync with transport service */
1076   addr = *session_ctx;
1077   if (NULL == info)
1078   {
1079     if (NULL != addr)
1080     {
1081       if (NULL != addr->asc)
1082       {
1083         GNUNET_TRANSPORT_address_to_string_cancel (addr->asc);
1084         addr->asc = NULL;
1085       }
1086       GNUNET_free_non_null (addr->str);
1087       GNUNET_free (addr);
1088       *session_ctx = NULL;
1089     }
1090     return; /* shutdown */
1091   }
1092   if ( (NULL != cpid) &&
1093        (0 != memcmp (&info->address->peer,
1094                      cpid,
1095                      sizeof (struct GNUNET_PeerIdentity))) )
1096     return; /* filtered */
1097   if (NULL == addr)
1098   {
1099     addr = GNUNET_new (struct PluginMonitorAddress);
1100     addr->asc = GNUNET_TRANSPORT_address_to_string (cfg,
1101                                                     info->address,
1102                                                     numeric,
1103                                                     GNUNET_TIME_UNIT_FOREVER_REL,
1104                                                     &address_cb,
1105                                                     addr);
1106     *session_ctx = addr;
1107   }
1108   if (NULL == addr->str)
1109     addr->si = *info;
1110   else
1111     print_plugin_event_info (addr,
1112                              info);
1113   if (GNUNET_TRANSPORT_SS_DONE == info->state)
1114   {
1115     if (NULL != addr->asc)
1116     {
1117       GNUNET_TRANSPORT_address_to_string_cancel (addr->asc);
1118       addr->asc = NULL;
1119     }
1120     GNUNET_free_non_null (addr->str);
1121     GNUNET_free (addr);
1122     *session_ctx = NULL;
1123   }
1124 }
1125
1126
1127 /**
1128  * Function called with information about a peers
1129  *
1130  * @param cls closure, NULL
1131  * @param peer identity of the peer, NULL for final callback when operation done
1132  * @param address binary address used to communicate with this peer,
1133  *  NULL on disconnect or when done
1134  * @param state current state this peer is in
1135  * @param state_timeout time out for the current state
1136  */
1137 static void
1138 process_peer_monitoring_cb (void *cls,
1139                             const struct GNUNET_PeerIdentity *peer,
1140                             const struct GNUNET_HELLO_Address *address,
1141                             enum GNUNET_TRANSPORT_PeerState state,
1142                             struct GNUNET_TIME_Absolute state_timeout)
1143 {
1144   struct MonitoredPeer *m;
1145
1146   if (NULL == peer)
1147   {
1148     FPRINTF (stdout,
1149              "%s",
1150              _("Monitor disconnected from transport service. Reconnecting.\n"));
1151     return;
1152   }
1153
1154   if (NULL != op_timeout)
1155     GNUNET_SCHEDULER_cancel (op_timeout);
1156   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
1157                                              &operation_timeout,
1158                                              NULL);
1159
1160   if (NULL == (m = GNUNET_CONTAINER_multipeermap_get (monitored_peers,
1161                                                       peer)))
1162   {
1163     m = GNUNET_new (struct MonitoredPeer);
1164     GNUNET_CONTAINER_multipeermap_put (monitored_peers,
1165                                        peer,
1166                                        m,
1167                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1168   }
1169   else
1170   {
1171     if ( (m->state == state) &&
1172          (m->state_timeout.abs_value_us == state_timeout.abs_value_us) &&
1173          (NULL == address) &&
1174          (NULL == m->address) )
1175     {
1176       return; /* No real change */
1177     }
1178     if ( (m->state == state) &&
1179          (NULL != address) &&
1180          (NULL != m->address) &&
1181          (0 == GNUNET_HELLO_address_cmp(m->address, address)) )
1182       return; /* No real change */
1183   }
1184
1185   if (NULL != m->address)
1186   {
1187     GNUNET_free (m->address);
1188     m->address = NULL;
1189   }
1190   if (NULL != address)
1191     m->address = GNUNET_HELLO_address_copy (address);
1192   m->state = state;
1193   m->state_timeout = state_timeout;
1194
1195   if (NULL != address)
1196     resolve_peer_address (m->address,
1197                           numeric,
1198                           m->state,
1199                           m->state_timeout);
1200   else
1201     print_info (peer,
1202                 NULL,
1203                 NULL,
1204                 m->state,
1205                 m->state_timeout);
1206 }
1207
1208
1209 /**
1210  * Function called with the transport service checking if we
1211  * want to blacklist a peer. Return #GNUNET_SYSERR for the
1212  * peer that we should disconnect from.
1213  *
1214  * @param cls NULL
1215  * @param cpid peer to check blacklisting for
1216  * @return #GNUNET_OK if the connection is allowed, #GNUNET_SYSERR if not
1217  */
1218 static int
1219 blacklist_cb (void *cls,
1220               const struct GNUNET_PeerIdentity *cpid)
1221 {
1222   if (0 == memcmp (cpid,
1223                    &pid,
1224                    sizeof (struct GNUNET_PeerIdentity)))
1225     return GNUNET_SYSERR;
1226   return GNUNET_OK;
1227 }
1228
1229
1230 /**
1231  * Main function that will be run by the scheduler.
1232  *
1233  * @param cls closure
1234  * @param args remaining command-line arguments
1235  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1236  * @param mycfg configuration
1237  */
1238 static void
1239 run (void *cls,
1240      char * const *args,
1241      const char *cfgfile,
1242      const struct GNUNET_CONFIGURATION_Handle *mycfg)
1243 {
1244   int counter = 0;
1245   ret = 1;
1246
1247   cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
1248   if ( (NULL != cpid) &&
1249        (GNUNET_OK !=
1250         GNUNET_CRYPTO_eddsa_public_key_from_string (cpid,
1251                                                     strlen (cpid),
1252                                                     &pid.public_key)))
1253   {
1254     FPRINTF (stderr,
1255              _("Failed to parse peer identity `%s'\n"),
1256              cpid);
1257     return;
1258   }
1259
1260   counter = benchmark_send + benchmark_receive + iterate_connections
1261       + monitor_connections + monitor_connects + do_disconnect +
1262       monitor_plugins;
1263
1264   if (1 < counter)
1265   {
1266     FPRINTF (stderr,
1267              _("Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s %s\n"),
1268              "disconnect",
1269              "benchmark send",
1270              "benchmark receive",
1271              "information",
1272              "monitor",
1273              "events",
1274              "plugins");
1275     return;
1276   }
1277   if (0 == counter)
1278   {
1279     FPRINTF (stderr,
1280              _("No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s, %s\n"),
1281              "disconnect",
1282              "benchmark send",
1283              "benchmark receive",
1284              "information",
1285              "monitor",
1286              "events",
1287              "plugins");
1288     return;
1289   }
1290
1291   if (do_disconnect) /* -D: Disconnect from peer */
1292   {
1293     if (NULL == cpid)
1294     {
1295       FPRINTF (stderr,
1296                _("Option `%s' makes no sense without option `%s'.\n"),
1297                "-D", "-p");
1298       ret = 1;
1299       return;
1300     }
1301     blacklist = GNUNET_TRANSPORT_blacklist (cfg,
1302                                             &blacklist_cb,
1303                                             NULL);
1304     if (NULL == blacklist)
1305     {
1306       FPRINTF (stderr,
1307                "%s",
1308                _("Failed to connect to transport service for disconnection\n"));
1309       ret = 1;
1310       return;
1311     }
1312     FPRINTF (stdout,
1313              "%s",
1314              _("Blacklisting request in place, stop with CTRL-C\n"));
1315   }
1316   else if (benchmark_send) /* -s: Benchmark sending */
1317   {
1318     if (NULL == cpid)
1319     {
1320       FPRINTF (stderr,
1321                _("Option `%s' makes no sense without option `%s'.\n"),
1322                "-s", "-p");
1323       ret = 1;
1324       return;
1325     }
1326     handle = GNUNET_TRANSPORT_core_connect (cfg,
1327                                             NULL,
1328                                             NULL,
1329                                             NULL,
1330                                             &notify_connect,
1331                                             &notify_disconnect,
1332                                             NULL);
1333     if (NULL == handle)
1334     {
1335       FPRINTF (stderr,
1336                "%s",
1337                _("Failed to connect to transport service\n"));
1338       ret = 1;
1339       return;
1340     }
1341     start_time = GNUNET_TIME_absolute_get ();
1342     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
1343                                                &operation_timeout,
1344                                                NULL);
1345   }
1346   else if (benchmark_receive) /* -b: Benchmark receiving */
1347   {
1348     struct GNUNET_MQ_MessageHandler handlers[] = {
1349       GNUNET_MQ_hd_var_size (dummy,
1350                              GNUNET_MESSAGE_TYPE_DUMMY,
1351                              struct GNUNET_MessageHeader,
1352                              NULL),
1353       GNUNET_MQ_handler_end ()
1354     };
1355     
1356     handle = GNUNET_TRANSPORT_core_connect (cfg,
1357                                             NULL,
1358                                             handlers,
1359                                             NULL,
1360                                             NULL,
1361                                             NULL,
1362                                             NULL);
1363     if (NULL == handle)
1364     {
1365       FPRINTF (stderr,
1366                "%s",
1367                _("Failed to connect to transport service\n"));
1368       ret = 1;
1369       return;
1370     }
1371     if (verbosity > 0)
1372       FPRINTF (stdout,
1373                "%s",
1374                _("Starting to receive benchmark data\n"));
1375     start_time = GNUNET_TIME_absolute_get ();
1376
1377   }
1378   else if (iterate_connections) /* -i: List information about peers once */
1379   {
1380     pic = GNUNET_TRANSPORT_monitor_peers (cfg,
1381                                           (NULL == cpid) ? NULL : &pid,
1382                                           GNUNET_YES,
1383                                           &process_peer_iteration_cb,
1384                                           (void *) cfg);
1385     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT,
1386                                                &operation_timeout,
1387                                                NULL);
1388   }
1389   else if (monitor_connections) /* -m: List information about peers continuously */
1390   {
1391     monitored_peers = GNUNET_CONTAINER_multipeermap_create (10,
1392                                                             GNUNET_NO);
1393     pic = GNUNET_TRANSPORT_monitor_peers (cfg,
1394                                           (NULL == cpid) ? NULL : &pid,
1395                                           GNUNET_NO,
1396                                           &process_peer_monitoring_cb,
1397                                           NULL);
1398   }
1399   else if (monitor_plugins) /* -P: List information about plugins continuously */
1400   {
1401     monitored_plugins = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
1402     pm = GNUNET_TRANSPORT_monitor_plugins (cfg,
1403                                            &plugin_monitoring_cb,
1404                                            NULL);
1405   }
1406   else if (monitor_connects) /* -e : Monitor (dis)connect events continuously */
1407   {
1408     monitor_connect_counter = 0;
1409     handle = GNUNET_TRANSPORT_core_connect (cfg,
1410                                             NULL,
1411                                             NULL,
1412                                             NULL,
1413                                             &monitor_notify_connect,
1414                                             &monitor_notify_disconnect,
1415                                             NULL);
1416     if (NULL == handle)
1417     {
1418       FPRINTF (stderr,
1419                "%s",
1420                _("Failed to connect to transport service\n"));
1421       ret = 1;
1422       return;
1423     }
1424     ret = 0;
1425   }
1426   else
1427   {
1428     GNUNET_break(0);
1429     return;
1430   }
1431
1432   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1433                                  NULL);
1434 }
1435
1436
1437 int
1438 main (int argc,
1439       char * const *argv)
1440 {
1441   int res;
1442   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1443     { 'a', "all", NULL,
1444       gettext_noop ("print information for all peers (instead of only connected peers)"),
1445       0, &GNUNET_GETOPT_set_one, &iterate_all },
1446     { 'b', "benchmark", NULL,
1447       gettext_noop ("measure how fast we are receiving data from all peers (until CTRL-C)"),
1448       0, &GNUNET_GETOPT_set_one, &benchmark_receive },
1449     { 'D', "disconnect",
1450       NULL, gettext_noop ("disconnect from a peer"), 0,
1451       &GNUNET_GETOPT_set_one, &do_disconnect },
1452     { 'i', "information", NULL,
1453       gettext_noop ("provide information about all current connections (once)"),
1454       0, &GNUNET_GETOPT_set_one, &iterate_connections },
1455     { 'm', "monitor", NULL,
1456       gettext_noop ("provide information about all current connections (continuously)"),
1457       0, &GNUNET_GETOPT_set_one, &monitor_connections },
1458     { 'e', "events", NULL,
1459       gettext_noop ("provide information about all connects and disconnect events (continuously)"),
1460       0, &GNUNET_GETOPT_set_one, &monitor_connects },
1461     { 'n', "numeric",
1462       NULL, gettext_noop ("do not resolve hostnames"), 0,
1463       &GNUNET_GETOPT_set_one, &numeric },
1464     { 'p', "peer", "PEER",
1465       gettext_noop ("peer identity"), 1, &GNUNET_GETOPT_set_string,
1466       &cpid },
1467     { 'P', "plugins", NULL,
1468       gettext_noop ("monitor plugin sessions"), 0, &GNUNET_GETOPT_set_one,
1469       &monitor_plugins },
1470     { 's', "send", NULL, gettext_noop
1471       ("send data for benchmarking to the other peer (until CTRL-C)"), 0,
1472       &GNUNET_GETOPT_set_one, &benchmark_send },
1473     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
1474     GNUNET_GETOPT_OPTION_END
1475   };
1476
1477   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1478     return 2;
1479
1480   res = GNUNET_PROGRAM_run (argc, argv,
1481                             "gnunet-transport",
1482                             gettext_noop ("Direct access to transport service."),
1483                             options,
1484                             &run, NULL);
1485   GNUNET_free ((void *) argv);
1486   if (GNUNET_OK == res)
1487     return ret;
1488   return 1;
1489 }
1490
1491 /* end of gnunet-transport.c */