first step to remove plibc
[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  * Task to shut down this application.
205  *
206  * @param cls Closure (unused).
207  */
208 static void
209 shutdown_task (void *cls)
210 {
211   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
212   if (NULL != lp)
213   {
214     GNUNET_CADET_close_port (lp);
215     lp = NULL;
216   }
217   if (NULL != ch)
218   {
219     GNUNET_CADET_channel_destroy (ch);
220     ch = NULL;
221   }
222   if (NULL != gpo)
223   {
224     GNUNET_CADET_get_path_cancel (gpo);
225     gpo = NULL;
226   }
227   if (NULL != plo)
228   {
229     GNUNET_CADET_list_peers_cancel (plo);
230     plo = NULL;
231   }
232   if (NULL != tio)
233   {
234     GNUNET_CADET_list_tunnels_cancel (tio);
235     tio = NULL;
236   }
237   if (NULL != mh)
238   {
239     GNUNET_CADET_disconnect (mh);
240     mh = NULL;
241   }
242   if (NULL != rd_task)
243   {
244     GNUNET_SCHEDULER_cancel (rd_task);
245     rd_task = NULL;
246   }
247   if (NULL != echo_task)
248   {
249     GNUNET_SCHEDULER_cancel (echo_task);
250     echo_task = NULL;
251   }
252   if (NULL != job)
253   {
254     GNUNET_SCHEDULER_cancel (job);
255     job = NULL;
256   }
257 }
258
259 void
260 mq_cb (void *cls)
261 {
262   listen_stdio ();
263 }
264
265
266 /**
267  * Task run in stdio mode, after some data is available at stdin.
268  *
269  * @param cls Closure (unused).
270  */
271 static void
272 read_stdio (void *cls)
273 {
274   struct GNUNET_MQ_Envelope *env;
275   struct GNUNET_MessageHeader *msg;
276   char buf[60000];
277   ssize_t data_size;
278
279   rd_task = NULL;
280   data_size = read (0, buf, 60000);
281   if (data_size < 1)
282   {
283     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284                 "read() returned  %s\n",
285                 strerror (errno));
286     GNUNET_SCHEDULER_shutdown ();
287     return;
288   }
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);
295
296   sent_pkt++;
297
298   if (GNUNET_NO == echo)
299   {
300     // Use MQ's notification if too much data of stdin is pooring in too fast.
301     if (STREAM_BUFFER_SIZE < sent_pkt)
302     {
303       GNUNET_MQ_notify_sent (env, mq_cb, cls);
304       sent_pkt = 0;
305     }
306     else
307     {
308       listen_stdio ();
309     }
310   }
311   else
312   {
313     echo_time = GNUNET_TIME_absolute_get ();
314   }
315 }
316
317
318 /**
319  * Wait for input on STDIO and send it out over the #ch.
320  */
321 static void
322 listen_stdio ()
323 {
324   struct GNUNET_NETWORK_FDSet *rs;
325
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,
331                                          rs,
332                                          NULL,
333                                          &read_stdio,
334                                          NULL);
335   GNUNET_NETWORK_fdset_destroy (rs);
336 }
337
338
339 /**
340  * Function called whenever a channel is destroyed.  Should clean up
341  * any associated state.
342  *
343  * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
344  *
345  * @param cls closure
346  * @param channel connection to the other end (henceforth invalid)
347  */
348 static void
349 channel_ended (void *cls, const struct GNUNET_CADET_Channel *channel)
350 {
351   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n");
352   GNUNET_assert (channel == ch);
353   ch = NULL;
354   GNUNET_SCHEDULER_shutdown ();
355 }
356
357
358 /**
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.
363  *
364  * A call to #GNUNET_CADET_channel_destroy causes the channel to be ignored.
365  * In this case the handler MUST return NULL.
366  *
367  * @param cls closure
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
371  */
372 static void *
373 channel_incoming (void *cls,
374                   struct GNUNET_CADET_Channel *channel,
375                   const struct GNUNET_PeerIdentity *initiator)
376 {
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);
383   lp = NULL;
384   ch = channel;
385   if (GNUNET_NO == echo)
386     listen_stdio ();
387   return channel;
388 }
389
390
391 /**
392  * @brief Send an echo request to the remote peer.
393  *
394  * @param cls Closure (NULL).
395  */
396 static void
397 send_echo (void *cls)
398 {
399   struct GNUNET_MQ_Envelope *env;
400   struct GNUNET_MessageHeader *msg;
401
402   echo_task = NULL;
403   if (NULL == ch)
404     return;
405   env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CADET_CLI);
406   GNUNET_MQ_send (GNUNET_CADET_get_mq (ch), env);
407 }
408
409
410 /**
411  * Check data message sanity. Does nothing so far (all messages are OK).
412  *
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).
417  */
418 static int
419 check_data (void *cls, const struct GNUNET_MessageHeader *message)
420 {
421   return GNUNET_OK; /* all is well-formed */
422 }
423
424
425 /**
426  * Function called whenever a message is received.
427  *
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.
431  *
432  * @param cls NULL
433  * @param message The actual message.
434  */
435 static void
436 handle_data (void *cls, const struct GNUNET_MessageHeader *message)
437 {
438   size_t payload_size = ntohs (message->size) - sizeof (*message);
439   uint16_t len;
440   ssize_t done;
441   uint16_t off;
442   const char *buf;
443
444   GNUNET_CADET_receive_done (ch);
445   if (GNUNET_YES == echo)
446   {
447     if (NULL != listen_port)
448     {
449       struct GNUNET_MQ_Envelope *env;
450       struct GNUNET_MessageHeader *msg;
451
452       env =
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);
456       return;
457     }
458     else
459     {
460       struct GNUNET_TIME_Relative latency;
461
462       latency = GNUNET_TIME_absolute_get_duration (echo_time);
463       echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
464       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
465                   "time: %s\n",
466                   GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
467       echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
468                                                 &send_echo,
469                                                 NULL);
470     }
471   }
472
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];
476   off = 0;
477   while (off < len)
478   {
479     done = write (1, &buf[off], len - off);
480     if (done <= 0)
481     {
482       if (-1 == done)
483         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
484       GNUNET_SCHEDULER_shutdown ();
485       return;
486     }
487     off += done;
488   }
489 }
490
491
492 /**
493  * Method called to retrieve information about all peers in CADET, called
494  * once per peer.
495  *
496  * After last peer has been reported, an additional call with NULL is done.
497  *
498  * @param cls Closure.
499  * @param ple information about peer, or NULL on "EOF".
500  */
501 static void
502 peers_callback (void *cls, const struct GNUNET_CADET_PeerListEntry *ple)
503 {
504   if (NULL == ple)
505   {
506     plo = NULL;
507     GNUNET_SCHEDULER_shutdown ();
508     return;
509   }
510   fprintf (stdout,
511            "%s tunnel: %c, paths: %u\n",
512            GNUNET_i2s_full (&ple->peer),
513            ple->have_tunnel ? 'Y' : 'N',
514            ple->n_paths);
515 }
516
517
518 /**
519  * Method called to retrieve information about paths to a specific peer
520  * known to the service.
521  *
522  * @param cls Closure.
523  * @param ppd path detail
524  */
525 static void
526 path_callback (void *cls, const struct GNUNET_CADET_PeerPathDetail *ppd)
527 {
528   if (NULL == ppd)
529   {
530     gpo = NULL;
531     GNUNET_SCHEDULER_shutdown ();
532     return;
533   }
534   fprintf (stdout, "Path of length %u: ", ppd->path_length);
535   for (unsigned int i = 0; i < ppd->path_length; i++)
536     fprintf (stdout,
537              (i == ppd->target_offset) ? "*%s* " : "%s ",
538              GNUNET_i2s (&ppd->path[i]));
539   fprintf (stdout, "\n");
540 }
541
542
543 /**
544  * Method called to retrieve information about all tunnels in CADET.
545  *
546  * @param cls Closure.
547  * @param td tunnel details
548  */
549 static void
550 tunnels_callback (void *cls, const struct GNUNET_CADET_TunnelDetails *td)
551 {
552   if (NULL == td)
553   {
554     tio = NULL;
555     GNUNET_SCHEDULER_shutdown ();
556     return;
557   }
558   fprintf (stdout,
559            "%s [ENC: %s, CON: %s] CHs: %u, CONNs: %u\n",
560            GNUNET_i2s_full (&td->peer),
561            enc_2s (td->estate),
562            conn_2s (td->cstate),
563            td->channels,
564            td->connections);
565 }
566
567
568 /**
569  * Call CADET's meta API, get all peers known to a peer.
570  *
571  * @param cls Closure (unused).
572  */
573 static void
574 get_peers (void *cls)
575 {
576   job = NULL;
577   plo = GNUNET_CADET_list_peers (my_cfg, &peers_callback, NULL);
578 }
579
580
581 /**
582  * Call CADET's monitor API, get info of one peer.
583  *
584  * @param cls Closure (unused).
585  */
586 static void
587 show_peer (void *cls)
588 {
589   struct GNUNET_PeerIdentity pid;
590
591   job = NULL;
592   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (peer_id,
593                                                                strlen (peer_id),
594                                                                &pid.public_key))
595   {
596     fprintf (stderr, _ ("Invalid peer ID `%s'\n"), peer_id);
597     GNUNET_SCHEDULER_shutdown ();
598     return;
599   }
600   gpo = GNUNET_CADET_get_path (my_cfg, &pid, &path_callback, NULL);
601 }
602
603
604 /**
605  * Call CADET's meta API, get all tunnels known to a peer.
606  *
607  * @param cls Closure (unused).
608  */
609 static void
610 get_tunnels (void *cls)
611 {
612   job = NULL;
613   tio = GNUNET_CADET_list_tunnels (my_cfg, &tunnels_callback, NULL);
614 }
615
616
617 /**
618  * Call CADET's monitor API, get info of one channel.
619  *
620  * @param cls Closure (unused).
621  */
622 static void
623 show_channel (void *cls)
624 {
625   job = NULL;
626   GNUNET_break (0);
627 }
628
629
630 /**
631  * Call CADET's monitor API, get info of one connection.
632  *
633  * @param cls Closure (unused).
634  */
635 static void
636 show_connection (void *cls)
637 {
638   job = NULL;
639   GNUNET_break (0);
640 }
641
642
643 /**
644  * Main function that will be run by the scheduler.
645  *
646  * @param cls closure
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
650  */
651 static void
652 run (void *cls,
653      char *const *args,
654      const char *cfgfile,
655      const struct GNUNET_CONFIGURATION_Handle *cfg)
656 {
657   struct GNUNET_MQ_MessageHandler handlers[] =
658     {GNUNET_MQ_hd_var_size (data,
659                             GNUNET_MESSAGE_TYPE_CADET_CLI,
660                             struct GNUNET_MessageHeader,
661                             NULL),
662      GNUNET_MQ_handler_end ()};
663
664   /* FIXME add option to monitor apps */
665   my_cfg = cfg;
666   target_id = args[0];
667   if (target_id && args[1])
668     target_port = args[1];
669
670   if ((0 != (request_peers | request_tunnels) || NULL != conn_id ||
671        NULL != channel_id) &&
672       target_id != NULL)
673   {
674     fprintf (stderr,
675              _ ("Extra arguments are not applicable "
676                 "in combination with this option.\n"));
677     return;
678   }
679
680   if (NULL != peer_id)
681   {
682     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show peer\n");
683     job = GNUNET_SCHEDULER_add_now (&show_peer, NULL);
684   }
685   else if (NULL != channel_id)
686   {
687     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show channel\n");
688     job = GNUNET_SCHEDULER_add_now (&show_channel, NULL);
689   }
690   else if (NULL != conn_id)
691   {
692     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show connection\n");
693     job = GNUNET_SCHEDULER_add_now (&show_connection, NULL);
694   }
695   else if (GNUNET_YES == request_peers)
696   {
697     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all peers\n");
698     job = GNUNET_SCHEDULER_add_now (&get_peers, NULL);
699   }
700   else if (GNUNET_YES == request_tunnels)
701   {
702     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n");
703     job = GNUNET_SCHEDULER_add_now (&get_tunnels, NULL);
704   }
705
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);
709   if (NULL == mh)
710   {
711     GNUNET_SCHEDULER_shutdown ();
712     return;
713   }
714   if (NULL != listen_port)
715   {
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,
719                                  &porthash,
720                                  &channel_incoming,
721                                  NULL,
722                                  NULL /* window changes */,
723                                  &channel_ended,
724                                  handlers);
725   }
726   if (NULL != target_id)
727   {
728     struct GNUNET_PeerIdentity pid;
729
730     if (GNUNET_OK !=
731         GNUNET_CRYPTO_eddsa_public_key_from_string (target_id,
732                                                     strlen (target_id),
733                                                     &pid.public_key))
734     {
735       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
736                   _ ("Invalid target `%s'\n"),
737                   target_id);
738       GNUNET_SCHEDULER_shutdown ();
739       return;
740     }
741     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
742                 "Connecting to `%s:%s'\n",
743                 target_id,
744                 target_port);
745     GNUNET_CRYPTO_hash (target_port, strlen (target_port), &porthash);
746     ch = GNUNET_CADET_channel_create (mh,
747                                       NULL,
748                                       &pid,
749                                       &porthash,
750                                       NULL /* window changes */,
751                                       &channel_ended,
752                                       handlers);
753     if (GNUNET_YES == echo)
754     {
755       echo_task = GNUNET_SCHEDULER_add_now (&send_echo, NULL);
756     }
757     else
758     {
759       listen_stdio ();
760     }
761   }
762
763   if ((NULL == lp) && (NULL == job) && (NULL == ch))
764   {
765     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, _ ("No action requested\n"));
766     GNUNET_SCHEDULER_shutdown ();
767     return;
768   }
769 }
770
771
772 /**
773  * The main function to obtain peer information.
774  *
775  * @param argc number of arguments from the command line
776  * @param argv command line arguments
777  * @return 0 ok, 1 on error
778  */
779 int
780 main (int argc, char *const *argv)
781 {
782   int res;
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 (
788        'C',
789        "connection",
790        "CONNECTION_ID",
791        gettext_noop ("Provide information about a particular connection"),
792        &conn_id),
793      GNUNET_GETOPT_option_flag ('e',
794                                 "echo",
795                                 gettext_noop ("Activate echo mode"),
796                                 &echo),
797      GNUNET_GETOPT_option_string (
798        'o',
799        "open-port",
800        "SHARED_SECRET",
801        gettext_noop (
802          "Listen for connections using a shared secret among sender and recipient"),
803        &listen_port),
804      GNUNET_GETOPT_option_string ('p',
805                                   "peer",
806                                   "PEER_ID",
807                                   gettext_noop (
808                                     "Provide information about a patricular peer"),
809                                   &peer_id),
810      GNUNET_GETOPT_option_flag ('P',
811                                 "peers",
812                                 gettext_noop (
813                                   "Provide information about all peers"),
814                                 &request_peers),
815      GNUNET_GETOPT_option_flag ('T',
816                                 "tunnels",
817                                 gettext_noop (
818                                   "Provide information about all tunnels"),
819                                 &request_tunnels),
820      GNUNET_GETOPT_OPTION_END};
821
822   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
823     return 2;
824
825   res = GNUNET_PROGRAM_run (argc,
826                             argv,
827                             "gnunet-cadet (OPTIONS | PEER_ID SHARED_SECRET)",
828                             gettext_noop (helpstr),
829                             options,
830                             &run,
831                             NULL);
832
833   GNUNET_free ((void *) argv);
834
835   if (GNUNET_OK == res)
836     return 0;
837   return 1;
838 }
839
840 /* end of gnunet-cadet.c */