2 This file is part of GNUnet.
3 Copyright (C) 2012, 2017, 2019 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file cadet/gnunet-cadet.c
23 * @brief Print information about cadet tunnels and peers.
24 * @author Bartlomiej Polot
25 * @author Christian Grothoff
28 #include "gnunet_util_lib.h"
29 #include "gnunet_cadet_service.h"
32 #define STREAM_BUFFER_SIZE 1024 // Pakets
37 static int request_peers;
47 static int request_tunnels;
57 static char *channel_id;
60 * Port to listen on (-o).
62 static char *listen_port;
65 * Request echo service
70 * Time of last echo request.
72 static struct GNUNET_TIME_Absolute echo_time;
75 * Task for next echo request.
77 static struct GNUNET_SCHEDULER_Task *echo_task;
82 static char *target_id;
87 static char *target_port = "default";
92 static struct GNUNET_CADET_Handle *mh;
97 static const struct GNUNET_CONFIGURATION_Handle *my_cfg;
100 * Active get path operation.
102 static struct GNUNET_CADET_GetPath *gpo;
105 * Active peer listing operation.
107 static struct GNUNET_CADET_PeersLister *plo;
110 * Active tunnel listing operation.
112 static struct GNUNET_CADET_ListTunnels *tio;
117 static struct GNUNET_CADET_Channel *ch;
120 * HashCode of the given port string
122 static struct GNUNET_HashCode porthash;
125 * Data structure for ongoing reception of incoming virtual circuits.
127 struct GNUNET_CADET_Port *lp;
130 * Task for reading from stdin.
132 static struct GNUNET_SCHEDULER_Task *rd_task;
137 static struct GNUNET_SCHEDULER_Task *job;
139 static unsigned int sent_pkt;
143 * Wait for input on STDIO and send it out over the #ch.
150 * Convert encryption status to human readable string.
152 * @param status Encryption status.
154 * @return Human readable string.
157 enc_2s (uint16_t status)
176 * Convert connection status to human readable string.
178 * @param status Connection status.
180 * @return Human readable string.
183 conn_2s (uint16_t status)
204 * Task to shut down this application.
206 * @param cls Closure (unused).
209 shutdown_task (void *cls)
211 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
214 GNUNET_CADET_close_port (lp);
219 GNUNET_CADET_channel_destroy (ch);
224 GNUNET_CADET_get_path_cancel (gpo);
229 GNUNET_CADET_list_peers_cancel (plo);
234 GNUNET_CADET_list_tunnels_cancel (tio);
239 GNUNET_CADET_disconnect (mh);
244 GNUNET_SCHEDULER_cancel (rd_task);
247 if (NULL != echo_task)
249 GNUNET_SCHEDULER_cancel (echo_task);
254 GNUNET_SCHEDULER_cancel (job);
267 * Task run in stdio mode, after some data is available at stdin.
269 * @param cls Closure (unused).
272 read_stdio (void *cls)
274 struct GNUNET_MQ_Envelope *env;
275 struct GNUNET_MessageHeader *msg;
280 data_size = read (0, buf, 60000);
283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284 "read() returned %s\n",
286 GNUNET_SCHEDULER_shutdown ();
289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
290 "Read %u bytes from stdio\n",
291 (unsigned int) data_size);
292 env = GNUNET_MQ_msg_extra (msg, data_size, GNUNET_MESSAGE_TYPE_CADET_CLI);
293 GNUNET_memcpy (&msg[1], buf, data_size);
294 GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env);
298 if (GNUNET_NO == echo)
300 // Use MQ's notification if too much data of stdin is pooring in too fast.
301 if (STREAM_BUFFER_SIZE < sent_pkt)
303 GNUNET_MQ_notify_sent (env, mq_cb, cls);
313 echo_time = GNUNET_TIME_absolute_get ();
319 * Wait for input on STDIO and send it out over the #ch.
324 struct GNUNET_NETWORK_FDSet *rs;
326 /* FIXME: why use 'rs' here, seems overly complicated... */
327 rs = GNUNET_NETWORK_fdset_create ();
328 GNUNET_NETWORK_fdset_set_native (rs, 0); /* STDIN */
329 rd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
330 GNUNET_TIME_UNIT_FOREVER_REL,
335 GNUNET_NETWORK_fdset_destroy (rs);
340 * Function called whenever a channel is destroyed. Should clean up
341 * any associated state.
343 * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
346 * @param channel connection to the other end (henceforth invalid)
349 channel_ended (void *cls, const struct GNUNET_CADET_Channel *channel)
351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n");
352 GNUNET_assert (channel == ch);
354 GNUNET_SCHEDULER_shutdown ();
359 * Method called whenever another peer has added us to a channel
360 * the other peer initiated.
361 * Only called (once) upon reception of data with a message type which was
362 * subscribed to in #GNUNET_CADET_connect.
364 * A call to #GNUNET_CADET_channel_destroy causes the channel to be ignored.
365 * In this case the handler MUST return NULL.
368 * @param channel new handle to the channel
369 * @param initiator peer that started the channel
370 * @return initial channel context for the channel, we use @a channel
373 channel_incoming (void *cls,
374 struct GNUNET_CADET_Channel *channel,
375 const struct GNUNET_PeerIdentity *initiator)
377 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
378 "Incoming connection from %s\n",
379 GNUNET_i2s_full (initiator));
380 GNUNET_assert (NULL == ch);
381 GNUNET_assert (NULL != lp);
382 GNUNET_CADET_close_port (lp);
385 if (GNUNET_NO == echo)
392 * @brief Send an echo request to the remote peer.
394 * @param cls Closure (NULL).
397 send_echo (void *cls)
399 struct GNUNET_MQ_Envelope *env;
400 struct GNUNET_MessageHeader *msg;
405 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_CLI);
406 GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env);
411 * Check data message sanity. Does nothing so far (all messages are OK).
413 * @param cls Closure (unused).
414 * @param message The message to check.
415 * @return #GNUNET_OK to keep the channel open,
416 * #GNUNET_SYSERR to close it (signal serious error).
419 check_data (void *cls, const struct GNUNET_MessageHeader *message)
421 return GNUNET_OK; /* all is well-formed */
426 * Function called whenever a message is received.
428 * Each time the function must call #GNUNET_CADET_receive_done on the channel
429 * in order to receive the next message. This doesn't need to be immediate:
430 * can be delayed if some processing is done on the message.
433 * @param message The actual message.
436 handle_data (void *cls, const struct GNUNET_MessageHeader *message)
438 size_t payload_size = ntohs (message->size) - sizeof (*message);
444 GNUNET_CADET_receive_done (ch);
445 if (GNUNET_YES == echo)
447 if (NULL != listen_port)
449 struct GNUNET_MQ_Envelope *env;
450 struct GNUNET_MessageHeader *msg;
453 GNUNET_MQ_msg_extra (msg, payload_size, GNUNET_MESSAGE_TYPE_CADET_CLI);
454 GNUNET_memcpy (&msg[1], &message[1], payload_size);
455 GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env);
460 struct GNUNET_TIME_Relative latency;
462 latency = GNUNET_TIME_absolute_get_duration (echo_time);
463 echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
464 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
466 GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
467 echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
473 len = ntohs (message->size) - sizeof (*message);
474 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len);
475 buf = (const char *) &message[1];
479 done = write (1, &buf[off], len - off);
483 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
484 GNUNET_SCHEDULER_shutdown ();
493 * Method called to retrieve information about all peers in CADET, called
496 * After last peer has been reported, an additional call with NULL is done.
498 * @param cls Closure.
499 * @param ple information about peer, or NULL on "EOF".
502 peers_callback (void *cls, const struct GNUNET_CADET_PeerListEntry *ple)
507 GNUNET_SCHEDULER_shutdown ();
511 "%s tunnel: %c, paths: %u\n",
512 GNUNET_i2s_full (&ple->peer),
513 ple->have_tunnel ? 'Y' : 'N',
519 * Method called to retrieve information about paths to a specific peer
520 * known to the service.
522 * @param cls Closure.
523 * @param ppd path detail
526 path_callback (void *cls, const struct GNUNET_CADET_PeerPathDetail *ppd)
531 GNUNET_SCHEDULER_shutdown ();
534 fprintf (stdout, "Path of length %u: ", ppd->path_length);
535 for (unsigned int i = 0; i < ppd->path_length; i++)
537 (i == ppd->target_offset) ? "*%s* " : "%s ",
538 GNUNET_i2s (&ppd->path[i]));
539 fprintf (stdout, "\n");
544 * Method called to retrieve information about all tunnels in CADET.
546 * @param cls Closure.
547 * @param td tunnel details
550 tunnels_callback (void *cls, const struct GNUNET_CADET_TunnelDetails *td)
555 GNUNET_SCHEDULER_shutdown ();
559 "%s [ENC: %s, CON: %s] CHs: %u, CONNs: %u\n",
560 GNUNET_i2s_full (&td->peer),
562 conn_2s (td->cstate),
569 * Call CADET's meta API, get all peers known to a peer.
571 * @param cls Closure (unused).
574 get_peers (void *cls)
577 plo = GNUNET_CADET_list_peers (my_cfg, &peers_callback, NULL);
582 * Call CADET's monitor API, get info of one peer.
584 * @param cls Closure (unused).
587 show_peer (void *cls)
589 struct GNUNET_PeerIdentity pid;
592 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (peer_id,
596 fprintf (stderr, _ ("Invalid peer ID `%s'\n"), peer_id);
597 GNUNET_SCHEDULER_shutdown ();
600 gpo = GNUNET_CADET_get_path (my_cfg, &pid, &path_callback, NULL);
605 * Call CADET's meta API, get all tunnels known to a peer.
607 * @param cls Closure (unused).
610 get_tunnels (void *cls)
613 tio = GNUNET_CADET_list_tunnels (my_cfg, &tunnels_callback, NULL);
618 * Call CADET's monitor API, get info of one channel.
620 * @param cls Closure (unused).
623 show_channel (void *cls)
631 * Call CADET's monitor API, get info of one connection.
633 * @param cls Closure (unused).
636 show_connection (void *cls)
644 * Main function that will be run by the scheduler.
647 * @param args remaining command-line arguments
648 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
649 * @param cfg configuration
655 const struct GNUNET_CONFIGURATION_Handle *cfg)
657 struct GNUNET_MQ_MessageHandler handlers[] =
658 {GNUNET_MQ_hd_var_size (data,
659 GNUNET_MESSAGE_TYPE_CADET_CLI,
660 struct GNUNET_MessageHeader,
662 GNUNET_MQ_handler_end ()};
664 /* FIXME add option to monitor apps */
667 if (target_id && args[1])
668 target_port = args[1];
670 if ((0 != (request_peers | request_tunnels) || NULL != conn_id ||
671 NULL != channel_id) &&
675 _ ("Extra arguments are not applicable "
676 "in combination with this option.\n"));
682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show peer\n");
683 job = GNUNET_SCHEDULER_add_now (&show_peer, NULL);
685 else if (NULL != channel_id)
687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show channel\n");
688 job = GNUNET_SCHEDULER_add_now (&show_channel, NULL);
690 else if (NULL != conn_id)
692 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show connection\n");
693 job = GNUNET_SCHEDULER_add_now (&show_connection, NULL);
695 else if (GNUNET_YES == request_peers)
697 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all peers\n");
698 job = GNUNET_SCHEDULER_add_now (&get_peers, NULL);
700 else if (GNUNET_YES == request_tunnels)
702 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n");
703 job = GNUNET_SCHEDULER_add_now (&get_tunnels, NULL);
706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CADET service\n");
707 mh = GNUNET_CADET_connect (cfg);
708 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
711 GNUNET_SCHEDULER_shutdown ();
714 if (NULL != listen_port)
716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opening CADET listen port\n");
717 GNUNET_CRYPTO_hash (listen_port, strlen (listen_port), &porthash);
718 lp = GNUNET_CADET_open_port (mh,
722 NULL /* window changes */,
726 if (NULL != target_id)
728 struct GNUNET_PeerIdentity pid;
731 GNUNET_CRYPTO_eddsa_public_key_from_string (target_id,
735 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
736 _ ("Invalid target `%s'\n"),
738 GNUNET_SCHEDULER_shutdown ();
741 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
742 "Connecting to `%s:%s'\n",
745 GNUNET_CRYPTO_hash (target_port, strlen (target_port), &porthash);
746 ch = GNUNET_CADET_channel_create (mh,
750 NULL /* window changes */,
753 if (GNUNET_YES == echo)
755 echo_task = GNUNET_SCHEDULER_add_now (&send_echo, NULL);
763 if ((NULL == lp) && (NULL == job) && (NULL == ch))
765 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, _ ("No action requested\n"));
766 GNUNET_SCHEDULER_shutdown ();
773 * The main function to obtain peer information.
775 * @param argc number of arguments from the command line
776 * @param argv command line arguments
777 * @return 0 ok, 1 on error
780 main (int argc, char *const *argv)
783 const char helpstr[] =
784 "Create tunnels and retrieve info about CADET's status.";
785 struct GNUNET_GETOPT_CommandLineOption options[] =
786 {/* I would use the terminology 'circuit' here... --lynX */
787 GNUNET_GETOPT_option_string (
791 gettext_noop ("Provide information about a particular connection"),
793 GNUNET_GETOPT_option_flag ('e',
795 gettext_noop ("Activate echo mode"),
797 GNUNET_GETOPT_option_string (
802 "Listen for connections using a shared secret among sender and recipient"),
804 GNUNET_GETOPT_option_string ('p',
808 "Provide information about a patricular peer"),
810 GNUNET_GETOPT_option_flag ('P',
813 "Provide information about all peers"),
815 GNUNET_GETOPT_option_flag ('T',
818 "Provide information about all tunnels"),
820 GNUNET_GETOPT_OPTION_END};
822 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
825 res = GNUNET_PROGRAM_run (argc,
827 "gnunet-cadet (OPTIONS | PEER_ID SHARED_SECRET)",
828 gettext_noop (helpstr),
833 GNUNET_free ((void *) argv);
835 if (GNUNET_OK == res)
840 /* end of gnunet-cadet.c */