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