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