finish #4623
[oweals/gnunet.git] / src / cadet / gnunet-cadet.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2017, 2019 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      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file cadet/gnunet-cadet.c
23  * @brief Print information about cadet tunnels and peers.
24  * @author Bartlomiej Polot
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_cadet_service.h"
30 #include "cadet.h"
31
32 #define STREAM_BUFFER_SIZE 1024  // Pakets
33
34 /**
35  * Option -P.
36  */
37 static int request_peers;
38
39 /**
40  * Option --peer
41  */
42 static char *peer_id;
43
44 /**
45  * Option -T.
46  */
47 static int request_tunnels;
48
49 /**
50  * Option --connection
51  */
52 static char *conn_id;
53
54 /**
55  * Option --channel
56  */
57 static char *channel_id;
58
59 /**
60  * Port to listen on (-o).
61  */
62 static char *listen_port;
63
64 /**
65  * Request echo service
66  */
67 static int echo;
68
69 /**
70  * Time of last echo request.
71  */
72 static struct GNUNET_TIME_Absolute echo_time;
73
74 /**
75  * Task for next echo request.
76  */
77 static struct GNUNET_SCHEDULER_Task *echo_task;
78
79 /**
80  * Peer to connect to.
81  */
82 static char *target_id;
83
84 /**
85  * Port to connect to
86  */
87 static char *target_port = "default";
88
89 /**
90  * Cadet handle.
91  */
92 static struct GNUNET_CADET_Handle *mh;
93
94 /**
95  * Our configuration.
96  */
97 static const struct GNUNET_CONFIGURATION_Handle *my_cfg;
98
99 /**
100  * Active get path operation.
101  */
102 static struct GNUNET_CADET_GetPath *gpo;
103
104 /**
105  * Active peer listing operation.
106  */
107 static struct GNUNET_CADET_PeersLister *plo;
108
109 /**
110  * Active tunnel listing operation.
111  */
112 static struct GNUNET_CADET_ListTunnels *tio;
113
114 /**
115  * Channel handle.
116  */
117 static struct GNUNET_CADET_Channel *ch;
118
119 /**
120  * HashCode of the given port string
121  */
122 static struct GNUNET_HashCode porthash;
123
124 /**
125  * Data structure for ongoing reception of incoming virtual circuits.
126  */
127 struct GNUNET_CADET_Port *lp;
128
129 /**
130  * Task for reading from stdin.
131  */
132 static struct GNUNET_SCHEDULER_Task *rd_task;
133
134 /**
135  * Task for main job.
136  */
137 static struct GNUNET_SCHEDULER_Task *job;
138
139 static unsigned int sent_pkt;
140
141
142 /**
143  * Wait for input on STDIO and send it out over the #ch.
144  */
145 static void
146 listen_stdio (void);
147
148
149 /**
150  * Convert encryption status to human readable string.
151  *
152  * @param status Encryption status.
153  *
154  * @return Human readable string.
155  */
156 static const char *
157 enc_2s (uint16_t status)
158 {
159   switch (status)
160   {
161     case 0:
162       return "NULL ";
163     case 1:
164       return "KSENT";
165     case 2:
166       return "KRECV";
167     case 3:
168       return "READY";
169     default:
170       return "";
171   }
172 }
173
174
175 /**
176  * Convert connection status to human readable string.
177  *
178  * @param status Connection status.
179  *
180  * @return Human readable string.
181  */
182 static const char *
183 conn_2s (uint16_t status)
184 {
185   switch (status)
186   {
187     case 0:
188       return "NEW  ";
189     case 1:
190       return "SRCH ";
191     case 2:
192       return "WAIT ";
193     case 3:
194       return "READY";
195     case 4:
196       return "SHUTD";
197     default:
198       return "";
199   }
200 }
201
202
203
204 /**
205  * Task to shut down this application.
206  *
207  * @param cls Closure (unused).
208  */
209 static void
210 shutdown_task (void *cls)
211 {
212   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213               "Shutdown\n");
214   if (NULL != lp)
215   {
216     GNUNET_CADET_close_port (lp);
217     lp = NULL;
218   }
219   if (NULL != ch)
220   {
221     GNUNET_CADET_channel_destroy (ch);
222     ch = NULL;
223   }
224   if (NULL != gpo)
225   {
226     GNUNET_CADET_get_path_cancel (gpo);
227     gpo = NULL;
228   }
229   if (NULL != plo)
230   {
231     GNUNET_CADET_list_peers_cancel (plo);
232     plo = NULL;
233   }
234   if (NULL != tio)
235   {
236     GNUNET_CADET_list_tunnels_cancel (tio);
237     tio = NULL;
238   }
239   if (NULL != mh)
240   {
241     GNUNET_CADET_disconnect (mh);
242     mh = NULL;
243   }
244   if (NULL != rd_task)
245   {
246     GNUNET_SCHEDULER_cancel (rd_task);
247     rd_task = NULL;
248   }
249   if (NULL != echo_task)
250   {
251     GNUNET_SCHEDULER_cancel (echo_task);
252     echo_task = NULL;
253   }
254   if (NULL != job)
255   {
256     GNUNET_SCHEDULER_cancel (job);
257     job = NULL;
258   }
259 }
260
261 void
262 mq_cb(void *cls)
263 {
264   listen_stdio ();
265 }
266
267
268 /**
269  * Task run in stdio mode, after some data is available at stdin.
270  *
271  * @param cls Closure (unused).
272  */
273 static void
274 read_stdio (void *cls)
275 {
276   struct GNUNET_MQ_Envelope *env;
277   struct GNUNET_MessageHeader *msg;
278   char buf[60000];
279   ssize_t data_size;
280
281   rd_task = NULL;
282   data_size = read (0,
283                     buf,
284                     60000);
285   if (data_size < 1)
286   {
287     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288                 "read() returned  %s\n", strerror(errno));
289     GNUNET_SCHEDULER_shutdown();
290     return;
291   }
292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293               "Read %u bytes from stdio\n",
294               (unsigned int) data_size);
295   env = GNUNET_MQ_msg_extra (msg,
296                              data_size,
297                              GNUNET_MESSAGE_TYPE_CADET_CLI);
298   GNUNET_memcpy (&msg[1],
299                  buf,
300                  data_size);
301   GNUNET_MQ_send (GNUNET_CADET_get_mq (ch),
302                   env);
303
304   sent_pkt++;
305
306   if (GNUNET_NO == echo)
307   {
308     // Use MQ's notification if too much data of stdin is pooring in too fast.
309     if (STREAM_BUFFER_SIZE < sent_pkt)
310     {
311       GNUNET_MQ_notify_sent (env, mq_cb, cls);
312       sent_pkt = 0;
313     }
314     else
315     {
316       listen_stdio ();
317     }
318   }
319   else
320   {
321     echo_time = GNUNET_TIME_absolute_get ();
322   }
323 }
324
325
326 /**
327  * Wait for input on STDIO and send it out over the #ch.
328  */
329 static void
330 listen_stdio ()
331 {
332   struct GNUNET_NETWORK_FDSet *rs;
333
334   /* FIXME: why use 'rs' here, seems overly complicated... */
335   rs = GNUNET_NETWORK_fdset_create ();
336   GNUNET_NETWORK_fdset_set_native (rs,
337                                    0); /* STDIN */
338   rd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
339                                          GNUNET_TIME_UNIT_FOREVER_REL,
340                                          rs,
341                                          NULL,
342                                          &read_stdio,
343                                          NULL);
344   GNUNET_NETWORK_fdset_destroy (rs);
345 }
346
347
348 /**
349  * Function called whenever a channel is destroyed.  Should clean up
350  * any associated state.
351  *
352  * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
353  *
354  * @param cls closure
355  * @param channel connection to the other end (henceforth invalid)
356  */
357 static void
358 channel_ended (void *cls,
359                const struct GNUNET_CADET_Channel *channel)
360 {
361   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362               "Channel ended!\n");
363   GNUNET_assert (channel == ch);
364   ch = NULL;
365   GNUNET_SCHEDULER_shutdown ();
366 }
367
368
369 /**
370  * Method called whenever another peer has added us to a channel
371  * the other peer initiated.
372  * Only called (once) upon reception of data with a message type which was
373  * subscribed to in #GNUNET_CADET_connect.
374  *
375  * A call to #GNUNET_CADET_channel_destroy causes the channel to be ignored.
376  * In this case the handler MUST return NULL.
377  *
378  * @param cls closure
379  * @param channel new handle to the channel
380  * @param initiator peer that started the channel
381  * @return initial channel context for the channel, we use @a channel
382  */
383 static void *
384 channel_incoming (void *cls,
385                   struct GNUNET_CADET_Channel *channel,
386                   const struct GNUNET_PeerIdentity *initiator)
387 {
388   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
389               "Incoming connection from %s\n",
390               GNUNET_i2s_full (initiator));
391   GNUNET_assert (NULL == ch);
392   GNUNET_assert (NULL != lp);
393   GNUNET_CADET_close_port (lp);
394   lp = NULL;
395   ch = channel;
396   if (GNUNET_NO == echo)
397     listen_stdio ();
398   return channel;
399 }
400
401
402 /**
403  * @brief Send an echo request to the remote peer.
404  *
405  * @param cls Closure (NULL).
406  */
407 static void
408 send_echo (void *cls)
409 {
410   struct GNUNET_MQ_Envelope *env;
411   struct GNUNET_MessageHeader *msg;
412
413   echo_task = NULL;
414   if (NULL == ch)
415     return;
416   env = GNUNET_MQ_msg (msg,
417                        GNUNET_MESSAGE_TYPE_CADET_CLI);
418   GNUNET_MQ_send (GNUNET_CADET_get_mq (ch),
419                   env);
420 }
421
422
423 /**
424  * Check data message sanity. Does nothing so far (all messages are OK).
425  *
426  * @param cls Closure (unused).
427  * @param message The message to check.
428  * @return #GNUNET_OK to keep the channel open,
429  *         #GNUNET_SYSERR to close it (signal serious error).
430  */
431 static int
432 check_data (void *cls,
433             const struct GNUNET_MessageHeader *message)
434 {
435   return GNUNET_OK; /* all is well-formed */
436 }
437
438
439 /**
440  * Function called whenever a message is received.
441  *
442  * Each time the function must call #GNUNET_CADET_receive_done on the channel
443  * in order to receive the next message. This doesn't need to be immediate:
444  * can be delayed if some processing is done on the message.
445  *
446  * @param cls NULL
447  * @param message The actual message.
448  */
449 static void
450 handle_data (void *cls,
451              const struct GNUNET_MessageHeader *message)
452 {
453   size_t payload_size = ntohs (message->size) - sizeof (*message);
454   uint16_t len;
455   ssize_t done;
456   uint16_t off;
457   const char *buf;
458
459   GNUNET_CADET_receive_done (ch);
460   if (GNUNET_YES == echo)
461   {
462     if (NULL != listen_port)
463     {
464       struct GNUNET_MQ_Envelope *env;
465       struct GNUNET_MessageHeader *msg;
466
467       env = GNUNET_MQ_msg_extra (msg,
468                                  payload_size,
469                                  GNUNET_MESSAGE_TYPE_CADET_CLI);
470       GNUNET_memcpy (&msg[1],
471                      &message[1],
472                      payload_size);
473       GNUNET_MQ_send (GNUNET_CADET_get_mq (ch),
474                       env);
475       return;
476     }
477     else
478     {
479       struct GNUNET_TIME_Relative latency;
480
481       latency = GNUNET_TIME_absolute_get_duration (echo_time);
482       echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
483       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
484                   "time: %s\n",
485                   GNUNET_STRINGS_relative_time_to_string (latency,
486                                                           GNUNET_NO));
487       echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
488                                                 &send_echo,
489                                                 NULL);
490     }
491   }
492
493   len = ntohs (message->size) - sizeof (*message);
494   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495               "Got %u bytes\n",
496               len);
497   buf = (const char *) &message[1];
498   off = 0;
499   while (off < len)
500   {
501     done = write (1,
502                   &buf[off],
503                   len - off);
504     if (done <= 0)
505     {
506       if (-1 == done)
507         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
508                              "write");
509       GNUNET_SCHEDULER_shutdown ();
510       return;
511     }
512     off += done;
513   }
514 }
515
516
517 /**
518  * Method called to retrieve information about all peers in CADET, called
519  * once per peer.
520  *
521  * After last peer has been reported, an additional call with NULL is done.
522  *
523  * @param cls Closure.
524  * @param ple information about peer, or NULL on "EOF".
525  */
526 static void
527 peers_callback (void *cls,
528                 const struct GNUNET_CADET_PeerListEntry *ple)
529 {
530   if (NULL == ple)
531   {
532     plo = NULL;
533     GNUNET_SCHEDULER_shutdown();
534     return;
535   }
536   FPRINTF (stdout,
537            "%s tunnel: %c, paths: %u\n",
538            GNUNET_i2s_full (&ple->peer),
539            ple->have_tunnel ? 'Y' : 'N',
540            ple->n_paths);
541 }
542
543
544 /**
545  * Method called to retrieve information about paths to a specific peer
546  * known to the service.
547  *
548  * @param cls Closure.
549  * @param ppd path detail
550  */
551 static void
552 path_callback (void *cls,
553                const struct GNUNET_CADET_PeerPathDetail *ppd)
554 {
555   if (NULL == ppd)
556   {
557     gpo = NULL;
558     GNUNET_SCHEDULER_shutdown();
559     return;
560   }
561   FPRINTF (stdout,
562            "Path of length %u: ",
563            ppd->path_length);
564   for (unsigned int i = 0; i < ppd->path_length; i++)
565     FPRINTF (stdout,
566              (i == ppd->target_offset) ? "*%s* " : "%s ",
567              GNUNET_i2s (&ppd->path[i]));
568   FPRINTF (stdout,
569            "\n");
570 }
571
572
573 /**
574  * Method called to retrieve information about all tunnels in CADET.
575  *
576  * @param cls Closure.
577  * @param td tunnel details
578  */
579 static void
580 tunnels_callback (void *cls,
581                   const struct GNUNET_CADET_TunnelDetails *td)
582 {
583   if (NULL == td)
584   {
585     tio = NULL;
586     GNUNET_SCHEDULER_shutdown();
587     return;
588   }
589   FPRINTF (stdout,
590            "%s [ENC: %s, CON: %s] CHs: %u, CONNs: %u\n",
591            GNUNET_i2s_full (&td->peer),
592            enc_2s (td->estate),
593            conn_2s (td->cstate),
594            td->channels,
595            td->connections);
596 }
597
598
599 /**
600  * Call CADET's meta API, get all peers known to a peer.
601  *
602  * @param cls Closure (unused).
603  */
604 static void
605 get_peers (void *cls)
606 {
607   job = NULL;
608   plo = GNUNET_CADET_list_peers (my_cfg,
609                                  &peers_callback,
610                                  NULL);
611 }
612
613
614 /**
615  * Call CADET's monitor API, get info of one peer.
616  *
617  * @param cls Closure (unused).
618  */
619 static void
620 show_peer (void *cls)
621 {
622   struct GNUNET_PeerIdentity pid;
623
624   job = NULL;
625   if (GNUNET_OK !=
626       GNUNET_CRYPTO_eddsa_public_key_from_string (peer_id,
627                                                   strlen (peer_id),
628                                                   &pid.public_key))
629   {
630     fprintf (stderr,
631              _("Invalid peer ID `%s'\n"),
632              peer_id);
633     GNUNET_SCHEDULER_shutdown();
634     return;
635   }
636   gpo = GNUNET_CADET_get_path (my_cfg,
637                                &pid,
638                                &path_callback,
639                                NULL);
640 }
641
642
643 /**
644  * Call CADET's meta API, get all tunnels known to a peer.
645  *
646  * @param cls Closure (unused).
647  */
648 static void
649 get_tunnels (void *cls)
650 {
651   job = NULL;
652   tio = GNUNET_CADET_list_tunnels (my_cfg,
653                                    &tunnels_callback,
654                                    NULL);
655 }
656
657
658 /**
659  * Call CADET's monitor API, get info of one channel.
660  *
661  * @param cls Closure (unused).
662  */
663 static void
664 show_channel (void *cls)
665 {
666   job = NULL;
667   GNUNET_break (0);
668 }
669
670
671 /**
672  * Call CADET's monitor API, get info of one connection.
673  *
674  * @param cls Closure (unused).
675  */
676 static void
677 show_connection (void *cls)
678 {
679   job = NULL;
680   GNUNET_break (0);
681 }
682
683
684 /**
685  * Main function that will be run by the scheduler.
686  *
687  * @param cls closure
688  * @param args remaining command-line arguments
689  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
690  * @param cfg configuration
691  */
692 static void
693 run (void *cls,
694      char *const *args,
695      const char *cfgfile,
696      const struct GNUNET_CONFIGURATION_Handle *cfg)
697 {
698   struct GNUNET_MQ_MessageHandler handlers[] = {
699     GNUNET_MQ_hd_var_size (data,
700                            GNUNET_MESSAGE_TYPE_CADET_CLI,
701                            struct GNUNET_MessageHeader,
702                            NULL),
703     GNUNET_MQ_handler_end ()
704   };
705
706   /* FIXME add option to monitor apps */
707   my_cfg = cfg;
708   target_id = args[0];
709   if (target_id && args[1])
710     target_port = args[1];
711
712   if ( (0 != (request_peers | request_tunnels)
713         || NULL != conn_id
714         || NULL != channel_id)
715        && target_id != NULL)
716   {
717     FPRINTF (stderr,
718              _("Extra arguments are not applicable "
719                "in combination with this option.\n"));
720     return;
721   }
722
723   if (NULL != peer_id)
724   {
725     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
726                 "Show peer\n");
727     job = GNUNET_SCHEDULER_add_now (&show_peer,
728                                     NULL);
729   }
730   else if (NULL != channel_id)
731   {
732     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733                 "Show channel\n");
734     job = GNUNET_SCHEDULER_add_now (&show_channel,
735                                     NULL);
736   }
737   else if (NULL != conn_id)
738   {
739     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
740                 "Show connection\n");
741     job = GNUNET_SCHEDULER_add_now (&show_connection,
742                                     NULL);
743   }
744   else if (GNUNET_YES == request_peers)
745   {
746     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747                 "Show all peers\n");
748     job = GNUNET_SCHEDULER_add_now (&get_peers,
749                                     NULL);
750   }
751   else if (GNUNET_YES == request_tunnels)
752   {
753     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
754                 "Show all tunnels\n");
755     job = GNUNET_SCHEDULER_add_now (&get_tunnels,
756                                     NULL);
757   }
758
759   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
760               "Connecting to CADET service\n");
761   mh = GNUNET_CADET_connect (cfg);
762   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
763                                  NULL);
764   if (NULL == mh)
765   {
766     GNUNET_SCHEDULER_shutdown ();
767     return;
768   }
769   if (NULL != listen_port)
770   {
771     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
772                 "Opening CADET listen port\n");
773     GNUNET_CRYPTO_hash (listen_port,
774                         strlen (listen_port),
775                         &porthash);
776     lp = GNUNET_CADET_open_port (mh,
777                                  &porthash,
778                                  &channel_incoming,
779                                  NULL,
780                                  NULL /* window changes */,
781                                  &channel_ended,
782                                  handlers);
783   }
784   if (NULL != target_id)
785   {
786     struct GNUNET_PeerIdentity pid;
787     enum GNUNET_CADET_ChannelOption opt;
788
789     if (GNUNET_OK !=
790         GNUNET_CRYPTO_eddsa_public_key_from_string (target_id,
791                                                     strlen (target_id),
792                                                     &pid.public_key))
793     {
794       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
795                   _("Invalid target `%s'\n"),
796                   target_id);
797       GNUNET_SCHEDULER_shutdown ();
798       return;
799     }
800     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801                 "Connecting to `%s:%s'\n",
802                 target_id,
803                 target_port);
804     opt = GNUNET_CADET_OPTION_DEFAULT | GNUNET_CADET_OPTION_RELIABLE;
805     GNUNET_CRYPTO_hash (target_port,
806                         strlen(target_port),
807                         &porthash);
808     ch = GNUNET_CADET_channel_create (mh,
809                                       NULL,
810                                       &pid,
811                                       &porthash,
812                                       opt,
813                                       NULL /* window changes */,
814                                       &channel_ended,
815                                       handlers);
816     if (GNUNET_YES == echo)
817     {
818       echo_task = GNUNET_SCHEDULER_add_now (&send_echo,
819                                             NULL);
820     }
821     else
822     {
823       listen_stdio ();
824     }
825   }
826
827   if ( (NULL == lp) &&
828        (NULL == job) &&
829        (NULL == ch) )
830   {
831     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
832                 _("No action requested\n"));
833     GNUNET_SCHEDULER_shutdown ();
834     return;
835   }
836 }
837
838
839 /**
840  * The main function to obtain peer information.
841  *
842  * @param argc number of arguments from the command line
843  * @param argv command line arguments
844  * @return 0 ok, 1 on error
845  */
846 int
847 main (int argc,
848       char *const *argv)
849 {
850   int res;
851   const char helpstr[] = "Create tunnels and retrieve info about CADET's status.";
852   struct GNUNET_GETOPT_CommandLineOption options[] = {
853     /* I would use the terminology 'circuit' here...  --lynX */
854     GNUNET_GETOPT_option_string ('C',
855                                  "connection",
856                                  "CONNECTION_ID",
857                                  gettext_noop ("Provide information about a particular connection"),
858                                  &conn_id),
859     GNUNET_GETOPT_option_flag ('e',
860                                "echo",
861                                gettext_noop ("Activate echo mode"),
862                                &echo),
863     GNUNET_GETOPT_option_string ('o',
864                                  "open-port",
865                                  "SHARED_SECRET",
866                                  gettext_noop ("Listen for connections using a shared secret among sender and recipient"),
867                                  &listen_port),
868     GNUNET_GETOPT_option_string ('p',
869                                  "peer",
870                                  "PEER_ID",
871                                  gettext_noop ("Provide information about a patricular peer"),
872                                  &peer_id),
873     GNUNET_GETOPT_option_flag ('P',
874                                "peers",
875                                gettext_noop ("Provide information about all peers"),
876                                &request_peers),
877     GNUNET_GETOPT_option_flag ('T',
878                                "tunnels",
879                                gettext_noop ("Provide information about all tunnels"),
880                                &request_tunnels),
881     GNUNET_GETOPT_OPTION_END
882   };
883
884   if (GNUNET_OK !=
885       GNUNET_STRINGS_get_utf8_args (argc, argv,
886                                     &argc, &argv))
887     return 2;
888
889   res = GNUNET_PROGRAM_run (argc, argv,
890                             "gnunet-cadet (OPTIONS | PEER_ID SHARED_SECRET)",
891                             gettext_noop (helpstr),
892                             options, &run, NULL);
893
894   GNUNET_free ((void *) argv);
895
896   if (GNUNET_OK == res)
897     return 0;
898   return 1;
899 }
900
901 /* end of gnunet-cadet.c */