123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2012, 2017, 2019 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file cadet/gnunet-cadet.c
- * @brief Print information about cadet tunnels and peers.
- * @author Bartlomiej Polot
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_util_lib.h"
- #include "gnunet_cadet_service.h"
- #include "cadet.h"
- #define STREAM_BUFFER_SIZE 1024 // Pakets
- /**
- * Option -P.
- */
- static int request_peers;
- /**
- * Option --peer
- */
- static char *peer_id;
- /**
- * Option -T.
- */
- static int request_tunnels;
- /**
- * Option --connection
- */
- static char *conn_id;
- /**
- * Option --channel
- */
- static char *channel_id;
- /**
- * Port to listen on (-o).
- */
- static char *listen_port;
- /**
- * Request echo service
- */
- static int echo;
- /**
- * Time of last echo request.
- */
- static struct GNUNET_TIME_Absolute echo_time;
- /**
- * Task for next echo request.
- */
- static struct GNUNET_SCHEDULER_Task *echo_task;
- /**
- * Peer to connect to.
- */
- static char *target_id;
- /**
- * Port to connect to
- */
- static char *target_port = "default";
- /**
- * Cadet handle.
- */
- static struct GNUNET_CADET_Handle *mh;
- /**
- * Our configuration.
- */
- static const struct GNUNET_CONFIGURATION_Handle *my_cfg;
- /**
- * Active get path operation.
- */
- static struct GNUNET_CADET_GetPath *gpo;
- /**
- * Active peer listing operation.
- */
- static struct GNUNET_CADET_PeersLister *plo;
- /**
- * Active tunnel listing operation.
- */
- static struct GNUNET_CADET_ListTunnels *tio;
- /**
- * Channel handle.
- */
- static struct GNUNET_CADET_Channel *ch;
- /**
- * HashCode of the given port string
- */
- static struct GNUNET_HashCode porthash;
- /**
- * Data structure for ongoing reception of incoming virtual circuits.
- */
- struct GNUNET_CADET_Port *lp;
- /**
- * Task for reading from stdin.
- */
- static struct GNUNET_SCHEDULER_Task *rd_task;
- /**
- * Task for main job.
- */
- static struct GNUNET_SCHEDULER_Task *job;
- static unsigned int sent_pkt;
- /**
- * Wait for input on STDIO and send it out over the #ch.
- */
- static void
- listen_stdio(void);
- /**
- * Convert encryption status to human readable string.
- *
- * @param status Encryption status.
- *
- * @return Human readable string.
- */
- static const char *
- enc_2s(uint16_t status)
- {
- switch (status)
- {
- case 0:
- return "NULL ";
- case 1:
- return "KSENT";
- case 2:
- return "KRECV";
- case 3:
- return "READY";
- default:
- return "";
- }
- }
- /**
- * Convert connection status to human readable string.
- *
- * @param status Connection status.
- *
- * @return Human readable string.
- */
- static const char *
- conn_2s(uint16_t status)
- {
- switch (status)
- {
- case 0:
- return "NEW ";
- case 1:
- return "SRCH ";
- case 2:
- return "WAIT ";
- case 3:
- return "READY";
- case 4:
- return "SHUTD";
- default:
- return "";
- }
- }
- /**
- * Task to shut down this application.
- *
- * @param cls Closure (unused).
- */
- static void
- shutdown_task(void *cls)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
- if (NULL != lp)
- {
- GNUNET_CADET_close_port(lp);
- lp = NULL;
- }
- if (NULL != ch)
- {
- GNUNET_CADET_channel_destroy(ch);
- ch = NULL;
- }
- if (NULL != gpo)
- {
- GNUNET_CADET_get_path_cancel(gpo);
- gpo = NULL;
- }
- if (NULL != plo)
- {
- GNUNET_CADET_list_peers_cancel(plo);
- plo = NULL;
- }
- if (NULL != tio)
- {
- GNUNET_CADET_list_tunnels_cancel(tio);
- tio = NULL;
- }
- if (NULL != mh)
- {
- GNUNET_CADET_disconnect(mh);
- mh = NULL;
- }
- if (NULL != rd_task)
- {
- GNUNET_SCHEDULER_cancel(rd_task);
- rd_task = NULL;
- }
- if (NULL != echo_task)
- {
- GNUNET_SCHEDULER_cancel(echo_task);
- echo_task = NULL;
- }
- if (NULL != job)
- {
- GNUNET_SCHEDULER_cancel(job);
- job = NULL;
- }
- }
- void
- mq_cb(void *cls)
- {
- listen_stdio();
- }
- /**
- * Task run in stdio mode, after some data is available at stdin.
- *
- * @param cls Closure (unused).
- */
- static void
- read_stdio(void *cls)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_MessageHeader *msg;
- char buf[60000];
- ssize_t data_size;
- rd_task = NULL;
- data_size = read(0, buf, 60000);
- if (data_size < 1)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
- "read() returned %s\n",
- strerror(errno));
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
- "Read %u bytes from stdio\n",
- (unsigned int)data_size);
- env = GNUNET_MQ_msg_extra(msg, data_size, GNUNET_MESSAGE_TYPE_CADET_CLI);
- GNUNET_memcpy(&msg[1], buf, data_size);
- GNUNET_MQ_send(GNUNET_CADET_get_mq(ch), env);
- sent_pkt++;
- if (GNUNET_NO == echo)
- {
- // Use MQ's notification if too much data of stdin is pooring in too fast.
- if (STREAM_BUFFER_SIZE < sent_pkt)
- {
- GNUNET_MQ_notify_sent(env, mq_cb, cls);
- sent_pkt = 0;
- }
- else
- {
- listen_stdio();
- }
- }
- else
- {
- echo_time = GNUNET_TIME_absolute_get();
- }
- }
- /**
- * Wait for input on STDIO and send it out over the #ch.
- */
- static void
- listen_stdio()
- {
- struct GNUNET_NETWORK_FDSet *rs;
- /* FIXME: why use 'rs' here, seems overly complicated... */
- rs = GNUNET_NETWORK_fdset_create();
- GNUNET_NETWORK_fdset_set_native(rs, 0); /* STDIN */
- rd_task = GNUNET_SCHEDULER_add_select(GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- GNUNET_TIME_UNIT_FOREVER_REL,
- rs,
- NULL,
- &read_stdio,
- NULL);
- GNUNET_NETWORK_fdset_destroy(rs);
- }
- /**
- * Function called whenever a channel is destroyed. Should clean up
- * any associated state.
- *
- * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
- *
- * @param cls closure
- * @param channel connection to the other end (henceforth invalid)
- */
- static void
- channel_ended(void *cls, const struct GNUNET_CADET_Channel *channel)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n");
- GNUNET_assert(channel == ch);
- ch = NULL;
- GNUNET_SCHEDULER_shutdown();
- }
- /**
- * Method called whenever another peer has added us to a channel
- * the other peer initiated.
- * Only called (once) upon reception of data with a message type which was
- * subscribed to in #GNUNET_CADET_connect.
- *
- * A call to #GNUNET_CADET_channel_destroy causes the channel to be ignored.
- * In this case the handler MUST return NULL.
- *
- * @param cls closure
- * @param channel new handle to the channel
- * @param initiator peer that started the channel
- * @return initial channel context for the channel, we use @a channel
- */
- static void *
- channel_incoming(void *cls,
- struct GNUNET_CADET_Channel *channel,
- const struct GNUNET_PeerIdentity *initiator)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_MESSAGE,
- "Incoming connection from %s\n",
- GNUNET_i2s_full(initiator));
- GNUNET_assert(NULL == ch);
- GNUNET_assert(NULL != lp);
- GNUNET_CADET_close_port(lp);
- lp = NULL;
- ch = channel;
- if (GNUNET_NO == echo)
- listen_stdio();
- return channel;
- }
- /**
- * @brief Send an echo request to the remote peer.
- *
- * @param cls Closure (NULL).
- */
- static void
- send_echo(void *cls)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_MessageHeader *msg;
- echo_task = NULL;
- if (NULL == ch)
- return;
- env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_CADET_CLI);
- GNUNET_MQ_send(GNUNET_CADET_get_mq(ch), env);
- }
- /**
- * Check data message sanity. Does nothing so far (all messages are OK).
- *
- * @param cls Closure (unused).
- * @param message The message to check.
- * @return #GNUNET_OK to keep the channel open,
- * #GNUNET_SYSERR to close it (signal serious error).
- */
- static int
- check_data(void *cls, const struct GNUNET_MessageHeader *message)
- {
- return GNUNET_OK; /* all is well-formed */
- }
- /**
- * Function called whenever a message is received.
- *
- * Each time the function must call #GNUNET_CADET_receive_done on the channel
- * in order to receive the next message. This doesn't need to be immediate:
- * can be delayed if some processing is done on the message.
- *
- * @param cls NULL
- * @param message The actual message.
- */
- static void
- handle_data(void *cls, const struct GNUNET_MessageHeader *message)
- {
- size_t payload_size = ntohs(message->size) - sizeof(*message);
- uint16_t len;
- ssize_t done;
- uint16_t off;
- const char *buf;
- GNUNET_CADET_receive_done(ch);
- if (GNUNET_YES == echo)
- {
- if (NULL != listen_port)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_MessageHeader *msg;
- env =
- GNUNET_MQ_msg_extra(msg, payload_size, GNUNET_MESSAGE_TYPE_CADET_CLI);
- GNUNET_memcpy(&msg[1], &message[1], payload_size);
- GNUNET_MQ_send(GNUNET_CADET_get_mq(ch), env);
- return;
- }
- else
- {
- struct GNUNET_TIME_Relative latency;
- latency = GNUNET_TIME_absolute_get_duration(echo_time);
- echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
- GNUNET_log(GNUNET_ERROR_TYPE_MESSAGE,
- "time: %s\n",
- GNUNET_STRINGS_relative_time_to_string(latency, GNUNET_NO));
- echo_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS,
- &send_echo,
- NULL);
- }
- }
- len = ntohs(message->size) - sizeof(*message);
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len);
- buf = (const char *)&message[1];
- off = 0;
- while (off < len)
- {
- done = write(1, &buf[off], len - off);
- if (done <= 0)
- {
- if (-1 == done)
- GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING, "write");
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- off += done;
- }
- }
- /**
- * Method called to retrieve information about all peers in CADET, called
- * once per peer.
- *
- * After last peer has been reported, an additional call with NULL is done.
- *
- * @param cls Closure.
- * @param ple information about peer, or NULL on "EOF".
- */
- static void
- peers_callback(void *cls, const struct GNUNET_CADET_PeerListEntry *ple)
- {
- if (NULL == ple)
- {
- plo = NULL;
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- fprintf(stdout,
- "%s tunnel: %c, paths: %u\n",
- GNUNET_i2s_full(&ple->peer),
- ple->have_tunnel ? 'Y' : 'N',
- ple->n_paths);
- }
- /**
- * Method called to retrieve information about paths to a specific peer
- * known to the service.
- *
- * @param cls Closure.
- * @param ppd path detail
- */
- static void
- path_callback(void *cls, const struct GNUNET_CADET_PeerPathDetail *ppd)
- {
- if (NULL == ppd)
- {
- gpo = NULL;
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- fprintf(stdout, "Path of length %u: ", ppd->path_length);
- for (unsigned int i = 0; i < ppd->path_length; i++)
- fprintf(stdout,
- (i == ppd->target_offset) ? "*%s* " : "%s ",
- GNUNET_i2s(&ppd->path[i]));
- fprintf(stdout, "\n");
- }
- /**
- * Method called to retrieve information about all tunnels in CADET.
- *
- * @param cls Closure.
- * @param td tunnel details
- */
- static void
- tunnels_callback(void *cls, const struct GNUNET_CADET_TunnelDetails *td)
- {
- if (NULL == td)
- {
- tio = NULL;
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- fprintf(stdout,
- "%s [ENC: %s, CON: %s] CHs: %u, CONNs: %u\n",
- GNUNET_i2s_full(&td->peer),
- enc_2s(td->estate),
- conn_2s(td->cstate),
- td->channels,
- td->connections);
- }
- /**
- * Call CADET's meta API, get all peers known to a peer.
- *
- * @param cls Closure (unused).
- */
- static void
- get_peers(void *cls)
- {
- job = NULL;
- plo = GNUNET_CADET_list_peers(my_cfg, &peers_callback, NULL);
- }
- /**
- * Call CADET's monitor API, get info of one peer.
- *
- * @param cls Closure (unused).
- */
- static void
- show_peer(void *cls)
- {
- struct GNUNET_PeerIdentity pid;
- job = NULL;
- if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string(peer_id,
- strlen(peer_id),
- &pid.public_key))
- {
- fprintf(stderr, _("Invalid peer ID `%s'\n"), peer_id);
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- gpo = GNUNET_CADET_get_path(my_cfg, &pid, &path_callback, NULL);
- }
- /**
- * Call CADET's meta API, get all tunnels known to a peer.
- *
- * @param cls Closure (unused).
- */
- static void
- get_tunnels(void *cls)
- {
- job = NULL;
- tio = GNUNET_CADET_list_tunnels(my_cfg, &tunnels_callback, NULL);
- }
- /**
- * Call CADET's monitor API, get info of one channel.
- *
- * @param cls Closure (unused).
- */
- static void
- show_channel(void *cls)
- {
- job = NULL;
- GNUNET_break(0);
- }
- /**
- * Call CADET's monitor API, get info of one connection.
- *
- * @param cls Closure (unused).
- */
- static void
- show_connection(void *cls)
- {
- job = NULL;
- GNUNET_break(0);
- }
- /**
- * Main function that will be run by the scheduler.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param cfg configuration
- */
- static void
- run(void *cls,
- char *const *args,
- const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
- {
- struct GNUNET_MQ_MessageHandler handlers[] =
- { GNUNET_MQ_hd_var_size(data,
- GNUNET_MESSAGE_TYPE_CADET_CLI,
- struct GNUNET_MessageHeader,
- NULL),
- GNUNET_MQ_handler_end() };
- /* FIXME add option to monitor apps */
- my_cfg = cfg;
- target_id = args[0];
- if (target_id && args[1])
- target_port = args[1];
- if ((0 != (request_peers | request_tunnels) || NULL != conn_id ||
- NULL != channel_id) &&
- target_id != NULL)
- {
- fprintf(stderr,
- _("Extra arguments are not applicable "
- "in combination with this option.\n"));
- return;
- }
- if (NULL != peer_id)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Show peer\n");
- job = GNUNET_SCHEDULER_add_now(&show_peer, NULL);
- }
- else if (NULL != channel_id)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Show channel\n");
- job = GNUNET_SCHEDULER_add_now(&show_channel, NULL);
- }
- else if (NULL != conn_id)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Show connection\n");
- job = GNUNET_SCHEDULER_add_now(&show_connection, NULL);
- }
- else if (GNUNET_YES == request_peers)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Show all peers\n");
- job = GNUNET_SCHEDULER_add_now(&get_peers, NULL);
- }
- else if (GNUNET_YES == request_tunnels)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n");
- job = GNUNET_SCHEDULER_add_now(&get_tunnels, NULL);
- }
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connecting to CADET service\n");
- mh = GNUNET_CADET_connect(cfg);
- GNUNET_SCHEDULER_add_shutdown(&shutdown_task, NULL);
- if (NULL == mh)
- {
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- if (NULL != listen_port)
- {
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening CADET listen port\n");
- GNUNET_CRYPTO_hash(listen_port, strlen(listen_port), &porthash);
- lp = GNUNET_CADET_open_port(mh,
- &porthash,
- &channel_incoming,
- NULL,
- NULL /* window changes */,
- &channel_ended,
- handlers);
- }
- if (NULL != target_id)
- {
- struct GNUNET_PeerIdentity pid;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_public_key_from_string(target_id,
- strlen(target_id),
- &pid.public_key))
- {
- GNUNET_log(GNUNET_ERROR_TYPE_MESSAGE,
- _("Invalid target `%s'\n"),
- target_id);
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
- "Connecting to `%s:%s'\n",
- target_id,
- target_port);
- GNUNET_CRYPTO_hash(target_port, strlen(target_port), &porthash);
- ch = GNUNET_CADET_channel_create(mh,
- NULL,
- &pid,
- &porthash,
- NULL /* window changes */,
- &channel_ended,
- handlers);
- if (GNUNET_YES == echo)
- {
- echo_task = GNUNET_SCHEDULER_add_now(&send_echo, NULL);
- }
- else
- {
- listen_stdio();
- }
- }
- if ((NULL == lp) && (NULL == job) && (NULL == ch))
- {
- GNUNET_log(GNUNET_ERROR_TYPE_MESSAGE, _("No action requested\n"));
- GNUNET_SCHEDULER_shutdown();
- return;
- }
- }
- /**
- * The main function to obtain peer information.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
- int
- main(int argc, char *const *argv)
- {
- int res;
- const char helpstr[] =
- "Create tunnels and retrieve info about CADET's status.";
- struct GNUNET_GETOPT_CommandLineOption options[] =
- { /* I would use the terminology 'circuit' here... --lynX */
- GNUNET_GETOPT_option_string(
- 'C',
- "connection",
- "CONNECTION_ID",
- gettext_noop("Provide information about a particular connection"),
- &conn_id),
- GNUNET_GETOPT_option_flag('e',
- "echo",
- gettext_noop("Activate echo mode"),
- &echo),
- GNUNET_GETOPT_option_string(
- 'o',
- "open-port",
- "SHARED_SECRET",
- gettext_noop(
- "Listen for connections using a shared secret among sender and recipient"),
- &listen_port),
- GNUNET_GETOPT_option_string('p',
- "peer",
- "PEER_ID",
- gettext_noop(
- "Provide information about a patricular peer"),
- &peer_id),
- GNUNET_GETOPT_option_flag('P',
- "peers",
- gettext_noop(
- "Provide information about all peers"),
- &request_peers),
- GNUNET_GETOPT_option_flag('T',
- "tunnels",
- gettext_noop(
- "Provide information about all tunnels"),
- &request_tunnels),
- GNUNET_GETOPT_OPTION_END
- };
- if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args(argc, argv, &argc, &argv))
- return 2;
- res = GNUNET_PROGRAM_run(argc,
- argv,
- "gnunet-cadet (OPTIONS | PEER_ID SHARED_SECRET)",
- gettext_noop(helpstr),
- options,
- &run,
- NULL);
- GNUNET_free((void *)argv);
- if (GNUNET_OK == res)
- return 0;
- return 1;
- }
- /* end of gnunet-cadet.c */
|