- don't try to rekey loopback tunnels (#3254)
[oweals/gnunet.git] / src / mesh / gnunet-mesh.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file mesh/gnunet-mesh.c
23  * @brief Print information about mesh tunnels and peers.
24  * @author Bartlomiej Polot
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_mesh_service.h"
29
30
31 /**
32  * Option -m.
33  */
34 static int monitor_connections;
35
36 /**
37  * Option -i.
38  */
39 static int get_info;
40
41 /**
42  * Option --tunnel
43  */
44 static char *tunnel_id;
45
46 /**
47  * Option --connection
48  */
49 static char *conn_id;
50
51 /**
52  * Option --channel
53  */
54 static char *channel_id;
55
56 /**
57  * Port to listen on (-p).
58  */
59 static uint32_t listen_port;
60
61 /**
62  * Request echo service
63  */
64 int echo;
65
66 /**
67  * Time of last echo request.
68  */
69 struct GNUNET_TIME_Absolute echo_time;
70
71 /**
72  * Task for next echo request.
73  */
74 GNUNET_SCHEDULER_TaskIdentifier echo_task;
75
76 /**
77  * Peer to connect to.
78  */
79 static char *target_id;
80
81 /**
82  * Port to connect to
83  */
84 static uint32_t target_port;
85
86 /**
87  * Data pending in netcat mode.
88  */
89 size_t data_size;
90
91
92 /**
93  * Mesh handle.
94  */
95 static struct GNUNET_MESH_Handle *mh;
96
97 /**
98  * Channel handle.
99  */
100 static struct GNUNET_MESH_Channel *ch;
101
102 /**
103  * Shutdown task handle.
104  */
105 GNUNET_SCHEDULER_TaskIdentifier sd;
106
107
108
109 static void
110 listen_stdio (void);
111
112
113
114 /**
115  * Task run in monitor mode when the user presses CTRL-C to abort.
116  * Stops monitoring activity.
117  *
118  * @param cls Closure (unused).
119  * @param tc scheduler context
120  */
121 static void
122 shutdown_task (void *cls,
123                const struct GNUNET_SCHEDULER_TaskContext *tc)
124 {
125   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
126   if (NULL != ch)
127   {
128     GNUNET_MESH_channel_destroy (ch);
129     ch = NULL;
130   }
131   if (NULL != mh)
132   {
133     GNUNET_MESH_disconnect (mh);
134         mh = NULL;
135   }
136 }
137
138
139 /**
140  * Function called to notify a client about the connection
141  * begin ready to queue more data.  "buf" will be
142  * NULL and "size" zero if the connection was closed for
143  * writing in the meantime.
144  *
145  * FIXME
146  *
147  * @param cls closure
148  * @param size number of bytes available in buf
149  * @param buf where the callee should write the message
150  * @return number of bytes written to buf
151  */
152 size_t
153 data_ready (void *cls, size_t size, void *buf)
154 {
155   struct GNUNET_MessageHeader *msg;
156   size_t total_size;
157
158   if (NULL == buf || 0 == size)
159   {
160     GNUNET_SCHEDULER_shutdown();
161     return 0;
162   }
163
164   total_size = data_size + sizeof (struct GNUNET_MessageHeader);
165   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sending %u bytes\n", data_size);
166   GNUNET_assert (size >= total_size);
167
168   msg = buf;
169   msg->size = htons (total_size);
170   msg->type = htons (GNUNET_MESSAGE_TYPE_MESH_CLI);
171   memcpy (&msg[1], cls, data_size);
172   if (GNUNET_NO == echo)
173   {
174     listen_stdio ();
175   }
176   else
177   {
178     echo_time = GNUNET_TIME_absolute_get ();
179   }
180
181   return total_size;
182 }
183
184
185 /**
186  * Task run in monitor mode when the user presses CTRL-C to abort.
187  * Stops monitoring activity.
188  *
189  * @param cls Closure (unused).
190  * @param tc scheduler context
191  */
192 static void
193 read_stdio (void *cls,
194             const struct GNUNET_SCHEDULER_TaskContext *tc)
195 {
196   static char buf[60000];
197
198   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
199   {
200     return;
201   }
202
203   data_size = read (0, buf, 60000);
204   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stdio read %u bytes\n", data_size);
205   if (data_size < 1)
206   {
207     GNUNET_SCHEDULER_shutdown();
208     return;
209   }
210   GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
211                                      GNUNET_TIME_UNIT_FOREVER_REL,
212                                      data_size
213                                      + sizeof (struct GNUNET_MessageHeader),
214                                      &data_ready, buf);
215 }
216
217
218 /**
219  * Start listening to stdin
220  */
221 static void
222 listen_stdio (void)
223 {
224   struct GNUNET_NETWORK_FDSet *rs;
225
226   rs = GNUNET_NETWORK_fdset_create ();
227   GNUNET_NETWORK_fdset_set_native (rs, 0);
228   GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
229                                GNUNET_TIME_UNIT_FOREVER_REL,
230                                rs, NULL,
231                                &read_stdio, NULL);
232   GNUNET_NETWORK_fdset_destroy (rs);
233 }
234
235
236 /**
237  * Function called whenever a channel is destroyed.  Should clean up
238  * any associated state.
239  *
240  * It must NOT call #GNUNET_MESH_channel_destroy on the channel.
241  *
242  * @param cls closure (set from #GNUNET_MESH_connect)
243  * @param channel connection to the other end (henceforth invalid)
244  * @param channel_ctx place where local state associated
245  *                   with the channel is stored
246  */
247 static void
248 channel_ended (void *cls,
249                const struct GNUNET_MESH_Channel *channel,
250                void *channel_ctx)
251 {
252   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n");
253   GNUNET_break (channel == ch);
254   ch = NULL;
255   GNUNET_SCHEDULER_shutdown ();
256 }
257
258
259 /**
260  * Method called whenever another peer has added us to a channel
261  * the other peer initiated.
262  * Only called (once) upon reception of data with a message type which was
263  * subscribed to in #GNUNET_MESH_connect.
264  *
265  * A call to #GNUNET_MESH_channel_destroy causes te channel to be ignored. In
266  * this case the handler MUST return NULL.
267  *
268  * @param cls closure
269  * @param channel new handle to the channel
270  * @param initiator peer that started the channel
271  * @param port Port this channel is for.
272  * @param options MeshOption flag field, with all active option bits set to 1.
273  *
274  * @return initial channel context for the channel
275  *         (can be NULL -- that's not an error)
276  */
277 static void *
278 channel_incoming (void *cls,
279                   struct GNUNET_MESH_Channel * channel,
280                   const struct GNUNET_PeerIdentity * initiator,
281                   uint32_t port, enum GNUNET_MESH_ChannelOption options)
282 {
283   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284               "Incoming channel %p on port %u\n",
285               channel, port);
286   if (NULL != ch)
287   {
288     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "A channel already exists\n");
289     return NULL;
290   }
291   if (0 == listen_port)
292   {
293     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not listening to channels\n");
294     return NULL;
295   }
296   ch = channel;
297   if (GNUNET_NO == echo)
298   {
299     listen_stdio ();
300     return NULL;
301   }
302   data_size = 0;
303   return NULL;
304 }
305
306 /**
307  * @brief Send an echo request to the remote peer.
308  *
309  * @param cls Closure (NULL).
310  * @param tc Task context.
311  */
312 static void
313 send_echo (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
314 {
315   GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
316                                      GNUNET_TIME_UNIT_FOREVER_REL,
317                                      sizeof (struct GNUNET_MessageHeader),
318                                      &data_ready, NULL);
319 }
320
321
322
323 /**
324  * Call MESH's monitor API, get info of one connection.
325  *
326  * @param cls Closure (unused).
327  * @param tc TaskContext
328  */
329 static void
330 create_channel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
331 {
332   struct GNUNET_PeerIdentity pid;
333   enum GNUNET_MESH_ChannelOption opt;
334
335   GNUNET_assert (NULL == ch);
336
337   if (GNUNET_OK !=
338       GNUNET_CRYPTO_eddsa_public_key_from_string (target_id,
339                                                   strlen (target_id),
340                                                   &pid.public_key))
341   {
342     FPRINTF (stderr,
343              _("Invalid target `%s'\n"),
344              target_id);
345     GNUNET_SCHEDULER_shutdown ();
346     return;
347   }
348   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to `%s'\n", target_id);
349   opt = GNUNET_MESH_OPTION_DEFAULT | GNUNET_MESH_OPTION_RELIABLE;
350   ch = GNUNET_MESH_channel_create (mh, NULL, &pid, target_port, opt);
351   if (GNUNET_NO == echo)
352     listen_stdio ();
353   else
354     GNUNET_SCHEDULER_add_now (send_echo, NULL);
355 }
356
357
358 /**
359  * Function called whenever a message is received.
360  *
361  * Each time the function must call #GNUNET_MESH_receive_done on the channel
362  * in order to receive the next message. This doesn't need to be immediate:
363  * can be delayed if some processing is done on the message.
364  *
365  * @param cls Closure (set from #GNUNET_MESH_connect).
366  * @param channel Connection to the other end.
367  * @param channel_ctx Place to store local state associated with the channel.
368  * @param message The actual message.
369  * @return #GNUNET_OK to keep the channel open,
370  *         #GNUNET_SYSERR to close it (signal serious error).
371  */
372 static int
373 data_callback (void *cls,
374                struct GNUNET_MESH_Channel *channel,
375                void **channel_ctx,
376                const struct GNUNET_MessageHeader *message)
377 {
378   uint16_t len;
379   ssize_t done;
380   uint16_t off;
381   const char *buf;
382   GNUNET_break (ch == channel);
383
384   if (GNUNET_YES == echo)
385   {
386     if (0 != listen_port)
387     {
388       /* Just listening to echo incoming messages*/
389       GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
390                                         GNUNET_TIME_UNIT_FOREVER_REL,
391                                         sizeof (struct GNUNET_MessageHeader),
392                                         &data_ready, NULL);
393       return GNUNET_OK;
394     }
395     else
396     {
397       struct GNUNET_TIME_Relative latency;
398
399       latency = GNUNET_TIME_absolute_get_duration (echo_time);
400       echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
401       FPRINTF (stdout, "time: %s\n",
402                GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
403       echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
404                                                 &send_echo, NULL);
405     }
406   }
407
408   len = ntohs (message->size) - sizeof (*message);
409   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len);
410   buf = (const char *) &message[1];
411   off = 0;
412   while (off < len)
413   {
414     done = write (1, &buf[off], len - off);
415     if (done <= 0)
416     {
417       if (-1 == done)
418         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
419                              "write");
420       return GNUNET_SYSERR;
421     }
422     off += done;
423   }
424   return GNUNET_OK;
425 }
426
427
428 /**
429  * Method called to retrieve information about all tunnels in MESH.
430  *
431  * @param cls Closure.
432  * @param peer Destination peer.
433  * @param channels Number of channels.
434  * @param connections Number of connections.
435  * @param estate Encryption state.
436  * @param cstate Connectivity state.
437  */
438 void
439 tunnels_callback (void *cls,
440                   const struct GNUNET_PeerIdentity *peer,
441                   unsigned int channels,
442                   unsigned int connections,
443                   uint16_t estate,
444                   uint16_t cstate)
445 {
446   if (NULL == peer)
447   {
448     if (GNUNET_YES != monitor_connections)
449     {
450       GNUNET_SCHEDULER_shutdown();
451     }
452     return;
453   }
454   FPRINTF (stdout, "%s [ENC: %u, CON: %u] CHs: %u, CONNs: %u\n",
455            GNUNET_i2s_full (peer), estate, cstate, channels, connections);
456 }
457
458
459 /**
460  * Method called to retrieve information about a specific tunnel the mesh peer
461  * has established, o`r is trying to establish.
462  *
463  * @param cls Closure.
464  * @param peer Peer towards whom the tunnel is directed.
465  * @param channels Number of channels.
466  * @param connections Number of connections.
467  * @param estate Encryption status.
468  * @param cstate Connectivity status.
469  */
470 void
471 tunnel_callback (void *cls,
472                  const struct GNUNET_PeerIdentity *peer,
473                  unsigned int channels,
474                  unsigned int connections,
475                  unsigned int estate,
476                  unsigned int cstate)
477 {
478   FPRINTF (stdout, "Tunnel %s\n", GNUNET_i2s_full (peer));
479   FPRINTF (stdout, "- %u channels\n", channels);
480   FPRINTF (stdout, "- %u connections\n", connections);
481   FPRINTF (stdout, "- enc state: %u\n", estate);
482   FPRINTF (stdout, "- con state: %u\n", cstate);
483 }
484
485
486 /**
487  * Call MESH's monitor API, get all tunnels known to peer.
488  *
489  * @param cls Closure (unused).
490  * @param tc TaskContext
491  */
492 static void
493 get_tunnels (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
494 {
495   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
496   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
497   {
498     return;
499   }
500   GNUNET_MESH_get_tunnels (mh, &tunnels_callback, NULL);
501 }
502
503
504 /**
505  * Call MESH's monitor API, get info of one tunnel.
506  *
507  * @param cls Closure (unused).
508  * @param tc TaskContext
509  */
510 static void
511 show_tunnel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
512 {
513   struct GNUNET_PeerIdentity pid;
514
515   if (GNUNET_OK !=
516       GNUNET_CRYPTO_eddsa_public_key_from_string (tunnel_id,
517                                                   strlen (tunnel_id),
518                                                   &pid.public_key))
519   {
520     fprintf (stderr,
521              _("Invalid tunnel owner `%s'\n"),
522              tunnel_id);
523     GNUNET_SCHEDULER_shutdown();
524     return;
525   }
526   GNUNET_MESH_get_tunnel (mh, &pid, tunnel_callback, NULL);
527 }
528
529
530 /**
531  * Call MESH's monitor API, get info of one channel.
532  *
533  * @param cls Closure (unused).
534  * @param tc TaskContext
535  */
536 static void
537 show_channel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
538 {
539
540 }
541
542
543 /**
544  * Call MESH's monitor API, get info of one connection.
545  *
546  * @param cls Closure (unused).
547  * @param tc TaskContext
548  */
549 static void
550 show_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
551 {
552
553 }
554
555
556 /**
557  * Main function that will be run by the scheduler.
558  *
559  * @param cls closure
560  * @param args remaining command-line arguments
561  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
562  * @param cfg configuration
563  */
564 static void
565 run (void *cls, char *const *args, const char *cfgfile,
566      const struct GNUNET_CONFIGURATION_Handle *cfg)
567 {
568   GNUNET_MESH_InboundChannelNotificationHandler *newch = NULL;
569   GNUNET_MESH_ChannelEndHandler *endch = NULL;
570   static const struct GNUNET_MESH_MessageHandler handlers[] = {
571     {&data_callback, GNUNET_MESSAGE_TYPE_MESH_CLI, 0},
572     {NULL, 0, 0} /* FIXME add option to monitor msg types */
573   };
574   static uint32_t *ports = NULL;
575   /* FIXME add option to monitor apps */
576
577   target_id = args[0];
578   target_port = args[0] && args[1] ? atoi(args[1]) : 0;
579   if ( (0 != get_info
580         || 0 != monitor_connections
581         || NULL != tunnel_id
582         || NULL != conn_id
583         || NULL != channel_id)
584        && target_id != NULL)
585   {
586     FPRINTF (stderr, _("You must NOT give a TARGET when using options\n"));
587     return;
588   }
589
590   if (NULL != target_id)
591   {
592     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
593                 "Creating channel to %s\n",
594                 target_id);
595     GNUNET_SCHEDULER_add_now (&create_channel, NULL);
596     endch = &channel_ended;
597   }
598   else if (0 != listen_port)
599   {
600     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Listen\n");
601     newch = &channel_incoming;
602     endch = &channel_ended;
603     ports = GNUNET_malloc (sizeof (uint32_t) * 2);
604     ports[0] = listen_port;
605   }
606   else if (NULL != tunnel_id)
607   {
608     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show tunnel\n");
609     GNUNET_SCHEDULER_add_now (&show_tunnel, NULL);
610   }
611   else if (NULL != channel_id)
612   {
613     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show channel\n");
614     GNUNET_SCHEDULER_add_now (&show_channel, NULL);
615   }
616   else if (NULL != conn_id)
617   {
618     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show connection\n");
619     GNUNET_SCHEDULER_add_now (&show_connection, NULL);
620   }
621   else if (GNUNET_YES == get_info)
622   {
623     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n");
624     GNUNET_SCHEDULER_add_now (&get_tunnels, NULL);
625   }
626   else
627   {
628     FPRINTF (stderr, "No action requested\n");
629     return;
630   }
631
632   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to mesh\n");
633   mh = GNUNET_MESH_connect (cfg,
634                             NULL, /* cls */
635                             newch, /* new channel */
636                             endch, /* cleaner */
637                             handlers,
638                             ports);
639   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done\n");
640   if (NULL == mh)
641     GNUNET_SCHEDULER_add_now (shutdown_task, NULL);
642   else
643     sd = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
644                                        shutdown_task, NULL);
645
646 }
647
648
649 /**
650  * The main function to obtain peer information.
651  *
652  * @param argc number of arguments from the command line
653  * @param argv command line arguments
654  * @return 0 ok, 1 on error
655  */
656 int
657 main (int argc, char *const *argv)
658 {
659   int res;
660   const char helpstr[] = "Create channels and retreive info about meshs status.";
661   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
662 //     {'a', "channel", "TUNNEL_ID:CHANNEL_ID",
663 //      gettext_noop ("provide information about a particular channel"),
664 //      GNUNET_YES, &GNUNET_GETOPT_set_string, &channel_id},
665 //     {'b', "connection", "TUNNEL_ID:CONNECTION_ID",
666 //      gettext_noop ("provide information about a particular connection"),
667 //      GNUNET_YES, &GNUNET_GETOPT_set_string, &conn_id},
668     {'e', "echo", NULL,
669      gettext_noop ("activate echo mode"),
670      GNUNET_NO, &GNUNET_GETOPT_set_one, &echo},
671     {'i', "info", NULL,
672      gettext_noop ("provide information about all tunnels"),
673      GNUNET_NO, &GNUNET_GETOPT_set_one, &get_info},
674 //     {'m', "monitor", NULL,
675 //      gettext_noop ("provide information about all tunnels (continuously) NOT IMPLEMENTED"), /* FIXME */
676 //      GNUNET_NO, &GNUNET_GETOPT_set_one, &monitor_connections},
677     {'p', "port", NULL,
678      gettext_noop ("port to listen to (default; 0)"),
679      GNUNET_YES, &GNUNET_GETOPT_set_uint, &listen_port},
680     {'t', "tunnel", "TUNNEL_ID",
681      gettext_noop ("provide information about a particular tunnel"),
682      GNUNET_YES, &GNUNET_GETOPT_set_string, &tunnel_id},
683
684     GNUNET_GETOPT_OPTION_END
685   };
686
687   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
688     return 2;
689
690   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-mesh (OPTIONS | TARGET PORT)",
691                             gettext_noop (helpstr),
692                             options, &run, NULL);
693
694   GNUNET_free ((void *) argv);
695
696   if (GNUNET_OK == res)
697     return 0;
698   else
699     return 1;
700 }
701
702 /* end of gnunet-mesh.c */