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)
180 * Convert connection status to human readable string.
182 * @param status Connection status.
184 * @return Human readable string.
187 conn_2s (uint16_t status)
213 * Task to shut down this application.
215 * @param cls Closure (unused).
218 shutdown_task (void *cls)
220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
223 GNUNET_CADET_close_port (lp);
228 GNUNET_CADET_channel_destroy (ch);
233 GNUNET_CADET_get_path_cancel (gpo);
238 GNUNET_CADET_list_peers_cancel (plo);
243 GNUNET_CADET_list_tunnels_cancel (tio);
248 GNUNET_CADET_disconnect (mh);
253 GNUNET_SCHEDULER_cancel (rd_task);
256 if (NULL != echo_task)
258 GNUNET_SCHEDULER_cancel (echo_task);
263 GNUNET_SCHEDULER_cancel (job);
277 * Task run in stdio mode, after some data is available at stdin.
279 * @param cls Closure (unused).
282 read_stdio (void *cls)
284 struct GNUNET_MQ_Envelope *env;
285 struct GNUNET_MessageHeader *msg;
290 data_size = read (0, buf, 60000);
293 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
294 "read() returned %s\n",
296 GNUNET_SCHEDULER_shutdown ();
299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300 "Read %u bytes from stdio\n",
301 (unsigned int) data_size);
302 env = GNUNET_MQ_msg_extra (msg, data_size, GNUNET_MESSAGE_TYPE_CADET_CLI);
303 GNUNET_memcpy (&msg[1], buf, data_size);
304 GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env);
308 if (GNUNET_NO == echo)
310 // Use MQ's notification if too much data of stdin is pooring in too fast.
311 if (STREAM_BUFFER_SIZE < sent_pkt)
313 GNUNET_MQ_notify_sent (env, mq_cb, cls);
323 echo_time = GNUNET_TIME_absolute_get ();
329 * Wait for input on STDIO and send it out over the #ch.
334 struct GNUNET_NETWORK_FDSet *rs;
336 /* FIXME: why use 'rs' here, seems overly complicated... */
337 rs = GNUNET_NETWORK_fdset_create ();
338 GNUNET_NETWORK_fdset_set_native (rs, 0); /* STDIN */
339 rd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
340 GNUNET_TIME_UNIT_FOREVER_REL,
345 GNUNET_NETWORK_fdset_destroy (rs);
350 * Function called whenever a channel is destroyed. Should clean up
351 * any associated state.
353 * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
356 * @param channel connection to the other end (henceforth invalid)
359 channel_ended (void *cls, const struct GNUNET_CADET_Channel *channel)
361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n");
362 GNUNET_assert (channel == ch);
364 GNUNET_SCHEDULER_shutdown ();
369 * Method called whenever another peer has added us to a channel
370 * the other peer initiated.
371 * Only called (once) upon reception of data with a message type which was
372 * subscribed to in #GNUNET_CADET_connect.
374 * A call to #GNUNET_CADET_channel_destroy causes the channel to be ignored.
375 * In this case the handler MUST return NULL.
378 * @param channel new handle to the channel
379 * @param initiator peer that started the channel
380 * @return initial channel context for the channel, we use @a channel
383 channel_incoming (void *cls,
384 struct GNUNET_CADET_Channel *channel,
385 const struct GNUNET_PeerIdentity *initiator)
387 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
388 "Incoming connection from %s\n",
389 GNUNET_i2s_full (initiator));
390 GNUNET_assert (NULL == ch);
391 GNUNET_assert (NULL != lp);
392 GNUNET_CADET_close_port (lp);
395 if (GNUNET_NO == echo)
402 * @brief Send an echo request to the remote peer.
404 * @param cls Closure (NULL).
407 send_echo (void *cls)
409 struct GNUNET_MQ_Envelope *env;
410 struct GNUNET_MessageHeader *msg;
415 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_CLI);
416 GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env);
421 * Check data message sanity. Does nothing so far (all messages are OK).
423 * @param cls Closure (unused).
424 * @param message The message to check.
425 * @return #GNUNET_OK to keep the channel open,
426 * #GNUNET_SYSERR to close it (signal serious error).
429 check_data (void *cls, const struct GNUNET_MessageHeader *message)
431 return GNUNET_OK; /* all is well-formed */
436 * Function called whenever a message is received.
438 * Each time the function must call #GNUNET_CADET_receive_done on the channel
439 * in order to receive the next message. This doesn't need to be immediate:
440 * can be delayed if some processing is done on the message.
443 * @param message The actual message.
446 handle_data (void *cls, const struct GNUNET_MessageHeader *message)
448 size_t payload_size = ntohs (message->size) - sizeof(*message);
454 GNUNET_CADET_receive_done (ch);
455 if (GNUNET_YES == echo)
457 if (NULL != listen_port)
459 struct GNUNET_MQ_Envelope *env;
460 struct GNUNET_MessageHeader *msg;
463 GNUNET_MQ_msg_extra (msg, payload_size, GNUNET_MESSAGE_TYPE_CADET_CLI);
464 GNUNET_memcpy (&msg[1], &message[1], payload_size);
465 GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env);
470 struct GNUNET_TIME_Relative latency;
472 latency = GNUNET_TIME_absolute_get_duration (echo_time);
473 echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
474 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
476 GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
477 echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
483 len = ntohs (message->size) - sizeof(*message);
484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len);
485 buf = (const char *) &message[1];
489 done = write (1, &buf[off], len - off);
493 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
494 GNUNET_SCHEDULER_shutdown ();
503 * Method called to retrieve information about all peers in CADET, called
506 * After last peer has been reported, an additional call with NULL is done.
508 * @param cls Closure.
509 * @param ple information about peer, or NULL on "EOF".
512 peers_callback (void *cls, const struct GNUNET_CADET_PeerListEntry *ple)
517 GNUNET_SCHEDULER_shutdown ();
521 "%s tunnel: %c, paths: %u\n",
522 GNUNET_i2s_full (&ple->peer),
523 ple->have_tunnel ? 'Y' : 'N',
529 * Method called to retrieve information about paths to a specific peer
530 * known to the service.
532 * @param cls Closure.
533 * @param ppd path detail
536 path_callback (void *cls, const struct GNUNET_CADET_PeerPathDetail *ppd)
541 GNUNET_SCHEDULER_shutdown ();
544 fprintf (stdout, "Path of length %u: ", ppd->path_length);
545 for (unsigned int i = 0; i < ppd->path_length; i++)
547 (i == ppd->target_offset) ? "*%s* " : "%s ",
548 GNUNET_i2s (&ppd->path[i]));
549 fprintf (stdout, "\n");
554 * Method called to retrieve information about all tunnels in CADET.
556 * @param cls Closure.
557 * @param td tunnel details
560 tunnels_callback (void *cls, const struct GNUNET_CADET_TunnelDetails *td)
565 GNUNET_SCHEDULER_shutdown ();
569 "%s [ENC: %s, CON: %s] CHs: %u, CONNs: %u\n",
570 GNUNET_i2s_full (&td->peer),
572 conn_2s (td->cstate),
579 * Call CADET's meta API, get all peers known to a peer.
581 * @param cls Closure (unused).
584 get_peers (void *cls)
587 plo = GNUNET_CADET_list_peers (my_cfg, &peers_callback, NULL);
592 * Call CADET's monitor API, get info of one peer.
594 * @param cls Closure (unused).
597 show_peer (void *cls)
599 struct GNUNET_PeerIdentity pid;
602 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (peer_id,
606 fprintf (stderr, _ ("Invalid peer ID `%s'\n"), peer_id);
607 GNUNET_SCHEDULER_shutdown ();
610 gpo = GNUNET_CADET_get_path (my_cfg, &pid, &path_callback, NULL);
615 * Call CADET's meta API, get all tunnels known to a peer.
617 * @param cls Closure (unused).
620 get_tunnels (void *cls)
623 tio = GNUNET_CADET_list_tunnels (my_cfg, &tunnels_callback, NULL);
628 * Call CADET's monitor API, get info of one channel.
630 * @param cls Closure (unused).
633 show_channel (void *cls)
641 * Call CADET's monitor API, get info of one connection.
643 * @param cls Closure (unused).
646 show_connection (void *cls)
654 * Main function that will be run by the scheduler.
657 * @param args remaining command-line arguments
658 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
659 * @param cfg configuration
665 const struct GNUNET_CONFIGURATION_Handle *cfg)
667 struct GNUNET_MQ_MessageHandler handlers[] =
668 { GNUNET_MQ_hd_var_size (data,
669 GNUNET_MESSAGE_TYPE_CADET_CLI,
670 struct GNUNET_MessageHeader,
672 GNUNET_MQ_handler_end () };
674 /* FIXME add option to monitor apps */
677 if (target_id && args[1])
678 target_port = args[1];
680 if (((0 != (request_peers | request_tunnels)) || (NULL != conn_id) ||
681 (NULL != channel_id) ) &&
682 (target_id != NULL) )
685 _ ("Extra arguments are not applicable "
686 "in combination with this option.\n"));
692 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show peer\n");
693 job = GNUNET_SCHEDULER_add_now (&show_peer, NULL);
695 else if (NULL != channel_id)
697 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show channel\n");
698 job = GNUNET_SCHEDULER_add_now (&show_channel, NULL);
700 else if (NULL != conn_id)
702 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show connection\n");
703 job = GNUNET_SCHEDULER_add_now (&show_connection, NULL);
705 else if (GNUNET_YES == request_peers)
707 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all peers\n");
708 job = GNUNET_SCHEDULER_add_now (&get_peers, NULL);
710 else if (GNUNET_YES == request_tunnels)
712 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n");
713 job = GNUNET_SCHEDULER_add_now (&get_tunnels, NULL);
716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CADET service\n");
717 mh = GNUNET_CADET_connect (cfg);
718 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
721 GNUNET_SCHEDULER_shutdown ();
724 if (NULL != listen_port)
726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opening CADET listen port\n");
727 GNUNET_CRYPTO_hash (listen_port, strlen (listen_port), &porthash);
728 lp = GNUNET_CADET_open_port (mh,
732 NULL /* window changes */,
736 if (NULL != target_id)
738 struct GNUNET_PeerIdentity pid;
741 GNUNET_CRYPTO_eddsa_public_key_from_string (target_id,
745 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
746 _ ("Invalid target `%s'\n"),
748 GNUNET_SCHEDULER_shutdown ();
751 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
752 "Connecting to `%s:%s'\n",
755 GNUNET_CRYPTO_hash (target_port, strlen (target_port), &porthash);
756 ch = GNUNET_CADET_channel_create (mh,
760 NULL /* window changes */,
763 if (GNUNET_YES == echo)
765 echo_task = GNUNET_SCHEDULER_add_now (&send_echo, NULL);
773 if ((NULL == lp) && (NULL == job) && (NULL == ch))
775 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, _ ("No action requested\n"));
776 GNUNET_SCHEDULER_shutdown ();
783 * The main function to obtain peer information.
785 * @param argc number of arguments from the command line
786 * @param argv command line arguments
787 * @return 0 ok, 1 on error
790 main (int argc, char *const *argv)
793 const char helpstr[] =
794 "Create tunnels and retrieve info about CADET's status.";
795 struct GNUNET_GETOPT_CommandLineOption options[] = { /* I would use the terminology 'circuit' here... --lynX */
796 GNUNET_GETOPT_option_string (
800 gettext_noop ("Provide information about a particular connection"),
802 GNUNET_GETOPT_option_flag ('e',
804 gettext_noop ("Activate echo mode"),
806 GNUNET_GETOPT_option_string (
811 "Listen for connections using a shared secret among sender and recipient"),
813 GNUNET_GETOPT_option_string ('p',
817 "Provide information about a patricular peer"),
819 GNUNET_GETOPT_option_flag ('P',
822 "Provide information about all peers"),
824 GNUNET_GETOPT_option_flag ('T',
827 "Provide information about all tunnels"),
829 GNUNET_GETOPT_OPTION_END
832 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
835 res = GNUNET_PROGRAM_run (argc,
837 "gnunet-cadet (OPTIONS | PEER_ID SHARED_SECRET)",
838 gettext_noop (helpstr),
843 GNUNET_free ((void *) argv);
845 if (GNUNET_OK == res)
851 /* end of gnunet-cadet.c */