- ping every second
[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   if (NULL != ch)
126   {
127     GNUNET_MESH_channel_destroy (ch);
128     ch = NULL;
129   }
130   if (NULL != mh)
131   {
132     GNUNET_MESH_disconnect (mh);
133         mh = NULL;
134   }
135 }
136
137
138 /**
139  * Function called to notify a client about the connection
140  * begin ready to queue more data.  "buf" will be
141  * NULL and "size" zero if the connection was closed for
142  * writing in the meantime.
143  *
144  * FIXME
145  *
146  * @param cls closure
147  * @param size number of bytes available in buf
148  * @param buf where the callee should write the message
149  * @return number of bytes written to buf
150  */
151 size_t
152 data_ready (void *cls, size_t size, void *buf)
153 {
154   struct GNUNET_MessageHeader *msg;
155   size_t total_size;
156
157   if (NULL == buf || 0 == size)
158   {
159     GNUNET_SCHEDULER_shutdown();
160     return 0;
161   }
162
163   total_size = data_size + sizeof (struct GNUNET_MessageHeader);
164   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sending %u bytes\n", data_size);
165   GNUNET_assert (size >= total_size);
166
167   msg = buf;
168   msg->size = htons (total_size);
169   msg->type = htons (GNUNET_MESSAGE_TYPE_MESH_CLI);
170   memcpy (&msg[1], cls, data_size);
171   if (GNUNET_NO == echo)
172   {
173     listen_stdio ();
174   }
175   else
176   {
177     echo_time = GNUNET_TIME_absolute_get ();
178   }
179
180   return total_size;
181 }
182
183
184 /**
185  * Task run in monitor mode when the user presses CTRL-C to abort.
186  * Stops monitoring activity.
187  *
188  * @param cls Closure (unused).
189  * @param tc scheduler context
190  */
191 static void
192 read_stdio (void *cls,
193             const struct GNUNET_SCHEDULER_TaskContext *tc)
194 {
195   static char buf[60000];
196
197   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
198   {
199     return;
200   }
201
202   data_size = read (0, buf, 60000);
203   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stdio read %u bytes\n", data_size);
204   if (data_size < 1)
205   {
206     GNUNET_SCHEDULER_shutdown();
207     return;
208   }
209   GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
210                                      GNUNET_TIME_UNIT_FOREVER_REL,
211                                      data_size
212                                      + sizeof (struct GNUNET_MessageHeader),
213                                      &data_ready, buf);
214 }
215
216
217 /**
218  * Start listening to stdin
219  */
220 static void
221 listen_stdio (void)
222 {
223   struct GNUNET_NETWORK_FDSet *rs;
224
225   rs = GNUNET_NETWORK_fdset_create ();
226   GNUNET_NETWORK_fdset_set_native (rs, 0);
227   GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
228                                GNUNET_TIME_UNIT_FOREVER_REL,
229                                rs, NULL,
230                                &read_stdio, NULL);
231   GNUNET_NETWORK_fdset_destroy (rs);
232 }
233
234
235 /**
236  * Function called whenever a channel is destroyed.  Should clean up
237  * any associated state.
238  *
239  * It must NOT call #GNUNET_MESH_channel_destroy on the channel.
240  *
241  * @param cls closure (set from #GNUNET_MESH_connect)
242  * @param channel connection to the other end (henceforth invalid)
243  * @param channel_ctx place where local state associated
244  *                   with the channel is stored
245  */
246 static void
247 channel_ended (void *cls,
248                const struct GNUNET_MESH_Channel *channel,
249                void *channel_ctx)
250 {
251   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n");
252   GNUNET_break (channel == ch);
253   ch = NULL;
254   GNUNET_SCHEDULER_shutdown ();
255 }
256
257
258 /**
259  * Method called whenever another peer has added us to a channel
260  * the other peer initiated.
261  * Only called (once) upon reception of data with a message type which was
262  * subscribed to in #GNUNET_MESH_connect.
263  *
264  * A call to #GNUNET_MESH_channel_destroy causes te channel to be ignored. In
265  * this case the handler MUST return NULL.
266  *
267  * @param cls closure
268  * @param channel new handle to the channel
269  * @param initiator peer that started the channel
270  * @param port Port this channel is for.
271  * @param options MeshOption flag field, with all active option bits set to 1.
272  *
273  * @return initial channel context for the channel
274  *         (can be NULL -- that's not an error)
275  */
276 static void *
277 channel_incoming (void *cls,
278                   struct GNUNET_MESH_Channel * channel,
279                   const struct GNUNET_PeerIdentity * initiator,
280                   uint32_t port, enum GNUNET_MESH_ChannelOption options)
281 {
282   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283               "Incoming channel %p on port %u\n",
284               channel, port);
285   if (NULL != ch)
286   {
287     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "A channel already exists\n");
288     return NULL;
289   }
290   if (0 == listen_port)
291   {
292     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not listening to channels\n");
293     return NULL;
294   }
295   ch = channel;
296   if (GNUNET_NO == echo)
297   {
298     listen_stdio ();
299     return NULL;
300   }
301   data_size = 0;
302   return NULL;
303 }
304
305 /**
306  * @brief Send an echo request to the remote peer.
307  *
308  * @param cls Closure (NULL).
309  * @param tc Task context.
310  */
311 static void
312 send_echo (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
313 {
314   GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
315                                      GNUNET_TIME_UNIT_FOREVER_REL,
316                                      sizeof (struct GNUNET_MessageHeader),
317                                      &data_ready, NULL);
318 }
319
320
321
322 /**
323  * Call MESH's monitor API, get info of one connection.
324  *
325  * @param cls Closure (unused).
326  * @param tc TaskContext
327  */
328 static void
329 create_channel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
330 {
331   struct GNUNET_PeerIdentity pid;
332   enum GNUNET_MESH_ChannelOption opt;
333
334   GNUNET_assert (NULL == ch);
335
336   if (GNUNET_OK !=
337       GNUNET_CRYPTO_eddsa_public_key_from_string (target_id,
338                                                   strlen (target_id),
339                                                   &pid.public_key))
340   {
341     FPRINTF (stderr,
342              _("Invalid target `%s'\n"),
343              target_id);
344     GNUNET_SCHEDULER_shutdown ();
345     return;
346   }
347   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to `%s'\n", target_id);
348   opt = GNUNET_MESH_OPTION_DEFAULT | GNUNET_MESH_OPTION_RELIABLE;
349   ch = GNUNET_MESH_channel_create (mh, NULL, &pid, target_port, opt);
350   if (GNUNET_NO == echo)
351     listen_stdio ();
352   else
353     GNUNET_SCHEDULER_add_now (send_echo, NULL);
354 }
355
356
357 /**
358  * Function called whenever a message is received.
359  *
360  * Each time the function must call #GNUNET_MESH_receive_done on the channel
361  * in order to receive the next message. This doesn't need to be immediate:
362  * can be delayed if some processing is done on the message.
363  *
364  * @param cls Closure (set from #GNUNET_MESH_connect).
365  * @param channel Connection to the other end.
366  * @param channel_ctx Place to store local state associated with the channel.
367  * @param message The actual message.
368  * @return #GNUNET_OK to keep the channel open,
369  *         #GNUNET_SYSERR to close it (signal serious error).
370  */
371 static int
372 data_callback (void *cls,
373                struct GNUNET_MESH_Channel *channel,
374                void **channel_ctx,
375                const struct GNUNET_MessageHeader *message)
376 {
377   uint16_t len;
378   ssize_t done;
379   uint16_t off;
380   const char *buf;
381   GNUNET_break (ch == channel);
382
383   if (GNUNET_YES == echo)
384   {
385     if (0 != listen_port)
386     {
387       /* Just listening to echo incoming messages*/
388       GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
389                                         GNUNET_TIME_UNIT_FOREVER_REL,
390                                         sizeof (struct GNUNET_MessageHeader),
391                                         &data_ready, NULL);
392       return GNUNET_OK;
393     }
394     else
395     {
396       struct GNUNET_TIME_Relative latency;
397
398       latency = GNUNET_TIME_absolute_get_duration (echo_time);
399       echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
400       FPRINTF (stdout, "time: %s\n",
401                GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
402       echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
403                                                 &send_echo, NULL);
404     }
405   }
406
407   len = ntohs (message->size) - sizeof (*message);
408   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len);
409   buf = (const char *) &message[1];
410   off = 0;
411   while (off < len)
412   {
413     done = write (1, &buf[off], len - off);
414     if (done <= 0)
415     {
416       if (-1 == done)
417         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
418                              "write");
419       return GNUNET_SYSERR;
420     }
421     off += done;
422   }
423   return GNUNET_OK;
424 }
425
426
427 /**
428  * Method called to retrieve information about each tunnel the mesh peer
429  * is aware of.
430  *
431  * @param cls Closure.
432  * @param tunnel_number Tunnel number.
433  * @param origin that started the tunnel (owner).
434  * @param target other endpoint of the tunnel
435  */
436 void /* FIXME static */
437 tunnels_callback (void *cls,
438                   uint32_t tunnel_number,
439                   const struct GNUNET_PeerIdentity *origin,
440                   const struct GNUNET_PeerIdentity *target)
441 {
442   FPRINTF (stdout, "Tunnel %s [%u]\n",
443            GNUNET_i2s_full (origin), tunnel_number);
444   FPRINTF (stdout, "\n");
445 }
446
447
448 /**
449  * Method called to retrieve information about each tunnel the mesh peer
450  * is aware of.
451  *
452  * @param cls Closure.
453  * @param peer Peer in the tunnel's tree.
454  * @param parent Parent of the current peer. All 0 when peer is root.
455  *
456  */
457 void /* FIXME static */
458 tunnel_callback (void *cls,
459                  const struct GNUNET_PeerIdentity *peer,
460                  const struct GNUNET_PeerIdentity *parent)
461 {
462 }
463
464
465 /**
466  * Call MESH's monitor API, get all tunnels known to peer.
467  *
468  * @param cls Closure (unused).
469  * @param tc TaskContext
470  */
471 static void
472 get_tunnels (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
473 {
474   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
475   {
476     return;
477   }
478 //   GNUNET_MESH_get_tunnels (mh, &tunnels_callback, NULL);
479   if (GNUNET_YES != monitor_connections)
480   {
481     GNUNET_SCHEDULER_shutdown();
482   }
483 }
484
485
486 /**
487  * Call MESH's monitor API, get info of one tunnel.
488  *
489  * @param cls Closure (unused).
490  * @param tc TaskContext
491  */
492 static void
493 show_tunnel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
494 {
495   struct GNUNET_PeerIdentity pid;
496
497   if (GNUNET_OK !=
498       GNUNET_CRYPTO_eddsa_public_key_from_string (tunnel_id,
499                                                   strlen (tunnel_id),
500                                                   &pid.public_key))
501   {
502     fprintf (stderr,
503              _("Invalid tunnel owner `%s'\n"),
504              tunnel_id);
505     GNUNET_SCHEDULER_shutdown();
506     return;
507   }
508 //   GNUNET_MESH_show_tunnel (mh, &pid, 0, tunnel_callback, NULL);
509 }
510
511
512 /**
513  * Call MESH's monitor API, get info of one channel.
514  *
515  * @param cls Closure (unused).
516  * @param tc TaskContext
517  */
518 static void
519 show_channel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
520 {
521
522 }
523
524
525 /**
526  * Call MESH's monitor API, get info of one connection.
527  *
528  * @param cls Closure (unused).
529  * @param tc TaskContext
530  */
531 static void
532 show_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
533 {
534
535 }
536
537
538 /**
539  * Main function that will be run by the scheduler.
540  *
541  * @param cls closure
542  * @param args remaining command-line arguments
543  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
544  * @param cfg configuration
545  */
546 static void
547 run (void *cls, char *const *args, const char *cfgfile,
548      const struct GNUNET_CONFIGURATION_Handle *cfg)
549 {
550   GNUNET_MESH_InboundChannelNotificationHandler *newch = NULL;
551   GNUNET_MESH_ChannelEndHandler *endch = NULL;
552   static const struct GNUNET_MESH_MessageHandler handlers[] = {
553     {&data_callback, GNUNET_MESSAGE_TYPE_MESH_CLI, 0},
554     {NULL, 0, 0} /* FIXME add option to monitor msg types */
555   };
556   static uint32_t *ports = NULL;
557   /* FIXME add option to monitor apps */
558
559   target_id = args[0];
560   target_port = args[0] && args[1] ? atoi(args[1]) : 0;
561   if ( (0 != get_info
562         || 0 != monitor_connections
563         || NULL != tunnel_id
564         || NULL != conn_id
565         || NULL != channel_id)
566        && target_id != NULL)
567   {
568     FPRINTF (stderr, _("You must NOT give a TARGET when using options\n"));
569     return;
570   }
571
572   if (NULL != target_id)
573   {
574     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575                 "Creating channel to %s\n",
576                 target_id);
577     GNUNET_SCHEDULER_add_now (&create_channel, NULL);
578     endch = &channel_ended;
579   }
580   else if (0 != listen_port)
581   {
582     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Listen\n");
583     newch = &channel_incoming;
584     endch = &channel_ended;
585     ports = GNUNET_malloc (sizeof (uint32_t) * 2);
586     ports[0] = listen_port;
587   }
588   else if (NULL != tunnel_id)
589   {
590     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show tunnel\n");
591     GNUNET_SCHEDULER_add_now (&show_tunnel, NULL);
592   }
593   else if (NULL != channel_id)
594   {
595     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show channel\n");
596     GNUNET_SCHEDULER_add_now (&show_channel, NULL);
597   }
598   else if (NULL != conn_id)
599   {
600     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show connection\n");
601     GNUNET_SCHEDULER_add_now (&show_connection, NULL);
602   }
603   else if (GNUNET_YES == get_info)
604   {
605     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n");
606     GNUNET_SCHEDULER_add_now (&get_tunnels, NULL);
607   }
608   else
609   {
610     FPRINTF (stderr, "No action requested\n");
611     return;
612   }
613
614   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to mesh\n");
615   mh = GNUNET_MESH_connect (cfg,
616                             NULL, /* cls */
617                             newch, /* new channel */
618                             endch, /* cleaner */
619                             handlers,
620                             ports);
621   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done\n");
622   if (NULL == mh)
623     GNUNET_SCHEDULER_add_now (shutdown_task, NULL);
624   else
625     sd = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
626                                        shutdown_task, NULL);
627
628 }
629
630
631 /**
632  * The main function to obtain peer information.
633  *
634  * @param argc number of arguments from the command line
635  * @param argv command line arguments
636  * @return 0 ok, 1 on error
637  */
638 int
639 main (int argc, char *const *argv)
640 {
641   int res;
642   const char helpstr[] = "Create channels and retreive info about meshs status.";
643   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
644     {'a', "channel", "TUNNEL_ID:CHANNEL_ID",
645      gettext_noop ("provide information about a particular channel"),
646      GNUNET_YES, &GNUNET_GETOPT_set_string, &channel_id},
647     {'b', "connection", "TUNNEL_ID:CONNECTION_ID",
648      gettext_noop ("provide information about a particular connection"),
649      GNUNET_YES, &GNUNET_GETOPT_set_string, &conn_id},
650     {'e', "echo", NULL,
651      gettext_noop ("activate echo mode"),
652      GNUNET_NO, &GNUNET_GETOPT_set_one, &echo},
653     {'i', "info", NULL,
654      gettext_noop ("provide information about all tunnels"),
655      GNUNET_NO, &GNUNET_GETOPT_set_one, &get_info},
656     {'m', "monitor", NULL,
657      gettext_noop ("provide information about all tunnels (continuously) NOT IMPLEMENTED"), /* FIXME */
658      GNUNET_NO, &GNUNET_GETOPT_set_one, &monitor_connections},
659     {'p', "port", NULL,
660      gettext_noop ("port to listen to (default; 0)"),
661      GNUNET_YES, &GNUNET_GETOPT_set_uint, &listen_port},
662     {'t', "tunnel", "TUNNEL_ID",
663      gettext_noop ("provide information about a particular tunnel"),
664      GNUNET_YES, &GNUNET_GETOPT_set_string, &tunnel_id},
665     GNUNET_GETOPT_OPTION_END
666   };
667
668   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
669     return 2;
670
671   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-mesh (OPTIONS | TARGET PORT)",
672                             gettext_noop (helpstr),
673                             options, &run, NULL);
674
675   GNUNET_free ((void *) argv);
676
677   if (GNUNET_OK == res)
678     return 0;
679   else
680     return 1;
681 }
682
683 /* end of gnunet-mesh.c */